"""
skills/registry.py - 技能注册表
================================
管理所有已注册的内置技能，提供查找、调用、列表功能。

[v1.23.0] 单工具架构 — 四层分类:
  - builtin_tools:   LLM 直接调用的平台工具（command / web_control / playaudio / playvideo / recall_memory）
  - cli_commands:    CLI 子命令（通过 command 工具间接调用，共 42 个）
  - python_skills:   Python 可执行技能（SkillRegistry 内部使用，CLI 底层实现）
  - skill_guides:    Markdown 技能指南（由 RAG get_knowledge 按需检索）
"""
from __future__ import annotations

import importlib
import inspect
from pathlib import Path
from typing import Any, Dict, List, Optional, Set

from core.logger import get_logger
from aiskills.base import Skill, SkillResult, SkillParameter

logger = get_logger("myagent.skills")


# ==============================================================================
# 内置平台工具 — LLM 直接调用的工具
# ==============================================================================
# [v1.23.0] 单工具架构: LLM 核心工具为 command 和 web_control。
# [v1.23.26] 恢复 file_send / playaudio / playvideo 直接工具调用。
#   原因: CLI 迁移后 LLM 不稳定地使用 command 包裹，导致工具经常失败。
#   这些工具需要同时存在于 BUILTIN_TOOLS（暴露给 LLM）和 dispatch()（执行处理）。

BUILTIN_TOOLS: List[Dict[str, Any]] = [
    {
        "name": "command",
        "description": "执行 Shell 命令 — 所有操作均通过此工具完成。Shell 原生命令（ls/cat/grep/ps/df/uname/python3/pip/npm/git 等）直接执行，不要加 myagent-ai 前缀。只有 myagent 专有命令（docx-create/search/ocr 等）才需要 myagent-ai 前缀。",
        "category": "platform",
        "handler": "ToolDispatcher._exec_command",
        "parameters": [
            {"name": "command", "type": "string", "description": "Shell 命令（如 ls -la, grep -r pattern dir, myagent-ai search xxx, python3 script.py 等）", "required": True},
        ],
    },
    {
        "name": "web_control",
        "description": "网页控制器 — 在聊天中打开可控制的浏览器面板，支持导航、点击、填写、截图、Cookie 管理等",
        "category": "platform",
        "handler": "ToolDispatcher._exec_web_control",
        "parameters": [
            {"name": "action", "type": "string", "description": "操作类型", "required": True, "enum": [
                "open", "navigate", "close", "click", "fill", "scroll",
                "evaluate", "get_content", "wait", "screenshot",
                "set_cookies", "get_cookies",
            ]},
            {"name": "url", "type": "string", "description": "目标 URL", "required": False},
            {"name": "session_id", "type": "string", "description": "会话 ID", "required": False},
            {"name": "selector", "type": "string", "description": "CSS 选择器", "required": False},
            {"name": "value", "type": "string", "description": "输入值", "required": False},
        ],
    },
    # [v1.23.26] 恢复直接工具调用 — LLM 可直接调用 file_send/playaudio/playvideo
    {
        "name": "file_send",
        "description": "向用户发送文件 — 将已存在的文件（图片、PDF、文档等）发送给用户，在聊天中显示文件卡片供下载/预览。",
        "category": "media",
        "handler": "ToolDispatcher._exec_file_send",
        "parameters": [
            {"name": "file_path", "type": "string", "description": "要发送的文件路径（绝对路径或相对路径）", "required": True},
            {"name": "description", "type": "string", "description": "文件描述（可选）", "required": False},
        ],
    },
    {
        "name": "playaudio",
        "description": "在聊天中内嵌播放音频 — 支持 YouTube Music、网易云音乐、QQ音乐、B站等在线平台，也可播放本地音频文件。",
        "category": "media",
        "handler": "ToolDispatcher._exec_media",
        "parameters": [
            {"name": "url", "type": "string", "description": "在线音频链接（YouTube Music / 网易云音乐 / QQ音乐 / B站等）", "required": False},
            {"name": "file_path", "type": "string", "description": "本地音频文件路径", "required": False},
            {"name": "title", "type": "string", "description": "音频标题（可选，用于嵌入播放器标题）", "required": False},
        ],
    },
    {
        "name": "playvideo",
        "description": "在聊天中内嵌播放视频 — 支持 YouTube、B站、抖音等在线平台，也可播放本地视频文件。",
        "category": "media",
        "handler": "ToolDispatcher._exec_media",
        "parameters": [
            {"name": "url", "type": "string", "description": "在线视频链接（YouTube / B站 / 抖音等）", "required": False},
            {"name": "file_path", "type": "string", "description": "本地视频文件路径", "required": False},
            {"name": "title", "type": "string", "description": "视频标题（可选，用于嵌入播放器标题）", "required": False},
        ],
    },
]

