Langsung ke konten utama

Documentation Index

Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt

Use this file to discover all available pages before exploring further.

Untuk panduan quickstart dengan contoh, lihat Otomatisasi alur kerja dengan hooks.
Hooks adalah perintah shell yang ditentukan pengguna, endpoint HTTP, atau prompt LLM yang dijalankan secara otomatis pada titik-titik tertentu dalam siklus hidup Claude Code. Gunakan referensi ini untuk mencari skema event, opsi konfigurasi, format JSON input/output, dan fitur lanjutan seperti async hooks, HTTP hooks, dan MCP tool hooks. Jika Anda menyiapkan hooks untuk pertama kalinya, mulai dengan panduan sebagai gantinya.

Siklus hidup hook

Hooks dijalankan pada titik-titik tertentu selama sesi Claude Code. Ketika event dijalankan dan matcher cocok, Claude Code meneruskan konteks JSON tentang event ke handler hook Anda. Untuk command hooks, input tiba di stdin. Untuk HTTP hooks, input tiba sebagai badan permintaan POST. Handler Anda kemudian dapat memeriksa input, mengambil tindakan, dan secara opsional mengembalikan keputusan. Events jatuh ke dalam tiga cadence: sekali per sesi (SessionStart, SessionEnd), sekali per turn (UserPromptSubmit, Stop, StopFailure), dan pada setiap pemanggilan tool di dalam loop agentic (PreToolUse, PostToolUse):
Diagram siklus hidup hook menunjukkan Setup opsional yang mengalir ke SessionStart, kemudian loop per-turn yang berisi UserPromptSubmit, UserPromptExpansion untuk slash commands, loop agentic bersarang (PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure, PostToolBatch, SubagentStart/Stop, TaskCreated, TaskCompleted), dan Stop atau StopFailure, diikuti TeammateIdle, PreCompact, PostCompact, dan SessionEnd, dengan Elicitation dan ElicitationResult bersarang di dalam eksekusi MCP tool, PermissionDenied sebagai cabang samping dari PermissionRequest untuk penolakan mode otomatis, dan WorktreeCreate, WorktreeRemove, Notification, ConfigChange, InstructionsLoaded, CwdChanged, dan FileChanged sebagai event asinkron mandiri
Tabel di bawah merangkum kapan setiap event dijalankan. Bagian Hook events mendokumentasikan skema input lengkap dan opsi kontrol keputusan untuk masing-masing.
EventWhen it fires
SessionStartWhen a session begins or resumes
SetupWhen you start Claude Code with --init-only, or with --init or --maintenance in -p mode. For one-time preparation in CI or scripts
UserPromptSubmitWhen you submit a prompt, before Claude processes it
UserPromptExpansionWhen a user-typed command expands into a prompt, before it reaches Claude. Can block the expansion
PreToolUseBefore a tool call executes. Can block it
PermissionRequestWhen a permission dialog appears
PermissionDeniedWhen a tool call is denied by the auto mode classifier. Return {retry: true} to tell the model it may retry the denied tool call
PostToolUseAfter a tool call succeeds
PostToolUseFailureAfter a tool call fails
PostToolBatchAfter a full batch of parallel tool calls resolves, before the next model call
NotificationWhen Claude Code sends a notification
SubagentStartWhen a subagent is spawned
SubagentStopWhen a subagent finishes
TaskCreatedWhen a task is being created via TaskCreate
TaskCompletedWhen a task is being marked as completed
StopWhen Claude finishes responding
StopFailureWhen the turn ends due to an API error. Output and exit code are ignored
TeammateIdleWhen an agent team teammate is about to go idle
InstructionsLoadedWhen a CLAUDE.md or .claude/rules/*.md file is loaded into context. Fires at session start and when files are lazily loaded during a session
ConfigChangeWhen a configuration file changes during a session
CwdChangedWhen the working directory changes, for example when Claude executes a cd command. Useful for reactive environment management with tools like direnv
FileChangedWhen a watched file changes on disk. The matcher field specifies which filenames to watch
WorktreeCreateWhen a worktree is being created via --worktree or isolation: "worktree". Replaces default git behavior
WorktreeRemoveWhen a worktree is being removed, either at session exit or when a subagent finishes
PreCompactBefore context compaction
PostCompactAfter context compaction completes
ElicitationWhen an MCP server requests user input during a tool call
ElicitationResultAfter a user responds to an MCP elicitation, before the response is sent back to the server
SessionEndWhen a session terminates

Bagaimana hook diselesaikan

Untuk melihat bagaimana potongan-potongan ini cocok bersama, pertimbangkan hook PreToolUse ini yang memblokir perintah shell yang merusak. matcher mempersempit ke pemanggilan tool Bash dan kondisi if mempersempit lebih lanjut ke perintah Bash yang cocok dengan rm *, jadi block-rm.sh hanya spawn ketika kedua filter cocok:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(rm *)",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh",
            "args": []
          }
        ]
      }
    ]
  }
}
Skrip membaca input JSON dari stdin, mengekstrak perintah, dan mengembalikan permissionDecision dari "deny" jika berisi rm -rf:
#!/bin/bash
# .claude/hooks/block-rm.sh
COMMAND=$(jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q 'rm -rf'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Destructive command blocked by hook"
    }
  }'
else
  exit 0  # allow the command
fi
Sekarang anggaplah Claude Code memutuskan untuk menjalankan Bash "rm -rf /tmp/build". Inilah yang terjadi:
Alur resolusi hook: event PreToolUse dijalankan, matcher memeriksa kecocokan Bash, kondisi if memeriksa kecocokan Bash(rm *), handler hook dijalankan, hasil dikembalikan ke Claude Code
1

Event dijalankan

Event PreToolUse dijalankan. Claude Code mengirimkan input tool sebagai JSON di stdin ke hook:
{ "tool_name": "Bash", "tool_input": { "command": "rm -rf /tmp/build" }, ... }
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 rm -rf, jadi itu mencetak keputusan ke stdout:
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Destructive command blocked by hook"
  }
}
Jika perintah telah menjadi varian rm yang lebih aman seperti rm file.txt, skrip akan mencapai exit 0 sebagai gantinya, yang memberitahu Claude Code untuk mengizinkan pemanggilan tool tanpa tindakan lebih lanjut.
5

Claude Code bertindak atas hasil

Claude Code membaca keputusan JSON, memblokir pemanggilan tool, dan menunjukkan alasannya kepada Claude.
Bagian Configuration di bawah mendokumentasikan skema lengkap, dan setiap bagian hook event mendokumentasikan input apa yang diterima perintah Anda dan output apa yang dapat dikembalikan.

Konfigurasi

Hooks didefinisikan dalam file pengaturan JSON. Konfigurasi memiliki tiga tingkat nesting:
  1. Pilih hook event untuk merespons, seperti PreToolUse atau Stop
  2. Tambahkan matcher group untuk memfilter kapan dijalankan, seperti “hanya untuk tool Bash”
  3. Tentukan satu atau lebih hook handlers untuk dijalankan saat cocok
Lihat Bagaimana hook diselesaikan di atas untuk panduan lengkap dengan contoh beranotasi.
Halaman ini menggunakan istilah spesifik untuk setiap tingkat: hook event untuk titik siklus hidup, matcher group untuk filter, dan hook handler untuk perintah shell, endpoint HTTP, tool MCP, prompt, atau agent yang dijalankan. “Hook” sendiri merujuk pada fitur umum.

Lokasi hook

Tempat Anda mendefinisikan hook menentukan cakupannya:
LokasiCakupanDapat Dibagikan
~/.claude/settings.jsonSemua proyek AndaTidak, lokal ke mesin Anda
.claude/settings.jsonProyek tunggalYa, dapat dikomit ke repo
.claude/settings.local.jsonProyek tunggalTidak, gitignored
Pengaturan kebijakan terkelolaSeluruh organisasiYa, dikendalikan admin
Plugin hooks/hooks.jsonKetika plugin diaktifkanYa, dibundel dengan plugin
Skill atau agent frontmatterSaat komponen aktifYa, didefinisikan dalam file komponen
Untuk detail tentang resolusi file pengaturan, lihat settings. Administrator enterprise dapat menggunakan allowManagedHooksOnly untuk memblokir hooks pengguna, proyek, dan plugin. Hooks dari plugins yang dipaksa-aktifkan dalam pengaturan terkelola enabledPlugins dikecualikan, jadi administrator dapat mendistribusikan hooks yang telah diverifikasi melalui marketplace organisasi. Lihat Hook configuration.

Pola matcher

Bidang matcher memfilter kapan hooks dijalankan. Bagaimana matcher dievaluasi tergantung pada karakter yang dikandungnya:
Nilai matcherDievaluasi sebagaiContoh
"*", "", atau dihilangkanCocokkan semuadijalankan pada setiap kemunculan event
Hanya huruf, digit, _, dan |String yang tepat, atau daftar string yang tepat dipisahkan |Bash cocok hanya dengan tool Bash; Edit|Write cocok dengan salah satu tool dengan tepat
Berisi karakter lain apa punEkspresi reguler JavaScript^Notebook cocok dengan tool apa pun yang dimulai dengan Notebook; mcp__memory__.* cocok dengan setiap tool dari server memory
Event FileChanged tidak mengikuti aturan ini saat membangun daftar watch-nya. Lihat FileChanged. Setiap tipe event cocok pada bidang yang berbeda:
EventApa yang difilter matcherContoh nilai matcher
PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDeniednama toolBash, Edit|Write, mcp__.*
SessionStartbagaimana sesi dimulaistartup, resume, clear, compact
Setupflag CLI mana yang memicu setupinit, maintenance
SessionEndmengapa sesi berakhirclear, resume, logout, prompt_input_exit, bypass_permissions_disabled, other
Notificationtipe notifikasipermission_prompt, idle_prompt, auth_success, elicitation_dialog, elicitation_complete, elicitation_response
SubagentStarttipe agentgeneral-purpose, Explore, Plan, atau nama agent kustom
PreCompact, PostCompactapa yang memicu compactionmanual, auto
SubagentStoptipe agentnilai yang sama seperti SubagentStart
ConfigChangesumber konfigurasiuser_settings, project_settings, local_settings, policy_settings, skills
CwdChangedtidak ada dukungan matcherselalu dijalankan pada setiap perubahan direktori
FileChangednama file literal untuk ditonton (lihat FileChanged).envrc|.env
StopFailuretipe kesalahanrate_limit, authentication_failed, oauth_org_not_allowed, billing_error, invalid_request, server_error, max_output_tokens, unknown
InstructionsLoadedalasan loadsession_start, nested_traversal, path_glob_match, include, compact
UserPromptExpansionnama commandnama skill atau command Anda
Elicitationnama server MCPnama server MCP yang dikonfigurasi Anda
ElicitationResultnama server MCPnilai yang sama seperti Elicitation
UserPromptSubmit, PostToolBatch, Stop, TeammateIdle, TaskCreated, TaskCompleted, WorktreeCreate, WorktreeRemovetidak ada dukungan matcherselalu dijalankan pada setiap kemunculan
Matcher dijalankan terhadap bidang dari JSON input yang Claude Code kirimkan ke hook Anda di stdin. Untuk tool events, bidang itu adalah tool_name. Setiap bagian hook event mencantumkan set lengkap nilai matcher dan skema input untuk event itu. Contoh ini menjalankan skrip linting hanya ketika Claude menulis atau mengedit file:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/lint-check.sh"
          }
        ]
      }
    ]
  }
}
UserPromptSubmit, PostToolBatch, Stop, TeammateIdle, TaskCreated, TaskCompleted, WorktreeCreate, WorktreeRemove, dan CwdChanged tidak mendukung matchers dan selalu dijalankan pada setiap kemunculan. Jika Anda menambahkan bidang matcher ke event ini, itu akan diabaikan secara diam-diam. Untuk tool events, Anda dapat memfilter lebih sempit dengan menetapkan bidang if pada handler hook individual. if menggunakan sintaks aturan izin untuk mencocokkan terhadap nama tool dan argumen bersama-sama, jadi "Bash(git *)" dijalankan ketika subperintah apa pun dari input Bash cocok dengan git * dan "Edit(*.ts)" dijalankan hanya untuk file TypeScript.

Cocokkan MCP tools

Tool server MCP muncul sebagai tool reguler dalam tool events (PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied), jadi Anda dapat mencocokkannya dengan cara yang sama seperti Anda mencocokkan nama tool lainnya. MCP tools mengikuti pola penamaan mcp__<server>__<tool>, misalnya:
  • mcp__memory__create_entities: tool create entities dari Memory server
  • mcp__filesystem__read_file: tool read file dari Filesystem server
  • mcp__github__search_repositories: tool search dari GitHub server
Untuk mencocokkan setiap tool dari server, tambahkan .* ke awalan server. .* diperlukan: matcher seperti mcp__memory hanya berisi huruf dan underscore, jadi dibandingkan sebagai string yang tepat dan tidak cocok dengan tool apa pun.
  • mcp__memory__.* cocok dengan semua tools dari server memory
  • mcp__.*__write.* cocok dengan tool apa pun yang namanya dimulai dengan write dari server apa pun
Contoh ini mencatat semua operasi memory server dan memvalidasi operasi write dari server MCP apa pun:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__memory__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
          }
        ]
      },
      {
        "matcher": "mcp__.*__write.*",
        "hooks": [
          {
            "type": "command",
            "command": "/home/user/scripts/validate-mcp-write.py"
          }
        ]
      }
    ]
  }
}

Bidang hook handler

Setiap objek dalam array hooks inner adalah hook handler: perintah shell, endpoint HTTP, tool MCP, prompt LLM, atau agent yang dijalankan saat matcher cocok. Ada lima tipe:
  • Command hooks (type: "command"): jalankan perintah shell. Skrip Anda menerima JSON input event di stdin dan mengkomunikasikan hasil kembali melalui kode keluar dan stdout.
  • HTTP hooks (type: "http"): kirimkan JSON input event sebagai permintaan HTTP POST ke URL. Endpoint mengkomunikasikan hasil kembali melalui badan respons menggunakan format JSON output yang sama seperti command hooks.
  • MCP tool hooks (type: "mcp_tool"): panggil tool pada MCP server yang sudah terhubung. Output teks tool diperlakukan seperti command-hook stdout.
  • Prompt hooks (type: "prompt"): kirimkan prompt ke model Claude untuk evaluasi single-turn. Model mengembalikan keputusan yes/no sebagai JSON. Lihat Prompt-based hooks.
  • Agent hooks (type: "agent"): spawn subagent yang dapat menggunakan tools seperti Read, Grep, dan Glob untuk memverifikasi kondisi sebelum mengembalikan keputusan. Agent hooks adalah eksperimental dan mungkin berubah. Lihat Agent-based hooks.

Bidang umum

Bidang-bidang ini berlaku untuk semua tipe hook:
BidangDiperlukanDeskripsi
typeya"command", "http", "mcp_tool", "prompt", atau "agent"
iftidakSintaks aturan izin untuk memfilter kapan hook ini dijalankan, seperti "Bash(git *)" atau "Edit(*.ts)". Hook hanya spawn jika pemanggilan tool cocok dengan pola, atau jika perintah Bash terlalu kompleks untuk diurai. Hanya dievaluasi pada tool events: PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, dan PermissionDenied. Pada event lain, hook dengan if yang ditetapkan tidak akan pernah dijalankan. Menggunakan sintaks yang sama seperti aturan izin
timeouttidakDetik sebelum membatalkan. Default: 600 untuk command, http, dan mcp_tool; 30 untuk prompt; 60 untuk agent. UserPromptSubmit menurunkan default command, http, dan mcp_tool menjadi 30
statusMessagetidakPesan spinner kustom ditampilkan saat hook dijalankan
oncetidakJika true, dijalankan hanya sekali per sesi kemudian dihapus. Hanya dihormati untuk hooks yang dideklarasikan dalam skill frontmatter; diabaikan dalam file pengaturan dan agent frontmatter
Bidang if menyimpan tepat satu aturan izin. Tidak ada sintaks &&, ||, atau list untuk menggabungkan aturan; untuk menerapkan beberapa kondisi, tentukan handler hook terpisah untuk masing-masing. Untuk Bash, aturan dicocokkan terhadap setiap subperintah dari input tool setelah penugasan VAR=value terkemuka dihapus, jadi if: "Bash(git push *)" cocok dengan FOO=bar git push dan npm test && git push. Hook dijalankan jika ada subperintah yang cocok, dan selalu dijalankan ketika perintah terlalu kompleks untuk diurai.

Bidang command hook

Selain bidang umum, command hooks menerima bidang-bidang ini:
BidangDiperlukanDeskripsi
commandyaPerintah shell untuk dijalankan. Dengan args, executable untuk spawn secara langsung. Lihat Exec form dan shell form
argstidakDaftar argumen. Ketika ada, command diselesaikan sebagai executable dan di-spawn secara langsung dengan args sebagai vektor argumen, tanpa shell yang terlibat. Lihat Exec form dan shell form
asynctidakJika true, dijalankan di latar belakang tanpa memblokir. Lihat Run hooks in the background
asyncRewaketidakJika true, dijalankan di latar belakang dan membangunkan Claude pada kode keluar 2. Menyiratkan async. stderr hook, atau stdout jika stderr kosong, ditampilkan ke Claude sebagai pengingat sistem sehingga dapat bereaksi terhadap kegagalan latar belakang yang berjalan lama
shelltidakShell untuk digunakan untuk hook ini. Menerima "bash" (default) atau "powershell". Menetapkan "powershell" menjalankan perintah melalui PowerShell di Windows. Tidak memerlukan CLAUDE_CODE_USE_POWERSHELL_TOOL karena hooks spawn PowerShell secara langsung. Diabaikan ketika args diatur
Exec form dan shell form
Hook command dijalankan sebagai exec form ketika args diatur, dan shell form ketika args dihilangkan. Atur args setiap kali hook mereferensikan path placeholder, karena setiap elemen dilewatkan sebagai satu argumen tanpa quoting. Hilangkan args ketika Anda memerlukan fitur shell seperti pipes atau &&, atau ketika tidak ada kekhawatiran yang berlaku. Exec form dijalankan ketika args ada. Claude Code menyelesaikan command sebagai executable di PATH dan spawn-nya secara langsung dengan args sebagai vektor argumen. Tidak ada shell, jadi setiap elemen args adalah satu argumen persis seperti yang ditulis, dan path placeholders seperti ${CLAUDE_PLUGIN_ROOT} disubstitusi ke dalam command dan ke dalam setiap elemen args sebagai string biasa. Karakter khusus seperti apostrophe, $, dan backticks melewati verbatim karena tidak ada shell untuk menginterpretasinya. Tidak ada tokenisasi shell yang terjadi di platform apa pun. Shell form dijalankan ketika args tidak ada. String command dilewatkan ke shell: sh -c di macOS dan Linux, Git Bash di Windows, atau PowerShell ketika Git Bash tidak diinstal. Atur bidang shell untuk memilih secara eksplisit. Shell melakukan tokenisasi string, memperluas variabel, dan menginterpretasi pipes, &&, redirects, dan globs.
Di Windows, exec form memerlukan command untuk diselesaikan ke executable nyata seperti .exe. Shim .cmd dan .bat yang npm, npx, eslint, dan tools lainnya instal di node_modules/.bin bukan executables dan tidak dapat di-spawn tanpa shell. Untuk menjalankannya dalam exec form, panggil skrip yang mendasar dengan node secara langsung, misalnya "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/node_modules/eslint/bin/eslint.js"]. Pola node plus script-path bekerja di setiap platform karena node.exe adalah binary nyata. Untuk menjalankan shim .cmd atau .bat berdasarkan nama, gunakan shell form.
Contoh ini menjalankan skrip Node yang dibundel dengan plugin. Exec form melewatkan path skrip yang diselesaikan sebagai satu argumen tanpa quoting:
{
  "type": "command",
  "command": "node",
  "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/format.js", "--fix"]
}
Shell form yang setara memerlukan quoting untuk menangani paths dengan spasi atau karakter khusus:
{
  "type": "command",
  "command": "node \"${CLAUDE_PLUGIN_ROOT}\"/scripts/format.js --fix"
}
Kedua form mendukung path placeholders yang sama, dan keduanya mengekspornya sebagai variabel lingkungan CLAUDE_PROJECT_DIR, CLAUDE_PLUGIN_ROOT, dan CLAUDE_PLUGIN_DATA pada proses yang di-spawn, jadi skrip dapat membaca process.env.CLAUDE_PLUGIN_ROOT terlepas dari bagaimana itu diluncurkan. Plugin hooks juga mensubstitusi nilai ${user_config.*}; lihat User configuration.
Dalam exec form, command adalah nama executable atau path saja. Jika command adalah nama bare tanpa path separator dan berisi whitespace bersama args, Claude Code mencatat warning karena spawn akan gagal: tidak ada executable bernama node script.js. Pindahkan token ekstra ke dalam args. Path absolut dengan spasi, seperti C:\Program Files\nodejs\node.exe, adalah executable tunggal yang valid dan tidak memicu warning.

Bidang HTTP hook

Selain bidang umum, HTTP hooks menerima bidang-bidang ini:
BidangDiperlukanDeskripsi
urlyaURL untuk mengirimkan permintaan POST ke
headerstidakHeader HTTP tambahan sebagai pasangan kunci-nilai. Nilai mendukung interpolasi variabel lingkungan menggunakan sintaks $VAR_NAME atau ${VAR_NAME}. Hanya variabel yang tercantum dalam allowedEnvVars yang diselesaikan
allowedEnvVarstidakDaftar nama variabel lingkungan yang dapat diinterpolasi ke nilai header. Referensi ke variabel yang tidak tercantum diganti dengan string kosong. Diperlukan untuk interpolasi variabel env apa pun untuk bekerja
Claude Code mengirimkan JSON input hook sebagai badan permintaan POST dengan Content-Type: application/json. Badan respons menggunakan format JSON output yang sama seperti command hooks. Penanganan kesalahan berbeda dari command hooks: respons non-2xx, kegagalan koneksi, dan timeout semuanya menghasilkan kesalahan non-blocking yang memungkinkan eksekusi berlanjut. Untuk memblokir pemanggilan tool atau menolak izin, kembalikan respons 2xx dengan badan JSON yang berisi decision: "block" atau hookSpecificOutput dengan permissionDecision: "deny". Contoh ini mengirimkan event PreToolUse ke layanan validasi lokal, mengautentikasi dengan token dari variabel lingkungan MY_TOKEN:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "http",
            "url": "http://localhost:8080/hooks/pre-tool-use",
            "timeout": 30,
            "headers": {
              "Authorization": "Bearer $MY_TOKEN"
            },
            "allowedEnvVars": ["MY_TOKEN"]
          }
        ]
      }
    ]
  }
}

Bidang MCP tool hook

Selain bidang umum, MCP tool hooks menerima bidang-bidang ini:
BidangDiperlukanDeskripsi
serveryaNama server MCP yang dikonfigurasi. Server harus sudah terhubung; hook tidak pernah memicu alur OAuth atau koneksi
toolyaNama tool untuk dipanggil di server itu
inputtidakArgumen yang dilewatkan ke tool. Nilai string mendukung substitusi ${path} dari JSON input hook, seperti "${tool_input.file_path}"
Output teks tool diperlakukan seperti command-hook stdout: jika itu diurai sebagai JSON output yang valid, itu diproses sebagai keputusan, jika tidak, itu ditampilkan sebagai teks biasa. Jika server bernama tidak terhubung, atau tool mengembalikan isError: true, hook menghasilkan kesalahan non-blocking dan eksekusi berlanjut. MCP tool hooks tersedia pada setiap hook event setelah Claude Code terhubung ke server MCP Anda. SessionStart dan Setup biasanya dijalankan sebelum server selesai terhubung, jadi hooks pada event tersebut harus mengharapkan kesalahan “not connected” pada run pertama. Contoh ini memanggil tool security_scan pada server MCP my_server setelah setiap Write atau Edit, melewatkan path file yang diedit:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "my_server",
            "tool": "security_scan",
            "input": { "file_path": "${tool_input.file_path}" }
          }
        ]
      }
    ]
  }
}

Bidang prompt dan agent hook

Selain bidang umum, prompt dan agent hooks menerima bidang-bidang ini:
BidangDiperlukanDeskripsi
promptyaTeks prompt untuk dikirim ke model. Gunakan $ARGUMENTS sebagai placeholder untuk JSON input hook
modeltidakModel untuk digunakan untuk evaluasi. Default ke model cepat
Semua matching hooks dijalankan secara paralel, dan handler identik dideduplikasi secara otomatis. Command hooks dideduplikasi berdasarkan string perintah dan args, dan HTTP hooks dideduplikasi berdasarkan URL. Handlers dijalankan di direktori saat ini dengan lingkungan Claude Code. Variabel lingkungan $CLAUDE_CODE_REMOTE diatur ke "true" di lingkungan web jarak jauh dan tidak diatur di CLI lokal.

Referensi skrip berdasarkan path

Gunakan placeholders ini untuk mereferensikan skrip hook relatif terhadap akar proyek atau plugin, terlepas dari direktori kerja saat hook dijalankan:
  • ${CLAUDE_PROJECT_DIR}: akar proyek.
  • ${CLAUDE_PLUGIN_ROOT}: direktori instalasi plugin, untuk skrip yang dibundel dengan plugin. Berubah pada setiap pembaruan plugin.
  • ${CLAUDE_PLUGIN_DATA}: direktori data persisten plugin, untuk dependensi dan status yang harus bertahan pembaruan plugin.
Lebih suka exec form untuk hook apa pun yang mereferensikan path placeholder. Exec form melewatkan setiap elemen args sebagai satu argumen tanpa tokenisasi shell, jadi paths dengan spasi atau karakter khusus tidak memerlukan quoting. Dalam shell form, bungkus setiap placeholder dalam tanda kutip ganda.
Contoh ini menggunakan ${CLAUDE_PROJECT_DIR} untuk menjalankan pemeriksa gaya dari direktori .claude/hooks/ proyek setelah pemanggilan tool Write atau Edit apa pun:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.sh",
            "args": []
          }
        ]
      }
    ]
  }
}

Hooks dalam skills dan agents

Selain file pengaturan dan plugin, hooks dapat didefinisikan langsung dalam skills dan subagents menggunakan frontmatter. Hooks ini dibatasi pada siklus hidup komponen dan hanya dijalankan ketika komponen itu aktif. Semua hook events didukung. Untuk subagents, Stop hooks secara otomatis dikonversi ke SubagentStop karena itu adalah event yang dijalankan ketika subagent selesai. Hooks menggunakan format konfigurasi yang sama seperti hooks berbasis pengaturan tetapi dibatasi pada masa hidup komponen dan dibersihkan saat selesai. Skill ini mendefinisikan hook PreToolUse yang menjalankan skrip validasi keamanan sebelum setiap perintah Bash:
---
name: secure-operations
description: Perform operations with security checks
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/security-check.sh"
---
Agents menggunakan format yang sama dalam frontmatter YAML mereka. Ketik /hooks di Claude Code untuk membuka browser hooks read-only. Menu menampilkan setiap hook event dengan jumlah hooks yang dikonfigurasi, memungkinkan Anda menggali ke dalam matchers, dan menampilkan detail lengkap setiap hook handler. Gunakan untuk memverifikasi konfigurasi, memeriksa file pengaturan mana hook berasal, atau memeriksa perintah, prompt, atau URL hook. Menu menampilkan semua lima tipe hook: command, prompt, agent, http, dan mcp_tool. Setiap hook diberi label dengan awalan [type] dan sumber menunjukkan di mana itu didefinisikan:
  • User: dari ~/.claude/settings.json
  • Project: dari .claude/settings.json
  • Local: dari .claude/settings.local.json
  • Plugin: dari hooks/hooks.json plugin
  • Session: terdaftar dalam memori untuk sesi saat ini
  • Built-in: terdaftar secara internal oleh Claude Code
Memilih hook membuka tampilan detail menampilkan event, matcher, tipe, file sumber, dan perintah lengkap, prompt, atau URL. Menu adalah read-only: untuk menambah, memodifikasi, atau menghapus hooks, edit JSON pengaturan secara langsung atau minta Claude membuat perubahan.

Nonaktifkan atau hapus hooks

Untuk menghapus hook, hapus entrinya dari file JSON pengaturan. Untuk menonaktifkan semua hooks sementara tanpa menghapusnya, atur "disableAllHooks": true dalam file pengaturan Anda. Tidak ada cara untuk menonaktifkan hook individual sambil menyimpannya dalam konfigurasi. Pengaturan disableAllHooks menghormati hierarki pengaturan terkelola. Jika administrator telah mengonfigurasi hooks melalui pengaturan kebijakan terkelola, disableAllHooks yang diatur dalam pengaturan pengguna, proyek, atau lokal tidak dapat menonaktifkan hooks terkelola tersebut. Hanya disableAllHooks yang diatur pada tingkat pengaturan terkelola yang dapat menonaktifkan hooks terkelola. Pengeditan langsung ke hooks dalam file pengaturan biasanya diambil secara otomatis oleh file watcher.

Input dan output hook

Command hooks menerima data JSON melalui stdin dan mengkomunikasikan hasil melalui kode keluar, stdout, dan stderr. HTTP hooks menerima JSON yang sama sebagai badan permintaan POST dan mengkomunikasikan hasil melalui badan respons HTTP. Bagian ini mencakup bidang dan perilaku yang umum untuk semua events. Setiap bagian event di bawah Hook events mencakup skema input spesifiknya dan opsi kontrol keputusan. Pada macOS dan Linux, command hooks berjalan dalam sesi mereka sendiri tanpa terminal pengontrol sejak v2.1.139. Proses hook dan proses anak apa pun tidak dapat membuka /dev/tty atau mengirim urutan escape langsung ke antarmuka Claude Code. Windows tidak memiliki /dev/tty. Untuk menampilkan pesan kepada pengguna di platform apa pun, kembalikan systemMessage dalam output JSON. Untuk memicu notifikasi desktop, atur judul jendela, atau bunyikan bel, kembalikan terminalSequence sebagai gantinya.

Bidang input umum

Hook events menerima bidang-bidang ini sebagai JSON, selain bidang spesifik event yang didokumentasikan dalam setiap bagian hook event. Untuk command hooks, JSON ini tiba melalui stdin. Untuk HTTP hooks, itu tiba sebagai badan permintaan POST.
BidangDeskripsi
session_idPengenal sesi saat ini
transcript_pathPath ke JSON percakapan
cwdDirektori kerja saat hook dipanggil
permission_modeMode izin saat ini: "default", "plan", "acceptEdits", "auto", "dontAsk", atau "bypassPermissions". Tidak semua events menerima bidang ini: lihat contoh JSON setiap event di bawah untuk memeriksa
effortObjek dengan bidang level yang menyimpan tingkat effort aktif untuk giliran: "low", "medium", "high", "xhigh", atau "max". Jika effort yang diminta melebihi apa yang didukung model saat ini, ini adalah tingkat yang diturunkan yang sebenarnya digunakan model, bukan tingkat yang Anda minta. Objek cocok dengan bidang effort status line. Hadir untuk events yang dijalankan dalam konteks penggunaan tool, seperti PreToolUse, PostToolUse, Stop, dan SubagentStop, ketika model saat ini mendukung parameter effort. Tingkat juga tersedia untuk perintah hook dan tool Bash sebagai variabel lingkungan $CLAUDE_EFFORT.
hook_event_nameNama event yang dijalankan
Saat berjalan dengan --agent atau di dalam subagent, dua bidang tambahan disertakan:
BidangDeskripsi
agent_idPengenal unik untuk subagent. Hadir hanya ketika hook dijalankan di dalam pemanggilan subagent. Gunakan ini untuk membedakan pemanggilan hook subagent dari pemanggilan thread utama.
agent_typeNama agent (misalnya, "Explore" atau "security-reviewer"). Hadir ketika sesi menggunakan --agent atau hook dijalankan di dalam subagent. Untuk subagents, tipe subagent mengambil alih nilai --agent sesi. Untuk custom subagents, ini adalah bidang name dari frontmatter agent, bukan nama file.
Hanya hooks SessionStart yang menerima bidang model. Tidak ada variabel lingkungan $CLAUDE_MODEL. Proses hook mewarisi lingkungan induk, jadi dapat membaca $ANTHROPIC_MODEL jika Anda menetapkannya di shell Anda, tetapi nilai itu tidak berubah ketika Anda beralih model dengan /model selama sesi. Misalnya, hook PreToolUse untuk perintah Bash menerima ini di stdin:
{
  "session_id": "abc123",
  "transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
  "cwd": "/home/user/my-project",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test"
  }
}
Bidang tool_name dan tool_input spesifik untuk event. Setiap bagian hook event mendokumentasikan bidang tambahan untuk event itu.

Output kode keluar

Kode keluar dari perintah hook Anda memberitahu Claude Code apakah tindakan harus dilanjutkan, diblokir, atau diabaikan. Exit 0 berarti sukses. Claude Code mengurai stdout untuk bidang output JSON. Output JSON hanya diproses pada exit 0. Untuk sebagian besar events, stdout ditulis ke debug log tetapi tidak ditampilkan dalam transkrip. Pengecualiannya adalah UserPromptSubmit, UserPromptExpansion, dan SessionStart, di mana stdout ditambahkan sebagai konteks yang dapat dilihat dan ditindaklanjuti Claude. Exit 2 berarti kesalahan blocking. Claude Code mengabaikan stdout dan JSON apa pun di dalamnya. Sebagai gantinya, teks stderr diumpankan kembali ke Claude sebagai pesan kesalahan. Efeknya tergantung pada event: PreToolUse memblokir pemanggilan tool, UserPromptSubmit menolak prompt, dan sebagainya. Lihat perilaku kode keluar 2 untuk daftar lengkap. Kode keluar lainnya adalah kesalahan non-blocking untuk sebagian besar hook events. Transkrip menampilkan pemberitahuan <hook name> hook error diikuti oleh baris pertama stderr, jadi Anda dapat mengidentifikasi penyebabnya tanpa --debug. Eksekusi berlanjut dan stderr lengkap ditulis ke debug log. Misalnya, skrip perintah hook yang memblokir perintah Bash berbahaya:
#!/bin/bash
# Membaca input JSON dari stdin, memeriksa perintah
command=$(jq -r '.tool_input.command' < /dev/stdin)

if [[ "$command" == rm* ]]; then
  echo "Blocked: rm commands are not allowed" >&2
  exit 2  # Blocking error: tool call is prevented
fi

exit 0  # Success: tool call proceeds
Untuk sebagian besar hook events, hanya kode keluar 2 yang memblokir tindakan. Claude Code memperlakukan kode keluar 1 sebagai kesalahan non-blocking dan melanjutkan dengan tindakan, meskipun 1 adalah kode kegagalan Unix konvensional. Jika hook Anda dimaksudkan untuk menegakkan kebijakan, gunakan exit 2. Pengecualiannya adalah WorktreeCreate, di mana kode keluar non-zero apa pun membatalkan pembuatan worktree.

Perilaku kode keluar 2 per event

Kode keluar 2 adalah cara hook menandakan “berhenti, jangan lakukan ini.” Efeknya tergantung pada event, karena beberapa event mewakili tindakan yang dapat diblokir (seperti pemanggilan tool yang belum terjadi) dan yang lain mewakili hal-hal yang sudah terjadi atau tidak dapat dicegah.
Hook eventDapat diblokir?Apa yang terjadi pada exit 2
PreToolUseYaMemblokir pemanggilan tool
PermissionRequestYaMenolak izin
UserPromptSubmitYaMemblokir pemrosesan prompt dan menghapus prompt
UserPromptExpansionYaMemblokir ekspansi
StopYaMencegah Claude berhenti, melanjutkan percakapan
SubagentStopYaMencegah subagent berhenti
TeammateIdleYaMencegah teammate menjadi idle (teammate terus bekerja)
TaskCreatedYaMembatalkan pembuatan tugas
TaskCompletedYaMencegah tugas ditandai sebagai selesai
ConfigChangeYaMemblokir perubahan konfigurasi dari berlaku (kecuali policy_settings)
StopFailureTidakOutput dan kode keluar diabaikan
PostToolUseTidakMenampilkan stderr ke Claude (tool sudah dijalankan)
PostToolUseFailureTidakMenampilkan stderr ke Claude (tool sudah gagal)
PostToolBatchYaMenghentikan loop agentic sebelum pemanggilan model berikutnya
PermissionDeniedTidakKode keluar dan stderr diabaikan (penolakan sudah terjadi). Gunakan JSON hookSpecificOutput.retry: true untuk memberitahu model itu dapat mencoba lagi
NotificationTidakMenampilkan stderr ke pengguna saja
SubagentStartTidakMenampilkan stderr ke pengguna saja
SessionStartTidakMenampilkan stderr ke pengguna saja
SetupTidakMenampilkan stderr ke pengguna saja
SessionEndTidakMenampilkan stderr ke pengguna saja
CwdChangedTidakMenampilkan stderr ke pengguna saja
FileChangedTidakMenampilkan stderr ke pengguna saja
PreCompactYaMemblokir compaction
PostCompactTidakMenampilkan stderr ke pengguna saja
ElicitationYaMenolak elicitation
ElicitationResultYaMemblokir respons (tindakan menjadi decline)
WorktreeCreateYaKode keluar non-zero apa pun menyebabkan pembuatan worktree gagal
WorktreeRemoveTidakKegagalan dicatat dalam mode debug saja
InstructionsLoadedTidakKode keluar diabaikan

Penanganan respons HTTP

HTTP hooks menggunakan kode status HTTP dan badan respons sebagai pengganti kode keluar dan stdout:
  • 2xx dengan badan kosong: sukses, setara dengan kode keluar 0 tanpa output
  • 2xx dengan badan teks biasa: sukses, teks ditambahkan sebagai konteks
  • 2xx dengan badan JSON: sukses, diurai menggunakan skema JSON output yang sama seperti command hooks
  • Status non-2xx: kesalahan non-blocking, eksekusi berlanjut
  • Kegagalan koneksi atau timeout: kesalahan non-blocking, eksekusi berlanjut
Tidak seperti command hooks, HTTP hooks tidak dapat menandakan kesalahan blocking hanya melalui kode status. Untuk memblokir pemanggilan tool atau menolak izin, kembalikan respons 2xx dengan badan JSON yang berisi bidang keputusan yang sesuai.

Output JSON

Kode keluar memungkinkan Anda mengizinkan atau memblokir, tetapi output JSON memberikan kontrol yang lebih halus. Alih-alih keluar dengan kode 2 untuk memblokir, keluar 0 dan cetak objek JSON ke stdout. Claude Code membaca bidang tertentu dari JSON itu untuk mengontrol perilaku, termasuk decision control untuk memblokir, mengizinkan, atau meningkatkan ke pengguna.
Anda harus memilih satu pendekatan per hook, bukan keduanya: gunakan kode keluar saja untuk signaling, atau keluar 0 dan cetak JSON untuk kontrol terstruktur. Claude Code hanya memproses JSON pada exit 0. Jika Anda keluar 2, JSON apa pun diabaikan.
Stdout hook Anda harus berisi hanya objek JSON. Jika profil shell Anda mencetak teks saat startup, itu dapat mengganggu parsing JSON. Lihat JSON validation failed dalam panduan troubleshooting. String output hook, termasuk additionalContext, systemMessage, dan plain stdout, dibatasi pada 10.000 karakter. Output yang melebihi batas ini disimpan ke file dan diganti dengan pratinjau dan path file, dengan cara yang sama seperti hasil tool besar ditangani. Objek JSON mendukung tiga jenis bidang:
  • Bidang universal seperti continue bekerja di semua events. Ini tercantum dalam tabel di bawah.
  • Top-level decision dan reason digunakan oleh beberapa events untuk memblokir atau memberikan umpan balik.
  • hookSpecificOutput adalah objek bersarang untuk events yang memerlukan kontrol yang lebih kaya. Ini memerlukan bidang hookEventName yang diatur ke nama event.
BidangDefaultDeskripsi
continuetrueJika false, Claude berhenti memproses sepenuhnya setelah hook dijalankan. Mengambil alih bidang keputusan spesifik event apa pun
stopReasontidak adaPesan ditampilkan ke pengguna saat continue adalah false. Tidak ditampilkan ke Claude
suppressOutputfalseJika true, menyembunyikan stdout dari debug log
systemMessagetidak adaPesan peringatan ditampilkan ke pengguna
terminalSequencetidak adaUrutan escape terminal untuk Claude Code yang akan dipancarkan atas nama Anda, seperti notifikasi desktop, judul jendela, atau bel. Dibatasi pada OSC 0/1/2/9/99/777 dan BEL. Jika nilai berisi apa pun di luar daftar putih, bidang diabaikan. Gunakan ini alih-alih menulis ke /dev/tty, yang tidak tersedia untuk hooks
Untuk menghentikan Claude sepenuhnya terlepas dari tipe event:
{ "continue": false, "stopReason": "Build failed, fix errors before continuing" }

Emit terminal notifications

Bidang terminalSequence memerlukan Claude Code v2.1.141 atau lebih baru. Hooks berjalan tanpa terminal pengontrol, jadi menulis urutan escape langsung ke /dev/tty gagal. Sebagai gantinya, kembalikan urutan escape dalam bidang terminalSequence dan Claude Code memancarkannya untuk Anda melalui jalur penulisan terminal miliknya sendiri. Ini bebas race, bekerja di dalam tmux dan GNU screen, dan bekerja di Windows di mana tidak ada /dev/tty. Bidang menerima string dari satu atau lebih urutan escape yang diizinkan:
  • OSC 0, 1, 2: judul jendela dan ikon
  • OSC 9: notifikasi iTerm2, ConEmu, Windows Terminal, dan WezTerm, termasuk 9;4 kemajuan taskbar
  • OSC 99: notifikasi Kitty
  • OSC 777: notifikasi urxvt, Ghostty, dan Warp
  • BEL telanjang
Urutan dapat diakhiri dengan BEL atau dengan ST. Apa pun di luar daftar putih, termasuk urutan kursor dan warna CSI, urutan palet OSC, hyperlink OSC 8, penulisan clipboard OSC 52, dan OSC 1337, ditolak dan bidang diabaikan. Contoh di bawah menjalankan notifikasi desktop dari hook Notification. Urutan escape dibangun dengan printf octal escapes sehingga byte kontrol tidak pernah muncul di baris perintah shell, dan jq -n --arg membangun output JSON sehingga tanda kutip, backslash, dan newline dalam pesan notifikasi diloloskan dengan benar:
#!/bin/bash
# Notification hook: ping desktop ketika Claude Code membutuhkan perhatian.
input=$(cat)
title="Claude Code'
body=$(jq -r '.message // 'Needs your attention"' <<<"$input")
seq=$(printf '\033]777;notify;%s;%s\007' "$title" "$body")
jq -nc --arg seq "$seq" '{terminalSequence: $seq}'
Bentuk { "terminalSequence": "..." } sama dari shell atau bahasa apa pun. Di Windows, bangun string escape di PowerShell atau skrip dan pancarkan objek JSON yang sama.
terminalSequence adalah pengganti yang didukung untuk hooks yang sebelumnya menulis urutan escape langsung ke /dev/tty. Daftar putih dibatasi pada urutan yang tidak dapat memindahkan kursor atau mengubah warna, jadi hook tidak pernah dapat merusak prompt di layar.

Tambahkan konteks untuk Claude

Bidang additionalContext meneruskan string dari hook Anda ke jendela konteks Claude. Claude Code membungkus string dalam pengingat sistem dan menyisipkannya ke dalam percakapan pada titik di mana hook dijalankan. Claude membaca pengingat pada permintaan model berikutnya, tetapi itu tidak muncul sebagai pesan chat dalam antarmuka. Kembalikan additionalContext di dalam hookSpecificOutput bersama nama event:
{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "This file is generated. Edit src/schema.ts and run `bun generate` instead."
  }
}
Di mana pengingat muncul tergantung pada event: Ketika beberapa hooks mengembalikan additionalContext untuk event yang sama, Claude menerima semua nilai. Jika nilai melebihi 10.000 karakter, Claude Code menulis teks lengkap ke file di direktori sesi dan meneruskan Claude path file dengan pratinjau singkat sebagai gantinya. Gunakan additionalContext untuk informasi yang harus diketahui Claude tentang keadaan saat ini lingkungan Anda atau operasi yang baru saja dijalankan:
  • Keadaan lingkungan: branch saat ini, target deployment, atau flag fitur aktif
  • Aturan proyek bersyarat: perintah test mana yang berlaku untuk file yang baru diedit, direktori mana yang read-only di worktree ini
  • Data eksternal: masalah terbuka yang ditugaskan kepada Anda, hasil CI terbaru, konten yang diambil dari layanan internal
Untuk instruksi yang tidak pernah berubah, lebih suka CLAUDE.md. Itu dimuat tanpa menjalankan skrip dan merupakan tempat standar untuk konvensi proyek statis. Tulis teks sebagai pernyataan faktual daripada instruksi sistem imperatif. Frasa seperti “Target deployment adalah production” atau “Repo ini menggunakan bun test” dibaca sebagai informasi proyek. Teks yang dibingkai sebagai perintah sistem out-of-band dapat memicu pertahanan injeksi prompt Claude, yang menyebabkan Claude menampilkan teks kepada Anda alih-alih memperlakukannya sebagai konteks. Setelah disuntikkan, teks disimpan dalam transkrip sesi. Untuk events mid-session seperti PostToolUse atau UserPromptSubmit, melanjutkan dengan --continue atau --resume memutar ulang teks yang disimpan daripada menjalankan kembali hook untuk giliran masa lalu, jadi nilai seperti timestamp atau commit SHA menjadi usang pada resume. Hook SessionStart dijalankan lagi pada resume dengan source diatur ke "resume", jadi mereka dapat menyegarkan konteks mereka.

Kontrol keputusan

Tidak setiap event mendukung pemblokiran atau kontrol perilaku melalui JSON. Events yang melakukannya masing-masing menggunakan set bidang yang berbeda untuk mengekspresikan keputusan itu. Gunakan tabel ini sebagai referensi cepat sebelum menulis hook:
EventsPola keputusanBidang kunci
UserPromptSubmit, UserPromptExpansion, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop, ConfigChange, PreCompactTop-level decisiondecision: "block", reason
TeammateIdle, TaskCreated, TaskCompletedKode keluar atau continue: falseKode keluar 2 memblokir tindakan dengan umpan balik stderr. JSON {"continue": false, "stopReason": "..."} juga menghentikan teammate sepenuhnya, mencocokkan perilaku hook Stop
PreToolUsehookSpecificOutputpermissionDecision (allow/deny/ask/defer), permissionDecisionReason
PermissionRequesthookSpecificOutputdecision.behavior (allow/deny)
PermissionDeniedhookSpecificOutputretry: true memberitahu model itu dapat mencoba lagi pemanggilan tool yang ditolak
WorktreeCreatepath returnCommand hook mencetak path di stdout; HTTP hook mengembalikan hookSpecificOutput.worktreePath. Kegagalan hook atau path yang hilang gagal membuat
ElicitationhookSpecificOutputaction (accept/decline/cancel), content (nilai field form untuk accept)
ElicitationResulthookSpecificOutputaction (accept/decline/cancel), content (nilai field form override)
WorktreeRemove, Notification, SessionEnd, PostCompact, InstructionsLoaded, StopFailure, CwdChanged, FileChangedTidak adaTidak ada kontrol keputusan. Digunakan untuk efek samping seperti logging atau cleanup
Berikut adalah contoh setiap pola dalam aksi:
Digunakan oleh UserPromptSubmit, UserPromptExpansion, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop, ConfigChange, dan PreCompact. Satu-satunya nilai adalah "block". Untuk mengizinkan tindakan dilanjutkan, hilangkan decision dari JSON Anda, atau keluar 0 tanpa JSON apa pun:
{
  "decision": "block",
  "reason": "Test suite must pass before proceeding"
}
Untuk contoh yang diperluas termasuk validasi perintah Bash, pemfilteran prompt, dan skrip persetujuan otomatis, lihat What you can automate dalam panduan dan Bash command validator reference implementation.

Hook events

Setiap event sesuai dengan titik dalam siklus hidup Claude Code di mana hooks dapat dijalankan. Bagian-bagian di bawah diurutkan untuk mencocokkan siklus hidup: dari pengaturan sesi melalui loop agentic ke akhir sesi. Setiap bagian menjelaskan kapan event dijalankan, matcher apa yang didukungnya, JSON input yang diterima, dan cara mengontrol perilaku melalui output.

SessionStart

Dijalankan ketika Claude Code memulai sesi baru atau melanjutkan sesi yang ada. Berguna untuk memuat konteks pengembangan seperti masalah yang ada atau perubahan terbaru pada codebase Anda, atau menyiapkan variabel lingkungan. Untuk konteks statis yang tidak memerlukan skrip, gunakan CLAUDE.md sebagai gantinya. SessionStart dijalankan pada setiap sesi, jadi jaga hooks ini tetap cepat. Hanya hooks type: "command" dan type: "mcp_tool" yang didukung. Nilai matcher sesuai dengan cara sesi dimulai:
MatcherKapan dijalankan
startupSesi baru
resume--resume, --continue, atau /resume
clear/clear
compactCompaction otomatis atau manual

Input SessionStart

Selain bidang input umum, SessionStart hooks menerima source, model, dan secara opsional agent_type. Bidang source menunjukkan bagaimana sesi dimulai: "startup" untuk sesi baru, "resume" untuk sesi yang dilanjutkan, "clear" setelah /clear, atau "compact" setelah compaction. Bidang model berisi pengenal model. Jika Anda memulai Claude Code dengan claude --agent <name>, bidang agent_type berisi nama agent.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "SessionStart",
  "source": "startup",
  "model": "claude-sonnet-4-6"
}

Kontrol keputusan SessionStart

Teks apa pun yang dicetak skrip hook ke stdout ditambahkan sebagai konteks untuk Claude. Selain bidang output JSON yang tersedia untuk semua hooks, Anda dapat mengembalikan bidang spesifik event ini:
BidangDeskripsi
additionalContextString ditambahkan ke konteks Claude pada awal percakapan, sebelum prompt pertama. Lihat Tambahkan konteks untuk Claude untuk cara teks disampaikan dan apa yang harus dimasukkan
{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "Current branch: feat/auth-refactor\nUncommitted changes: src/auth.ts, src/login.tsx\nActive issue: #4211 Migrate to OAuth2"
  }
}
Karena plain stdout sudah mencapai Claude untuk event ini, hook yang hanya memuat konteks dapat mencetak ke stdout secara langsung tanpa membangun JSON. Gunakan bentuk JSON ketika Anda perlu menggabungkan konteks dengan bidang lain seperti suppressOutput.

Pertahankan variabel lingkungan

SessionStart hooks memiliki akses ke variabel lingkungan CLAUDE_ENV_FILE, yang menyediakan path file di mana Anda dapat mempertahankan variabel lingkungan untuk perintah Bash berikutnya. Untuk menetapkan variabel lingkungan individual, tulis pernyataan export ke CLAUDE_ENV_FILE. Gunakan append (>>) untuk mempertahankan variabel yang ditetapkan oleh hooks lain:
#!/bin/bash

if [ -n "$CLAUDE_ENV_FILE" ]; then
  echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
  echo 'export DEBUG_LOG=true' >> "$CLAUDE_ENV_FILE"
  echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
fi

exit 0
Untuk menangkap semua perubahan lingkungan dari perintah setup, bandingkan variabel yang diekspor sebelum dan sesudah:
#!/bin/bash

ENV_BEFORE=$(export -p | sort)

# Jalankan perintah setup Anda yang memodifikasi lingkungan
source ~/.nvm/nvm.sh
nvm use 20

if [ -n "$CLAUDE_ENV_FILE" ]; then
  ENV_AFTER=$(export -p | sort)
  comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
fi

exit 0
Variabel apa pun yang ditulis ke file ini akan tersedia dalam semua perintah Bash berikutnya yang dijalankan Claude Code selama sesi.
CLAUDE_ENV_FILE tersedia untuk SessionStart, Setup, CwdChanged, dan FileChanged hooks. Tipe hook lainnya tidak memiliki akses ke variabel ini.

Setup

Dijalankan hanya ketika Anda meluncurkan Claude Code dengan --init-only, atau dengan --init atau --maintenance dalam mode print (-p). Itu tidak dijalankan 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 sesuai dengan flag CLI yang memicu hook:
MatcherKapan dijalankan
initclaude --init-only atau claude -p --init
maintenanceclaude -p --maintenance
--init-only menjalankan Setup hooks dan SessionStart hooks dengan matcher startup, kemudian keluar tanpa memulai percakapan. --init dan --maintenance menjalankan Setup hooks hanya ketika digabungkan dengan -p (mode print); dalam sesi interaktif dua flag itu saat ini tidak menjalankan Setup hooks. Karena Setup tidak dijalankan pada setiap peluncuran, plugin yang memerlukan dependensi yang diinstal tidak dapat mengandalkan Setup saja. Pola praktis adalah memeriksa dependensi pada penggunaan pertama dan menginstal jika tidak ada, misalnya hook atau skill yang menguji ${CLAUDE_PLUGIN_DATA}/node_modules dan menjalankan npm install jika tidak ada. Lihat direktori data persisten untuk tempat menyimpan dependensi yang diinstal.

Input Setup

Selain bidang input umum, Setup hooks menerima bidang trigger yang diatur ke "init" atau "maintenance":
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "Setup",
  "trigger": "init"
}

Kontrol keputusan Setup

Setup hooks tidak dapat memblokir. Pada kode keluar 2, stderr ditampilkan kepada pengguna; pada kode keluar non-nol lainnya, stderr muncul hanya ketika Anda meluncurkan dengan --verbose. Dalam kedua kasus eksekusi berlanjut. Untuk meneruskan informasi ke konteks Claude, kembalikan additionalContext dalam output JSON; plain stdout ditulis ke debug log saja. Selain bidang output JSON yang tersedia untuk semua hooks, Anda dapat mengembalikan bidang spesifik event ini:
BidangDeskripsi
additionalContextString ditambahkan ke konteks Claude. Nilai dari beberapa hooks digabungkan
{
  "hookSpecificOutput": {
    "hookEventName": "Setup",
    "additionalContext": "Dependencies installed: node_modules, .venv"
  }
}
Setup hooks memiliki akses ke CLAUDE_ENV_FILE. Variabel yang ditulis ke file itu bertahan ke perintah Bash berikutnya untuk sesi, sama seperti dalam SessionStart hooks. Hanya hooks type: "command" dan type: "mcp_tool" yang didukung.

InstructionsLoaded

Dijalankan ketika file CLAUDE.md atau .claude/rules/*.md dimuat ke dalam konteks. Event ini dijalankan saat startup sesi untuk file yang dimuat dengan eager dan lagi nanti ketika file dimuat dengan lazy, misalnya ketika Claude mengakses subdirektori yang berisi CLAUDE.md bersarang atau ketika aturan bersyarat dengan frontmatter paths: cocok. Hook tidak mendukung pemblokiran atau kontrol keputusan. Itu dijalankan secara asinkron untuk tujuan observabilitas. Matcher dijalankan terhadap load_reason. Misalnya, gunakan "matcher": "session_start" untuk dijalankan hanya untuk file yang dimuat saat startup sesi, atau "matcher": "path_glob_match|nested_traversal" untuk dijalankan hanya untuk lazy loads.

Input InstructionsLoaded

Selain bidang input umum, InstructionsLoaded hooks menerima bidang-bidang ini:
BidangDeskripsi
file_pathPath absolut ke file instruksi yang dimuat
memory_typeCakupan file: "User", "Project", "Local", atau "Managed"
load_reasonMengapa file dimuat: "session_start", "nested_traversal", "path_glob_match", "include", atau "compact". Nilai "compact" dijalankan ketika file instruksi dimuat ulang setelah event compaction
globsPola glob path dari frontmatter paths: file, jika ada. Hadir hanya untuk load path_glob_match
trigger_file_pathPath ke file yang akses memicu load ini, untuk lazy loads
parent_file_pathPath ke file instruksi induk yang menyertakan ini, untuk load include
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project",
  "hook_event_name": "InstructionsLoaded",
  "file_path": "/Users/my-project/CLAUDE.md",
  "memory_type": "Project",
  "load_reason": "session_start"
}

Kontrol keputusan InstructionsLoaded

InstructionsLoaded hooks tidak memiliki kontrol keputusan. Mereka tidak dapat memblokir atau memodifikasi pemuatan instruksi. Gunakan event ini untuk audit logging, compliance tracking, atau observabilitas.

UserPromptSubmit

Dijalankan ketika pengguna mengirimkan prompt, sebelum Claude memproses. Ini memungkinkan Anda menambahkan konteks tambahan berdasarkan prompt/percakapan, memvalidasi prompts, atau memblokir jenis prompts tertentu. Hooks UserPromptSubmit memiliki timeout default 30 detik untuk tipe command, http, dan mcp_tool, lebih pendek dari default 600 detik untuk tipe tersebut pada event lain. Karena hook ini dijalankan sebelum setiap prompt dan memblokir pemrosesan model sampai selesai, hook yang macet menghentikan sesi. Jika hook Anda memerlukan lebih banyak waktu, atur bidang timeout dalam entri hook.

Input UserPromptSubmit

Selain bidang input umum, UserPromptSubmit hooks menerima bidang prompt yang berisi teks yang dikirimkan pengguna.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "Write a function to calculate the factorial of a number"
}

Kontrol keputusan UserPromptSubmit

Hooks UserPromptSubmit dapat mengontrol apakah prompt pengguna diproses dan menambahkan konteks. Semua bidang output JSON tersedia. Ada dua cara untuk menambahkan konteks ke percakapan pada kode keluar 0:
  • Plain text stdout: teks non-JSON apa pun yang ditulis ke stdout ditambahkan sebagai konteks
  • JSON dengan additionalContext: gunakan format JSON di bawah untuk kontrol lebih. Bidang additionalContext ditambahkan sebagai konteks
Plain stdout ditampilkan sebagai output hook dalam transkrip. Bidang additionalContext ditambahkan lebih diskrit. Untuk memblokir prompt, kembalikan objek JSON dengan decision diatur ke "block":
BidangDeskripsi
decision"block" mencegah prompt diproses dan menghapusnya dari konteks. Hilangkan untuk mengizinkan prompt dilanjutkan
reasonDitampilkan ke pengguna saat decision adalah "block". Tidak ditambahkan ke konteks
additionalContextString ditambahkan ke konteks Claude bersama prompt yang dikirimkan. Lihat Tambahkan konteks untuk Claude
sessionTitleMenetapkan judul sesi. Gunakan untuk memberi nama sesi secara otomatis berdasarkan konten prompt
{
  "decision": "block",
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "My additional context here",
    "sessionTitle": "My session title"
  }
}
Format JSON tidak diperlukan untuk kasus penggunaan sederhana. Untuk menambahkan konteks, Anda dapat mencetak teks biasa ke stdout dengan kode keluar 0. Gunakan JSON ketika Anda perlu memblokir prompts atau menginginkan kontrol yang lebih terstruktur.

UserPromptExpansion

Dijalankan ketika perintah slash yang diketik pengguna berkembang menjadi prompt sebelum mencapai Claude. Gunakan ini untuk memblokir perintah tertentu dari invokasi langsung, menyuntikkan konteks untuk skill tertentu, atau mencatat perintah mana yang diinvokasi pengguna. Misalnya, hook yang cocok dengan deploy dapat memblokir /deploy kecuali file persetujuan ada, atau hook yang cocok dengan skill review dapat menambahkan checklist review tim sebagai additionalContext. Event ini mencakup path yang PreToolUse tidak: hook PreToolUse yang cocok dengan tool Skill hanya dijalankan ketika Claude memanggil tool, tetapi mengetik /skillname secara langsung melewati PreToolUse. UserPromptExpansion dijalankan pada path langsung itu. Cocok pada command_name. Biarkan matcher kosong untuk dijalankan pada setiap slash command prompt-type.

Input UserPromptExpansion

Selain bidang input umum, UserPromptExpansion hooks menerima expansion_type, command_name, command_args, command_source, dan string prompt asli. Bidang expansion_type adalah slash_command untuk skill dan custom commands, atau mcp_prompt untuk MCP server prompts.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../00893aaf.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "UserPromptExpansion",
  "expansion_type": "slash_command",
  "command_name": "example-skill",
  "command_args": "arg1 arg2",
  "command_source": "plugin",
  "prompt": "/example-skill arg1 arg2"
}

Kontrol keputusan UserPromptExpansion

Hooks UserPromptExpansion dapat memblokir ekspansi atau menambahkan konteks. Semua bidang output JSON tersedia.
BidangDeskripsi
decision"block" mencegah slash command berkembang. Hilangkan untuk mengizinkannya dilanjutkan
reasonDitampilkan ke pengguna saat decision adalah "block"
additionalContextString ditambahkan ke konteks Claude bersama prompt yang berkembang. Lihat Tambahkan konteks untuk Claude
{
  "decision": "block",
  "reason": "This slash command is not available",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptExpansion",
    "additionalContext": "Additional context for this expansion"
  }
}

PreToolUse

Dijalankan setelah Claude membuat parameter tool dan sebelum memproses pemanggilan tool. Cocok pada nama tool: Bash, Edit, Write, Read, Glob, Grep, Agent, WebFetch, WebSearch, AskUserQuestion, ExitPlanMode, dan nama MCP tool apa pun. Gunakan PreToolUse decision control untuk mengizinkan, menolak, menanyakan, atau menunda pemanggilan tool.

Input PreToolUse

Selain bidang input umum, PreToolUse hooks menerima tool_name, tool_input, dan tool_use_id. Bidang tool_input tergantung pada tool:
Bash
Menjalankan perintah shell.
BidangTipeContohDeskripsi
commandstring"npm test"Perintah shell untuk dijalankan
descriptionstring"Run test suite"Deskripsi opsional tentang apa yang dilakukan perintah
timeoutnumber120000Timeout opsional dalam milidetik
run_in_backgroundbooleanfalseApakah menjalankan perintah di latar belakang
Write
Membuat atau menimpa file.
BidangTipeContohDeskripsi
file_pathstring"/path/to/file.txt"Path absolut ke file untuk ditulis
contentstring"file content"Konten untuk ditulis ke file
Edit
Mengganti string dalam file yang ada.
BidangTipeContohDeskripsi
file_pathstring"/path/to/file.txt"Path absolut ke file untuk diedit
old_stringstring"original text"Teks untuk dicari dan diganti
new_stringstring"replacement text"Teks pengganti
replace_allbooleanfalseApakah mengganti semua kemunculan
Read
Membaca konten file.
BidangTipeContohDeskripsi
file_pathstring"/path/to/file.txt"Path absolut ke file untuk dibaca
offsetnumber10Nomor baris opsional untuk mulai membaca dari
limitnumber50Jumlah baris opsional untuk dibaca
Glob
Menemukan file yang cocok dengan pola glob.
BidangTipeContohDeskripsi
patternstring"**/*.ts"Pola glob untuk mencocokkan file terhadap
pathstring"/path/to/dir"Direktori opsional untuk dicari. Default ke direktori kerja saat ini
Grep
Mencari konten file dengan ekspresi reguler.
BidangTipeContohDeskripsi
patternstring"TODO.*fix"Pola ekspresi reguler untuk dicari
pathstring"/path/to/dir"File atau direktori opsional untuk dicari
globstring"*.ts"Pola glob opsional untuk memfilter file
output_modestring"content""content", "files_with_matches", atau "count". Default ke "files_with_matches"
-ibooleantruePencarian case insensitive
multilinebooleanfalseAktifkan pencocokan multiline
WebFetch
Mengambil dan memproses konten web.
BidangTipeContohDeskripsi
urlstring"https://example.com/api"URL untuk mengambil konten dari
promptstring"Extract the API endpoints"Prompt untuk dijalankan pada konten yang diambil
WebSearch
Mencari web.
BidangTipeContohDeskripsi
querystring"react hooks best practices"Query pencarian
allowed_domainsarray["docs.example.com"]Opsional: hanya sertakan hasil dari domain ini
blocked_domainsarray["spam.example.com"]Opsional: kecualikan hasil dari domain ini
Agent
Spawn subagent.
BidangTipeContohDeskripsi
promptstring"Find all API endpoints"Tugas untuk agent lakukan
descriptionstring"Find API endpoints"Deskripsi singkat tugas
subagent_typestring"Explore"Tipe agent khusus untuk digunakan
modelstring"sonnet"Alias model opsional untuk menimpa default
Dalam PostToolUse, tool_response untuk panggilan Agent yang selesai membawa teks akhir subagent bersama dengan telemetri penggunaan. Baca bidang-bidang ini untuk mencatat biaya per-subagent dari hook:
BidangTipeContohDeskripsi
statusstring"completed""completed" untuk panggilan sinkron, "async_launched" untuk run_in_background: true
agentIdstring"a4d2c8f1e0b3a297"Pengenal untuk run subagent
contentarray[{"type": "text", "text": "Found 12 endpoints..."}]Blok teks akhir subagent
totalTokensnumber12450Total tokens yang ditagih di seluruh giliran subagent
totalDurationMsnumber48211Durasi wall-clock dari run subagent
totalToolUseCountnumber7Jumlah pemanggilan tool yang dibuat subagent
usageobject{"input_tokens": 8320, ...}Breakdown token per-tipe: input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens
Untuk panggilan run_in_background: true, tool mengembalikan segera setelah meluncurkan subagent, jadi tool_response tidak membawa bidang penggunaan. Itu memiliki status: "async_launched", agentId, description, prompt, dan outputFile sebagai gantinya.
AskUserQuestion
Mengajukan pertanyaan multiple-choice satu hingga empat kepada pengguna.
BidangTipeContohDeskripsi
questionsarray[{"question": "Which framework?", "header": "Framework", "options": [{"label": "React"}], "multiSelect": false}]Pertanyaan untuk disajikan, masing-masing dengan string question, header pendek, array options, dan flag multiSelect opsional
answersobject{"Which framework?": "React"}Opsional. Memetakan teks pertanyaan ke label opsi yang dipilih. Jawaban multi-select menggabungkan label dengan koma. Claude tidak menetapkan bidang ini; sediakan melalui updatedInput untuk menjawab secara programatis
ExitPlanMode
Menyajikan rencana dan meminta pengguna untuk menyetujuinya sebelum Claude meninggalkan plan mode. Claude menulis rencana ke file di disk sebelum memanggil tool, jadi tool_input literal dari model hanya membawa allowedPrompts. Claude Code menyuntikkan konten rencana dan path file sebelum meneruskan input ke hooks.
BidangTipeContohDeskripsi
planstring"## Refactor auth\n1. Extract..."Konten rencana dalam Markdown. Disuntikkan dari file rencana di disk
planFilePathstring"/Users/.../plans/refactor-auth.md"Path ke file rencana. Disuntikkan
allowedPromptsarray[{"tool": "Bash", "prompt": "run tests"}]Opsional. Izin berbasis prompt yang diminta Claude untuk mengimplementasikan rencana, masing-masing dengan nama tool dan prompt yang menjelaskan kategori tindakan
Dalam PostToolUse, tool_response adalah objek dengan bidang plan dan filePath yang menyimpan rencana yang disetujui, ditambah flag status internal. Baca tool_response.plan untuk konten rencana daripada membaca ulang file dari disk.

