From dc428e7de0f25ebbb1157b8723bdce2cf8e3087c Mon Sep 17 00:00:00 2001 From: jxxghp Date: Thu, 9 Apr 2026 07:17:04 +0800 Subject: [PATCH] =?UTF-8?q?feat(skills):=20=E5=86=85=E7=BD=AE=E6=8A=80?= =?UTF-8?q?=E8=83=BD=E6=94=AF=E6=8C=81=E7=89=88=E6=9C=AC=E5=8F=B7=E7=AE=A1?= =?UTF-8?q?=E7=90=86=EF=BC=8C=E6=9B=B4=E6=96=B0=E6=97=B6=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E8=A6=86=E7=9B=96=E6=97=A7=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SKILL.md frontmatter新增version字段,同步时比较版本号, 内置版本更高时直接覆盖用户目录中的旧版本。 --- app/agent/middleware/skills.py | 85 ++++++++++++++++++++++++++-- skills/command-dispatch/SKILL.md | 1 + skills/database-operation/SKILL.md | 1 + skills/generate-identifiers/SKILL.md | 1 + skills/moviepilot-api/SKILL.md | 1 + skills/moviepilot-cli/SKILL.md | 1 + skills/moviepilot-update/SKILL.md | 1 + 7 files changed, 86 insertions(+), 5 deletions(-) diff --git a/app/agent/middleware/skills.py b/app/agent/middleware/skills.py index abb47a0e..a93ff797 100644 --- a/app/agent/middleware/skills.py +++ b/app/agent/middleware/skills.py @@ -47,6 +47,11 @@ class SkillMetadata(TypedDict): 约束: Skill中文描述。 """ + version: int + """Skill 版本号。 + 用于内置技能的版本管理,同步时比较版本号决定是否覆盖用户目录中的旧版本。 + """ + description: str """Skill 功能描述。 约束: 1-1024 字符,应说明功能及适用场景。 @@ -154,9 +159,23 @@ def _parse_skill_metadata( # noqa: C901 ) compatibility_str = compatibility_str[:MAX_SKILL_COMPATIBILITY_LENGTH] + # 版本号,默认为 0(表示未设置版本) + raw_version = frontmatter_data.get("version") + version = 0 + if raw_version is not None: + try: + version = int(raw_version) + except (ValueError, TypeError): + logger.warning( + "Invalid 'version' in %s (got %r), defaulting to 0", + skill_path, + raw_version, + ) + return SkillMetadata( id=skill_id, name=name, + version=version, description=description_str, path=skill_path, metadata=_validate_metadata(frontmatter_data.get("metadata", {}), skill_path), @@ -287,10 +306,38 @@ Remember: Skills make you more capable and consistent. When in doubt, check if a """ +def _extract_version(skill_md: Path) -> int: + """从 SKILL.md 文件中快速提取 version 字段,无法提取时返回 0。""" + try: + content = skill_md.read_text(encoding="utf-8") + except Exception: + return 0 + match = re.match(r"^---\s*\n(.*?)\n---\s*\n", content, re.DOTALL) + if not match: + return 0 + try: + frontmatter = yaml.safe_load(match.group(1)) + except yaml.YAMLError: + return 0 + if not isinstance(frontmatter, dict): + return 0 + raw = frontmatter.get("version") + if raw is None: + return 0 + try: + return int(raw) + except (ValueError, TypeError): + return 0 + + def _sync_bundled_skills(bundled_dir: Path, target_dir: Path) -> None: """将项目自带的技能同步到用户目录。 - 仅当目标目录中不存在对应技能子目录时才复制,已存在则跳过(不覆盖用户修改)。 + - 目标目录中不存在对应技能子目录时,直接复制。 + - 目标目录中已存在时,比较内置与用户目录中 SKILL.md 的 version 字段: + - 内置版本更高时,直接覆盖用户目录中的旧版本。 + - 版本相同或用户版本更高时,跳过。 + - 内置 SKILL.md 无 version 字段(视为 0)时,不覆盖。 Parameters ---------- @@ -312,15 +359,43 @@ def _sync_bundled_skills(bundled_dir: Path, target_dir: Path) -> None: continue skill_dst = target_dir / skill_src.name - if skill_dst.exists(): - # 目标已存在,跳过(不覆盖用户自定义修改) + + if not skill_dst.exists(): + # 目标不存在,直接复制 + try: + shutil.copytree(str(skill_src), str(skill_dst)) + logger.info( + "已自动复制内置技能 '%s' -> '%s'", skill_src.name, skill_dst + ) + except Exception as e: + logger.warning("复制内置技能 '%s' 失败: %s", skill_src.name, e) continue + # 目标已存在,比较版本号 + bundled_version = _extract_version(skill_md) + if bundled_version <= 0: + # 内置技能无版本号,保持旧逻辑不覆盖 + continue + + user_skill_md = skill_dst / "SKILL.md" + user_version = _extract_version(user_skill_md) if user_skill_md.is_file() else 0 + + if bundled_version <= user_version: + # 用户版本 >= 内置版本,跳过 + continue + + # 内置版本更高,删除旧版本后覆盖 try: + shutil.rmtree(str(skill_dst)) shutil.copytree(str(skill_src), str(skill_dst)) - logger.info("已自动复制内置技能 '%s' -> '%s'", skill_src.name, skill_dst) + logger.info( + "已更新内置技能 '%s' (v%d -> v%d)", + skill_src.name, + user_version, + bundled_version, + ) except Exception as e: - logger.warning("复制内置技能 '%s' 失败: %s", skill_src.name, e) + logger.warning("更新内置技能 '%s' 失败: %s", skill_src.name, e) class SkillsMiddleware(AgentMiddleware[SkillsState, ContextT, ResponseT]): # noqa diff --git a/skills/command-dispatch/SKILL.md b/skills/command-dispatch/SKILL.md index 9b4ebd72..1a4539a2 100644 --- a/skills/command-dispatch/SKILL.md +++ b/skills/command-dispatch/SKILL.md @@ -1,5 +1,6 @@ --- name: command-dispatch +version: 1 description: >- Use this skill when the user's intent is to execute a system or plugin function. Applicable scenarios include: 1) The user sends a slash command starting with / (e.g. /cookiecloud, /sites, /subscribes, etc.); diff --git a/skills/database-operation/SKILL.md b/skills/database-operation/SKILL.md index f4415392..afe1bbaf 100644 --- a/skills/database-operation/SKILL.md +++ b/skills/database-operation/SKILL.md @@ -1,5 +1,6 @@ --- name: database-operation +version: 1 description: >- Use this skill when you need to execute SQL against the MoviePilot database. This skill guides you through connecting to the database and executing SQL statements. diff --git a/skills/generate-identifiers/SKILL.md b/skills/generate-identifiers/SKILL.md index 0dd646f4..463426ca 100644 --- a/skills/generate-identifiers/SKILL.md +++ b/skills/generate-identifiers/SKILL.md @@ -1,5 +1,6 @@ --- name: generate-identifiers +version: 1 description: >- Use this skill when a user provides a torrent name or file name and wants to fix recognition issues, or asks to add/manage custom identifiers (自定义识别词). diff --git a/skills/moviepilot-api/SKILL.md b/skills/moviepilot-api/SKILL.md index b98ec9f8..56ffb8c6 100644 --- a/skills/moviepilot-api/SKILL.md +++ b/skills/moviepilot-api/SKILL.md @@ -1,5 +1,6 @@ --- name: moviepilot-api +version: 1 description: Use this skill when you need to call MoviePilot REST API endpoints directly. Covers all 237 API endpoints across 27 categories including media search, downloads, subscriptions, library management, site management, system administration, plugins, workflows, and more. Use this skill whenever the user asks to interact with MoviePilot via its HTTP API, or when the moviepilot-cli skill cannot cover a specific operation. --- diff --git a/skills/moviepilot-cli/SKILL.md b/skills/moviepilot-cli/SKILL.md index a05893f7..8870096f 100644 --- a/skills/moviepilot-cli/SKILL.md +++ b/skills/moviepilot-cli/SKILL.md @@ -1,5 +1,6 @@ --- name: moviepilot-cli +version: 1 description: Use this skill for any request involving movies, TV shows, or anime, including searching, downloads, subscriptions, library management. Also use this skill whenever the user explicitly mentions MoviePilot. --- diff --git a/skills/moviepilot-update/SKILL.md b/skills/moviepilot-update/SKILL.md index 38bba5d5..d12f0d5a 100644 --- a/skills/moviepilot-update/SKILL.md +++ b/skills/moviepilot-update/SKILL.md @@ -1,5 +1,6 @@ --- name: moviepilot-update +version: 1 description: Use this skill when you need to restart or upgrade MoviePilot. This skill covers system restart, version check, and manual upgrade procedures. ---