{
"event": "PreToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "\/var\/www\/mcp-servers\/mcp_contracts\/validators\/scope_resolver.py",
"content": "\"\"\"Scope Resolver - Pfad-Aufloesung und Glob-Pattern-Matching.\"\"\"\n\nimport os\nimport glob as globlib\nfrom pathlib import Path\nfrom typing import Optional\n\n\nclass ScopeResolver:\n \"\"\"Loest Pfade und Glob-Patterns auf.\"\"\"\n\n def __init__(self, base_path: str = \"\/var\/www\/dev.campus.systemische-tools.de\"):\n self.base_path = base_path\n\n def resolve_paths(\n self,\n scope: dict,\n target_path: Optional[str] = None,\n legacy_applicability: Optional[dict] = None,\n ) -> list[str]:\n \"\"\"\n Ermittelt zu pruefende Pfade aus Scope.\n\n Args:\n scope: Contract scope dict mit paths\/includes\/excludes\n target_path: Optional spezifischer Pfad\n legacy_applicability: Legacy applicability dict\n\n Returns:\n Liste der aufgeloesten Pfade (dedupliziert)\n \"\"\"\n if target_path:\n return [target_path]\n\n paths_list = self._get_paths_list(scope, legacy_applicability)\n excludes = scope.get(\"excludes\", [])\n\n check_paths = []\n for pattern in paths_list:\n matched = self._expand_glob(pattern)\n for path in matched:\n if not self._is_excluded(path, excludes):\n check_paths.append(path)\n\n return list(set(check_paths))\n\n def _get_paths_list(\n self, scope: dict, legacy_applicability: Optional[dict] = None\n ) -> list[str]:\n \"\"\"Extrahiert Pfade aus verschiedenen Contract-Formaten.\"\"\"\n # Neues Standard-Format\n paths_list = scope.get(\"paths\", [])\n\n # Legacy-Formate als Fallback\n if not paths_list:\n paths_list = scope.get(\"includes\", [])\n if not paths_list:\n paths_list = scope.get(\"applies_to_paths\", [])\n if not paths_list and legacy_applicability:\n paths_list = legacy_applicability.get(\"scope\", [])\n if isinstance(paths_list, str):\n paths_list = [paths_list]\n\n return paths_list\n\n def _expand_glob(self, pattern: str) -> list[str]:\n \"\"\"Expandiert Glob-Pattern zu konkreten Pfaden.\"\"\"\n full_pattern = os.path.join(self.base_path, pattern.lstrip(\"\/\"))\n matched = globlib.glob(full_pattern, recursive=True)\n\n if matched:\n return matched\n\n # Fallback: Basisverzeichnis wenn kein Match\n pattern_path = pattern.replace(\"**\", \"\").replace(\"*\", \"\").rstrip(\"\/\")\n full_path = os.path.join(self.base_path, pattern_path.lstrip(\"\/\"))\n if os.path.exists(full_path):\n return [full_path]\n\n return []\n\n def _is_excluded(self, path: str, excludes: list[str]) -> bool:\n \"\"\"Prueft ob Pfad durch Exclude-Pattern ausgeschlossen wird.\"\"\"\n for excl in excludes:\n excl_pattern = os.path.join(self.base_path, excl.lstrip(\"\/\"))\n if globlib.fnmatch.fnmatch(path, excl_pattern):\n return True\n return False\n\n def file_matches_pattern(self, file_path: str, pattern: str) -> bool:\n \"\"\"Prueft ob Datei zum Glob-Pattern passt.\"\"\"\n rel_path = file_path.replace(self.base_path, \"\").lstrip(\"\/\")\n\n if \"**\" in pattern:\n parts = pattern.split(\"**\")\n if len(parts) == 2:\n prefix = parts[0].rstrip(\"\/\")\n suffix = parts[1].lstrip(\"\/\")\n if prefix and not rel_path.startswith(prefix.lstrip(\"\/\")):\n return False\n if suffix and not globlib.fnmatch.fnmatch(rel_path, f\"*{suffix}\"):\n return False\n return True\n elif \"*\" in pattern:\n return globlib.fnmatch.fnmatch(rel_path, pattern)\n else:\n return pattern in rel_path\n\n return True\n"
}
}
{
"tool_response": {
"type": "create",
"filePath": "\/var\/www\/mcp-servers\/mcp_contracts\/validators\/scope_resolver.py",
"content": "\"\"\"Scope Resolver - Pfad-Aufloesung und Glob-Pattern-Matching.\"\"\"\n\nimport os\nimport glob as globlib\nfrom pathlib import Path\nfrom typing import Optional\n\n\nclass ScopeResolver:\n \"\"\"Loest Pfade und Glob-Patterns auf.\"\"\"\n\n def __init__(self, base_path: str = \"\/var\/www\/dev.campus.systemische-tools.de\"):\n self.base_path = base_path\n\n def resolve_paths(\n self,\n scope: dict,\n target_path: Optional[str] = None,\n legacy_applicability: Optional[dict] = None,\n ) -> list[str]:\n \"\"\"\n Ermittelt zu pruefende Pfade aus Scope.\n\n Args:\n scope: Contract scope dict mit paths\/includes\/excludes\n target_path: Optional spezifischer Pfad\n legacy_applicability: Legacy applicability dict\n\n Returns:\n Liste der aufgeloesten Pfade (dedupliziert)\n \"\"\"\n if target_path:\n return [target_path]\n\n paths_list = self._get_paths_list(scope, legacy_applicability)\n excludes = scope.get(\"excludes\", [])\n\n check_paths = []\n for pattern in paths_list:\n matched = self._expand_glob(pattern)\n for path in matched:\n if not self._is_excluded(path, excludes):\n check_paths.append(path)\n\n return list(set(check_paths))\n\n def _get_paths_list(\n self, scope: dict, legacy_applicability: Optional[dict] = None\n ) -> list[str]:\n \"\"\"Extrahiert Pfade aus verschiedenen Contract-Formaten.\"\"\"\n # Neues Standard-Format\n paths_list = scope.get(\"paths\", [])\n\n # Legacy-Formate als Fallback\n if not paths_list:\n paths_list = scope.get(\"includes\", [])\n if not paths_list:\n paths_list = scope.get(\"applies_to_paths\", [])\n if not paths_list and legacy_applicability:\n paths_list = legacy_applicability.get(\"scope\", [])\n if isinstance(paths_list, str):\n paths_list = [paths_list]\n\n return paths_list\n\n def _expand_glob(self, pattern: str) -> list[str]:\n \"\"\"Expandiert Glob-Pattern zu konkreten Pfaden.\"\"\"\n full_pattern = os.path.join(self.base_path, pattern.lstrip(\"\/\"))\n matched = globlib.glob(full_pattern, recursive=True)\n\n if matched:\n return matched\n\n # Fallback: Basisverzeichnis wenn kein Match\n pattern_path = pattern.replace(\"**\", \"\").replace(\"*\", \"\").rstrip(\"\/\")\n full_path = os.path.join(self.base_path, pattern_path.lstrip(\"\/\"))\n if os.path.exists(full_path):\n return [full_path]\n\n return []\n\n def _is_excluded(self, path: str, excludes: list[str]) -> bool:\n \"\"\"Prueft ob Pfad durch Exclude-Pattern ausgeschlossen wird.\"\"\"\n for excl in excludes:\n excl_pattern = os.path.join(self.base_path, excl.lstrip(\"\/\"))\n if globlib.fnmatch.fnmatch(path, excl_pattern):\n return True\n return False\n\n def file_matches_pattern(self, file_path: str, pattern: str) -> bool:\n \"\"\"Prueft ob Datei zum Glob-Pattern passt.\"\"\"\n rel_path = file_path.replace(self.base_path, \"\").lstrip(\"\/\")\n\n if \"**\" in pattern:\n parts = pattern.split(\"**\")\n if len(parts) == 2:\n prefix = parts[0].rstrip(\"\/\")\n suffix = parts[1].lstrip(\"\/\")\n if prefix and not rel_path.startswith(prefix.lstrip(\"\/\")):\n return False\n if suffix and not globlib.fnmatch.fnmatch(rel_path, f\"*{suffix}\"):\n return False\n return True\n elif \"*\" in pattern:\n return globlib.fnmatch.fnmatch(rel_path, pattern)\n else:\n return pattern in rel_path\n\n return True\n",
"structuredPatch": [],
"originalFile": null
}
}