SSD Nodes Learn Hosting plans →
Panduan Matt ConnorOleh Matt Connor · Diperbarui 2026-08-29

Hook Claude Code: Event, Exit Code 2, dan Keamanan

Pahami hook Claude Code yang tetap berjalan tanpa persetujuan model, lokasi konfigurasinya, event pemicu, dampak exit code 2, serta risiko keamanannya.

Apa itu hook Claude Code

Hook Claude Code adalah perintah shell yang dijalankan Claude Code secara otomatis pada titik-titik tertentu dalam siklus hidupnya. Inilah perbedaan utama antara hook dan file aturan. Instruksi dalam CLAUDE.md adalah saran, dan model mempertimbangkannya bersama seluruh konteks lainnya. Hook adalah kode, dan kode tersebut tetap dijalankan terlepas dari apakah model menyetujuinya. Jika agent Anda terus melewati formatter yang sudah Anda sebutkan dua kali, Anda tidak memerlukan instruksi yang lebih tegas. Anda memerlukan hook.

Mekanismenya sederhana. Anda mendaftarkan sebuah perintah dalam file pengaturan di bawah nama event. Saat event tersebut terjadi, Claude Code menjalankan perintah Anda dan menulis data event ke standard input (stdin) dalam format JSON (JavaScript object notation). Perintah Anda membaca data tersebut, menjalankan tugasnya, lalu mengembalikan status keluar. Exit 2 dari hook PreToolUse membatalkan pemanggilan tool sebelum dijalankan, dan apa pun yang ditulis skrip Anda ke standard error (stderr) dikembalikan kepada model sebagai alasannya.

Nama event dan nama field di sini berasal dari referensi hooks Claude Code, yang diperiksa pada Agustus 2026 terhadap release 2.1.232. Antarmuka ini berubah dengan cepat, jadi periksa referensi untuk versi Anda sendiri sebelum menyalin JSON dari posting blog mana pun, termasuk yang ini. Tampilkan versi Anda dengan claude --version.

Lokasi konfigurasi hook

Hook adalah blok JSON dalam file pengaturan. Enam lokasi dapat memuat hook, dan cakupan file menentukan cakupan hook tersebut.

  • ~/.claude/settings.json: setiap project di mesin Anda, tetapi tidak di mesin orang lain.
  • .claude/settings.json: satu project, di-commit ke repository, sehingga setiap orang yang melakukan clone akan mendapatkan hook tersebut.
  • .claude/settings.local.json: satu project, hanya di mesin Anda.
  • Pengaturan kebijakan terkelola: berlaku di seluruh organisasi dan ditetapkan oleh administrator.
  • hooks/hooks.json di dalam plugin, aktif selama plugin tersebut diaktifkan.
  • Frontmatter skill atau subagent, aktif selama komponen tersebut aktif.

Entri hook dari file-file ini digabungkan, bukan saling menimpa. File pengaturan project menambahkan hook-nya ke hook dalam pengaturan pengguna, bukan menggantikannya. Karena itu, satu event dapat memuat beberapa hook dari beberapa file. Menetapkan "disableAllHooks": true akan menonaktifkannya, dengan satu pengecualian: hook dari pengaturan kebijakan terkelola tetap berjalan, kecuali pengaturan tersebut juga diterapkan dalam pengaturan terkelola.

Jalankan /hooks di dalam session untuk menampilkan semua hook yang sedang terdaftar, dikelompokkan berdasarkan event, beserta file sumber dan matcher masing-masing. Menu ini hanya dapat dibaca, sehingga Anda mengubah hook dengan mengedit file pengaturan. File watcher biasanya mendeteksi perubahan tersebut tanpa perlu restart.

Event hook Claude Code yang tersedia

Rilis 2.1.232 mencantumkan tiga puluh satu event, mulai dari SessionStart hingga SessionEnd. Event tersebut mencakup compaction, subagent, worktree, dan file konfigurasi. Pekerjaan administrasi server hanya menggunakan beberapa di antaranya.

  • PreToolUse: sebelum pemanggilan tool dijalankan. Event ini dapat memblokir.
  • PostToolUse: setelah pemanggilan tool berhasil. PostToolUseFailure dijalankan jika pemanggilan gagal. Jadi, hook yang harus melihat setiap hasil memerlukan keduanya.
  • PermissionRequest: ketika pemanggilan tool memerlukan keputusan izin, yaitu saat prompt persetujuan akan ditampilkan.
  • UserPromptSubmit: ketika Anda mengirimkan prompt, sebelum Claude memprosesnya. Apa pun yang dicetak hook ini ke stdout akan ditambahkan ke konteks model.
  • SessionStart dan SessionEnd: pada setiap akhir sesi. SessionStart juga dijalankan setelah compaction, dengan nilai matcher compact.
  • Stop: ketika Claude selesai merespons. Event ini dijalankan satu kali per turn, bukan satu kali per task yang selesai.

