pre_rules_guard.py
- Pfad:
/var/www/tools/ki-protokoll/claude-hook/quality/pre_rules_guard.py - Namespace: claude-hook.quality
- Zeilen: 87 | Größe: 2,822 Bytes
- Geändert: 2025-12-25 16:58:58 | Gescannt: 2025-12-31 10:22:15
Code Hygiene Score: 98
- Dependencies: 90 (25%)
- LOC: 100 (20%)
- Methods: 100 (20%)
- Secrets: 100 (15%)
- Classes: 100 (10%)
- Magic Numbers: 100 (10%)
Keine Issues gefunden.
Dependencies 6
- use re
- use pathlib.Path
- use typing.Optional
- use rule_base.GLOBAL_ALLOWLIST
- use rule_base.is_in_allowlist
- use rule_base.block
Funktionen 3
-
p1_1_responsibility_header()Zeile 19 -
p1_2_garbage_names()Zeile 40 -
p4_1_no_public_properties_in_domain()Zeile 59
Code
#!/usr/bin/env python3
"""
Pre-Hook Guard Regeln (BLOCK) - SRP + OOP Basics.
P1.x Regeln: Single Responsibility, KISS, Naming
P4.x Regeln: OOP Basics, Encapsulation
"""
import re
from pathlib import Path
from typing import Optional
from .rule_base import GLOBAL_ALLOWLIST, is_in_allowlist, block
# =============================================================================
# PRÜFUNG 1: SRP + KISS
# =============================================================================
def p1_1_responsibility_header(file_path: str, content: str) -> Optional[dict]:
"""P1.1: @responsibility Header erforderlich."""
if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):
return None
header_pattern = r"//\s*@responsibility:\s*(.+)"
match = re.search(header_pattern, content)
if not match:
return block("P1.1", "Missing @responsibility header. Add: // @responsibility: <single-responsibility>")
responsibility_text = match.group(1).strip()
# Prüfe auf Multi-Responsibility-Wörter
multi_words = r"\b(und|sowie|außerdem|also|zusätzlich|and|also|additionally)\b"
if re.search(multi_words, responsibility_text, re.IGNORECASE):
return block("P1.1", f"@responsibility contains multi-responsibility indicator: '{responsibility_text}'")
return None
def p1_2_garbage_names(file_path: str, content: str) -> Optional[dict]:
"""P1.2: Müllhalden-Namen blockieren."""
if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):
return None
filename = Path(file_path).stem.lower()
forbidden = ["helper", "utils", "common", "misc", "base"]
for term in forbidden:
if term in filename:
return block("P1.2", f"Forbidden name pattern: '{term}' indicates unclear responsibility")
return None
# =============================================================================
# PRÜFUNG 4: OOP
# =============================================================================
def p4_1_no_public_properties_in_domain(file_path: str, content: str) -> Optional[dict]:
"""P4.1: Keine public Properties in Domain."""
if "/Domain/" not in file_path:
return None
if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):
return None
# public readonly ist erlaubt
public_props = re.findall(
r"public\s+(?!readonly|function|const|static\s+function).*\$\w+",
content
)
if public_props:
return block("P4.1", "Public property in Domain. Use getter methods or public readonly.")
return None
# =============================================================================
# RULE COLLECTION
# =============================================================================
RULES = [
p1_1_responsibility_header,
p1_2_garbage_names,
p4_1_no_public_properties_in_domain,
]