fix(skills): accept CRLF in inline skill definitions (#7504)
Some checks failed
CodeQL Advanced / Analyze (actions) (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Vulnerability Scan / Detect changes (push) Has been cancelled
Vulnerability Scan / pip-audit (push) Has been cancelled

Co-authored-by: wangtaotaotao95 <328929485+wangtaotaotao95@users.noreply.github.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
This commit is contained in:
wangtao
2026-09-16 22:05:35 +08:00
committed by GitHub
parent b4fd395d8d
commit c0af9badb8
4 changed files with 26 additions and 2 deletions

View File

@@ -187,7 +187,7 @@ def load_skill(
if activate:
return [resolve_registry_ref(skill, source=source)]
return [resolve_registry_ref(skill, source=source, activate=False)]
if isinstance(skill, str) and skill.lstrip().startswith("---\n"):
if isinstance(skill, str) and skill.lstrip().startswith(("---\n", "---\r\n")):
frontmatter_dict, body = parse_frontmatter(skill.strip())
return [
Skill(

View File

@@ -28,7 +28,7 @@ _logger = logging.getLogger(__name__)
SKILL_FILENAME: Final[str] = "SKILL.md"
_CLOSING_DELIMITER: Final[re.Pattern[str]] = re.compile(r"\n---[ \t]*(?:\n|$)")
_CLOSING_DELIMITER: Final[re.Pattern[str]] = re.compile(r"\r?\n---[ \t]*(?:\r?\n|$)")
_MAX_BODY_CHARS: Final[int] = 50_000

View File

@@ -134,6 +134,22 @@ class TestLoadSkill:
"Follow these instructions."
]
def test_loads_inline_skill_with_crlf(self) -> None:
inline_skill = (
"---\r\n"
"name: inline-skill\r\n"
"description: Inline guidance\r\n"
"---\r\n"
"Follow these instructions."
)
skills = load_skill(inline_skill)
assert [skill.name for skill in skills] == ["inline-skill"]
assert [skill.instructions for skill in skills] == [
"Follow these instructions."
]
def test_invalid_inline_skill_raises_parse_error(self) -> None:
with pytest.raises(SkillParseError, match="missing closing"):
load_skill("---\nname: inline-skill\n")

View File

@@ -25,6 +25,14 @@ class TestParseFrontmatter:
assert fm["description"] == "A test"
assert body == "Body text here."
def test_crlf_frontmatter_and_body(self) -> None:
content = "---\r\nname: test\r\ndescription: A test\r\n---\r\nBody text here."
fm, body = parse_frontmatter(content)
assert fm == {"name": "test", "description": "A test"}
assert body == "Body text here."
def test_empty_body(self) -> None:
content = "---\nname: test\ndescription: A test\n---"
fm, body = parse_frontmatter(content)