Blog

Hooks di Claude Code 2026: penyiapan langkah demi langkah dan 7 contoh siap pakai

Markdown

7 hooks siap pakai untuk .claude/settings.json, siap disalin dan ditempel: perlindungan .env, prettier, force-push ke main, tanda pisah panjang, dan server dev. 4 jenis handler dan 31 event.

Baca artikel lengkap

Hooks di Claude Code adalah perintah shell, panggilan HTTP, atau pemeriksaan LLM singkat yang berjalan otomatis sebelum atau sesudah tindakan Claude. Hook menambahkan pemeriksaan pada peristiwa alat tertentu, memformat kode setelah edit, dan menyisipkan konteks baru saat sesi dimulai. Hook PreToolUse yang cocok dapat menolak pemanggilan itu; contoh regex ini merupakan pengaman praktis, bukan batas keamanan untuk setiap jalur akses file atau Git. Berikut uraian 4 jenis handler, 31 event siklus hidup, tes untuk “kapan aturan sebaiknya menjadi hook,” 7 hook siap salin untuk .claude/settings.json, dan 3 kendala yang pernah saya alami sendiri.

Dokumentasi Ailoot: API, kunci, dan penyiapan.

Apa itu hooks di Claude Code?

Poin utama

Hook adalah perintah shell, panggilan HTTP, prompt LLM singkat, atau subagen yang dijalankan Claude Code sebagai respons terhadap event siklus hidup tertentu. Tidak seperti instruksi di CLAUDE.md, yang mungkin diabaikan atau dilupakan oleh model, hook selalu berjalan dengan cara yang sama: hook adalah kode, bukan saran.

Selama setengah tahun, saya menyimpan aturan “jangan pernah mengedit .env secara langsung” di CLAUDE.md. Claude melanggarnya sebulan sekali. Bukan dengan sengaja; aturan itu hanya terlupakan di akhir sesi yang panjang. Suatu kali, token tambahan masuk ke .env; di lain waktu, variabel dengan nama duplikat menimpa variabel yang sedang digunakan.

Saya memindahkan aturan itu ke hook sehingga mustahil untuk dilupakan. Ketika alat edit langsung yang cocok menyediakan jalur .env, hook terkonfigurasi menolak pemanggilan itu. Pengalihan Bash, skrip, alat MCP, serta jalur akses lain memerlukan kontrol terpisah; contoh ini tidak membuat file mustahil diubah.

Hooks adalah perintah shell, endpoint HTTP, atau prompt LLM yang Anda tentukan dan berjalan otomatis pada titik tertentu dalam siklus hidup Claude Code.

— Anthropic, https://code.claude.com/docs/en/hooks

Dengan kata lain, hook adalah perintah Anda yang dijalankan Claude Code saat terjadi event tertentu. Setiap pengeditan file, peluncuran Bash, awal sesi, atau penyelesaian subagen adalah event yang dapat dipasangi hook.

Mengapa ini penting bagi vibe coder:

  • Lindungi file yang berisi rahasia dari pengeditan yang tidak disengaja.
  • Jalankan prettier/eslint secara otomatis setelah setiap pembuatan kode.
  • Blokir git push --force ke main tanpa izin eksplisit.
  • Sisipkan konteks baru pada awal setiap sesi.
  • Catat tindakan Claude untuk keperluan audit.

CLAUDE.md menangani konteks. Hooks menangani tindakan. Keduanya berbeda, dan mencampuradukkannya adalah kesalahan utama pemula.

Apa perbedaan hooks dengan CLAUDE.md dan skills?

Poin utama

CLAUDE.md adalah teks yang dibaca model pada awal sesi dan berusaha diikuti. Hook adalah perintah shell yang dijalankan proses Claude Code sebagai respons terhadap suatu event, terlepas dari model. Skill adalah paket instruksi bernama yang dapat dipanggil Claude dengan alat Skill, seperti sebuah fungsi. Ketiganya menangani jenis tugas yang berbeda.

Dokumentasi Anthropic menjelaskan secara eksplisit perbedaan antara CLAUDE.md dan hooks:

Konten CLAUDE.md disampaikan sebagai pesan pengguna setelah prompt sistem, bukan sebagai bagian dari prompt sistem. Claude membacanya dan berusaha mengikutinya, tetapi tidak ada jaminan kepatuhan yang ketat.

— Anthropic, dokumentasi memori, https://code.claude.com/docs/en/memory

