Prompt adalah saran. Hook adalah penghenti mutlak.

Selama berbulan-bulan, saya memperlakukan Claude Code seperti pengembang junior yang hanya membutuhkan aturan dasar yang jelas. Instruksi proyek saya sangat eksplisit: jangan pernah melakukan force-push, jangan pernah menghapus branch, jangan pernah menjalankan perintah destruktif. Sebagian besar malam, hal itu berhasil. Agen tersebut menulis tes, melakukan refaktor fungsi, dan tidak menyentuh riwayat git. Lalu, satu proses rebase berjalan kacau.

Jendela konteks (context window) dipenuhi dengan output kesalahan git. Penanda konflik (conflict markers), pesan detached HEAD, dan peringatan divergensi branch menumpuk, token demi token. Terkubur di bawah kebisingan itu adalah instruksi sopan saya untuk menghindari force-pushing. Bagi model, teks terbaru dan paling menonjol dalam utas tersebut adalah aliran kesalahan (error stream). Atensi statistik mengalahkan kebijakan. Agen tersebut mengeksekusi perintah yang menghapus dua jam perubahan lokal yang belum di-commit. Itu bukan tindakan jahat; ia hanya terdistraksi. Perbedaan itu penting. Sebuah LLM tidak melanggar aturan karena dendam. Ia melanggarnya karena pola yang lebih "berisik" di dalam jendela konteks secara sementara menimpa instruksi sebelumnya.

Insiden itu mengubah cara saya berpikir tentang keamanan agen (agent safety). Guardrail yang bekerja sembilan puluh sembilan persen dari waktu yang ada adalah sebuah risiko. Jika mode kegagalan tersebut memakan waktu, uang, atau data produksi Anda, Anda tidak bisa membiarkannya hanya berada di dalam prompt. Anda memerlukan penegakan di luar loop penalaran model.

Hook Claude Code menyelesaikan masalah ini secara tepat. Hook adalah skrip kecil yang mencegat panggilan alat (tool calls) pada tiga momen spesifik: sebelum alat dieksekusi (PreToolUse), setelah alat selesai (PostToolUse), dan ketika agen memutuskan bahwa ia telah selesai (Stop). Karena mereka berjalan sebagai kode eksternal, mereka tidak bergantung pada memori, suasana hati, atau tekanan konteks model. Model bisa saja melupakan setiap instruksi yang pernah Anda berikan; hook akan tetap mengatakan tidak.

Inilah sistem pengaman (harness) yang saya bangun setelah malam yang hilang itu.

The Guard Hook: Intersepsi Sebelum Terjadi Kerusakan

Hook PreToolUse saya memeriksa setiap perintah Bash sebelum shell mengeksekusinya. Saya menyimpan denylist yang ketat untuk pola-pola destruktif. Jika string perintah cocok dengan sesuatu yang berbahaya, hook akan membatalkan eksekusi dan mengembalikan kesalahan langsung kembali ke agen.

Pola yang saya blokir sangat sederhana dan tidak ambigu:

  • git push --force atau varian force-with-lease apa pun yang belum saya percayai
  • git reset --hard
  • rm -rf

Ini bukan riset keamanan yang canggih. Ini adalah sabuk pengaman. Namun, detail krusialnya adalah apa yang terjadi setelah pemblokiran tersebut.

Saya tidak pernah memberikan jawaban mentah "Blocked". Penolakan mentah-mentah akan membingungkan agen dan dapat menjebaknya dalam loop di mana ia mencoba variasi dari perintah destruktif yang sama. Sebaliknya, pesan kesalahan menyertakan jalan keluar. Ketika hook menangkap perintah hard reset, ia memberi tahu agen: "Perintah ini diblokir untuk melindungi pekerjaan yang belum di-commit. Lakukan commit checkpoint terlebih dahulu, lalu tinjau kembali." Kalimat tambahan itu mengubah perilaku agen sepenuhnya. Ia beralih dari mencoba pengendalian kerusakan menjadi menciptakan keamanan. Hook ini bukan sekadar dinding; ia adalah pengatur lalu lintas.