Kontrol keputusan PreToolUse

Hooks PreToolUse dapat mengontrol apakah pemanggilan tool dilanjutkan. Tidak seperti hooks lain yang menggunakan bidang decision tingkat atas, PreToolUse mengembalikan keputusannya di dalam objek hookSpecificOutput. Ini memberikannya kontrol yang lebih kaya: empat hasil (izinkan, tolak, tanya, atau tunda) ditambah kemampuan untuk memodifikasi input tool sebelum eksekusi.
BidangDeskripsi
permissionDecision"allow" melewati prompt izin. "deny" mencegah pemanggilan tool. "ask" meminta pengguna untuk mengkonfirmasi. "defer" keluar dengan baik sehingga tool dapat dilanjutkan nanti. Deny and ask rules masih berlaku terlepas dari apa yang dikembalikan hook
permissionDecisionReasonUntuk "allow" dan "ask", ditampilkan ke pengguna tetapi bukan Claude. Untuk "deny", ditampilkan ke Claude. Untuk "defer", diabaikan
updatedInputMemodifikasi parameter input tool sebelum eksekusi. Menggantikan seluruh objek input, jadi sertakan bidang yang tidak berubah bersama yang dimodifikasi. Gabungkan dengan "allow" untuk persetujuan otomatis, atau "ask" untuk menampilkan input yang dimodifikasi ke pengguna. Untuk "defer", diabaikan
additionalContextString ditambahkan ke konteks Claude bersama hasil tool. Diabaikan ketika permissionDecision adalah "defer". Lihat Tambahkan konteks untuk Claude
Ketika beberapa PreToolUse hooks mengembalikan keputusan berbeda, prioritas adalah deny > defer > ask > allow. Ketika hook mengembalikan "ask", dialog izin yang ditampilkan kepada pengguna mencakup label yang mengidentifikasi dari mana hook berasal: misalnya, [User], [Project], [Plugin], atau [Local]. Ini membantu pengguna memahami sumber konfigurasi mana yang meminta konfirmasi.
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "My reason here",
    "updatedInput": {
      "field_to_modify": "new value"
    },
    "additionalContext": "Current environment: production. Proceed with caution."
  }
}
AskUserQuestion dan ExitPlanMode memerlukan interaksi pengguna dan biasanya memblokir dalam mode non-interaktif dengan flag -p. Mengembalikan permissionDecision: "allow" bersama dengan updatedInput memenuhi persyaratan itu: hook membaca input tool dari stdin, mengumpulkan jawaban melalui UI Anda sendiri, dan mengembalikannya dalam updatedInput sehingga tool dijalankan tanpa meminta. Mengembalikan "allow" saja tidak cukup untuk tools ini. Untuk AskUserQuestion, kembalikan array questions asli dan tambahkan objek answers yang memetakan teks setiap pertanyaan ke jawaban yang dipilih.
PreToolUse sebelumnya menggunakan bidang decision dan reason tingkat atas, tetapi ini sudah usang untuk event ini. Gunakan hookSpecificOutput.permissionDecision dan hookSpecificOutput.permissionDecisionReason sebagai gantinya. Nilai usang "approve" dan "block" memetakan ke "allow" dan "deny" masing-masing. Events lain seperti PostToolUse dan Stop terus menggunakan decision dan reason tingkat atas sebagai format saat ini mereka.