Dengan kata lain: konten CLAUDE.md sampai kepada Claude sebagai pesan pertama dari pengguna, bukan sebagai instruksi sistem. Model akan membacanya dan berusaha mengikutinya. Namun tidak ada jaminan, terutama menjelang akhir sesi panjang, ketika konteks membesar dan instruksi lama terdorong keluar.

Hook bekerja dengan cara berbeda. Hook berada di settings.json, tidak bergantung pada model, dan dijalankan oleh proses Claude Code saat event tertentu terjadi: PreToolUse, PostToolUse, SessionStart, dan lainnya. Perintah berjalan sebelum Claude menerima hasilnya dan dapat memblokir tindakan secara langsung (exit 2) atau mengubah file setelah diedit (exit 0).

Perbandingan:

FiturCLAUDE.mdHookSkill
LokasiCLAUDE.md di root proyek.claude/settings.jsonFolder .claude/skills/
Yang menjalankannyaModel membacanya sebagai bagian dari pesan penggunaProses Claude Code saat suatu event terjadiModel melalui alat Skill
Jaminan eksekusiTidak (upaya terbaik)Hook perintah terkonfigurasi yang cocok menjalankan kode; kegagalan serta perilaku keluar bergantung pada peristiwaMelalui panggilan model (dengan pemberian izin, tetapi bukan pada tingkat shell)
KegunaanKonteks, nada, gaya, dan perutean proyekPembatasan, pemformatan, pemeriksaan pada file tertentuProsedur kompleks multilangkah dengan parameter
Waktu berjalanSetiap pesan model dalam sesiSaat event siklus hidup terjadiSaat dipanggil oleh model
Dapat memblokir tindakan?TidakYa (exit 2)Tidak

Template CLAUDE.md lengkap dan 6 aturan untuk file yang terus diperbarui ada di panduan Cara mengonfigurasi CLAUDE.md pada 2026: template siap pakai dan 6 aturan. Pelajari skills sebagai kapabilitas bernama di Claude Code Skills: Cara membangun pustaka Anda sendiri.

Hook adalah lapisan paling rendah: tanpa model, tanpa interpretasi, hanya shell.

Hooks adalah salah satu alat rekayasa konteks, bersama Plan Mode dan /compact.

4 jenis hook: Command, HTTP, Prompt, Agent—mana yang sebaiknya dipilih?

Poin utama

Claude Code memiliki 4 jenis handler. Command adalah skrip shell dan jenis yang paling umum. HTTP mengirim permintaan POST ke URL Anda, untuk tim dan integrasi. Prompt membuat panggilan Haiku singkat untuk pemeriksaan semantik. Agent membuat subagen dengan akses alat untuk validasi mendalam. Pemula memerlukan Command dan Prompt; keduanya mencakup 90% tugas.

JenisFungsinyaKapan dipilihKompleksitas
CommandMenjalankan perintah shell, menerima JSON melalui stdin, dan merespons dengan kode keluar serta stdout90% kasus: memeriksa path, memformat, grep pada konten, lsof, operasi gitrendah
HTTPMengirim permintaan POST dengan isi JSON ke URL Anda dan menerima JSON sebagai responsSaat aturan berlaku untuk tim/CI: validasi terpusat, dasbor status bersama, audit jarak jauhsedang (memerlukan server)
PromptMengirim prompt singkat ke Haiku (secara default) dan memperoleh jawaban ya/tidak untuk penilaian semantikSaat grep tidak cukup: “nilai apakah teks ini terdengar korporat”rendah
AgentMembuat subagen dengan akses Read/Grep/Glob untuk pemeriksaan multilangkahValidasi berat: “temukan semua file yang diubah dan periksa apakah tes yang sesuai tersedia”tinggi

Kesalahan paling umum adalah mencoba memakai Command untuk tugas yang seharusnya menggunakan Prompt. Misalnya, “jangan gunakan tanda pisah panjang dalam file .md” adalah tugas grep, jadi Command ideal. Namun, “jangan menulis dengan nada korporat” bersifat semantik sehingga memerlukan Prompt.

Jika Anda baru memulai, gunakan Command. 7 hook siap pakai di bawah ini semuanya menggunakan Command. HTTP/Prompt/Agent bisa dipakai nanti, saat Anda siap.

Di mana settings.json berada, dan apa 4 tingkat konfigurasinya?

Poin utama

