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.
This commit is contained in:
root
2026-07-29 01:06:15 +08:00
parent ac822662f5
commit 67a9126c64
2 changed files with 42 additions and 21 deletions

View File

@@ -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()

View File

@@ -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,