Tunda pemanggilan tool untuk nanti

"defer" adalah untuk integrasi yang menjalankan claude -p sebagai subprocess dan membaca output JSON-nya, seperti aplikasi Agent SDK atau UI kustom yang dibangun di atas Claude Code. Ini memungkinkan proses pemanggil itu menjeda Claude pada pemanggilan tool, mengumpulkan input melalui antarmuka miliknya sendiri, dan melanjutkan di mana ia berhenti. Claude Code menghormati nilai ini hanya dalam mode non-interaktif dengan flag -p. Dalam sesi interaktif itu mencatat peringatan dan mengabaikan hasil hook.
Nilai defer memerlukan Claude Code v2.1.89 atau lebih baru. Versi sebelumnya tidak mengenalinya dan tool melanjutkan melalui alur izin normal.
Tool AskUserQuestion adalah kasus tipikal: Claude ingin menanyakan sesuatu kepada pengguna, tetapi tidak ada terminal untuk menjawab. Perjalanan bolak-balik bekerja seperti ini:
  1. Claude memanggil AskUserQuestion. Hook PreToolUse dijalankan.
  2. Hook mengembalikan permissionDecision: "defer". Tool tidak dijalankan. Proses keluar dengan stop_reason: "tool_deferred" dan pemanggilan tool yang tertunda dipertahankan dalam transkrip.
  3. Proses pemanggil membaca deferred_tool_use dari hasil SDK, menampilkan pertanyaan di UI miliknya sendiri, dan menunggu jawaban.
  4. Proses pemanggil menjalankan claude -p --resume <session-id>. Pemanggilan tool yang sama menjalankan PreToolUse lagi.
  5. Hook mengembalikan permissionDecision: "allow" dengan jawaban dalam updatedInput. Tool dijalankan dan Claude melanjutkan.
