Protokoll #15798

ID15798
Zeitstempel2025-12-25 16:56:54.723384
Clientroot
IP145.224.72.140
Modellclaude-sonnet-4-20250514
Statuscompleted
Tokens2,199 (Input: 1,097, Output: 1,102)
Dauer124 ms
Request-Zeit2025-12-25 16:56:54.723384
Response-Zeit2025-12-25 16:56:54.847558

Request

{
    "event": "PreToolUse",
    "tool_name": "Write",
    "tool_input": {
        "file_path": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/quality\/pre_rules_validation.py",
        "content": "#!\/usr\/bin\/env python3\n\"\"\"\nPre-Hook Validation Regeln (BLOCK) - PSR + Types.\n\nP3.x Regeln: strict_types, namespace, classname, return types\n\"\"\"\n\nimport re\nfrom pathlib import Path\nfrom typing import Optional\nfrom .rule_base import GLOBAL_ALLOWLIST, is_in_allowlist\n\n\n# =============================================================================\n# HELPER FUNCTIONS\n# =============================================================================\n\ndef block(rule_id: str, message: str) -> dict:\n    \"\"\"Erzeugt Block-Response.\"\"\"\n    return {\n        \"allowed\": False,\n        \"message\": f\"QUALITY VIOLATION [{rule_id}]: {message}\"\n    }\n\n\n# =============================================================================\n# PRÜFUNG 3: PSR + Types\n# =============================================================================\n\ndef p3_1_strict_types(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P3.1: strict_types erforderlich.\"\"\"\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    strict_pattern = r\"declare\\s*\\(\\s*strict_types\\s*=\\s*1\\s*\\)\\s*;\"\n    if not re.search(strict_pattern, content):\n        return block(\"P3.1\", \"Missing declare(strict_types=1)\")\n\n    return None\n\n\ndef p3_2_namespace_matches_path(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P3.2: Namespace muss Pfad entsprechen.\"\"\"\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    # Namespace-Mapping\n    path_to_namespace = {\n        \"\/src\/Controller\/\": \"Controller\\\\\",\n        \"\/src\/Domain\/\": \"Domain\\\\\",\n        \"\/src\/UseCases\/\": \"UseCases\\\\\",\n        \"\/src\/Application\/\": \"Application\\\\\",\n        \"\/src\/Infrastructure\/\": \"Infrastructure\\\\\",\n        \"\/src\/Framework\/\": \"Framework\\\\\",\n        \"\/app\/\": \"App\\\\\",\n    }\n\n    namespace_match = re.search(r\"namespace\\s+([^;]+);\", content)\n    if not namespace_match:\n        return None  # Kein Namespace = kein Check\n\n    declared_namespace = namespace_match.group(1).strip()\n\n    for path_prefix, ns_prefix in path_to_namespace.items():\n        if path_prefix in file_path:\n            if not declared_namespace.startswith(ns_prefix.rstrip(\"\\\\\")):\n                return block(\"P3.2\", f\"Namespace '{declared_namespace}' does not match path. Expected: '{ns_prefix}...'\")\n            break\n\n    return None\n\n\ndef p3_3_classname_matches_filename(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P3.3: Klassenname muss Dateiname entsprechen.\"\"\"\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    class_match = re.search(r\"(?:class|interface|trait|enum)\\s+(\\w+)\", content)\n    if not class_match:\n        return None  # Keine Klasse = kein Check\n\n    class_name = class_match.group(1)\n    expected_name = Path(file_path).stem\n\n    if class_name != expected_name:\n        return block(\"P3.3\", f\"Class '{class_name}' does not match filename '{expected_name}'\")\n\n    return None\n\n\ndef p3_4_public_method_return_type(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P3.4: Public Methods müssen Return-Type haben.\"\"\"\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    # Finde public functions\n    public_methods = re.findall(\n        r\"public\\s+function\\s+(\\w+)\\s*\\([^)]*\\)\\s*(?::\\s*\\S+)?\",\n        content\n    )\n\n    for method in public_methods:\n        if method.startswith(\"__\"):\n            continue  # Magic methods überspringen\n\n        # Prüfe ob Return-Type vorhanden\n        pattern = rf\"public\\s+function\\s+{method}\\s*\\([^)]*\\)\\s*:\\s*\\S+\"\n        if not re.search(pattern, content):\n            return block(\"P3.4\", f\"Public method '{method}' missing return type\")\n\n    return None\n\n\n# =============================================================================\n# RULE COLLECTION\n# =============================================================================\n\nRULES = [\n    p3_1_strict_types,\n    p3_2_namespace_matches_path,\n    p3_3_classname_matches_filename,\n    p3_4_public_method_return_type,\n]\n"
    }
}