Claude Code mencari konfigurasi di 4 tempat: enterprise (diterapkan melalui MDM), pengguna (~/.claude/settings.json), proyek (.claude/settings.json di root repositori), dan lokal (.claude/settings.local.json, tidak disimpan di git). Keempat file digabungkan saat sesi dimulai, dengan tingkat yang lebih rendah diprioritaskan.

TingkatPathCakupanDi git?
Enterprise/Library/Application Support/ClaudeCode/settings.json (Mac), /etc/claude-code/settings.json (Linux)Untuk seluruh tim melalui MDMtidak
Pengguna~/.claude/settings.jsonSemua proyek untuk satu penggunatidak
Proyek.claude/settings.json di root repositoriProyek ini, untuk seluruh timya
Lokal.claude/settings.local.jsonHanya proyek ini, hanya pengguna initidak (di .gitignore)

Saya menyimpan 7 hook yang dijelaskan di bawah dalam .claude/settings.json proyek, sehingga hook tersebut otomatis disertakan saat repositori dikloning dan Claude mengikuti aturan yang sama di jendela baru. Hook eksperimental pribadi disimpan di .claude/settings.local.json agar tidak mengganggu tim.

Keempat file digabungkan saat sesi dimulai. Jika aturan bertentangan, tingkat yang lebih rendah diprioritaskan (lokal di atas proyek, proyek di atas pengguna).

Cara tercepat membuat hook pertama Anda adalah menu interaktif /hooks langsung di Claude Code. Pilih event dari daftar, pilih matcher (misalnya, Bash atau Edit), lalu tempel perintah. Claude Code akan menulis JSON yang valid ke settings.json.

31 event siklus hidup: mana yang diperlukan pemula?

Poin utama

Claude Code saat ini memiliki 31 event siklus hidup yang terbagi dalam 3 frekuensi: sekali per sesi (SessionStart/SessionEnd), sekali per giliran (UserPromptSubmit/Stop), dan pada setiap pemanggilan alat (PreToolUse/PostToolUse). Pemula memerlukan 4 dari 31 event: PreToolUse untuk pembatasan, PostToolUse untuk pengeditan otomatis, SessionStart untuk konteks, dan Stop untuk pemeriksaan akhir.

Event terjadi pada tiga frekuensi: sekali per sesi (SessionStart, SessionEnd), sekali per giliran (UserPromptSubmit, Stop, StopFailure), dan pada setiap pemanggilan alat di dalam loop agentik (PreToolUse, PostToolUse).

— Anthropic, referensi hooks, https://code.claude.com/docs/en/hooks

Penting untuk memahami ketiga frekuensi ini agar tidak bingung:

  1. Sekali per sesi - SessionStart, SessionEnd, Setup. Event ini berjalan sekali saat Claude Code dibuka dan ditutup. Gunakan untuk menyiapkan konteks dan membersihkan file sementara.
  2. Sekali per giliran - UserPromptSubmit, UserPromptExpansion, Stop, StopFailure. Event ini terpicu pada setiap pesan Anda dan setiap kali model berhenti. Gunakan untuk mencatat percakapan dan melakukan pemeriksaan hasil akhir.
  3. Pada setiap pemanggilan alat - PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied, PostToolBatch, SubagentStart, SubagentStop. Event ini terpicu pada setiap pengeditan file, peluncuran Bash, pembacaan, atau pencarian. Inilah lapisan yang paling sering aktif, tempat pembatasan, pemformatan, dan validasi dilakukan.

Daftar lengkap 31 event mencakup event asinkron/reaktif: TeammateIdle, PreCompact, PostCompact, Elicitation, ElicitationResult, InstructionsLoaded, ConfigChange, CwdChanged, FileChanged, WorktreeCreate, WorktreeRemove, Notification. Event ini untuk skenario lanjutan; lewati dulu.

Tes lima pertanyaan: kapan sebuah aturan sebaiknya menjadi hook, dan kapan tetap di CLAUDE.md?

Poin utama

Jika aturan berkaitan dengan tindakan (bukan konteks), menyangkut keselamatan/uang/data, dapat diperiksa dengan grep/regex, sulit diperbaiki jika dilanggar, dan Claude pernah melanggarnya setidaknya sekali, aturan itu sebaiknya menjadi hook. Jika kurang dari tiga dari lima jawaban adalah “ya,” aturan itu tetap di CLAUDE.md. Saya menerapkan tes ini pada 80 baris aturan saya, dan memindahkan 5 aturan ke hooks.

