CLAUDE.md: sesiunea principala e doar orchestrator, plus hook de context la 250k
Cerut explicit de user: contextul principal e resursa rara, deci munca de volum (citit transcrieri, scris sumarizari, convertit diagrame, cautari largi) se da la agenti, iar sesiunea principala imparte munca, citeste rapoartele, verifica prin sondaj si comite. Adaugat: - CLAUDE.md cu tabelul "ce faci tu vs. ce dai la agenti", regulile care se pun in promptul fiecarui agent (acelasi prag de context, raport scurt, fara commit, fara sub-agenti, domeniu clar, verificare in sarcina lui), si capcanele de orchestrare pe care le-am patit azi. - .claude/hooks/context_guard.py: citeste usage din transcriere si anunta cand contextul trece de 250k, o singura data per prag. Testat pe payload sintetic (sub prag, peste prag, repetare, fara transcript) si live. - .claude/settings.json: hook PostToolUse care il ruleaza. - PROGRESS.md: pointer catre regulile astea. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MtSTyTmt6AbCL9j5ajEDm1
This commit is contained in:
101
.claude/hooks/context_guard.py
Executable file
101
.claude/hooks/context_guard.py
Executable file
@@ -0,0 +1,101 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Anunță când contextul sesiunii depășește un prag, ca să se predea sesiunea.
|
||||
|
||||
Userul a cerut asta pe 13.09.2026: la ~250k tokeni, sesiunea salvează
|
||||
PROGRESS.md și se oprește, iar munca o continuă o sesiune nouă. Fără un
|
||||
semnal automat, pragul se ratează — contextul crește în tăcere.
|
||||
|
||||
Rulează ca hook PostToolUse. Primește pe stdin JSON-ul hook-ului, citește
|
||||
`usage` din ultimul mesaj al transcrierii și, dacă totalul depășește pragul,
|
||||
injectează un mesaj în context. Anunță o singură dată per prag, ca să nu
|
||||
umple contextul chiar el.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
THRESHOLD = int(os.environ.get("CTX_GUARD_THRESHOLD", "250000"))
|
||||
STEP = 50_000 # re-anunță din 50k în 50k peste prag
|
||||
STATE_DIR = os.path.expanduser("~/.claude/ctx_guard")
|
||||
|
||||
MSG = """[context guard] Contextul sesiunii a ajuns la ~{tokens:,} de tokeni (prag: {threshold:,}).
|
||||
|
||||
Conform instrucțiunilor userului, acum:
|
||||
1. Salvează starea în PROGRESS.md — ce s-a făcut, ce a rămas, ce rulează.
|
||||
2. Comite și fă push.
|
||||
3. Oprește agenții care au terminat (SendMessage cu shutdown_request).
|
||||
4. Spune userului că sesiunea e gata de predat și oprește-te.
|
||||
|
||||
Nu începe o sarcină nouă mare. Dacă un agent încă lucrează, cere-i raportul
|
||||
parțial și notează în PROGRESS.md ce rămâne de reluat."""
|
||||
|
||||
|
||||
def total_tokens(transcript_path: str) -> int:
|
||||
"""Suma din ultimul `usage` găsit în transcriere — aproximarea contextului."""
|
||||
try:
|
||||
with open(transcript_path, "r", encoding="utf-8") as fh:
|
||||
lines = fh.readlines()
|
||||
except OSError:
|
||||
return 0
|
||||
|
||||
for line in reversed(lines):
|
||||
try:
|
||||
usage = json.loads(line).get("message", {}).get("usage")
|
||||
except (json.JSONDecodeError, AttributeError):
|
||||
continue
|
||||
if not usage:
|
||||
continue
|
||||
return (usage.get("input_tokens", 0)
|
||||
+ usage.get("cache_read_input_tokens", 0)
|
||||
+ usage.get("cache_creation_input_tokens", 0)
|
||||
+ usage.get("output_tokens", 0))
|
||||
return 0
|
||||
|
||||
|
||||
def already_warned(session_id: str, bucket: int) -> bool:
|
||||
"""Marcăm pragul atins, ca să nu repetăm anunțul la fiecare tool call."""
|
||||
os.makedirs(STATE_DIR, exist_ok=True)
|
||||
path = os.path.join(STATE_DIR, f"{session_id}.txt")
|
||||
try:
|
||||
with open(path) as fh:
|
||||
if int(fh.read().strip() or 0) >= bucket:
|
||||
return True
|
||||
except (OSError, ValueError):
|
||||
pass
|
||||
with open(path, "w") as fh:
|
||||
fh.write(str(bucket))
|
||||
return False
|
||||
|
||||
|
||||
def main() -> None:
|
||||
try:
|
||||
payload = json.load(sys.stdin)
|
||||
except (json.JSONDecodeError, ValueError):
|
||||
return
|
||||
|
||||
transcript = payload.get("transcript_path")
|
||||
session_id = payload.get("session_id", "unknown")
|
||||
if not transcript:
|
||||
return
|
||||
|
||||
tokens = total_tokens(transcript)
|
||||
if tokens < THRESHOLD:
|
||||
return
|
||||
|
||||
bucket = THRESHOLD + ((tokens - THRESHOLD) // STEP) * STEP
|
||||
if already_warned(session_id, bucket):
|
||||
return
|
||||
|
||||
text = MSG.format(tokens=tokens, threshold=THRESHOLD)
|
||||
print(json.dumps({
|
||||
"systemMessage": f"Context la ~{tokens:,} tokeni — salvează PROGRESS.md și predă sesiunea.",
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": payload.get("hook_event_name", "PostToolUse"),
|
||||
"additionalContext": text,
|
||||
},
|
||||
}))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
16
.claude/settings.json
Normal file
16
.claude/settings.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "python3 \"$CLAUDE_PROJECT_DIR/.claude/hooks/context_guard.py\" 2>/dev/null || true",
|
||||
"timeout": 10,
|
||||
"statusMessage": "verific contextul"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user