Protokoll #28018

ID28018
Zeitstempel2025-12-28 12:41:50.195129
Clientroot
IP145.224.72.140
Modellclaude-sonnet-4-20250514
Statuscompleted
Tokens4,153 (Input: 2,074, Output: 2,079)
Dauer79 ms
Request-Zeit2025-12-28 12:41:50.195129
Response-Zeit2025-12-28 12:41:50.273731

Request

{
    "event": "PreToolUse",
    "tool_name": "Write",
    "tool_input": {
        "file_path": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/quality\/rules_leastsurprise.py",
        "content": "#!\/usr\/bin\/env python3\n\"\"\"\nPost-Hook Least Surprise Regeln (WARN) - Least Surprise Principle.\n\nW15.x Regeln: Warnt bei Code der sich anders verhält als erwartet.\n\nPrinzip: \"Code verhält sich so, wie Name und Struktur erwarten lassen. Keine Überraschungen.\"\n\"\"\"\n\nimport re\nfrom typing import List\nfrom .rule_base import Rule\n\n\n# =============================================================================\n# W15: LEAST SURPRISE PRINCIPLE\n# =============================================================================\n\nclass W15_1_GetterWithSideEffect(Rule):\n    \"\"\"W15.1: Getter mit Seiteneffekten - unerwartet bei lesenden Methoden.\"\"\"\n\n    # Seiteneffekt-Indikatoren\n    SIDE_EFFECT_PATTERNS = [\n        r\"\\bsave\\s*\\(\",\n        r\"\\bupdate\\s*\\(\",\n        r\"\\bdelete\\s*\\(\",\n        r\"\\binsert\\s*\\(\",\n        r\"\\bwrite\\s*\\(\",\n        r\"\\bpersist\\s*\\(\",\n        r\"\\bflush\\s*\\(\",\n        r\"\\bsend\\s*\\(\",\n        r\"\\bexecute\\s*\\(\",\n        r\"\\$this->\\w+\\s*=\",  # Property-Änderung\n        r\"\\$this->\\w+\\s*\\[\\s*\\]\\s*=\",  # Array-Push\n        r\"\\$this->\\w+\\s*\\+\\+\",  # Increment\n        r\"\\$this->\\w+\\s*--\",  # Decrement\n    ]\n\n    def check(self, file_path: str, content: str) -> List[str]:\n        warnings = []\n\n        # Finde alle get*-Methoden\n        getter_pattern = r\"function\\s+(get[A-Z]\\w*)\\s*\\([^)]*\\)\\s*(?::\\s*[^{]+)?\\s*\\{\"\n        getter_matches = list(re.finditer(getter_pattern, content))\n\n        for match in getter_matches:\n            method_name = match.group(1)\n            method_start = match.end()\n\n            # Finde das Ende der Methode\n            brace_count = 1\n            pos = method_start\n            while pos < len(content) and brace_count > 0:\n                if content[pos] == '{':\n                    brace_count += 1\n                elif content[pos] == '}':\n                    brace_count -= 1\n                pos += 1\n\n            method_body = content[method_start:pos]\n\n            # Prüfe auf Seiteneffekte\n            for side_effect_pattern in self.SIDE_EFFECT_PATTERNS:\n                if re.search(side_effect_pattern, method_body):\n                    warnings.append(\n                        f\"W15.1: Getter '{method_name}' appears to have side effects. \"\n                        f\"Getters should be pure read operations.\"\n                    )\n                    break  # Nur eine Warnung pro Getter\n\n        return warnings\n\n\nclass W15_2_SetterWithReturn(Rule):\n    \"\"\"W15.2: Setter mit Return-Wert - unerwartet bei schreibenden Methoden.\"\"\"\n\n    def check(self, file_path: str, content: str) -> List[str]:\n        warnings = []\n\n        # Finde set*-Methoden mit Return-Type (außer self\/static für fluent)\n        pattern = r\"function\\s+(set[A-Z]\\w*)\\s*\\([^)]+\\)\\s*:\\s*(?!self|static|void|\\$this)[A-Z]\\w*\"\n\n        matches = re.findall(pattern, content)\n        for method_name in matches:\n            warnings.append(\n                f\"W15.2: Setter '{method_name}' has unexpected return type. \"\n                f\"Setters should return void or self (for fluent interface).\"\n            )\n\n        return warnings\n\n\nclass W15_3_BoolMethodWithoutQuestion(Rule):\n    \"\"\"W15.3: Boolean-Methode ohne is\/has\/can\/should Präfix.\"\"\"\n\n    def check(self, file_path: str, content: str) -> List[str]:\n        warnings = []\n\n        # Finde Methoden die bool zurückgeben\n        pattern = r\"function\\s+(\\w+)\\s*\\([^)]*\\)\\s*:\\s*bool\"\n\n        matches = re.findall(pattern, content)\n        for method_name in matches:\n            # Erlaubte Präfixe für bool-Methoden\n            if not re.match(r\"^(is|has|can|should|will|was|does|did|are)\", method_name):\n                warnings.append(\n                    f\"W15.3: Boolean method '{method_name}' should start with \"\n                    f\"is\/has\/can\/should for clarity.\"\n                )\n\n        return warnings\n\n\nclass W15_4_ConstructorWithReturn(Rule):\n    \"\"\"W15.4: Constructor mit explizitem return - unerwartet.\"\"\"\n\n    def check(self, file_path: str, content: str) -> List[str]:\n        warnings = []\n\n        # Finde __construct Methoden\n        construct_match = re.search(\n            r\"function\\s+__construct\\s*\\([^)]*\\)\\s*\\{\",\n            content\n        )\n\n        if construct_match:\n            construct_start = construct_match.end()\n\n            # Finde das Ende des Constructors\n            brace_count = 1\n            pos = construct_start\n            while pos < len(content) and brace_count > 0:\n                if content[pos] == '{':\n                    brace_count += 1\n                elif content[pos] == '}':\n                    brace_count -= 1\n                pos += 1\n\n            construct_body = content[construct_start:pos]\n\n            # Prüfe auf return mit Wert (nicht nur 'return;')\n            if re.search(r\"\\breturn\\s+[^;]+;\", construct_body):\n                warnings.append(\n                    \"W15.4: Constructor has return statement with value. \"\n                    \"Constructors should not return values.\"\n                )\n\n        return warnings\n\n\nclass W15_5_VoidMethodWithEcho(Rule):\n    \"\"\"W15.5: Void-Methode mit Echo - unerwartete Ausgabe.\"\"\"\n\n    def check(self, file_path: str, content: str) -> List[str]:\n        # Überspringe Views und Templates\n        if \"\/View\/\" in file_path or \"\/templates\/\" in file_path:\n            return []\n\n        warnings = []\n\n        # Finde void-Methoden\n        pattern = r\"function\\s+(\\w+)\\s*\\([^)]*\\)\\s*:\\s*void\\s*\\{\"\n\n        for match in re.finditer(pattern, content):\n            method_name = match.group(1)\n            method_start = match.end()\n\n            # Finde das Ende der Methode\n            brace_count = 1\n            pos = method_start\n            while pos < len(content) and brace_count > 0:\n                if content[pos] == '{':\n                    brace_count += 1\n                elif content[pos] == '}':\n                    brace_count -= 1\n                pos += 1\n\n            method_body = content[method_start:pos]\n\n            # Prüfe auf echo\/print\n            if re.search(r\"\\b(echo|print)\\b\", method_body):\n                warnings.append(\n                    f\"W15.5: Void method '{method_name}' produces output. \"\n                    f\"Consider returning a value instead of echoing.\"\n                )\n\n        return warnings\n\n\nclass W15_6_MagicMethodOverride(Rule):\n    \"\"\"W15.6: Magic Methods mit unerwarteter Semantik.\"\"\"\n\n    def check(self, file_path: str, content: str) -> List[str]:\n        warnings = []\n\n        # __toString sollte keine Exception werfen\n        tostring_match = re.search(r\"function\\s+__toString\\s*\\([^)]*\\)\\s*:\\s*string\\s*\\{\", content)\n        if tostring_match:\n            method_start = tostring_match.end()\n            brace_count = 1\n            pos = method_start\n            while pos < len(content) and brace_count > 0:\n                if content[pos] == '{':\n                    brace_count += 1\n                elif content[pos] == '}':\n                    brace_count -= 1\n                pos += 1\n\n            method_body = content[method_start:pos]\n\n            if re.search(r\"\\bthrow\\s+\", method_body):\n                warnings.append(\n                    \"W15.6: __toString() throws exception. This can cause hard-to-debug errors. \"\n                    \"Return error string instead.\"\n                )\n\n        return warnings\n\n\n# =============================================================================\n# RULE COLLECTION\n# =============================================================================\n\nRULES = [\n    W15_1_GetterWithSideEffect(),\n    W15_2_SetterWithReturn(),\n    W15_3_BoolMethodWithoutQuestion(),\n    W15_4_ConstructorWithReturn(),\n    W15_5_VoidMethodWithEcho(),\n    W15_6_MagicMethodOverride(),\n]\n"
    }
}

