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

194 lines
12 KiB
Markdown

# Verificare "function hooks" — Claude Code 2.1.274 (nativ, win32-x64)
Sursa de verificat: https://claudefa.st/blog/tools/hooks/function-hooks (blog tert, neoficial).
Metoda: (1) incercare de rulare reala `/plugin-types`; (2) cautare de siruri si context in
binarul nativ instalat, `C:\Users\mmari\.local\bin\claude.exe` (233691808 octeti, Bun-compiled,
comitul `1efcc1361e64`). Fiecare constatare de mai jos e marcata cu sursa ei.
## 1. Rularea comenzii cerute
```
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude -p "/plugin-types ./types"
```
Rezultat: **nu s-a generat niciun fisier**. Raspunsul modelului a fost text conversational
("Ready — no task given yet." / la reincercare cu `--output-format json`: `"I'm ready. What
would you like me to work on?"`). Niciun folder `types/` sau `.claude/types/` nu a aparut in
scratchpad dupa rulare (verificat cu `ls`).
**Cauza identificata, nu specifica function hooks**: am testat control cu `claude -p "/help"`
(comanda locala cea mai de baza din tot CLI-ul, intotdeauna inregistrata, fara nicio poarta de
feature-flag). A esuat identic — modelul a primit "/help" ca text mangled si a raspuns
conversational, in loc sa afiseze ajutorul. **Concluzie: invocarea `claude -p "<text>"` din acest
mediu nu ruteaza deloc prin dispecerul de comenzi locale ("slash commands")** — cererea merge
direct la model ca prompt text. Din `--debug-file`, header-ul de atribuire arata
`cc_entrypoint=sdk-cli`, adica sesiunea porneste prin calea SDK, nu prin REPL-ul interactiv complet;
acolo comenzile locale par sa nu fie interceptate. Deci esecul lui `/plugin-types` **nu e dovada
ca function hooks sunt dezactivate** — e o limitare a modului de invocare folosit aici, valabila
si pentru comenzi complet neutre ca `/help`. N-am putut testa varianta interactiva (fara TTY in
acest mediu).
`claude --help` si `claude plugin --help` (rulate separat, capturate integral) **nu listeaza**
nicio comanda `plugin-types` la nivel de CLI top-level — motivul e ca `/plugin-types` e o comanda
*in-sesiune* ("slash command"), nu o subcomanda a executabilului `claude`, deci absenta ei din
`--help` e normala si nu spune nimic despre activare.
## 2. Ce exista REAL in binar (cautare de siruri, cu offset de octet verificabil)
Nu s-au generat `.d.ts`, deci n-am putut citi declaratii TypeScript complete ca "dovada
canonica" ceruta in sarcina. In schimb, am gasit in binar cod sursa minificat (nu doar siruri
izolate) care confirma mecanismul de facto. Tot ce urmeaza e citat verbatim din binar, cu offset.
### 2a. Flag-ul si poarta lui reala
La offset ~103270720 (grep -a -b), blocul de cod al flag-ului:
```
var kYe="tengu_plugin_hooks_modules";
var Azt=()=>!1;
var AFe=()=>a.CLAUDE_CODE_ENABLE_FUNCTION_HOOKS??P(kYe,Azt());
var h8=()=>AFe()&&!qb()&&!Br("hooks")&&!zm();
```
Interpretare directa din cod:
- flag-ul GrowthBook real se numeste **`tengu_plugin_hooks_modules`**, default **oprit** (`Azt=()=>!1`).
- `CLAUDE_CODE_ENABLE_FUNCTION_HOOKS` e o suprascriere locala peste acel flag (`??`) — confirma
exact ce zice blogul despre numele variabilei de mediu.
- **dar** poarta finala folosita in productie, `h8()`, cere si `!qb() && !Br("hooks") && !zm()`
— trei conditii suplimentare. Numele acestor functii sunt minificate si reciclate in zeci de
module din bundle (acelasi nume `qb`/`Br`/`zm` apare cu corpuri complet diferite in alte
scope-uri), deci **nu pot atribui cu certitudine ce verifica exact aici** fara deminificare
completa — onest: aceasta e limita cercetarii, nu o concluzie.
- Alaturi, la acelasi offset, un tabel de atribuire a sursei flag-ului pentru afisare in UI:
`{override:"from a local override", payload:"from GrowthBook (this session's payload)",
disk:"from GrowthBook (the disk cache of an earlier session)", disabled:"from the default
(GrowthBook is off ...)"}` — confirma ca e un flag GrowthBook cu override local, exact
mecanismul descris de blog.
### 2b. Comanda `/plugin-types` — confirmata REAL, dar NEgatata de flag
La offset 203438732, definitia completa a comenzii (citat verbatim):
```
var mer=Object.freeze({type:"local",name:"plugin-types",
description:"Write claude-code.d.ts, claude-code-plugins.d.ts and claude-code-mcp.d.ts:
the plugin API's TypeScript declarations, the enabled plugins' type contracts and the
inputs of the connected MCP tools, for typing a hooks module against this session",
argumentHint:"[dir]", supportsNonInteractive:!0,
load:()=>import("B:/~BUN/root/chunk-0rpsm23r.js")});
```
Important: **acest obiect n-are nicio conditie `isEnabled`/gate legata de `h8()` sau de
flag** — e inregistrata neconditionat. Descrierea confirma explicit sintagma "hooks module"
din blog: comanda exista ca sa tipizeze "a hooks module against this session". Deci comanda in
sine e reala si documentata intern, indiferent de starea flag-ului; problema empirica de mai
sus (sectiunea 1) e doar despre modul de invocare `-p` din acest mediu.
### 2c. Lista REALA de evenimente si API-uri ("scan manifest"), gasita ca date, nu ca documentatie
La offset 223160560 exista un manifest static de tip "scan" folosit (aparent) pentru analiza
statica / sandboxing a unui modul de plugin, cu lista completa si explicita:
```js
scan: {
hooks: ["session.start","ui.render","command.run","ui.close","ui.focus","ui.scroll",
"tool.call","prompt.submit"],
calls: ["clock.after","clock.every","clock.now","command.register","fs.list","fs.read",
"fs.stat","process.run","session.id","session.messages","store.get","store.set",
"telemetry.log","telemetry.mark","ui.close","ui.invalidate","ui.log","ui.open",
"ui.resolve","ui.status"]
}
```
Aceasta e cea mai tare dovada gasita — e o **lista de date**, nu un string de proza, deci
foarte probabil chiar suprafata reala (sau foarte apropiata) a API-ului de hooks module:
- **evenimente de hook confirmate**: `session.start`, `ui.render`, `command.run`, `ui.close`,
`ui.focus`, `ui.scroll`, `tool.call`, `prompt.submit` (8 la numar).
- **`turn.complete` si `turn.start` NU sunt in lista de evenimente de hook**, desi cele doua
siruri exista in alta parte a binarului (24, respectiv 68 aparitii) — apartin mecanismului
intern de motor ("engine turn 1 start/end", vazut si in log-ul de debug la rularea reala),
nu suprafetei de hooks expuse pluginurilor.
- **`prompt.submit` e confirmat**, si separat, la offset 206959908, exista codul intern real
care implementeaza acest hook point: un pipeline `core`/`managed` cu posibilitate de a
rescrie textul promptului (`"prompt.submit: text rewritten by a hook (...)"`) sau de a-l
bloca (`"Prompt dropped by a hook: ..."`). Semnatura exacta gasita e apelul
`.prompt.submit({submission, origin, turnId, shouldWait})` — **nu am gasit litera `$` ca
alias/receiver exact** in siruri; forma `$.prompt.submit({text})` din blog e plauzibila ca
sugar-syntax peste acest mecanism, dar nu e confirmata verbatim.
- **API-uri de tip "calls" confirmate ca namespace-uri cu metode**: `fs.read/list/stat`
(deci `$.fs` probabil e un namespace, nu o valoare simpla), `store.get/set`,
`clock.after/every/now`, `session.id/messages` — coincide cu ce cerea sarcina sa verific
(`$.session.messages()`, `$.fs`, `$.store`, `$.clock.every`), dar iar, prefixul `$.` in sine
nu apare ca sir literal langa aceste nume — e o inferenta rezonabila, nu o citare directa.
### 2d. Tokeni / context / usage / compactare — CAUTATE EXPLICIT, NU GASITE
Am cautat explicit `token`, `usage`, `context`, `compact` in vecinatatea manifestului de mai
sus si in restul zonei de "function hooks". **Niciunul dintre cele 20 de `calls` sau 8 `hooks`
de mai sus nu are legatura cu tokeni, context window, usage sau compactare.** Sirurile
`contextWindow` (8 aparitii) si `compact` (405 aparitii) exista din abundenta in binar, dar in
alte module (afisarea usage-ului in `--output-format json`, autocompact intern al motorului
— vazut si in logul de debug: `autocompact: tokens=[REDACTED] level=ok effectiveWindow=980000`),
**nu ca hook sau call disponibil unui modul de function hooks**. Nu exista dovada in acest
binar ca un modul de hooks ar putea citi tokenii ramasi sau starea de compactare.
### 2e. Forma `register` si `hooks/hooks.json`
Doua descoperiri, ambele reale, dar **doua lucruri diferite**:
1. **`hooks.json` din binar (27 aparitii) e manifestul VECHI/existent de plugin-hooks**
(PreToolUse, SessionStart etc. — cel folosit deja de pluginul `ponytail` din acest proiect,
vazut in logul de debug: `Read manifest hooks for plugin ponytail (enabled=true):
./hooks/claude-codex-hooks.json`). Contextul din binar confirma: sirul e `"plugin
hooks.json"`, langa mesaje despre `SKILL.md` si actualizarea regulilor de auto-mode/permisiuni
— sistemul clasic de hooks pe evenimente de tool-use, nesuprapus cu "function hooks".
2. **Forma `register` pentru "function hooks" (modulul nou)**, gasita in doua exemple interne
reale de pluginuri deja construite cu acest mecanism:
- `tengu_quiet_dolphin` — "panoul de diff ca plugin: /diff", cu
`isAvailable:()=>At` unde `At=()=>h8()&&lu(Rr(),bt)` — **confirma ca h8() (poarta de
function hooks descrisa la 2a) chiar gateaza un plugin real deja construit**, nu doar un
flag mort.
- `tengu_tips_mod` — "spinner tips as a plugin", cu forma exacta:
```js
var P=(e)=>({register:(o)=>{e.registerHooks(o,x())}});
```
adica modulul exporta un `register(engine)` care apeleaza
`engine.registerHooks(implementareHooks, apiSuplimentar)`. E cea mai apropiata dovada
gasita de "forma exacta a functiei register", dar e exemplul unui plugin intern concret,
nu semnatura generica documentata explicit undeva ca atare.
## VERDICT
Se poate construi o bucla "prag context -> scrie handoff -> /clear -> reia din handoff" complet
automat, FARA interventia utilizatorului, folosind acest surface?
**Nu, nu cu ce am vazut efectiv in acest binar.** Motive, punct cu punct:
1. Suprafata de hooks confirmata (`session.start`, `ui.*`, `command.run`, `tool.call`,
`prompt.submit`) **nu contine niciun eveniment sau apel legat de prag de context, tokeni,
usage sau compactare** (sectiunea 2d) — deci un modul de hooks n-ar avea de unde sa afle
"am trecut de 200k tokeni" din interior. Ar trebui sa deduca asta indirect (ex. numarand
caractere din `session.messages()`), fara nicio garantie ca se potriveste cu tokenizarea
reala sau cu `autocompact` intern.
2. Nu exista niciun hook de tipul "inainte de compactare" / "sesiune se inchide" in lista —
deci nu exista un punct de agatare curat pentru "scrie handoff chiar inainte sa se piarda
contextul".
3. `/clear` (sau relansarea sesiunii) nu apare in `calls`-urile confirmate (`command.register`
exista, dar a inregistra o comanda noua nu e totuna cu a declansa `/clear` programatic din
interiorul unui hook).
4. Flag-ul e in continuare oprit implicit (`tengu_plugin_hooks_modules` default `!1`),
marcat "early access" chiar in textul din comanda `/plugin-types` insasi
("module 'claude-code', early access: it may change between releases") — deci si daca
API-ul de mai sus ar acoperi cazul, Anthropic il trateaza explicit ca nestabil.
5. **Nu am reusit sa validam empiric nimic din surface** (n-am putut genera `.d.ts` din cauza
limitarii de mediu descrise la sectiunea 1) — tot ce e mai sus e din citirea codului
minificat, nu din declaratii TypeScript generate si citite ca atare, cum cerea procedura.
Ce ar lipsi/ar trebui reincercat ca sa se stie sigur: rulare **interactiva** reala (TTY, nu
`-p`/SDK) cu `CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1`, ca sa se vada daca `/plugin-types` chiar
scrie fisierele si daca `h8()` se evalueaza `true` in acel context — asta ar da acces la
declaratiile TypeScript reale si ar inchide toate necunoscutele de mai sus (in special conditiile
`qb()`/`Br("hooks")`/`zm()` neatribuite cu certitudine).