Setiap grup memiliki matcher yang menentukan occurrence mana yang menjalankan hook. Pada event tool, matcher memfilter berdasarkan nama tool. Jadi, "Edit|Write" dijalankan saat mengedit file dan tidak pada occurrence lain. Matcher peka huruf besar-kecil. Matcher kosong dijalankan pada setiap occurrence. Tool dari server MCP (model context protocol) diberi nama mcp__<server>__<tool>. Karena itu, matcher "mcp__github__.*" hanya menangkap tool dari satu server dan mengabaikan tool dari server lain.

Hook Stop memiliki hal penting yang perlu diketahui sebelum Anda menulisnya. Hook Stop yang memblokir akan mengirim model kembali untuk bekerja, dan Claude Code akan mengabaikan hook tersebut setelah delapan kali pemblokiran berturut-turut. Baca field stop_hook_active dari input hook dan keluar dengan status 0 jika nilainya true. Jika tidak, hook Anda akan berulang sampai mencapai batas tersebut.

Data yang diterima hook melalui stdin

Saat Claude akan menjalankan npm test, hook PreToolUse pada Bash membaca data berikut melalui stdin:

{
  "session_id": "abc123",
  "cwd": "/home/deploy/myproject",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test"
  }
}

Setiap event memuat session_id, cwd, permission_mode, transcript_path, dan hook_event_name. Event tool menambahkan tool_name, tool_input, dan tool_use_id. Event lain memuat field masing-masing: UserPromptSubmit mendapatkan teks prompt, sedangkan SessionStart mendapatkan source dari startup, resume, clear, compact, atau fork.

jq adalah cara umum untuk membaca data ini di dalam shell script, dan minimal server image tidak menyertakannya. Instal terlebih dahulu dengan sudo apt install -y jq pada Ubuntu dan Debian.

Dampak status keluar terhadap pemanggilan alat yang sedang berlangsung

Ada tiga hasil.

  • Exit 0 berarti hook Anda tidak mengajukan keberatan. Pada PreToolUse, hal itu tidak sama dengan persetujuan, dan alur izin normal tetap berjalan. Pada UserPromptSubmit dan SessionStart, stdout ditambahkan ke konteks model.
  • Exit 2 memblokir tindakan pada event yang dapat diblokir, termasuk PreToolUse, dan stderr menjadi alasan yang ditampilkan kepada model. Pada event yang tidak dapat diblokir, seperti PostToolUse, pemblokiran diabaikan, tetapi stderr tetap diteruskan kepada model sebagai umpan balik.
  • Kode exit lainnya merupakan error yang tidak memblokir. Tindakan tetap dilanjutkan. Transkrip menampilkan pemberitahuan error hook yang memuat baris pertama stderr setelah teks Failed with non-blocking status code:.

Untuk hasil selain memblokir atau tidak melakukan apa pun, gunakan exit 0 lalu cetak objek JSON ke stdout. Hook PreToolUse mengambil keputusan dengan permissionDecision:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Database drops go through a migration, not through the agent."
  }
}

"allow" melewati prompt interaktif, "deny" membatalkan pemanggilan dan mengirimkan alasannya kepada model, sedangkan "ask" menampilkan prompt seperti biasa. Pilih satu gaya untuk setiap hook. Mencampur exit 2 dengan keputusan JSON di stdout menghasilkan perilaku yang harus Anda telusuri sendiri.

Jika beberapa hook cocok dengan satu event, hook tersebut berjalan secara paralel dan semuanya berjalan hingga selesai. deny dari satu hook tidak menghentikan hook lain yang berjalan bersamanya, sehingga hook logging tetap menulis barisnya saat hook guardrail menolak pemanggilan yang sama. Claude Code kemudian menggabungkan semua jawaban dan mempertahankan jawaban yang paling ketat, dengan urutan deny, defer, ask, allow.

Contoh 1: blokir perintah destruktif sebelum dijalankan

Simpan ini sebagai .claude/hooks/block-destructive.sh di project Anda:

