From f94d34468aa9f1d8b2b99d23feb3c32c2473c7e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E9=A6=A8=E5=85=83?= Date: Fri, 3 Apr 2026 21:24:34 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=20skill-creator=20?= =?UTF-8?q?=E5=86=85=E7=BD=AE=E6=8A=80=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 适配 DesireCore 系统的技能创建器,兼容 Claude Code 基础格式: - SKILL.md: 完整 frontmatter + L0/L1/L2 分层内容 - init_skill.py: 支持 --format basic|desirecore - quick_validate.py: 移除白名单限制,改 Schema 校验 - package_skill.py: 新增 --install API 安装模式 - references/desirecore-format.md: 完整字段参考 --- builtin-skills.json | 1 + skills/skill-creator/SKILL.md | 389 ++++++++++++++++++ .../references/desirecore-format.md | 208 ++++++++++ .../references/output-patterns.md | 82 ++++ skills/skill-creator/references/workflows.md | 28 ++ skills/skill-creator/scripts/init_skill.py | 261 ++++++++++++ skills/skill-creator/scripts/package_skill.py | 213 ++++++++++ .../skill-creator/scripts/quick_validate.py | 164 ++++++++ 8 files changed, 1346 insertions(+) create mode 100644 skills/skill-creator/SKILL.md create mode 100644 skills/skill-creator/references/desirecore-format.md create mode 100644 skills/skill-creator/references/output-patterns.md create mode 100644 skills/skill-creator/references/workflows.md create mode 100755 skills/skill-creator/scripts/init_skill.py create mode 100755 skills/skill-creator/scripts/package_skill.py create mode 100755 skills/skill-creator/scripts/quick_validate.py diff --git a/builtin-skills.json b/builtin-skills.json index bc23b88..a18f829 100644 --- a/builtin-skills.json +++ b/builtin-skills.json @@ -6,6 +6,7 @@ "manage-skills", "manage-teams", "s3-storage-operations", + "skill-creator", "update-agent" ] } diff --git a/skills/skill-creator/SKILL.md b/skills/skill-creator/SKILL.md new file mode 100644 index 0000000..0743a1f --- /dev/null +++ b/skills/skill-creator/SKILL.md @@ -0,0 +1,389 @@ +--- +name: 技能创建器 +description: >- + 引导用户创建和编辑符合规范的 SKILL.md 技能包。支持 DesireCore 完整格式 + (frontmatter 元数据 + L0/L1/L2 分层内容 + 脚本/参考/资产)和 Claude Code + 基础格式。Use when 用户要求创建新技能、更新已有技能、或将经验封装为可复用 + 的技能包。 +version: 1.0.0 +type: meta +risk_level: low +status: enabled +disable-model-invocation: true +tags: + - skill + - creation + - meta + - template + - authoring +metadata: + author: desirecore + updated_at: '2026-04-03' +market: + icon: >- + + short_desc: 引导创建符合规范的 SKILL.md 技能包,支持完整元数据与分层内容 + category: productivity + maintainer: + name: DesireCore Official + verified: true + compatible_agents: [] + channel: latest +--- + +# skill-creator 技能 + +## L0:一句话摘要 + +引导用户将需求、经验和工作流封装为结构化的 SKILL.md 技能包。 + +## L1:概述与使用场景 + +### 能力描述 + +skill-creator 是一个**元技能(Meta-Skill)**,赋予 Agent 创建和编辑技能的能力。技能是模块化、自包含的能力包,通过 SKILL.md 为 Agent 提供专业知识、工作流和工具集成——将 Agent 从通用助手转变为领域专家。 + +### 使用场景 + +- 用户想把反复执行的工作流封装为可复用技能 +- 用户想创建新技能教会 Agent 新的能力 +- 用户想更新已有技能、优化其效果 +- 用户分享了参考资料,需要组织为结构化的技能包 + +### 核心价值 + +- **沉淀经验**:将个人知识和工作流固化为可复用的 Skill +- **自我扩展**:创建的技能让 Agent 能力持续增长 +- **规范化**:生成符合标准的 SKILL.md,确保技能系统正确解析和分发 + +## L2:详细规范 + +### 关于技能 + +技能是模块化、自包含的能力包,为 Agent 提供: + +1. **专业工作流** — 特定领域的多步骤流程 +2. **工具集成** — 处理特定文件格式或 API 的指南 +3. **领域知识** — 公司规范、业务逻辑、专业 Schema +4. **捆绑资源** — 脚本、参考文档和资产文件 + +### 核心原则 + +#### 简洁优先 + +上下文窗口是公共资源。技能与系统提示、对话历史、其他技能元数据和用户请求共享上下文窗口。 + +**默认假设:AI 已经非常聪明。** 只添加 AI 不知道的内容。对每条信息问自己:"AI 真的需要这个解释吗?" "这段话值得它的 Token 成本吗?" + +优先使用简洁的例子而非冗长的解释。 + +#### 设置适当的自由度 + +根据任务的脆弱性和可变性匹配指令的具体程度: + +- **高自由度(文本指引)**:多种方案都可行时,决策依赖上下文 +- **中自由度(伪代码或带参脚本)**:存在首选模式,允许一定变化 +- **低自由度(固定脚本,少量参数)**:操作脆弱易错,一致性至关重要 + +#### 渐进式披露 + +技能使用三层加载系统高效管理上下文: + +1. **元数据(name + description)** — 始终在上下文中(~100 词) +2. **SKILL.md body** — 技能触发时加载(<5k 词) +3. **捆绑资源** — Agent 按需加载(无限制,脚本可直接执行无需读入上下文) + +### 技能结构 + +``` +skill-name/ +├── SKILL.md (必须:技能定义文件) +├── scripts/ (可选:可执行脚本) +├── references/ (可选:参考文档) +└── assets/ (可选:输出用资源文件) +``` + +#### SKILL.md 格式 + +SKILL.md 由两部分组成:**Frontmatter(YAML 元数据)** 和 **Body(Markdown 指令)**。 + +##### Frontmatter 字段 + +**必填**: + +| 字段 | 类型 | 说明 | +|------|------|------| +| `description` | string | 技能用途描述。**必须包含 "Use when" 触发提示**——AI 据此判断何时使用该技能 | + +**推荐**: + +| 字段 | 类型 | 说明 | 默认值 | +|------|------|------|--------| +| `name` | string | 技能显示名称 | 目录名 | +| `version` | string | 语义版本号(如 `1.0.0`) | — | +| `type` | enum | `procedural` / `conversational` / `meta` | — | +| `risk_level` | enum | `low` / `medium` / `high` | — | +| `status` | enum | `enabled` / `disabled` | `enabled` | +| `tags` | string[] | 标签列表 | — | +| `metadata` | object | `author`、`updated_at` | — | + +**功能控制**: + +| 字段 | 类型 | 默认 | 说明 | +|------|------|------|------| +| `disable-model-invocation` | boolean | `true` | `true`=仅显式调用触发;`false`=自动注入 system prompt | +| `user-invocable` | boolean | `true` | `false`=不出现在命令补全,仅作为背景知识 | +| `allowed-tools` | string[] | — | 限制执行时可用的工具列表 | +| `requires` | object | — | 依赖声明:`tools`、`optional_tools`、`connections` | + +完整字段表(含市场发布、JSON 输出、fork 执行等高级字段)见 [references/desirecore-format.md](references/desirecore-format.md)。 + +> **Claude Code 兼容说明**:Claude Code 仅使用 `name` + `description`(+ 可选 `license`、`compatibility`)。这些字段在 DesireCore 中完全合法——DesireCore 格式是 Claude Code 的超集。 + +##### Body 结构 + +**推荐使用 L0/L1/L2 分层**: + +```markdown +# skill-id 技能 + +## L0:一句话摘要 +用一句话描述这个技能做什么。 + +## L1:概述与使用场景 +### 能力描述 / ### 使用场景 / ### 核心价值 + +## L2:详细规范 +### 具体操作步骤 / ### 错误处理 +``` + +分层加载机制: +- **L0**(~50 字):快速理解技能做什么 +- **L1**(~300 字):判断是否适用于当前任务 +- **L2**(不限):完整的执行指南 + +> 分层不是强制的。如果技能内容简短(<100 行),可以不分层——解析器会以整段内容作为 fallback。Claude Code 的无分层格式在 DesireCore 中同样正常工作。 + +#### Bundled Resources + +##### Scripts(`scripts/`) + +可执行代码(Python/Bash 等),用于需要确定性可靠性或被反复编写的任务。 + +- **何时使用**:相同代码被反复编写,或需要确定性可靠性 +- **示例**:`scripts/rotate_pdf.py`(PDF 旋转)、`scripts/fill_form.py`(表单填充) +- **优势**:Token 高效,确定性,可直接执行无需读入上下文 +- **注意**:脚本可能仍需被 AI 读取以做环境适配 + +##### References(`references/`) + +文档和参考资料,按需加载到上下文中。 + +- **何时使用**:AI 工作时需要参考的详细文档 +- **示例**:API 文档、数据库 Schema、领域知识、公司政策 +- **最佳实践**:大文件(>10k 词)在 SKILL.md 中提供 grep 搜索模式 +- **避免重复**:信息只放 SKILL.md 或 references 中的一处 + +##### Assets(`assets/`) + +不加载到上下文、而是用于输出的文件。 + +- **何时使用**:技能需要在最终输出中使用的文件 +- **示例**:PPT 模板、HTML 骨架、logo 图片、字体文件 +- **优势**:将输出资源与文档分离 + +#### 不应包含的内容 + +技能应只包含 AI 执行任务所需的文件。**不要**创建:README.md、INSTALLATION_GUIDE.md、CHANGELOG.md 等辅助文档。 + +### 渐进式披露模式 + +保持 SKILL.md body 在 500 行以内。接近限制时拆分到 references。 + +**模式 1:高层指南 + 参考文件** + +```markdown +# PDF Processing + +## Quick start +[核心代码示例] + +## Advanced features +- **Form filling**: See [FORMS.md](FORMS.md) +- **API reference**: See [REFERENCE.md](REFERENCE.md) +``` + +**模式 2:按领域组织** + +``` +bigquery-skill/ +├── SKILL.md (overview) +└── references/ + ├── finance.md + ├── sales.md + └── product.md +``` + +用户问销售指标时,AI 只读 sales.md。 + +**模式 3:基本内容 + 条件高级内容** + +```markdown +## Editing documents +For simple edits, modify the XML directly. +**For tracked changes**: See [REDLINING.md](REDLINING.md) +``` + +**重要**:避免深层嵌套引用——references 只从 SKILL.md 直接链接一层。长 reference 文件(>100 行)在顶部加目录。 + +### 创建流程 + +1. 用具体例子理解技能需求 +2. 规划可复用资源(脚本、参考、资产) +3. 初始化技能(运行 init_skill.py) +4. 编辑技能(实现资源,编写 SKILL.md) +5. 验证技能(运行 quick_validate.py) +6. 安装技能 +7. 迭代优化 + +#### 步骤 1:理解技能需求 + +跳过此步仅当技能的使用模式已经完全清晰。即使处理已有技能时,此步仍有价值。 + +通过具体例子理解技能将如何被使用。例如构建 image-editor 技能时: + +- "这个技能应支持哪些功能?编辑、旋转、其他?" +- "能举几个使用场景吗?" +- "什么操作应该触发这个技能?" + +避免一次问太多问题——从最重要的开始,按需跟进。当对技能应支持的功能有清晰认知时,结束此步。 + +#### 步骤 2:规划资源 + +分析每个例子: + +1. 考虑如何从零执行 +2. 识别哪些脚本、参考、资产在反复执行时有帮助 + +示例分析: + +- `pdf-editor` 处理"旋转 PDF"→ 每次都要写相同代码 → `scripts/rotate_pdf.py` +- `frontend-webapp-builder` 处理"创建 todo app"→ 每次都要写样板代码 → `assets/hello-world/` +- `big-query` 处理"今天多少用户登录"→ 每次都要查 Schema → `references/schema.md` + +#### 步骤 3:初始化 + +使用 init_skill.py 创建模板: + +```bash +# DesireCore 完整格式(默认,推荐) +scripts/init_skill.py --path + +# Claude Code 基础格式 +scripts/init_skill.py --path --format basic +``` + +默认生成 DesireCore 格式(含完整 frontmatter + L0/L1/L2 结构)。`--format basic` 生成 Claude Code 兼容的最小格式。 + +初始化后,根据需要定制或删除生成的示例文件。 + +#### 步骤 4:编辑技能 + +##### 学习设计模式 + +根据技能需求查阅参考: + +- **多步骤流程**:见 [references/workflows.md](references/workflows.md) +- **输出格式标准**:见 [references/output-patterns.md](references/output-patterns.md) + +##### 从资源开始 + +先实现步骤 2 识别的资源文件(scripts/、references/、assets/)。此步骤可能需要用户输入,如品牌资产需要用户提供 logo。 + +添加的脚本必须实际运行测试,确保无 bug 且输出符合预期。不需要的示例文件应删除。 + +##### 编写 SKILL.md + +**Frontmatter 编写要点**: + +- `description` 是最关键的字段——AI 据此判断何时触发技能 +- 在 description 中包含 "Use when" 触发提示和典型使用场景 +- 所有 "when to use" 信息放 description 中,不放 body 里(body 只在触发后加载) + +**Body 编写要点**: + +- 始终使用祈使句/不定式形式 +- L0 不超过一句话 +- L1 用于判断适用性,不超过 300 字 +- L2 放完整的操作步骤、API 调用、错误处理 + +#### 步骤 5:验证 + +```bash +scripts/quick_validate.py +``` + +验证 SKILL.md 格式、frontmatter 字段合法性和目录结构。 + +#### 步骤 6:安装 + +**方式 A:通过 API 安装(推荐,需 Agent Service 运行中)** + +```bash +PORT=$(cat ~/.desirecore/agent-service.port 2>/dev/null) + +# 安装为全局技能(所有 Agent 可见) +curl -k -X POST "https://127.0.0.1:${PORT}/api/skills" \ + -H "Content-Type: application/json" \ + -d "{\"skillId\": \"\", \"content\": \"$(cat path/to/SKILL.md | jq -Rsa .)\"}" + +# 安装为 Agent 级技能(仅指定 Agent 可见) +curl -k -X POST "https://127.0.0.1:${PORT}/api/agents//skills" \ + -H "Content-Type: application/json" \ + -d "{\"id\": \"\", \"fullContent\": \"$(cat path/to/SKILL.md | jq -Rsa .)\"}" +``` + +**方式 B:文件系统直写** + +```bash +# 全局技能 +cp -r path/to/skill-name ~/.desirecore/skills/ + +# Agent 级技能 +cp -r path/to/skill-name ~/.desirecore/agents//skills/ +``` + +**方式 C:打包为 .skill 文件(Claude Code 兼容)** + +```bash +scripts/package_skill.py +``` + +生成 `skill-name.skill` 文件(ZIP 格式),可在 Claude Code 中使用。 + +#### 步骤 7:迭代 + +1. 在真实任务中使用技能 +2. 观察不足或低效之处 +3. 确定 SKILL.md 或资源需要如何改进 +4. 实施修改并再次测试 + +### 作用域说明 + +技能存在三个作用域层级,按优先级从高到低: + +| 优先级 | 作用域 | 路径 | 可见范围 | +|--------|--------|------|---------| +| 最高 | Project | `.claude/skills/` | 当前项目所有 Agent | +| 中 | Agent | `~/.desirecore/agents/{agentId}/skills/` | 仅该 Agent | +| 最低 | Global | `~/.desirecore/skills/` | 所有 Agent | + +同名技能按优先级覆盖——高优先级的技能会遮蔽低优先级的同名技能。 diff --git a/skills/skill-creator/references/desirecore-format.md b/skills/skill-creator/references/desirecore-format.md new file mode 100644 index 0000000..587ae6d --- /dev/null +++ b/skills/skill-creator/references/desirecore-format.md @@ -0,0 +1,208 @@ +# DesireCore SKILL.md 完整格式参考 + +## Frontmatter 完整字段表 + +### 必填字段 + +| 字段 | 类型 | 说明 | +|------|------|------| +| `description` | string | 技能用途描述,必须包含 "Use when" 触发提示 | + +### 推荐字段 + +| 字段 | 类型 | 说明 | 示例 | +|------|------|------|------| +| `name` | string | 显示名称(中英文均可) | `"数据分析"` | +| `version` | string | 语义版本号 | `"1.0.0"` | +| `type` | enum | `procedural` / `conversational` / `meta` | `procedural` | +| `risk_level` | enum | `low` / `medium` / `high` | `low` | +| `status` | enum | `enabled` / `disabled` | `enabled` | +| `tags` | string[] | 标签列表 | `[analysis, data]` | +| `metadata.author` | string | 技能作者 | `"user"` | +| `metadata.updated_at` | string | 更新日期 | `"2026-04-03"` | + +### 功能控制字段 + +| 字段 | 类型 | 默认 | 说明 | +|------|------|------|------| +| `disable-model-invocation` | boolean | `true` | `true`=仅显式调用触发;`false`=自动注入 system prompt | +| `user-invocable` | boolean | `true` | `false`=不出现在命令补全,仅作为背景知识 | +| `allowed-tools` | string[] | 全部 | 限制执行时可用的工具列表(如 `["Edit", "Read", "Bash"]`) | +| `model` | string | 继承 | 覆盖使用的模型 ID(如 `"claude-sonnet-4-20250514"`) | +| `context` | enum | `default` | `fork`=在独立子 Agent 中执行 | +| `agent` | string | — | `context=fork` 时子 Agent 的角色描述 | +| `argument-hint` | string | — | 参数提示,显示在自动补全中(如 `""`) | + +### 依赖声明 + +```yaml +requires: + tools: + - Bash + - Read + optional_tools: + - Edit + connections: + - database-x +``` + +### 市场展示字段 + +发布到市场时需要填写: + +```yaml +market: + icon: >- + ... + short_desc: 一句话简介,用于市场卡片展示 + category: productivity + maintainer: + name: Your Name + verified: false + compatible_agents: [] + required_client_version: "10.0.20" + channel: latest +``` + +| 字段 | 类型 | 说明 | +|------|------|------| +| `market.icon` | string | 内联 SVG 图标 | +| `market.short_desc` | string | 一句话简介 | +| `market.category` | string | 分类 slug(如 `productivity`、`knowledge`、`development`) | +| `market.maintainer.name` | string | 维护者名称 | +| `market.maintainer.verified` | boolean | 是否官方认证 | +| `market.compatible_agents` | string[] | 兼容的 Agent ID | +| `market.required_client_version` | string | 最低客户端版本(semver) | +| `market.channel` | enum | `latest` / `stable` | + +### JSON 输出控制 + +```yaml +json_output: + enabled: true + shape: object +``` + +启用后,AI 的最终回复会被自动解析修复为合法 JSON。`shape` 指定顶层形状:`object`(默认)或 `array`。 + +### Claude Code 兼容字段 + +以下字段来自 Claude Code 规范,在 DesireCore 中同样合法(Schema 设置了 `additionalProperties: true`): + +| 字段 | 类型 | 说明 | +|------|------|------| +| `license` | string | 许可证声明 | +| `compatibility` | string | 环境兼容性说明 | + +## type 类型详解 + +| 类型 | 含义 | 交互模式 | 典型示例 | +|------|------|---------|---------| +| `procedural` | 流程型 | 按步骤执行,较少交互 | 数据分析、文档处理、API 操作 | +| `conversational` | 对话型 | 多轮交互完成 | 需求收集、头脑风暴、方案评审 | +| `meta` | 元技能 | 管理其他系统资源 | 创建 Agent、管理技能、团队管理 | + +## Body 分层详解 + +### L0:一句话摘要 + +- 不超过一句话(~50 字) +- 用于快速理解技能做什么 +- 标题格式:`## L0:一句话摘要` + +### L1:概述与使用场景 + +- 不超过 300 字 +- 用于判断当前任务是否适用该技能 +- 推荐子标题:`### 能力描述`、`### 使用场景`、`### 核心价值` +- 标题格式:`## L1:概述与使用场景` + +### L2:详细规范 + +- 无长度限制(但 SKILL.md 整体建议 <500 行) +- 完整的执行指南、API 调用、错误处理、权限要求 +- 标题格式:`## L2:详细规范` + +### 分层加载机制 + +- `disable-model-invocation: false` 时:L0 + L1 自动注入 system prompt +- `disable-model-invocation: true` 时:显式调用时加载完整内容(L0 + L1 + L2) +- 不分层时:整段内容作为 fallback + +## 完整示例 + +### procedural 类型(数据分析) + +```yaml +--- +name: 数据分析 +description: >- + 对结构化数据进行深度分析和可视化。Use when 用户要求分析 + CSV/Excel 数据、生成统计报告、或创建数据图表。 +version: 1.0.0 +type: procedural +risk_level: low +status: enabled +tags: [analysis, data, visualization] +metadata: + author: user + updated_at: '2026-04-03' +--- + +# data-analysis 技能 + +## L0:一句话摘要 + +对结构化数据进行深度分析、统计和可视化。 + +## L1:概述与使用场景 + +### 能力描述 + +支持 CSV、Excel、JSON 等格式数据的读取、清洗、统计分析和图表生成。 + +### 使用场景 + +- 分析销售数据并生成月度报告 +- 数据清洗和格式转换 +- 生成统计图表 + +## L2:详细规范 + +### 分析流程 + +1. 读取并检查数据格式 +2. 数据清洗(缺失值、异常值) +3. 统计分析 +4. 可视化输出 +``` + +### meta 类型(资源管理) + +```yaml +--- +name: 知识库管理 +description: >- + 管理 Agent 的知识库:导入文档、更新索引、清理过期内容。 + Use when 用户要求导入新文档到知识库、更新或删除已有内容。 +version: 1.0.0 +type: meta +risk_level: medium +status: enabled +disable-model-invocation: true +tags: [knowledge, management, meta] +metadata: + author: user + updated_at: '2026-04-03' +--- +``` + +## 与 Claude Code 格式对比 + +| 维度 | Claude Code 格式 | DesireCore 格式 | +|------|-----------------|----------------| +| 必填 frontmatter | `name` + `description` | `description` | +| 可选 frontmatter | `license`、`compatibility`、`metadata` | 20+ 字段(全部可选) | +| Body 结构 | 自由 Markdown | L0/L1/L2 分层(推荐,非强制) | +| 分发方式 | `.skill` ZIP 包 | API 安装 / 文件系统 / 市场 | +| 兼容性 | — | DesireCore 是超集,完全向下兼容 | diff --git a/skills/skill-creator/references/output-patterns.md b/skills/skill-creator/references/output-patterns.md new file mode 100644 index 0000000..073ddda --- /dev/null +++ b/skills/skill-creator/references/output-patterns.md @@ -0,0 +1,82 @@ +# Output Patterns + +Use these patterns when skills need to produce consistent, high-quality output. + +## Template Pattern + +Provide templates for output format. Match the level of strictness to your needs. + +**For strict requirements (like API responses or data formats):** + +```markdown +## Report structure + +ALWAYS use this exact template structure: + +# [Analysis Title] + +## Executive summary +[One-paragraph overview of key findings] + +## Key findings +- Finding 1 with supporting data +- Finding 2 with supporting data +- Finding 3 with supporting data + +## Recommendations +1. Specific actionable recommendation +2. Specific actionable recommendation +``` + +**For flexible guidance (when adaptation is useful):** + +```markdown +## Report structure + +Here is a sensible default format, but use your best judgment: + +# [Analysis Title] + +## Executive summary +[Overview] + +## Key findings +[Adapt sections based on what you discover] + +## Recommendations +[Tailor to the specific context] + +Adjust sections as needed for the specific analysis type. +``` + +## Examples Pattern + +For skills where output quality depends on seeing examples, provide input/output pairs: + +```markdown +## Commit message format + +Generate commit messages following these examples: + +**Example 1:** +Input: Added user authentication with JWT tokens +Output: +``` +feat(auth): implement JWT-based authentication + +Add login endpoint and token validation middleware +``` + +**Example 2:** +Input: Fixed bug where dates displayed incorrectly in reports +Output: +``` +fix(reports): correct date formatting in timezone conversion + +Use UTC timestamps consistently across report generation +``` + +Follow this style: type(scope): brief description, then detailed explanation. +``` + +Examples help Claude understand the desired style and level of detail more clearly than descriptions alone. diff --git a/skills/skill-creator/references/workflows.md b/skills/skill-creator/references/workflows.md new file mode 100644 index 0000000..a350c3c --- /dev/null +++ b/skills/skill-creator/references/workflows.md @@ -0,0 +1,28 @@ +# Workflow Patterns + +## Sequential Workflows + +For complex tasks, break operations into clear, sequential steps. It is often helpful to give Claude an overview of the process towards the beginning of SKILL.md: + +```markdown +Filling a PDF form involves these steps: + +1. Analyze the form (run analyze_form.py) +2. Create field mapping (edit fields.json) +3. Validate mapping (run validate_fields.py) +4. Fill the form (run fill_form.py) +5. Verify output (run verify_output.py) +``` + +## Conditional Workflows + +For tasks with branching logic, guide Claude through decision points: + +```markdown +1. Determine the modification type: + **Creating new content?** → Follow "Creation workflow" below + **Editing existing content?** → Follow "Editing workflow" below + +2. Creation workflow: [steps] +3. Editing workflow: [steps] +``` \ No newline at end of file diff --git a/skills/skill-creator/scripts/init_skill.py b/skills/skill-creator/scripts/init_skill.py new file mode 100755 index 0000000..c595ed0 --- /dev/null +++ b/skills/skill-creator/scripts/init_skill.py @@ -0,0 +1,261 @@ +#!/usr/bin/env python3 +""" +Skill Initializer - Creates a new skill from template + +Usage: + init_skill.py --path [--format basic|desirecore] + +Examples: + init_skill.py my-new-skill --path ~/.desirecore/skills + init_skill.py my-api-helper --path ~/.desirecore/skills --format basic +""" + +import sys +import argparse +import re +from pathlib import Path +from datetime import date + + +# ==================== DesireCore 完整格式模板 ==================== + +DESIRECORE_TEMPLATE = """\ +--- +name: {skill_name} +description: >- + [TODO: 完整描述技能用途。必须包含 "Use when" 触发提示, + 帮助 AI 判断何时使用该技能。] +version: 1.0.0 +type: procedural +risk_level: low +status: enabled +tags: + - [TODO: 添加标签] +metadata: + author: user + updated_at: '{today}' +--- + +# {skill_title} + +## L0:一句话摘要 + +[TODO: 用一句话描述这个技能做什么] + +## L1:概述与使用场景 + +### 能力描述 + +[TODO: 详细描述技能的核心能力] + +### 使用场景 + +- [TODO: 场景 1] +- [TODO: 场景 2] + +### 核心价值 + +- [TODO: 价值 1] + +## L2:详细规范 + +### 具体操作步骤 + +[TODO: 按步骤描述执行流程] + +### 错误处理 + +| 错误场景 | 处理方式 | +|---------|---------| +| [TODO] | [TODO] | +""" + + +# ==================== Claude Code 基础格式模板 ==================== + +BASIC_TEMPLATE = """\ +--- +name: {skill_name} +description: [TODO: Complete and informative explanation of what the skill does and when to use it. Include WHEN to use this skill - specific scenarios, file types, or tasks that trigger it.] +--- + +# {skill_title} + +## Overview + +[TODO: 1-2 sentences explaining what this skill enables] + +## [TODO: Replace with first main section] + +[TODO: Add content here] + +## Resources + +This skill includes example resource directories: + +### scripts/ +Executable code for tasks that require deterministic reliability. + +### references/ +Documentation and reference material loaded into context as needed. + +### assets/ +Files used within the output (templates, images, fonts, etc.). + +--- + +**Delete any unneeded directories.** Not every skill requires all three. +""" + + +EXAMPLE_SCRIPT = '''\ +#!/usr/bin/env python3 +""" +Example helper script for {skill_name} + +Replace with actual implementation or delete if not needed. +""" + +def main(): + print("Example script for {skill_name}") + # TODO: Add actual script logic + +if __name__ == "__main__": + main() +''' + +EXAMPLE_REFERENCE = """\ +# Reference Documentation for {skill_title} + +Replace with actual reference content or delete if not needed. + +Reference docs are ideal for: +- API documentation +- Detailed workflow guides +- Database schemas +- Content too lengthy for main SKILL.md +""" + +EXAMPLE_ASSET = """\ +This is a placeholder for asset files. +Replace with actual assets (templates, images, fonts, etc.) or delete if not needed. + +Asset files are NOT loaded into context — they are used within the output. +""" + + +def title_case_skill_name(skill_name): + """Convert hyphenated skill name to Title Case.""" + return ' '.join(word.capitalize() for word in skill_name.split('-')) + + +def validate_skill_name(name): + """Validate skill name format (kebab-case).""" + if not re.match(r'^[a-z0-9][a-z0-9-]*[a-z0-9]$', name) and not re.match(r'^[a-z0-9]$', name): + return False, "Name must be kebab-case (lowercase letters, digits, hyphens)" + if '--' in name: + return False, "Name cannot contain consecutive hyphens" + if len(name) > 64: + return False, f"Name too long ({len(name)} chars, max 64)" + return True, "" + + +def init_skill(skill_name, path, fmt='desirecore'): + """Initialize a new skill directory with template SKILL.md.""" + skill_dir = Path(path).resolve() / skill_name + + if skill_dir.exists(): + print(f"❌ Error: Skill directory already exists: {skill_dir}") + return None + + # Create skill directory + try: + skill_dir.mkdir(parents=True, exist_ok=False) + print(f"✅ Created skill directory: {skill_dir}") + except Exception as e: + print(f"❌ Error creating directory: {e}") + return None + + # Create SKILL.md from template + skill_title = title_case_skill_name(skill_name) + template = DESIRECORE_TEMPLATE if fmt == 'desirecore' else BASIC_TEMPLATE + skill_content = template.format( + skill_name=skill_name, + skill_title=skill_title, + today=date.today().isoformat(), + ) + + skill_md_path = skill_dir / 'SKILL.md' + try: + skill_md_path.write_text(skill_content) + print(f"✅ Created SKILL.md ({fmt} format)") + except Exception as e: + print(f"❌ Error creating SKILL.md: {e}") + return None + + # Create resource directories with example files + try: + scripts_dir = skill_dir / 'scripts' + scripts_dir.mkdir(exist_ok=True) + example_script = scripts_dir / 'example.py' + example_script.write_text(EXAMPLE_SCRIPT.format(skill_name=skill_name)) + example_script.chmod(0o755) + print("✅ Created scripts/example.py") + + references_dir = skill_dir / 'references' + references_dir.mkdir(exist_ok=True) + example_ref = references_dir / 'api_reference.md' + example_ref.write_text(EXAMPLE_REFERENCE.format(skill_title=skill_title)) + print("✅ Created references/api_reference.md") + + assets_dir = skill_dir / 'assets' + assets_dir.mkdir(exist_ok=True) + example_asset = assets_dir / 'example_asset.txt' + example_asset.write_text(EXAMPLE_ASSET) + print("✅ Created assets/example_asset.txt") + except Exception as e: + print(f"❌ Error creating resource directories: {e}") + return None + + print(f"\n✅ Skill '{skill_name}' initialized at {skill_dir}") + print("\nNext steps:") + print("1. Edit SKILL.md — complete TODO items and update description") + print("2. Customize or delete example files in scripts/, references/, assets/") + print("3. Run quick_validate.py to check the skill structure") + + return skill_dir + + +def main(): + parser = argparse.ArgumentParser( + description='Initialize a new skill from template', + epilog='Examples:\n' + ' init_skill.py my-new-skill --path ~/.desirecore/skills\n' + ' init_skill.py my-api-helper --path ~/.desirecore/skills --format basic', + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + parser.add_argument('skill_name', help='Skill name (kebab-case, max 64 chars)') + parser.add_argument('--path', required=True, help='Parent directory for the skill') + parser.add_argument( + '--format', choices=['desirecore', 'basic'], default='desirecore', + help='Template format: desirecore (full, default) or basic (Claude Code compatible)', + ) + args = parser.parse_args() + + # Validate name + valid, msg = validate_skill_name(args.skill_name) + if not valid: + print(f"❌ Invalid skill name: {msg}") + sys.exit(1) + + print(f"🚀 Initializing skill: {args.skill_name}") + print(f" Location: {args.path}") + print(f" Format: {args.format}") + print() + + result = init_skill(args.skill_name, args.path, args.format) + sys.exit(0 if result else 1) + + +if __name__ == "__main__": + main() diff --git a/skills/skill-creator/scripts/package_skill.py b/skills/skill-creator/scripts/package_skill.py new file mode 100755 index 0000000..29984da --- /dev/null +++ b/skills/skill-creator/scripts/package_skill.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python3 +""" +Skill Packager & Installer + +Supports two modes: + - Package: Create a .skill file (ZIP) for Claude Code distribution + - Install: Install directly to DesireCore via HTTP API + +Usage: + # Package as .skill file (Claude Code compatible) + package_skill.py [output-directory] + + # Install to DesireCore via API + package_skill.py --install [--scope global|agent] [--agent-id ] +""" + +import sys +import os +import json +import zipfile +import argparse +import ssl +import urllib.request +import urllib.error +from pathlib import Path + +# Import validate_skill from sibling script +_script_dir = Path(__file__).resolve().parent +sys.path.insert(0, str(_script_dir)) +from quick_validate import validate_skill + + +# ==================== Package Mode ==================== + +def package_skill(skill_path, output_dir=None): + """Package a skill folder into a .skill file (ZIP format).""" + skill_path = Path(skill_path).resolve() + + if not skill_path.exists(): + print(f"❌ Error: Skill folder not found: {skill_path}") + return None + + if not skill_path.is_dir(): + print(f"❌ Error: Path is not a directory: {skill_path}") + return None + + skill_md = skill_path / "SKILL.md" + if not skill_md.exists(): + print(f"❌ Error: SKILL.md not found in {skill_path}") + return None + + # Validate before packaging + print("🔍 Validating skill...") + valid, errors, warnings = validate_skill(skill_path) + if not valid: + print(f"❌ Validation failed:") + for e in errors: + print(f" ✗ {e}") + return None + if warnings: + for w in warnings: + print(f" ⚠ {w}") + print(f"✅ Validation passed\n") + + # Determine output location + skill_name = skill_path.name + if output_dir: + output_path = Path(output_dir).resolve() + output_path.mkdir(parents=True, exist_ok=True) + else: + output_path = Path.cwd() + + skill_filename = output_path / f"{skill_name}.skill" + + # Create .skill file (zip format) + try: + with zipfile.ZipFile(skill_filename, 'w', zipfile.ZIP_DEFLATED) as zipf: + for file_path in skill_path.rglob('*'): + if file_path.is_file(): + arcname = file_path.relative_to(skill_path.parent) + zipf.write(file_path, arcname) + print(f" Added: {arcname}") + + print(f"\n✅ Packaged to: {skill_filename}") + return skill_filename + + except Exception as e: + print(f"❌ Error creating .skill file: {e}") + return None + + +# ==================== Install Mode ==================== + +def read_agent_service_port(): + """Read Agent Service port from port file.""" + port_file = Path.home() / '.desirecore' / 'agent-service.port' + if not port_file.exists(): + return None + return port_file.read_text().strip() + + +def install_skill(skill_path, scope='global', agent_id=None): + """Install a skill to DesireCore via HTTP API.""" + skill_path = Path(skill_path).resolve() + skill_md = skill_path / 'SKILL.md' + + if not skill_md.exists(): + print(f"❌ Error: SKILL.md not found in {skill_path}") + return None + + # Validate first + print("🔍 Validating skill...") + valid, errors, warnings = validate_skill(skill_path) + if not valid: + print(f"❌ Validation failed:") + for e in errors: + print(f" ✗ {e}") + return None + if warnings: + for w in warnings: + print(f" ⚠ {w}") + print(f"✅ Validation passed\n") + + # Check Agent Service + port = read_agent_service_port() + if not port: + print("❌ Error: Agent Service not running (port file not found)") + print("\nFallback — install via file system:") + if scope == 'agent' and agent_id: + print(f" cp -r {skill_path} ~/.desirecore/agents/{agent_id}/skills/") + else: + print(f" cp -r {skill_path} ~/.desirecore/skills/") + return None + + content = skill_md.read_text() + skill_id = skill_path.name + + # Build API request + ctx = ssl.create_default_context() + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + + if scope == 'agent': + if not agent_id: + print("❌ Error: --agent-id is required for agent scope") + return None + url = f"https://127.0.0.1:{port}/api/agents/{agent_id}/skills" + payload = {"id": skill_id, "fullContent": content} + else: + url = f"https://127.0.0.1:{port}/api/skills" + payload = {"skillId": skill_id, "content": content} + + data = json.dumps(payload).encode('utf-8') + req = urllib.request.Request( + url, data=data, method='POST', + headers={'Content-Type': 'application/json'}, + ) + + try: + with urllib.request.urlopen(req, context=ctx) as resp: + result = json.loads(resp.read()) + print(f"✅ Installed '{skill_id}' ({scope} scope)") + return result + except urllib.error.HTTPError as e: + body = e.read().decode('utf-8', errors='replace') + print(f"❌ API error ({e.code}): {body}") + return None + except urllib.error.URLError as e: + print(f"❌ Connection error: {e.reason}") + print("Is Agent Service running?") + return None + + +# ==================== Main ==================== + +def main(): + parser = argparse.ArgumentParser( + description='Package or install a skill', + epilog='Examples:\n' + ' package_skill.py my-skill/ # Package as .skill ZIP\n' + ' package_skill.py my-skill/ ./dist # Package to specific dir\n' + ' package_skill.py my-skill/ --install # Install via API (global)\n' + ' package_skill.py my-skill/ --install --scope agent --agent-id abc123', + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + parser.add_argument('skill_path', help='Path to skill folder') + parser.add_argument('output_dir', nargs='?', default=None, + help='Output directory for .skill file (package mode only)') + parser.add_argument('--install', action='store_true', + help='Install via DesireCore API instead of packaging') + parser.add_argument('--scope', choices=['global', 'agent'], default='global', + help='Installation scope (default: global)') + parser.add_argument('--agent-id', + help='Agent ID (required when --scope agent)') + + args = parser.parse_args() + + if args.install: + print(f"📦 Installing skill: {args.skill_path} ({args.scope} scope)") + print() + result = install_skill(args.skill_path, args.scope, args.agent_id) + else: + print(f"📦 Packaging skill: {args.skill_path}") + if args.output_dir: + print(f" Output: {args.output_dir}") + print() + result = package_skill(args.skill_path, args.output_dir) + + sys.exit(0 if result else 1) + + +if __name__ == "__main__": + main() diff --git a/skills/skill-creator/scripts/quick_validate.py b/skills/skill-creator/scripts/quick_validate.py new file mode 100755 index 0000000..5e94dee --- /dev/null +++ b/skills/skill-creator/scripts/quick_validate.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +""" +Quick validation script for skills. + +Validates against DesireCore SKILL.md frontmatter schema. +Also accepts Claude Code basic format (name + description only). +""" + +import sys +import re +from pathlib import Path + +try: + import yaml +except ImportError: + print("Error: PyYAML is required. Install with: pip install pyyaml") + sys.exit(1) + + +# DesireCore 已知的顶层字段集合 +# 来源:lib/schemas/agent/skill-frontmatter.ts 的 properties 定义 +# Schema 设置了 additionalProperties: true,所以未知字段只警告不报错 +KNOWN_PROPERTIES = { + # 核心字段 + 'name', 'description', 'version', 'type', 'requires', + 'risk_level', 'status', 'tags', 'metadata', + # 功能控制 + 'disable-model-invocation', 'disable_model_invocation', + 'allowed-tools', 'user-invocable', 'argument-hint', + 'model', 'context', 'agent', + # 高级字段 + 'error_message', 'skill_package', 'input_schema', 'output_schema', + 'market', 'x_desirecore', 'json_output', + # Claude Code 兼容字段 + 'license', 'compatibility', +} + +VALID_TYPES = {'procedural', 'conversational', 'meta'} +VALID_RISK_LEVELS = {'low', 'medium', 'high'} +VALID_STATUSES = {'enabled', 'disabled'} +VALID_CONTEXTS = {'default', 'fork'} +SEMVER_RE = re.compile(r'^\d+\.\d+\.\d+$') +KEBAB_RE = re.compile(r'^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$') + + +def validate_skill(skill_path): + """ + Validate a skill directory. + + Returns: + (valid: bool, errors: list[str], warnings: list[str]) + """ + skill_path = Path(skill_path) + errors = [] + warnings = [] + + # Check SKILL.md exists + skill_md = skill_path / 'SKILL.md' + if not skill_md.exists(): + return False, ["SKILL.md not found"], [] + + content = skill_md.read_text() + if not content.startswith('---'): + return False, ["No YAML frontmatter found (must start with ---)"], [] + + # Extract frontmatter + match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL) + if not match: + return False, ["Invalid frontmatter format (missing closing ---)"], [] + + try: + frontmatter = yaml.safe_load(match.group(1)) + if not isinstance(frontmatter, dict): + return False, ["Frontmatter must be a YAML dictionary"], [] + except yaml.YAMLError as e: + return False, [f"Invalid YAML: {e}"], [] + + # === 必填字段 === + if 'description' not in frontmatter: + errors.append("Missing required field: 'description'") + + # === description 质量检查 === + description = frontmatter.get('description', '') + if isinstance(description, str): + desc_stripped = description.strip() + if desc_stripped and len(desc_stripped) < 10: + warnings.append("Description is very short — include 'Use when' trigger hints") + if len(desc_stripped) > 1024: + errors.append(f"Description too long ({len(desc_stripped)} chars, max 1024)") + if '<' in desc_stripped or '>' in desc_stripped: + warnings.append("Description contains angle brackets (< or >) — may cause parsing issues") + + # === name 格式检查 === + name = frontmatter.get('name', '') + if isinstance(name, str) and name.strip(): + n = name.strip() + if len(n) > 64: + errors.append(f"Name too long ({len(n)} chars, max 64)") + # kebab-case 检查仅当 name 是英文时 + if re.match(r'^[a-z0-9-]+$', n): + if not KEBAB_RE.match(n): + warnings.append(f"Name '{n}' starts/ends with hyphen or has consecutive hyphens") + + # === version 格式检查 === + version = frontmatter.get('version') + if version is not None and not SEMVER_RE.match(str(version)): + warnings.append(f"Version '{version}' is not valid semver (expected x.y.z)") + + # === 枚举字段检查 === + skill_type = frontmatter.get('type') + if skill_type is not None and skill_type not in VALID_TYPES: + errors.append(f"Invalid type: '{skill_type}'. Must be one of: {', '.join(sorted(VALID_TYPES))}") + + risk = frontmatter.get('risk_level') + if risk is not None and risk not in VALID_RISK_LEVELS: + errors.append(f"Invalid risk_level: '{risk}'. Must be one of: {', '.join(sorted(VALID_RISK_LEVELS))}") + + status = frontmatter.get('status') + if status is not None and status not in VALID_STATUSES: + errors.append(f"Invalid status: '{status}'. Must be one of: {', '.join(sorted(VALID_STATUSES))}") + + context = frontmatter.get('context') + if context is not None and context not in VALID_CONTEXTS: + errors.append(f"Invalid context: '{context}'. Must be one of: {', '.join(sorted(VALID_CONTEXTS))}") + + # === 未知字段警告(不阻断) === + unknown = set(frontmatter.keys()) - KNOWN_PROPERTIES + if unknown: + warnings.append(f"Unknown fields (will be preserved): {', '.join(sorted(unknown))}") + + valid = len(errors) == 0 + return valid, errors, warnings + + +def main(): + if len(sys.argv) != 2: + print("Usage: quick_validate.py ") + print("\nValidates SKILL.md frontmatter against DesireCore schema.") + print("Also accepts Claude Code basic format (name + description).") + sys.exit(1) + + skill_path = sys.argv[1] + valid, errors, warnings = validate_skill(skill_path) + + if valid and not warnings: + print(f"✅ Skill is valid!") + elif valid and warnings: + print(f"✅ Skill is valid (with warnings):") + for w in warnings: + print(f" ⚠ {w}") + else: + print(f"❌ Validation failed:") + for e in errors: + print(f" ✗ {e}") + if warnings: + print(f" Warnings:") + for w in warnings: + print(f" ⚠ {w}") + + sys.exit(0 if valid else 1) + + +if __name__ == "__main__": + main()