Saya juga memilih denylist daripada allowlist untuk perintah shell. Awalnya, saya mempertimbangkan untuk hanya mengizinkan set perintah sub-git yang aman secara eksplisit. Itu gagal dengan cepat. Agen sangat literal secara kreatif. Mereka menjalankan perintah yang sah tetapi tidak terduga seperti git stash push -m "wip" atau git branch --show-current untuk memeriksa status. Sebuah allowlist akan merusak alur kerja normal saat model menciptakan perintah valid namun tidak terdaftar. Sebuah denylist singkat yang dikurasi dari pola-pola yang benar-benar destruktif memberikan ruang gerak bagi agen sambil tetap melindungi batasan-batasannya.

The Formatter Hook: Mengotomatiskan Pekerjaan Rutin

Dulu saya membuang-buang token prompt untuk memberi tahu agen agar "selalu jalankan formatter setelah mengedit file." Ia sering lupa. Di lain waktu, ia akan berhenti sejenak dan bertanya apakah perlu memformat file, membuang-buang panggilan alat untuk sebuah keputusan yang sebenarnya hanya memiliki satu jawaban benar.

Sekarang saya menanganinya dengan hook PostToolUse. Setelah agen mengedit file, hook akan memeriksa ekstensi file tersebut. Jika itu Python, ia menjalankan Ruff. Jika itu JavaScript atau TypeScript, ia menjalankan Prettier. Jika itu Go, ia menjalankan gofmt. Agen tidak tahu bahwa formatter tersebut ada. Ia tidak perlu tahu.

Memindahkan hal ini keluar dari prompt memberikan dua efek. Pertama, kode secara konsisten bersih tanpa menambah beban kognitif pada model. Kedua, instruksi proyek saya menjadi lebih pendek. Setiap kata "selalu" dan "jangan pernah" yang Anda hapus dari prompt adalah token yang dapat digunakan model untuk pemecahan masalah yang sebenarnya. Hook mengelola hal yang bersifat tetap (invariant); prompt mengelola niat (intent).

The Quality Gate: Mendefinisikan Ulang "Selesai"

The Stop hook runs when the agent decides it has finished the task and attempts to terminate the session. I do not let it. Instead, the hook runs the full test suite. If any test fails, the hook blocks the stop command and returns the failure output to the agent.

This changes the definition of completion. “Done” is no longer a feeling the model has. It is a measurable gate. The agent can only finish when the harness confirms the code works. In practice, this creates a tight feedback loop. The agent writes code, thinks it is finished, hits the stop button, and immediately sees a pytest traceback. It then self-corrects, fixes the import error or broken assertion, and tries to stop again. I have watched agents iterate three or four times inside this loop without human intervention. The harness enforces quality; the model supplies the patches.

What This Teaches About Agent Engineering

Building reliable autonomous systems requires a shift in mindset. You move from writing longer prompts to building tighter harnesses.

Use hooks for enforcement and prompts for policy. If a rule must hold one hundred percent of the time, it belongs in code, not in natural language. Prompts excel at ambiguity, taste, and architecture. They are terrible at invariants. If a mistake would cost you an afternoon of recovery time or, worse, production uptime, write a hook.

Shorter prompts produce better results. When you move mechanical rules into scripts, the model has less to remember and less to contradict. The agent’s context window is a scarce resource. Do not fill it with formatting reminders.

Finally, accept that your role is changing. As agents gain autonomy, the human’s job shifts from generating content to designing the guardrails. You are building the harness that decides what the model can touch, when it can finish, and how it must behave when things go wrong. That is engineering, not prompting.

The source that inspired this approach and additional implementation detail can be found here.

If you are building with AI agents and want to trade notes with other practitioners, you can find the GyaanSetu learning community here.