Bagi saya, ini berlaku seperti hukum, bukan saran. Saya menyederhanakan pilihan antara CLAUDE.md dan hook menjadi tes lima pertanyaan. Jika setidaknya tiga jawabannya “ya,” aturan itu menjadi hook. Jika kurang dari tiga, aturan itu tetap dalam teks.

Tes “kapan aturan sebaiknya menjadi hook?”:

  1. Apakah aturan ini berkaitan dengan tindakan, bukan konteks? (“jangan jalankan rm -rf” adalah tindakan; “Saya punya proyek TypeScript” adalah konteks.)
  2. Apakah aturan ini berkaitan dengan keselamatan, uang, atau data? (“jangan sentuh .env,” “jangan publikasikan kunci.”)
  3. Dapatkah Anda memeriksanya dengan grep, regex, atau kode keluar perintah shell? (“setelah mengedit .md, periksa bahwa tidak ada tanda pisah panjang.”)
  4. Jika aturan dilanggar, apakah akibatnya akan sulit diperbaiki? (“force push ke main” sulit diperbaiki; “lupa titik di akhir” mudah.)
  5. Apakah Anda pernah melihat Claude melupakan aturan ini setidaknya sekali?

Contoh: “Jangan gunakan tanda pisah panjang; gunakan hanya tanda hubung.” Tindakan? Ya. Keselamatan? Tidak. Dapat diperiksa dengan grep? Ya. Sulit diperbaiki? Tidak. Pernah dilanggar? Ya. 3 dari 5: jadikan hook.

Contoh tandingan: “Saya punya vibe coder, bukan developer; tulislah tanpa bahasa slang.” Tindakan? Lebih mirip konteks. Keselamatan? Tidak. Dapat diperiksa dengan regex? Terlalu sulit. Sulit diperbaiki? Tidak. Pernah dilanggar? Ya. 1 dari 5: tetap simpan di CLAUDE.md.

Lima aturan saya lolos tes. Aturan-aturan itu dipindahkan ke hooks. 75% sisanya tetap di file teks.

7 hook siap salin

Poin utama

Berikut 7 hook siap pakai untuk .claude/settings.json: blokir pengeditan .env, jalankan prettier secara otomatis, cegah force-push ke main, periksa tanda pisah panjang, kosongkan port sebelum npm run dev, buat commit otomatis setelah tes berhasil, dan sisipkan konteks baru pada awal sesi. Salin masing-masing ke file, mulai ulang Claude Code, dan hook akan berfungsi.

Ketujuh hook disimpan dalam satu file .claude/settings.json di root proyek. Setiap entri memiliki jenis event, filter alat (matcher), dan perintah shell. Kode keluar 2 menolak pemanggilan PreToolUse yang cocok sebelum eksekusi. Pada PostToolUse, tindakan sudah terjadi: hook melaporkan umpan balik dan tidak dapat membatalkannya.

Untuk sebagian besar event hook, hanya kode keluar 2 yang memblokir tindakan. Claude Code menganggap kode keluar 1 sebagai kesalahan yang tidak memblokir dan tetap melanjutkan tindakan, meskipun dalam Unix angka 1 secara konvensional berarti gagal.

— Anthropic, referensi hooks, https://code.claude.com/docs/en/hooks

exit 0 berarti berhasil. exit 1 berarti ada kesalahan, tetapi Claude tetap melanjutkan (ini antipola pertama di bawah). exit 2 memiliki perilaku khusus peristiwa: dapat memblokir PreToolUse, sedangkan PostToolUse hanya melaporkan umpan balik tentang tindakan yang sudah terjadi.

Hook 1. Jaga edit langsung .env (PreToolUse, exit 2)

Memblokir upaya untuk mengedit secara langsung file apa pun yang cocok dengan .env, .env.local, .env.production, dan seterusnya.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(jq -r '.tool_input.file_path // empty'); [ -z \"$FILE\" ] && exit 0; echo \"$FILE\" | grep -qE '(^|/)\\.env(\\.|$)' && { echo 'Do not edit `.env` directly. Tell me which variable to add, and I’ll open the file myself.' >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}

Fungsinya: Sebelum setiap Edit/Write/MultiEdit, hook mengambil path file, memeriksa dengan grep apakah path itu cocok dengan pola .env, lalu mengembalikan exit 2 beserta pesan jika cocok. Claude melihat stderr dan memberi tahu saya: “Saya mencoba mengedit .env, tetapi hook memblokirnya; beri tahu saya variabel mana yang perlu ditambahkan.”

Hook 2. Jalankan prettier secara otomatis setelah setiap pengeditan kode (PostToolUse, exit 0)

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(jq -r '.tool_input.file_path // empty'); [ -z \"$FILE\" ] && exit 0; echo \"$FILE\" | grep -qE '\\.(ts|tsx|js|jsx|json)$' && npx prettier --write \"$FILE\" 2>/dev/null & exit 0"
          }
        ]
      }
    ]
  }
}

