"""Shared SKILL.md frontmatter parsing helpers. The runtime parser, install-time validator, and review core all use this module as the schema source for DeerFlow SKILL.md metadata. """ from __future__ import annotations import re from dataclasses import dataclass from typing import Any import yaml ALLOWED_FRONTMATTER_PROPERTIES = { "name", "description", "license", "allowed-tools", "required-secrets", "secrets-autonomous", "metadata", "compatibility", "version", "author", } _FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---\s*\n?", re.DOTALL) @dataclass(frozen=True) class SkillMarkdownParts: """Parsed pieces of a SKILL.md document.""" metadata: dict[str, Any] frontmatter_text: str body: str def split_skill_markdown(content: str) -> tuple[SkillMarkdownParts | None, str | None]: """Split a SKILL.md document into frontmatter and body. Returns ``(parts, None)`` on success and ``(None, message)`` on failure. The message intentionally avoids host paths so callers can reuse it in deterministic review output. """ match = _FRONTMATTER_RE.match(content) if not match: return None, "No YAML frontmatter found" frontmatter_text = match.group(1) try: metadata = yaml.safe_load(frontmatter_text) except yaml.YAMLError as exc: return None, f"Invalid YAML in frontmatter: {exc}" if not isinstance(metadata, dict): return None, "Frontmatter must be a YAML dictionary" return ( SkillMarkdownParts( metadata=metadata, frontmatter_text=frontmatter_text, body=content[match.end() :], ), None, )