Bidang deferred_tool_use membawa id, name, dan input tool. input adalah parameter yang Claude hasilkan untuk pemanggilan tool, ditangkap sebelum eksekusi:
{
  "type": "result",
  "subtype": "success",
  "stop_reason": "tool_deferred",
  "session_id": "abc123",
  "deferred_tool_use": {
    "id": "toolu_01abc",
    "name": "AskUserQuestion",
    "input": { "questions": [{ "question": "Which framework?", "header": "Framework", "options": [{"label": "React"}, {"label": "Vue"}], "multiSelect": false }] }
  }
}
Tidak ada timeout atau batas retry. Sesi tetap di disk sampai Anda melanjutkannya, tunduk pada penyapuan retensi cleanupPeriodDays yang menghapus file sesi setelah 30 hari secara default. Jika jawaban tidak siap saat Anda melanjutkan, hook dapat mengembalikan "defer" lagi dan proses keluar dengan cara yang sama. Proses pemanggil mengontrol kapan harus memecah loop dengan akhirnya mengembalikan "allow" atau "deny" dari hook. "defer" hanya bekerja ketika Claude membuat satu pemanggilan tool dalam giliran. Jika Claude membuat beberapa pemanggilan tool sekaligus, "defer" diabaikan dengan peringatan dan tool melanjutkan melalui alur izin normal. Batasan ada karena resume hanya dapat menjalankan kembali satu tool: tidak ada cara untuk menunda satu pemanggilan dari batch tanpa meninggalkan yang lain tidak terselesaikan. Jika tool yang ditunda tidak lagi tersedia saat Anda melanjutkan, proses keluar dengan stop_reason: "tool_deferred_unavailable" dan is_error: true sebelum hook dijalankan. Ini terjadi ketika server MCP yang menyediakan tool tidak terhubung untuk sesi yang dilanjutkan. Payload deferred_tool_use masih disertakan sehingga Anda dapat mengidentifikasi tool mana yang hilang.
--resume memulihkan mode izin yang aktif saat tool ditunda, jadi Anda tidak perlu meneruskan --permission-mode lagi. Pengecualiannya adalah plan dan bypassPermissions, yang tidak pernah dibawa. Meneruskan --permission-mode secara eksplisit pada resume menimpa nilai yang dipulihkan.

