feat: Add 8 exciting skills from market research - one-click project,… #9
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Skill Validation | |
| on: | |
| push: | |
| paths: | |
| - '.trae/skills/**' | |
| pull_request: | |
| paths: | |
| - '.trae/skills/**' | |
| workflow_dispatch: | |
| jobs: | |
| validate-skills: | |
| name: Validate All Skills | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| - name: Validate skill files | |
| run: | | |
| python -c " | |
| import os | |
| import re | |
| import sys | |
| from pathlib import Path | |
| skills_dir = Path('.trae/skills') | |
| errors = [] | |
| warnings = [] | |
| skill_count = 0 | |
| required_fields = ['name', 'description', 'layer', 'role', 'version'] | |
| for skill_path in skills_dir.glob('**/SKILL.md'): | |
| skill_count += 1 | |
| skill_name = str(skill_path.parent.relative_to(skills_dir)) | |
| with open(skill_path, 'r', encoding='utf-8') as f: | |
| content = f.read() | |
| if not content.startswith('---'): | |
| errors.append(f'{skill_name}: Missing frontmatter') | |
| continue | |
| frontmatter_match = re.search(r'^---\s*\n(.*?)\n---', content, re.DOTALL) | |
| if not frontmatter_match: | |
| errors.append(f'{skill_name}: Invalid frontmatter format') | |
| continue | |
| frontmatter = frontmatter_match.group(1) | |
| for field in required_fields: | |
| if f'{field}:' not in frontmatter: | |
| errors.append(f'{skill_name}: Missing required field \"{field}\"') | |
| main_content = content[frontmatter_match.end():].strip() | |
| if len(main_content) < 200: | |
| warnings.append(f'{skill_name}: Content is short ({len(main_content)} chars)') | |
| if 'description:' in frontmatter: | |
| desc_match = re.search(r'description:\s*[\"|\']?(.+?)[\"|\']?\s*$', frontmatter, re.MULTILINE) | |
| if desc_match: | |
| desc = desc_match.group(1).strip() | |
| if not desc: | |
| errors.append(f'{skill_name}: Empty description') | |
| print(f'✓ {skill_name}') | |
| print(f'\n📊 Validation Summary:') | |
| print(f' Total skills: {skill_count}') | |
| print(f' Errors: {len(errors)}') | |
| print(f' Warnings: {len(warnings)}') | |
| if errors: | |
| print('\n❌ Errors:') | |
| for e in errors: | |
| print(f' - {e}') | |
| if warnings: | |
| print('\n⚠️ Warnings:') | |
| for w in warnings: | |
| print(f' - {w}') | |
| if errors: | |
| sys.exit(1) | |
| print('\n✅ All skills validated successfully!') | |
| " | |
| - name: Check skill layers | |
| run: | | |
| python -c " | |
| import os | |
| from pathlib import Path | |
| from collections import Counter | |
| skills_dir = Path('.trae/skills') | |
| layers = Counter() | |
| for skill_path in skills_dir.glob('**/SKILL.md'): | |
| with open(skill_path, 'r', encoding='utf-8') as f: | |
| content = f.read() | |
| for line in content.split('\n'): | |
| if line.startswith('layer:'): | |
| layer = line.split(':', 1)[1].strip().strip('\"').strip(\"'\") | |
| layers[layer] += 1 | |
| break | |
| print('📊 Skills by Layer:') | |
| print('-' * 40) | |
| for layer, count in sorted(layers.items(), key=lambda x: -x[1]): | |
| print(f' {layer}: {count}') | |
| print('-' * 40) | |
| print(f' Total: {sum(layers.values())}') | |
| " | |
| - name: Generate skill report | |
| run: | | |
| echo "## Skill Validation Report" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| skill_count=$(find .trae/skills -name "SKILL.md" | wc -l) | |
| echo "✅ **$skill_count skills validated successfully**" >> $GITHUB_STEP_SUMMARY |