feat(agents): Phase 7-10 hook system, plugins, skills, orchestration

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
This commit is contained in:
2026-04-04 22:56:27 +08:00
parent e5bd492d74
commit a3fe4d24fc
35 changed files with 8501 additions and 0 deletions

View File

@@ -0,0 +1,100 @@
"""本地 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

View File

@@ -0,0 +1,51 @@
"""插件 Skills 加载器 - Phase 9.2"""
from app.agents.skills.metadata import SkillMetadata
from app.agents.plugins.manager import get_plugin_manager
class PluginSkillLoader:
"""插件 Skills 加载器
从已安装的插件中加载 Skills。
"""
def __init__(self):
self.plugin_manager = get_plugin_manager()
def load_all(self) -> list[SkillMetadata]:
"""从所有已启用的插件加载 Skills
Returns:
Skill 元数据列表
"""
skills = []
for plugin in self.plugin_manager.list_plugins():
if not self.plugin_manager.is_enabled(plugin.id):
continue
# 从插件加载 Skills
plugin_skills = self._load_from_plugin(plugin)
skills.extend(plugin_skills)
return skills
def _load_from_plugin(self, plugin: Any) -> list[SkillMetadata]:
"""从单个插件加载 Skills"""
skills = []
for skill_name in plugin.skills:
skill = SkillMetadata(
name=f"{plugin.id}/{skill_name}",
description=f"Skill from plugin: {plugin.name}",
version=plugin.version,
author=plugin.author,
tags=["plugin", plugin.id],
content=f"# {skill_name}\n\nFrom plugin: {plugin.name}",
source="plugin",
source_id=plugin.id,
)
skills.append(skill)
return skills