PermissionRequest

Dijalankan ketika pengguna ditampilkan dialog izin. Gunakan PermissionRequest decision control untuk mengizinkan atau menolak atas nama pengguna. Cocok pada nama tool, nilai yang sama seperti PreToolUse.

Input PermissionRequest

PermissionRequest hooks menerima bidang tool_name dan tool_input seperti PreToolUse hooks, tetapi tanpa tool_use_id. Array permission_suggestions opsional berisi opsi “selalu izinkan” yang biasanya dilihat pengguna dalam dialog izin. Perbedaannya adalah kapan hook dijalankan: PermissionRequest hooks dijalankan ketika dialog izin akan ditampilkan ke pengguna, sementara PreToolUse hooks dijalankan sebelum eksekusi tool terlepas dari status izin.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PermissionRequest",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf node_modules",
    "description": "Remove node_modules directory"
  },
  "permission_suggestions": [
    {
      "type": "addRules",
      "rules": [{ "toolName": "Bash", "ruleContent": "rm -rf node_modules" }],
      "behavior": "allow",
      "destination": "localSettings"
    }
  ]
}

Kontrol keputusan PermissionRequest

Hooks PermissionRequest dapat mengizinkan atau menolak permintaan izin. Selain bidang output JSON yang tersedia untuk semua hooks, skrip hook Anda dapat mengembalikan objek decision dengan bidang spesifik event ini:
BidangDeskripsi
behavior"allow" memberikan izin, "deny" menolaknya. Deny and ask rules masih dievaluasi, jadi hook yang mengembalikan "allow" tidak menimpa aturan deny yang cocok
updatedInputUntuk "allow" saja: memodifikasi parameter input tool sebelum eksekusi. Menggantikan seluruh objek input, jadi sertakan bidang yang tidak berubah bersama yang dimodifikasi. Input yang dimodifikasi dievaluasi ulang terhadap aturan deny dan ask
updatedPermissionsUntuk "allow" saja: array dari permission update entries untuk diterapkan, seperti menambahkan aturan allow atau mengubah mode izin sesi
messageUntuk "deny" saja: memberitahu Claude mengapa izin ditolak
interruptUntuk "deny" saja: jika true, menghentikan Claude
{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedInput": {
        "command": "npm run lint"
      }
    }
  }
}

Permission update entries

Bidang output updatedPermissions dan bidang input permission_suggestions keduanya menggunakan array objek entry yang sama. Setiap entry memiliki type yang menentukan bidang lainnya, dan destination yang mengontrol di mana perubahan ditulis.
typeBidangEfek
addRulesrules, behavior, destinationMenambahkan aturan izin. rules adalah array dari objek {toolName, ruleContent?}. Hilangkan ruleContent untuk mencocokkan seluruh tool. behavior adalah "allow", "deny", atau "ask"
replaceRulesrules, behavior, destinationMengganti semua aturan dari behavior yang diberikan di destination dengan rules yang disediakan
removeRulesrules, behavior, destinationMenghapus aturan yang cocok dari behavior yang diberikan
setModemode, destinationMengubah mode izin. Mode yang valid adalah default, acceptEdits, dontAsk, bypassPermissions, dan plan
addDirectoriesdirectories, destinationMenambahkan direktori kerja. directories adalah array dari string path
removeDirectoriesdirectories, destinationMenghapus direktori kerja
setMode dengan bypassPermissions hanya berlaku jika sesi diluncurkan dengan mode bypass sudah tersedia: --dangerously-skip-permissions, --permission-mode bypassPermissions, --allow-dangerously-skip-permissions, atau permissions.defaultMode: "bypassPermissions" dalam pengaturan, dan mode tidak dinonaktifkan oleh permissions.disableBypassPermissionsMode. Jika tidak, update adalah no-op. bypassPermissions tidak pernah dipertahankan sebagai defaultMode terlepas dari destination.
Bidang destination pada setiap entry menentukan apakah perubahan tetap dalam memori atau persisten ke file pengaturan.
destinationMenulis ke
sessionhanya dalam memori, dibuang ketika sesi berakhir
localSettings.claude/settings.local.json
projectSettings.claude/settings.json
userSettings~/.claude/settings.json
Hook dapat mengembalikan salah satu dari permission_suggestions yang diterima sebagai output updatedPermissions miliknya sendiri, yang setara dengan pengguna memilih opsi “selalu izinkan” itu dalam dialog.

PostToolUse

Dijalankan segera setelah tool selesai dengan sukses. Cocok pada nama tool, nilai yang sama seperti PreToolUse.

Input PostToolUse

Hooks PostToolUse dijalankan setelah tool sudah dijalankan dengan sukses. Input mencakup tool_input, argumen yang dikirim ke tool, dan tool_response, hasil yang dikembalikan. Skema yang tepat untuk keduanya tergantung pada tool.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  },
  "tool_response": {
    "filePath": "/path/to/file.txt",
    "success": true
  },
  "tool_use_id": "toolu_01ABC123...",
  "duration_ms": 12
}
BidangDeskripsi
duration_msOpsional. Waktu eksekusi tool dalam milidetik. Mengecualikan waktu yang dihabiskan dalam prompt izin dan PreToolUse hooks

Kontrol keputusan PostToolUse

