Protokoll #6565

ID6565
Zeitstempel2025-12-23 06:17:58.727146
Clientroot
IP
Warning: Undefined array key "request_ip" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 13

Deprecated: htmlspecialchars(): Passing null to parameter #1 ($string) of type string is deprecated in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 13
Modellclaude-sonnet-4-20250514
Statuscompleted
Tokens6,509 (Input: 0, Output: 0)
Dauer97 ms
Request-Zeit
Warning: Undefined array key "request_timestamp" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 18
Response-Zeit-

Warning: Undefined array key "error_message" in /var/www/dev.campus.systemische-tools.de/src/View/protokoll/show.php on line 22

Request

{
    "event": "PreToolUse",
    "tool_name": "Write",
    "tool_input": {
        "file_path": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/architecture_guard.py",
        "content": "#!\/usr\/bin\/env python3\n\"\"\"\nArchitecture Guard - Pre-Hook (Blocking)\n\nEnforces hard_constraints from architecture-gate-contract v1.1\nBlocks file creation on violation. No exceptions.\n\nRules:\n  H1: strict_types_required (all PHP files)\n  H2: domain_no_infrastructure (Domain layer)\n  H3: db_factory_only (only in Factory classes)\n  H4: no_new_repository_in_controller (Controller layer)\n  H5: no_new_infrastructure_in_controller (Controller layer)\n\nTrigger: PreToolUse (Write) on *.php\n\"\"\"\n\nimport json\nimport re\nimport sys\n\n# Hard rules from architecture-gate-contract v1.1\nHARD_RULES = [\n    {\n        \"id\": \"H1\",\n        \"name\": \"strict_types_required\",\n        \"pattern\": r\"declare\\s*\\(\\s*strict_types\\s*=\\s*1\\s*\\)\",\n        \"must_match\": True,\n        \"applies_to\": \"all\",\n        \"message\": \"Missing declare(strict_types=1). Add at top of file after <?php\"\n    },\n    {\n        \"id\": \"H2\",\n        \"name\": \"domain_no_infrastructure\",\n        \"pattern\": r\"use\\s+Infrastructure\\\\\",\n        \"must_match\": False,\n        \"applies_to\": \"\/Domain\/\",\n        \"message\": \"Domain must not use Infrastructure. Violates DIP.\"\n    },\n    {\n        \"id\": \"H3\",\n        \"name\": \"db_factory_only\",\n        \"pattern\": r\"DatabaseFactory::\",\n        \"must_match\": False,\n        \"applies_to_not\": \"\/Factory\/\",\n        \"message\": \"DatabaseFactory only allowed in Factory classes. Use DI.\"\n    },\n    {\n        \"id\": \"H4\",\n        \"name\": \"no_new_repository_in_controller\",\n        \"pattern\": r\"new\\s+\\w+Repository\\s*\\(\",\n        \"must_match\": False,\n        \"applies_to\": \"\/Controller\/\",\n        \"message\": \"new Repository in Controller not allowed. Use DI via constructor.\"\n    },\n    {\n        \"id\": \"H5\",\n        \"name\": \"no_new_infrastructure_in_controller\",\n        \"pattern\": r\"new\\s+Infrastructure\\\\\",\n        \"must_match\": False,\n        \"applies_to\": \"\/Controller\/\",\n        \"message\": \"new Infrastructure in Controller not allowed. Use DI via constructor.\"\n    }\n]\n\n# Paths exempt from all rules\nALLOWED_PATHS = [\n    \"\/Factory\/\",\n    \"\/Bootstrap\/\",\n    \"\/tests\/\",\n    \"\/Test\/\",\n    \"\/vendor\/\",\n]\n\n\ndef is_allowed_path(file_path: str) -> bool:\n    \"\"\"Check if file is in allowlist.\"\"\"\n    for allowed in ALLOWED_PATHS:\n        if allowed in file_path:\n            return True\n    return False\n\n\ndef rule_applies(rule: dict, file_path: str) -> bool:\n    \"\"\"Check if rule applies to this file path.\"\"\"\n    if \"applies_to\" in rule:\n        if rule[\"applies_to\"] == \"all\":\n            return True\n        return rule[\"applies_to\"] in file_path\n\n    if \"applies_to_not\" in rule:\n        return rule[\"applies_to_not\"] not in file_path\n\n    return True\n\n\ndef check_rule(rule: dict, content: str) -> bool:\n    \"\"\"\n    Check if content violates the rule.\n\n    Returns True if VIOLATED, False if OK.\n    \"\"\"\n    match = re.search(rule[\"pattern\"], content)\n\n    if rule[\"must_match\"]:\n        # Pattern MUST be present\n        return match is None  # Violated if NOT found\n    else:\n        # Pattern must NOT be present\n        return match is not None  # Violated if found\n\n\ndef check_all_rules(file_path: str, content: str) -> dict:\n    \"\"\"\n    Check all rules against file.\n\n    Returns:\n        {\"allowed\": True} if all pass\n        {\"allowed\": False, \"message\": \"...\"} on first violation\n    \"\"\"\n    # Skip non-PHP files\n    if not file_path.endswith(\".php\"):\n        return {\"allowed\": True}\n\n    # Skip allowlisted paths\n    if is_allowed_path(file_path):\n        return {\"allowed\": True}\n\n    for rule in HARD_RULES:\n        if not rule_applies(rule, file_path):\n            continue\n\n        if check_rule(rule, content):\n            return {\n                \"allowed\": False,\n                \"message\": f\"ARCHITECTURE VIOLATION [{rule['id']}]: {rule['message']}\"\n            }\n\n    return {\"allowed\": True}\n\n\ndef format_output(result: dict) -> str:\n    \"\"\"Format output as JSON for Claude Code hook protocol.\"\"\"\n    return json.dumps(result)\n\n\ndef main():\n    try:\n        input_data = json.load(sys.stdin)\n    except json.JSONDecodeError:\n        # Invalid input, allow by default\n        print(json.dumps({\"allowed\": True}))\n        sys.exit(0)\n\n    tool_name = input_data.get(\"tool_name\", \"\")\n\n    # Only check Write operations\n    if tool_name != \"Write\":\n        print(json.dumps({\"allowed\": True}))\n        sys.exit(0)\n\n    tool_input = input_data.get(\"tool_input\", {})\n    file_path = tool_input.get(\"file_path\", \"\")\n    content = tool_input.get(\"content\", \"\")\n\n    result = check_all_rules(file_path, content)\n\n    print(format_output(result))\n\n    if result[\"allowed\"]:\n        sys.exit(0)\n    else:\n        sys.exit(0)  # Exit 0 with allowed:false triggers block\n\n\nif __name__ == \"__main__\":\n    main()\n"
    }
}