Fungsinya: Setelah setiap Edit/Write, hook mengambil path dan, jika ekstensinya .ts/.tsx/.js/.jsx/.json, menjalankan prettier di latar belakang dengan &. Hook selalu keluar dengan kode 0 karena ini pemformatan, bukan pembatasan. Jika prettier gagal, tidak masalah; file tetap seperti semula.

Perbedaan hook 1 sangat mendasar: PreToolUse + exit 2 adalah pemblokiran keras sebelum tindakan. PostToolUse + exit 0 adalah pengeditan lunak setelah tindakan. Keduanya adalah dua mode berbeda untuk tugas yang berbeda.

Hook 3. Blokir git push --force ke cabang main (PreToolUse, exit 2)

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "CMD=$(jq -r '.tool_input.command // empty'); [ -z \"$CMD\" ] && exit 0; echo \"$CMD\" | grep -qE 'git[[:space:]]+push' && echo \"$CMD\" | grep -qE '(\\-\\-force|\\-\\-force-with-lease|[[:space:]]-f([[:space:]]|$))' && echo \"$CMD\" | grep -qE '([[:space:]/:])main([[:space:]/:]|$)' && { echo 'Force push to main is forbidden. Create a new branch or ask me to do it manually.' >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}

Fungsinya: Setiap kali Bash diluncurkan, hook memeriksa apakah perintah tersebut memuat push, memuat --force/--force-with-lease/-f, dan menyebut main. Jika ketiga kondisi cocok, hook memblokirnya dengan pesan yang jelas.

Dulu aturan ini saya tulis dengan huruf kapital semua di CLAUDE.md. Claude tetap melanggarnya kira-kira sekali setiap dua minggu saat saya memintanya melakukan “force push untuk membatalkan commit buruk saya.” Hook ini menangkap bentuk perintah literal yang ditampilkan, tetapi alias, skrip, cabang tujuan implisit, atau klien Git lain dapat melewati pemeriksaan string itu. Tegakkan perlindungan cabang main pada remote dan batasi kredensial yang tersedia bagi agen.

Hook 4. Blokir tanda pisah panjang di file teks (PostToolUse, exit 2)

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(jq -r '.tool_input.file_path // empty'); [ -z \"$FILE\" ] && exit 0; echo \"$FILE\" | grep -qE '\\.(md|mdx|tsx?)$' && grep -q $'\\xe2\\x80\\x94' \"$FILE\" && { echo \"File $FILE contains an em dash (U+2014). Replace it with a regular hyphen (U+002D).\" >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}

Fungsinya: PostToolUse dengan exit 2 melaporkan kegagalan pemeriksaan kepada Claude setelah edit. Hook tidak membatalkan atau mencegah penulisan. Jika Claude mengedit file tetapi masih menyisakan tanda pisah panjang, hook gagal, Claude menerima umpan balik, lalu mencoba lagi. Setelah 2–3 sesi, Claude berhenti menyisipkan karakter ini.

Perhatikan bahwa grep secara spesifik mencari karakter tanda pisah panjang (U+2014) menggunakan $'\xe2\x80\x94', notasi Bash untuk menuliskannya sebagai byte UTF-8. Jika Anda hanya menulis grep -q '-', perintah itu akan menangkap tanda hubung biasa dan berjalan pada setiap file. Satu karakter tak terlihat dapat merusak seluruh logika.

Hook 5. Kosongkan port 3000 sebelum npm run dev (PreToolUse, exit 0)

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "CMD=$(jq -r '.tool_input.command // empty'); [ -z \"$CMD\" ] && exit 0; echo \"$CMD\" | grep -qE '(npm|pnpm|yarn)[[:space:]]+run[[:space:]]+dev' && { lsof -ti:3000 | xargs -r kill -9 2>/dev/null; echo 'Port 3000 freed before launch' >&2; }; exit 0"
          }
        ]
      }
    ]
  }
}

