Claude Pre-/Post-Hook System
Das Hook-System greift automatisch bei jeder Interaktion mit Claude Code ein und stellt sicher, dass alle Aktionen protokolliert, validiert und abgesichert werden.
Operationale Übersicht: Claude Hooks (Betrieb)
Hook-Typen
| Hook-Event | Zeitpunkt | Zweck |
|---|---|---|
SessionStart |
Session-Beginn | Protokollierung des Session-Starts |
UserPromptSubmit |
Nach User-Eingabe | Prompt-Logging |
PreToolUse |
Vor Tool-Ausführung | Validierung, Backup, Blockierung |
PostToolUse |
Nach Tool-Ausführung | Permissions, Response-Logging |
Stop |
Task-Ende | Abschluss-Protokollierung |
SessionEnd |
Session-Ende | Session-Abschluss |
Konfiguration
Die Hooks werden in /root/.claude/settings.json konfiguriert.
Ablaufdiagramm: Edit/Write Operation
┌─────────────────┐
│ User-Prompt │
└────────┬────────┘
│
▼
┌─────────────────┐
│ UserPromptSubmit│──▶ log_to_db.py (Logging)
└────────┬────────┘
│
▼
┌─────────────────┐
│ PreToolUse │
│ (Edit/Write) │
└────────┬────────┘
│
┌────┴────┐
│ │
▼ ▼
┌───────┐ ┌───────────┐ ┌────────────┐
│Block- │ │file_backup│ │log_to_db.py│
│DB.py │ │_hook.py │ │(Logging) │
└───┬───┘ └─────┬─────┘ └──────┬─────┘
│ │ │
│ Backup erstellt │
│ │ │
└───────────┴──────────────┘
│
┌──────┴──────┐
│ Tool │
│ Execution │
└──────┬──────┘
│
▼
┌─────────────────┐
│ PostToolUse │
│ (Edit/Write) │
└────────┬────────┘
│
┌────┴────┐
│ │
▼ ▼
┌───────────┐ ┌────────────┐
│fix-permi- │ │log_to_db.py│
│ssions.sh │ │(Response) │
└─────┬─────┘ └──────┬─────┘
│ │
▼ │
Permissions │
korrigiert │
└──────────────┘
Unterkapitel
- Pre-Hook Ablauf - Was passiert VOR der Tool-Ausführung
- Post-Hook Ablauf - Was passiert NACH der Tool-Ausführung
- Hook-Scripts - Übersicht aller Scripts
- Protokollierung - Logging in ki_dev.protokoll
- Task-Completion Guard - Workflow-Validierung
Verwandte Themen
- Claude Hooks (Betrieb) - Alle Hooks mit Konfiguration
- Quality Gates - Code-Qualitätsprüfung
Pre-Hook Ablauf
Der Pre-Hook wird vor jeder Tool-Ausführung aufgerufen und kann die Aktion blockieren, modifizieren oder durchlassen.
Trigger
Pre-Hooks werden bei folgenden Tools ausgelöst:
Bash- Shell-BefehleEdit- Datei-BearbeitungWrite- Datei-Erstellung- Alle anderen Tools (generisches Logging)
Ablauf bei Edit/Write
1. Sicherheits-Validierung (Bash)
Bei Bash-Befehlen werden zwei Blocker-Hooks ausgeführt:
| Hook | Prüft | Blockiert |
|---|---|---|
block_direct_db.py |
MySQL/MariaDB-Zugriffe | Direkte DB-Verbindungen mit Passwort |
block_direct_task_db.py |
Task-Tabellen-Zugriffe | INSERT/UPDATE/DELETE auf tasks-Tabellen |
Empfehlung bei Blockierung: MCP-Tools verwenden (db_select, tasks_create, etc.)
2. File Backup (Edit/Write)
Bei Edit- und Write-Operationen:
file_backup_hook.py
├── Prüft: Liegt Datei in BACKUP_DIRS?
│ ├── /var/www/dev.campus.../src
│ ├── /var/www/dev.campus.../public
│ ├── /var/www/dev.campus.../config
│ └── ... (weitere Verzeichnisse)
│
├── Prüft: Datei < 10 MB?
├── Prüft: Nicht in EXCLUDE_PATTERNS?
│ ├── /vendor/
│ ├── /node_modules/
│ ├── /.git/
│ └── ... (weitere Ausschlüsse)
│
├── Berechnet: SHA256-Hash des Inhalts
├── Vergleicht: Mit letzter Version
│
└── Bei Änderung:
└── INSERT INTO file_backup_history
├── file_path
├── file_content (vollständig)
├── content_hash
├── version (auto-increment pro Datei)
└── reason: "Claude Code Pre-Hook Backup"
3. Request-Logging
Für alle Tools wird der Request protokolliert:
log_to_db.py
├── Extrahiert: tool_name, tool_input
├── Sanitized: Sensible Daten (Passwörter, Tokens)
├── Schätzt: tokens_input
│
└── INSERT INTO protokoll
├── request (JSON)
├── request_timestamp
├── status: 'pending'
└── Speichert: pending_request für PostToolUse-Matching
Blockierung
Ein Pre-Hook kann die Ausführung blockieren durch:
exit(1)- Tool wird nicht ausgeführt{"allowed": false, "message": "..."}- Mit Fehlermeldung
Durchlassen
Bei Erfolg gibt der Hook zurück:
{"continue": true}
oder
{"allowed": true}
Geschützte Verzeichnisse
Backups werden erstellt für:
/var/www/dev.campus.systemische-tools.de/src/var/www/dev.campus.systemische-tools.de/public/var/www/dev.campus.systemische-tools.de/config/var/www/prod.campus.systemische-tools.de/src- ... und weitere
Post-Hook Ablauf
Der Post-Hook wird nach jeder Tool-Ausführung aufgerufen und führt Aufräumarbeiten sowie Logging durch.
Trigger
Post-Hooks werden bei folgenden Tools ausgelöst:
Edit- Nach Datei-BearbeitungWrite- Nach Datei-Erstellung- Alle anderen Tools (generisches Response-Logging)
Ablauf bei Edit/Write
1. Berechtigungskorrektur
Nach jeder Edit/Write-Operation:
fix-permissions.sh ├── chown -R www-data:www-data /var/www/dev.campus... ├── chown -R www-data:www-data /var/www/prod.campus... ├── chmod -R 755 /var/www/dev.campus... └── chmod -R 755 /var/www/prod.campus...
Warum? Claude Code läuft als root. Ohne Korrektur hätten die Dateien falschen Owner und Apache könnte sie nicht lesen.
2. Response-Logging
Für alle Tools wird die Response protokolliert:
log_to_db.py
├── Sucht: Matching PreToolUse-Request
│ └── Über: session_id + tool_name
│
├── Extrahiert: tool_response
├── Sanitized: Sensible Daten
├── Schätzt: tokens_output
│
└── UPDATE protokoll SET
├── response (JSON)
├── response_timestamp
├── tokens_output
├── tokens_total = tokens_input + tokens_output
├── duration_ms (berechnet aus Timestamps)
└── status: 'completed'
Request/Response-Pairing
Das Logging-System verbindet Pre- und Post-Hook-Events:
PreToolUse (Edit "file.php")
├── INSERT protokoll → ID=123
└── Speichert: /tmp/claude_hooks/{session}_{tool}_PreToolUse.json
└── {"db_id": 123, "timestamp": "..."}
[Tool-Ausführung]
PostToolUse (Edit "file.php")
├── Lädt: /tmp/claude_hooks/{session}_{tool}_PreToolUse.json
├── Findet: db_id=123
├── UPDATE protokoll WHERE id=123
│ └── SET response, duration_ms, status='completed'
└── Löscht: Tracking-Datei
Duration-Berechnung
Die Dauer wird präzise aus den Timestamps berechnet:
duration_ms = TIMESTAMPDIFF(MICROSECOND, request_timestamp, response_timestamp) / 1000
Damit ist die exakte Tool-Ausführungszeit in Millisekunden erfasst.
Fail-Open Prinzip
Bei Fehlern im Post-Hook:
- Hook gibt trotzdem
exit(0)zurück - Fehler wird zu stderr geloggt
- Claude Code arbeitet normal weiter
Post-Hooks sind nie blockierend, da die Aktion bereits abgeschlossen ist.
Hook-Scripts
Übersicht aller Scripts, die im Hook-System verwendet werden.
Hauptverzeichnisse
| Pfad | Zweck |
|---|---|
/var/www/tools/ki-protokoll/claude-hook/ |
Protokollierung, Backup, Workflow-Guards |
/var/www/scripts/hooks/ |
Sicherheits-Blocker |
/var/www/scripts/ |
Utility-Scripts |
Script-Übersicht
log_to_db.py
| Pfad | /var/www/tools/ki-protokoll/claude-hook/log_to_db.py |
|---|---|
| Events | UserPromptSubmit, PreToolUse, PostToolUse, Stop, SessionStart, SessionEnd |
| Datenbank | ki_dev.protokoll |
| Funktion |
|
file_backup_hook.py
| Pfad | /var/www/tools/ki-protokoll/claude-hook/file_backup_hook.py |
|---|---|
| Events | PreToolUse (Edit, Write) |
| Datenbank | ki_dev.file_backup_history |
| Funktion |
|
task_completion_guard.py
| Pfad | /var/www/tools/ki-protokoll/claude-hook/task_completion_guard.py |
|---|---|
| Events | PreToolUse (mcp__mcp-tasks__tasks_status) |
| Datenbank | ki_dev.task_results |
| Funktion |
|
| Dokumentation | Task-Completion Guard Hook |
block_direct_db.py
| Pfad | /var/www/scripts/hooks/block_direct_db.py |
|---|---|
| Events | PreToolUse (Bash) |
| Funktion |
|
block_direct_task_db.py
| Pfad | /var/www/scripts/hooks/block_direct_task_db.py |
|---|---|
| Events | PreToolUse (Bash) |
| Funktion |
|
fix-permissions.sh
| Pfad | /var/www/scripts/fix-permissions.sh |
|---|---|
| Events | PostToolUse (Edit, Write) |
| Funktion |
|
Hook-Konfiguration
Die Hooks werden in /root/.claude/settings.json definiert:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{"type": "command", "command": "/var/www/scripts/hooks/block_direct_db.py"},
{"type": "command", "command": "/var/www/scripts/hooks/block_direct_task_db.py"}
]
},
{
"matcher": "mcp__mcp-tasks__tasks_status",
"hooks": [
{"type": "command", "command": ".../task_completion_guard.py", "timeout": 5}
]
},
{
"matcher": "Edit|Write",
"hooks": [
{"type": "command", "command": ".../file_backup_hook.py", "timeout": 10}
]
},
{
"matcher": "",
"hooks": [
{"type": "command", "command": ".../log_to_db.py", "timeout": 5}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{"type": "command", "command": "/var/www/scripts/fix-permissions.sh"}
]
},
{
"matcher": "",
"hooks": [
{"type": "command", "command": ".../log_to_db.py", "timeout": 10}
]
}
]
}
}
Protokollierung
Alle Claude Code Aktivitäten werden in der Datenbank ki_dev.protokoll protokolliert.
Tabellen-Schema
protokoll
| Spalte | Typ | Beschreibung |
|---|---|---|
| id | BIGINT | Primary Key |
| timestamp | DATETIME(6) | Event-Zeitpunkt |
| request_ip | VARCHAR(45) | Client-IP (aus SSH_CLIENT) |
| client_name | VARCHAR(255) | User (aus $USER) |
| request | TEXT | Request-Daten (sanitized) |
| request_timestamp | DATETIME(6) | Request-Zeitpunkt (Microseconds) |
| response | TEXT | Response-Daten (sanitized) |
| response_timestamp | DATETIME(6) | Response-Zeitpunkt |
| duration_ms | INT UNSIGNED | Dauer in Millisekunden |
| tokens_input | INT UNSIGNED | Geschätzte Input-Tokens |
| tokens_output | INT UNSIGNED | Geschätzte Output-Tokens |
| tokens_total | INT UNSIGNED | Gesamt-Tokens |
| model_name | VARCHAR(255) | Verwendetes Modell |
| status | ENUM | 'pending', 'completed', 'error' |
| error_message | TEXT | Fehlermeldung bei status='error' |
file_backup_history
| Spalte | Typ | Beschreibung |
|---|---|---|
| id | INT | Primary Key |
| file_path | VARCHAR(512) | Absoluter Dateipfad |
| file_content | LONGTEXT | Vollständiger Dateiinhalt |
| content_hash | CHAR(64) | SHA256-Hash |
| file_size | INT | Dateigröße in Bytes |
| version | INT | Version pro Datei |
| change_type | ENUM | 'created', 'modified', 'deleted' |
| changed_at | TIMESTAMP | Backup-Zeitpunkt |
| changed_by | VARCHAR(100) | claude-code-hook |
| reason | TEXT | Backup-Grund |
| diff_summary | TEXT | Zusammenfassung der Änderungen |
| affected_entities | LONGTEXT | Betroffene Code-Entitäten (JSON) |
Event-Typen im Request
UserPromptSubmit
{
"event": "UserPromptSubmit",
"prompt": "Erstelle eine neue Funktion..."
}
PreToolUse
{
"event": "PreToolUse",
"tool_name": "Edit",
"tool_input": {
"file_path": "/var/www/.../file.php",
"old_string": "...",
"new_string": "..."
}
}
PostToolUse (Response)
{
"tool_response": {
"success": true,
"message": "File updated"
}
}
Sicherheit: Data Sanitization
Folgende Daten werden automatisch redacted:
Key-Patterns
- password, pass, secret
- token, apikey, api_key
- authorization, auth, bearer
- credential
Value-Patterns
- AWS Access Keys (AKIA...)
- API Keys (sk..., rk..., pk...)
- JWT Tokens (eyJ...)
Truncation
Felder > 10.000 Zeichen werden gekürzt mit Hash-Suffix:
...content... [TRUNCATED-a1b2c3d4]
Abfrage-Beispiele
Letzte Tool-Aufrufe
SELECT
timestamp,
JSON_EXTRACT(request, '$.tool_name') as tool,
duration_ms,
status
FROM protokoll
WHERE JSON_EXTRACT(request, '$.event') = 'PreToolUse'
ORDER BY id DESC
LIMIT 10;
Token-Verbrauch pro Tag
SELECT
DATE(timestamp) as tag,
SUM(tokens_total) as tokens,
COUNT(*) as requests
FROM protokoll
GROUP BY DATE(timestamp)
ORDER BY tag DESC;
Datei-Versionen
SELECT version, changed_at, file_size, content_hash FROM file_backup_history WHERE file_path = '/var/www/.../Controller.php' ORDER BY version DESC;
Verbesserungsvorschläge für Pre-/Post-Hooks
Basierend auf der Analyse des KI-Protokolls wurden folgende wiederkehrende Korrektur-Muster identifiziert:
Analyse: Häufige Korrekturen durch Supervision
| Kategorie | Häufigkeit | Beispiele |
|---|---|---|
| Fatal Errors | Hoch | Class not found, Undefined method, PDOException |
| Architektur-Violations | Hoch | Controller mit direktem PDO-Zugriff, fehlende DI |
| Code Style | Mittel | PHP-CS-Fixer Issues, Import-Sortierung |
| UI/UX Logik | Mittel | Progress 1500%, falsche Terminologie |
| 404 Errors | Mittel | Fehlende Routes, falsche Pfade |
Vorschlag 1: PHP Syntax Check (Pre-Hook)
| Hook-Event | PreToolUse (Write) |
|---|---|
| Trigger | Neue PHP-Datei wird erstellt |
| Aktion | php -l auf neuen Inhalt |
| Bei Fehler | Blockieren mit Syntax-Fehlermeldung |
| Status | ⏳ Offen |
Vorschlag 2: Class/Use Statement Validator (Pre-Hook)
| Hook-Event | PreToolUse (Edit, Write) |
|---|---|
| Trigger | PHP-Datei mit new ClassName |
| Aktion | Prüft ob use-Statement oder FQCN vorhanden |
| Bei Fehler | Warnung mit fehlenden Imports |
| Status | ⏳ Offen |
Vorschlag 3: Route Existence Check (Post-Hook)
| Hook-Event | PostToolUse (Edit auf routes/*.php) |
|---|---|
| Trigger | Route-Datei wurde geändert |
| Aktion | Prüft ob alle Controller-Methoden existieren |
| Bei Fehler | Warnung mit fehlenden Methoden |
| Status | ⏳ Offen |
Vorschlag 4: Contract Pre-Validation (Pre-Hook)
| Hook-Event | PreToolUse (Edit auf src/Controller/*.php) |
|---|---|
| Trigger | Controller-Datei wird bearbeitet |
| Aktion | Schnelle Regex-Prüfung der Architecture-Regeln |
| Bei Fehler | Blockieren mit Violation-Details |
| Status | ✅ Implementiert |
| Script | /var/www/tools/ki-protokoll/claude-hook/architecture_guard.py |
| Contract | layered-architecture-pruefung_v1.0 |
Hinweis: Der Architecture Guard ist seit 2025-12-23 aktiv und blockiert Architektur-Violations wie:
- H1: strict_types_required (alle PHP-Dateien)
- H2: domain_no_infrastructure (Domain Layer)
- H3: db_factory_only (nur in Factory-Klassen)
- H4: no_new_repository_in_controller
- H5: no_new_infrastructure_in_controller
Vorschlag 5: PHPStan Quick Check (Post-Hook)
| Hook-Event | PostToolUse (Edit, Write auf *.php) |
|---|---|
| Trigger | PHP-Datei wurde geändert |
| Aktion | PHPStan Level 5 auf geänderte Datei |
| Bei Fehler | Warnung (nicht blockierend) |
| Status | ⏳ Offen |
Vorschlag 6: Services.php Sync Check (Post-Hook)
| Hook-Event | PostToolUse (Write auf src/Controller/*.php) |
|---|---|
| Trigger | Neuer Controller erstellt |
| Aktion | Prüft ob Constructor-Dependencies in services.php registriert |
| Bei Fehler | Warnung mit fehlenden Registrierungen |
| Status | ⏳ Offen |
Implementierungs-Priorität
| Priorität | Hook | Status | Begründung |
|---|---|---|---|
| 1 (Kritisch) | PHP Syntax Check | ⏳ | Verhindert Fatal Errors vor Deployment |
| 2 (Kritisch) | Architecture Guard | ✅ | Verhindert Layer-Violations sofort |
| 3 (Hoch) | Class Import Validator | ⏳ | Verhindert "Class not found" Errors |
| 4 (Mittel) | Route Existence Check | ⏳ | Verhindert 404 bei neuen Routes |
| 5 (Mittel) | PHPStan Quick Check | ⏳ | Frühe Type-Error Erkennung |
| 6 (Niedrig) | DI Sync Check | ⏳ | Verhindert Runtime DI-Fehler |
Erwarteter Impact
Mit den vorgeschlagenen Hooks würden geschätzt 70-80% der im Protokoll gefundenen Korrekturen bereits vor/während der Code-Erstellung abgefangen:
- Fatal Errors: 95% durch Syntax + Class Import Check
- Architecture Violations: 100% durch Architecture Guard ✅
- 404 Errors: 80% durch Route Existence Check
- Type Errors: 60% durch PHPStan Quick Check
Task-Completion Guard Hook
Pre-Hook der verhindert, dass Tasks als "completed" markiert werden, ohne dass vorher ein Ergebnis via tasks_result() geschrieben wurde.
Problem
Claude ruft tasks_status(id, "completed") auf, ohne vorher tasks_result() zu rufen. Dies führt zu 77+ Fehlern pro Woche mit der Meldung:
Task kann nicht abgeschlossen werden. Fehlend: Ergebnis (tasks_result)
Lösung
Ein PreToolUse-Hook der VOR dem MCP-Call prüft, ob ein Result existiert und bei Fehlen die Aktion blockiert.
| Datei | /var/www/tools/ki-protokoll/claude-hook/task_completion_guard.py |
|---|---|
| Trigger | PreToolUse mit Matcher mcp__mcp-tasks__tasks_status |
| Exit-Codes | 0 = allow, 2 = block |
Technische Details
1. Input-Format (stdin)
{
"tool_name": "mcp__mcp-tasks__tasks_status",
"tool_input": {
"id": 425,
"status": "completed"
}
}
2. Prüflogik
- Parse JSON von stdin
- Prüfe:
tool_name == "mcp__mcp-tasks__tasks_status" - Prüfe:
status == "completed" - Wenn ja: DB-Abfrage
SELECT COUNT(*) FROM task_results WHERE task_id = ? - Wenn count == 0: Block mit Exit-Code 2 + stderr-Nachricht
3. Block-Nachricht
BLOCKIERT: Task kann nicht als 'completed' markiert werden!
GRUND: Kein Ergebnis vorhanden (tasks_result fehlt).
LÖSUNG: Vor tasks_status(id, "completed") MUSS tasks_result() aufgerufen werden:
tasks_result(
id=TASK_ID,
response="Zusammenfassung der erledigten Arbeit...",
executor="claude",
executor_type="claude"
)
Erst danach: tasks_status(id, "completed")
4. Konfiguration in settings.json
{
"PreToolUse": [
{
"matcher": "mcp__mcp-tasks__tasks_status",
"hooks": [
{
"type": "command",
"command": "/var/www/tools/ki-protokoll/claude-hook/task_completion_guard.py",
"timeout": 5
}
]
}
]
}
Ablauf
┌─────────────────────────────┐
│ Claude: tasks_status( │
│ id=425, status="completed"│
│ ) │
└─────────────┬───────────────┘
│
▼
┌─────────────────────────────┐
│ PreToolUse Hook auslösen │
│ Matcher: mcp__mcp-tasks__ │
│ tasks_status │
└─────────────┬───────────────┘
│
▼
┌─────────────────────────────┐
│ task_completion_guard.py │
│ - Parse Input │
│ - Check: status=="completed"│
└─────────────┬───────────────┘
│
┌──────┴──────┐
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ status != │ │ status == │
│ "completed" │ │ "completed" │
└──────┬──────┘ └──────┬──────┘
│ │
▼ ▼
┌─────────────┐ ┌─────────────────┐
│ exit(0) │ │ DB-Check: │
│ → Allow │ │ task_results │
└─────────────┘ │ WHERE task_id=? │
└────────┬────────┘
│
┌──────┴──────┐
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ count > 0│ │ count = 0│
└────┬─────┘ └────┬─────┘
│ │
▼ ▼
┌─────────┐ ┌─────────────┐
│ exit(0) │ │ stderr: │
│ → Allow │ │ BLOCKIERT │
└─────────┘ │ exit(2) │
│ → Block │
└─────────────┘
Implementierungsplan
Schritt 1: Hook-Script erstellen
Datei: /var/www/tools/ki-protokoll/claude-hook/task_completion_guard.py
#!/usr/bin/env python3
"""
Task Completion Guard Hook
Blockiert tasks_status(completed) wenn kein Result existiert.
Erzwingt korrekten Workflow: tasks_result() vor tasks_status(completed).
Trigger: PreToolUse (mcp__mcp-tasks__tasks_status)
"""
import json
import sys
import os
from pathlib import Path
from dotenv import load_dotenv
import pymysql
# Lade Environment aus .env im Hook-Verzeichnis
load_dotenv(Path(__file__).parent / '.env')
DB_CONFIG = {
'host': os.environ.get('CLAUDE_DB_HOST', 'localhost'),
'port': int(os.environ.get('CLAUDE_DB_PORT', '3306')),
'user': os.environ.get('CLAUDE_DB_USER', 'root'),
'password': os.environ.get('CLAUDE_DB_PASSWORD', ''),
'database': 'ki_dev',
'charset': 'utf8mb4'
}
BLOCK_MESSAGE = """BLOCKIERT: Task kann nicht als 'completed' markiert werden!
GRUND: Kein Ergebnis vorhanden (tasks_result fehlt).
LÖSUNG: Vor tasks_status(id, "completed") MUSS tasks_result() aufgerufen werden:
tasks_result(
id={task_id},
response="Zusammenfassung der erledigten Arbeit...",
executor="claude",
executor_type="claude"
)
Erst danach: tasks_status({task_id}, "completed")"""
def check_result_exists(task_id: int) -> bool:
"""Prüft ob ein Result für den Task existiert."""
try:
conn = pymysql.connect(**DB_CONFIG)
cursor = conn.cursor()
cursor.execute(
"SELECT COUNT(*) FROM task_results WHERE task_id = %s",
(task_id,)
)
count = cursor.fetchone()[0]
conn.close()
return count > 0
except Exception as e:
# Bei DB-Fehler durchlassen (fail-open)
print(f"DB-Check fehlgeschlagen: {e}", file=sys.stderr)
return True
def main():
try:
input_data = json.load(sys.stdin)
except json.JSONDecodeError:
sys.exit(0)
# Nur mcp__mcp-tasks__tasks_status prüfen
tool_name = input_data.get("tool_name", "")
if tool_name != "mcp__mcp-tasks__tasks_status":
sys.exit(0)
# Prüfe ob Status auf "completed" gesetzt wird
tool_input = input_data.get("tool_input", {})
status = tool_input.get("status", "")
task_id = tool_input.get("id")
if status.lower() != "completed":
sys.exit(0)
if not task_id:
sys.exit(0)
# Prüfe ob Result existiert
if not check_result_exists(task_id):
print(BLOCK_MESSAGE.format(task_id=task_id), file=sys.stderr)
sys.exit(2)
sys.exit(0)
if __name__ == "__main__":
main()
Schritt 2: Script ausführbar machen
chmod +x /var/www/tools/ki-protokoll/claude-hook/task_completion_guard.py
Schritt 3: settings.json erweitern
Datei: /root/.claude/settings.json
Füge folgenden Eintrag in "PreToolUse" Array hinzu:
{
"matcher": "mcp__mcp-tasks__tasks_status",
"hooks": [
{
"type": "command",
"command": "/var/www/tools/ki-protokoll/claude-hook/task_completion_guard.py",
"timeout": 5
}
]
}
Schritt 4: Testen
# Test 1: Task ohne Result → sollte blockiert werden (Exit 2)
echo '{"tool_name":"mcp__mcp-tasks__tasks_status","tool_input":{"id":999,"status":"completed"}}' | \
/var/www/tools/ki-protokoll/claude-hook/task_completion_guard.py
echo "Exit code: $?"
# Test 2: Anderer Status → sollte durchgelassen werden (Exit 0)
echo '{"tool_name":"mcp__mcp-tasks__tasks_status","tool_input":{"id":999,"status":"in_progress"}}' | \
/var/www/tools/ki-protokoll/claude-hook/task_completion_guard.py
echo "Exit code: $?"
# Test 3: Anderes Tool → sollte durchgelassen werden (Exit 0)
echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | \
/var/www/tools/ki-protokoll/claude-hook/task_completion_guard.py
echo "Exit code: $?"
Voraussetzungen
- Python 3.x
- PyMySQL (bereits installiert:
pip3 install pymysql) - python-dotenv (bereits installiert)
- Zugriff auf ki_dev.task_results
- .env Datei im Hook-Verzeichnis mit DB-Credentials
Fail-Safe Verhalten
Bei DB-Verbindungsfehlern wird der Hook "fail-open" - d.h. die Aktion wird durchgelassen, um Claude nicht zu blockieren. Die Server-seitige Validierung in MCP-Tasks greift dann als zweite Verteidigung.
Validierung
| Prüfpunkt | Status |
|---|---|
| PyMySQL verfügbar | ✓ PyMySQL 1.1.2 installiert |
| python-dotenv Pattern | ✓ Konsistent mit log_storage.py |
| DB_CONFIG Pattern | ✓ Identisch zu bestehenden Hooks |
| Exit-Code Pattern | ✓ 0=allow, 2=block (wie block_direct_db.py) |
| Matcher-Syntax | ✓ Regex-kompatibel für MCP-Tools |
| Fail-Open Design | ✓ Bei Fehler durchlassen |
Hook-System Python-Erweiterung
Status: ✅ IMPLEMENTIERT (2025-12-28)
Task: #506
Problem
Das Claude Code Hook-System (/var/www/tools/ki-protokoll/claude-hook/) prüfte nur PHP-Dateien. Python-Dateien in der Pipeline (/var/www/scripts/pipeline/) wurden nicht validiert.
Root Cause
# hook_dispatcher.py Zeile 78-80 (vorher)
if not file_path.endswith(".php"):
sys.exit(0)
Implementierung
Phase 1: Dispatcher (hook_dispatcher.py)
ALLOWED_EXTENSIONS = {".php", ".py"}
PYTHON_CHECK_PATHS = ["/var/www/scripts/pipeline/"]
SKIP_PATHS = ["/venv/", "/__pycache__/", "/tests/", "/vendor/"]
def should_check(file_path: str) -> bool:
if any(skip in file_path for skip in SKIP_PATHS):
return False
ext = Path(file_path).suffix
if ext not in ALLOWED_EXTENSIONS:
return False
if ext == ".php":
return True
if ext == ".py":
return any(file_path.startswith(p) for p in PYTHON_CHECK_PATHS)
return False
Phase 2: Python-Regeln (pre_rules_python.py)
PP1.1: Hardcoded Model-Namen blockieren
- BLOCKIERT:
default="mistral",model="gemma" - ERLAUBT:
get_pipeline_model(),DEFAULT_MODEL = "mistral"
PP1.2: Hardcoded Pipeline-IDs blockieren
- BLOCKIERT:
pipeline_id = 5 - ERLAUBT:
DEFAULT_PIPELINE_ID = 5,args.pipeline_id
Phase 3: Integration (pre_rules.py)
# Regelfilterung nach Dateityp
if file_path.endswith(".php"):
rules = PHP_RULES
elif file_path.endswith(".py"):
rules = PYTHON_RULES
else:
return allow()
Testmatrix (7/7 bestanden)
| Test | Szenario | Erwartet | Ergebnis |
|---|---|---|---|
| 1 | PP1.1 default="mistral" | DENY | ✅ |
| 2 | PP1.1 get_pipeline_model() | ALLOW | ✅ |
| 3 | PP1.1 DEFAULT_MODEL = "mistral" | ALLOW | ✅ |
| 4 | PP1.2 pipeline_id = 5 | DENY | ✅ |
| 5 | PP1.2 DEFAULT_PIPELINE_ID = 5 | ALLOW | ✅ |
| 6 | Skip /venv/ Pfade | ALLOW | ✅ |
| 7 | PHP P1.1 @responsibility | DENY | ✅ |
Dateien
| Datei | Aktion |
|---|---|
hook_dispatcher.py | MODIFIZIERT |
quality/pre_rules_python.py | NEU |
quality/pre_rules.py | MODIFIZIERT |