Response

{
    "tool_response": {
        "type": "update",
        "filePath": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/architecture_guard.py",
        "content": "#!\/usr\/bin\/env python3\n\"\"\"\nArchitecture Guard - Pre-Hook (Blocking)\n\nEnforces hard_constraints from architecture-gate-contract v1.1\nBlocks file creation on violation. No exceptions.\n\nRules:\n  H1: strict_types_required (all PHP files)\n  H2: domain_no_infrastructure (Domain layer)\n  H3: db_factory_only (only in Factory classes)\n  H4: no_new_repository_in_controller (Controller layer)\n  H5: no_new_infrastructure_in_controller (Controller layer)\n\nTrigger: PreToolUse (Write) on *.php\n\"\"\"\n\nimport json\nimport re\nimport sys\n\n# Hard rules from architecture-gate-contract v1.1\nHARD_RULES = [\n    {\n        \"id\": \"H1\",\n        \"name\": \"strict_types_required\",\n        \"pattern\": r\"declare\\s*\\(\\s*strict_types\\s*=\\s*1\\s*\\)\",\n        \"must_match\": True,\n        \"applies_to\": \"all\",\n        \"message\": \"Missing declare(strict_types=1). Add at top of file after <?php\"\n    },\n    {\n        \"id\": \"H2\",\n        \"name\": \"domain_no_infrastructure\",\n        \"pattern\": r\"use\\s+Infrastructure\\\\\",\n        \"must_match\": False,\n        \"applies_to\": \"\/Domain\/\",\n        \"message\": \"Domain must not use Infrastructure. Violates DIP.\"\n    },\n    {\n        \"id\": \"H3\",\n        \"name\": \"db_factory_only\",\n        \"pattern\": r\"DatabaseFactory::\",\n        \"must_match\": False,\n        \"applies_to_not\": \"\/Factory\/\",\n        \"message\": \"DatabaseFactory only allowed in Factory classes. Use DI.\"\n    },\n    {\n        \"id\": \"H4\",\n        \"name\": \"no_new_repository_in_controller\",\n        \"pattern\": r\"new\\s+\\w+Repository\\s*\\(\",\n        \"must_match\": False,\n        \"applies_to\": \"\/Controller\/\",\n        \"message\": \"new Repository in Controller not allowed. Use DI via constructor.\"\n    },\n    {\n        \"id\": \"H5\",\n        \"name\": \"no_new_infrastructure_in_controller\",\n        \"pattern\": r\"new\\s+Infrastructure\\\\\",\n        \"must_match\": False,\n        \"applies_to\": \"\/Controller\/\",\n        \"message\": \"new Infrastructure in Controller not allowed. Use DI via constructor.\"\n    }\n]\n\n# Paths exempt from all rules\nALLOWED_PATHS = [\n    \"\/Factory\/\",\n    \"\/Bootstrap\/\",\n    \"\/tests\/\",\n    \"\/Test\/\",\n    \"\/vendor\/\",\n]\n\n\ndef is_allowed_path(file_path: str) -> bool:\n    \"\"\"Check if file is in allowlist.\"\"\"\n    for allowed in ALLOWED_PATHS:\n        if allowed in file_path:\n            return True\n    return False\n\n\ndef rule_applies(rule: dict, file_path: str) -> bool:\n    \"\"\"Check if rule applies to this file path.\"\"\"\n    if \"applies_to\" in rule:\n        if rule[\"applies_to\"] == \"all\":\n            return True\n        return rule[\"applies_to\"] in file_path\n\n    if \"applies_to_not\" in rule:\n        return rule[\"applies_to_not\"] not in file_path\n\n    return True\n\n\ndef check_rule(rule: dict, content: str) -> bool:\n    \"\"\"\n    Check if content violates the rule.\n\n    Returns True if VIOLATED, False if OK.\n    \"\"\"\n    match = re.search(rule[\"pattern\"], content)\n\n    if rule[\"must_match\"]:\n        # Pattern MUST be present\n        return match is None  # Violated if NOT found\n    else:\n        # Pattern must NOT be present\n        return match is not None  # Violated if found\n\n\ndef check_all_rules(file_path: str, content: str) -> dict:\n    \"\"\"\n    Check all rules against file.\n\n    Returns:\n        {\"allowed\": True} if all pass\n        {\"allowed\": False, \"message\": \"...\"} on first violation\n    \"\"\"\n    # Skip non-PHP files\n    if not file_path.endswith(\".php\"):\n        return {\"allowed\": True}\n\n    # Skip allowlisted paths\n    if is_allowed_path(file_path):\n        return {\"allowed\": True}\n\n    for rule in HARD_RULES:\n        if not rule_applies(rule, file_path):\n            continue\n\n        if check_rule(rule, content):\n            return {\n                \"allowed\": False,\n                \"message\": f\"ARCHITECTURE VIOLATION [{rule['id']}]: {rule['message']}\"\n            }\n\n    return {\"allowed\": True}\n\n\ndef format_output(result: dict) -> str:\n    \"\"\"Format output as JSON for Claude Code hook protocol.\"\"\"\n    return json.dumps(result)\n\n\ndef main():\n    try:\n        input_data = json.load(sys.stdin)\n    except json.JSONDecodeError:\n        # Invalid input, allow by default\n        print(json.dumps({\"allowed\": True}))\n        sys.exit(0)\n\n    tool_name = input_data.get(\"tool_name\", \"\")\n\n    # Only check Write operations\n    if tool_name != \"Write\":\n        print(json.dumps({\"allowed\": True}))\n        sys.exit(0)\n\n    tool_input = input_data.get(\"tool_input\", {})\n    file_path = tool_input.get(\"file_path\", \"\")\n    content = tool_input.get(\"content\", \"\")\n\n    result = check_all_rules(file_path, content)\n\n    print(format_output(result))\n\n    if result[\"allowed\"]:\n        sys.exit(0)\n    else:\n        sys.exit(0)  # Exit 0 with allowed:false triggers block\n\n\nif __name__ == \"__main__\":\n    main()\n",
        "structuredPatch": [
            {
                "oldStart": 1,
                "oldLines": 167,
                "newStart": 1,
                "newLines": 177,
                "lines": [
                    " #!\/usr\/bin\/env python3",
                    " \"\"\"",
                    "-Architecture Guard Hook",
                    "+Architecture Guard - Pre-Hook (Blocking)",
                    " ",
                    "-Blockiert Architektur-Verletzungen BEVOR Code geschrieben wird.",
                    "-- Controller > 500 LOC: Block",
                    "-- Controller > 400 LOC: Warnung",
                    "-- PDO\/SQL in Controller: Block",
                    "+Enforces hard_constraints from architecture-gate-contract v1.1",
                    "+Blocks file creation on violation. No exceptions.",
                    " ",
                    "-Trigger: PreToolUse (Write, Edit)",
                    "+Rules:",
                    "+  H1: strict_types_required (all PHP files)",
                    "+  H2: domain_no_infrastructure (Domain layer)",
                    "+  H3: db_factory_only (only in Factory classes)",
                    "+  H4: no_new_repository_in_controller (Controller layer)",
                    "+  H5: no_new_infrastructure_in_controller (Controller layer)",
                    "+",
                    "+Trigger: PreToolUse (Write) on *.php",
                    " \"\"\"",
                    " ",
                    " import json",
                    " import re",
                    " import sys",
                    " ",
                    "-# Patterns für direkten DB-Zugriff in Controllern",
                    "-PDO_PATTERNS = [",
                    "-    r'\\$this->db\\s*=',            # $this->db = ...",
                    "-    r'\\$this->db->',              # $this->db->query() etc.",
                    "-    r'new\\s+\\\\?PDO\\(',            # new PDO( oder new \\PDO(",
                    "-    r'->query\\s*\\(',              # ->query(",
                    "-    r'->prepare\\s*\\(',            # ->prepare(",
                    "-    r'->exec\\s*\\(',               # ->exec(",
                    "-    r'DatabaseFactory::',          # Direkter Factory-Zugriff",
                    "-    r'getDatabase\\(\\)',           # getDatabase() Methode",
                    "+# Hard rules from architecture-gate-contract v1.1",
                    "+HARD_RULES = [",
                    "+    {",
                    "+        \"id\": \"H1\",",
                    "+        \"name\": \"strict_types_required\",",
                    "+        \"pattern\": r\"declare\\s*\\(\\s*strict_types\\s*=\\s*1\\s*\\)\",",
                    "+        \"must_match\": True,",
                    "+        \"applies_to\": \"all\",",
                    "+        \"message\": \"Missing declare(strict_types=1). Add at top of file after <?php\"",
                    "+    },",
                    "+    {",
                    "+        \"id\": \"H2\",",
                    "+        \"name\": \"domain_no_infrastructure\",",
                    "+        \"pattern\": r\"use\\s+Infrastructure\\\\\",",
                    "+        \"must_match\": False,",
                    "+        \"applies_to\": \"\/Domain\/\",",
                    "+        \"message\": \"Domain must not use Infrastructure. Violates DIP.\"",
                    "+    },",
                    "+    {",
                    "+        \"id\": \"H3\",",
                    "+        \"name\": \"db_factory_only\",",
                    "+        \"pattern\": r\"DatabaseFactory::\",",
                    "+        \"must_match\": False,",
                    "+        \"applies_to_not\": \"\/Factory\/\",",
                    "+        \"message\": \"DatabaseFactory only allowed in Factory classes. Use DI.\"",
                    "+    },",
                    "+    {",
                    "+        \"id\": \"H4\",",
                    "+        \"name\": \"no_new_repository_in_controller\",",
                    "+        \"pattern\": r\"new\\s+\\w+Repository\\s*\\(\",",
                    "+        \"must_match\": False,",
                    "+        \"applies_to\": \"\/Controller\/\",",
                    "+        \"message\": \"new Repository in Controller not allowed. Use DI via constructor.\"",
                    "+    },",
                    "+    {",
                    "+        \"id\": \"H5\",",
                    "+        \"name\": \"no_new_infrastructure_in_controller\",",
                    "+        \"pattern\": r\"new\\s+Infrastructure\\\\\",",
                    "+        \"must_match\": False,",
                    "+        \"applies_to\": \"\/Controller\/\",",
                    "+        \"message\": \"new Infrastructure in Controller not allowed. Use DI via constructor.\"",
                    "+    }",
                    " ]",
                    " ",
                    "-LOC_WARNING_THRESHOLD = 400",
                    "-LOC_BLOCK_THRESHOLD = 500",
                    "+# Paths exempt from all rules",
                    "+ALLOWED_PATHS = [",
                    "+    \"\/Factory\/\",",
                    "+    \"\/Bootstrap\/\",",
                    "+    \"\/tests\/\",",
                    "+    \"\/Test\/\",",
                    "+    \"\/vendor\/\",",
                    "+]",
                    " ",
                    " ",
                    "-def count_lines(content: str) -> int:",
                    "-    \"\"\"Zählt nicht-leere Zeilen.\"\"\"",
                    "-    return len([line for line in content.split('\\n') if line.strip()])",
                    "+def is_allowed_path(file_path: str) -> bool:",
                    "+    \"\"\"Check if file is in allowlist.\"\"\"",
                    "+    for allowed in ALLOWED_PATHS:",
                    "+        if allowed in file_path:",
                    "+            return True",
                    "+    return False",
                    " ",
                    " ",
                    "-def is_controller_file(file_path: str) -> bool:",
                    "-    \"\"\"Prüft ob es sich um einen Controller handelt.\"\"\"",
                    "-    return '\/Controller\/' in file_path and file_path.endswith('.php')",
                    "+def rule_applies(rule: dict, file_path: str) -> bool:",
                    "+    \"\"\"Check if rule applies to this file path.\"\"\"",
                    "+    if \"applies_to\" in rule:",
                    "+        if rule[\"applies_to\"] == \"all\":",
                    "+            return True",
                    "+        return rule[\"applies_to\"] in file_path",
                    " ",
                    "+    if \"applies_to_not\" in rule:",
                    "+        return rule[\"applies_to_not\"] not in file_path",
                    " ",
                    "-def check_pdo_patterns(content: str) -> list[str]:",
                    "-    \"\"\"Findet PDO\/SQL-Patterns im Content.\"\"\"",
                    "-    found = []",
                    "-    for pattern in PDO_PATTERNS:",
                    "-        if re.search(pattern, content):",
                    "-            found.append(pattern)",
                    "-    return found",
                    "+    return True",
                    " ",
                    " ",
                    "-def check_controller(file_path: str, content: str) -> dict:",
                    "+def check_rule(rule: dict, content: str) -> bool:",
                    "     \"\"\"",
                    "-    Prüft Controller auf Architektur-Verletzungen.",
                    "+    Check if content violates the rule.",
                    " ",
                    "-    Returns:",
                    "-        dict mit keys: block, warn, reason",
                    "+    Returns True if VIOLATED, False if OK.",
                    "     \"\"\"",
                    "-    if not is_controller_file(file_path):",
                    "-        return {'block': False, 'warn': False, 'reason': ''}",
                    "+    match = re.search(rule[\"pattern\"], content)",
                    " ",
                    "-    result = {'block': False, 'warn': False, 'reason': ''}",
                    "-    messages = []",
                    "+    if rule[\"must_match\"]:",
                    "+        # Pattern MUST be present",
                    "+        return match is None  # Violated if NOT found",
                    "+    else:",
                    "+        # Pattern must NOT be present",
                    "+        return match is not None  # Violated if found",
                    " ",
                    "-    # LOC-Prüfung",
                    "-    loc = count_lines(content)",
                    " ",
                    "-    if loc > LOC_BLOCK_THRESHOLD:",
                    "-        result['block'] = True",
                    "-        messages.append(f\"Controller hat {loc} LOC (max {LOC_BLOCK_THRESHOLD})\")",
                    "-    elif loc > LOC_WARNING_THRESHOLD:",
                    "-        result['warn'] = True",
                    "-        messages.append(f\"Controller nähert sich LOC-Limit: {loc}\/{LOC_BLOCK_THRESHOLD}\")",
                    "+def check_all_rules(file_path: str, content: str) -> dict:",
                    "+    \"\"\"",
                    "+    Check all rules against file.",
                    " ",
                    "-    # PDO-Pattern-Prüfung",
                    "-    pdo_matches = check_pdo_patterns(content)",
                    "+    Returns:",
                    "+        {\"allowed\": True} if all pass",
                    "+        {\"allowed\": False, \"message\": \"...\"} on first violation",
                    "+    \"\"\"",
                    "+    # Skip non-PHP files",
                    "+    if not file_path.endswith(\".php\"):",
                    "+        return {\"allowed\": True}",
                    " ",
                    "-    if pdo_matches:",
                    "-        result['block'] = True",
                    "-        messages.append(f\"Controller enthält direkten DB-Zugriff: {', '.join(pdo_matches[:3])}\")",
                    "+    # Skip allowlisted paths",
                    "+    if is_allowed_path(file_path):",
                    "+        return {\"allowed\": True}",
                    " ",
                    "-    if messages:",
                    "-        result['reason'] = '\\n'.join(messages)",
                    "+    for rule in HARD_RULES:",
                    "+        if not rule_applies(rule, file_path):",
                    "+            continue",
                    " ",
                    "-    return result",
                    "+        if check_rule(rule, content):",
                    "+            return {",
                    "+                \"allowed\": False,",
                    "+                \"message\": f\"ARCHITECTURE VIOLATION [{rule['id']}]: {rule['message']}\"",
                    "+            }",
                    " ",
                    "+    return {\"allowed\": True}",
                    " ",
                    "-def format_block_message(reason: str) -> str:",
                    "-    \"\"\"Formatiert Blocknachricht mit Lösungshinweisen.\"\"\"",
                    "-    return f\"\"\"ARCHITEKTUR-VERLETZUNG BLOCKIERT!",
                    " ",
                    "-{reason}",
                    "+def format_output(result: dict) -> str:",
                    "+    \"\"\"Format output as JSON for Claude Code hook protocol.\"\"\"",
                    "+    return json.dumps(result)",
                    " ",
                    "-Lösungen:",
                    "-1. LOC zu hoch: Service\/Repository extrahieren",
                    "-2. PDO in Controller:",
                    "-   - Nutze Repository-Pattern",
                    "-   - Erstelle Infrastructure\/Persistence\/*Repository.php",
                    "-   - Controller ruft nur Repository-Methoden auf",
                    " ",
                    "-Beispiel:",
                    "-  \/\/ FALSCH (im Controller):",
                    "-  $this->db->query(\"SELECT * FROM users\")",
                    "-",
                    "-  \/\/ RICHTIG:",
                    "-  $this->userRepository->findAll()",
                    "-",
                    "-Architektur-Regeln: \/var\/www\/docs\/contracts\/code-quality-standards.yaml\"\"\"",
                    "-",
                    "-",
                    "-def format_warning_message(reason: str) -> str:",
                    "-    \"\"\"Formatiert Warnungsnachricht.\"\"\"",
                    "-    return f\"\"\"ARCHITEKTUR-WARNUNG:",
                    "-",
                    "-{reason}",
                    "-",
                    "-Empfehlung: Erwäge Refactoring bevor das Limit erreicht wird.\"\"\"",
                    "-",
                    "-",
                    " def main():",
                    "     try:",
                    "         input_data = json.load(sys.stdin)",
                    "     except json.JSONDecodeError:",
                    "+        # Invalid input, allow by default",
                    "+        print(json.dumps({\"allowed\": True}))",
                    "         sys.exit(0)",
                    " ",
                    "-    tool_name = input_data.get('tool_name', '')",
                    "+    tool_name = input_data.get(\"tool_name\", \"\")",
                    " ",
                    "-    # Nur Write und Edit prüfen",
                    "-    if tool_name not in ('Write', 'Edit'):",
                    "+    # Only check Write operations",
                    "+    if tool_name != \"Write\":",
                    "+        print(json.dumps({\"allowed\": True}))",
                    "         sys.exit(0)",
                    " ",
                    "-    tool_input = input_data.get('tool_input', {})",
                    "-    file_path = tool_input.get('file_path', '')",
                    "+    tool_input = input_data.get(\"tool_input\", {})",
                    "+    file_path = tool_input.get(\"file_path\", \"\")",
                    "+    content = tool_input.get(\"content\", \"\")",
                    " ",
                    "-    # Content ermitteln",
                    "-    if tool_name == 'Write':",
                    "-        content = tool_input.get('content', '')",
                    "-    else:  # Edit",
                    "-        # Bei Edit: neuen String prüfen (vereinfacht)",
                    "-        content = tool_input.get('new_string', '')",
                    "-        # Edit-Prüfung nur auf PDO-Patterns, nicht LOC",
                    "-        if is_controller_file(file_path):",
                    "-            pdo_matches = check_pdo_patterns(content)",
                    "-            if pdo_matches:",
                    "-                print(format_block_message(",
                    "-                    f\"Edit fügt direkten DB-Zugriff hinzu: {', '.join(pdo_matches[:3])}\"",
                    "-                ), file=sys.stderr)",
                    "-                sys.exit(2)",
                    "-        sys.exit(0)",
                    "+    result = check_all_rules(file_path, content)",
                    " ",
                    "-    # Vollständige Prüfung nur bei Write",
                    "-    result = check_controller(file_path, content)",
                    "+    print(format_output(result))",
                    " ",
                    "-    if result['block']:",
                    "-        print(format_block_message(result['reason']), file=sys.stderr)",
                    "-        sys.exit(2)",
                    "-",
                    "-    if result['warn']:",
                    "-        # Warnung ausgeben aber durchlassen",
                    "-        print(format_warning_message(result['reason']), file=sys.stderr)",
                    "+    if result[\"allowed\"]:",
                    "         sys.exit(0)",
                    "+    else:",
                    "+        sys.exit(0)  # Exit 0 with allowed:false triggers block",
                    " ",
                    "-    sys.exit(0)",
                    " ",
                    "-",
                    "-if __name__ == '__main__':",
                    "+if __name__ == \"__main__\":",
                    "     main()"
                ]
            }
        ],
        "originalFile": "#!\/usr\/bin\/env python3\n\"\"\"\nArchitecture Guard Hook\n\nBlockiert Architektur-Verletzungen BEVOR Code geschrieben wird.\n- Controller > 500 LOC: Block\n- Controller > 400 LOC: Warnung\n- PDO\/SQL in Controller: Block\n\nTrigger: PreToolUse (Write, Edit)\n\"\"\"\n\nimport json\nimport re\nimport sys\n\n# Patterns für direkten DB-Zugriff in Controllern\nPDO_PATTERNS = [\n    r'\\$this->db\\s*=',            # $this->db = ...\n    r'\\$this->db->',              # $this->db->query() etc.\n    r'new\\s+\\\\?PDO\\(',            # new PDO( oder new \\PDO(\n    r'->query\\s*\\(',              # ->query(\n    r'->prepare\\s*\\(',            # ->prepare(\n    r'->exec\\s*\\(',               # ->exec(\n    r'DatabaseFactory::',          # Direkter Factory-Zugriff\n    r'getDatabase\\(\\)',           # getDatabase() Methode\n]\n\nLOC_WARNING_THRESHOLD = 400\nLOC_BLOCK_THRESHOLD = 500\n\n\ndef count_lines(content: str) -> int:\n    \"\"\"Zählt nicht-leere Zeilen.\"\"\"\n    return len([line for line in content.split('\\n') if line.strip()])\n\n\ndef is_controller_file(file_path: str) -> bool:\n    \"\"\"Prüft ob es sich um einen Controller handelt.\"\"\"\n    return '\/Controller\/' in file_path and file_path.endswith('.php')\n\n\ndef check_pdo_patterns(content: str) -> list[str]:\n    \"\"\"Findet PDO\/SQL-Patterns im Content.\"\"\"\n    found = []\n    for pattern in PDO_PATTERNS:\n        if re.search(pattern, content):\n            found.append(pattern)\n    return found\n\n\ndef check_controller(file_path: str, content: str) -> dict:\n    \"\"\"\n    Prüft Controller auf Architektur-Verletzungen.\n\n    Returns:\n        dict mit keys: block, warn, reason\n    \"\"\"\n    if not is_controller_file(file_path):\n        return {'block': False, 'warn': False, 'reason': ''}\n\n    result = {'block': False, 'warn': False, 'reason': ''}\n    messages = []\n\n    # LOC-Prüfung\n    loc = count_lines(content)\n\n    if loc > LOC_BLOCK_THRESHOLD:\n        result['block'] = True\n        messages.append(f\"Controller hat {loc} LOC (max {LOC_BLOCK_THRESHOLD})\")\n    elif loc > LOC_WARNING_THRESHOLD:\n        result['warn'] = True\n        messages.append(f\"Controller nähert sich LOC-Limit: {loc}\/{LOC_BLOCK_THRESHOLD}\")\n\n    # PDO-Pattern-Prüfung\n    pdo_matches = check_pdo_patterns(content)\n\n    if pdo_matches:\n        result['block'] = True\n        messages.append(f\"Controller enthält direkten DB-Zugriff: {', '.join(pdo_matches[:3])}\")\n\n    if messages:\n        result['reason'] = '\\n'.join(messages)\n\n    return result\n\n\ndef format_block_message(reason: str) -> str:\n    \"\"\"Formatiert Blocknachricht mit Lösungshinweisen.\"\"\"\n    return f\"\"\"ARCHITEKTUR-VERLETZUNG BLOCKIERT!\n\n{reason}\n\nLösungen:\n1. LOC zu hoch: Service\/Repository extrahieren\n2. PDO in Controller:\n   - Nutze Repository-Pattern\n   - Erstelle Infrastructure\/Persistence\/*Repository.php\n   - Controller ruft nur Repository-Methoden auf\n\nBeispiel:\n  \/\/ FALSCH (im Controller):\n  $this->db->query(\"SELECT * FROM users\")\n\n  \/\/ RICHTIG:\n  $this->userRepository->findAll()\n\nArchitektur-Regeln: \/var\/www\/docs\/contracts\/code-quality-standards.yaml\"\"\"\n\n\ndef format_warning_message(reason: str) -> str:\n    \"\"\"Formatiert Warnungsnachricht.\"\"\"\n    return f\"\"\"ARCHITEKTUR-WARNUNG:\n\n{reason}\n\nEmpfehlung: Erwäge Refactoring bevor das Limit erreicht wird.\"\"\"\n\n\ndef main():\n    try:\n        input_data = json.load(sys.stdin)\n    except json.JSONDecodeError:\n        sys.exit(0)\n\n    tool_name = input_data.get('tool_name', '')\n\n    # Nur Write und Edit prüfen\n    if tool_name not in ('Write', 'Edit'):\n        sys.exit(0)\n\n    tool_input = input_data.get('tool_input', {})\n    file_path = tool_input.get('file_path', '')\n\n    # Content ermitteln\n    if tool_name == 'Write':\n        content = tool_input.get('content', '')\n    else:  # Edit\n        # Bei Edit: neuen String prüfen (vereinfacht)\n        content = tool_input.get('new_string', '')\n        # Edit-Prüfung nur auf PDO-Patterns, nicht LOC\n        if is_controller_file(file_path):\n            pdo_matches = check_pdo_patterns(content)\n            if pdo_matches:\n                print(format_block_message(\n                    f\"Edit fügt direkten DB-Zugriff hinzu: {', '.join(pdo_matches[:3])}\"\n                ), file=sys.stderr)\n                sys.exit(2)\n        sys.exit(0)\n\n    # Vollständige Prüfung nur bei Write\n    result = check_controller(file_path, content)\n\n    if result['block']:\n        print(format_block_message(result['reason']), file=sys.stderr)\n        sys.exit(2)\n\n    if result['warn']:\n        # Warnung ausgeben aber durchlassen\n        print(format_warning_message(result['reason']), file=sys.stderr)\n        sys.exit(0)\n\n    sys.exit(0)\n\n\nif __name__ == '__main__':\n    main()\n"
    }
}
← Vorheriger Zur Liste Nächster →