"""PreToolUse-hook for Claude Code: stopper endringer i testene og i låsfila. Claude Code sender en JSON på standard inn før verktøyet kjøres. Avslutter skriptet med kode 2, blokkeres kallet, og teksten på standard feil går tilbake til Claude som forklaring. Kode 1 ville IKKE stoppet noe: dokumentasjonen sier at Claude Code regner alt annet enn 2 som en feil som ikke blokkerer, og går videre. Slik står det i code.claude.com/docs/en/hooks, lest 9. oktober 2026. Legg fila i .claude/hooks/ og registrer den i en egen innstillingsfil, se eksempel-settings.json i samme mappe. Den egne fila er et valg: økta som skal skrive testene, må kunne skrive dem, og starter derfor uten den. Hva den dekker: - Edit og Write mot test_*.py, alt under en mappe som heter tests (uansett store og små bokstaver), og låsfila tester.laas - Bash-kommandoer som nevner tester.laas eller kjører «laas.py lag» Hva den ikke dekker: Bash-sjekken er ren tekstsammenligning, og et skript agenten skriver og kjører selv, går rett forbi. Den er en fartsdump mot snarveien, ikke en mur. Det som holder, er at endringer i testene og i tester.laas må godkjennes av et menneske før de kommer inn, se test-forst-med-ai.html. """ import json import pathlib import sys sys.stderr.reconfigure(encoding="utf-8") data = json.load(sys.stdin) verktoy = data.get("tool_name", "") inn = data.get("tool_input") or {} def er_vernet(sti: str) -> bool: # Windows sender stien med omvendt skråstrek. Normaliser før sammenligning, # og se bort fra store og små bokstaver, ellers slipper Tests\ forbi. sti = sti.replace("\\", "/").lower() navn = pathlib.PurePosixPath(sti).name deler = pathlib.PurePosixPath(sti).parts[:-1] return ((navn.startswith("test_") and navn.endswith(".py")) or navn == "tester.laas" or "tests" in deler) def stopp(grunn: str) -> None: print(f"Blokkert: {grunn} Rett koden, ikke testen. " "Mener du at testen er feil, si det til brukeren og stopp.", file=sys.stderr) sys.exit(2) if verktoy in ("Edit", "Write"): sti = inn.get("file_path", "") if er_vernet(sti): navn = sti.replace("\\", "/").rsplit("/", 1)[-1] stopp(f"{navn} er låst.") if verktoy == "Bash": kommando = inn.get("command", "").lower() if "tester.laas" in kommando or ("laas.py" in kommando and " lag" in kommando): stopp("Låsen på testene lages av et menneske, ikke av deg.") sys.exit(0)