# ==============================================================================
# 内部平台服务 — 由 Agent 内核直接处理，不暴露给 LLM
# ==============================================================================
# 这些服务通过 CLI 子命令间接调用（memory 通过 myagent-ai memory，
# playaudio/playvideo 通过 myagent-ai playaudio/playvideo CLI 标记触发），
# 或由系统自动处理（recall_memory 通过 <recall> 标签）。

INTERNAL_SERVICES: List[Dict[str, Any]] = [
    {
        "name": "recall_memory",
        "description": "主动召回记忆 — 由 <recall> 标签自动触发，也可通过 myagent-ai memory CLI 调用",
        "category": "platform",
        "handler": "ToolDispatcher._exec_recall_memory",
        "note": "已迁移: 通过 <recall> 标签或 myagent-ai memory CLI 调用",
    },
    # [v1.23.26] playaudio/playvideo 已恢复到 BUILTIN_TOOLS，但 CLI 子命令仍可用
    # 作为 command 工具的备用调用方式。
]


# ==============================================================================
# CLI 子命令元数据 — 通过 command 工具间接调用
# ==============================================================================
# [v1.23.0] 这些命令由 scripts/cli.py 实现，LLM 通过 command 工具调用:
#   <toolname>command</toolname><parms><command>myagent-ai <cmd> [args...]</command></parms>
# [v1.27.0] parms 从 JSON 改为 XML 子标签格式