Response

{
    "tool_response": {
        "type": "create",
        "filePath": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/quality\/pre_rules_validation.py",
        "content": "#!\/usr\/bin\/env python3\n\"\"\"\nPre-Hook Validation Regeln (BLOCK) - PSR + Types.\n\nP3.x Regeln: strict_types, namespace, classname, return types\n\"\"\"\n\nimport re\nfrom pathlib import Path\nfrom typing import Optional\nfrom .rule_base import GLOBAL_ALLOWLIST, is_in_allowlist\n\n\n# =============================================================================\n# HELPER FUNCTIONS\n# =============================================================================\n\ndef block(rule_id: str, message: str) -> dict:\n    \"\"\"Erzeugt Block-Response.\"\"\"\n    return {\n        \"allowed\": False,\n        \"message\": f\"QUALITY VIOLATION [{rule_id}]: {message}\"\n    }\n\n\n# =============================================================================\n# PRÜFUNG 3: PSR + Types\n# =============================================================================\n\ndef p3_1_strict_types(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P3.1: strict_types erforderlich.\"\"\"\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    strict_pattern = r\"declare\\s*\\(\\s*strict_types\\s*=\\s*1\\s*\\)\\s*;\"\n    if not re.search(strict_pattern, content):\n        return block(\"P3.1\", \"Missing declare(strict_types=1)\")\n\n    return None\n\n\ndef p3_2_namespace_matches_path(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P3.2: Namespace muss Pfad entsprechen.\"\"\"\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    # Namespace-Mapping\n    path_to_namespace = {\n        \"\/src\/Controller\/\": \"Controller\\\\\",\n        \"\/src\/Domain\/\": \"Domain\\\\\",\n        \"\/src\/UseCases\/\": \"UseCases\\\\\",\n        \"\/src\/Application\/\": \"Application\\\\\",\n        \"\/src\/Infrastructure\/\": \"Infrastructure\\\\\",\n        \"\/src\/Framework\/\": \"Framework\\\\\",\n        \"\/app\/\": \"App\\\\\",\n    }\n\n    namespace_match = re.search(r\"namespace\\s+([^;]+);\", content)\n    if not namespace_match:\n        return None  # Kein Namespace = kein Check\n\n    declared_namespace = namespace_match.group(1).strip()\n\n    for path_prefix, ns_prefix in path_to_namespace.items():\n        if path_prefix in file_path:\n            if not declared_namespace.startswith(ns_prefix.rstrip(\"\\\\\")):\n                return block(\"P3.2\", f\"Namespace '{declared_namespace}' does not match path. Expected: '{ns_prefix}...'\")\n            break\n\n    return None\n\n\ndef p3_3_classname_matches_filename(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P3.3: Klassenname muss Dateiname entsprechen.\"\"\"\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    class_match = re.search(r\"(?:class|interface|trait|enum)\\s+(\\w+)\", content)\n    if not class_match:\n        return None  # Keine Klasse = kein Check\n\n    class_name = class_match.group(1)\n    expected_name = Path(file_path).stem\n\n    if class_name != expected_name:\n        return block(\"P3.3\", f\"Class '{class_name}' does not match filename '{expected_name}'\")\n\n    return None\n\n\ndef p3_4_public_method_return_type(file_path: str, content: str) -> Optional[dict]:\n    \"\"\"P3.4: Public Methods müssen Return-Type haben.\"\"\"\n    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):\n        return None\n\n    # Finde public functions\n    public_methods = re.findall(\n        r\"public\\s+function\\s+(\\w+)\\s*\\([^)]*\\)\\s*(?::\\s*\\S+)?\",\n        content\n    )\n\n    for method in public_methods:\n        if method.startswith(\"__\"):\n            continue  # Magic methods überspringen\n\n        # Prüfe ob Return-Type vorhanden\n        pattern = rf\"public\\s+function\\s+{method}\\s*\\([^)]*\\)\\s*:\\s*\\S+\"\n        if not re.search(pattern, content):\n            return block(\"P3.4\", f\"Public method '{method}' missing return type\")\n\n    return None\n\n\n# =============================================================================\n# RULE COLLECTION\n# =============================================================================\n\nRULES = [\n    p3_1_strict_types,\n    p3_2_namespace_matches_path,\n    p3_3_classname_matches_filename,\n    p3_4_public_method_return_type,\n]\n",
        "structuredPatch": [],
        "originalFile": null
    }
}
← Vorheriger Zur Liste Nächster →