From 1dc9d6d1f76c160fc1bbd1206e43f071f746e2b5 Mon Sep 17 00:00:00 2001 From: RhoninSeiei Date: Mon, 16 Mar 2026 16:37:08 +0000 Subject: [PATCH 1/3] fix(skills): support multiline frontmatter descriptions --- astrbot/core/computer/computer_client.py | 22 +++++++++++----- astrbot/core/skills/skill_manager.py | 22 +++++++++++----- tests/test_skill_metadata_enrichment.py | 33 ++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/astrbot/core/computer/computer_client.py b/astrbot/core/computer/computer_client.py index 6e80ac3ab7..a4a3082ba5 100644 --- a/astrbot/core/computer/computer_client.py +++ b/astrbot/core/computer/computer_client.py @@ -213,13 +213,21 @@ def parse_description(text: str) -> str: break if end_idx is None: return "" - for line in lines[1:end_idx]: - if ":" not in line: - continue - key, value = line.split(":", 1) - if key.strip().lower() == "description": - return value.strip().strip('"').strip("'") - return "" + + frontmatter = "\n".join(lines[1:end_idx]) + try: + import yaml + + payload = yaml.safe_load(frontmatter) or {} + except Exception: + return "" + if not isinstance(payload, dict): + return "" + + description = payload.get("description", "") + if not isinstance(description, str): + return "" + return description.strip() def load_managed_skills() -> list[str]: diff --git a/astrbot/core/skills/skill_manager.py b/astrbot/core/skills/skill_manager.py index 9bbdb5aee8..d5d46f177e 100644 --- a/astrbot/core/skills/skill_manager.py +++ b/astrbot/core/skills/skill_manager.py @@ -7,6 +7,8 @@ import shutil import tempfile import zipfile + +import yaml from dataclasses import dataclass from datetime import datetime, timezone from pathlib import Path, PurePosixPath @@ -69,13 +71,19 @@ def _parse_frontmatter_description(text: str) -> str: break if end_idx is None: return "" - for line in lines[1:end_idx]: - if ":" not in line: - continue - key, value = line.split(":", 1) - if key.strip().lower() == "description": - return value.strip().strip('"').strip("'") - return "" + + frontmatter = "\n".join(lines[1:end_idx]) + try: + payload = yaml.safe_load(frontmatter) or {} + except yaml.YAMLError: + return "" + if not isinstance(payload, dict): + return "" + + description = payload.get("description", "") + if not isinstance(description, str): + return "" + return description.strip() # Regex for sanitizing paths used in prompt examples — only allow diff --git a/tests/test_skill_metadata_enrichment.py b/tests/test_skill_metadata_enrichment.py index ba2a215879..1d511af026 100644 --- a/tests/test_skill_metadata_enrichment.py +++ b/tests/test_skill_metadata_enrichment.py @@ -49,6 +49,39 @@ def test_parse_frontmatter_quoted_description(): assert _parse_frontmatter_description(text) == "quoted value" +def test_parse_frontmatter_multiline_literal_description(): + text = ( + "---\n" + "name: humanizer-zh\n" + "description: |\n" + " 去除文本中的 AI 生成痕迹。\n" + " 适用于编辑或审阅文本,使其听起来更自然。\n" + "---\n" + ) + assert _parse_frontmatter_description(text) == ( + "去除文本中的 AI 生成痕迹。\n适用于编辑或审阅文本,使其听起来更自然。" + ) + + +def test_parse_frontmatter_multiline_folded_description(): + text = ( + "---\n" + "name: humanizer-zh\n" + "description: >\n" + " 去除文本中的 AI 生成痕迹。\n" + " 适用于编辑或审阅文本,使其听起来更自然。\n" + "---\n" + ) + assert _parse_frontmatter_description(text) == ( + "去除文本中的 AI 生成痕迹。 适用于编辑或审阅文本,使其听起来更自然。" + ) + + +def test_parse_frontmatter_invalid_yaml_returns_empty(): + text = "---\ndescription: [broken\n---\n" + assert _parse_frontmatter_description(text) == "" + + # ---------- build_skills_prompt tests ---------- From 6e12ed57d881128e9fbbd40afbc3ba712da61764 Mon Sep 17 00:00:00 2001 From: RhoninSeiei Date: Mon, 16 Mar 2026 16:55:15 +0000 Subject: [PATCH 2/3] =?UTF-8?q?fix(skills):=20=E4=BF=AE=E5=A4=8D=E5=A4=9A?= =?UTF-8?q?=E8=A1=8C=20frontmatter=20=E6=8F=8F=E8=BF=B0=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/computer/computer_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/astrbot/core/computer/computer_client.py b/astrbot/core/computer/computer_client.py index a4a3082ba5..b63da1be9d 100644 --- a/astrbot/core/computer/computer_client.py +++ b/astrbot/core/computer/computer_client.py @@ -218,7 +218,7 @@ def parse_description(text: str) -> str: try: import yaml - payload = yaml.safe_load(frontmatter) or {} + payload = yaml.safe_load(frontmatter) or dict() except Exception: return "" if not isinstance(payload, dict): From b57733a086a34809cc4725095f82dc386e4e8009 Mon Sep 17 00:00:00 2001 From: RhoninSeiei Date: Mon, 16 Mar 2026 17:06:55 +0000 Subject: [PATCH 3/3] style(skills): clean up frontmatter parser follow-ups --- astrbot/core/computer/computer_client.py | 5 ++++- astrbot/core/skills/skill_manager.py | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/astrbot/core/computer/computer_client.py b/astrbot/core/computer/computer_client.py index b63da1be9d..7e142fa38e 100644 --- a/astrbot/core/computer/computer_client.py +++ b/astrbot/core/computer/computer_client.py @@ -217,9 +217,12 @@ def parse_description(text: str) -> str: frontmatter = "\n".join(lines[1:end_idx]) try: import yaml + except ImportError: + return "" + try: payload = yaml.safe_load(frontmatter) or dict() - except Exception: + except yaml.YAMLError: return "" if not isinstance(payload, dict): return "" diff --git a/astrbot/core/skills/skill_manager.py b/astrbot/core/skills/skill_manager.py index d5d46f177e..191fa4aae9 100644 --- a/astrbot/core/skills/skill_manager.py +++ b/astrbot/core/skills/skill_manager.py @@ -7,12 +7,12 @@ import shutil import tempfile import zipfile - -import yaml from dataclasses import dataclass from datetime import datetime, timezone from pathlib import Path, PurePosixPath +import yaml + from astrbot.core.utils.astrbot_path import ( get_astrbot_data_path, get_astrbot_skills_path,