Phase 7: Built-in Hooks (audit_log, dangerous_confirmation, security_scan) Phase 8: Plugin system (PluginManager, PluginSandbox, PluginManifest) Phase 9: Skills registry (SkillRegistry, local/plugin/MCP loaders) Phase 10: TeamLeader, RemoteTransport, BackgroundTaskManager
101 lines
2.8 KiB
Python
101 lines
2.8 KiB
Python
"""本地 Skills 加载器 - Phase 9.2"""
|
|
|
|
import os
|
|
import re
|
|
from typing import Any
|
|
|
|
from app.agents.skills.metadata import SkillMetadata
|
|
|
|
|
|
class LocalSkillLoader:
|
|
"""本地 Skills 加载器
|
|
|
|
从 skills_dir 目录加载 SKILL.md 文件。
|
|
"""
|
|
|
|
def __init__(self, skills_dir: str):
|
|
self.skills_dir = skills_dir
|
|
|
|
def load_all(self) -> list[SkillMetadata]:
|
|
"""加载所有本地 Skills
|
|
|
|
Returns:
|
|
Skill 元数据列表
|
|
"""
|
|
skills = []
|
|
|
|
if not os.path.exists(self.skills_dir):
|
|
return skills
|
|
|
|
for root, dirs, files in os.walk(self.skills_dir):
|
|
# 跳过隐藏目录
|
|
dirs[:] = [d for d in dirs if not d.startswith(".")]
|
|
|
|
if "SKILL.md" in files:
|
|
skill = self._load_skill_from_dir(root)
|
|
if skill:
|
|
skills.append(skill)
|
|
|
|
return skills
|
|
|
|
def _load_skill_from_dir(self, skill_dir: str) -> SkillMetadata | None:
|
|
"""从目录加载 Skill
|
|
|
|
Args:
|
|
skill_dir: Skill 目录
|
|
|
|
Returns:
|
|
Skill 元数据
|
|
"""
|
|
skill_path = os.path.join(skill_dir, "SKILL.md")
|
|
|
|
try:
|
|
with open(skill_path, "r", encoding="utf-8") as f:
|
|
content = f.read()
|
|
|
|
# 解析 frontmatter
|
|
metadata = self._parse_frontmatter(content)
|
|
|
|
# 获取 Skill 名称(目录名)
|
|
name = os.path.basename(skill_dir)
|
|
|
|
return SkillMetadata(
|
|
name=metadata.get("name", name),
|
|
description=metadata.get("description", ""),
|
|
version=metadata.get("version", "1.0.0"),
|
|
author=metadata.get("author", ""),
|
|
tags=metadata.get("tags", []),
|
|
triggers=metadata.get("triggers", []),
|
|
content=content,
|
|
source="local",
|
|
source_id=skill_dir,
|
|
)
|
|
|
|
except Exception:
|
|
return None
|
|
|
|
def _parse_frontmatter(self, content: str) -> dict[str, Any]:
|
|
"""解析 frontmatter"""
|
|
metadata = {}
|
|
|
|
# 匹配 --- 包裹的 frontmatter
|
|
match = re.match(r"^---\n(.*?)\n---", content, re.DOTALL)
|
|
if match:
|
|
frontmatter = match.group(1)
|
|
|
|
for line in frontmatter.split("\n"):
|
|
if ":" in line:
|
|
key, value = line.split(":", 1)
|
|
key = key.strip()
|
|
value = value.strip()
|
|
|
|
# 处理列表
|
|
if value.startswith("[") and value.endswith("]"):
|
|
value = [v.strip().strip('"').strip("'") for v in value[1:-1].split(",")]
|
|
elif value.lower() in ("true", "false"):
|
|
value = value.lower() == "true"
|
|
|
|
metadata[key] = value
|
|
|
|
return metadata
|