sync SVN r18174

This commit is contained in:
2026-09-17 22:36:46 +03:00
parent 04a78f99db
commit ca2056d06a
13 changed files with 2159 additions and 0 deletions

330
docs/sessionstart_ref.md Normal file
View File

@@ -0,0 +1,330 @@
\# 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 <name>` |
> | `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.