CLI_COMMANDS: List[Dict[str, Any]] = [
    # ── 感知 ──
    {"name": "ocr", "category": "perception", "cli": "myagent-ai ocr <image> [ch|en]",
     "description": "OCR 文字识别 — 从图片中提取文字"},
    {"name": "analyze-image", "category": "perception", "cli": "myagent-ai analyze-image <image> [prompt]",
     "description": "图片内容分析 — 使用 VLM 分析图片"},
    {"name": "transcribe", "category": "perception", "cli": "myagent-ai transcribe <audio> [zh|en|ja]",
     "description": "语音转文字 — 将音频转录为文本"},
    # ── 搜索 ──
    {"name": "search", "category": "search", "cli": "myagent-ai search <query> [-n num]",
     "description": "网络搜索 — 搜索互联网信息"},
    {"name": "read-url", "category": "search", "cli": "myagent-ai read-url <url> [--raw]",
     "description": "读取网页正文内容"},
    {"name": "fetch-url", "category": "search", "cli": "myagent-ai fetch-url <url> [-m METHOD] [-H 'K:V'] [-d DATA]",
     "description": "获取 URL 原始内容 (API 调用)"},
    # ── 文件操作 ──
    # [v1.26.1] read/write/ls/rm/grep/mv 为原生命令，直接用 cat/tee/ls/rm/grep/mv，不再包装
    {"name": "send-file", "category": "file", "cli": "myagent-ai send-file <path> [description]",
     "description": "发送文件给用户"},
    # ── 文档生成 ──
    {"name": "docx-create", "category": "document", "cli": "myagent-ai docx-create -c '<JSON>' -t title",
     "description": "创建 Word 文档"},
    {"name": "docx-read", "category": "document", "cli": "myagent-ai docx-read <path>",
     "description": "读取 Word 文档内容"},
    {"name": "xlsx-create", "category": "document", "cli": "myagent-ai xlsx-create -s '<JSON>' -t title",
     "description": "创建 Excel 文件"},
    {"name": "xlsx-read", "category": "document", "cli": "myagent-ai xlsx-read <path> [--sheet name]",
     "description": "读取 Excel 文件内容"},
    {"name": "xlsx-edit", "category": "document", "cli": "myagent-ai xlsx-edit <path> <action> -d '<JSON>'",
     "description": "编辑 Excel 文件"},
    {"name": "ppt-create", "category": "document", "cli": "myagent-ai ppt-create -s '<JSON>' [--theme]",
     "description": "创建 PowerPoint 演示文稿"},
    {"name": "ppt-read", "category": "document", "cli": "myagent-ai ppt-read <path>",
     "description": "读取 PowerPoint 演示文稿内容"},
    {"name": "pdf-create", "category": "document", "cli": "myagent-ai pdf-create -c '<JSON>' [--palette]",
     "description": "创建 PDF 文件"},
    {"name": "pdf-read", "category": "document", "cli": "myagent-ai pdf-read <path> [--start N] [--end N]",
     "description": "读取 PDF 文件内容"},
    # ── 系统 ──
    # [v1.26.1] sysinfo/ps/env 为原生命令，直接用 uname/ps/env/free/df 等，不再包装
    {"name": "pathinfo", "category": "system", "cli": "myagent-ai pathinfo <path>",
     "description": "获取路径详细信息"},
    # ── 浏览器 (ChromeDev MCP — 标准自动化) ──
    {"name": "browser-open", "category": "browser", "cli": "myagent-ai browser-open <url> [--no-headless]",
     "description": "打开浏览器页面 (ChromeDev MCP)"},
    {"name": "browser-screenshot", "category": "browser", "cli": "myagent-ai browser-screenshot [-o path]",
     "description": "浏览器页面截图 (ChromeDev MCP)"},
    {"name": "browser-close", "category": "browser", "cli": "myagent-ai browser-close",
     "description": "关闭浏览器 (ChromeDev MCP)"},
    {"name": "browser-click", "category": "browser", "cli": "myagent-ai browser-click <selector>",
     "description": "点击页面元素 (ChromeDev MCP)"},
    {"name": "browser-fill", "category": "browser", "cli": "myagent-ai browser-fill <selector> <value>",
     "description": "填写页面输入框 (ChromeDev MCP)"},
    {"name": "browser-eval", "category": "browser", "cli": "myagent-ai browser-eval <script>",
     "description": "在页面中执行 JavaScript (ChromeDev MCP)"},
    {"name": "browser-navigate", "category": "browser", "cli": "myagent-ai browser-navigate <list|select|new|close> [arg]",
     "description": "浏览器标签页管理 (ChromeDev MCP)"},
    # ── 反检测浏览器 (DrissionPage — 登录/防检测) ──
    {"name": "stealth-open", "category": "stealth", "cli": "myagent-ai stealth-open [profile] [--no-headless]",
     "description": "启动反检测浏览器 (DrissionPage) — 适合登录网站"},
    {"name": "stealth-navigate", "category": "stealth", "cli": "myagent-ai stealth-navigate <url> [-p profile]",
     "description": "反检测浏览器导航"},
    {"name": "stealth-click", "category": "stealth", "cli": "myagent-ai stealth-click <selector> [-p profile]",
     "description": "反检测浏览器点击元素"},
    {"name": "stealth-fill", "category": "stealth", "cli": "myagent-ai stealth-fill <selector> <value> [-p profile]",
     "description": "反检测浏览器填写输入框"},
    {"name": "stealth-screenshot", "category": "stealth", "cli": "myagent-ai stealth-screenshot [-o path] [-p profile]",
     "description": "反检测浏览器截图"},
    {"name": "stealth-content", "category": "stealth", "cli": "myagent-ai stealth-content [-p profile]",
     "description": "获取反检测浏览器页面内容"},
    {"name": "stealth-cookies", "category": "stealth", "cli": "myagent-ai stealth-cookies <save|load|get|clear> [-p profile]",
     "description": "反检测浏览器 Cookie 管理"},
    {"name": "stealth-wait", "category": "stealth", "cli": "myagent-ai stealth-wait <selector> [-t timeout] [-p profile]",
     "description": "反检测浏览器等待元素出现"},
    {"name": "stealth-wait-manual", "category": "stealth", "cli": "myagent-ai stealth-wait-manual [reason] [-t timeout] [-p profile]",
     "description": "等待用户手动操作 (验证码/2FA)"},
    {"name": "stealth-close", "category": "stealth", "cli": "myagent-ai stealth-close [profile]",
     "description": "关闭反检测浏览器"},
    {"name": "stealth-eval", "category": "stealth", "cli": "myagent-ai stealth-eval <script> [-p profile]",
     "description": "反检测浏览器执行 JavaScript"},
    # ── 浏览器 Profile 管理 ──
    {"name": "profile-list", "category": "stealth", "cli": "myagent-ai profile-list",
     "description": "列出所有浏览器 Profile"},
    {"name": "profile-create", "category": "stealth", "cli": "myagent-ai profile-create <name> [--display name] [--login-url url]",
     "description": "创建浏览器 Profile"},
    {"name": "profile-delete", "category": "stealth", "cli": "myagent-ai profile-delete <name>",
     "description": "删除浏览器 Profile"},
    # ── Site 管理 (v1.31.0) ──
    {"name": "site-manage", "category": "stealth", "cli": "myagent-ai site-manage <list|show|add|remove|init> [args]",
     "description": "网站注册管理 — 列出/查看/添加/删除/初始化网站配置（支持 20+ 内置网站和动态扩展）"},
    # ── GUI 桌面 ──
    {"name": "screenshot", "category": "gui", "cli": "myagent-ai screenshot [region] [-m monitor]",
     "description": "屏幕截图（仅 Windows/macOS）"},
    {"name": "mouse-click", "category": "gui", "cli": "myagent-ai mouse-click <X> <Y> [-b btn] [-c n]",
     "description": "鼠标点击"},
    {"name": "mouse-drag", "category": "gui", "cli": "myagent-ai mouse-drag <X1> <Y1> <X2> <Y2>",
     "description": "鼠标拖拽"},
    {"name": "type-text", "category": "gui", "cli": "myagent-ai type-text <text> [--clear]",
     "description": "输入文本"},
    {"name": "hotkey", "category": "gui", "cli": "myagent-ai hotkey <combo>",
     "description": "按下快捷键"},
    {"name": "window-list", "category": "gui", "cli": "myagent-ai window-list [--filter kw]",
     "description": "列出所有打开的窗口"},
    {"name": "window-focus", "category": "gui", "cli": "myagent-ai window-focus <title> [--maximize]",
     "description": "聚焦/置顶窗口"},
    {"name": "screen-element", "category": "gui", "cli": "myagent-ai screen-element <描述> [区域]",
     "description": "屏幕元素识别 — 通过描述定位屏幕上的元素位置"},
    # ── 记忆 ──
    {"name": "memory", "category": "memory", "cli": "myagent-ai memory [--keyword kw] [--limit N]",
     "description": "搜索历史记忆"},
    # ── 媒体播放 ──
    {"name": "playaudio", "category": "media", "cli": "myagent-ai playaudio --url URL [--title] 或 myagent-ai playaudio --file 本地路径",
     "description": "在聊天中内嵌播放音频（QQ音乐、网易云、YouTube Music、B站等）"},
    {"name": "playvideo", "category": "media", "cli": "myagent-ai playvideo --url URL [--title] 或 myagent-ai playvideo --file 本地路径",
     "description": "在聊天中内嵌播放视频（YouTube、B站、抖音等）"},
]