Hooks PostToolUse dapat memberikan umpan balik ke Claude setelah eksekusi tool. Selain bidang output JSON yang tersedia untuk semua hooks, skrip hook Anda dapat mengembalikan bidang spesifik event ini:
BidangDeskripsi
decision"block" menambahkan reason di sebelah hasil tool. Claude masih melihat output asli; untuk menggantinya, gunakan updatedToolOutput
reasonPenjelasan ditampilkan ke Claude saat decision adalah "block"
additionalContextString ditambahkan ke konteks Claude bersama hasil tool. Lihat Tambahkan konteks untuk Claude
updatedToolOutputMengganti output tool dengan nilai yang disediakan sebelum dikirim ke Claude. Nilai harus cocok dengan bentuk output tool
updatedMCPToolOutputMengganti output untuk MCP tools saja. Lebih suka updatedToolOutput, yang bekerja untuk semua tools
Contoh di bawah mengganti output pemanggilan Bash. Nilai pengganti cocok dengan bentuk output tool Bash:
{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "Additional information for Claude",
    "updatedToolOutput": {
      "stdout": "[redacted]",
      "stderr": "",
      "interrupted": false,
      "isImage": false
    }
  }
}
updatedToolOutput hanya mengubah apa yang dilihat Claude. Tool sudah dijalankan pada saat hook dijalankan, jadi file apa pun yang ditulis, perintah yang dijalankan, atau permintaan jaringan yang dikirim sudah berlaku. Telemetri seperti span tool OpenTelemetry dan acara analitik juga menangkap output asli sebelum hook dijalankan. Untuk mencegah atau memodifikasi pemanggilan tool sebelum dijalankan, gunakan hook PreToolUse sebagai gantinya.Nilai pengganti harus cocok dengan bentuk output tool. Tools bawaan mengembalikan objek terstruktur daripada string biasa. Misalnya, Bash mengembalikan objek dengan bidang stdout, stderr, interrupted, dan isImage. Untuk tools bawaan, nilai yang tidak cocok dengan skema output tool diabaikan dan output asli digunakan. Output tool MCP dilewatkan tanpa validasi skema. Menghapus detail kesalahan yang Claude butuhkan dapat menyebabkannya melanjutkan dengan asumsi yang salah.

PostToolUseFailure

Dijalankan ketika eksekusi tool gagal. Event ini dijalankan untuk pemanggilan tool yang melempar kesalahan atau mengembalikan hasil kegagalan. Gunakan ini untuk mencatat kegagalan, mengirim alert, atau memberikan umpan balik korektif ke Claude. Cocok pada nama tool, nilai yang sama seperti PreToolUse.

Input PostToolUseFailure

PostToolUseFailure hooks menerima bidang tool_name dan tool_input yang sama seperti PostToolUse, bersama dengan informasi kesalahan sebagai bidang tingkat atas:
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolUseFailure",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test",
    "description": "Run test suite"
  },
  "tool_use_id": "toolu_01ABC123...",
  "error": "Command exited with non-zero status code 1",
  "is_interrupt": false,
  "duration_ms": 4187
}
BidangDeskripsi
errorString menjelaskan apa yang salah
is_interruptBoolean opsional menunjukkan apakah kegagalan disebabkan oleh interupsi pengguna
duration_msOpsional. Waktu eksekusi tool dalam milidetik. Mengecualikan waktu yang dihabiskan dalam prompt izin dan PreToolUse hooks

Kontrol keputusan PostToolUseFailure

Hooks PostToolUseFailure dapat memberikan konteks ke Claude setelah kegagalan tool. Selain bidang output JSON yang tersedia untuk semua hooks, skrip hook Anda dapat mengembalikan bidang spesifik event ini:
BidangDeskripsi
additionalContextString ditambahkan ke konteks Claude bersama kesalahan. Lihat Tambahkan konteks untuk Claude
{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUseFailure",
    "additionalContext": "Additional information about the failure for Claude"
  }
}

PostToolBatch

Dijalankan sekali setelah setiap tool call dalam batch telah terselesaikan, sebelum Claude Code mengirimkan permintaan berikutnya ke model. PostToolUse dijalankan sekali per tool, yang berarti dijalankan secara bersamaan ketika Claude membuat pemanggilan tool paralel. PostToolBatch dijalankan tepat sekali dengan batch lengkap, jadi ini adalah tempat yang tepat untuk menyuntikkan konteks yang bergantung pada set tools yang dijalankan daripada pada tool tunggal. Tidak ada matcher untuk event ini.

Input PostToolBatch

Selain bidang input umum, PostToolBatch hooks menerima tool_calls, array yang menjelaskan setiap pemanggilan tool dalam batch:
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolBatch",
  "tool_calls": [
    {
      "tool_name": "Read",
      "tool_input": {"file_path": "/.../ledger/accounts.py"},
      "tool_use_id": "toolu_01...",
      "tool_response": "     1\tfrom __future__ import annotations\n     2\t..."
    },
    {
      "tool_name": "Read",
      "tool_input": {"file_path": "/.../ledger/transactions.py"},
      "tool_use_id": "toolu_02...",
      "tool_response": "     1\tfrom __future__ import annotations\n     2\t..."
    }
  ]
}
tool_response berisi konten yang sama yang diterima model dalam blok tool_result yang sesuai. Nilainya adalah string yang diserialisasi atau array blok konten, persis seperti yang dikeluarkan tool. Untuk Read, itu berarti teks dengan awalan nomor baris daripada konten file mentah. Respons dapat besar, jadi hanya parse bidang yang Anda butuhkan.
Bentuk tool_response berbeda dari PostToolUse. PostToolUse meneruskan objek Output terstruktur tool, seperti {filePath: "...", success: true} untuk Write; PostToolBatch meneruskan konten tool_result yang diserialisasi yang dilihat model.

Kontrol keputusan PostToolBatch

Hooks PostToolBatch dapat menyuntikkan konteks untuk Claude. Selain bidang output JSON yang tersedia untuk semua hooks, skrip hook Anda dapat mengembalikan bidang spesifik event ini:
BidangDeskripsi
additionalContextString konteks yang disuntikkan sekali sebelum panggilan model berikutnya. Lihat Tambahkan konteks untuk Claude untuk detail pengiriman, apa yang harus dimasukkan, dan cara sesi yang dilanjutkan menangani nilai masa lalu
{
  "hookSpecificOutput": {
    "hookEventName": "PostToolBatch",
    "additionalContext": "These files are part of the ledger module. Run pytest before marking the task complete."
  }
}
Mengembalikan decision: "block" atau continue: false menghentikan loop agentic sebelum panggilan model berikutnya.

PermissionDenied

Dijalankan ketika pengklasifikasi mode otomatis menolak pemanggilan tool. Hook ini hanya dijalankan dalam mode otomatis: itu tidak dijalankan ketika Anda secara manual menolak dialog izin, ketika hook PreToolUse memblokir pemanggilan, atau ketika aturan deny cocok. Gunakan untuk mencatat penolakan pengklasifikasi, menyesuaikan konfigurasi, atau memberitahu model itu dapat mencoba lagi pemanggilan tool. Cocok pada nama tool, nilai yang sama seperti PreToolUse.

Input PermissionDenied

Selain bidang input umum, PermissionDenied hooks menerima tool_name, tool_input, tool_use_id, dan reason.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "auto",
  "hook_event_name": "PermissionDenied",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf /tmp/build",
    "description": "Clean build directory"
  },
  "tool_use_id": "toolu_01ABC123...",
  "reason": "Auto mode denied: command targets a path outside the project"
}
BidangDeskripsi
reasonPenjelasan pengklasifikasi tentang mengapa pemanggilan tool ditolak

Kontrol keputusan PermissionDenied

PermissionDenied hooks dapat memberitahu model itu dapat mencoba lagi pemanggilan tool yang ditolak. Kembalikan objek JSON dengan hookSpecificOutput.retry diatur ke true:
{
  "hookSpecificOutput": {
    "hookEventName": "PermissionDenied",
    "retry": true
  }
}
Ketika retry adalah true, Claude Code menambahkan pesan ke percakapan memberitahu model itu dapat mencoba lagi pemanggilan tool. Penolakan itu sendiri tidak dibatalkan. Jika hook Anda tidak mengembalikan JSON, atau mengembalikan retry: false, penolakan tetap dan model menerima pesan penolakan asli.

Notification

Dijalankan ketika Claude Code mengirimkan notifikasi. Cocok pada tipe notifikasi: permission_prompt, idle_prompt, auth_success, elicitation_dialog, elicitation_complete, elicitation_response. Hilangkan matcher untuk menjalankan hooks untuk semua tipe notifikasi. Gunakan matchers terpisah untuk menjalankan handler berbeda tergantung pada tipe notifikasi. Konfigurasi ini memicu skrip alert khusus izin ketika Claude memerlukan persetujuan izin dan notifikasi berbeda ketika Claude telah idle:
{
  "hooks": {
    "Notification": [
      {
        "matcher": "permission_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/permission-alert.sh"
          }
        ]
      },
      {
        "matcher": "idle_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/idle-notification.sh"
          }
        ]
      }
    ]
  }
}

Input Notification

Selain bidang input umum, Notification hooks menerima message dengan teks notifikasi, title opsional, dan notification_type menunjukkan tipe mana yang dijalankan.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "Notification",
  "message": "Claude needs your permission to use Bash",
  "title": "Permission needed",
  "notification_type": "permission_prompt"
}
Notification hooks tidak dapat memblokir atau memodifikasi notifikasi. Mereka dimaksudkan untuk efek samping seperti meneruskan notifikasi ke layanan eksternal. Bidang output JSON umum seperti systemMessage berlaku.

SubagentStart

Dijalankan ketika subagent Claude Code dispawn melalui tool Agent. Mendukung matchers untuk memfilter berdasarkan nama tipe agent. Untuk agent bawaan, ini adalah nama agent seperti general-purpose, Explore, atau Plan. Untuk custom subagents, ini adalah bidang name dari frontmatter agent, bukan nama file.

Input SubagentStart

Selain bidang input umum, SubagentStart hooks menerima agent_id dengan pengenal unik untuk subagent dan agent_type dengan nama agent (agent bawaan seperti "general-purpose", "Explore", "Plan", atau nama agent kustom).
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "SubagentStart",
  "agent_id": "agent-abc123",
  "agent_type": "Explore"
}
SubagentStart hooks tidak dapat memblokir pembuatan subagent, tetapi mereka dapat menyuntikkan konteks ke subagent. Selain bidang output JSON yang tersedia untuk semua hooks, Anda dapat mengembalikan:
BidangDeskripsi
additionalContextString ditambahkan ke konteks subagent pada awal percakapannya, sebelum prompt pertamanya. Lihat Tambahkan konteks untuk Claude
{
  "hookSpecificOutput": {
    "hookEventName": "SubagentStart",
    "additionalContext": "Follow security guidelines for this task"
  }
}

SubagentStop

Dijalankan ketika subagent Claude Code telah selesai merespons. Cocok pada tipe agent, nilai yang sama seperti SubagentStart.

Input SubagentStop

Selain bidang input umum, SubagentStop hooks menerima stop_hook_active, agent_id, agent_type, agent_transcript_path, dan last_assistant_message. Bidang agent_type adalah nilai yang digunakan untuk pemfilteran matcher. transcript_path adalah transkrip sesi utama, sementara agent_transcript_path adalah transkrip subagent sendiri yang disimpan dalam folder subagents/ bersarang. Bidang last_assistant_message berisi konten teks respons akhir subagent, jadi hooks dapat mengaksesnya tanpa mengurai file transkrip.
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../abc123.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "SubagentStop",
  "stop_hook_active": false,
  "agent_id": "def456",
  "agent_type": "Explore",
  "agent_transcript_path": "~/.claude/projects/.../abc123/subagents/agent-def456.jsonl",
  "last_assistant_message": "Analysis complete. Found 3 potential issues..."
}
SubagentStop hooks menggunakan format kontrol keputusan yang sama seperti Stop hooks. Mereka tidak mendukung additionalContext. Mengembalikan decision: "block" dengan reason membuat subagent tetap berjalan dan mengirimkan reason ke subagent sebagai instruksi berikutnya. Untuk menyuntikkan konteks ke sesi induk setelah subagent kembali, gunakan hook PostToolUse pada tool Agent sebagai gantinya.

TaskCreated

Dijalankan ketika tugas sedang dibuat melalui tool TaskCreate. Gunakan ini untuk menegakkan konvensi penamaan, memerlukan deskripsi tugas, atau mencegah tugas tertentu dari dibuat. Ketika hook TaskCreated keluar dengan kode 2, tugas tidak dibuat dan pesan stderr diumpankan kembali ke model sebagai umpan balik. Untuk menghentikan teammate sepenuhnya alih-alih menjalankannya kembali, kembalikan JSON dengan {"continue": false, "stopReason": "..."}. TaskCreated hooks tidak mendukung matchers dan dijalankan pada setiap kemunculan.

Input TaskCreated

Selain bidang input umum, TaskCreated hooks menerima task_id, task_subject, dan secara opsional task_description, teammate_name, dan team_name.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "TaskCreated",
  "task_id": "task-001",
  "task_subject": "Implement user authentication",
  "task_description": "Add login and signup endpoints",
  "teammate_name": "implementer",
  "team_name": "my-project"
}
BidangDeskripsi
task_idPengenal tugas yang sedang dibuat
task_subjectJudul tugas
task_descriptionDeskripsi detail tugas. Mungkin tidak ada
teammate_nameNama teammate yang membuat tugas. Mungkin tidak ada
team_nameNama team. Mungkin tidak ada

Kontrol keputusan TaskCreated

TaskCreated hooks mendukung dua cara untuk mengontrol pembuatan tugas:
  • Kode keluar 2: tugas tidak dibuat dan pesan stderr diumpankan kembali ke model sebagai umpan balik.
  • JSON {"continue": false, "stopReason": "..."}: menghentikan teammate sepenuhnya, mencocokkan perilaku hook Stop. stopReason ditampilkan ke pengguna.
Contoh ini memblokir tugas yang subjeknya tidak mengikuti format yang diperlukan:
#!/bin/bash
INPUT=$(cat)
TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')

if [[ ! "$TASK_SUBJECT" =~ ^\[TICKET-[0-9]+\] ]]; then
  echo "Task subject must start with a ticket number, e.g. '[TICKET-123] Add feature'" >&2
  exit 2
fi

exit 0

TaskCompleted

Dijalankan ketika tugas sedang ditandai sebagai selesai. Ini dijalankan dalam dua situasi: ketika agent apa pun secara eksplisit menandai tugas sebagai selesai melalui tool TaskUpdate, atau ketika agent team teammate menyelesaikan giliran dengan tugas yang sedang berlangsung. Gunakan ini untuk menegakkan kriteria penyelesaian seperti passing tests atau lint checks sebelum tugas dapat ditutup. Ketika hook TaskCompleted keluar dengan kode 2, tugas tidak ditandai sebagai selesai dan pesan stderr diumpankan kembali ke model sebagai umpan balik. Untuk menghentikan teammate sepenuhnya alih-alih menjalankannya kembali, kembalikan JSON dengan {"continue": false, "stopReason": "..."}. TaskCompleted hooks tidak mendukung matchers dan dijalankan pada setiap kemunculan.

Input TaskCompleted

Selain bidang input umum, TaskCompleted hooks menerima task_id, task_subject, dan secara opsional task_description, teammate_name, dan team_name.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "TaskCompleted",
  "task_id": "task-001",
  "task_subject": "Implement user authentication",
  "task_description": "Add login and signup endpoints",
  "teammate_name": "implementer",
  "team_name": "my-project"
}
BidangDeskripsi
task_idPengenal tugas yang sedang diselesaikan
task_subjectJudul tugas
task_descriptionDeskripsi detail tugas. Mungkin tidak ada
teammate_nameNama teammate yang menyelesaikan tugas. Mungkin tidak ada
team_nameNama team. Mungkin tidak ada

Kontrol keputusan TaskCompleted

TaskCompleted hooks mendukung dua cara untuk mengontrol penyelesaian tugas:
  • Kode keluar 2: tugas tidak ditandai sebagai selesai dan pesan stderr diumpankan kembali ke model sebagai umpan balik.
  • JSON {"continue": false, "stopReason": "..."}: menghentikan teammate sepenuhnya, mencocokkan perilaku hook Stop. stopReason ditampilkan ke pengguna.
Contoh ini menjalankan tests dan memblokir penyelesaian tugas jika gagal:
#!/bin/bash
INPUT=$(cat)
TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')

# Jalankan test suite
if ! npm test 2>&1; then
  echo "Tests not passing. Fix failing tests before completing: $TASK_SUBJECT" >&2
  exit 2
fi

exit 0

Stop

Dijalankan ketika agent Claude Code utama telah selesai merespons. Tidak dijalankan jika penghentian terjadi karena interupsi pengguna. Kesalahan API menjalankan StopFailure sebagai gantinya.
Perintah /goal adalah pintasan bawaan untuk hook Stop berbasis prompt yang bersifat sesi. Gunakan ketika Anda ingin Claude terus bekerja sampai kondisi terpenuhi tanpa menulis konfigurasi hook.

Input Stop