#!/bin/bash
# Deny a Bash tool call whose command matches a banned pattern.
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

for pattern in 'rm -rf /' 'mkfs' 'dd if=' 'DROP TABLE'; do
  if printf '%s' "$COMMAND" | grep -qiF -- "$pattern"; then
    echo "Blocked by policy: the command matches '$pattern'. A human runs this one." >&2
    exit 2
  fi
done

exit 0

Jadikan file tersebut executable, lalu daftarkan pada PreToolUse di .claude/settings.json:

chmod +x .claude/hooks/block-destructive.sh
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-destructive.sh",
            "timeout": 10,
            "statusMessage": "Checking the command against policy"
          }
        ]
      }
    ]
  }
}

Uji script secara manual sebelum mempercayainya, karena hook yang crash saat memproses inputnya sendiri akan gagal secara terbuka:

echo '{"tool_name":"Bash","tool_input":{"command":"rm -rf /var/lib/postgresql"}}' \
  | .claude/hooks/block-destructive.sh
echo $?

Anda akan melihat baris Blocked by policy: pada stderr dan exit code 2. Berikan perintah yang tidak berbahaya, seperti ls -la, dan Anda seharusnya tidak melihat output apa pun serta mendapatkan exit code 0. Dalam sebuah session, pemanggilan yang ditolak muncul dalam transcript dengan pesan Anda sebagai alasannya, lalu model membaca pesan tersebut dan menyesuaikan tindakannya.

Satu karakteristik membuat pendekatan ini layak digunakan: hook PreToolUse dijalankan sebelum pemeriksaan permission mode, dalam setiap permission mode. Karena itu, penolakan tetap berlaku bahkan dalam bypassPermissions. Inilah yang membuat hook berguna bersama mode otomatis Claude Code dan pengaturan permission-nya, ketika prompt dikurangi tetapi hook tetap dijalankan.

Pahami batasannya. Pencocokan pola pada string perintah merupakan guardrail terhadap agent yang bertindak ceroboh. Ini bukan batasan terhadap agent yang bertindak dengan sengaja menghindarinya, karena perintah yang sama dapat ditulis dalam bentuk yang tidak pernah dilihat oleh grep Anda. Aturan yang ketat harus diterapkan dalam sistem permission dan pada account yang digunakan untuk menjalankan process.

Contoh 2: format dan lint setelah setiap pengeditan

PostToolUse dengan matcher Edit|Write dijalankan setelah tool pengeditan file apa pun. Simpan konfigurasi ini sebagai .claude/hooks/after-edit.sh:

#!/bin/bash
# Format the edited file, then report lint failures back to the model.
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
[ -z "$FILE" ] && exit 0

case "$FILE" in
  *.py)
    ruff format "$FILE" >/dev/null 2>&1
    if ! ruff check "$FILE" >&2; then
      exit 2
    fi
    ;;
  *.sh)
    if ! shellcheck "$FILE" >&2; then
      exit 2
    fi
    ;;
esac

exit 0
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/after-edit.sh",
            "timeout": 60
          }
        ]
      }
    ]
  }
}

Minta Claude menambahkan fungsi dengan indentasi yang buruk ke file Python, lalu buka file tersebut. File akan muncul dalam format yang benar. Ini memastikan hook dijalankan, karena hook yang berhasil tidak menampilkan apa pun dalam percakapan.

Exit 2 di sini tidak membatalkan apa pun. PostToolUse dijalankan setelah tool selesai dieksekusi, sehingga hasil pengeditan tetap tersimpan di disk. Fungsi exit 2 adalah meneruskan output ruff check ke model sebagai umpan balik, sehingga model memperbaiki error yang baru saja dibuatnya, bukan melanjutkan ke langkah berikutnya. Inilah perbedaan antara kegagalan lint yang baru ditemukan saat commit dan kegagalan yang diperbaiki agent pada giliran yang sama.

Dua batasan matcher penting dalam kasus ini. Edit|Write tidak melihat file yang diubah oleh perintah shell, sedangkan Claude cukup sering menulis file melalui Bash sehingga celah ini nyata. Untuk cakupan setiap pemanggilan, cocokkan Bash juga dan minta script mencantumkan file yang berubah dengan git status --porcelain. Untuk cakupan satu kali per giliran, letakkan pemindaian dalam hook Stop.

Contoh 3: catat setiap pemanggilan tool untuk audit

