Files
Pascal Michel 9e506ff588 Abgleich mit dem Vault-Stand 7f484f9
38 Datei(en) geändert.
Erzeugt von .vault-tools/auto-push/werkzeuge_spiegel.py.
2026-11-02 03:00:03 +01:00

10 KiB
Raw Permalink Blame History

type, title, status, modell, summary, tags, created, updated
type title status modell summary tags created updated
plan Task 01: shell_wache.py und Regressionstreiber active sonnet PreToolUse-Hook, der im Befehl des Bash-Werkzeugs vier Windows-Shell-Fallen erkennt und nur warnt, dazu ein Treiber mit Treffer- und Gegenfall je Muster.
plan
vault-tools
2026-10-30 2026-10-30

Übersicht: 00-uebersicht.md

Task 01: shell_wache.py und Regressionstreiber

Ziel

Zwei neue Dateien, sonst nichts:

  • D:/Claude/.vault-tools/shell-wache/shell_wache.py
  • D:/Claude/.vault-tools/shell-wache/faelle_shell_wache.py

Verboten: .claude/settings.json anfassen, irgendeine andere Datei ändern, committen. Nur Standardbibliothek, keine Importe aus dem eigenen Ordner (der Hook läuft mit python -I, das den Skriptordner aus sys.path nimmt). Python immer als python -I -X utf8, nie python3. Kein Heredoc für Python-Code: Dateien mit dem Write-Werkzeug anlegen. Vor jedem Lauf python -m py_compile <datei>.

Die verbindliche Spezifikation ist die Entscheidungsliste in D:/Claude/.vault-tools/shell-wache/ENTWURF.md (Fragen 1 bis 20) samt Angriffsliste. Lies sie ganz. Was hier steht, ergänzt sie; bei Widerspruch gilt ENTWURF.md, und du meldest den Widerspruch.

shell_wache.py

Kopfkommentar (Prüfskript, also volle Länge nach AI/Regelwerk/codedoku.md, „Ausnahme: Prüfskripte“), im Stil von D:/Claude/.vault-tools/session-hooks/regelwerk_nach_edit.py:

"""PreToolUse-Hook auf das Bash-Werkzeug: warnt vor vier bekannten Windows-Shell-Fallen, lehnt nie ab.

Ersteller: Pascal Michel
Erstellt:  2026-10-30
Entwurf:   ENTWURF.md im selben Ordner · Doku: docs/code/shell_wache.md

GELTUNGSBEREICH
  (vier Muster, Windows-Kontext, Linux-Weiche, Ausgabeform, immer Exit 0 – knapp aus ENTWURF.md)

BENANNTE LÜCKEN
  (die Liste aus ENTWURF.md, Abschnitt „Benannte Lücken“)

DIESER HOOK IST EIN FANGNETZ, KEIN NACHWEIS. ...
"""

Ablauf:

  1. stdin als Bytes lesen, decode("utf-8", "replace").
  2. Eingabe prüfen (ENTWURF 14 bis 16). Gründe für [eingabe], wörtlich: stdin leer, kein JSON, kein JSON-Objekt, tool_input fehlt oder ist kein Objekt, command fehlt oder ist keine Zeichenkette, tool_name ist <name>, nicht Bash. Leerer command ("" oder nur Leerraum): still.
  3. Die vier Detektoren laufen je in eigenem try; ein Fehler darin wird [intern] mit type(e).__name__, die übrigen laufen weiter.
  4. Ausgabe: ohne Befund nichts auf stdout. Mit Befund json.dumps({"hookSpecificOutput": {"hookEventName": "PreToolUse", "additionalContext": text}}, ensure_ascii=True) auf stdout. Nie permissionDecision, nie decision, nie continue.
  5. main() in try/except BaseException (außer KeyboardInterrupt ist egal); auch ein Fehler dort wird als [intern] ausgegeben. Das Skript endet in jedem Fall mit Exit 0 – nach der Ausgabe sys.exit(0) am Ende von if __name__ == "__main__":.

Text des Hinweises: erste Zeile wörtlich shell-wache warnt nur, der Befehl läuft trotzdem. Danach je getroffenem Schlüssel genau eine Zeile in dieser Reihenfolge, auch wenn das Muster mehrfach trifft:

