From 67a9126c6435a8654ba7a6f68c0fd2130f00a462 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 29 Jul 2026 01:06:15 +0800 Subject: [PATCH] fix(s08,s20): sync frontmatter parser with s07 fix from PR #434 Replace old manual/split-based _parse_frontmatter with the line-delimited YAML parser from s07. This fixes skill loading for YAML block scalars, multiline descriptions, and CRLF line endings in both s08 and s20. Also update name/desc fallback from meta.get(k, default) to meta.get(k) or default so empty string values correctly fall through. s08 previously used a manual split(':', 1) parser that could not handle YAML syntax at all. s20 used yaml.safe_load but with the old text.split('---', 2) approach that misses boundary cases. --- s08_context_compact/code.py | 34 ++++++++++++++++++++++------------ s20_comprehensive/code.py | 29 ++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/s08_context_compact/code.py b/s08_context_compact/code.py index 7186df55..a8cb7dd2 100644 --- a/s08_context_compact/code.py +++ b/s08_context_compact/code.py @@ -32,7 +32,7 @@ Builds on s07 (skill loading). Usage: Needs: pip install anthropic python-dotenv + ANTHROPIC_API_KEY in .env """ -import ast, json, os, subprocess, time +import ast, json, os, subprocess, time, yaml from pathlib import Path try: @@ -57,17 +57,27 @@ CURRENT_TODOS: list[dict] = [] # s07: Skill catalog scan (inherited from s07) def _parse_frontmatter(text: str) -> tuple[dict, str]: - if not text.startswith("---"): + """Parse YAML frontmatter from SKILL.md. Returns (meta, body).""" + if not (text.startswith("---\n") or text.startswith("---\r\n")): return {}, text - parts = text.split("---", 2) - if len(parts) < 3: + + lines = text.splitlines(keepends=True) + closing_index = None + for index, line in enumerate(lines[1:], start=1): + if line.strip() == "---": + closing_index = index + break + if closing_index is None: return {}, text - meta = {} - for line in parts[1].strip().splitlines(): - if ":" in line: - k, v = line.split(":", 1) - meta[k.strip()] = v.strip().strip('"').strip("'") - return meta, parts[2].strip() + + frontmatter = "".join(lines[1:closing_index]) + body = "".join(lines[closing_index + 1 :]).strip() + try: + loaded = yaml.safe_load(frontmatter) or {} + except yaml.YAMLError: + loaded = {} + meta = loaded if isinstance(loaded, dict) else {} + return meta, body SKILL_REGISTRY: dict[str, dict] = {} @@ -81,8 +91,8 @@ def _scan_skills(): if manifest.exists(): raw = manifest.read_text() meta, body = _parse_frontmatter(raw) - name = meta.get("name", d.name) - desc = meta.get("description", raw.split("\n")[0].lstrip("#").strip()) + name = meta.get("name") or d.name + desc = meta.get("description") or body.split("\n", 1)[0].lstrip("#").strip() SKILL_REGISTRY[name] = {"name": name, "description": desc, "content": raw} _scan_skills() diff --git a/s20_comprehensive/code.py b/s20_comprehensive/code.py index 417d6065..082e226b 100644 --- a/s20_comprehensive/code.py +++ b/s20_comprehensive/code.py @@ -288,16 +288,27 @@ SKILL_REGISTRY: dict[str, dict] = {} def _parse_frontmatter(text: str) -> tuple[dict, str]: - if not text.startswith("---"): + """Parse YAML frontmatter from SKILL.md. Returns (meta, body).""" + if not (text.startswith("---\n") or text.startswith("---\r\n")): return {}, text - parts = text.split("---", 2) - if len(parts) < 3: + + lines = text.splitlines(keepends=True) + closing_index = None + for index, line in enumerate(lines[1:], start=1): + if line.strip() == "---": + closing_index = index + break + if closing_index is None: return {}, text + + frontmatter = "".join(lines[1:closing_index]) + body = "".join(lines[closing_index + 1 :]).strip() try: - meta = yaml.safe_load(parts[1]) or {} + loaded = yaml.safe_load(frontmatter) or {} except yaml.YAMLError: - meta = {} - return meta, parts[2].strip() + loaded = {} + meta = loaded if isinstance(loaded, dict) else {} + return meta, body def scan_skills(): @@ -311,9 +322,9 @@ def scan_skills(): if not manifest.exists(): continue raw = manifest.read_text() - meta, _ = _parse_frontmatter(raw) - name = meta.get("name", directory.name) - desc = meta.get("description", raw.split("\n")[0].lstrip("#").strip()) + meta, body = _parse_frontmatter(raw) + name = meta.get("name") or directory.name + desc = meta.get("description") or body.split("\n", 1)[0].lstrip("#").strip() SKILL_REGISTRY[name] = { "name": name, "description": desc,