# CLI 命令按分类的显示名称（用于后台管理界面）
CLI_CATEGORY_LABELS: Dict[str, str] = {
    "perception": "感知",
    "search": "搜索",
    "file": "文件操作",
    "document": "文档生成",
    "system": "系统",
    "browser": "浏览器 (MCP)",
    "stealth": "反检测浏览器",
    "gui": "GUI 桌面",
    "memory": "记忆",
    "media": "媒体播放",
}

# CLI 命令按分类的图标（用于后台管理界面）
CLI_CATEGORY_ICONS: Dict[str, str] = {
    "perception": "👁",
    "search": "🔍",
    "file": "📁",
    "document": "📄",
    "system": "💻",
    "browser": "🌐",
    "stealth": "🔐",
    "gui": "🖥",
    "memory": "🧠",
    "media": "🎵",
}


class SkillRegistry:
    """
    技能注册表。

    使用示例:
        registry = SkillRegistry()
        registry.register(FileReadSkill())

        # 查找技能
        skill = registry.get("file_read")
        result = await skill.execute(path="/tmp/test.txt")

        # 获取所有工具定义(给 LLM 用)
        tools = registry.get_all_schemas()

        # 按名称执行
        result = await registry.execute("file_read", path="/tmp/test.txt")
    """

    def __init__(self):
        self._skills: Dict[str, Skill] = {}
        self.disabled_skills: Set[str] = set()

    def toggle(self, name: str, enabled: bool):
        """启用或禁用技能"""
        if enabled:
            self.disabled_skills.discard(name)
            logger.info(f"技能已启用: {name}")
        else:
            self.disabled_skills.add(name)
            logger.info(f"技能已禁用: {name}")

    def _is_disabled(self, name: str) -> bool:
        """检查技能是否被禁用"""
        return name in self.disabled_skills

    def register(self, skill: Skill):
        """注册技能"""
        if not isinstance(skill, Skill):
            raise TypeError(f"必须是 Skill 子类，得到: {type(skill)}")
        self._skills[skill.name] = skill
        logger.debug(f"技能已注册: {skill.name}")

    def unregister(self, name: str) -> bool:
        """注销技能，返回是否成功"""
        if name in self._skills:
            del self._skills[name]
            logger.debug(f"技能已注销: {name}")
            return True
        return False

    def get(self, name: str) -> Optional[Skill]:
        """获取技能（跳过禁用）"""
        if self._is_disabled(name):
            return None
        return self._skills.get(name)

    def list_skills(self) -> List[str]:
        """列出所有技能名称（跳过禁用）"""
        return [name for name in self._skills.keys() if not self._is_disabled(name)]

    def list_skills_info(self) -> List[Dict]:
        """列出所有技能的详细信息（含禁用状态）"""
        results = []
        for skill in self._skills.values():
            info = skill.to_openclaw_format()
            info["disabled"] = self._is_disabled(skill.name)
            results.append(info)
        return results

    def list_skills_by_category(self) -> Dict[str, List[Dict]]:
        """
        [v1.23.0] 按四层分类返回所有工具/技能信息。

        Returns:
            {
                "builtin_tools":      [...],   # LLM 直接调用的平台工具 (command, web_control)
                "internal_services":  [...],   # 内部平台服务 (不暴露给 LLM, 由 CLI/系统间接调用)
                "cli_commands":       [...],   # CLI 子命令 (通过 command 工具间接调用)
                "python_skills":      [...],   # Python 可执行技能 (SkillRegistry, CLI 底层实现)
                "skill_guides":       [...],   # Markdown 技能指南 (RAG)
            }
        """
        builtin = []
        python_skills = []
        skill_guides = []

        for skill in self._skills.values():
            info = skill.to_openclaw_format()
            info["disabled"] = self._is_disabled(skill.name)

            # 判断技能类型
            skill_type = getattr(skill, '_skill_type', None)
            if skill_type == "markdown":
                skill_guides.append(info)
            elif skill_type == "builtin_agent_tool":
                # [兼容] 旧版 agent_tool_skill 存根，归类到内置工具
                info["note"] = info.get("note", "由 Agent 内核直接处理")
                builtin.append(info)
            else:
                python_skills.append(info)

        # 补充 BUILTIN_TOOLS 元数据中尚未在 registry 中的工具
        registered_builtin_names = {s["name"] for s in builtin}
        for bt in BUILTIN_TOOLS:
            if bt["name"] not in registered_builtin_names:
                builtin.append({
                    "name": bt["name"],
                    "description": bt["description"],
                    "category": bt["category"],
                    "parameters": bt["parameters"],
                    "dangerous": False,
                    "disabled": False,
                    "skill_type": "builtin_platform",
                    "note": f"由 {bt['handler']} 处理",
                })

        # [v1.23.0] 构建 CLI 命令列表，按分类分组
        cli_commands = []
        for cmd in CLI_COMMANDS:
            cli_commands.append({
                "name": cmd["name"],
                "description": cmd["description"],
                "category": cmd["category"],
                "cli": cmd.get("cli", ""),
                "aliases": cmd.get("aliases", []),
                "disabled": False,
                "skill_type": "cli_command",
            })

        # [v1.23.0] 构建内部平台服务列表
        internal_services = []
        for svc in INTERNAL_SERVICES:
            internal_services.append({
                "name": svc["name"],
                "description": svc["description"],
                "category": svc["category"],
                "handler": svc.get("handler", ""),
                "note": svc.get("note", ""),
                "disabled": False,
                "skill_type": "internal_service",
            })

        # 按 category + name 排序
        builtin.sort(key=lambda x: (x.get("category", ""), x["name"]))
        internal_services.sort(key=lambda x: (x.get("category", ""), x["name"]))
        cli_commands.sort(key=lambda x: (x.get("category", ""), x["name"]))
        python_skills.sort(key=lambda x: (x.get("category", ""), x["name"]))
        skill_guides.sort(key=lambda x: (x.get("category", ""), x["name"]))

        return {
            "builtin_tools": builtin,
            "internal_services": internal_services,
            "cli_commands": cli_commands,
            "python_skills": python_skills,
            "skill_guides": skill_guides,
        }

    def get_all_schemas(self) -> List[Dict]:
        """获取所有技能的 JSON Schema (用于 LLM function calling)，跳过禁用"""
        return [skill.get_schema() for name, skill in self._skills.items()
                if not self._is_disabled(name)]

    async def execute(self, skill_name: str, _agent_path: str = "", **kwargs) -> SkillResult:
        """
        按名称执行技能。

        Args:
            skill_name: 技能名称（避免与技能自身参数名冲突，不使用 name）
            _agent_path: Agent 路径（传递给技能用于浏览器锁标识）
            **kwargs: 技能参数

        Returns:
            SkillResult
        """
        skill = self.get(skill_name)
        if not skill:
            return SkillResult(
                success=False,
                error=f"技能不存在: {skill_name}",
            )

        # 参数校验
        valid, err = skill.validate_params(kwargs)
        if not valid:
            return SkillResult(success=False, error=err)

        try:
            logger.info(f"执行技能: {skill_name} (参数: {list(kwargs.keys())})")
            result = await skill.execute(**kwargs, _agent_path=_agent_path)
            return result
        except Exception as e:
            logger.error(f"技能执行失败 ({skill_name}): {e}")
            return SkillResult(
                success=False,
                error=f"技能执行异常: {skill_name} - {str(e)}",
            )

    def auto_discover(self, package: str = "aiskills"):
        """
        自动发现并注册 aiskills/ 目录下的所有内置技能。
        支持两种格式：
        1. Python Skill 子类（*_skill.py 文件）
        2. SKILL.md 目录格式（目录下包含 SKILL.md 的 markdown 技能）

        Args:
            package: 技能包路径
        """
        skills_dir = Path(__file__).parent
        if not skills_dir.exists():
            return

        # 格式1: 扫描 *_skill.py 文件
        for file in skills_dir.glob("*_skill.py"):
            if file.name.startswith("_") or file.name == "base.py":
                continue

            module_name = f"{package}.{file.stem}"
            try:
                module = importlib.import_module(module_name)
                # 查找模块中的 Skill 子类
                for attr_name in dir(module):
                    attr = getattr(module, attr_name)
                    if (inspect.isclass(attr)
                            and issubclass(attr, Skill)
                            and attr is not Skill
                            and not attr.__name__.startswith("_")):
                        try:
                            instance = attr()
                            self.register(instance)
                            logger.info(f"自动发现技能: {instance.name}")
                        except Exception as e:
                            logger.warning(f"技能实例化失败 ({attr_name}): {e}")
            except Exception as e:
                logger.warning(f"模块导入失败 ({module_name}): {e}")

        # 格式2: 扫描 SKILL.md 目录格式的技能
        for subdir in skills_dir.iterdir():
            if not subdir.is_dir() or subdir.name.startswith("_") or subdir.name.startswith("."):
                continue
            skill_md = subdir / "SKILL.md"
            if not skill_md.exists():
                continue
            try:
                instance = _load_skill_from_md(subdir)
                if instance and instance.name not in self._skills:
                    self.register(instance)
                    logger.info(f"自动发现 SKILL.md 技能: {instance.name} ({subdir.name})")
            except Exception as e:
                logger.warning(f"SKILL.md 技能加载失败 ({subdir.name}): {e}")