Schlüssel Zeile (wörtlich)
[python-I] [python-I] python -I ohne -X utf8: -I ignoriert PYTHONIOENCODING, Umlaute brechen unter cp1252. Nimm python -I -X utf8 (AI/Regelwerk/windows-shell.md, „Welches Python“).
[python3] [python3] python3 ist unter Windows der Microsoft-Store-Stub. Nimm python (AI/Regelwerk/windows-shell.md, „Welches Python“).
[bash-windows] [bash-windows] bash ohne Pfad aus cmd, PowerShell oder Python startet unter Windows den WSL-Starter. Git-Bash mit vollem Pfad C:\Program Files\Git\bin\bash.exe, WSL mit wsl -d <Distro> (AI/Regelwerk/windows-shell.md, „Welches Python“).
[pkill-f] [pkill-f] pkill -f trifft jede Befehlszeile mit dem Muster, auch die eigene Shell und fremde Läufe in WSL. Erst pgrep -af lesen, dann gezielt kill <pid> oder ein Muster wie "[s]leep 7199" (Erinnerung pkill-f-trifft-die-eigene-shell; AI/Regelwerk/windows-shell.md).
[eingabe] [eingabe] Hook-Eingabe unbrauchbar (<grund>), nichts geprüft.
[intern] [intern] shell-wache ist abgestürzt (<Ausnahmetyp>), Prüfung unvollständig.

Detektoren

