From 58093e0877868a8c77429b15e279be03ed47a6ac Mon Sep 17 00:00:00 2001 From: lanyuanxiaoyao Date: Mon, 9 Mar 2026 01:37:36 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20skill=20=E6=89=93?= =?UTF-8?q?=E5=8C=85=E8=84=9A=E6=9C=AC=20build.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 build.py 实现 skill 自动化打包: - 一键完成 skill/SKILL.md 和 scripts/ 打包到 build/ - 时间戳版本号格式 YYYYMMDD_HHMMSS - 仅复制 .py 文件,避免创建空目录 - 添加 skill-packaging spec 文档 --- build.py | 134 +++++++++++++++++++++++++ openspec/specs/skill-packaging/spec.md | 54 ++++++++++ 2 files changed, 188 insertions(+) create mode 100644 build.py create mode 100644 openspec/specs/skill-packaging/spec.md diff --git a/build.py b/build.py new file mode 100644 index 0000000..01403fe --- /dev/null +++ b/build.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +""" +Skill 打包构建脚本 +将 skill/SKILL.md 和 scripts/ 目录打包到 build/ 目录 +""" + +import os +import shutil +from datetime import datetime + + +def generate_timestamp() -> str: + """ + 生成 YYYYMMDD_HHMMSS 格式的时间戳 + + Returns: + 时间戳字符串 + """ + return datetime.now().strftime("%Y%m%d_%H%M%S") + + +def clean_and_create_build_dir(build_dir: str) -> None: + """ + 删除旧 build 目录并创建新的空目录 + + Args: + build_dir: 构建目录路径 + """ + if os.path.exists(build_dir): + print(f"清理旧构建目录: {build_dir}") + shutil.rmtree(build_dir) + os.makedirs(build_dir) + print(f"创建构建目录: {build_dir}") + + +def copy_skill_md(source_path: str, target_dir: str) -> None: + """ + 复制 skill/SKILL.md 到 build/SKILL.md + + Args: + source_path: 源 SKILL.md 路径 + target_dir: 目标目录 + """ + target_path = os.path.join(target_dir, "SKILL.md") + shutil.copy2(source_path, target_path) + print(f"复制: {source_path} -> {target_path}") + + +def copy_scripts_dir(source_dir: str, target_dir: str) -> int: + """ + 递归复制 scripts/ 目录,仅复制 .py 文件 + + Args: + source_dir: 源目录 + target_dir: 目标目录 + + Returns: + 复制的文件数量 + """ + file_count = 0 + + for root, dirs, files in os.walk(source_dir): + # 计算相对路径 + rel_path = os.path.relpath(root, source_dir) + # 处理相对路径为 "." 的情况 + if rel_path == ".": + target_root = target_dir + else: + target_root = os.path.join(target_dir, rel_path) + + # 检查此目录下是否有 .py 文件需要复制 + has_py_files = any(file.endswith(".py") for file in files) + + # 只有当有 .py 文件需要复制时才创建目录并复制 + if has_py_files: + if not os.path.exists(target_root): + os.makedirs(target_root) + + # 只复制 .py 文件 + for file in files: + if file.endswith(".py"): + source_file = os.path.join(root, file) + target_file = os.path.join(target_root, file) + shutil.copy2(source_file, target_file) + file_count += 1 + print(f"复制: {source_file} -> {target_file}") + + return file_count + + +def main() -> None: + """ + 主函数:执行完整的打包流程 + """ + print("=" * 60) + print("Skill 打包构建") + print("=" * 60) + + # 路径配置 + project_root = os.path.dirname(os.path.abspath(__file__)) + skill_md_path = os.path.join(project_root, "skill", "SKILL.md") + scripts_source_dir = os.path.join(project_root, "scripts") + build_dir = os.path.join(project_root, "build") + scripts_target_dir = os.path.join(build_dir, "scripts") + + # 生成时间戳 + version = generate_timestamp() + print(f"版本号: {version}") + print() + + # 清理并创建 build 目录 + clean_and_create_build_dir(build_dir) + print() + + # 复制 SKILL.md + copy_skill_md(skill_md_path, build_dir) + print() + + # 复制 scripts 目录 + print("复制 scripts/ 目录(仅 .py 文件):") + file_count = copy_scripts_dir(scripts_source_dir, scripts_target_dir) + print() + + # 完成信息 + print("=" * 60) + print("构建完成!") + print(f"版本号: {version}") + print(f"复制文件数: {file_count}") + print(f"输出目录: {build_dir}") + print("=" * 60) + + +if __name__ == "__main__": + main() diff --git a/openspec/specs/skill-packaging/spec.md b/openspec/specs/skill-packaging/spec.md new file mode 100644 index 0000000..0d73863 --- /dev/null +++ b/openspec/specs/skill-packaging/spec.md @@ -0,0 +1,54 @@ +## Purpose + +提供自动化的 skill 打包能力,将 skill/SKILL.md 和 scripts/ 目录打包到 build/ 目录,便于 skill 分发。 + +## Requirements + +### Requirement: build.py 一键打包 +系统 SHALL 提供 build.py 脚本,运行后完成 skill 的完整打包流程。 + +#### Scenario: 运行 build.py 成功 +- **WHEN** 用户执行 `uv run python build.py` +- **THEN** 脚本完成所有打包步骤并输出成功信息 + +### Requirement: 构建目录清理重建 +系统 SHALL 在每次构建前删除整个 build 目录,然后重建空的 build 目录。 + +#### Scenario: 删除并重建 build 目录 +- **WHEN** 构建开始 +- **THEN** 脚本删除整个 build 目录(如有),然后创建新的空 build 目录 + +### Requirement: SKILL.md 复制 +系统 SHALL 将 skill/SKILL.md 直接复制到 build/SKILL.md,不保留 skill 这一级目录。 + +#### Scenario: SKILL.md 成功复制 +- **WHEN** 构建执行 +- **THEN** build/SKILL.md 文件存在且内容与 skill/SKILL.md 一致 + +### Requirement: scripts 目录复制 +系统 SHALL 将 scripts/ 目录完整复制到 build/scripts/,保持目录结构。 + +#### Scenario: scripts 目录结构保留 +- **WHEN** 构建执行 +- **THEN** build/scripts/ 下的子目录结构与原 scripts/ 一致 + +### Requirement: 仅复制 Python 文件 +系统 SHALL 只复制 .py 扩展名的文件,其他文件类型自然被过滤。 + +#### Scenario: 只保留 py 文件 +- **WHEN** 原目录包含多种文件类型 +- **THEN** build/scripts/ 中只存在 .py 文件 + +### Requirement: 时间戳版本号 +系统 SHALL 生成 YYYYMMDD_HHMMSS 格式的时间戳作为构建版本标识。 + +#### Scenario: 时间戳格式正确 +- **WHEN** 构建在 2025年3月9日 14点30分22秒执行 +- **THEN** 生成的版本号为 20250309_143022 + +### Requirement: 输出构建信息 +系统 SHALL 在构建完成后打印版本号和构建结果信息。 + +#### Scenario: 显示构建信息 +- **WHEN** 构建成功完成 +- **THEN** 控制台输出版本号和构建文件清单