Fungsinya: Sebelum setiap npm run dev (atau pnpm/yarn), hook menghentikan proses pada port 3000. Hook selalu keluar dengan kode 0 karena ini persiapan, bukan pembatasan. Ini mencegah jenis jebakan ketika Claude melihat “port sedang digunakan,” mengira ada masalah kode, lalu mulai memperbaiki sesuatu, padahal server lama hanya dibiarkan berjalan.

Hook 6. Buat commit otomatis setelah tes berhasil (PostToolUse, exit 0)

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "INPUT=$(cat); CMD=$(printf '%s' \"$INPUT\" | jq -r '.tool_input.command // empty') || exit 1; EXIT=$(printf '%s' \"$INPUT\" | jq -r '.tool_response.exit_code // empty') || exit 1; case \"$CMD\" in 'npm test'|'pnpm test'|'yarn test') ;; *) exit 0 ;; esac; [ \"$EXIT\" = \"0\" ] || exit 0; git diff --cached --quiet || { echo 'Existing staged changes: review manually' >&2; exit 0; }; git add -- src/orders.ts tests/orders.test.ts || exit 1; git diff --cached --quiet && exit 0; git commit -m 'tests: checkpoint after successful run'"
          }
        ]
      }
    ]
  }
}

Fungsinya: Membaca JSON stdin sekali dan menerima hanya perintah pengujian biasa yang tepat seperti ditampilkan. Proses berlanjut hanya ketika tool_response.exit_code yang diberikan secara eksplisit nol; jika versi terpasang tidak menyediakan field itu, hook melewati commit alih-alih menebak keberhasilan. Ganti src/orders.ts dan tests/orders.test.ts dengan file tepat yang dimiliki tugas. Perubahan yang sudah di-stage menghentikan resep agar pekerjaan tidak terkait tidak ikut masuk. Add gagal menghentikannya, diff kosong tidak membuat commit, dan hook commit biasa tetap aktif. Uji masukan berhasil, gagal, serta hasil hilang secara offline sebelum mengaktifkan.

Antipola: menambahkan git commit --no-verify pada setiap Edit. Pertahankan pemeriksaan commit. Jika pemeriksaan memicu otomatisasi ini secara rekursif, persempit pemicu atau desain ulang pemeriksaan; jangan melewati verifikasi secara default. Tinjau diff yang di-stage dan kepemilikan tugas sebelum mengaktifkan commit otomatis.

Hook 7. Sisipkan konteks baru saat sesi dimulai (SessionStart dengan additionalContext)

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo 'no-branch'); STATUS=$(git status --short 2>/dev/null | head -10); echo \"{\\\"additionalContext\\\":\\\"Current branch: $BRANCH. Uncommitted changes:\\n$STATUS\\\"}\""
          }
        ]
      }
    ]
  }
}

Fungsinya: Saat sesi dimulai, hook mengumpulkan cabang Git saat ini dan daftar perubahan yang belum di-commit, lalu mengembalikan JSON dengan field additionalContext. Claude Code menyisipkan teks ini ke pesan sistem pertama, sehingga Claude langsung mengetahui lokasinya dan pekerjaan yang tersisa dari sesi sebelumnya.

Gunakan additionalContext untuk informasi yang perlu diketahui Claude tentang keadaan lingkungan saat ini atau operasi yang baru selesai: keadaan lingkungan (cabang saat ini, target deployment, feature flag aktif), aturan proyek bersyarat (perintah tes yang berlaku untuk file yang baru diedit), data eksternal (isu terbuka yang ditugaskan kepada Anda, hasil CI terbaru).

— Anthropic, referensi hooks, https://code.claude.com/docs/en/hooks

Pengembangan hook ini: ambil commit terbaru beserta deskripsinya, status GitHub Actions, sprint aktif dari Linear/Jira, dan isu terbuka untuk repositori saat ini.

3 kendala hook yang dialami satu dari dua orang

Poin utama

Kendala pertama: hook dengan exit 1 lolos diam-diam, dan Claude mengabaikan kesalahannya. Kedua: pekerjaan berat di hook dihentikan setelah timeout 60 detik. Ketiga: hook hanya melihat variabel lingkungan dari ~/.zshrc, bukan .env proyek. Masing-masing dapat diperbaiki dengan satu baris, tetapi tanpa mengetahui kendala ini, Anda mungkin mengira “hooks tidak berfungsi.”