Regeln genau nach ENTWURF.md, Fragen 8 bis 13 und 19. Ergänzend, damit es eindeutig ist:

  • Wortgrenze: Ein Programmwort beginnt am Befehlsanfang oder nach Leerraum, ;, &, |, (, `, ', ", =; es darf einen Pfad davor tragen (/c/x/python.exe, "C:/P/python.exe"), außer bei bash (Frage 9). Es endet vor Leerraum, ', ", ;, ), |, & oder dem Textende. python3-config, .bashrc, bash_profile, bash-completion, ipython, mypython sind keine Programmwörter.
  • python3: python3 und python3.<n>, mit oder ohne .exe.
  • Optionen lesen (python, pkill): den Rest der Zeile nach dem Programmwort an Leerraum zerlegen, je Wort umschließende ' und " abstreifen, Wörter lesen, solange sie mit - beginnen und nicht genau - oder -- sind.
  • Windows-Kontext (a): gilt vom Kontextwort bis zum Zeilenende. Darin trifft jedes bash oder bash.exe ohne Pfad, vor dem auf derselben Zeile hinter dem Kontextwort keine Weiche steht.
  • Muster (b): regulärer Ausdruck auf [ plus optionalem Leerraum plus "bash", 'bash', "bash.exe" oder 'bash.exe', ohne Rücksicht auf Groß- und Kleinschreibung.

faelle_shell_wache.py

Treiber im Stil von D:/Claude/.vault-tools/session-hooks/faelle_nach_edit.py: startet den Hook je Fall als eigenen Prozess ([sys.executable, "-I", "-X", "utf8", HOOK], stdin als Bytes, Zeitlimit 10 s), ohne PYTHONIOENCODING in der Umgebung. Je Fall prüft er:

  • Exit-Code ist 0 (nie 2),
  • stdout ist leer, wenn der Fall still sein soll; sonst genau ein JSON-Objekt, hookSpecificOutput.hookEventName == "PreToolUse", kein Schlüssel permissionDecision irgendwo, und die Menge der Schlüssel in eckigen Klammern am Zeilenanfang von additionalContext ist genau die erwartete,
  • die Laufzeit liegt unter 2 s.

Grundlinie zuerst: Ein gesunder Befehl (ls -la && python -I -X utf8 x.py) muss still sein. Scheitert sie, bricht der Treiber ab, weil der Rest nichts misst.

Fälle, jeder als Eintrag (name, stdin_bytes, erwartete_schluessel). Ein Hilfsaufbau eingabe(cmd) baut {"tool_name": "Bash", "tool_input": {"command": cmd}}. Pflicht, mindestens:

Gruppe Treffer Gegenfall (still)
python-I python -I x.py; python -IB x.py; python -BI x.py; python -Ic "print(1)"; python -I -c "x" -X utf8; python -I -X utf8=0 x.py; /c/Py/python.exe -I x.py; "C:/Py/python.exe" -I x.py; cd a && python -I x.py; PYTHONIOENCODING=utf-8 python -I x.py python -I -X utf8 x.py; python -IXutf8 x.py; python -IX utf8 x.py; python -X utf8 -I -B x.py; python -I -X utf8=1 x.py; python x.py -I; python -c "import x" -I; ipython -I; python -m pytest -I
python3 python3 x.py; python3.12 -m venv v; ls; python3 -V; python3.exe x.py python x.py; python3-config --libs; wsl -d Debian -- python3 x.py; ssh h python3 x.py; docker exec c python3 x.py
bash-windows cmd //c "bash x.sh"; cmd.exe /c bash -c "x"; CMD /C BASH x; powershell -c "bash -lc 'x'"; powershell.exe -NoProfile -Command "& bash x"; pwsh -c "bash x"; python -X utf8 -c "import subprocess; subprocess.run(['bash', '-c', 'x'])"; python x.py <<'EOF' + Zeilenumbruch + subprocess.run(["bash.exe", "x"]) + Zeilenumbruch + EOF bash -c "x"; bash x.sh; cmd //c "C:/Program Files/Git/bin/bash.exe" x; cmd //c "C:\Program Files\Git\bin\bash.exe" x; powershell -c "wsl -d Debian -- bash -lc 'x'"; cmd /c "ssh.exe -o A=B root@h bash -s < f.sh"; powershell -c "Get-Content ~/.bashrc"; powershell -c "x" + Zeilenumbruch + bash y.sh; ssh h python - <<'EOF' + Zeilenumbruch + subprocess.run(['bash','x']) + Zeilenumbruch + EOF; subprocess.run([GITBASH, '-c']) in python -c
pkill-f pkill -f x; pkill -TERM -f "[s]leep 1"; pkill -fx x; pkill --full x; pkill -u root -f x; sudo pkill -f x; wsl -d Debian -- bash -c 'a; pkill -f "y"'; /usr/bin/pkill -f x pkill x; pkill -TERM x; pgrep -f x; pkill -u fx x (fx ist Argument von -u)
mehrere alle vier Muster in einem Befehl: vier Schlüssel; python -I a.py; python -I b.py: ein Schlüssel
Reihenfolge je Muster: Treffer am Anfang (<treffer>; ls), am Ende (ls; <treffer>), in Zeile 3 eines dreizeiligen Befehls
Eingabe leer; nicht json; []; null; {}; {"tool_name": "Bash"}; tool_input null, als Zeichenkette "x"; command fehlt, null, 5, ["ls"]; ungültiges UTF-8 (b'{"tool_name":"Bash","tool_input":{"command":"ls \xff"}}', still); tool_name PowerShell mit python3 x → nur [eingabe] command gleich "" und " ": still; zusätzliche Felder ("tool_use_id", "description", "timeout"): still

Ausgabe des Treibers: je Fall eine Zeile OK oder FEHLER mit Name und Abweichung, am Ende die Zahl der Fälle und der Fehler, gerechnet, nicht geschrieben. Exit 0 nur, wenn alle Fälle passen; Exit 1 sonst; ein Absturz des Treibers selbst wird gefangen und als Fehler gezählt.

Prüfen

# Syntax beider Dateien
python -I -X utf8 -m py_compile D:/Claude/.vault-tools/shell-wache/shell_wache.py D:/Claude/.vault-tools/shell-wache/faelle_shell_wache.py
# Alle Fälle
python -I -X utf8 D:/Claude/.vault-tools/shell-wache/faelle_shell_wache.py
# Kein CR in den neuen Dateien (je 0)
tr -cd '\r' < D:/Claude/.vault-tools/shell-wache/shell_wache.py | wc -c
tr -cd '\r' < D:/Claude/.vault-tools/shell-wache/faelle_shell_wache.py | wc -c

Rotnachweis, Pflicht im Bericht: Kopiere shell_wache.py in einen neuen Ordner unter dem Scratchpad oder mktemp -d, lösche dort je Detektor dessen Aufruf (vier Mutanten, einzeln, mit Rücksetzen) und fahre den Treiber gegen die Kopie (der Treiber nimmt den Hook-Pfad als optionales erstes Argument). Jeder Mutant muss mindestens einen FEHLER erzeugen. Nenne je Mutant die Zahl der Fehler.

Berichte: Zahl der Fälle, Ergebnis, die vier Mutanten mit Fehlerzahl, jede Stelle, an der du von diesem Task oder ENTWURF.md abgewichen bist, und jeden Fall, den du nicht eindeutig umsetzen konntest.