Claude Code hooks: paano gawing garantiya ang mga panuntunan ng agent
Ang Claude Code hooks ay mga shell command na mismong Claude Code ang nagpapatakbo sa tiyak na mga pangyayari: bago at pagkatapos tumawag ng tool, kapag may ipinadalang prompt, sa simula ng session, at bago huminto ang agent. Itinatakda ang mga ito sa settings.json, at hinaharangan ng exit code 2 ang aksyon at ibinabalik sa agent ang mensahe ng error, kaya ginagawang garantiyang hindi malilimutan ng modelo ang panuntunan mula sa CLAUDE.md.
Ano ang mga hook ng Claude Code
Ang hook ay command na pinapatakbo hindi ng modelo, kundi mismo ng programang Claude Code sa itinakdang sandali ng trabaho ng agent. Akmang gagawin ng agent ang rm -rf: tumatakbo ang hook at pinapasya kung papayagan ang command. Nag-edit ng file ang agent: pinapatakbo ng hook ang formatter. Gustong tapusin ng agent ang trabaho: pinapatakbo ng hook ang mga test at hindi siya hinahayaang huminto habang pula pa ang mga ito.
Ang pangunahing pagkakaiba sa mga tagubilin sa CLAUDE.md: ang tagubilin ay teksto na isinasaalang-alang ng modelo nang may tiyak na posibilidad, samantalang ang hook ay deterministikong code. Laging tatakbo ito, anuman ang haba ng context, anuman ang "napagpasyahan" ng agent, at kahit makalimutan niya ang panuntunan sa ika-100 minuto ng session. Kaya ang claude code hooks ang pangunahing paraan para gawing garantiya ang kagustuhan.
Bakit hindi sapat ang mga panuntunan sa CLAUDE.md
Karaniwang eksena: tinapos ng agent ang feature, berde ang unit test, at may tiwalang "Tapos na" sa chat. Pero walang nagpatakbo ng end-to-end na senaryo, hindi tumakbo ang linter, at pumalya ang migration sa kalagitnaan. Nakasulat naman sa CLAUDE.md na "bago tapusin, patakbuhin ang test." Hindi nagsisinungaling nang sadya ang modelo; sinusuri lang niya ang sarili niya, at hindi maaasahan ang ganoong pagsusuri.
Dito nanggagaling ang prinsipyo ng harness: ang anumang kayang suriin nang mekanikal ay dapat suriin ng mekanismo, hindi ng modelo. Ang panuntunang "patakbuhin ang linter" ay nagiging hook na nagpapatakbo ng linter. Ang panuntunang "huwag galawin ang prod config" ay nagiging hook na humaharang sa pagsulat sa file na iyon. Nananatili ang tekstong tagubilin, pero nagsisilbi na lamang itong paliwanag sa agent kung bakit siya hinarang.
Mga event: kailan tumatakbo ang mga hook
Bawat hook ay nakakabit sa isang event ng lifecycle ng session. Ang mga pangunahing event:
| Event | Kailan tumatakbo | Karaniwang gamit |
|---|---|---|
PreToolUse | bago tumawag ng tool (Bash, Edit, Write at iba pa) | pagbabawal ng mapanganib na command, proteksyon ng file |
PostToolUse | pagkatapos ng matagumpay na tawag ng tool | formatter, linter, mabilis na test pagkatapos ng edit |
UserPromptSubmit | kapag naipadala ng user ang prompt, bago ito iproseso | magdagdag ng context, tanggihan ang prompt na may secret |
Stop | kapag malapit nang tapusin ng agent ang sagot | huwag hayaang huminto hangga't hindi pumapasa ang mga pagsusuri |
SubagentStop | kapag tapos na ang trabaho ng subagent | pagsusuri sa resulta ng subagent |
SessionStart | sa pagsisimula o pagpapatuloy ng session | ilabas ang estado: git status, plano, log ng progreso |
SessionEnd | kapag natapos ang session | paglilinis, pagsulat ng log |
Notification | kapag nagpadala ng notification ang Claude Code (halimbawa, naghihintay ng pahintulot) | tunog o mensahe sa telepono |
PreCompact | bago paikliin ang context | itago ang mahalaga bago ang compaction |
Ang pinakamakabuluhan para sa pagiging maaasahan ay ang PreToolUse, PostToolUse, at Stop: tinatakpan nila ang tatlong mahinang bahagi ng agent: mapanganib na aksyon, hindi nasuring pagbabago, at maagang pag-aangking tapos na.
Saan itinatakda ang mga hook: settings.json
Inilalarawan ang mga hook sa mga file ng setting ng Claude Code:
~/.claude/settings.json— inyong personal na setting, para sa lahat ng proyekto;.claude/settings.jsonsa repositoryo — para sa buong team, kino-commit sa git;.claude/settings.local.json— lokal sa proyekto, hindi napupunta sa git.
Ganito ang istruktura: event → listahan ng mga grupo na may matcher → listahan ng mga command.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/lint.sh",
"timeout": 60
}
]
}
]
}
}
Ang matcher ay pangalan ng tool o regular expression sa pangalan nito: Bash, Edit|Write, .*. Kailangan ito para sa mga event na may kinalaman sa tool; para sa Stop o UserPromptSubmit ay maaari itong hindi ilagay. Itinuturo ng variable na CLAUDE_PROJECT_DIR ang ugat ng proyekto, kaya maginhawang itago ang mga script sa .claude/hooks/ at tawagin sa pamamagitan nito. Makikita ang kasalukuyang hook gamit ang command na /hooks sa loob ng Claude Code.
Paano kumukuha ng datos at sumasagot ang hook
Input: JSON sa stdin
Ipinapasa ng Claude Code sa hook ang JSON na may impormasyon tungkol sa event: ID ng session, working directory, pangalan ng event, at para sa mga tool ay ang tool_name at tool_input. Sa Bash, nasa tool_input.command ang command; sa Edit at Write, nasa tool_input.file_path ang landas ng file. Maginhawang basahin ito gamit ang jq.
{
"session_id": "abc123",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": { "command": "rm -rf build/" },
"cwd": "/home/dev/project"
}
Output: exit code
| Code | Ano ang nangyayari |
|---|---|
0 | tagumpay, tuloy ang trabaho; para sa UserPromptSubmit at SessionStart, idinadagdag ang stdout sa context ng agent |
2 | nakaharang na error: ipinapasa ang stderr sa agent at hinihinto ang aksyon (sa PreToolUse — kinakansela ang tawag ng tool; sa Stop — nagpapatuloy ang agent) |
| iba pa | hindi nakaharang na error: ipinapakita ang stderr sa user, tuloy ang trabaho |
Ang exit code 2 ang pangunahing mekanismo. Tinatanggap ng agent ang teksto mula sa stderr bilang feedback at maaari siyang mag-ayos: isulat muli ang command, ayusin ang test, o huwag galawin ang file. Kaya sa stderr ay huwag isulat ang "error" lamang, kundi ang malinaw na dahilan at kung ano ang dapat gawin sa halip.
Bukod sa exit code, maaaring maglabas ang hook ng JSON sa stdout na may desisyon, halimbawa "decision": "block" at "reason". Kapaki-pakinabang ito para sa mas pinong setup, pero sa karamihan ng gawain ay sapat na ang code 2 at ang mensahe sa stderr.
Halimbawa 1: linter at test pagkatapos ng bawat edit
Ang hook sa PostToolUse na may matcher na Edit|Write ay tumatakbo pagkatapos baguhin ang file. Tapos na ang tool, hindi na mababawi ang edit, pero ibabalik ng code 2 sa agent ang output ng linter, at aayusin niya agad ang error, hindi makalipas ang kalahating oras.
#!/usr/bin/env bash
# .claude/hooks/lint.sh
file=$(jq -r '.tool_input.file_path // empty')
case "$file" in
*.py)
if ! out=$(ruff check "$file" 2>&1); then
echo "Nakita ng linter ang mga error sa $file:" >&2
echo "$out" >&2
exit 2
fi ;;
*.php)
if ! out=$(php -l "$file" 2>&1); then
echo "$out" >&2
exit 2
fi ;;
esac
exit 0
Huwag ilagay dito ang buong pagpapatakbo ng test na sampung minuto: tumatakbo ang hook sa bawat edit. Pagkatapos ng edit, mabilis na pagsusuri lamang (syntax, linter, test ng iisang module), at ang mabibigat ay sa hook na Stop.
Halimbawa 2: pagbabawal ng mapanganib na command
Ang hook sa PreToolUse na may matcher na Bash ay nakikita ang command bago ito patakbuhin. Kinakansela ng code 2 ang tawag at nakukuha ng agent ang paliwanag.
#!/usr/bin/env bash
# .claude/hooks/guard.sh
cmd=$(jq -r '.tool_input.command // empty')
if echo "$cmd" | grep -Eq 'rm -rf /|git push (-f|--force)|DROP (TABLE|DATABASE)'; then
echo "Hinarang ang command ayon sa patakaran ng proyekto: $cmd. Ang tao ang nagpapatakbo ng mapanganib na operasyon." >&2
exit 2
fi
exit 0
Ganito rin ang proteksyon ng mga file: sinusuri ng hook sa Edit|Write ang file_path at hinaharangan ang pagsulat sa .env, prod config, nabuong code, o mga migration file na nailapat na. Tandaan na nalulusutan ang paghahanap gamit ang regular expression: maaaring isulat ng agent ang command sa ibang paraan. Ang hook ay malakas na proteksyon, pero hindi kapalit ng karapatan ng access at sandbox.
Halimbawa 3: hindi maisusulat ng agent na "tapos na" habang pula ang test
Tumatakbo ang hook na Stop kapag malapit nang tapusin ng agent. Kung hindi pumapasa ang mga test, pinipigilan ito ng code 2 sa paghinto, at ang output ng test ay ipinapasa sa agent bilang susunod niyang gawain. Ito ang mekanikal na bersyon ng Definition of Done.
#!/usr/bin/env bash
# .claude/hooks/done.sh
input=$(cat)
# proteksyon laban sa walang katapusang loop: minsan nang pinahaba ng hook ang trabaho
if [ "$(echo "$input" | jq -r '.stop_hook_active')" = "true" ]; then
exit 0
fi
if ! out=$(npm test --silent 2>&1); then
echo "Hindi pumasa ang mga test, hindi pa tapos ang trabaho:" >&2
echo "$out" | tail -n 40 >&2
exit 2
fi
exit 0
Nagiging true ang field na stop_hook_active kapag nagpapatuloy na ang agent dahil sa stop hook. Kung walang ganitong tseke, maaaring paikutin ng hook ang agent nang walang katapusan, ang sitwasyong "nagtatrabaho ang agent buong gabi sa maling direksyon." Sa mga autonomous loop, magdagdag pa ng limitasyon ng pagsubok at kondisyong tumatawag ng tao.
Halimbawa 4: ilabas ang estado sa simula ng session
Sa SessionStart, napupunta sa context ang stdout ng hook. Maginhawang ilabas dito ang kasalukuyang branch, mga hindi pa naka-commit na pagbabago, at susunod na gawain mula sa plano, para hindi magsimula sa wala ang agent.
#!/usr/bin/env bash
echo "Branch: $(git branch --show-current)"
git status --short | head -n 20
[ -f PROGRESS.md ] && tail -n 15 PROGRESS.md
exit 0
Paano ginagawang garantiya ng mga hook ang mga panuntunan
Makatutulong na hatiin ang mga panuntunan ng harness sa tatlong antas. Una, ang teksto sa CLAUDE.md: nagpapaliwanag kung paano magtrabaho, pero sinusunod nang probabilistiko. Pangalawa, ang mga executable na pagsusuri: test, E2E, at mga architectural na panuntunan sa anyo ng script na pumapalya kapag may nalabag. Pangatlo, ang mga hook na awtomatikong nagpapatakbo ng mga pagsusuring ito sa tamang sandali, nang walang pakikialam ng modelo.
Praktikal na panuntunan: kung paulit-ulit nalabag ng agent ang iisang panuntunan, huwag magdagdag ng isa pang babala sa CLAUDE.md na naka-CAPS LOCK. Itanong kung maaaring suriin ang paglabag sa pamamagitan ng script. Kung oo, gumawa ng script at ikabit ito sa hook. Sa ganitong paraan, mas maikli ang tagubilin at mas matatag ang pag-uugali.
| Panuntunan sa teksto | Garantiya sa pamamagitan ng hook |
|---|---|
| "Sundin ang pamantayan sa pag-format ng code" | Pinapatakbo ng PostToolUse ang formatter sa binagong file |
| "Huwag mag-push nang may force" | Hinaharang ng PreToolUse sa Bash ang command, exit 2 |
| "Huwag sabihing tapos na habang pula ang test" | Pinapatakbo ng Stop ang test at hindi pinapayagang tapusin |
| "Huwag i-edit ang .env" | Sinusuri ng PreToolUse sa Edit|Write ang landas |
| "Tandaan kung saan tumigil" | Inilalabas ng SessionStart ang plano at git status |
Karaniwang pagkakamali
- Code 1 sa halip na 2. Pumapalya ang script na may code 1, hindi malalaman ng agent, at dadaan ang aksyon. Tanging code 2 ang humaharang.
- Mensahe sa stdout. Sa code 2, stderr ang ipinapasa sa agent. Isulat sa pamamagitan ng
>&2. - Mabagal na hook sa madalas na event. Ang buong pagpapatakbo ng test sa PostToolUse ay nagpapabagal sa bawat edit nang isang minuto. Ang mabigat ay ilagay sa Stop.
- Walang katapusang Stop. Walang tseke ng
stop_hook_activeo limitasyon, kaya umiikot ang agent nang walang hanggan. - Kamag-anak na landas. Tumatakbo ang hook sa working directory ng session; gamitin ang
$CLAUDE_PROJECT_DIR. - Hindi nasuring hook ng iba. Tumatakbo ang hook gamit ang inyong karapatan nang walang kumpirmasyon. Basahin ang mga script mula sa ibang repositoryo bago sila pagkatiwalaan.
Buod
Inililipat ng Claude Code hooks ang pinakamahahalagang panuntunan mula sa teksto papunta sa code: hinihinto ng PreToolUse ang mapanganib na aksyon, sinusuri ng PostToolUse ang bawat edit, hindi hinahayaang mag-ulat ng pagiging handa ang Stop nang maaga, at ibinabalik ng SessionStart sa agent ang alaala tungkol sa proyekto. Magsimula sa dalawa o tatlong hook para sa mga panuntunang nalabag na ng agent, at panatilihing malinaw ang mga mensahe sa stderr. Kaya ng agent na ayusin ang sarili niyang mga error, at ang inyong trabaho ay suriin ang resulta sa halip na ang proseso. Ang beripikasyon, pagsusuri ng pagiging handa, at mga autonomous loop ay tinatalakay nang praktikal sa kurso.
Mga madalas itanong
Kailangan bang i-restart ang Claude Code pagkatapos baguhin ang mga hook?
Pinakamaaasahang nailalapat ang mga pagbabago sa settings.json sa bagong session; makikita ang kasalukuyang configuration ng hook gamit ang /hooks.
Puwede bang isulat ang mga hook sa ibang wika bukod sa bash?
Oo, anumang command ang hook: script sa Python, Node.js, o binary. Ang mahalaga lamang ay basahin ang JSON mula sa stdin at magbalik ng tamang exit code.
Nakikita ba ng agent ang output ng matagumpay na hook?
Karaniwan hindi: sa code 0, hindi napupunta sa context ang output. Ang eksepsyon ay ang UserPromptSubmit at SessionStart, kung saan idinadagdag ang stdout sa context ng agent.
Gumagana ba ang mga hook para sa mga subagent?
Oo, dumadaan sa parehong hook na PreToolUse at PostToolUse ang mga tawag ng tool ng subagent, at may hiwalay na event na SubagentStop para sa pagtatapos nila.
Ligtas bang i-commit ang mga hook sa repositoryo?
Oo, ang .claude/settings.json at mga script sa .claude/hooks/ ay karaniwang gawain para sa team; ang personal na hook ay itago sa settings.local.json o sa ~/.claude/settings.json.
Detalyadong tinatalakay ang paksang ito, may praktis, sa kursong “Harness Engineering: инженерия надёжности AI-агентов”.
- Агент написал «готово», а фича не работает: verification gap и Definition of Done
- Все тесты зелёные, а фича сломана: E2E-тесты и архитектурные правила для AI-агента
- Автономный AI-агент часами без присмотра: Loop Engineering и условия остановки