Selain bidang input umum, Stop hooks menerima stop_hook_active dan last_assistant_message. Bidang stop_hook_active adalah true ketika Claude Code sudah melanjutkan sebagai hasil dari stop hook. Periksa nilai ini atau proses transkrip untuk mencegah Claude Code berjalan tanpa batas. Bidang last_assistant_message berisi konten teks respons akhir Claude, jadi hooks dapat mengaksesnya tanpa mengurai file transkrip.
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "Stop",
  "stop_hook_active": true,
  "last_assistant_message": "I've completed the refactoring. Here's a summary..."
}

Kontrol keputusan Stop

Hooks Stop dan SubagentStop dapat mengontrol apakah Claude melanjutkan. Selain bidang output JSON yang tersedia untuk semua hooks, skrip hook Anda dapat mengembalikan bidang spesifik event ini:
BidangDeskripsi
decision"block" mencegah Claude berhenti. Hilangkan untuk mengizinkan Claude berhenti
reasonDiperlukan saat decision adalah "block". Memberitahu Claude mengapa itu harus melanjutkan
{
  "decision": "block",
  "reason": "Must be provided when Claude is blocked from stopping"
}

StopFailure

Dijalankan alih-alih Stop ketika giliran berakhir karena kesalahan API. Output dan kode keluar diabaikan. Gunakan ini untuk mencatat kegagalan, mengirim alert, atau mengambil tindakan pemulihan ketika Claude tidak dapat menyelesaikan respons karena rate limits, masalah autentikasi, atau kesalahan API lainnya.

Input StopFailure

Selain bidang input umum, StopFailure hooks menerima error, error_details opsional, dan last_assistant_message opsional. Bidang error mengidentifikasi tipe kesalahan dan digunakan untuk pemfilteran matcher.
BidangDeskripsi
errorTipe kesalahan: rate_limit, authentication_failed, oauth_org_not_allowed, billing_error, invalid_request, server_error, max_output_tokens, atau unknown
error_detailsDetail tambahan tentang kesalahan, ketika tersedia
last_assistant_messageTeks kesalahan yang dirender ditampilkan dalam percakapan. Tidak seperti Stop dan SubagentStop, di mana bidang ini menyimpan output percakapan Claude, untuk StopFailure itu berisi string kesalahan API itu sendiri, seperti "API Error: Rate limit reached"
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "StopFailure",
  "error": "rate_limit",
  "error_details": "429 Too Many Requests",
  "last_assistant_message": "API Error: Rate limit reached"
}
StopFailure hooks tidak memiliki kontrol keputusan. Mereka dijalankan untuk tujuan notifikasi dan logging saja.

TeammateIdle

Dijalankan ketika agent team teammate akan menjadi idle setelah menyelesaikan giliran. Gunakan ini untuk menegakkan quality gates sebelum teammate berhenti bekerja, seperti memerlukan passing lint checks atau memverifikasi bahwa file output ada. Ketika hook TeammateIdle keluar dengan kode 2, teammate menerima pesan stderr sebagai umpan balik dan terus bekerja alih-alih menjadi idle. Untuk menghentikan teammate sepenuhnya alih-alih menjalankannya kembali, kembalikan JSON dengan {"continue": false, "stopReason": "..."}. TeammateIdle hooks tidak mendukung matchers dan dijalankan pada setiap kemunculan.

Input TeammateIdle

Selain bidang input umum, TeammateIdle hooks menerima teammate_name dan team_name.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "TeammateIdle",
  "teammate_name": "researcher",
  "team_name": "my-project"
}
BidangDeskripsi
teammate_nameNama teammate yang akan menjadi idle
team_nameNama team

Kontrol keputusan TeammateIdle

TeammateIdle hooks mendukung dua cara untuk mengontrol perilaku teammate:
  • Kode keluar 2: teammate menerima pesan stderr sebagai umpan balik dan terus bekerja alih-alih menjadi idle.
  • JSON {"continue": false, "stopReason": "..."}: menghentikan teammate sepenuhnya, mencocokkan perilaku hook Stop. stopReason ditampilkan ke pengguna.
Contoh ini memeriksa bahwa artefak build ada sebelum mengizinkan teammate menjadi idle:
#!/bin/bash

if [ ! -f "./dist/output.js" ]; then
  echo "Build artifact missing. Run the build before stopping." >&2
  exit 2
fi

exit 0

ConfigChange

Dijalankan ketika file konfigurasi berubah selama sesi. Gunakan ini untuk mengaudit perubahan pengaturan, menegakkan kebijakan keamanan, atau memblokir modifikasi tidak sah ke file konfigurasi. ConfigChange hooks dijalankan untuk perubahan ke file pengaturan, pengaturan kebijakan terkelola, dan file skill. Bidang source dalam input memberitahu Anda tipe konfigurasi mana yang berubah, dan bidang file_path opsional menyediakan path ke file yang berubah. Matcher memfilter pada sumber konfigurasi:
MatcherKapan dijalankan
user_settings~/.claude/settings.json berubah
project_settings.claude/settings.json berubah
local_settings.claude/settings.local.json berubah
policy_settingsPengaturan kebijakan terkelola berubah
skillsFile skill dalam .claude/skills/ berubah
Contoh ini mencatat semua perubahan konfigurasi untuk audit keamanan:
{
  "hooks": {
    "ConfigChange": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/audit-config-change.sh",
            "args": []
          }
        ]
      }
    ]
  }
}

Input ConfigChange

Selain bidang input umum, ConfigChange hooks menerima source dan secara opsional file_path. Bidang source menunjukkan tipe konfigurasi mana yang berubah, dan file_path menyediakan path ke file spesifik yang dimodifikasi.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "ConfigChange",
  "source": "project_settings",
  "file_path": "/Users/.../my-project/.claude/settings.json"
}

Kontrol keputusan ConfigChange

ConfigChange hooks dapat memblokir perubahan konfigurasi dari berlaku. Gunakan kode keluar 2 atau JSON decision untuk mencegah perubahan. Ketika diblokir, pengaturan baru tidak diterapkan ke sesi yang berjalan.
BidangDeskripsi
decision"block" mencegah perubahan konfigurasi diterapkan. Hilangkan untuk mengizinkan perubahan
reasonPenjelasan ditampilkan ke pengguna saat decision adalah "block"
{
  "decision": "block",
  "reason": "Configuration changes to project settings require admin approval"
}
Perubahan policy_settings tidak dapat diblokir. Hooks masih dijalankan untuk sumber policy_settings, jadi Anda dapat menggunakannya untuk audit logging, tetapi keputusan blocking apa pun diabaikan. Ini memastikan pengaturan yang dikelola enterprise selalu berlaku.

CwdChanged

Dijalankan ketika direktori kerja berubah selama sesi, misalnya ketika Claude menjalankan perintah cd. Gunakan ini untuk bereaksi terhadap perubahan direktori: muat ulang variabel lingkungan, aktifkan toolchains khusus proyek, atau jalankan skrip setup secara otomatis. Berpasangan dengan FileChanged untuk tools seperti direnv yang mengelola lingkungan per-direktori. CwdChanged hooks memiliki akses ke CLAUDE_ENV_FILE. Variabel yang ditulis ke file itu bertahan ke perintah Bash berikutnya untuk sesi, sama seperti dalam SessionStart hooks. CwdChanged tidak mendukung matchers dan dijalankan pada setiap perubahan direktori.

Input CwdChanged

Selain bidang input umum, CwdChanged hooks menerima old_cwd dan new_cwd.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project/src",
  "hook_event_name": "CwdChanged",
  "old_cwd": "/Users/my-project",
  "new_cwd": "/Users/my-project/src"
}

Output CwdChanged

Selain bidang output JSON yang tersedia untuk semua hooks, CwdChanged hooks dapat mengembalikan watchPaths untuk secara dinamis menetapkan path file mana yang FileChanged pantau:
BidangDeskripsi
watchPathsArray path absolut. Menggantikan daftar watch dinamis saat ini (path dari konfigurasi matcher Anda selalu dipantau). Mengembalikan array kosong menghapus daftar dinamis, yang khas saat memasuki direktori baru
CwdChanged hooks tidak memiliki kontrol keputusan. Mereka tidak dapat memblokir perubahan direktori.

FileChanged

Dijalankan ketika file yang dipantau berubah di disk. Berguna untuk memuat ulang variabel lingkungan ketika file konfigurasi proyek dimodifikasi. Bidang matcher untuk event ini melayani dua peran:
  • Bangun daftar watch: nilai dibagi pada | dan setiap segmen terdaftar sebagai nama file literal di direktori kerja, jadi ".envrc|.env" menonton tepat dua file itu. Pola regex tidak berguna di sini: nilai seperti ^\.env akan menonton file yang secara harfiah bernama ^\.env.
  • Filter hooks mana yang dijalankan: ketika file yang dipantau berubah, nilai yang sama memfilter grup hook mana yang dijalankan menggunakan aturan matcher standar terhadap basename file yang berubah.
FileChanged hooks memiliki akses ke CLAUDE_ENV_FILE. Variabel yang ditulis ke file itu bertahan ke perintah Bash berikutnya untuk sesi, sama seperti dalam SessionStart hooks.

Input FileChanged

Selain bidang input umum, FileChanged hooks menerima file_path dan event.
BidangDeskripsi
file_pathPath absolut ke file yang berubah
eventApa yang terjadi: "change" (file dimodifikasi), "add" (file dibuat), atau "unlink" (file dihapus)
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project",
  "hook_event_name": "FileChanged",
  "file_path": "/Users/my-project/.envrc",
  "event": "change"
}

Output FileChanged

Selain bidang output JSON yang tersedia untuk semua hooks, FileChanged hooks dapat mengembalikan watchPaths untuk secara dinamis memperbarui path file mana yang dipantau:
BidangDeskripsi
watchPathsArray path absolut. Menggantikan daftar watch dinamis saat ini (path dari konfigurasi matcher Anda selalu dipantau). Gunakan ini ketika skrip hook Anda menemukan file tambahan untuk dipantau berdasarkan file yang berubah
FileChanged hooks tidak memiliki kontrol keputusan. Mereka tidak dapat memblokir perubahan file dari terjadi.

WorktreeCreate

Ketika Anda menjalankan claude --worktree atau subagent menggunakan isolation: "worktree", Claude Code membuat salinan kerja terisolasi menggunakan git worktree. Jika Anda mengonfigurasi hook WorktreeCreate, itu menggantikan perilaku git default, memungkinkan Anda menggunakan sistem kontrol versi berbeda seperti SVN, Perforce, atau Mercurial. Karena hook menggantikan perilaku default sepenuhnya, .worktreeinclude tidak diproses. Jika Anda perlu menyalin file konfigurasi lokal seperti .env ke worktree baru, lakukan di dalam skrip hook Anda. Hook harus mengembalikan path absolut ke direktori worktree yang dibuat. Claude Code menggunakan path ini sebagai direktori kerja untuk sesi terisolasi. Command hooks mencetaknya di stdout; HTTP hooks mengembalikannya melalui hookSpecificOutput.worktreePath. Contoh ini membuat salinan kerja SVN dan mencetak path untuk Claude Code gunakan. Ganti URL repositori dengan milik Anda sendiri:
{
  "hooks": {
    "WorktreeCreate": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'NAME=$(jq -r .name); DIR=\"$HOME/.claude/worktrees/$NAME\"; svn checkout https://svn.example.com/repo/trunk \"$DIR\" >&2 && echo \"$DIR\"'"
          }
        ]
      }
    ]
  }
}
Hook membaca name worktree dari input JSON di stdin, melakukan checkout salinan segar ke direktori baru, dan mencetak path direktori. echo pada baris terakhir adalah apa yang Claude Code baca sebagai path worktree. Alihkan output lainnya ke stderr sehingga tidak mengganggu path.

Input WorktreeCreate

Selain bidang input umum, WorktreeCreate hooks menerima bidang name. Ini adalah pengenal slug untuk worktree baru, baik ditentukan oleh pengguna atau auto-generated (misalnya, bold-oak-a3f2).
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "WorktreeCreate",
  "name": "feature-auth"
}

Output WorktreeCreate

WorktreeCreate hooks tidak menggunakan model keputusan allow/block standar. Sebaliknya, kesuksesan atau kegagalan hook menentukan hasil. Hook harus mengembalikan path absolut ke direktori worktree yang dibuat:
  • Command hooks (type: "command"): cetak path di stdout.
  • HTTP hooks (type: "http"): kembalikan { "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } } dalam badan respons.
Jika hook gagal atau tidak menghasilkan path, pembuatan worktree gagal dengan kesalahan.

WorktreeRemove

Pasangan cleanup untuk WorktreeCreate. Hook ini dijalankan ketika worktree sedang dihapus, baik ketika Anda keluar dari sesi --worktree dan memilih untuk menghapusnya, atau ketika subagent dengan isolation: "worktree" selesai. Untuk worktrees berbasis git, Claude menangani cleanup secara otomatis dengan git worktree remove. Jika Anda mengonfigurasi hook WorktreeCreate untuk sistem kontrol versi non-git, pasangkan dengan hook WorktreeRemove untuk menangani cleanup. Tanpanya, direktori worktree ditinggalkan di disk. Claude Code meneruskan path yang dikembalikan oleh WorktreeCreate sebagai worktree_path dalam input hook. Contoh ini membaca path itu dan menghapus direktori:
{
  "hooks": {
    "WorktreeRemove": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'jq -r .worktree_path | xargs rm -rf'"
          }
        ]
      }
    ]
  }
}

Input WorktreeRemove

Selain bidang input umum, WorktreeRemove hooks menerima bidang worktree_path, yang merupakan path absolut ke worktree yang sedang dihapus.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "WorktreeRemove",
  "worktree_path": "/Users/.../my-project/.claude/worktrees/feature-auth"
}
WorktreeRemove hooks tidak memiliki kontrol keputusan. Mereka tidak dapat memblokir penghapusan worktree tetapi dapat melakukan tugas cleanup seperti menghapus status kontrol versi atau mengarsipkan perubahan. Kegagalan hook dicatat dalam mode debug saja.

PreCompact

Dijalankan sebelum Claude Code akan menjalankan operasi compact. Nilai matcher menunjukkan apakah compaction dipicu secara manual atau otomatis:
MatcherKapan dijalankan
manual/compact
autoAuto-compact ketika context window penuh
Keluar dengan kode 2 untuk memblokir compaction. Untuk manual /compact, pesan stderr ditampilkan ke pengguna. Anda juga dapat memblokir dengan mengembalikan JSON dengan "decision": "block". Memblokir automatic compaction memiliki efek berbeda tergantung pada kapan dijalankan. Jika compaction dipicu secara proaktif sebelum batas konteks, Claude Code melewatinya dan percakapan berlanjut tanpa compaction. Jika compaction dipicu untuk pulih dari kesalahan batas konteks yang sudah dikembalikan oleh API, kesalahan yang mendasar muncul dan permintaan saat ini gagal.

Input PreCompact

Selain bidang input umum, PreCompact hooks menerima trigger dan custom_instructions. Untuk manual, custom_instructions berisi apa yang diteruskan pengguna ke /compact. Untuk auto, custom_instructions kosong.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PreCompact",
  "trigger": "manual",
  "custom_instructions": ""
}

PostCompact

Dijalankan setelah Claude Code menyelesaikan operasi compact. Gunakan event ini untuk bereaksi terhadap status compacted baru, misalnya untuk mencatat ringkasan yang dihasilkan atau memperbarui status eksternal. Nilai matcher yang sama berlaku seperti untuk PreCompact:
MatcherKapan dijalankan
manualSetelah /compact
autoSetelah auto-compact ketika context window penuh

Input PostCompact

Selain bidang input umum, PostCompact hooks menerima trigger dan compact_summary. Bidang compact_summary berisi ringkasan percakapan yang dihasilkan oleh operasi compact.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PostCompact",
  "trigger": "manual",
  "compact_summary": "Summary of the compacted conversation..."
}
PostCompact hooks tidak memiliki kontrol keputusan. Mereka tidak dapat mempengaruhi hasil compaction tetapi dapat melakukan tugas follow-up.

SessionEnd

