21 KiB
# Referinta oficiala: SessionStart / PostCompact / Stop (surse: code.claude.com/docs)
Toate citatele de mai jos sunt verbatim din versiunile .md brute ale paginilor
(https://code.claude.com/docs/en/hooks.md, .../hooks-guide.md), fetch-uite direct cu
curl pe 2026-09-17 (WebFetch trunchiaza paginile astea, sunt prea mari pentru modelul
intern al tool-ului — vezi nota metodologica de la final). Nu am dedus nimic; unde n-am gasit
un raspuns explicit, scriu "NEDOCUMENTAT".
1. SessionStart — valori de matcher acceptate
CONFIRMAT, dar lista din brief e incompleta: sunt 5 valori, nu 4. Lipsea fork.
Sursa: https://code.claude.com/docs/en/hooks.md, sectiunea ### SessionStart:
The matcher value corresponds to how the session was initiated:
Matcher When it fires startupNew session resume--resume,--continue, or/resumeclear/clearcompactAuto or manual compaction forkA new session forked from an existing one: --fork-sessionwith--resumeor--continue, the/forkbackground copy, or/branchBefore v2.1.214, forked sessions reported source
"resume".
Important pentru mecanismul propus: compact e si el un matcher de SessionStart, separat
de evenimentul PostCompact (vezi punctul 4). Adica dupa o compactare (auto sau manuala),
Claude Code re-porneste efectiv un SessionStart cu source: "compact", iar acela SUPORTA
additionalContext — spre deosebire de PostCompact propriu-zis, care nu suporta (punctul 4).
2. SessionStart — campuri pe stdin, cum distinge /clear de pornire normala
Sursa: https://code.claude.com/docs/en/hooks.md, #### SessionStart input:
In addition to the common input fields, SessionStart hooks receive
sourceand optionallymodel,agent_type, andsession_title:
Field Description sourceHow the session started: "startup"for new sessions,"resume"for resumed sessions,"clear"after/clear,"compact"after compaction, or"fork"for a new session forked from an existing onemodelThe active model identifier. It can be omitted, for example after /clearor when a session is restored through conversation recovery, so check for the field before reading itagent_typeThe agent name, present when you start Claude Code with claude --agent <name>session_titleThe current session title if one is already set, for example via --nameor/rename. A hook that emitssessionTitlecan checksession_titlefirst to avoid overwriting a title the user set explicitly
Distinctia /clear vs pornire normala se face STRICT prin campul source: "clear" vs
"startup". Nu exista alt semnal necesar.
Campuri comune (din #### Common input fields, aceeasi pagina), relevante pentru mecanism:
session_id, prompt_id, transcript_path, cwd, scratchpad_dir, permission_mode,
effort, hook_event_name. Citat exact pentru transcript_path:
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.
Cand source e "resume" sau "fork" si transcript-ul are cel putin un raspuns Claude, mai
vin 4 campuri (necesare v2.1.251+): seconds_since_last_response, context_tokens,
prompt_cache_likely_expired, estimated_cache_write_usd. Aceste campuri NU apar pentru
source: "clear" — deci hook-ul nu primeste de la Claude Code o estimare gata facuta a
cate token-i "costa" reluarea; asta ramane treaba handoff-ului scris pe disc.
agent_type si agent_id (subagent-only) — prezente doar cand hook-ul ruleaza intr-un
subagent sau sesiunea foloseste --agent.
Timing: la pornire/--resume/--continue//clear, hook-urile SessionStart ruleaza in
fundal — poti scrie imediat, dar primul raspuns al lui Claude asteapta sa termine hook-urile.
La /resume in interiorul unei sesiuni, switch-ul asteapta hook-urile. Citat:
When you start an interactive session, resume a conversation at launch with
--continueor--resume, or run/clear, SessionStart hooks run in the background. You can type right away, and a conversation you resumed appears without waiting for the hooks. Claude's first response still waits for the hooks to finish, so their context reaches Claude.
3. SessionStart — cum returneaza context, forma exacta, limita de marime
Sursa: https://code.claude.com/docs/en/hooks.md, #### SessionStart decision control +
exemplu JSON:
In addition to the JSON output fields available to all hooks, you can return these event-specific fields:
Field Description additionalContextString added to Claude's context at the start of the conversation, before the first prompt. initialUserMessageString used as the first user message of the session. Applies in non-interactive mode with -p...sessionTitleSets the session title, with the same effect as /rename... Applies whensourceis"startup","resume", or"fork"; ignored on"clear"and"compact"watchPathsArray of absolute paths to watch for FileChanged events during this session reloadSkillsBoolean. When true, re-scans skill/command dirs after SessionStart hooks complete...{ "hookSpecificOutput": { "hookEventName": "SessionStart", "additionalContext": "Current branch: feat/auth-refactor\nUncommitted changes: src/auth.ts, src/login.tsx\nActive issue: #4211 Migrate to OAuth2", "sessionTitle": "auth-refactor" } }
Confirmat: e hookSpecificOutput cu hookEventName: "SessionStart" si additionalContext
imbricat inauntru — exact forma presupusa in brief.
Limita de marime (generala, se aplica la SessionStart la fel ca la toate evenimentele),
sursa: https://code.claude.com/docs/en/hooks.md, #### Add context for Claude:
The
additionalContextfield passes a string from your hook into Claude's context window. Claude Code wraps the string in a system reminder and inserts it into the conversation at the point where the hook fired.[...]
If a value exceeds 10,000 characters, Claude Code writes the text to a file in the session directory and passes Claude the file path with a short preview instead.
Deci: 10.000 de caractere e pragul. Peste el, Claude Code NU trunchiaza si NU refuza — scrie continutul intr-un fisier in directorul sesiunii si trimite lui Claude calea + un preview scurt. Pentru un handoff mare, asta e de fapt convenabil: hook-ul poate trimite tot textul, iar peste prag Claude Code il redirectioneaza singur spre fisier.
Unde ajunge textul pentru SessionStart specific, din acelasi paragraf:
- SessionStart and SubagentStart: at the start of the conversation, before the first prompt
Nota despre re-rulare la resume, aceeasi sectiune:
SessionStarthooks run again on resume withsourceset to"resume", or"fork"if you added--fork-session, so they can refresh their context.
(Nu mentioneaza explicit re-rularea la /clear, dar tabelul de matchere de la punctul 1 o
confirma separat: clear e chiar unul dintre matcherele native ale evenimentului.)
Exit code: stdout simplu (fara JSON) e tratat ca text simplu si ajunge in context, la fel ca
additionalContext — nu trebuie neaparat JSON daca hook-ul nu seteaza si alte campuri. Citat:
Claude Code adds stdout it treats as plain text to Claude's context. [...] Since plain stdout already reaches Claude for this event, a hook that only loads context can print to stdout directly without building JSON. Use the JSON form when you need to combine context with other fields such as
sessionTitle.
4. PostCompact — aceleasi intrebari, plus manual vs auto
Sursa: https://code.claude.com/docs/en/hooks.md, ### PostCompact:
Runs after Claude Code completes a compact operation. Use this event to react to the new compacted state, for example to log the generated summary or update external state. Claude Code discards a PostCompact hook's
systemMessageandcontinuefields.The same matcher values apply as for
PreCompact:
Matcher When it fires manualAfter /compactautoAfter auto-compact when the conversation reaches the auto-compact window PostCompact input
In addition to the common input fields, PostCompact hooks receive
triggerandcompact_summary. Thecompact_summaryfield contains the conversation summary generated by the compact operation.{ "session_id": "abc123", "transcript_path": "...", "cwd": "...", "hook_event_name": "PostCompact", "trigger": "manual", "compact_summary": "Summary of the compacted conversation..." }PostCompact hooks have no decision control. They can't affect the compaction result but can perform follow-up tasks.
Constatare critica pentru mecanismul propus: PostCompact NU poate injecta
additionalContext si nu poate influenta rezultatul compactarii — e strict pentru efecte
secundare (log, notificare externa etc.). Daca planul se baza pe "PostCompact reinjecteaza
handoff-ul", premisa e falsa. Calea care CHIAR functioneaza pentru compactare e
SessionStart cu matcher compact (punctul 1 si 3) — un eveniment diferit, care ruleaza dupa
PostCompact si care are additionalContext. Exemplul oficial de "re-inject context after
compaction" din hooks-guide.md confirma asta explicit:
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.
Pentru PreCompact (nu a fost cerut explicit, dar e relevant ca sa nu confundati): poate bloca
(decision: "block" sau exit 2) si primeste trigger + custom_instructions, dar la fel
"discards systemMessage and continue". PreCompact nu e mecanismul de reinjectare — ruleaza
INAINTE de compactare.
5. Poate un hook sa declanseze /clear sau /compact?
NU exista niciun mecanism. Confirmat explicit, nu doar prin absenta.
Sursa: https://code.claude.com/docs/en/hooks-guide.md, linia 952:
Command hooks communicate through stdout, stderr, and exit codes only. They can't trigger
/commands or tool calls. Text returned viaadditionalContextis injected as a system reminder that Claude reads as plain text. HTTP hooks communicate through the response body instead.
Am cautat explicit orice camp de tip "command" sau mecanism de rulare a unei comenzi slash
din output-ul unui hook, in tot hooks.md (3840 linii) si hooks-guide.md (1064 linii) — nu
exista. Singurele cai care declanseaza efectiv o compactare/clear raman actiunile native ale
utilizatorului (/clear, /compact) sau host-ul/SDK-ul care porneste sesiunea.
6. Stop hook — poate bloca oprirea si injecta o instructiune?
Da, in doua moduri diferite, sursa https://code.claude.com/docs/en/hooks.md,
### Stop + #### Stop input + #### Stop decision control:
Input:
In addition to the common input fields, Stop hooks receive
stop_hook_active,last_assistant_message,background_tasks, andsession_crons. Thestop_hook_activefield istruewhen Claude Code is already continuing as a result of a stop hook. Check this value or process the transcript to avoid blocking on a condition that will never resolve. Claude Code overrides the hook and ends the turn after 8 consecutive blocks.
Decision control:
StopandSubagentStophooks can control whether Claude continues. In addition to the JSON output fields available to all hooks, your hook script can return these event-specific fields:
Field Description decision"block"prevents Claude from stopping. Omit to allow Claude to stopreasonRequired when decisionis"block". Tells Claude why it should continuehookSpecificOutput.additionalContextNon-error feedback for Claude. The conversation continues so Claude can act on it, but unlike decision: "block"it is shown in the transcript as hook feedback rather than a hook errorA hook that blocks by exiting 2 routes the same way as
reason: Claude receives the stderr message as the explanation for why it should continue.{ "decision": "block", "reason": "Must be provided when Claude is blocked from stopping" }Use
additionalContextwhen the hook is working as designed and giving Claude guidance, such as "run the test suite before finishing". It keeps the conversation going through the same loop protections asdecision: "block", namely thestop_hook_activeinput and the 8-consecutive-continuation cap, but the transcript labels itStop hook feedbackand no hook error notification is shown.
Deci: decision: "block" + reason (top-level, NU in hookSpecificOutput) forteaza
continuarea si arata reason ca eroare de hook; hookSpecificOutput.additionalContext face
acelasi lucru dar apare ca "Stop hook feedback", nu ca eroare. Ambele variante trec prin
aceleasi limite de bucla: campul stop_hook_active (hook-ul trebuie sa verifice singur ca sa
nu se blocheze la infinit) si plafonul intern de 8 blocari consecutive fara progres, dupa
care Claude Code forteaza oprirea oricum.
Exit code 2 pe Stop: din tabelul citat separat in hooks.md, poate bloca (spre deosebire de
SessionStart, unde exit 2 doar arata mesajul utilizatorului si continua executia).
Aplicabil direct la ideea "Stop scrie handoff-ul acum": da, se poate implementa —
hook-ul Stop verifica o conditie (context mare, sarcina neterminata etc.), daca handoff-ul nu
exista inca pe disc raspunde cu decision: "block" + reason: "scrie handoff-ul pe disc inainte de oprire", Claude continua turul, scrie handoff-ul, iar hook-ul (cand ruleaza din nou la
urmatoarea incercare de Stop) vede handoff-ul pe disc si lasa oprirea sa treaca. Trebuie
verificat stop_hook_active ca sa nu intre in bucla si respectat plafonul de 8.
VERDICT PRACTIC
Lantul complet "handoff pe disc -> /clear -> reinjectare automata" se poate construi, cu doua completari fata de premisa initiala:
-
Partea automata, confirmata:
SessionStartcu matcherclear(sau, pentru compactare, matchercompact) fireste dupa/clear/compactare, primestesourceexplicit ("clear"/"compact") — hook-ul poate citi handoff-ul de pe disc si il injecta prinhookSpecificOutput.additionalContext, INAINTE de primul prompt al sesiunii noi. Asta merge din prima, fara nicio interventie manuala suplimentara, pentru ambele declansatoare (/clearSI compactare — nu doar/clear).- Nu exista limita blocanta de marime: sub 10.000 caractere textul intra direct in context; peste, Claude Code il scrie singur intr-un fisier si trimite calea — deci un handoff mare nu se pierde, doar se livreaza indirect.
Stoppoate fi folosit ca plasa de siguranta care FORTEAZA scrierea handoff-ului inainte de oprire (decision: "block"+reason), cu conditia sa respectestop_hook_activesi plafonul de 8 blocari.
-
Ce ramane obligatoriu manual / in afara hook-urilor:
- Niciun hook nu poate declansa
/clearsau/compactsingur — confirmat explicit in documentatie ("can't trigger/commands or tool calls"). Utilizatorul (sau orchestrator-ul, daca ruleaza in headless/SDK cu control asupra sesiunii) trebuie sa emita el actiunea. - Niciun hook nu anunta "am ajuns la ~50% context" — nu exista un eveniment de tip
"context threshold reached". Campurile
context_tokensetc. apar DOAR peSessionStartcusource: "resume"/"fork", dupa fapt — nu in timp real, in timpul sesiunii curente. Decizia de "e timpul sa scriu handoff-ul" ramane a modelului/sesiunii, exact cum descrie deja regula dinCLAUDE.md— hook-urile nu o pot automatiza. - Premisa gresita de reparat: daca planul se baza pe
PostCompactpentru reinjectare, nu functioneaza —PostCompactn-aredecision controldeloc. Mecanismul corect pentru compactare eSessionStartcu matchercompact, nuPostCompact.
- Niciun hook nu poate declansa
Pe scurt: partea "citeste handoff de pe disc si baga-l inapoi in context la (re)pornire" e
100% automata prin SessionStart. Partea "declanseaza tu insuti /clear" ramane 100% manuala —
nu exista ocolire documentata. Stop poate automatiza doar "nu te opri pana nu ai scris
handoff-ul pe disc", nu si declansarea lui /clear dupa aceea.
Nota metodologica
WebFetch (tool-ul standard) trunchiaza/rezuma paginile hooks si hooks-guide inainte sa
ajunga la sectiunile per-eveniment (confirmat empiric: trei incercari diferite de prompt au
esuat sa extraga ### SessionStart, ### Stop, ### PostCompact din pagina hooks, desi
tabelele de sus ale paginii ies corect). Documentatia Mintlify expune si varianta bruta:
https://code.claude.com/docs/en/hooks.md si .../hooks-guide.md (mentionate chiar de pagina:
"Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt"). Am fetch-uit
acele .md-uri direct prin curl (citire read-only, fara nicio scriere in afara acestui
livrabil) si am citat din ele. Toate citatele de mai sus sunt verbatim din acele fisiere.