Claude Code hooks sunt comenzi, cereri HTTP, apeluri de unelte MCP, prompturi sau subagenți pe care Claude Code le execută singur, în puncte fixe ale unei sesiuni. Diferența față de o regulă scrisă în CLAUDE.md este cine o execută: instrucțiunea o citește modelul, care o poate ignora; hook-ul îl rulează harness-ul, de fiecare dată. Mai jos sunt referința oficială, citită la 12 octombrie 2026, și cinci exemple rulate, cu scripturile lor.
| Pe scurt | |
|---|---|
| Ce este | codul tău, rulat de Claude Code la un eveniment; primește JSON pe stdin |
| Câte evenimente | 33 în referință, la 12 octombrie 2026 |
| Cum blochează | ieșire cu codul 2 sau JSON pe stdout, cu ieșire 0 |
| Verificat pe | Claude Code 2.1.280; documentația descrie deja 2.1.296 |
Instrucțiune sau hook: cine execută
Documentația despre memorie spune că fișierele de instrucțiuni sunt tratate „as context, not enforced configuration”, iar pentru o acțiune care trebuie blocată „regardless of what Claude decides” trimite la un hook PreToolUse. Ce scrii în fișierul de instrucțiuni e subiectul ghidului despre AGENTS.md și CLAUDE.md; aici e cealaltă jumătate.
| Vrei ca... | Pui în |
|---|---|
| agentul să știe o convenție, o comandă, o capcană | instrucțiuni (CLAUDE.md, AGENTS.md) |
| o unealtă sau o cale să fie refuzată după un tipar fix | o regulă de permisiuni |
| decizia să ceară logică: căi rezolvate, ramura curentă, textul comenzii | un hook PreToolUse |
| ceva să se întâmple după fiecare editare ori la fiecare final de tură | un hook PostToolUse sau Stop |
Regulile de permisiuni și hook-urile sunt aplicate amândouă de Claude Code, nu de model. Regula e un tipar declarativ pe numele uneltei și pe argumente; hook-ul e un program care primește tot apelul și decide cu logica ta. Se compun asimetric: un hook care refuză bate și o regulă allow, dar un hook care răspunde allow nu trece peste o regulă deny sau ask. Regulile, sandbox-ul și limitele lor sunt în ghidul despre izolarea agenților de programare.
Un hook scris greșit e mai rău decât niciunul, pentru că dă impresia de protecție. Proiectarea unui set de guardrails pe un depozit real, alături de permisiuni, claude -p în CI și MCP, e tratată în modulul „Hooks Lifecycle” al cursului Claude Code Mastery: Coding Agentic din Terminal; cursul lucrează cu evenimentele de bază (PreToolUse, PostToolUse, Stop, SubagentStop) și nu parcurge toată lista de mai jos.
Articolul presupune că folosești deja Claude Code. Alegerea uneltei este în comparația Claude Code vs Cursor vs GitHub Copilot.
Cele 33 de evenimente din referință
Referința le grupează pe trei cadențe: pe sesiune (SessionStart, SessionEnd), pe tură (UserPromptSubmit, Stop, StopFailure) și la fiecare apel de unealtă (PreToolUse, PostToolUse). Ultima coloană spune dacă ieșirea cu codul 2 oprește acțiunea.
| Eveniment | Când se declanșează | Codul 2 blochează? |
|---|---|---|
SessionStart |
sesiunea începe sau e reluată | nu |
Setup |
pornire cu --init-only; cu --init sau --maintenance, în modul -p |
nu |
UserPromptSubmit |
un prompt e trimis, înainte să ajungă la model | da |
UserPromptExpansion |
o comandă tastată se extinde într-un prompt | da |
PreToolUse |
înaintea unui apel de unealtă | da |
PermissionRequest |
un apel are nevoie de o decizie de permisiune | nu; refuzi prin JSON |
PermissionDenied |
auto mode refuză un apel | nu |
PostToolUse |
după un apel reușit | nu |
PostToolUseFailure |
după un apel eșuat | nu |
PostToolBatch |
după un lot de apeluri paralele, înaintea următorului apel la model | da |
Notification |
Claude Code trimite o notificare | nu |
MessageDisplay |
cât timp se afișează textul răspunsului | nu |
SubagentStart |
pornește un subagent | nu |
SubagentStop |
un subagent termină | da |
TaskCreated |
se creează un task cu TaskCreate |
da |
TaskCompleted |
un task e marcat ca terminat | da |
Stop |
Claude termină de răspuns | da |
StopFailure |
tura se încheie cu o eroare de API | nu |
TeammateIdle |
un coechipier dintr-un agent team urmează să intre în așteptare | da |
InstructionsLoaded |
se încarcă un CLAUDE.md sau un fișier din .claude/rules/ |
nu |
ConfigChange |
un fișier de configurare se schimbă în timpul sesiunii | da |
CwdChanged |
se schimbă directorul de lucru | nu |
DirectoryAdded |
se adaugă un director de lucru, de pildă cu /add-dir |
nu |
FileChanged |
un fișier urmărit se schimbă pe disc | nu |
WorktreeCreate |
se creează un worktree | da |
WorktreeRemove |
se șterge un worktree creat de un hook | da |
PreCompact |
înaintea compactării contextului | da |
PostCompact |
după compactare | nu |
PreModelSwitch |
înaintea unei schimbări de model cerute | da |
PostModelSwitch |
după ce modelul sesiunii s-a schimbat | nu |
Elicitation |
un server MCP cere date de la utilizator | da |
ElicitationResult |
utilizatorul a răspuns, înainte ca răspunsul să plece la server | da |
SessionEnd |
sesiunea se termină | nu |
Patru dintre ele acoperă aproape tot ce cere un dezvoltator:
PreToolUserulează după ce modelul a construit parametrii și înainte de execuție. Se potrivește pe numele uneltei (Bash,Edit,Write,Read,Agent,WebFetch; uneltele MCP se numescmcp__<server>__<unealtă>) și rulează înaintea verificării de permisiuni: undenyține și cu--dangerously-skip-permissions.PostToolUserulează după un apel reușit: poți formata, verifica și trimite feedback, dar nu poți anula.Notificationse potrivește pe tipul notificării:permission_promptdupă ce o cerere de permisiune a așteptat circa șase secunde,idle_promptla circa 60 de secunde după ultimul răspuns, dacă n-ai tastat nimic. Nu blochează.Stoprulează la fiecare răspuns încheiat al agentului principal, nu doar la finalul sarcinii, și nu la întreruperea de către utilizator. Poate împiedica oprirea.
Unde se configurează și cum arată în settings.json
| Loc | Se aplică | Se partajează |
|---|---|---|
~/.claude/settings.json |
în toate proiectele tale | nu |
.claude/settings.json |
în proiect | da, se comite |
.claude/settings.local.json |
în proiect | nu |
| setări administrate (managed) | în toată organizația | da, le controlează administratorul |
hooks/hooks.json dintr-un plugin |
cât pluginul e activ | da |
| frontmatter de skill sau de subagent | după invocarea skill-ului, respectiv cât rulează subagentul | da |
Pentru o cheie obișnuită, precedența setărilor din documentație este: administrate, --settings din linia de comandă, local, proiect, utilizator. Hook-urile fac excepție: se adună între niveluri, nu se înlocuiesc, iar același handler definit în mai multe fișiere rulează o singură dată. Frontmatterul unui skill e explicat în articolul despre Agent Skills pentru cod.
Configurația are trei niveluri: evenimentul, grupul cu matcher și lista de handlere.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/guard-bash.py",
"args": [],
"timeout": 10
}
]
}
]
}
}
matcher:"*",""sau absent înseamnă „orice”. Un șir numai din litere, cifre,_,-, spații,,și|e un nume exact sau o listă (Edit|Write); orice alt caracter îl face expresie regulată JavaScript neancorată, deciEdit.*prinde șiNotebookEdit. E sensibil la majuscule.StopșiUserPromptSubmitnu au matcher.if: filtru opțional, în sintaxa regulilor de permisiuni (Bash(git *),Edit(*.ts)), doar pe evenimentele de unealtă. Documentația îl numește „best-effort”: când nu poate descompune comanda, hook-ul rulează oricum, deci scriptul verifică singur ce a primit.type:command,http,mcp_tool,promptsauagent(experimental). Cuargs, un hookcommandpornește executabilul direct, fără shell, și${CLAUDE_PROJECT_DIR}nu cere ghilimele; fărăargs, șirul trece printr-un shell și calea se pune între ghilimele duble.timeout: în secunde. Implicit 600 pentrucommand,httpșimcp_tool, 30 pentruprompt, 60 pentruagent; peUserPromptSubmit, 30.
Hook-urile care se potrivesc rulează în paralel și blochează agentul până termină; "async": true mută un hook command în fundal, unde nu mai poate decide nimic.
Ce primește hook-ul pe stdin
Un obiect JSON. Câmpurile comune: session_id, prompt_id, transcript_path, cwd, permission_mode (nu la toate evenimentele) și hook_event_name. Restul depinde de eveniment:
| Eveniment | Câmpuri specifice |
|---|---|
PreToolUse |
tool_name, tool_input, tool_use_id |
PostToolUse |
aceleași, plus tool_response și duration_ms |
Notification |
message, title (opțional), notification_type |
Stop |
stop_hook_active, last_assistant_message, background_tasks, session_crons |
SessionStart |
source, opțional model |
Pentru Bash, tool_input are command, description, timeout și run_in_background. Pentru Write, Edit și Read, tool_input.file_path e mereu o cale absolută.
Cum răspunde: coduri de ieșire și JSON
| Cod de ieșire | Sens | Efect |
|---|---|---|
| 0 | succes | acțiunea continuă; dacă stdout e un singur obiect JSON, câmpurile lui se aplică |
| 2 | eroare blocantă | pe evenimentele care pot bloca, acțiunea e oprită; stderr e motivul |
| orice alt cod | eroare neblocantă | acțiunea continuă; în transcript apare o notă hook error |
Capcana clasică: fără JSON valid pe stdout, exit 1 nu blochează nimic.
Varianta cu JSON cere ieșire 0 și un singur obiect pe stdout, fără alt text. Câmpurile universale sunt continue, stopReason, systemMessage și terminalSequence; suppressOutput e acceptat, dar nu are efect. Decizia stă în alt loc la fiecare eveniment:
| Eveniment | Câmpurile de decizie |
|---|---|
PreToolUse |
hookSpecificOutput.permissionDecision (allow, deny, ask, defer) și permissionDecisionReason; opțional updatedInput, additionalContext |
PostToolUse |
decision: "block" cu reason; opțional additionalContext, updatedToolOutput |
Stop |
decision: "block" cu reason, sau hookSpecificOutput.additionalContext |
Notification |
niciunul |
SessionStart |
doar context: additionalContext sau text simplu pe stdout |
La PreToolUse, câmpurile decision și reason de la primul nivel sunt depreciate; vechile valori approve și block corespund lui allow și deny. defer este gândit pentru integrările care rulează claude -p ca subproces și funcționează doar când tura are un singur apel de unealtă. Când mai multe hook-uri răspund diferit, câștigă cel mai restrictiv: deny, defer, ask, allow.
Cinci exemple, rulate
Scripturile stau în .claude/hooks/, executabile (chmod +x); configurația, în .claude/settings.json din proiect. Toate au fost testate izolat, cu JSON pe stdin; patru au rulat și în sesiuni reale claude -p, pe Claude Code 2.1.280.
1. PreToolUse: comenzi Bash refuzate
Configurarea e fragmentul de mai sus. Scriptul refuză rm recursiv în afara proiectului, git push forțat și orice comandă care atinge un fișier .env:
#!/usr/bin/env python3
# PreToolUse pe Bash: refuză rm recursiv în afara proiectului,
# git push forțat și orice comandă care atinge un fișier .env.
import json
import os
import re
import shlex
import sys
data = json.load(sys.stdin.buffer) # octeți: nu depinde de localizarea shell-ului
command = data.get("tool_input", {}).get("command", "")
cwd = data.get("cwd") or os.getcwd()
root = os.path.realpath(os.environ.get("CLAUDE_PROJECT_DIR") or cwd)
def deny(reason):
# Ieșire 0 + JSON pe stdout: motivul ajunge la model ca rezultat al apelului
print(json.dumps({"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": reason,
}}))
sys.exit(0)
def subcommands(text):
# Desparte linia la ; && || | & ( ) și întoarce cuvintele fiecărei părți
lexer = shlex.shlex(text, posix=True, punctuation_chars=True)
lexer.whitespace_split = True
try:
tokens = list(lexer)
except ValueError: # ghilimele neînchise, de pildă într-un heredoc
tokens = text.split()
part = []
for token in tokens + [";"]:
if set(token) <= set(";&|()"):
if part:
yield part
part = []
else:
part.append(token)
for words in subcommands(command):
# Sari peste atribuirile de mediu (VAR=valoare) și peste sudo
while words and (re.match(r"\w+=", words[0]) or words[0] == "sudo"):
words = words[1:]
if not words:
continue
program, args = os.path.basename(words[0]), words[1:]
if program == "rm":
short_flags = "".join(a[1:] for a in args if re.match(r"-[^-]", a))
if "r" in short_flags.lower() or "--recursive" in args:
for target in (a for a in args if not a.startswith("-")):
path = os.path.realpath(os.path.join(cwd, os.path.expanduser(target)))
if "$" in target or "`" in target or not path.startswith(root + os.sep):
deny(f"rm recursiv refuzat: ținta '{target}' nu este în interiorul "
f"proiectului ({root}). Șterge doar căi din proiect sau cere "
"utilizatorului să ruleze comanda manual.")
if program == "git" and "push" in args:
if any(a.startswith(("--force", "+")) or re.match(r"-[a-zA-Z]*f", a) for a in args):
deny("git push forțat refuzat de politica proiectului. Creează un commit "
"nou sau cere utilizatorului să ruleze comanda manual.")
for arg in args:
name = os.path.basename(arg.split("=")[-1])
if re.fullmatch(r"\.env(\..+)?", name) and name != ".env.example":
deny(f"Acces refuzat la '{name}': fișierele .env conțin secrete. "
"Folosește .env.example pentru numele variabilelor.")
sys.exit(0) # nicio obiecție: decide fluxul normal de permisiuni
Rulat real, cu Bash permis prin --allowedTools: modelul a cerut rm -rf ../outside/victim, wc -l .env și git push --force-with-lease; toate trei au fost refuzate, iar directorul a rămas neatins. Modelul vede motivul ca rezultat al apelului:
PreToolUse:Bash hook error: git push forțat refuzat de politica proiectului. Creează un commit nou sau cere utilizatorului să ruleze comanda manual.
Testul izolat a acoperit 27 de comenzi și a arătat limita: bash -c "rm -rf /" trece, fiindcă scriptul nu intră în șirul dat altui shell. E o plasă pentru greșeli, nu o graniță. Nu vede nici unealta Read, nici fișierele pomenite cu @ în prompt, care nu trec prin niciun apel de unealtă; pentru ele, documentația cere o regulă deny de tip Read(./.env).
2. PostToolUse: Prettier după fiecare editare
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/format-file.sh",
"args": [],
"timeout": 30
}
]
}
]
}
}
#!/bin/bash
# PostToolUse pe Edit|Write: formatează fișierul pe care Claude tocmai l-a scris.
input=$(cat)
file=$(jq -r '.tool_input.file_path // empty' <<<"$input")
[ -f "$file" ] || exit 0
cd "$CLAUDE_PROJECT_DIR" || exit 0
case "$file" in
*.js|*.jsx|*.ts|*.tsx|*.json|*.css)
# Aici schimbi comanda pentru alt limbaj: adaugi o ramură cu formatorul lui.
if ! out=$(npx prettier --write "$file" 2>&1); then
echo "Prettier nu a putut formata $file:" >&2
echo "$out" >&2
exit 2 # unealta a rulat deja; ieșirea 2 doar îi arată lui Claude stderr
fi
;;
esac
exit 0
Cu --write, Prettier rescrie fișierul pe loc. Pentru alt limbaj adaugi o ramură în case, cu formatorul lui; astfel de ramuri nu au fost rulate aici. Rulat real: Claude a scris src/greet.js pe o singură linie, cu apostrofuri, iar pe disc fișierul a ajuns pe trei linii, cu ghilimele duble. Izolat, un fișier cu eroare de sintaxă a produs codul 2 și mesajul Prettier pe stderr, pe care PostToolUse i-l arată lui Claude. Hook-ul nu rulează când fișierul e rescris de o comandă Bash; pentru asta există FileChanged.
3. Notification: află când Claude te așteaptă
{
"hooks": {
"Notification": [
{
"matcher": "permission_prompt|idle_prompt",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/notify.sh",
"args": []
}
]
}
]
}
}
#!/bin/bash
# Notification: trimite mesajul primit de la Claude Code ca notificare de desktop.
input=$(cat)
message=$(jq -r '.message // "Claude Code are nevoie de tine"' <<<"$input")
title=$(jq -r '.title // "Claude Code"' <<<"$input")
if command -v osascript >/dev/null 2>&1; then
# macOS: textul intră ca argument, nu lipit în script, deci ghilimelele nu strică nimic
osascript -e 'on run argv' \
-e 'display notification (item 1 of argv) with title (item 2 of argv)' \
-e 'end run' "$message" "$title"
elif command -v notify-send >/dev/null 2>&1; then
# Linux, cu un serviciu de notificări pornit
notify-send "$title" "$message"
fi
exit 0
Pentru toate proiectele, fragmentul merge în ~/.claude/settings.json, cu o cale absolută spre script. Testat doar izolat: pe macOS iese cu 0, inclusiv cu ghilimele și apostrof în mesaj; ramura Linux a rulat cu un notify-send simulat, nu cu cel real. Într-o sesiune reală nu s-a declanșat, rulările fiind neinteractive. Dacă pe macOS nu apare nimic, ghidul oficial de hooks explică de ce: osascript trimite notificările prin Script Editor, căruia trebuie să-i permiți notificările.
4. Stop: testele trec sau Claude continuă
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/check-before-stop.sh",
"args": [],
"timeout": 120
}
]
}
]
}
}
#!/bin/bash
# Stop: rulează testele; dacă pică, Claude primește motivul și continuă.
input=$(cat)
already_continuing=$(jq -r '.stop_hook_active' <<<"$input")
cd "$CLAUDE_PROJECT_DIR" || exit 0
# Arbore git curat: nimic de verificat, deci nu rulăm testele.
if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && [ -z "$(git status --porcelain)" ]; then
exit 0
fi
if out=$(npm test 2>&1); then
exit 0 # testele trec: Claude se poate opri
fi
summary=$(tail -n 15 <<<"$out")
if [ "$already_continuing" = "true" ]; then
# Garda de buclă: a doua oară nu mai blocăm, doar anunțăm omul.
jq -n --arg m "Testele pică și după o rundă de reparații. Verifică manual." \
'{systemMessage: $m}'
else
jq -n --arg r "Testele pică. Repară cauza, nu testul, apoi încheie. Ieșirea npm test:
$summary" '{decision: "block", reason: $r}'
fi
exit 0
stop_hook_active este garda din documentație împotriva buclei: e true când Claude continuă deja din cauza unui hook Stop. Scriptul blochează o singură dată; a doua oară doar anunță omul, prin systemMessage. Fără gardă rămâne plafonul lui Claude Code: după opt continuări consecutive oprirea trece, dar numărătoarea se resetează la fiecare apel de unealtă.
Rulat real: cu un defect lăsat intenționat în src/sum.js, Claude a terminat o sarcină fără legătură și a spus „gata”. Hook-ul a blocat oprirea, modelul a primit motivul cu prefixul Stop hook feedback:, a reparat funcția și s-a oprit din nou; a doua intrare avea stop_hook_active: true, iar testele treceau.
5. SessionStart: context injectat la pornire
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/session-context.sh",
"args": []
}
]
}
]
}
}
#!/bin/bash
# SessionStart: fapte despre mediul local, ca text simplu pe stdout (ajunge în contextul lui Claude).
cd "$CLAUDE_PROJECT_DIR" || exit 0
echo "Versiunea Node activă în acest terminal: $(node -v 2>/dev/null || echo 'node lipsește')."
echo "Ținta de deploy a acestui terminal (DEPLOY_TARGET): ${DEPLOY_TARGET:-nesetată}."
[ -d node_modules ] || echo "Dependențele nu sunt instalate: lipsește node_modules."
exit 0
La SessionStart, textul simplu de pe stdout intră în contextul lui Claude. Documentația recomandă propoziții factuale, nu ordine, și CLAUDE.md pentru ce e static. Rulat real: întrebat fără unelte care e ținta de deploy, modelul a răspuns cu valoarea pe care o avea doar hook-ul. O primă variantă injecta ramura git și era redundantă: Claude Code pune singur în context un instantaneu al stării git. Ce merită să ajungă în context e subiectul cursului Context Engineering și memorie pentru agenți AI.
Depanare
/hooksdeschide un browser doar-citire al hook-urilor configurate, cu sursa fiecăruia. Dacă hook-ul lipsește de acolo, problema e în fișier: JSON invalid (virgulă la final, comentarii) ori alt director.claude --debug-file <cale>scrie jurnalul într-un loc cunoscut;claude --debugîl scrie în~/.claude/debug/<session-id>.txt.Ctrl+Odeschide transcriptul, unde apar blocările și erorile; un hook reușit nu lasă urme acolo.- Testează scriptul singur, cu JSON pe stdin, și citește codul de ieșire cu
echo $?.
Greșelile frecvente, toate descrise în documentația oficială: script neexecutabil; cale relativă, care depinde de directorul curent (folosește ${CLAUDE_PROJECT_DIR}); cale cu spații fără ghilimele, în forma cu shell; text tipărit de profilul shell-ului înaintea JSON-ului; permissionDecision pus la primul nivel, unde e ignorat fără eroare; matcher scris cu alte majuscule; hook Stop fără gardă.
Securitate
Un hook command rulează cu toate drepturile contului tău. De aici, trei reguli.
Hook-urile dintr-un depozit sunt cod străin. Într-o sesiune interactivă, Claude Code le reține până accepți dialogul de încredere pentru director. În modul -p și în SDK, dialogul nu apare și directorul e considerat de încredere, deci hook-urile comise în .claude/settings.json rulează. Înainte de a rula claude -p peste un depozit străin, citește-i directorul .claude/ ori pornește cu --bare sau cu --settings '{"disableAllHooks": true}'.
Intrarea e nesigură. Comanda din tool_input poate fi scrisă sub influența unui text ostil citit de agent: pune variabilele între ghilimele și verifică secvențele .. din căi. Apărarea pe straturi e în ghidul despre prompt injection la agenți, iar trust boundaries și sandboxing-ul, în cursul OpenClaw: Securizarea Agenților AI Open-Source în Producție.
Administratorul are ultimul cuvânt. Cu allowManagedHooksOnly în setările administrate, hook-urile de utilizator, de proiect, locale și de plugin sunt blocate; disableAllHooks pus în fișierele tale nu oprește hook-urile administrate.
Ce nu rezolvă un hook
- Un hook care nu pornește nu blochează. O cale greșită, un script care crapă ori un timeout sunt erori neblocante: acțiunea trece. Remediul,
"onFailure": "block", apare în changelog la 2.1.295 și nu a fost rulat aici, pe 2.1.280. - Textul unei comenzi nu e o graniță. Pentru restricții de fișiere și de rețea care nu depind de text, documentația trimite la sandbox.
PostToolUsenu anulează nimic. Fișierul e scris, comanda a rulat.- Hook-ul nu judecă dacă modificarea e corectă. Tipurile
promptșiagentpun un model să decidă, deci nu mai sunt deterministe. - Nu înlocuiește CI-ul. Rulează acolo unde rulează Claude Code și se poate opri din setări. Ce verifici pe server e în ghidul despre code review și CI cu agenți.
Stopnu înseamnă „sarcină terminată”. Rulează la fiecare răspuns, deci testele lente se plătesc la fiecare tură.
Hook-urile din setări rulează și în subagenți; despre subagenți, vezi Haiku 5.5 și Opus 5.5 folosite împreună.
Întrebări frecvente
Ce este un hook în Claude Code? O comandă, o cerere HTTP, un apel de unealtă MCP, un prompt sau un subagent pe care Claude Code îl execută automat la un eveniment al sesiunii. Primește contextul ca JSON și poate întoarce o decizie.
Care este diferența dintre PreToolUse și PostToolUse?
PreToolUse rulează înaintea apelului și îl poate refuza, trimite la confirmare ori rescrie. PostToolUse rulează după un apel reușit: adaugă feedback sau context, dar unealta a rulat deja.
De ce nu blochează hook-ul meu, deși iese cu eroare? Probabil iese cu codul 1. Doar codul 2 blochează; orice alt cod diferit de zero e o eroare neblocantă și acțiunea continuă. Alternativa e ieșirea 0 cu un JSON de decizie pe stdout.
Cum evit bucla infinită la un hook Stop?
Citești stop_hook_active din intrare și nu mai blochezi când e true. Ca plasă, Claude Code lasă oprirea să treacă după opt continuări consecutive, limită reglabilă cu CLAUDE_CODE_STOP_HOOK_BLOCK_CAP.
Unde pun hook-urile ca să le primească toată echipa?
În .claude/settings.json, comis în depozit, cu scripturile referite prin ${CLAUDE_PROJECT_DIR}. Preferințele personale merg în .claude/settings.local.json sau în ~/.claude/settings.json.
Concluzie
Instrucțiunile spun agentului ce ai vrea; hook-urile hotărăsc ce se întâmplă. Patru evenimente acoperă nevoile obișnuite: PreToolUse pentru refuzuri, PostToolUse pentru formatare, Notification pentru atenția ta și Stop pentru verificarea de la final. Contractul e scurt: JSON pe stdin, codul 2 sau un JSON de decizie la ieșire. Greșelile se repetă: exit 1 în loc de 2, o cale care nu pornește, un Stop fără gardă. Un hook rămâne o plasă; granițele sunt permisiunile, sandbox-ul și CI-ul.
Surse
- Claude Code Docs: Hooks reference
- Claude Code Docs: Automate actions with hooks
- Claude Code Docs: Settings files and precedence
- Claude Code Docs: Configure permissions
- Claude Code Docs: How Claude remembers your project
- Claude Code Docs: Changelog
- Prettier Docs: CLI
Articol redactat la 12 octombrie 2026. Faptele despre hook-uri vin din documentația oficială citită în aceeași zi; exemplele 1, 2, 4 și 5 au rulat în sesiuni reale cu Claude Code 2.1.280, iar exemplul 3 a fost testat doar izolat, cu intrare simulată. Cursuri-AI.ro este furnizor de cursuri plătite, deci parte interesată.
Cursul care continuă acest articol
Claude Code Mastery: Coding Agentic din Terminal (multi-fișier, git, CI, MCP)
Ai citit teoria. În curs o aplici: lecții structurate pe module, exerciții și quiz-uri cu feedback imediat, Profesorul AI integrat în fiecare lecție și progres salvat automat. La final primești o atestare privată de finalizare.
Din programa cursului
- Fundamente Claude Code: Instalare, CLI și Modelul Mental Agent-First
- Multi-Fișier și Codebase-uri Mari: Plan, Edit, Review și Context Management
- Workflow Git Complet: Branch-uri, Commit-uri, Conflicte, Code Review și PR-uri
- Headless Mode (claude -p) și Automatizare Non-Interactivă
+ încă 7 module în programa completă
499 lei pe lună pentru acest curs, TVA 21% inclus · sau 1.999 lei pe lună pentru toate cele 25 de cursuri IT Pro (vezi ce înveți în tot parcursul).
Abonament lunar, cu reînnoire automată · anulezi oricând din contul tău · conținut digital cu acces imediat, vezi condițiile de retragere. Atestarea confirmă parcurgerea cursului și este privată — nu este diplomă și nu este calificare recunoscută de stat.