22 KiB
Referinta oficiala hooks Claude Code (pentru diagnosticul "subagent stop violation")
Surse fetch-uite azi (2026-09-17), toate redirecteaza de pe docs.claude.com pe code.claude.com:
- https://code.claude.com/docs/en/hooks (fost https://docs.claude.com/en/docs/claude-code/hooks)
- https://code.claude.com/docs/en/hooks-guide (fost .../hooks-guide)
- https://code.claude.com/docs/en/settings (fost .../settings)
- https://code.claude.com/docs/en/settings-reference
- https://code.claude.com/docs/en/settings-example
- https://code.claude.com/docs/en/env-vars
Limitare tehnica intalnita: paginile hooks si settings-reference sunt prea mari pentru
fetch-ul folosit (WebFetch trece continutul brut printr-un model mic inainte sa-l intoarca); la
cereri repetate pe aceeasi pagina, portiuni identice (tabelul "Common input fields", tabelul
"Exit code 2 behavior per event" pana la randul Stop, exemplele din hooks-guide) au iesit
IDENTIC de mai multe ori — acelea sunt tratate mai jos ca sigure/verbatim. O extractie initiala,
mai larga, a produs scheme JSON pentru SessionStart/Stop/SubagentStop/PreCompact care NU
s-au mai reprodus la cereri ulterioare tintite pe aceleasi sectiuni (acelea au raspuns explicit
"nu e in continutul furnizat, pagina e trunchiata") — acea extractie e tratata ca nesigura si
nu e citata mai jos. Sectiunile marcate NEDOCUMENTAT de mai jos sunt cele pe care nu am putut
sa le confirm verbatim, nu neaparat cele care lipsesc din documentatia reala.
1. Lista completa a evenimentelor de hook
Tabelul de mai jos e citat verbatim din hooks-guide (sectiunea "How hooks work"), confirmat prin
citire directa a continutului brut al fetch-ului (nu prin sumarizare):
| Event | When it fires | |
SessionStart| When a session begins or resumes | |Setup| When you start Claude Code with--init-only, or with--initor--maintenancein-pmode. For one-time preparation in CI or scripts | |UserPromptSubmit| When you submit a prompt, before Claude processes it | |UserPromptExpansion| When a user-typed command expands into a prompt, before it reaches Claude. Can block the expansion | |PreToolUse| Before a tool call executes. Can block it | |PermissionRequest| When a tool call needs a permission decision | |PermissionDenied| When auto mode denies a tool call, including denials without a classifier verdict. ... | |PostToolUse| After a tool call succeeds | |PostToolUseFailure| After a tool call fails | |PostToolBatch| After a full batch of parallel tool calls resolves, before the next model call | |Notification| When Claude Code sends a notification | |MessageDisplay| While assistant message text is displayed | |SubagentStart| When a subagent is spawned | |SubagentStop| When a subagent finishes | |TaskCreated| When a task is being created viaTaskCreate| |TaskCompleted| When a task is being marked as completed | |Stop| When Claude finishes responding | |StopFailure| When the turn ends due to an API error | |TeammateIdle| When an agent team teammate is about to go idle | |InstructionsLoaded| When a CLAUDE.md or.claude/rules/*.mdfile is loaded into context. ... | |ConfigChange| When a configuration file changes during a session | |CwdChanged| When the working directory changes, for example when Claude executes acdcommand. ... | |DirectoryAdded| When a working directory is added mid-session via/add-diror the SDKregister_repo_rootcontrol request | |FileChanged| When a watched file changes on disk. Thematcherfield specifies which filenames to watch | |WorktreeCreate| When a worktree is being created via--worktree,isolation: "worktree", or for a background session. ... | |WorktreeRemove| When a worktree is being removed at session exit, when a subagent finishes, or when you delete a background session | |PreCompact| Before context compaction | |PostCompact| After context compaction completes | |PreModelSwitch| Before Claude Code applies a model switch that you or a client requested. Can block the switch | |PostModelSwitch| After the session's model changes, including changes Claude Code makes on its own, ... | |Elicitation| When an MCP server requests user input during a tool call | |ElicitationResult| After a user responds to an MCP elicitation, before the response is sent back to the server | |SessionEnd| When a session terminates |
Sursa: https://code.claude.com/docs/en/hooks-guide, sectiunea "How hooks work".
Schema JSON exacta pe stdin/stdout pentru PreCompact / SessionStart / Stop / SubagentStop
NEDOCUMENTAT (in sensul de mai sus: nu am putut confirma verbatim). Fetch-ul repetat pe
hooks (inclusiv varianta .md) intoarce constant tabelul "Common input fields" (comun tuturor
evenimentelor, vezi mai jos) si tabelul "Exit code 2 behavior per event" doar pana la randul
Stop; sectiunile individuale ### SessionStart, ### PreCompact, ### Stop, ### SubagentStop
cu exemplele lor JSON complete nu au putut fi extrase — raspunsul explicit al fetch-ului a fost
"the actual detailed 'Hook events' section ... appears to be cut off or not included" si "I
cannot find a subsection literally titled 'PreCompact'... in the provided content". Nu inseamna
ca documentatia oficiala nu contine acele scheme (aproape sigur le contine, pagina fiind
"Hooks reference" completa), ci ca uneltele disponibile in aceasta sesiune nu au putut sa le
aduca integral.
Ce s-a confirmat verbatim despre campurile comune (tabelul "Common input fields", identic la
doua fetch-uri separate pe hooks si pe hooks.md):
| Field | Description | |
session_id| Current session identifier | |prompt_id| UUID identifying the user prompt currently being processed. ... Absent until the first user input. Requires Claude Code v2.1.196 or later | |transcript_path| Path to conversation JSON. The transcript file is written asynchronously and may lag the in-memory conversation, so it may not yet include the current turn's most recent messages when a hook fires. Hooks that need the final assistant text of the current turn should uselast_assistant_messageon Stop and SubagentStop instead of reading the transcript | |cwd| Current working directory when the hook is invoked | |scratchpad_dir| Path to the session's scratchpad directory, where Claude keeps temporary working files. Absent when the session has no scratchpad or the temp directory is unavailable. Requires Claude Code v2.1.257 or later | |permission_mode| Current permission mode:"default","plan","acceptEdits","auto","dontAsk", or"bypassPermissions". ... | |effort| Object with alevelfield holding the effort level in effect when the hook runs... Present for events that fire within a tool-use context, such asPreToolUse,PostToolUse,Stop, andSubagentStop, when the current model supports the effort parameter. | |hook_event_name| Name of the event that fired |When running with
--agentor inside a subagent, two additional fields are included:| Field | Description | |
agent_id| Unique identifier for the subagent. Present only when the hook fires inside a subagent call. Use this to distinguish subagent hook calls from main-thread calls. | |agent_type| Agent name (for example,"Explore"or"security-reviewer"). Present when the session uses--agentor the hook fires inside a subagent. For subagents, the subagent's type takes precedence over the session's--agentvalue. |
Sursa: https://code.claude.com/docs/en/hooks, sectiunea "Common input fields" (confirmat identic si pe varianta .md).
Nota: campul trigger (pentru PreCompact/PostCompact, valori manual/auto) si source
(pentru SessionStart, valori startup/resume/clear/compact/fork) sunt mentionate ca
valori de matcher, nu confirmate ca nume exact de camp JSON pe stdin — vezi tabelul de
matchere de la punctul 3.
Ce s-a confirmat despre SessionStart prin exemplu real de configurare (verbatim, din
hooks-guide, sectiunea "Re-inject context after compaction"):
When Claude's context window fills up, compaction summarizes the conversation to free space. This can lose important details. Use a
SessionStarthook with acompactmatcher to re-inject critical context after every compaction.Claude Code adds plain text your command writes to stdout to Claude's context.
{
"hooks": {
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{ "type": "command", "command": "echo 'Reminder: use Bun, not npm. ...'" }
]
}
]
}
}
Schema de iesire (hookSpecificOutput, additionalContext, systemMessage, decision,
continue, stopReason) pentru cele 4 evenimente cerute: NEDOCUMENTAT in sensul de mai sus
pentru forma completa exacta. S-a confirmat insa, verbatim, forma generala de output pentru
UserPromptSubmit (acelasi tipar hookSpecificOutput.additionalContext, aplicabil probabil si
altor evenimente, dar nu s-a putut confirma explicit pentru SessionStart/Stop/SubagentStop):
For
UserPromptSubmithooks, usehookSpecificOutput.additionalContextinstead to inject text into Claude's context. NestadditionalContextinsidehookSpecificOutput; if you place it at the top level of the JSON, Claude Code silently ignores it.
{
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "Current branch: release-42. Deploy freeze until Friday."
}
}
Other events use different decision patterns. For example,
PostToolUseandStophooks use a top-leveldecision: "block"field, whilePermissionRequestuseshookSpecificOutput.decision.behavior.
Sursa: https://code.claude.com/docs/en/hooks-guide, sectiunea "Structured JSON output".
2. Tokeni consumati / marimea contextului in input-ul hook-urilor
Confirmat, de doua ori, cautare pe intreaga pagina hooks: nu exista niciun camp de tokeni
in input-ul hook-urilor.
Search for "stop_hook_active": No matches found for the term "stop_hook_active" on this page. Search for "token", "usage", and "input_tokens": No matches found for these terms on this page.
(cautarea a fost facuta explicit pe pagina "Hooks reference"; rezultatul e consistent la doua apeluri separate — semnal ca reflecta continutul real, nu o presupunere a modelului de sumarizare)
Concluzie: singura sursa documentata pentru starea conversatiei ramane transcript_path
(fisier .jsonl), descris astfel (verbatim, vezi campurile comune de mai sus):
Path to conversation JSON. The transcript file is written asynchronously and may lag the in-memory conversation, so it may not yet include the current turn's most recent messages when a hook fires.
NEDOCUMENTAT: schema exacta a unei linii din transcript_path (daca fiecare linie JSONL are
un obiect usage cu input_tokens/cache_read_input_tokens). Nu am gasit nicio sectiune care sa
descrie formatul intern al transcriptului; pagina de hooks il trateaza doar ca „path to
conversation JSON", fara schema de linie.
Singurul loc unde a aparut o notiune de "context folosit" e statusline-ul (alta functionalitate,
nu un hook), in exemplul din settings-example:
"command": "jq -r '"[\(.model.display_name)] \(.context_window.used_percentage // 0)% context"'"
adica statusline-ul primeste context_window.used_percentage — dar acesta e inputul JSON al
comenzii de statusLine, nu al vreunui hook (PreCompact/Stop/etc.). Sursa:
https://code.claude.com/docs/en/settings-example, sectiunea "Your own settings".
3. Declansarea programatica a compactarii; PreCompact poate bloca?
Confirmat verbatim (tabelul de matchere, hooks-guide, sectiunea "Filter hooks with matchers"):
| Event | What the matcher filters | Example matcher values | |
PreCompact,PostCompact| what triggered compaction |manual,auto|
Nu exista alta explicatie a diferentei functionale dintre manual si auto in continutul pe care
am putut sa-l confirm — NEDOCUMENTAT in acest fetch (probabil documentat in sectiunea
"Hooks reference" pe care nu am putut-o extrage integral).
Despre blocare: tabelul "Exit code 2 behavior per event" s-a confirmat identic de 3 ori, dar
mereu trunchiat la randul Stop:
| Hook event | Can block? | What happens on exit 2 | |
PreToolUse| Yes | Blocks the tool call | |PermissionRequest| No | Exit code 2 isn't honored for this event and the permission flow proceeds unchanged. ... | |UserPromptSubmit| Yes | Blocks prompt processing and erases the prompt | |UserPromptExpansion| Yes | Blocks the expansion | |Stop| Yes | Prevents Claude from stopping, continues the conversation |
Randul pentru PreCompact (si SubagentStop, SessionStart) nu a putut fi extras — nici prin
fetch normal, nici prin varianta .md, nici prin cereri tintite doar pe randul lipsa.
NEDOCUMENTAT explicit aici: daca PreCompact poate bloca compactarea prin exit code 2.
(Rationament indirect, NEconfirmat ca fapt: linia generala din ghid — "Some events can't be
blocked: for SessionStart and others, exit 2 shows stderr to the user and execution continues" —
sugereaza ca exista o categorie de evenimente needitabile prin exit 2, dar nu specifica daca
PreCompact e in acea categorie sau in cealalta.)
Citat sigur, din hooks-guide, care mentioneaza explicit ca SessionStart NU poate fi blocat:
Some events can't be blocked: for
SessionStartand others, exit 2 shows stderr to the user and execution continues.
Declansare programatica a compactarii: NEDOCUMENTAT in continutul confirmat — nu am gasit un flag/comanda explicita de tip "trigger compaction now" in paginile fetch-uite (hooks, hooks-guide, settings, settings-reference, env-vars). Doar variabila urmatoare influenteaza PRAGUL, nu declansarea manuala programatica:
CLAUDE_AUTOCOMPACT_PCT_OVERRIDE— Set the percentage (1-100) of the auto-compact window at which auto-compaction triggers. Use lower values like50to compact earlier; the variable can't raise the threshold, so values above the default percentage are ignored. It applies only in sessions that compact before the model's context limit. Applies to both main conversations and subagents.
Sursa: https://code.claude.com/docs/en/env-vars.
4. Hook-uri in subagenti (Task/Agent); SubagentStop; bucla stop_hook_active
Confirmat verbatim (hooks-guide, sectiunea "Limitations"):
Background subagents can't show a prompt in non-interactive mode. Claude Code still runs the hooks for their tool calls, and if no hook returns a decision, it denies the call. In an interactive session, background subagent prompts surface in your main session and the hooks fire as usual.
Deci: hook-urile de tip PreToolUse/PostToolUse etc. se declanseaza si in interiorul
subagentilor, pentru apelurile lor de unelte — confirmat explicit doar pentru cazul
PermissionRequest/permisiuni; pentru restul evenimentelor (PreToolUse, PostToolUse propriu-zise
in subagent) nu am gasit o fraza separata la fel de explicita, dar tabelul de scope de configurare
confirma ca hook-urile de subagent exista ca mecanism dedicat:
| Location | Scope | Shareable | | Subagent frontmatter | While that subagent is running | Yes, defined in the subagent file |
Sursa: https://code.claude.com/docs/en/hooks-guide, sectiunea "Configure hook location".
SubagentStop — definitie confirmata din tabelul de evenimente (punctul 1):
SubagentStop| When a subagent finishes
NEDOCUMENTAT (nesigur): o extractie initiala a afirmat ca exista fraza "Claude Code converts
a Stop hook here to SubagentStop, the event it fires when a subagent completes" — aceasta
fraza NU s-a mai reprodus la recitirea directa a continutului brut al hooks-guide (1065 de
linii citite integral), asa ca nu o citez ca fapt confirmat. Nu neg ca ar fi adevarata (e
plauzibila si consistenta cu restul mecanismului de scope pe subagent), doar ca nu am reusit sa o
verific verbatim in aceasta sesiune.
stop_hook_active si bucla
Confirmat verbatim, din hooks-guide, sectiunea "Stop hook hits the block cap":
Claude keeps working instead of stopping, then ends the turn with a warning that the Stop hook blocked too many consecutive times.
Claude Code overrides a Stop hook after it blocks eight times in a row without progress. Your hook script needs to check whether it already triggered a continuation. Parse the
stop_hook_activefield from the JSON input and exit early if it'strue:
#!/bin/bash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
exit 0 # Allow Claude to stop
fi
# ... rest of your hook logic
If your hook legitimately needs more than eight iterations to converge, raise the cap with
CLAUDE_CODE_STOP_HOOK_BLOCK_CAP.
Deci: cauza tipica a buclei e un hook Stop/SubagentStop care intoarce mereu o decizie de
blocare (exit 2 / decision: "block") fara sa verifice stop_hook_active, astfel incat Claude
Code il tot re-invoca; plafonul e 8 blocari consecutive "fara progres", dupa care Claude Code
suprascrie hook-ul si opreste turul cu un avertisment. Campul stop_hook_active exista exact ca
sa permita hook-ului sa detecteze ca a mai fost invocat o data in acelasi ciclu de "stop" si sa
cedeze (exit 0) in loc sa continue sa blocheze.
Variabila de mediu asociata, confirmata din env-vars (extras separat, cu wording plauzibil dar
NEverificat printr-un al doilea fetch identic — trateaza ca moderat sigur, nu ca sigur):
CLAUDE_CODE_STOP_HOOK_BLOCK_CAP— Maximum number of blocks a stop hook can request before Claude Code stops respecting further requests from that hook and logs a warning (default:100). Useful when a hook inadvertently loops and repeatedly requests stops.
Nota: valoarea implicita citata aici de fetch (100) contrazice cifra "eight times in a row"
din hooks-guide (confirmata de doua ori, sigura). Nu pot reconcilia cele doua cifre din
continutul disponibil — posibil ca 8 sa fie plafonul implicit "fara progres" mentionat in ghid, iar
100 sa fie un plafon absolut diferit citit gresit de fetch-ul pe env-vars (surse nereconciliate,
posibil eroare de extractie pe aceasta a doua cifra). Trateaza cifra "100" ca NEDOCUMENTAT/de
reverificat manual, foloseste "8" ca fiind confirmat de doua ori pe pagina oficiala a ghidului.
5. Setari relevante in settings.json
Confirmat din tabelul settings-reference (randuri, fara detaliu de default/exemplu — sectiunile detaliate de sub tabel nu au putut fi extrase, pagina prea mare):
| Key | Description | Topic | Scope | |
autoCompactEnabled| Turn automatic compaction off or on | Memory and context | Any file | |autoCompactWindow| Set how full the context gets before Claude Code compacts | Memory and context | Any file | |cleanupPeriodDays| Choose how many days Claude Code keeps transcripts before deleting them | Privacy and telemetry | Any file | |env| Set environment variables for every session and its subprocesses | Memory and context | Any file |
Sursa: https://code.claude.com/docs/en/settings-reference. NEDOCUMENTAT aici: valorile
implicite exacte pentru autoCompactEnabled si autoCompactWindow (sectiunile detaliate nu s-au
putut extrage).
Exemple reale confirmate (cleanupPeriodDays, env), din https://code.claude.com/docs/en/settings-example:
// ~/.claude/settings.json — un dezvoltator
{
"...": "...",
"cleanupPeriodDays": 20
}
Delete session transcripts and other local session data older than 20 days
// .claude/settings.json — o echipa
{
"env": {
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
"OTEL_METRICS_EXPORTER": "otlp",
"OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://collector.example.com:4317"
},
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh" }
]
}
]
}
}
// managed-settings.json — o organizatie
{
"...": "...",
"cleanupPeriodDays": 7
}
Delete session transcripts and other local session data after 7 days
Variabile CLAUDE_CODE_* legate de context/compactare, confirmate din
https://code.claude.com/docs/en/env-vars:
CLAUDE_AUTOCOMPACT_PCT_OVERRIDE— Set the percentage (1-100) of the auto-compact window at which auto-compaction triggers. Use lower values like50to compact earlier; the variable can't raise the threshold, so values above the default percentage are ignored. Applies to both main conversations and subagents.
CLAUDE_CODE_STOP_HOOK_BLOCK_CAP— vezi punctul 4 (cifra de default nereconciliata).
NEDOCUMENTAT in acest fetch: nu am gasit alte variabile CLAUDE_CODE_* explicit legate de
"context size" ca numar de tokeni (cautarea pe env-vars a fost limitata la cuvintele
COMPACT/CONTEXT/TOKEN/STOP_HOOK; nu a intors nimic cu "CONTEXT" in nume).
Rezumat pentru cine investigheaza "subagent stop violation"
- Nu exista niciun camp de tokeni/marime-context in inputul niciunui hook (confirmat, cautare
directa pe pagina oficiala). Singura sursa e
transcript_path, iar formatul intern al liniilor JSONL nu e documentat in paginile verificate. - Bucla clasica de
Stop/SubagentStopare o cauza documentata si un mecanism de iesire: campulstop_hook_activepe input, plafon confirmat de "8 blocari la rand fara progres" dupa care Claude Code preia controlul si opreste turul cu avertisment (hooks-guide, sectiunea "Stop hook hits the block cap"). - Hook-urile ruleaza si in subagenti; input-ul lor primeste in plus
agent_id/agent_typefata de campurile comune. - Schemele JSON complete (toate campurile) pentru
PreCompact/SessionStart/Stop/SubagentStopsi detaliul exact al blocarii pentruPreCompactNU au putut fi confirmate verbatim in aceasta sesiune — pagina oficiala "Hooks reference" (https://code.claude.com/docs/en/hooks) le contine aproape sigur, dar depaseste ce a putut extrage fetch-ul disponibil; de reluat cu acces direct (browser) daca e nevoie de schema exacta camp-cu-camp.