Kendala 1. Hook dengan exit 1 lolos diam-diam

Ini yang paling berbahaya. Selama seminggu, saya mengira pelindung .env saya berfungsi, tetapi ternyata hook menggunakan jq -r '.tool_input.file_path' tanpa memeriksa null. Saat tool_input kosong, jq keluar dengan kode 1. Claude menganggap exit 1 sebagai “kesalahan hook, lanjutkan,” sehingga tindakan tetap dilakukan. Saya tidak mengerti mengapa .env masih disentuh. Saya baru melihat penyebabnya saat menjalankan hook secara manual di terminal dengan input kosong.

Perbaikannya cukup dengan satu aturan: setiap hook harus dimulai dengan pemeriksaan null dan diakhiri dengan || exit 0. Exit tak terduga apa pun kemudian menghasilkan exit “aman”, bukan lolos diam-diam.

# Template awal dan akhir yang aman:
FILE=$(jq -r '.tool_input.file_path // empty')
[ -z "$FILE" ] && exit 0
# ... logika Anda ...
exit 0   # atau || exit 0 pada satu baris

Kendala 2. Hook lambat dihentikan oleh timeout

Saya pernah memiliki hook yang menjalankan eslint --fix pada seluruh file setelah Edit. File besar (1000+ baris) membutuhkan 8–10 detik untuk diproses. Timeout hook default Claude Code adalah 60 detik, jadi saya mengira masih dalam batas. Namun, saat beberapa Edit terjadi dalam satu menit, hook menumpuk dalam antrean, dan beberapa di antaranya dilewati begitu saja. Saya baru menyadarinya ketika log menampilkan “hook timed out.”

Perbaikan: jika hook memerlukan pekerjaan berat, jalankan di latar belakang. eslint --fix "$FILE" & tanpa menunggu hasilnya. Hook kemudian mengembalikan exit 0 dalam hitungan milidetik sementara pekerjaan sebenarnya berjalan di latar belakang.

# Operasi berat di latar belakang:
npx eslint --fix "$FILE" &
exit 0

Kendala 3. Hook tidak dapat melihat variabel lingkungan proyek

Saya mengira process.env di hook akan tersedia seperti di aplikasi Node.js. Ternyata tidak. Hook berjalan di shell yang dibuat oleh proses induk Claude Code dan hanya melihat isi ~/.zshrc atau ~/.bashrc, ditambah apa pun yang secara eksplisit diteruskan Claude Code. Jika hook Anda memerlukan OPENAI_API_KEY dari .env, Anda harus membaca .env secara manual.

Perbaikan: baca .env secara eksplisit dalam perintah hook.

# Teruskan lingkungan proyek ke hook:
cd "$PWD" && source .env && curl -H "Authorization: Bearer $OPENAI_API_KEY" ...
# Atau dengan dotenv:
dotenv -e .env -- node my-validator.js

Hal ini sangat merepotkan di monorepo, tempat tiap paket memiliki file .env sendiri. Atasi dengan meneruskan PWD dan menjalankan cd "$PWD" secara eksplisit di awal perintah.

Bagaimana cara mengetes hooks sebelum membuat commit?

Poin utama

Hook adalah perintah shell. Anda dapat menjalankannya secara manual di terminal dengan stdin tiruan, memeriksa kode keluar dan stderr-nya, dan melakukan semua itu sebelum Claude Code menjalankannya. Cara ini menghemat waktu debug berjam-jam dan melindungi Anda dari hook yang memblokir operasinya sendiri.

Skenario tes dasar. Ambil perintah hook dari JSON, buka terminal, lalu berikan JSON yang akan dikirim Claude Code:

# Tes hook 1 (lindungi .env):
echo '{"tool_input":{"file_path":".env"}}' | bash -c '
FILE=$(jq -r ".tool_input.file_path // empty")
[ -z "$FILE" ] && exit 0
echo "$FILE" | grep -qE "(^|/)\.env(\.|$)" && {
  echo "BLOCKED" >&2
  exit 2
} || exit 0
'
echo "Exit code: $?"

Anda seharusnya melihat BLOCKED di stderr dan Exit code: 2. Jika hasilnya exit 0, regex Anda tidak berfungsi; perbaiki grep.