Response

{
    "tool_response": {
        "type": "create",
        "filePath": "\/var\/www\/tools\/ki-protokoll\/claude-hook\/quality\/rules_leastsurprise.py",
        "content": "#!\/usr\/bin\/env python3\n\"\"\"\nPost-Hook Least Surprise Regeln (WARN) - Least Surprise Principle.\n\nW15.x Regeln: Warnt bei Code der sich anders verhält als erwartet.\n\nPrinzip: \"Code verhält sich so, wie Name und Struktur erwarten lassen. Keine Überraschungen.\"\n\"\"\"\n\nimport re\nfrom typing import List\nfrom .rule_base import Rule\n\n\n# =============================================================================\n# W15: LEAST SURPRISE PRINCIPLE\n# =============================================================================\n\nclass W15_1_GetterWithSideEffect(Rule):\n    \"\"\"W15.1: Getter mit Seiteneffekten - unerwartet bei lesenden Methoden.\"\"\"\n\n    # Seiteneffekt-Indikatoren\n    SIDE_EFFECT_PATTERNS = [\n        r\"\\bsave\\s*\\(\",\n        r\"\\bupdate\\s*\\(\",\n        r\"\\bdelete\\s*\\(\",\n        r\"\\binsert\\s*\\(\",\n        r\"\\bwrite\\s*\\(\",\n        r\"\\bpersist\\s*\\(\",\n        r\"\\bflush\\s*\\(\",\n        r\"\\bsend\\s*\\(\",\n        r\"\\bexecute\\s*\\(\",\n        r\"\\$this->\\w+\\s*=\",  # Property-Änderung\n        r\"\\$this->\\w+\\s*\\[\\s*\\]\\s*=\",  # Array-Push\n        r\"\\$this->\\w+\\s*\\+\\+\",  # Increment\n        r\"\\$this->\\w+\\s*--\",  # Decrement\n    ]\n\n    def check(self, file_path: str, content: str) -> List[str]:\n        warnings = []\n\n        # Finde alle get*-Methoden\n        getter_pattern = r\"function\\s+(get[A-Z]\\w*)\\s*\\([^)]*\\)\\s*(?::\\s*[^{]+)?\\s*\\{\"\n        getter_matches = list(re.finditer(getter_pattern, content))\n\n        for match in getter_matches:\n            method_name = match.group(1)\n            method_start = match.end()\n\n            # Finde das Ende der Methode\n            brace_count = 1\n            pos = method_start\n            while pos < len(content) and brace_count > 0:\n                if content[pos] == '{':\n                    brace_count += 1\n                elif content[pos] == '}':\n                    brace_count -= 1\n                pos += 1\n\n            method_body = content[method_start:pos]\n\n            # Prüfe auf Seiteneffekte\n            for side_effect_pattern in self.SIDE_EFFECT_PATTERNS:\n                if re.search(side_effect_pattern, method_body):\n                    warnings.append(\n                        f\"W15.1: Getter '{method_name}' appears to have side effects. \"\n                        f\"Getters should be pure read operations.\"\n                    )\n                    break  # Nur eine Warnung pro Getter\n\n        return warnings\n\n\nclass W15_2_SetterWithReturn(Rule):\n    \"\"\"W15.2: Setter mit Return-Wert - unerwartet bei schreibenden Methoden.\"\"\"\n\n    def check(self, file_path: str, content: str) -> List[str]:\n        warnings = []\n\n        # Finde set*-Methoden mit Return-Type (außer self\/static für fluent)\n        pattern = r\"function\\s+(set[A-Z]\\w*)\\s*\\([^)]+\\)\\s*:\\s*(?!self|static|void|\\$this)[A-Z]\\w*\"\n\n        matches = re.findall(pattern, content)\n        for method_name in matches:\n            warnings.append(\n                f\"W15.2: Setter '{method_name}' has unexpected return type. \"\n                f\"Setters should return void or self (for fluent interface).\"\n            )\n\n        return warnings\n\n\nclass W15_3_BoolMethodWithoutQuestion(Rule):\n    \"\"\"W15.3: Boolean-Methode ohne is\/has\/can\/should Präfix.\"\"\"\n\n    def check(self, file_path: str, content: str) -> List[str]:\n        warnings = []\n\n        # Finde Methoden die bool zurückgeben\n        pattern = r\"function\\s+(\\w+)\\s*\\([^)]*\\)\\s*:\\s*bool\"\n\n        matches = re.findall(pattern, content)\n        for method_name in matches:\n            # Erlaubte Präfixe für bool-Methoden\n            if not re.match(r\"^(is|has|can|should|will|was|does|did|are)\", method_name):\n                warnings.append(\n                    f\"W15.3: Boolean method '{method_name}' should start with \"\n                    f\"is\/has\/can\/should for clarity.\"\n                )\n\n        return warnings\n\n\nclass W15_4_ConstructorWithReturn(Rule):\n    \"\"\"W15.4: Constructor mit explizitem return - unerwartet.\"\"\"\n\n    def check(self, file_path: str, content: str) -> List[str]:\n        warnings = []\n\n        # Finde __construct Methoden\n        construct_match = re.search(\n            r\"function\\s+__construct\\s*\\([^)]*\\)\\s*\\{\",\n            content\n        )\n\n        if construct_match:\n            construct_start = construct_match.end()\n\n            # Finde das Ende des Constructors\n            brace_count = 1\n            pos = construct_start\n            while pos < len(content) and brace_count > 0:\n                if content[pos] == '{':\n                    brace_count += 1\n                elif content[pos] == '}':\n                    brace_count -= 1\n                pos += 1\n\n            construct_body = content[construct_start:pos]\n\n            # Prüfe auf return mit Wert (nicht nur 'return;')\n            if re.search(r\"\\breturn\\s+[^;]+;\", construct_body):\n                warnings.append(\n                    \"W15.4: Constructor has return statement with value. \"\n                    \"Constructors should not return values.\"\n                )\n\n        return warnings\n\n\nclass W15_5_VoidMethodWithEcho(Rule):\n    \"\"\"W15.5: Void-Methode mit Echo - unerwartete Ausgabe.\"\"\"\n\n    def check(self, file_path: str, content: str) -> List[str]:\n        # Überspringe Views und Templates\n        if \"\/View\/\" in file_path or \"\/templates\/\" in file_path:\n            return []\n\n        warnings = []\n\n        # Finde void-Methoden\n        pattern = r\"function\\s+(\\w+)\\s*\\([^)]*\\)\\s*:\\s*void\\s*\\{\"\n\n        for match in re.finditer(pattern, content):\n            method_name = match.group(1)\n            method_start = match.end()\n\n            # Finde das Ende der Methode\n            brace_count = 1\n            pos = method_start\n            while pos < len(content) and brace_count > 0:\n                if content[pos] == '{':\n                    brace_count += 1\n                elif content[pos] == '}':\n                    brace_count -= 1\n                pos += 1\n\n            method_body = content[method_start:pos]\n\n            # Prüfe auf echo\/print\n            if re.search(r\"\\b(echo|print)\\b\", method_body):\n                warnings.append(\n                    f\"W15.5: Void method '{method_name}' produces output. \"\n                    f\"Consider returning a value instead of echoing.\"\n                )\n\n        return warnings\n\n\nclass W15_6_MagicMethodOverride(Rule):\n    \"\"\"W15.6: Magic Methods mit unerwarteter Semantik.\"\"\"\n\n    def check(self, file_path: str, content: str) -> List[str]:\n        warnings = []\n\n        # __toString sollte keine Exception werfen\n        tostring_match = re.search(r\"function\\s+__toString\\s*\\([^)]*\\)\\s*:\\s*string\\s*\\{\", content)\n        if tostring_match:\n            method_start = tostring_match.end()\n            brace_count = 1\n            pos = method_start\n            while pos < len(content) and brace_count > 0:\n                if content[pos] == '{':\n                    brace_count += 1\n                elif content[pos] == '}':\n                    brace_count -= 1\n                pos += 1\n\n            method_body = content[method_start:pos]\n\n            if re.search(r\"\\bthrow\\s+\", method_body):\n                warnings.append(\n                    \"W15.6: __toString() throws exception. This can cause hard-to-debug errors. \"\n                    \"Return error string instead.\"\n                )\n\n        return warnings\n\n\n# =============================================================================\n# RULE COLLECTION\n# =============================================================================\n\nRULES = [\n    W15_1_GetterWithSideEffect(),\n    W15_2_SetterWithReturn(),\n    W15_3_BoolMethodWithoutQuestion(),\n    W15_4_ConstructorWithReturn(),\n    W15_5_VoidMethodWithEcho(),\n    W15_6_MagicMethodOverride(),\n]\n",
        "structuredPatch": [],
        "originalFile": null
    }
}
← Vorheriger Zur Liste Nächster →