\# 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 | > | :-------- | :------------------------------------------------------------------------------------------------------------------------------------- | > | `startup` | New session | > | `resume` | `--resume`, `--continue`, or `/resume` | > | `clear` | `/clear` | > | `compact` | Auto or manual compaction | > | `fork` | A new session forked from an existing one: `--fork-session` with `--resume` or `--continue`, the `/fork` background copy, or `/branch` | > > Before 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](#common-input-fields), SessionStart hooks receive > `source` and optionally `model`, `agent_type`, and `session_title`: > > | Field | Description | > | :-------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | > | `source` | How 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 one | > | `model` | The active model identifier. It can be omitted, for example after `/clear` or when a session is restored through conversation recovery, so check for the field before reading it | > | `agent_type` | The agent name, present when you start Claude Code with `claude --agent ` | > | `session_title` | The current session title if one is already set, for example via `--name` or `/rename`. A hook that emits `sessionTitle` can check `session_title` first 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 `--continue` or > `--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](#json-output) available to all hooks, you can return > these event-specific fields: > > | Field | Description | > | :-------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | > | `additionalContext` | String added to Claude's context at the start of the conversation, before the first prompt. | > | `initialUserMessage` | String used as the first user message of the session. Applies in non-interactive mode with `-p`... | > | `sessionTitle` | Sets the session title, with the same effect as `/rename`... Applies when `source` is `"startup"`, `"resume"`, or `"fork"`; ignored on `"clear"` and `"compact"` | > | `watchPaths` | Array of absolute paths to watch for FileChanged events during this session | > | `reloadSkills` | Boolean. When `true`, re-scans skill/command dirs after SessionStart hooks complete... | > > ```json > { > "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 `additionalContext` field 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](#sessionstart) and [SubagentStart](#subagentstart): at the start of the > conversation, before the first prompt Nota despre re-rulare la resume, aceeasi sectiune: > `SessionStart` hooks run again on resume with `source` set 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 `systemMessage` and `continue` fields. > > The same matcher values apply as for `PreCompact`: > > | Matcher | When it fires | > | :------- | :----------------------------------------------------------------------------------------------------------------------- | > | `manual` | After `/compact` | > | `auto` | After auto-compact when the conversation reaches the auto-compact window | > > #### PostCompact input > > In addition to the common input fields, PostCompact hooks receive `trigger` and > `compact_summary`. The `compact_summary` field contains the conversation summary generated by > the compact operation. > > ```json > { > "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 `SessionStart` hook with a `compact` matcher 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 via `additionalContext` is 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`, and `session_crons`. The `stop_hook_active` > field is `true` when 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: > `Stop` and `SubagentStop` hooks 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 stop | > | `reason` | Required when `decision` is `"block"`. Tells Claude why it should continue | > | `hookSpecificOutput.additionalContext` | Non-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 error | > > A 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. > > ```json > { > "decision": "block", > "reason": "Must be provided when Claude is blocked from stopping" > } > ``` > > Use `additionalContext` when 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 as `decision: "block"`, namely the `stop_hook_active` input and the > 8-consecutive-continuation cap, but the transcript labels it `Stop hook feedback` and 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:** 1. **Partea automata, confirmata**: - `SessionStart` cu matcher `clear` (sau, pentru compactare, matcher `compact`) fireste dupa `/clear`/compactare, primeste `source` explicit (`"clear"` / `"compact"`) — hook-ul poate citi handoff-ul de pe disc si il injecta prin `hookSpecificOutput.additionalContext`, INAINTE de primul prompt al sesiunii noi. Asta merge din prima, fara nicio interventie manuala suplimentara, pentru ambele declansatoare (`/clear` SI 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. - `Stop` poate fi folosit ca plasa de siguranta care FORTEAZA scrierea handoff-ului inainte de oprire (`decision: "block"` + `reason`), cu conditia sa respecte `stop_hook_active` si plafonul de 8 blocari. 2. **Ce ramane obligatoriu manual / in afara hook-urilor**: - **Niciun hook nu poate declansa `/clear` sau `/compact` singur** — 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_tokens` etc. apar DOAR pe `SessionStart` cu `source: "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 din `CLAUDE.md` — hook-urile nu o pot automatiza. - **Premisa gresita de reparat**: daca planul se baza pe `PostCompact` pentru reinjectare, nu functioneaza — `PostCompact` n-are `decision control` deloc. Mecanismul corect pentru compactare e `SessionStart` cu matcher `compact`, nu `PostCompact`. **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.