Files
roafacturare/docs/qa_factura_hooks_ref.md
2026-09-17 22:36:46 +03:00

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:

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 --init or --maintenance in -p mode. 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 via TaskCreate | | 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/*.md file is loaded into context. ... | | ConfigChange | When a configuration file changes during a session | | CwdChanged | When the working directory changes, for example when Claude executes a cd command. ... | | DirectoryAdded | When a working directory is added mid-session via /add-dir or the SDK register_repo_root control request | | FileChanged | When a watched file changes on disk. The matcher field 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 use last_assistant_message on 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 a level field holding the effort level in effect when the hook runs... Present for events that fire within a tool-use context, such as PreToolUse, PostToolUse, Stop, and SubagentStop, when the current model supports the effort parameter. | | hook_event_name | Name of the event that fired |

When running with --agent or 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 --agent or the hook fires inside a subagent. For subagents, the subagent's type takes precedence over the session's --agent value. |

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 SessionStart hook with a compact matcher 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 UserPromptSubmit hooks, use hookSpecificOutput.additionalContext instead to inject text into Claude's context. Nest additionalContext inside hookSpecificOutput; 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, PostToolUse and Stop hooks use a top-level decision: "block" field, while PermissionRequest uses hookSpecificOutput.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 SessionStart and 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 like 50 to 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_active field from the JSON input and exit early if it's true:

#!/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 like 50 to 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/SubagentStop are o cauza documentata si un mecanism de iesire: campul stop_hook_active pe 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_type fata de campurile comune.
  • Schemele JSON complete (toate campurile) pentru PreCompact/SessionStart/Stop/SubagentStop si detaliul exact al blocarii pentru PreCompact NU 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.