Matcher kosong pada PostToolUse dipicu untuk setiap tool. Mengirim catatan ke system journal, bukan ke file di home directory, membuatnya tidak dapat diakses oleh shell milik agent:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "jq -c '{time: now|todate, session: .session_id, cwd: .cwd, tool: .tool_name, input: .tool_input}' | logger -t claude-code -p local0.info"
          }
        ]
      }
    ]
  }
}

Baca kembali catatan tersebut dengan journalctl -t claude-code -o cat | tail -n 5. Anda akan melihat satu baris JSON untuk setiap pemanggilan tool, dengan entri terbaru di baris terakhir. Jika tidak ada entri, hook tidak dijalankan. Bagian pemecahan masalah di bawah membahas hal ini.

Tambahkan blok yang sama di bawah PostToolUseFailure untuk mencatat pemanggilan yang gagal, karena PostToolUse hanya dipicu saat berhasil dan perintah yang gagal biasanya lebih penting untuk diperiksa. Alasan menggunakan logger, bukan menambahkan catatan ke file di home directory, adalah kepemilikan: hook dijalankan sebagai user yang sama dengan shell milik agent. Karena itu, apa pun yang dapat ditambahkan oleh user tersebut juga dapat dikosongkan olehnya. Journal ditulis oleh systemd-journald menggunakan account-nya sendiri.

Berapa lama hook dapat berjalan

ChartDefault hook timeout in seconds, by hook type and event
The data behind this chart
[
  {
    "label": "command, http or mcp_tool hook",
    "default_timeout_seconds": 600
  },
  {
    "label": "agent hook",
    "default_timeout_seconds": 60
  },
  {
    "label": "prompt hook",
    "default_timeout_seconds": 30
  },
  {
    "label": "command hook on UserPromptSubmit",
    "default_timeout_seconds": 30
  },
  {
    "label": "command hook on MessageDisplay",
    "default_timeout_seconds": 10
  },
  {
    "label": "any hook on SessionEnd",
    "default_timeout_seconds": 1.5
  }
]

Secara default, command hook memiliki waktu 600 detik, yaitu sepuluh menit. Beberapa event mengurangi waktu tersebut secara signifikan. Semua hook SessionEnd menggunakan anggaran waktu bersama sebesar 1.5 detik, sehingga pembersihan pada akhir sesi harus berlangsung cepat. Namun, dengan menetapkan timeout yang lebih lama pada hook, anggaran bersama tersebut dapat ditingkatkan hingga nilai yang sama, dengan batas maksimum 60 detik.

Hook yang mencapai batas waktunya akan dibatalkan dan tidak menghasilkan keputusan. Untuk PreToolUse guardrail, ini berarti hook tersebut tidak memblokir proses: pemanggilan tool tetap dilanjutkan ke alur izin normal. Karena itu, buat skrip guardrail tetap kecil. Untuk pekerjaan lambat yang tidak perlu ditunggu, seperti mengirim log ke lokasi lain, tetapkan "async": true agar hook berjalan di latar belakang tanpa menunda pemanggilan tool.

Hook, file aturan, skill, dan server MCP

Empat hal ini sering tertukar karena semuanya mengubah perilaku agen. Hanya satu yang tidak lagi bersifat saran.

File aturan (CLAUDE.md, atau file di bawah .claude/rules/) adalah teks yang dimuat ke dalam konteks model. File ini membentuk perilaku, tetapi tidak menegakkan apa pun. Dalam percakapan panjang, diff besar, dan permintaan pengguna baru, satu baris di dalamnya dapat terabaikan. Inilah mekanisme umum di balik agen yang mengabaikan instruksi yang Anda tulis.

Skill adalah folder berisi instruksi dan skrip yang dimuat model ketika model menilai skill tersebut relevan. Penilaian itu merupakan tujuan skill, sekaligus batasannya: model tetap mengambil keputusan. Anda dapat melihat kedua sisi ini pada skill seperti Ponytail, yang mengarahkan agen untuk menerapkan perubahan terkecil yang berfungsi, karena skill tersebut membentuk cara seluruh tugas dikerjakan dengan cara yang tidak dapat dilakukan hook, dan hanya selama model memilih untuk memuatnya.

Server MCP (model context protocol) memberi model alat baru untuk dipanggil. Server ini memperluas hal-hal yang dapat dijangkau agen. Server ini tidak membuat agen menggunakan alat apa pun, dan merupakan proses terpisah yang harus Anda operasikan. Pengoperasian tersebut merupakan tugas tersendiri. Lihat menjalankan server MCP pada VPS.