def _load_skill_from_md(skill_dir: Path) -> Optional[Skill]:
    """从 SKILL.md 目录加载一个 Skill 实例"""
    import yaml  # lazy import

    skill_md_path = skill_dir / "SKILL.md"
    if not skill_md_path.exists():
        return None

    content = skill_md_path.read_text(encoding="utf-8")

    # 解析 YAML front matter
    if not content.startswith("---"):
        return None
    parts = content.split("---", 2)
    if len(parts) < 3:
        return None
    try:
        meta = yaml.safe_load(parts[1]) or {}
    except Exception:
        meta = {}

    name = meta.get("name", skill_dir.name)
    description = meta.get("description", "")
    if isinstance(description, list):
        description = "\n".join(description)
    description = description.strip()
    category = meta.get("metadata", {}).get("category", "") if isinstance(meta.get("metadata"), dict) else ""
    version = meta.get("metadata", {}).get("version", "") if isinstance(meta.get("metadata"), dict) else ""

    # 计算 references 目录中的参考文档
    ref_dir = skill_dir / "references"
    references = []
    if ref_dir.exists():
        for ref_file in sorted(ref_dir.glob("*.md")):
            references.append(ref_file.name)

    # 提取 SKILL.md body（YAML front matter 之后的内容）
    body = ""
    if len(parts) >= 3:
        body = parts[2].strip()

    class MarkdownSkill(Skill):
        """SKILL.md 格式的技能包装器"""
        def __init__(self):
            super().__init__()
            self.name = name
            self.description = description or f"Markdown 技能 ({name})"
            self.parameters = []
            # 存储额外元信息
            self._skill_type = "markdown"
            self._skill_dir = str(skill_dir)
            self._category = category
            self._version = version
            self._references = references
            self._has_templates = (skill_dir / "templates").exists()
            self._has_scripts = (skill_dir / "scripts").exists()
            self._body = body

        async def execute(self, **kwargs) -> SkillResult:
            # SKILL.md 技能的指令已通过 <skill_prompts> 注入 system prompt
            # 如果 LLM 仍然调用了此工具，返回确认信息
            return SkillResult(
                success=True,
                output=f"[{self.name}] 技能指令已激活，请按照 <skill_prompts> 中 {self.name} 的完整指令执行任务。",
                message=f"技能 {self.name} 已激活",
            )

        def get_body(self) -> str:
            """返回 SKILL.md 的 body 内容"""
            return self._body

        def to_openclaw_format(self) -> Dict[str, Any]:
            info = super().to_openclaw_format()
            info["skill_type"] = "markdown"
            info["category"] = self._category
            if self._version:
                info["version"] = self._version
            if self._references:
                info["references"] = self._references
            info["has_templates"] = self._has_templates
            info["has_scripts"] = self._has_scripts
            return info

    return MarkdownSkill()


# ==============================================================================
# 全局注册表
# ==============================================================================

_global_registry: Optional[SkillRegistry] = None


def get_skill_registry() -> SkillRegistry:
    """获取全局技能注册表"""
    global _global_registry
    if _global_registry is None:
        _global_registry = SkillRegistry()
        _global_registry.auto_discover()
    return _global_registry