Skenario yang perlu diperiksa:

  1. Jalur berhasil - file seharusnya diblokir. .env untuk hook 1, git push --force main untuk hook 3.
  2. Jalur negatif - file seharusnya tidak diblokir. README.md untuk hook 1, git push origin feature-branch untuk hook 3.
  3. Kasus tepi - input kosong, nilai null, karakter tak terduga. Hook seharusnya mengembalikan exit 0 atau gagal dengan pesan yang jelas, bukan lolos diam-diam.

Tempat menyimpan hook eksperimental. Bukan di .claude/settings.json, melainkan di .claude/settings.local.json (tidak disimpan di git). Tambahkan hook baru di sana di bawah PostToolUse dengan echo sebagai pengganti tindakan sebenarnya, periksa apakah hook terpicu, lalu pindahkan ke settings.json produksi.

// Eksperimen di settings.local.json - menulis ke /tmp/hook-debug.log:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"[hook fired] $(date) $(jq -r '.tool_input.file_path // \"?\"')\" >> /tmp/hook-debug.log"
          }
        ]
      }
    ]
  }
}

Setelah 5–10 pengeditan di Claude Code, buka /tmp/hook-debug.log untuk melihat file mana yang memicu hook. Jika file itu kosong, matcher tidak cocok; perbaiki regex.

Apa langkah selanjutnya?

Poin utama

Mulailah dengan hooks PostToolUse yang keluar dengan kode 0; hooks ini paling aman dan tidak memblokir apa pun. Setelah seminggu, tambahkan hook PreToolUse pertama dengan exit 2 untuk melindungi .env atau mencegah force-push. Setelah dua minggu, CLAUDE.md akan menyusut dari kontrak 200 baris menjadi deskripsi konteks singkat sepanjang 50–80 baris, dengan pemeriksaan peristiwa terpilih dipindahkan ke hook dan batas akses keras ditegakkan terpisah melalui izin, sandbox, serta perlindungan remote.

5 langkah untuk minggu mendatang:

  1. Cari dengan grep di CLAUDE.md kata-kata seperti “jangan pernah,” “dilarang,” “selalu,” dan “harus.” Setiap aturan seperti itu adalah kandidat hook. Jika sebuah aturan bersifat perintah, file teks tidak akan menegakkannya.
  2. Mulai dengan PostToolUse + exit 0. Ini yang paling aman: hooks melakukan perubahan, tetapi tidak memblokir apa pun. Prettier (hook 2), menghentikan server dev (hook 5), membuat commit otomatis setelah tes (hook 6).
  3. Setelah yakin, tambahkan PreToolUse + exit 2 pertama Anda. Misalnya, lindungi .env (hook 1) atau cegah force-push (hook 3). Ini adalah pemblokiran keras, dan kesalahan pada perintah grep dapat memblokir Anda sendiri.
  4. Tes hooks secara manual sebelum membuat commit (lihat bagian di atas). Ini menghemat waktu debug berjam-jam.
  5. Gunakan settings.local.json untuk eksperimen. Tambahkan hooks baru di sana dengan echo sebagai pengganti tindakan sebenarnya.

Setelah hooks Anda dikonfigurasi, langkah berikutnya dalam rangkaian panduan ini adalah cara membatalkan perubahan di Claude Code, jika ada hook yang membiarkan sesuatu yang berbahaya lolos. Jika Anda ingin Claude mengingat struktur proyek di antara sesi, bangun Second Brain di Claude Code (folder business/ + basis pengetahuan tertaut). Dan untuk memberi nama pada skills proyek agar dapat digunakan kembali, lihat Claude Code Skills.

Jika Anda memerlukan akses ke jaringan saraf melalui Ailoot, lihat Ailoot.

Rincian selengkapnya

Pertanyaan tentang Hooks di Claude Code 2026: penyiapan langkah demi langkah dan 7 contoh siap pakai

Mengapa hook harus membaca masukan JSON hanya sekali?

Stdin adalah aliran: proses jq pertama mengonsumsinya, sehingga pembacaan berikutnya berada di EOF. Simpan masukan sekali, lalu ekstrak field perintah dan hasil dari nilai tersimpan. Perlakukan hasil hilang atau tidak dikenal sebagai belum terverifikasi dan lewati commit otomatis.

Apakah contoh hook regex membuat batas keamanan?

Tidak. Hook memeriksa peristiwa tertentu dan string perintah yang dikenali. Pengalihan shell, alias, skrip, alat MCP, atau jalur tulis lain dapat melewatinya; gunakan kredensial terbatas, izin sistem berkas, sandbox, dan cabang terlindungi untuk batas keras.

Semua artikel