Hook adalah satu-satunya dari keempat hal ini yang berjalan tanpa dipilih oleh model. Gunakan file aturan untuk preferensi dan skill untuk prosedur yang harus diikuti model ketika prosedur tersebut berlaku. Gunakan hook untuk langkah yang harus dijalankan setiap kali, atau untuk hal yang tidak boleh terjadi. Perbandingan yang lebih mendalam, termasuk kondisi ketika skill lebih tepat daripada file aturan, tersedia di perbandingan skill, MCP, dan file aturan.

Plugin adalah mekanisme pengemasan, bukan mekanisme kelima. Plugin menggabungkan hook dan skill menjadi satu unit yang dapat diinstal. Dengan cara ini, tim dapat menerapkan guardrail yang sama pada setiap mesin. Lihat cara kerja plugin Claude Code.

Keputusan keamanan pada VPS bersama

Hook adalah kode yang dipicu oleh agent, dan kode tersebut berjalan sebagai user yang memulai Claude Code. Hook mewarisi environment serta izin file user tersebut. Pada laptop, ini merupakan persoalan alur kerja. Pada VPS tempat agent berjalan tanpa pengawasan, ini merupakan persoalan keamanan dengan empat aspek praktis.

Hook dalam repository adalah kode yang tidak Anda tulis. .claude/settings.json di-commit, sehingga melakukan clone repository lalu memulai session di dalamnya dapat mendaftarkan hook yang disertakan bersama repository. Claude Code menempatkan hook proyek di balik dialog kepercayaan workspace untuk folder tersebut. Artinya, menerima kepercayaan adalah saat Anda memutuskan untuk menjalankan hook tersebut. Baca blok hooks terlebih dahulu.

Hook melihat seluruh input tool. Hook audit yang mencatat tool_input menulis setiap argumen dari setiap command ke dalam file, termasuk token apa pun yang kebetulan berada pada command line. Log tersebut kemudian memerlukan perlindungan yang sama seperti secret itu sendiri. Ini merupakan bagian dari persoalan yang lebih luas, yaitu menjauhkan secret dari jangkauan agent AI.

Hook dapat menulis ke dalam context model. Apa pun yang dicetak oleh hook SessionStart atau UserPromptSubmit ke stdout akan ditambahkan ke percakapan. Hook yang memasukkan teks dari luar, issue tracker, atau file log melalui pipe sedang memberikan teks yang tidak tepercaya kepada model seolah-olah Anda sendiri yang mengetikkannya. Hook yang meneruskan catatan dari session Claude Code lain pada VPS yang sama melakukan hal yang sama. Output dari satu agent tidak lebih layak dipercaya daripada output issue tracker. Perlakukan stdout tersebut sebagai input, bukan output.

Privilege adalah kontrol yang sebenarnya. Jalankan agent sebagai user khusus tanpa privilege, dengan hanya aturan sudo yang diperlukan. Deny PreToolUse tetap berguna, tetapi mekanisme ini memang dirancang sebagai upaya terbaik: dokumentasi referensi menyatakan hal yang sama tentang filter if dan mengarahkan Anda untuk menggunakan sistem izin ketika memerlukan deny yang benar-benar diterapkan. Aturan izin dan account tempat proses berjalan adalah bagian yang tetap berlaku saat kondisi menjadi kritis.

Satu properti berlaku pada setiap konfigurasi. Hook PreToolUse dijalankan sebelum pemeriksaan permission mode dalam setiap permission mode. Karena itu, hook yang mengembalikan deny akan memblokir tool, bahkan dalam bypassPermissions. Hook dapat memperketat hal yang diizinkan oleh aturan izin. Hook tidak dapat melonggarkannya.

Mengapa hook saya tidak dijalankan?

