Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 36 additions & 4 deletions agents/s05_skill_loading.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,44 @@ def _parse_frontmatter(self, text: str) -> tuple:
if not match:
return {}, text
meta = {}
for line in match.group(1).strip().splitlines():
if ":" in line:
key, val = line.split(":", 1)
meta[key.strip()] = val.strip()
lines = match.group(1).strip().splitlines()
index = 0
while index < len(lines):
line = lines[index]
if ":" not in line:
index += 1
continue

key, val = line.split(":", 1)
key = key.strip()
val = val.strip()
index += 1

if val in {"|", ">"}:
block_lines = []
while index < len(lines):
next_line = lines[index]
if next_line.startswith((" ", "\t")) or not next_line.strip():
block_lines.append(next_line)
index += 1
continue
break
meta[key] = self._parse_block_scalar(block_lines, val)
continue

meta[key] = val
return meta, match.group(2).strip()

def _parse_block_scalar(self, lines: list[str], style: str) -> str:
"""Parse a minimal YAML block scalar for skill metadata."""
non_empty = [len(line) - len(line.lstrip()) for line in lines if line.strip()]
indent = min(non_empty) if non_empty else 0
normalized = [line[indent:] if line.strip() else "" for line in lines]

if style == ">":
return " ".join(line.strip() for line in normalized if line.strip())
return "\n".join(normalized).strip()

def get_descriptions(self) -> str:
"""Layer 1: short descriptions for the system prompt."""
if not self.skills:
Expand Down
90 changes: 90 additions & 0 deletions tests/test_s05_skill_loading.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import importlib.util
import os
import sys
import tempfile
import textwrap
import types
import unittest
from pathlib import Path


REPO_ROOT = Path(__file__).resolve().parents[1]
MODULE_PATH = REPO_ROOT / "agents" / "s05_skill_loading.py"


def load_s05_module(temp_cwd: Path):
fake_anthropic = types.ModuleType("anthropic")

class FakeAnthropic:
def __init__(self, *args, **kwargs):
self.messages = types.SimpleNamespace(create=None)

fake_dotenv = types.ModuleType("dotenv")
setattr(fake_anthropic, "Anthropic", FakeAnthropic)
setattr(fake_dotenv, "load_dotenv", lambda override=True: None)

previous_anthropic = sys.modules.get("anthropic")
previous_dotenv = sys.modules.get("dotenv")
previous_cwd = Path.cwd()
spec = importlib.util.spec_from_file_location("s05_under_test", MODULE_PATH)
if spec is None or spec.loader is None:
raise RuntimeError(f"Unable to load {MODULE_PATH}")
module = importlib.util.module_from_spec(spec)

sys.modules["anthropic"] = fake_anthropic
sys.modules["dotenv"] = fake_dotenv
try:
os.chdir(temp_cwd)
os.environ.setdefault("MODEL_ID", "test-model")
spec.loader.exec_module(module)
return module
finally:
os.chdir(previous_cwd)
if previous_anthropic is None:
sys.modules.pop("anthropic", None)
else:
sys.modules["anthropic"] = previous_anthropic
if previous_dotenv is None:
sys.modules.pop("dotenv", None)
else:
sys.modules["dotenv"] = previous_dotenv


class SkillLoaderTests(unittest.TestCase):
def test_get_descriptions_keeps_folded_frontmatter_description(self):
with tempfile.TemporaryDirectory() as tmp:
temp_cwd = Path(tmp)
module = load_s05_module(temp_cwd)
skills_dir = temp_cwd / "skills" / "demo-skill"
skills_dir.mkdir(parents=True)
(skills_dir / "SKILL.md").write_text(
textwrap.dedent(
"""\
---
name: demo
description: >
Loads the skill metadata correctly
even when the description spans lines.
tags: parsing, regression
---
# Demo Skill

Body content.
"""
)
)

loader = module.SkillLoader(temp_cwd / "skills")

self.assertEqual(
loader.skills["demo"]["meta"]["description"],
"Loads the skill metadata correctly even when the description spans lines.",
)
self.assertEqual(
loader.get_descriptions(),
" - demo: Loads the skill metadata correctly even when the description spans lines. [parsing, regression]",
)


if __name__ == "__main__":
unittest.main()