diff --git a/.claude/skills/toolDev.zip b/.claude/skills/toolDev.zip new file mode 100644 index 00000000..5275c25c Binary files /dev/null and b/.claude/skills/toolDev.zip differ diff --git a/.claude/skills/toolDev/SKILL.md b/.claude/skills/toolDev/SKILL.md new file mode 100644 index 00000000..28627081 --- /dev/null +++ b/.claude/skills/toolDev/SKILL.md @@ -0,0 +1,181 @@ +--- +name: tool-dev +description: 专门用于开发 FastGPT 系统工具和工具集的技能,包含 Zod 类型安全验证、共享配置管理和完整测试工作流。适用于创建新的 FastGPT 工具、开发包含子工具的工具集、或实现带有正确配置和测试的 API 集成。 +license: MIT +category: development +author: FastGPT Team +version: 2.0.0 +--- + +# FastGPT 工具开发技能 + +为开发 FastGPT 系统工具提供简化的工作流程,包括类型安全、正确的配置管理和自动化测试。 + +## 何时使用此技能 + +在进行 FastGPT 工具开发任务时使用此技能: +- 创建新的独立工具或工具集 +- 实现带有适当身份验证的 API 集成 +- 开发具有 Zod 验证和 TypeScript 类型的工具 +- 在工具集子工具之间设置共享配置 +- 为工具功能编写全面的测试 + +## 核心能力 + +- **类型安全开发**: Zod schema 验证 + TypeScript 类型推导 +- **配置管理**: 工具集内共享密钥配置,支持版本控制 +- **系统化测试**: 自动化测试生成和验证 +- **代码质量**: 内置代码审查和优化步骤 +- **最佳实践**: 遵循 FastGPT 设计模式和规范 + +--- + +## 开发工作流 + +开发 FastGPT 工具时遵循此 4 阶段工作流程。每个阶段都有明确的目标、操作和质量标准。 + +### 阶段 1: 需求与设计 + +**目标**: 创建全面的需求文档 + +**操作步骤**: +1. 查看 `references/design_spec.md` 中的现有设计模式 +2. 定义工具/工具集的范围和功能 +3. 使用 Zod schemas 指定输入/输出配置 +4. 记录 API 要求和测试凭证 +5. 使用 `references/requirement_template.md` 作为起点 + +**质量标准**: +- ✅ 所有功能清晰定义 +- ✅ 输入/输出类型已指定 +- ✅ 边界情况和错误场景已记录 + +**交付物**: 完整的设计文档(参见 `references/requirement_template.md`) + +--- + +### 阶段 2: 实现 + +**目标**: 按照 FastGPT 模式开发工具 + +**操作步骤**: +1. 创建目录结构(`tool/` 或 `toolset/children/`) +2. 实现 `config.ts`,包含正确的版本列表 +3. 开发 `src/index.ts`,包含 Zod 验证的业务逻辑 +4. 如需要,添加共享模块(`client.ts`、`utils.ts`) + +**代码规范**: +- 采用 camelCase 命名规范 +- 类型导入: `import { type Foo } from 'bar'` +- 最小化错误处理(让框架处理常见情况) +- 函数简洁、职责单一 + +**质量标准**: +- ✅ TypeScript 编译无错误 +- ✅ 遵循现有项目模式 +- ✅ 配置结构正确 + +--- + +### 阶段 3: 测试与验证 + +**目标**: 通过全面测试确保可靠性 + +**操作步骤**: +1. 安装依赖: `bun install` +2. 在 `test/index.test.ts` 中编写测试用例 +3. 运行测试套件: `bun test` +4. 修复失败的测试和边界情况 +5. 验证 TypeScript: `bun run tsc --noEmit` + +**测试覆盖**: +- 输入验证(Zod schema 边界情况) +- 业务逻辑正确性 +- 错误处理场景 +- 资源清理(如适用) + +**质量标准**: +- ✅ 所有测试通过 +- ✅ 无 TypeScript 错误 +- ✅ 构建成功: `bun run build` + +--- + +### 阶段 4: 审查与优化 + +**目标**: 完成前优化代码质量 + +**操作步骤**: +1. 代码审查,寻找简化机会 +2. 删除未使用的代码和依赖 +3. 优化性能瓶颈 +4. 最终集成测试 +5. 更新文档 + +**质量标准**: +- ✅ 代码清晰且可维护 +- ✅ 无不必要的复杂性 +- ✅ 最终测试运行成功 + +--- + +## 快速参考 + +### 常用命令 +```bash +# 安装依赖 +bun install + +# 运行测试 +bun test + +# 类型检查 +bun run tsc --noEmit + +# 构建 +bun run build + +# 测试特定文件 +bun test test/index.test.ts +``` + +### 文件模板 +访问详细的模板和规范: +- **需求模板**: `references/requirement_template.md` +- **完整设计规范**: `references/design_spec.md` +- **工具配置示例**: design_spec.md 第 4.1 节 +- **工具集配置示例**: design_spec.md 第 5.1 节 +- **Zod 验证模式**: design_spec.md 第 7.2 节 +- **测试结构**: design_spec.md 第 9.1 节 + +### 关键设计原则 +1. **关注点分离**: 配置 ≠ 逻辑 ≠ 工具 +2. **类型安全**: Zod 验证 → TypeScript 类型推导 +3. **共享配置**: 工具集密钥由子工具继承 +4. **单一职责**: 一个工具 = 一个功能 +5. **快速失败**: 让框架处理常见错误 + +--- + +## 绑定资源 + +### Scripts 目录 +- `init_skill.py`: 初始化具有正确结构的新 skill +- `package_skill.py`: 验证并打包 skill 为可分发的 zip 文件 +- `quick_validate.py`: 快速验证 skill 结构 + +### References 目录 +- `design_spec.md`: 完整的 FastGPT 工具设计规范(10 个章节) +- `requirement_template.md`: 创建工具需求文档的模板 + +### 使用模式 +1. 查看详细技术规范 → 阅读 `references/design_spec.md` +2. 创建新工具需求 → 使用 `references/requirement_template.md` +3. 管理 skill → 使用 `scripts/*.py` 工具 + +--- + +## 相关文档 +- 工具类型: `@tool/type/tool` 中的 `ToolTypeEnum` +- 配置类型: `@tool/type` 中的 `ToolConfigType`、`ToolSetConfigType` +- FastGPT 类型: `WorkflowIOValueTypeEnum`、`FlowNodeInputTypeEnum` diff --git a/.claude/agents/toolCodeAgent.md b/.claude/skills/toolDev/references/design_spec.md similarity index 90% rename from .claude/agents/toolCodeAgent.md rename to .claude/skills/toolDev/references/design_spec.md index d436f681..49e43564 100644 --- a/.claude/agents/toolCodeAgent.md +++ b/.claude/skills/toolDev/references/design_spec.md @@ -1,59 +1,3 @@ ---- -name: toolCodeAgent -description: 当用户需要开发一个工具/工具集时调用 -model: sonnet ---- - -# 工作流程 - -开发新工具时,参考该流程完成任务。 - -1. 参考 DESIGN.md 已有信息,完成需求文档编写。 -2. 审核需求文档,检查是否有遗漏。 -3. 依据需求文档完成代码开发。 -4. 依据需求文档,编写测试案例。 -5. 安装对应依赖后,进行测试并纠错问题。 -6. 进行代码 Review,简化无用代码。 -7. 最后再进行一次测试,保证代码可用。 - -## 需求文档模板 - -``` -# xxx 设计文档 - -## 参考信息 - -这里可能会给一些参考文档,测试密钥等。 - -## 功能描述 - -实现 xxxx。 - -* 需要一个 tool/toolset -* 包含 n 个工具,具体如下: - -1. xxx -2. xxx - -## 目录结构 - -## 输入输出配置 - -## 代码示例 - -## 测试方案 - -## 可能存在的问题和重点检查内容 -``` - -## 测试流程 - -1. 检查 TS 是否有错误 -2. 检查是否可以正常运行和build -3. 检查测试案例是否通过 - ---- - # FastGPT 系统工具设计规范 本文档定义了 FastGPT Plugin 系统中工具(Tool)和工具集(ToolSet)的统一设计规范,基于 Redis 工具集的最佳实践总结。 @@ -191,7 +135,7 @@ export default defineTool({ required: true, valueType: WorkflowIOValueTypeEnum.string, renderTypeList: [FlowNodeInputTypeEnum.input, FlowNodeInputTypeEnum.reference], - toolDescription: 'Input description for AI', // 可选,当该变量需要由 LLM 模型动态生成时才存在 + toolDescription: 'Input description for AI', // 可选,当该变量需要由 LLM 模型动态生成时才存在 defaultValue: 'default value', // 可选 placeholder: 'Enter value...', // 可选 maxLength: 1000 // 可选,字符串最大长度 @@ -361,7 +305,7 @@ export const OutputType = z.object({ success: z.boolean() }); -// 工具回调函数(简单版,由上层自动处理错误) +// 工具回调函数(简单版,由上层自动处理错误) export async function tool({ connectionString, apiKey, @@ -376,7 +320,7 @@ export async function tool({ }; } -// 工具回调函数(需处理错误/关闭链接) +// 工具回调函数(需处理错误/关闭链接) export async function tool({ connectionString, apiKey, @@ -415,7 +359,7 @@ export async function tool({ ### 6.1 客户端模块 (client.ts) -用于封装工具集中的子工具共享连接逻辑,比如数据库实例,请求实例封装。 +用于封装工具集中的子工具共享连接逻辑,比如数据库实例,请求实例封装。 ```typescript import SomeClient from 'some-library'; @@ -517,9 +461,9 @@ export async function withTimeout( ### 6.3 错误处理 (error.ts) -对于一个工具集来说,如果统一用的是同一家的 API,通常会有错误映射码,可以在该文件中声明,并统一做错误映射处理。如果是常见的错误,则无需该文件和报错捕获,直接抛错即可。 +对于一个工具集来说,如果统一用的是同一家的 API,通常会有错误映射码,可以在该文件中声明,并统一做错误映射处理。如果是常见的错误,则无需该文件和报错捕获,直接抛错即可。 -如果不需要做错误映射, src/index.ts 逻辑处理中,无需 try catch,直接由上层自动捕获错误即可。 +如果不需要做错误映射, src/index.ts 逻辑处理中,无需 try catch,直接由上层自动捕获错误即可。 ```ts const errorMap = { @@ -724,7 +668,7 @@ secretInputConfig: [ ### 8.2 输入验证 -如果用户未制定特殊校验,则无需增加额外自定义校验,使用下面常见的几种校验即可: +如果用户未制定特殊校验,则无需增加额外自定义校验,使用下面常见的几种校验即可: ```typescript import { z } from 'zod'; @@ -732,7 +676,7 @@ import { z } from 'zod'; export const InputType = z.object({ // 字符串验证 name: z.string() - .nonempty('Name cannot be empty') // 如果是长度限制:minLength(1, 'Name cannot be empty') + .nonempty('Name cannot be empty') // 如果是长度限制:minLength(1, 'Name cannot be empty') .maxLength(100, 'Name too long'), // 数字验证 @@ -898,7 +842,6 @@ describe('ToolName Tests', () => { ## 10. 代码规范 * 采用小驼峰命名规范。 -* 类型引入需要强制什么 type,例如 import {type xx} from xxx; +* 类型引入需要强制什么 type,例如 import {type xx} from xxx; * 代码精简。 -* 简单内容无需额外报错捕获,外层已做处理。 - +* 简单内容无需额外报错捕获,外层已做处理。 diff --git a/.claude/skills/toolDev/references/requirement_template.md b/.claude/skills/toolDev/references/requirement_template.md new file mode 100644 index 00000000..2bfb4b72 --- /dev/null +++ b/.claude/skills/toolDev/references/requirement_template.md @@ -0,0 +1,86 @@ +# [Tool Name] Design Document + +## 🔑 Reference Information +- API Documentation: [URL] +- Test Credentials: [Keys/Tokens] +- Related Issues/PRs: [Links] + +## 🎯 Functionality Overview +Implement [clear description of what the tool does]. + +**Type**: +- [ ] Independent Tool +- [ ] ToolSet with N child tools + +**Child Tools** (if toolset): +1. Tool 1: [Description] +2. Tool 2: [Description] +3. Tool N: [Description] + +## 📁 Directory Structure +``` +modules/tool/packages/[toolName]/ +├── index.ts +├── config.ts +├── package.json +├── src/ +│ └── index.ts +└── test/ + └── index.test.ts +``` + +## 🔧 Configuration Details + +### Secret Inputs (if applicable) +- `apiKey`: API authentication key (required) +- `connectionString`: Service connection URL (optional) + +### Input Parameters +| Key | Type | Required | Description | AI Description | +|-----|------|----------|-------------|----------------| +| `input1` | `string` | ✓ | User input description | "The text to process" | +| `count` | `number` | | Number of items (default: 10) | "How many items to return" | + +### Output Parameters +| Key | Type | Description | +|-----|------|-------------| +| `result` | `string` | Processed result | +| `success` | `boolean` | Operation status | + +## 💻 Implementation Approach + +### Zod Schema +```typescript +export const InputType = z.object({ + apiKey: z.string().optional(), + input1: z.string().min(1, 'Cannot be empty'), + count: z.number().default(10) +}); + +export const OutputType = z.object({ + result: z.string(), + success: z.boolean() +}); +``` + +### Core Logic +```typescript +export async function tool(input: z.infer) { + // Implementation logic + const result = await processData(input); + return { result, success: true }; +} +``` + +## 🧪 Test Strategy +- **Unit Tests**: Input validation, business logic +- **Integration Tests**: API calls, database operations +- **Edge Cases**: Empty inputs, invalid data, timeouts +- **Error Scenarios**: Network failures, authentication errors + +## ⚠️ Potential Issues & Review Points +- [ ] Rate limiting considerations +- [ ] Timeout handling for long operations +- [ ] Resource cleanup (connections, files) +- [ ] Error message clarity +- [ ] Security: No sensitive data in logs diff --git a/.claude/skills/toolDev/scripts/__pycache__/quick_validate.cpython-314.pyc b/.claude/skills/toolDev/scripts/__pycache__/quick_validate.cpython-314.pyc new file mode 100644 index 00000000..26436188 Binary files /dev/null and b/.claude/skills/toolDev/scripts/__pycache__/quick_validate.cpython-314.pyc differ diff --git a/.claude/skills/toolDev/scripts/init_skill.py b/.claude/skills/toolDev/scripts/init_skill.py new file mode 100755 index 00000000..329ad4e5 --- /dev/null +++ b/.claude/skills/toolDev/scripts/init_skill.py @@ -0,0 +1,303 @@ +#!/usr/bin/env python3 +""" +Skill Initializer - Creates a new skill from template + +Usage: + init_skill.py --path + +Examples: + init_skill.py my-new-skill --path skills/public + init_skill.py my-api-helper --path skills/private + init_skill.py custom-skill --path /custom/location +""" + +import sys +from pathlib import Path + + +SKILL_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] + +## Structuring This Skill + +[TODO: Choose the structure that best fits this skill's purpose. Common patterns: + +**1. Workflow-Based** (best for sequential processes) +- Works well when there are clear step-by-step procedures +- Example: DOCX skill with "Workflow Decision Tree" → "Reading" → "Creating" → "Editing" +- Structure: ## Overview → ## Workflow Decision Tree → ## Step 1 → ## Step 2... + +**2. Task-Based** (best for tool collections) +- Works well when the skill offers different operations/capabilities +- Example: PDF skill with "Quick Start" → "Merge PDFs" → "Split PDFs" → "Extract Text" +- Structure: ## Overview → ## Quick Start → ## Task Category 1 → ## Task Category 2... + +**3. Reference/Guidelines** (best for standards or specifications) +- Works well for brand guidelines, coding standards, or requirements +- Example: Brand styling with "Brand Guidelines" → "Colors" → "Typography" → "Features" +- Structure: ## Overview → ## Guidelines → ## Specifications → ## Usage... + +**4. Capabilities-Based** (best for integrated systems) +- Works well when the skill provides multiple interrelated features +- Example: Product Management with "Core Capabilities" → numbered capability list +- Structure: ## Overview → ## Core Capabilities → ### 1. Feature → ### 2. Feature... + +Patterns can be mixed and matched as needed. Most skills combine patterns (e.g., start with task-based, add workflow for complex operations). + +Delete this entire "Structuring This Skill" section when done - it's just guidance.] + +## [TODO: Replace with the first main section based on chosen structure] + +[TODO: Add content here. See examples in existing skills: +- Code samples for technical skills +- Decision trees for complex workflows +- Concrete examples with realistic user requests +- References to scripts/templates/references as needed] + +## Resources + +This skill includes example resource directories that demonstrate how to organize different types of bundled resources: + +### scripts/ +Executable code (Python/Bash/etc.) that can be run directly to perform specific operations. + +**Examples from other skills:** +- PDF skill: `fill_fillable_fields.py`, `extract_form_field_info.py` - utilities for PDF manipulation +- DOCX skill: `document.py`, `utilities.py` - Python modules for document processing + +**Appropriate for:** Python scripts, shell scripts, or any executable code that performs automation, data processing, or specific operations. + +**Note:** Scripts may be executed without loading into context, but can still be read by Claude for patching or environment adjustments. + +### references/ +Documentation and reference material intended to be loaded into context to inform Claude's process and thinking. + +**Examples from other skills:** +- Product management: `communication.md`, `context_building.md` - detailed workflow guides +- BigQuery: API reference documentation and query examples +- Finance: Schema documentation, company policies + +**Appropriate for:** In-depth documentation, API references, database schemas, comprehensive guides, or any detailed information that Claude should reference while working. + +### assets/ +Files not intended to be loaded into context, but rather used within the output Claude produces. + +**Examples from other skills:** +- Brand styling: PowerPoint template files (.pptx), logo files +- Frontend builder: HTML/React boilerplate project directories +- Typography: Font files (.ttf, .woff2) + +**Appropriate for:** Templates, boilerplate code, document templates, images, icons, fonts, or any files meant to be copied or used in the final output. + +--- + +**Any unneeded directories can be deleted.** Not every skill requires all three types of resources. +""" + +EXAMPLE_SCRIPT = '''#!/usr/bin/env python3 +""" +Example helper script for {skill_name} + +This is a placeholder script that can be executed directly. +Replace with actual implementation or delete if not needed. + +Example real scripts from other skills: +- pdf/scripts/fill_fillable_fields.py - Fills PDF form fields +- pdf/scripts/convert_pdf_to_images.py - Converts PDF pages to images +""" + +def main(): + print("This is an example script for {skill_name}") + # TODO: Add actual script logic here + # This could be data processing, file conversion, API calls, etc. + +if __name__ == "__main__": + main() +''' + +EXAMPLE_REFERENCE = """# Reference Documentation for {skill_title} + +This is a placeholder for detailed reference documentation. +Replace with actual reference content or delete if not needed. + +Example real reference docs from other skills: +- product-management/references/communication.md - Comprehensive guide for status updates +- product-management/references/context_building.md - Deep-dive on gathering context +- bigquery/references/ - API references and query examples + +## When Reference Docs Are Useful + +Reference docs are ideal for: +- Comprehensive API documentation +- Detailed workflow guides +- Complex multi-step processes +- Information too lengthy for main SKILL.md +- Content that's only needed for specific use cases + +## Structure Suggestions + +### API Reference Example +- Overview +- Authentication +- Endpoints with examples +- Error codes +- Rate limits + +### Workflow Guide Example +- Prerequisites +- Step-by-step instructions +- Common patterns +- Troubleshooting +- Best practices +""" + +EXAMPLE_ASSET = """# Example Asset File + +This placeholder represents where asset files would be stored. +Replace with actual asset files (templates, images, fonts, etc.) or delete if not needed. + +Asset files are NOT intended to be loaded into context, but rather used within +the output Claude produces. + +Example asset files from other skills: +- Brand guidelines: logo.png, slides_template.pptx +- Frontend builder: hello-world/ directory with HTML/React boilerplate +- Typography: custom-font.ttf, font-family.woff2 +- Data: sample_data.csv, test_dataset.json + +## Common Asset Types + +- Templates: .pptx, .docx, boilerplate directories +- Images: .png, .jpg, .svg, .gif +- Fonts: .ttf, .otf, .woff, .woff2 +- Boilerplate code: Project directories, starter files +- Icons: .ico, .svg +- Data files: .csv, .json, .xml, .yaml + +Note: This is a text placeholder. Actual assets can be any file type. +""" + + +def title_case_skill_name(skill_name): + """Convert hyphenated skill name to Title Case for display.""" + return ' '.join(word.capitalize() for word in skill_name.split('-')) + + +def init_skill(skill_name, path): + """ + Initialize a new skill directory with template SKILL.md. + + Args: + skill_name: Name of the skill + path: Path where the skill directory should be created + + Returns: + Path to created skill directory, or None if error + """ + # Determine skill directory path + skill_dir = Path(path).resolve() / skill_name + + # Check if directory already exists + 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) + skill_content = SKILL_TEMPLATE.format( + skill_name=skill_name, + skill_title=skill_title + ) + + skill_md_path = skill_dir / 'SKILL.md' + try: + skill_md_path.write_text(skill_content) + print("✅ Created SKILL.md") + except Exception as e: + print(f"❌ Error creating SKILL.md: {e}") + return None + + # Create resource directories with example files + try: + # Create scripts/ directory with example script + 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") + + # Create references/ directory with example reference doc + references_dir = skill_dir / 'references' + references_dir.mkdir(exist_ok=True) + example_reference = references_dir / 'api_reference.md' + example_reference.write_text(EXAMPLE_REFERENCE.format(skill_title=skill_title)) + print("✅ Created references/api_reference.md") + + # Create assets/ directory with example asset placeholder + 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 next steps + print(f"\n✅ Skill '{skill_name}' initialized successfully at {skill_dir}") + print("\nNext steps:") + print("1. Edit SKILL.md to complete the TODO items and update the description") + print("2. Customize or delete the example files in scripts/, references/, and assets/") + print("3. Run the validator when ready to check the skill structure") + + return skill_dir + + +def main(): + if len(sys.argv) < 4 or sys.argv[2] != '--path': + print("Usage: init_skill.py --path ") + print("\nSkill name requirements:") + print(" - Hyphen-case identifier (e.g., 'data-analyzer')") + print(" - Lowercase letters, digits, and hyphens only") + print(" - Max 40 characters") + print(" - Must match directory name exactly") + print("\nExamples:") + print(" init_skill.py my-new-skill --path skills/public") + print(" init_skill.py my-api-helper --path skills/private") + print(" init_skill.py custom-skill --path /custom/location") + sys.exit(1) + + skill_name = sys.argv[1] + path = sys.argv[3] + + print(f"🚀 Initializing skill: {skill_name}") + print(f" Location: {path}") + print() + + result = init_skill(skill_name, path) + + if result: + sys.exit(0) + else: + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/.claude/skills/toolDev/scripts/package_skill.py b/.claude/skills/toolDev/scripts/package_skill.py new file mode 100755 index 00000000..3ee8e8e9 --- /dev/null +++ b/.claude/skills/toolDev/scripts/package_skill.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +""" +Skill Packager - Creates a distributable zip file of a skill folder + +Usage: + python utils/package_skill.py [output-directory] + +Example: + python utils/package_skill.py skills/public/my-skill + python utils/package_skill.py skills/public/my-skill ./dist +""" + +import sys +import zipfile +from pathlib import Path +from quick_validate import validate_skill + + +def package_skill(skill_path, output_dir=None): + """ + Package a skill folder into a zip file. + + Args: + skill_path: Path to the skill folder + output_dir: Optional output directory for the zip file (defaults to current directory) + + Returns: + Path to the created zip file, or None if error + """ + skill_path = Path(skill_path).resolve() + + # Validate skill folder exists + 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 + + # Validate SKILL.md exists + skill_md = skill_path / "SKILL.md" + if not skill_md.exists(): + print(f"❌ Error: SKILL.md not found in {skill_path}") + return None + + # Run validation before packaging + print("🔍 Validating skill...") + valid, message = validate_skill(skill_path) + if not valid: + print(f"❌ Validation failed: {message}") + print(" Please fix the validation errors before packaging.") + return None + print(f"✅ {message}\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() + + zip_filename = output_path / f"{skill_name}.zip" + + # Create the zip file + try: + with zipfile.ZipFile(zip_filename, 'w', zipfile.ZIP_DEFLATED) as zipf: + # Walk through the skill directory + for file_path in skill_path.rglob('*'): + if file_path.is_file(): + # Calculate the relative path within the zip + arcname = file_path.relative_to(skill_path.parent) + zipf.write(file_path, arcname) + print(f" Added: {arcname}") + + print(f"\n✅ Successfully packaged skill to: {zip_filename}") + return zip_filename + + except Exception as e: + print(f"❌ Error creating zip file: {e}") + return None + + +def main(): + if len(sys.argv) < 2: + print("Usage: python utils/package_skill.py [output-directory]") + print("\nExample:") + print(" python utils/package_skill.py skills/public/my-skill") + print(" python utils/package_skill.py skills/public/my-skill ./dist") + sys.exit(1) + + skill_path = sys.argv[1] + output_dir = sys.argv[2] if len(sys.argv) > 2 else None + + print(f"📦 Packaging skill: {skill_path}") + if output_dir: + print(f" Output directory: {output_dir}") + print() + + result = package_skill(skill_path, output_dir) + + if result: + sys.exit(0) + else: + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/.claude/skills/toolDev/scripts/quick_validate.py b/.claude/skills/toolDev/scripts/quick_validate.py new file mode 100755 index 00000000..6fa6c636 --- /dev/null +++ b/.claude/skills/toolDev/scripts/quick_validate.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +""" +Quick validation script for skills - minimal version +""" + +import sys +import os +import re +from pathlib import Path + +def validate_skill(skill_path): + """Basic validation of a skill""" + skill_path = Path(skill_path) + + # Check SKILL.md exists + skill_md = skill_path / 'SKILL.md' + if not skill_md.exists(): + return False, "SKILL.md not found" + + # Read and validate frontmatter + content = skill_md.read_text() + if not content.startswith('---'): + return False, "No YAML frontmatter found" + + # Extract frontmatter + match = re.match(r'^---\n(.*?)\n---', content, re.DOTALL) + if not match: + return False, "Invalid frontmatter format" + + frontmatter = match.group(1) + + # Check required fields + if 'name:' not in frontmatter: + return False, "Missing 'name' in frontmatter" + if 'description:' not in frontmatter: + return False, "Missing 'description' in frontmatter" + + # Extract name for validation + name_match = re.search(r'name:\s*(.+)', frontmatter) + if name_match: + name = name_match.group(1).strip() + # Check naming convention (hyphen-case: lowercase with hyphens) + if not re.match(r'^[a-z0-9-]+$', name): + return False, f"Name '{name}' should be hyphen-case (lowercase letters, digits, and hyphens only)" + if name.startswith('-') or name.endswith('-') or '--' in name: + return False, f"Name '{name}' cannot start/end with hyphen or contain consecutive hyphens" + + # Extract and validate description + desc_match = re.search(r'description:\s*(.+)', frontmatter) + if desc_match: + description = desc_match.group(1).strip() + # Check for angle brackets + if '<' in description or '>' in description: + return False, "Description cannot contain angle brackets (< or >)" + + return True, "Skill is valid!" + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Usage: python quick_validate.py ") + sys.exit(1) + + valid, message = validate_skill(sys.argv[1]) + print(message) + sys.exit(0 if valid else 1) \ No newline at end of file