Ikuti langkah-langkah berikut secara berurutan. Setiap langkah menyebutkan gejala yang akan Anda lihat.

  • Jalankan /hooks dan periksa apakah hook muncul di bawah event yang Anda harapkan. Hook yang tidak muncul di menu biasanya berarti file pengaturan memiliki kesalahan sintaks JSON, karena koma di akhir dan komentar tidak diizinkan, atau file tersebut tidak berada di salah satu dari enam lokasi di atas.
  • Bandingkan matcher dengan nama tool secara persis. Matcher peka huruf besar-kecil, sehingga "bash" tidak pernah cocok dengan tool Bash.
  • Jalankan skrip secara manual dengan input contoh, seperti pada contoh 1 di atas. Kode keluar yang tidak sesuai harapan menunjukkan bug dalam skrip Anda. Claude Code melaporkannya sebagai error hook, bukan sebagai keputusan.
  • Notifikasi yang berbunyi jq: command not found berarti jq tidak tersedia pada mesin tersebut. command not found untuk skrip Anda sendiri berarti path tidak dapat ditemukan, jadi gunakan ${CLAUDE_PROJECT_DIR} atau path absolut. Jika skrip sama sekali tidak pernah dijalankan, kemungkinan skrip tersebut tidak memiliki izin eksekusi.
  • Hook mencetak JSON yang valid, tetapi tidak terjadi apa-apa. Hook dalam bentuk shell dijalankan melalui sh -c. Jika profil shell Anda mencetak banner, banner tersebut ditambahkan di depan JSON. Output standar tidak lagi diawali dengan {, sehingga Claude Code membaca seluruhnya sebagai teks biasa dan mengabaikan keputusan tersebut. Jika keluar dengan kode 0, tidak ada laporan di mana pun selain di log debug. Bungkus setiap echo dalam profil Anda agar hanya dijalankan pada shell interaktif.
  • Masih mengalami masalah? Mulai sesi dengan claude --debug-file /tmp/claude.log, lalu jalankan tail -f /tmp/claude.log di terminal kedua. Log debug mencatat hook yang cocok, kode keluar yang dikembalikan masing-masing hook, serta semua output yang ditulis ke output standar dan error standar.

FAQ

Apa perbedaan antara hook Claude Code dan instruksi CLAUDE.md?

Instruksi CLAUDE.md adalah teks dalam konteks model. Instruksi tersebut bersaing untuk mendapatkan perhatian bersama percakapan dan permintaan saat ini, lalu model dapat mempertimbangkannya terhadap keduanya. Hook adalah perintah shell yang dijalankan Claude Code pada titik tetap dalam siklus hidupnya. Dengan demikian, hook dijalankan setiap kali event tersebut terjadi, terlepas dari keputusan model. Gunakan instruksi untuk preferensi. Gunakan hook untuk langkah yang harus selalu dijalankan atau tindakan yang tidak boleh pernah dijalankan.

Bagaimana cara menghentikan Claude Code agar tidak menjalankan perintah shell tertentu?

Daftarkan hook PreToolUse dengan matcher Bash yang membaca perintah dari .tool_input.command, menulis alasan ke stderr, lalu keluar dengan kode 2. Claude Code membatalkan pemanggilan tersebut dan menampilkan alasan Anda kepada model. Proses ini terjadi sebelum pemeriksaan mode izin, sehingga penolakan tetap berlaku bahkan dalam mode bypassPermissions. Pencocokan pola pada string perintah adalah pengaman, bukan batas keamanan, karena perintah yang sama dapat ditulis dalam bentuk yang tidak cocok dengan pola. Karena itu, lengkapi dengan aturan izin dan akun tanpa hak istimewa.

Hook saya mencetak JSON yang valid, tetapi tidak terjadi apa-apa. Mengapa?

Penyebab yang paling umum adalah profil shell Anda. Hook tanpa field args dijalankan melalui sh -c, dan beberapa profil mencetak banner setiap kali shell dijalankan. Banner tersebut masuk ke stdout sebelum JSON Anda. Karena output tidak lagi diawali {, Claude Code memperlakukan seluruh output sebagai teks biasa dan mengabaikan keputusan tersebut. Jika proses keluar dengan kode 0, tidak ada apa pun yang dilaporkan dalam transkrip. Lindungi setiap echo dalam profil Anda dengan pemeriksaan interactive shell, lalu pastikan perbaikannya dengan membaca debug log dari claude --debug-file /tmp/claude.log.

Apakah aman menjalankan hook Claude Code pada server bersama?

Hook dijalankan sebagai user yang memulai Claude Code, dengan izin file milik user tersebut. Karena itu, hook dapat melakukan apa pun yang dapat dilakukan akun tersebut. Dua kebiasaan dapat mengatasi sebagian besar risiko: jalankan agent sebagai akun khusus tanpa hak istimewa dengan kebijakan sudo yang terbatas, lalu baca blok hooks dari setiap repository sebelum menerima dialog kepercayaan workspace karena project hook disertakan dalam .claude/settings.json. Tetapkan "disableAllHooks": true dalam file pengaturan jika Anda tidak ingin hook tersebut dijalankan.