Dijalankan ketika sesi Claude Code berakhir. Berguna untuk tugas cleanup, logging statistik sesi, atau menyimpan status sesi. Mendukung matchers untuk memfilter berdasarkan alasan keluar. Bidang reason dalam input hook menunjukkan mengapa sesi berakhir:
AlasanDeskripsi
clearSesi dihapus dengan perintah /clear
resumeSesi beralih melalui /resume interaktif
logoutPengguna logout
prompt_input_exitPengguna keluar saat input prompt terlihat
bypass_permissions_disabledMode bypass permissions dinonaktifkan
otherAlasan keluar lainnya

Input SessionEnd

Selain bidang input umum, SessionEnd hooks menerima bidang reason menunjukkan mengapa sesi berakhir. Lihat tabel reason di atas untuk semua nilai.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "SessionEnd",
  "reason": "other"
}
SessionEnd hooks tidak memiliki kontrol keputusan. Mereka tidak dapat memblokir penghentian sesi tetapi dapat melakukan tugas cleanup. SessionEnd hooks memiliki timeout default 1.5 detik. Ini berlaku untuk keluar sesi, /clear, dan beralih sesi melalui /resume interaktif. Jika hook memerlukan lebih banyak waktu, atur per-hook timeout dalam konfigurasi hook. Anggaran keseluruhan secara otomatis dinaikkan ke timeout per-hook tertinggi yang dikonfigurasi dalam file pengaturan, hingga 60 detik. Timeout yang ditetapkan pada hooks yang disediakan plugin tidak menaikkan anggaran. Untuk menimpa anggaran secara eksplisit, atur variabel lingkungan CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS dalam milidetik.
CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=5000 claude

Elicitation

Dijalankan ketika server MCP meminta input pengguna mid-task. Secara default, Claude Code menampilkan dialog interaktif untuk pengguna merespons. Hooks dapat mengintersepsi permintaan ini dan merespons secara programatis, melewati dialog sepenuhnya. Bidang matcher mencocokkan nama server MCP.

Input Elicitation

Selain bidang input umum, Elicitation hooks menerima mcp_server_name, message, dan bidang opsional mode, url, elicitation_id, dan requested_schema. Untuk form-mode elicitation (kasus paling umum):
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "Elicitation",
  "mcp_server_name": "my-mcp-server",
  "message": "Please provide your credentials",
  "mode": "form",
  "requested_schema": {
    "type": "object",
    "properties": {
      "username": { "type": "string", "title": "Username" }
    }
  }
}
Untuk URL-mode elicitation (autentikasi berbasis browser):
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "Elicitation",
  "mcp_server_name": "my-mcp-server",
  "message": "Please authenticate",
  "mode": "url",
  "url": "https://auth.example.com/login"
}

Output Elicitation

Untuk merespons secara programatis tanpa menampilkan dialog, kembalikan objek JSON dengan hookSpecificOutput:
{
  "hookSpecificOutput": {
    "hookEventName": "Elicitation",
    "action": "accept",
    "content": {
      "username": "alice"
    }
  }
}
BidangNilaiDeskripsi
actionaccept, decline, cancelApakah menerima, menolak, atau membatalkan permintaan
contentobjectNilai field form untuk dikirimkan. Hanya digunakan saat action adalah accept
Kode keluar 2 menolak elicitation dan menampilkan stderr ke pengguna.

ElicitationResult

Dijalankan setelah pengguna merespons elicitation MCP. Hooks dapat mengamati, memodifikasi, atau memblokir respons sebelum dikirim kembali ke server MCP. Bidang matcher mencocokkan nama server MCP.

Input ElicitationResult

Selain bidang input umum, ElicitationResult hooks menerima mcp_server_name, action, dan bidang opsional mode, elicitation_id, dan content.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "ElicitationResult",
  "mcp_server_name": "my-mcp-server",
  "action": "accept",
  "content": { "username": "alice" },
  "mode": "form",
  "elicitation_id": "elicit-123"
}

Output ElicitationResult

Untuk menimpa respons pengguna, kembalikan objek JSON dengan hookSpecificOutput:
{
  "hookSpecificOutput": {
    "hookEventName": "ElicitationResult",
    "action": "decline",
    "content": {}
  }
}
BidangNilaiDeskripsi
actionaccept, decline, cancelMenimpa tindakan pengguna
contentobjectMenimpa nilai field form. Hanya bermakna saat action adalah accept
Kode keluar 2 memblokir respons, mengubah tindakan efektif menjadi decline.

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):
  • PermissionRequest
  • PostToolBatch
  • PostToolUse
  • PostToolUseFailure
  • PreToolUse
  • Stop
  • SubagentStop
  • TaskCompleted
  • TaskCreated
  • UserPromptExpansion
  • UserPromptSubmit
Events yang mendukung hooks command, http, dan mcp_tool tetapi bukan prompt atau agent:
  • ConfigChange
  • CwdChanged
  • Elicitation
  • ElicitationResult
  • FileChanged
  • InstructionsLoaded
  • Notification
  • PermissionDenied
  • PostCompact
  • PreCompact
  • SessionEnd
  • StopFailure
  • SubagentStart
  • TeammateIdle
  • WorktreeCreate
  • WorktreeRemove
SessionStart dan Setup mendukung hooks command dan mcp_tool. Mereka tidak mendukung hooks http, prompt, atau agent.

Bagaimana prompt-based hooks bekerja

Alih-alih menjalankan perintah Bash, prompt-based hooks:
  1. Mengirimkan input hook dan prompt Anda ke model Claude, Haiku secara default
  2. LLM merespons dengan JSON terstruktur yang berisi keputusan
  3. Claude Code memproses keputusan secara otomatis

Konfigurasi prompt hook

Atur type ke "prompt" dan sediakan string prompt alih-alih command. Gunakan placeholder $ARGUMENTS untuk menyuntikkan data JSON input hook ke dalam teks prompt Anda. Claude Code mengirimkan prompt gabungan dan input ke model Claude cepat, yang mengembalikan keputusan JSON. Hook Stop ini meminta LLM untuk mengevaluasi apakah semua tugas selesai sebelum mengizinkan Claude selesai:
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks are complete."
          }
        ]
      }
    ]
  }
}
BidangDiperlukanDeskripsi
typeyaHarus "prompt"
promptyaTeks prompt untuk dikirim ke LLM. Gunakan $ARGUMENTS sebagai placeholder untuk JSON input hook. Jika $ARGUMENTS tidak ada, JSON input ditambahkan ke prompt
modeltidakModel untuk digunakan untuk evaluasi. Default ke model cepat
timeouttidakTimeout dalam detik. Default: 30
continueOnBlocktidakKetika prompt mengembalikan ok: false, umpankan alasan kembali ke Claude dan lanjutkan giliran alih-alih berhenti. Default: false. Diimplementasikan sebagai continue: true pada decision: "block" yang dihasilkan. Lihat Response schema untuk perilaku per-event

Skema respons

LLM harus merespons dengan JSON yang berisi:
{
  "ok": true | false,
  "reason": "Explanation for the decision"
}
BidangDeskripsi
oktrue untuk mengizinkan. false menghasilkan decision: "block". Lihat perilaku per-event di bawah
reasonDiperlukan saat ok adalah false. Digunakan sebagai alasan blokir
Apa yang terjadi pada ok: false tergantung pada event:
  • Stop dan SubagentStop: alasan diumpankan kembali ke Claude sebagai instruksi berikutnya dan giliran berlanjut
  • PreToolUse: panggilan tool ditolak dan alasan dikembalikan ke Claude sebagai kesalahan tool, setara dengan permissionDecision: "deny" dari command hook
  • PostToolUse: secara default giliran berakhir dan alasan muncul dalam chat sebagai baris peringatan. Atur continueOnBlock: true untuk umpankan alasan kembali ke Claude dan lanjutkan giliran alih-alih
  • PostToolBatch, UserPromptSubmit, dan UserPromptExpansion: giliran berakhir dan alasan muncul sebagai baris peringatan. Events ini mengakhiri giliran pada decision: "block" terlepas dari continue
  • PostToolUseFailure, TaskCreated, dan TaskCompleted: alasan dikembalikan ke Claude sebagai kesalahan tool, mirip dengan PreToolUse
  • PermissionRequest: ok: false tidak berpengaruh. Untuk menolak persetujuan dari hook, gunakan command hook yang mengembalikan hookSpecificOutput.decision.behavior: "deny"
Jika Anda memerlukan kontrol yang lebih halus pada event apa pun, gunakan command hook dengan bidang per-event yang dijelaskan dalam Decision control.

Contoh: Multi-criteria Stop hook

Hook Stop ini menggunakan prompt detail untuk memeriksa tiga kondisi sebelum mengizinkan Claude berhenti. Jika "ok" adalah false, Claude terus bekerja dengan alasan yang disediakan sebagai instruksi berikutnya. Hooks SubagentStop menggunakan format yang sama untuk mengevaluasi apakah subagent harus berhenti:
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "You are evaluating whether Claude should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"ok\": true} to allow stopping, or {\"ok\": false, \"reason\": \"your explanation\"} to continue working.",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Agent-based hooks

Agent hooks adalah eksperimental. Perilaku dan konfigurasi mungkin berubah di rilis mendatang. Untuk alur kerja produksi, lebih suka command hooks.
Agent-based hooks (type: "agent") seperti prompt-based hooks tetapi dengan akses tool multi-turn. Alih-alih pemanggilan LLM tunggal, hook agent spawn subagent yang dapat membaca file, mencari kode, dan memeriksa codebase untuk memverifikasi kondisi. Agent hooks mendukung events yang sama seperti prompt-based hooks.

Bagaimana agent hooks bekerja

Ketika hook agent dijalankan:
  1. Claude Code spawn subagent dengan prompt Anda dan JSON input hook
  2. Subagent dapat menggunakan tools seperti Read, Grep, dan Glob untuk menyelidiki
  3. Setelah hingga 50 turn, subagent mengembalikan keputusan terstruktur { "ok": true/false }
  4. Claude Code memproses keputusan dengan cara yang sama seperti prompt hook
Agent hooks berguna ketika verifikasi memerlukan memeriksa file aktual atau output test, bukan hanya mengevaluasi data input hook saja.

Konfigurasi agent hook

Atur type ke "agent" dan sediakan string prompt. Bidang konfigurasi sama seperti prompt hooks, dengan timeout default lebih lama:
BidangDiperlukanDeskripsi
typeyaHarus "agent"
promptyaPrompt menjelaskan apa yang diverifikasi. Gunakan $ARGUMENTS sebagai placeholder untuk JSON input hook
modeltidakModel untuk digunakan. Default ke model cepat
timeouttidakTimeout dalam detik. Default: 60
Skema respons sama seperti prompt hooks: { "ok": true } untuk mengizinkan atau { "ok": false, "reason": "..." } untuk memblokir. Hook Stop ini memverifikasi bahwa semua unit tests lulus sebelum mengizinkan Claude selesai:
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

Jalankan hooks di latar belakang

Secara default, hooks memblokir eksekusi Claude sampai selesai. Untuk tugas yang berjalan lama seperti deployments, test suites, atau panggilan API eksternal, atur "async": true untuk menjalankan hook di latar belakang sementara Claude terus bekerja. Async hooks tidak dapat memblokir atau mengontrol perilaku Claude: bidang respons seperti decision, permissionDecision, dan continue tidak berpengaruh, karena tindakan yang akan mereka kontrol sudah selesai.

Konfigurasi async hook

Tambahkan "async": true ke konfigurasi command hook untuk menjalankannya di latar belakang tanpa memblokir Claude. Bidang ini hanya tersedia pada hooks type: "command". Hook ini menjalankan skrip test setelah setiap pemanggilan tool Write. Claude terus bekerja segera sementara run-tests.sh dijalankan hingga 120 detik. Ketika skrip selesai, outputnya disampaikan pada turn percakapan berikutnya:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/run-tests.sh",
            "async": true,
            "timeout": 120
          }
        ]
      }
    ]
  }
}
Bidang timeout menetapkan waktu maksimum dalam detik untuk proses latar belakang. Jika tidak ditentukan, async hooks menggunakan default 10 menit yang sama seperti sync hooks.

Bagaimana async hooks dijalankan

Ketika async hook dijalankan, Claude Code memulai proses hook dan segera melanjutkan tanpa menunggu selesai. Hook menerima JSON input yang sama melalui stdin seperti hook sinkron. Setelah proses latar belakang keluar, jika hook menghasilkan respons JSON dengan bidang additionalContext, konten itu disampaikan ke Claude sebagai konteks pada turn percakapan berikutnya. Bidang systemMessage ditampilkan kepada Anda, bukan kepada Claude. Notifikasi penyelesaian async hook ditekan secara default. Untuk melihatnya, aktifkan mode verbose dengan Ctrl+O atau mulai Claude Code dengan --verbose.

Contoh: jalankan tests setelah perubahan file

Hook ini memulai test suite di latar belakang setiap kali Claude menulis file, kemudian melaporkan hasil kembali ke Claude ketika tests selesai. Simpan skrip ini ke .claude/hooks/run-tests-async.sh dalam proyek Anda dan buat dapat dijalankan dengan chmod +x:
#!/bin/bash
# run-tests-async.sh

# Baca hook input dari stdin
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# Hanya jalankan tests untuk file sumber
if [[ "$FILE_PATH" != *.ts && "$FILE_PATH" != *.js ]]; then
  exit 0
fi

# Jalankan tests dan laporkan hasil ke Claude via additionalContext
RESULT=$(npm test 2>&1)
EXIT_CODE=$?

if [ $EXIT_CODE -eq 0 ]; then
  MSG="Tests passed after editing $FILE_PATH"
else
  MSG="Tests failed after editing $FILE_PATH: $RESULT"
fi
jq -nc --arg msg "$MSG" '{hookSpecificOutput: {hookEventName: "PostToolUse", additionalContext: $msg}}'
Kemudian tambahkan konfigurasi ini ke .claude/settings.json dalam akar proyek Anda. Flag async: true memungkinkan Claude terus bekerja sementara tests dijalankan:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/run-tests-async.sh",
            "args": [],
            "async": true,
            "timeout": 300
          }
        ]
      }
    ]
  }
}

Keterbatasan

Async hooks memiliki beberapa batasan dibandingkan dengan hooks sinkron:
  • Hanya hooks type: "command" yang mendukung async. Prompt-based hooks tidak dapat dijalankan secara asinkron.
  • Async hooks tidak dapat memblokir pemanggilan tool atau mengembalikan keputusan. Pada saat hook selesai, tindakan pemicu sudah dilanjutkan.
  • Output hook disampaikan pada turn percakapan berikutnya. Jika sesi idle, respons menunggu sampai interaksi pengguna berikutnya. Pengecualian: hook asyncRewake yang keluar dengan kode 2 membangunkan Claude segera bahkan ketika sesi idle.
  • Setiap eksekusi membuat proses latar belakang terpisah. Tidak ada deduplikasi di seluruh beberapa penjalankan hook async yang sama.

Pertimbangan keamanan

Penafian

Command hooks dijalankan dengan izin pengguna sistem penuh Anda.
Command hooks menjalankan perintah shell dengan izin pengguna penuh Anda. Mereka dapat memodifikasi, menghapus, atau mengakses file apa pun yang dapat diakses akun pengguna Anda. Tinjau dan uji semua perintah hook sebelum menambahkannya ke konfigurasi Anda.

Praktik terbaik keamanan

Ingat praktik-praktik ini saat menulis hooks:
  • Validasi dan sanitasi input: jangan pernah mempercayai data input secara membabi buta
  • Selalu kutip variabel shell: gunakan "$VAR" bukan $VAR
  • Blokir path traversal: periksa .. dalam path file
  • Gunakan path absolut: tentukan path lengkap untuk skrip. 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. Hooks spawn PowerShell secara langsung, jadi ini bekerja terlepas dari apakah CLAUDE_CODE_USE_POWERSHELL_TOOL diatur. Claude Code auto-detects pwsh.exe (PowerShell 7+) dengan fallback ke powershell.exe (5.1).
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "shell": "powershell",
            "command": "Write-Host 'File written'"
          }
        ]
      }
    ]
  }
}

Debug hooks

Hook execution details, termasuk hooks mana yang cocok, kode keluar mereka, dan stdout dan stderr lengkap, ditulis ke file debug log. Mulai Claude Code dengan claude --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.
[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: <Your command> with timeout 600000ms
[DEBUG] Hook command completed with status 0: <Your stdout>
Untuk 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, infinite Stop hook loops, 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.