Initial commit
This commit is contained in:
12
.claude-plugin/plugin.json
Normal file
12
.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "skill-reviewer-and-enhancer",
|
||||||
|
"description": "This skill should be used when reviewing, auditing, or improving existing Claude Code skills to ensure they follow Anthropic best practices, have proper structure, use current domain-specific patterns, and include all necessary resources. It analyzes skill quality, identifies gaps, suggests improvements, and can automatically enhance skills with updated best practices. Trigger terms include review skill, audit skill, improve skill, enhance skill, update skill, check skill quality, skill best pra",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "Hope Overture",
|
||||||
|
"email": "support@worldbuilding-app-skills.dev"
|
||||||
|
},
|
||||||
|
"skills": [
|
||||||
|
"./skills"
|
||||||
|
]
|
||||||
|
}
|
||||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# skill-reviewer-and-enhancer
|
||||||
|
|
||||||
|
This skill should be used when reviewing, auditing, or improving existing Claude Code skills to ensure they follow Anthropic best practices, have proper structure, use current domain-specific patterns, and include all necessary resources. It analyzes skill quality, identifies gaps, suggests improvements, and can automatically enhance skills with updated best practices. Trigger terms include review skill, audit skill, improve skill, enhance skill, update skill, check skill quality, skill best pra
|
||||||
65
plugin.lock.json
Normal file
65
plugin.lock.json
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"$schema": "internal://schemas/plugin.lock.v1.json",
|
||||||
|
"pluginId": "gh:hopeoverture/worldbuilding-app-skills:plugins/skill-reviewer-and-enhancer",
|
||||||
|
"normalized": {
|
||||||
|
"repo": null,
|
||||||
|
"ref": "refs/tags/v20251128.0",
|
||||||
|
"commit": "eaff913bfc6d7aea2a0bc0de461f143c102eed25",
|
||||||
|
"treeHash": "9e97e02a8ad99de342c0e73fe940ac174f06492f72159d76bb7ec1099df8b6ff",
|
||||||
|
"generatedAt": "2025-11-28T10:17:35.183183Z",
|
||||||
|
"toolVersion": "publish_plugins.py@0.2.0"
|
||||||
|
},
|
||||||
|
"origin": {
|
||||||
|
"remote": "git@github.com:zhongweili/42plugin-data.git",
|
||||||
|
"branch": "master",
|
||||||
|
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
|
||||||
|
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
|
||||||
|
},
|
||||||
|
"manifest": {
|
||||||
|
"name": "skill-reviewer-and-enhancer",
|
||||||
|
"description": "This skill should be used when reviewing, auditing, or improving existing Claude Code skills to ensure they follow Anthropic best practices, have proper structure, use current domain-specific patterns, and include all necessary resources. It analyzes skill quality, identifies gaps, suggests improvements, and can automatically enhance skills with updated best practices. Trigger terms include review skill, audit skill, improve skill, enhance skill, update skill, check skill quality, skill best pra",
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"path": "README.md",
|
||||||
|
"sha256": "e188041b22d94d17f144d52cc3e23d413a34d983317347b984dbbcba576e433c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": ".claude-plugin/plugin.json",
|
||||||
|
"sha256": "2eb63573a840dede6b491a1db499a6c1788b81a0ba0ccb5261b0dd1989905138"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/skill-reviewer-and-enhancer/SKILL.md",
|
||||||
|
"sha256": "58f8f8f485693d31701dc000bbbdcf13c2eddaf4a6b5f5a9b939d01c40b200b2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/skill-reviewer-and-enhancer/references/anthropic-skill-standards.md",
|
||||||
|
"sha256": "1c85d8cb088efef2887deb0dd9079ec94e3eb17915b0e48e0fca194882e297af"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/skill-reviewer-and-enhancer/references/nextjs-best-practices.md",
|
||||||
|
"sha256": "706653b1dd7a430df55b992466e5368ad712a38c591239fa6374a031aca71fa3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/skill-reviewer-and-enhancer/references/testing-best-practices.md",
|
||||||
|
"sha256": "29a44303987d4e8016156ecb9678375f4744c0dd2492df9b3be4ab8fd1cb17d1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/skill-reviewer-and-enhancer/scripts/analyze_skill_structure.py",
|
||||||
|
"sha256": "70b800a707e3e76f60c0fe4691efa781faa48f393a0d381d38efe3be7f5c3b4b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "skills/skill-reviewer-and-enhancer/assets/review-report-template.md",
|
||||||
|
"sha256": "bb0b3cb0403a19d80aef58bf450ffd0273b2fbd5501efb8152b150e8554596ff"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dirSha256": "9e97e02a8ad99de342c0e73fe940ac174f06492f72159d76bb7ec1099df8b6ff"
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"scannedAt": null,
|
||||||
|
"scannerVersion": null,
|
||||||
|
"flags": []
|
||||||
|
}
|
||||||
|
}
|
||||||
610
skills/skill-reviewer-and-enhancer/SKILL.md
Normal file
610
skills/skill-reviewer-and-enhancer/SKILL.md
Normal file
@@ -0,0 +1,610 @@
|
|||||||
|
---
|
||||||
|
name: skill-reviewer-and-enhancer
|
||||||
|
description: This skill should be used when reviewing, auditing, or improving existing Claude Code skills to ensure they follow Anthropic best practices, have proper structure, use current domain-specific patterns, and include all necessary resources. It analyzes skill quality, identifies gaps, suggests improvements, and can automatically enhance skills with updated best practices. Trigger terms include review skill, audit skill, improve skill, enhance skill, update skill, check skill quality, skill best practices, fix skill, optimize skill, validate skill structure.
|
||||||
|
allowed-tools: Read, Grep, Glob, Edit, Write
|
||||||
|
---
|
||||||
|
|
||||||
|
# Skill Reviewer and Enhancer
|
||||||
|
|
||||||
|
Review, audit, and enhance existing Claude Code skills to ensure they follow Anthropic best practices and current domain-specific patterns.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
To improve skill quality and ensure adherence to official standards, this skill performs comprehensive analysis of existing skills, identifies structural issues, verifies domain-specific best practices, and automatically applies improvements.
|
||||||
|
|
||||||
|
## When to Use This Skill
|
||||||
|
|
||||||
|
Apply this skill when:
|
||||||
|
- Reviewing an existing skill for quality and compliance
|
||||||
|
- Auditing skills before deployment to production
|
||||||
|
- Updating skills to follow latest Anthropic guidelines
|
||||||
|
- Ensuring skills use current framework/library patterns
|
||||||
|
- Identifying missing resources or incomplete implementations
|
||||||
|
- Enhancing skill descriptions for better discoverability
|
||||||
|
- Fixing structural or formatting issues
|
||||||
|
- Modernizing outdated skill instructions
|
||||||
|
|
||||||
|
## Step 1: Locate and Read the Skill
|
||||||
|
|
||||||
|
To begin the review process:
|
||||||
|
|
||||||
|
1. Ask user for the skill path or name if not provided
|
||||||
|
2. Verify the skill directory exists
|
||||||
|
3. Read the SKILL.md file completely
|
||||||
|
4. Note the skill's category and purpose
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Find skill
|
||||||
|
ls skills/[category]/[skill-name]/SKILL.md
|
||||||
|
|
||||||
|
# Read skill
|
||||||
|
Read: skills/[category]/[skill-name]/SKILL.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 2: Analyze Skill Structure
|
||||||
|
|
||||||
|
To verify proper skill structure, check for:
|
||||||
|
|
||||||
|
### Frontmatter Validation
|
||||||
|
|
||||||
|
**Required Fields:**
|
||||||
|
- `name`: Must be present, hyphen-case, no angle brackets
|
||||||
|
- `description`: Must be present, third-person voice, includes trigger terms, under 1024 characters
|
||||||
|
|
||||||
|
**Optional Fields:**
|
||||||
|
- `allowed-tools`: Present for read-only/analysis skills
|
||||||
|
|
||||||
|
**Validation Script:**
|
||||||
|
```bash
|
||||||
|
python scripts/analyze_skill_structure.py --skill skills/[category]/[skill-name]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Name Convention Check
|
||||||
|
|
||||||
|
Verify name follows hyphen-case:
|
||||||
|
- [OK] Correct: `skill-reviewer-and-enhancer`, `nextjs-fullstack-scaffold`
|
||||||
|
- [WRONG] Incorrect: `SkillReviewer`, `skill_reviewer`, `skillReviewer`
|
||||||
|
|
||||||
|
Check for invalid patterns:
|
||||||
|
- Starts or ends with hyphen
|
||||||
|
- Contains consecutive hyphens (`--`)
|
||||||
|
- Contains uppercase letters
|
||||||
|
- Contains underscores or other special characters
|
||||||
|
|
||||||
|
### Description Quality Check
|
||||||
|
|
||||||
|
Verify description:
|
||||||
|
- **Voice**: Uses third-person ("This skill should be used when...")
|
||||||
|
- **Content**: Explains WHAT it does and WHEN to use it
|
||||||
|
- **Trigger Terms**: Includes specific keywords users would search for
|
||||||
|
- **Length**: Under 1024 characters
|
||||||
|
- **Format**: No angle brackets, proper grammar
|
||||||
|
|
||||||
|
## Step 3: Review Instruction Style
|
||||||
|
|
||||||
|
To verify proper instruction format, scan the SKILL.md body for:
|
||||||
|
|
||||||
|
### Imperative Form Check
|
||||||
|
|
||||||
|
Instructions should use imperative/infinitive form:
|
||||||
|
- [OK] Correct: "To create a form, use the generator script"
|
||||||
|
- [OK] Correct: "Generate schemas using the Zod validator"
|
||||||
|
- [WRONG] Incorrect: "You should create forms using the generator"
|
||||||
|
- [WRONG] Incorrect: "You can generate schemas with Zod"
|
||||||
|
|
||||||
|
Use Grep to find second-person usage:
|
||||||
|
```bash
|
||||||
|
Grep: pattern="\\b[Yy]ou\\b" path="skills/[category]/[skill-name]/SKILL.md" output_mode="content"
|
||||||
|
```
|
||||||
|
|
||||||
|
Flag any instances for correction.
|
||||||
|
|
||||||
|
### Section Structure Check
|
||||||
|
|
||||||
|
Verify logical organization:
|
||||||
|
- Overview/Introduction
|
||||||
|
- When to Use (optional but recommended)
|
||||||
|
- Prerequisites (if applicable)
|
||||||
|
- Step-by-step implementation
|
||||||
|
- Resource references (scripts, references, assets)
|
||||||
|
- Best practices
|
||||||
|
- Troubleshooting (optional)
|
||||||
|
|
||||||
|
## Step 4: Verify Domain-Specific Best Practices
|
||||||
|
|
||||||
|
To ensure the skill follows current best practices for its domain, consult the appropriate reference:
|
||||||
|
|
||||||
|
### Development Skills
|
||||||
|
For Next.js, React, TypeScript, database skills, check:
|
||||||
|
- Uses latest framework versions (Next.js 15/16, React 19)
|
||||||
|
- Follows Server Components patterns
|
||||||
|
- Uses App Router (not Pages Router)
|
||||||
|
- Implements proper TypeScript types
|
||||||
|
- Uses modern tooling (Vite, Vitest, ESM)
|
||||||
|
|
||||||
|
Consult `references/nextjs-best-practices.md` for detailed checks.
|
||||||
|
|
||||||
|
### Testing Skills
|
||||||
|
For testing-related skills, check:
|
||||||
|
- Uses modern test runners (Vitest, not Jest)
|
||||||
|
- Uses Testing Library patterns
|
||||||
|
- Implements accessibility testing
|
||||||
|
- Follows AAA pattern (Arrange, Act, Assert)
|
||||||
|
- Uses proper mocking strategies
|
||||||
|
|
||||||
|
Consult `references/testing-best-practices.md` for detailed checks.
|
||||||
|
|
||||||
|
### UI Component Skills
|
||||||
|
For UI and component skills, check:
|
||||||
|
- Uses shadcn/ui patterns correctly
|
||||||
|
- Implements accessibility (ARIA, semantic HTML)
|
||||||
|
- Follows Tailwind CSS conventions
|
||||||
|
- Uses proper composition patterns
|
||||||
|
- Implements dark mode support
|
||||||
|
|
||||||
|
Consult `references/ui-best-practices.md` for detailed checks.
|
||||||
|
|
||||||
|
### Database Skills
|
||||||
|
For database and ORM skills, check:
|
||||||
|
- Uses Prisma 5+ patterns
|
||||||
|
- Implements proper connection pooling
|
||||||
|
- Uses prepared statements/parameterized queries
|
||||||
|
- Implements RLS for Supabase
|
||||||
|
- Uses proper migration patterns
|
||||||
|
|
||||||
|
Consult `references/database-best-practices.md` for detailed checks.
|
||||||
|
|
||||||
|
### Security Skills
|
||||||
|
For security-related skills, check:
|
||||||
|
- Follows OWASP guidelines
|
||||||
|
- Implements proper authentication patterns
|
||||||
|
- Uses secure session management
|
||||||
|
- Implements CSRF protection
|
||||||
|
- Uses Content Security Policy
|
||||||
|
- Validates and sanitizes inputs
|
||||||
|
|
||||||
|
Consult `references/security-best-practices.md` for detailed checks.
|
||||||
|
|
||||||
|
## Step 5: Check Resource References
|
||||||
|
|
||||||
|
To verify bundled resources are properly referenced:
|
||||||
|
|
||||||
|
### Scripts Directory
|
||||||
|
Check if skill references scripts and whether they exist:
|
||||||
|
```bash
|
||||||
|
Grep: pattern="scripts/" path="skills/[category]/[skill-name]/SKILL.md" output_mode="content"
|
||||||
|
Glob: pattern="skills/[category]/[skill-name]/scripts/*"
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify:
|
||||||
|
- Scripts mentioned in SKILL.md exist in scripts/ directory
|
||||||
|
- Scripts have proper documentation
|
||||||
|
- Scripts are executable (Python/Bash)
|
||||||
|
- Usage examples are provided
|
||||||
|
|
||||||
|
### References Directory
|
||||||
|
Check if skill references documentation:
|
||||||
|
```bash
|
||||||
|
Grep: pattern="references/" path="skills/[category]/[skill-name]/SKILL.md" output_mode="content"
|
||||||
|
Glob: pattern="skills/[category]/[skill-name]/references/*"
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify:
|
||||||
|
- References mentioned exist
|
||||||
|
- Large documents (>5k words) use grep patterns for selective loading
|
||||||
|
- References are up-to-date with current practices
|
||||||
|
|
||||||
|
### Assets Directory
|
||||||
|
Check if skill references templates or output files:
|
||||||
|
```bash
|
||||||
|
Grep: pattern="assets/" path="skills/[category]/[skill-name]/SKILL.md" output_mode="content"
|
||||||
|
Glob: pattern="skills/[category]/[skill-name]/assets/*"
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify:
|
||||||
|
- Assets mentioned exist
|
||||||
|
- Templates are properly formatted
|
||||||
|
- File names are descriptive
|
||||||
|
|
||||||
|
## Step 6: Generate Improvement Report
|
||||||
|
|
||||||
|
To create a comprehensive review report, use the template from `assets/review-report-template.md`:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Skill Review Report: [Skill Name]
|
||||||
|
|
||||||
|
**Skill Path:** skills/[category]/[skill-name]
|
||||||
|
**Review Date:** [Date]
|
||||||
|
**Reviewer:** Claude Code (skill-reviewer-and-enhancer)
|
||||||
|
|
||||||
|
## Overall Assessment
|
||||||
|
|
||||||
|
**Grade:** [A/B/C/D/F]
|
||||||
|
**Status:** [Production Ready / Needs Minor Fixes / Needs Major Revision]
|
||||||
|
|
||||||
|
## Structural Compliance
|
||||||
|
|
||||||
|
### Frontmatter
|
||||||
|
- [x] Name present and valid
|
||||||
|
- [x] Description present and well-formatted
|
||||||
|
- [ ] allowed-tools specified (if applicable)
|
||||||
|
|
||||||
|
### Naming Convention
|
||||||
|
- [x] Uses hyphen-case
|
||||||
|
- [ ] Issue: Contains uppercase/invalid characters
|
||||||
|
|
||||||
|
### Description Quality
|
||||||
|
- [x] Third-person voice
|
||||||
|
- [x] Includes trigger terms
|
||||||
|
- [x] Under 1024 characters
|
||||||
|
- [ ] Issue: Missing WHEN to use explanation
|
||||||
|
|
||||||
|
## Instruction Style
|
||||||
|
|
||||||
|
### Imperative Form
|
||||||
|
- [x] Uses verb-first instructions
|
||||||
|
- [ ] Issue: Found 5 instances of "you should" (lines: 45, 67, 89, 102, 134)
|
||||||
|
|
||||||
|
**Recommended Changes:**
|
||||||
|
```
|
||||||
|
Line 45: "You should use the script" → "Use the script"
|
||||||
|
Line 67: "You can generate forms" → "Generate forms" or "To generate forms"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Domain-Specific Best Practices
|
||||||
|
|
||||||
|
### [Domain] Patterns
|
||||||
|
- [x] Uses current framework version
|
||||||
|
- [x] Follows recommended patterns
|
||||||
|
- [ ] Issue: References deprecated API (NextAuth v3, should use v5)
|
||||||
|
- [ ] Issue: Uses Pages Router patterns (should use App Router)
|
||||||
|
|
||||||
|
**Recommended Updates:**
|
||||||
|
[Specific suggestions for domain improvements]
|
||||||
|
|
||||||
|
## Resource Completeness
|
||||||
|
|
||||||
|
### Scripts
|
||||||
|
- [ ] Script `generate_form.py` mentioned but not found
|
||||||
|
- [x] Script `validate_schema.py` exists and documented
|
||||||
|
|
||||||
|
### References
|
||||||
|
- [x] All references exist
|
||||||
|
- [ ] `api-patterns.md` is outdated (2022 patterns)
|
||||||
|
|
||||||
|
### Assets
|
||||||
|
- [x] All assets exist and properly formatted
|
||||||
|
|
||||||
|
## Suggested Improvements
|
||||||
|
|
||||||
|
### Critical (Must Fix)
|
||||||
|
1. **Fix second-person usage**: Replace 5 instances with imperative form
|
||||||
|
2. **Add missing script**: Create `generate_form.py` or remove reference
|
||||||
|
3. **Update deprecated API**: Replace NextAuth v3 with v5 patterns
|
||||||
|
|
||||||
|
### Recommended (Should Fix)
|
||||||
|
1. **Enhance description**: Add more trigger terms related to [domain]
|
||||||
|
2. **Update reference**: Modernize `api-patterns.md` with 2025 patterns
|
||||||
|
3. **Add troubleshooting**: Include common error scenarios
|
||||||
|
|
||||||
|
### Optional (Nice to Have)
|
||||||
|
1. **Add examples**: Include more code examples for common use cases
|
||||||
|
2. **Expand prerequisites**: Document required dependencies
|
||||||
|
3. **Add related skills**: Link to complementary skills
|
||||||
|
|
||||||
|
## Modernization Opportunities
|
||||||
|
|
||||||
|
1. **Framework Updates**: Update from Next.js 14 to Next.js 15/16
|
||||||
|
2. **Tooling Updates**: Replace Jest with Vitest
|
||||||
|
3. **Pattern Updates**: Use Server Actions instead of API routes
|
||||||
|
4. **Type Safety**: Add more TypeScript examples
|
||||||
|
|
||||||
|
## Automated Fixes Available
|
||||||
|
|
||||||
|
The following fixes can be applied automatically:
|
||||||
|
- [ ] Convert "you" to imperative form (5 instances)
|
||||||
|
- [ ] Update frontmatter format
|
||||||
|
- [ ] Fix hyphenation in name
|
||||||
|
- [ ] Add missing allowed-tools field
|
||||||
|
|
||||||
|
**Apply automated fixes?** (Yes/No)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Step 7: Apply Improvements (If Approved)
|
||||||
|
|
||||||
|
To apply improvements to the skill, proceed with user approval:
|
||||||
|
|
||||||
|
### Automated Fixes
|
||||||
|
|
||||||
|
For structural and style issues:
|
||||||
|
|
||||||
|
**Fix Second-Person Usage:**
|
||||||
|
```typescript
|
||||||
|
// Replace patterns like:
|
||||||
|
"You should use" → "Use"
|
||||||
|
"You can generate" → "Generate" or "To generate"
|
||||||
|
"You need to configure" → "Configure" or "To configure"
|
||||||
|
"You will see" → "This displays" or "This shows"
|
||||||
|
"You have to install" → "Install"
|
||||||
|
```
|
||||||
|
|
||||||
|
Use Edit tool to apply changes:
|
||||||
|
```bash
|
||||||
|
Edit: file_path="skills/[category]/[skill-name]/SKILL.md"
|
||||||
|
old_string="You should use the generator script"
|
||||||
|
new_string="Use the generator script"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fix Frontmatter:**
|
||||||
|
Add missing fields or correct format:
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
name: skill-name-in-hyphen-case
|
||||||
|
description: Third-person description with trigger terms under 1024 chars.
|
||||||
|
allowed-tools: Read, Grep, Glob # For read-only skills
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fix Name Convention:**
|
||||||
|
If name uses wrong format, update both frontmatter and directory:
|
||||||
|
1. Update name in frontmatter
|
||||||
|
2. Notify user directory rename needed (cannot be done automatically)
|
||||||
|
|
||||||
|
### Domain-Specific Updates
|
||||||
|
|
||||||
|
For framework/library updates:
|
||||||
|
|
||||||
|
**Update Next.js Patterns:**
|
||||||
|
- Replace `pages/` with `app/` directory examples
|
||||||
|
- Replace `getServerSideProps` with Server Components
|
||||||
|
- Replace API routes with Server Actions (where appropriate)
|
||||||
|
- Update to Next.js 15/16 syntax
|
||||||
|
|
||||||
|
**Update Testing Patterns:**
|
||||||
|
- Replace Jest with Vitest configuration
|
||||||
|
- Update Testing Library imports
|
||||||
|
- Use modern assertion syntax
|
||||||
|
- Add accessibility testing with axe
|
||||||
|
|
||||||
|
**Update Database Patterns:**
|
||||||
|
- Update Prisma to v5 syntax
|
||||||
|
- Add proper connection pooling
|
||||||
|
- Use latest Supabase patterns
|
||||||
|
- Implement RLS examples
|
||||||
|
|
||||||
|
**Update Security Patterns:**
|
||||||
|
- Use latest OWASP recommendations
|
||||||
|
- Update CSP directives
|
||||||
|
- Use modern auth patterns (NextAuth v5, Supabase Auth)
|
||||||
|
- Add secure header configurations
|
||||||
|
|
||||||
|
### Resource Creation
|
||||||
|
|
||||||
|
For missing resources:
|
||||||
|
|
||||||
|
**Create Missing Scripts:**
|
||||||
|
Use templates from `assets/script-templates/` to generate placeholder scripts:
|
||||||
|
```python
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Script for [skill-name]: [purpose]
|
||||||
|
"""
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Implementation here
|
||||||
|
pass
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
```
|
||||||
|
|
||||||
|
**Create Missing References:**
|
||||||
|
Generate reference documents with appropriate content:
|
||||||
|
```markdown
|
||||||
|
# [Topic] Best Practices
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
[Introduction to the topic]
|
||||||
|
|
||||||
|
## Patterns
|
||||||
|
[Common patterns and examples]
|
||||||
|
|
||||||
|
## Anti-Patterns
|
||||||
|
[What to avoid]
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
[External references]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Create Missing Assets:**
|
||||||
|
Generate template files as needed for the skill's purpose.
|
||||||
|
|
||||||
|
## Step 8: Validate Enhanced Skill
|
||||||
|
|
||||||
|
To verify improvements, run validation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/quick_validate.py skills/[category]/[skill-name]
|
||||||
|
```
|
||||||
|
|
||||||
|
Confirm:
|
||||||
|
- Validation passes
|
||||||
|
- Frontmatter is correct
|
||||||
|
- Name follows convention
|
||||||
|
- Description is well-formatted
|
||||||
|
- Instructions use imperative form
|
||||||
|
- Resources are referenced correctly
|
||||||
|
- Domain best practices followed
|
||||||
|
|
||||||
|
## Step 9: Generate Enhancement Summary
|
||||||
|
|
||||||
|
To document changes made, create a summary:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Skill Enhancement Summary
|
||||||
|
|
||||||
|
**Skill:** [name]
|
||||||
|
**Date:** [date]
|
||||||
|
**Changes Applied:** [count]
|
||||||
|
|
||||||
|
## Structural Fixes
|
||||||
|
- Fixed second-person usage (5 instances)
|
||||||
|
- Updated frontmatter format
|
||||||
|
- Added allowed-tools field
|
||||||
|
|
||||||
|
## Domain Updates
|
||||||
|
- Updated Next.js patterns to v15
|
||||||
|
- Replaced deprecated APIs
|
||||||
|
- Added modern code examples
|
||||||
|
|
||||||
|
## Resource Updates
|
||||||
|
- Created missing script: generate_form.py
|
||||||
|
- Updated reference: api-patterns.md
|
||||||
|
- Added asset: template.tsx
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
[OK] Skill passes all validation checks
|
||||||
|
[OK] Ready for production use
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
1. Review changes in SKILL.md
|
||||||
|
2. Test any new scripts
|
||||||
|
3. Update CATALOG.md if needed
|
||||||
|
4. Deploy to project or personal directory
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices for Skill Review
|
||||||
|
|
||||||
|
### Thoroughness
|
||||||
|
- Review entire SKILL.md, not just frontmatter
|
||||||
|
- Check all resource references
|
||||||
|
- Verify domain-specific patterns
|
||||||
|
- Test any scripts if possible
|
||||||
|
|
||||||
|
### Accuracy
|
||||||
|
- Consult official documentation for frameworks
|
||||||
|
- Use latest version numbers
|
||||||
|
- Verify deprecated APIs
|
||||||
|
- Check security recommendations
|
||||||
|
|
||||||
|
### Improvement Focus
|
||||||
|
- Prioritize critical structural issues
|
||||||
|
- Modernize outdated patterns
|
||||||
|
- Enhance discoverability with better descriptions
|
||||||
|
- Maintain backward compatibility when possible
|
||||||
|
|
||||||
|
### User Communication
|
||||||
|
- Clearly explain issues found
|
||||||
|
- Provide specific examples of problems
|
||||||
|
- Offer actionable recommendations
|
||||||
|
- Show before/after comparisons
|
||||||
|
|
||||||
|
## Common Issues and Fixes
|
||||||
|
|
||||||
|
### Issue: Second-Person Usage
|
||||||
|
|
||||||
|
**Problem:** Instructions use "you should" or "you can"
|
||||||
|
|
||||||
|
**Fix:** Convert to imperative form using Edit tool
|
||||||
|
|
||||||
|
### Issue: Outdated Framework Patterns
|
||||||
|
|
||||||
|
**Problem:** Skill references old API or deprecated patterns
|
||||||
|
|
||||||
|
**Fix:** Update to current version following official documentation
|
||||||
|
|
||||||
|
### Issue: Missing Resources
|
||||||
|
|
||||||
|
**Problem:** Skill references scripts/references/assets that don't exist
|
||||||
|
|
||||||
|
**Fix:** Either create placeholder resources or remove references
|
||||||
|
|
||||||
|
### Issue: Poor Description
|
||||||
|
|
||||||
|
**Problem:** Description doesn't include trigger terms or explain when to use
|
||||||
|
|
||||||
|
**Fix:** Enhance description with specific use cases and keywords
|
||||||
|
|
||||||
|
### Issue: Incomplete Instructions
|
||||||
|
|
||||||
|
**Problem:** Steps are vague or missing critical details
|
||||||
|
|
||||||
|
**Fix:** Add detailed substeps, examples, and edge case handling
|
||||||
|
|
||||||
|
## Resource Files
|
||||||
|
|
||||||
|
### scripts/analyze_skill_structure.py
|
||||||
|
Automated analysis tool that parses SKILL.md, validates frontmatter, checks naming conventions, and identifies structural issues.
|
||||||
|
|
||||||
|
### scripts/check_domain_patterns.py
|
||||||
|
Domain-specific pattern validator that checks for current best practices in Next.js, React, testing, databases, security, etc.
|
||||||
|
|
||||||
|
### scripts/apply_automated_fixes.py
|
||||||
|
Batch fix application tool that can automatically correct common issues like second-person usage, frontmatter format, and naming.
|
||||||
|
|
||||||
|
### references/nextjs-best-practices.md
|
||||||
|
Current Next.js patterns including App Router, Server Components, Server Actions, and modern configuration.
|
||||||
|
|
||||||
|
### references/testing-best-practices.md
|
||||||
|
Modern testing patterns with Vitest, React Testing Library, Playwright, and accessibility testing.
|
||||||
|
|
||||||
|
### references/ui-best-practices.md
|
||||||
|
shadcn/ui usage patterns, Tailwind CSS conventions, accessibility guidelines, and component composition.
|
||||||
|
|
||||||
|
### references/database-best-practices.md
|
||||||
|
Prisma ORM patterns, Supabase integration, RLS policies, and database security.
|
||||||
|
|
||||||
|
### references/security-best-practices.md
|
||||||
|
OWASP guidelines, authentication patterns, session management, CSRF protection, and secure headers.
|
||||||
|
|
||||||
|
### references/anthropic-skill-standards.md
|
||||||
|
Official Anthropic skill creation standards including structure, naming, voice, and resource organization.
|
||||||
|
|
||||||
|
### assets/review-report-template.md
|
||||||
|
Structured template for generating comprehensive skill review reports.
|
||||||
|
|
||||||
|
### assets/script-templates/
|
||||||
|
Python and Bash script templates for creating missing skill resources.
|
||||||
|
|
||||||
|
### assets/reference-templates/
|
||||||
|
Markdown templates for creating missing reference documentation.
|
||||||
|
|
||||||
|
## Implementation Checklist
|
||||||
|
|
||||||
|
When reviewing and enhancing skills:
|
||||||
|
|
||||||
|
- [ ] Read complete SKILL.md file
|
||||||
|
- [ ] Validate frontmatter structure
|
||||||
|
- [ ] Check naming convention
|
||||||
|
- [ ] Verify description quality
|
||||||
|
- [ ] Scan for second-person usage
|
||||||
|
- [ ] Verify section organization
|
||||||
|
- [ ] Check domain-specific best practices
|
||||||
|
- [ ] Verify all resource references
|
||||||
|
- [ ] Generate improvement report
|
||||||
|
- [ ] Get user approval for changes
|
||||||
|
- [ ] Apply automated fixes
|
||||||
|
- [ ] Apply domain-specific updates
|
||||||
|
- [ ] Create missing resources
|
||||||
|
- [ ] Validate enhanced skill
|
||||||
|
- [ ] Generate enhancement summary
|
||||||
|
- [ ] Offer to update CATALOG.md
|
||||||
|
|
||||||
|
## Integration with Development Workflow
|
||||||
|
|
||||||
|
Use this skill as part of:
|
||||||
|
- **Pre-deployment Review**: Before publishing skills
|
||||||
|
- **Quality Audit**: Regular skill inventory checks
|
||||||
|
- **Modernization**: Updating skills with new patterns
|
||||||
|
- **Onboarding**: Ensuring new skills meet standards
|
||||||
|
- **Maintenance**: Keeping skills current and accurate
|
||||||
|
|
||||||
|
## Continuous Improvement
|
||||||
|
|
||||||
|
After enhancing skills:
|
||||||
|
1. Document patterns found in multiple skills
|
||||||
|
2. Update reference documents with new best practices
|
||||||
|
3. Create reusable templates for common fixes
|
||||||
|
4. Share learnings in team documentation
|
||||||
|
5. Adjust review criteria based on findings
|
||||||
|
6. Schedule regular skill audits
|
||||||
@@ -0,0 +1,369 @@
|
|||||||
|
# Skill Review Report: [Skill Name]
|
||||||
|
|
||||||
|
**Skill Path:** `skills/[category]/[skill-name]`
|
||||||
|
**Review Date:** [YYYY-MM-DD]
|
||||||
|
**Reviewer:** Claude Code (skill-reviewer-and-enhancer)
|
||||||
|
**Skill Purpose:** [Brief description of what the skill does]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Executive Summary
|
||||||
|
|
||||||
|
**Overall Grade:** [A / B / C / D / F]
|
||||||
|
**Status:** [[OK] Production Ready / [WARN] Needs Minor Fixes / [ERROR] Needs Major Revision]
|
||||||
|
**Critical Issues:** [count]
|
||||||
|
**Warnings:** [count]
|
||||||
|
**Suggestions:** [count]
|
||||||
|
|
||||||
|
### Quick Assessment
|
||||||
|
[1-2 sentence summary of skill quality and readiness]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Structural Compliance
|
||||||
|
|
||||||
|
### Frontmatter Validation
|
||||||
|
|
||||||
|
#### Name Field
|
||||||
|
- [x] Present
|
||||||
|
- [x] Uses hyphen-case
|
||||||
|
- [ ] Issue: [Description if any]
|
||||||
|
|
||||||
|
**Current:** `[current-name]`
|
||||||
|
**Expected:** `[correct-name]` (if different)
|
||||||
|
|
||||||
|
#### Description Field
|
||||||
|
- [x] Present
|
||||||
|
- [x] Third-person voice
|
||||||
|
- [x] Includes trigger terms
|
||||||
|
- [x] Under 1024 characters ([X] chars)
|
||||||
|
- [ ] Issue: [Description if any]
|
||||||
|
|
||||||
|
**Current Length:** [X] characters
|
||||||
|
**Quality Score:** [High / Medium / Low]
|
||||||
|
|
||||||
|
#### Optional Fields
|
||||||
|
- [x] `allowed-tools` specified (if applicable)
|
||||||
|
- [ ] Missing but recommended: [field name]
|
||||||
|
|
||||||
|
### Naming Convention
|
||||||
|
- [x] Lowercase only
|
||||||
|
- [x] Uses hyphens (not underscores or spaces)
|
||||||
|
- [x] No consecutive hyphens
|
||||||
|
- [x] Doesn't start/end with hyphen
|
||||||
|
|
||||||
|
**Validation:** [[OK] PASS / [ERROR] FAIL]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Instruction Style Analysis
|
||||||
|
|
||||||
|
### Imperative Form Check
|
||||||
|
|
||||||
|
**Second-Person Usage Found:** [count] instances
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
| Line | Current Text | Suggested Fix |
|
||||||
|
|------|-------------|---------------|
|
||||||
|
| [N] | "[text with 'you should']" | "[imperative version]" |
|
||||||
|
| [N] | "[text with 'you can']" | "[imperative version]" |
|
||||||
|
|
||||||
|
**Pattern Breakdown:**
|
||||||
|
- "you should" → [count] instances
|
||||||
|
- "you can" → [count] instances
|
||||||
|
- "you need to" → [count] instances
|
||||||
|
- "you will" → [count] instances
|
||||||
|
|
||||||
|
### Section Organization
|
||||||
|
- [x] Clear overview section
|
||||||
|
- [x] When to Use section (recommended)
|
||||||
|
- [x] Step-by-step implementation
|
||||||
|
- [x] Resource references
|
||||||
|
- [x] Best practices section
|
||||||
|
- [ ] Missing: [section name]
|
||||||
|
|
||||||
|
**Organization Score:** [Excellent / Good / Needs Improvement]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Domain-Specific Best Practices
|
||||||
|
|
||||||
|
**Skill Domain:** [Next.js / Testing / UI Components / Database / Security / Other]
|
||||||
|
|
||||||
|
### Framework/Library Versions
|
||||||
|
|
||||||
|
| Component | Current | Latest | Status |
|
||||||
|
|-----------|---------|--------|--------|
|
||||||
|
| [Framework] | [version] | [version] | [[OK] / [WARN] / [ERROR]] |
|
||||||
|
| [Library] | [version] | [version] | [[OK] / [WARN] / [ERROR]] |
|
||||||
|
|
||||||
|
### Pattern Compliance
|
||||||
|
|
||||||
|
#### Modern Patterns Used [OK]
|
||||||
|
- [x] [Pattern name and description]
|
||||||
|
- [x] [Pattern name and description]
|
||||||
|
|
||||||
|
#### Deprecated Patterns Found [ERROR]
|
||||||
|
- [ ] **Issue:** [Description of deprecated pattern]
|
||||||
|
- **Line/Section:** [location]
|
||||||
|
- **Recommendation:** [modern replacement]
|
||||||
|
|
||||||
|
- [ ] **Issue:** [Description of deprecated pattern]
|
||||||
|
- **Line/Section:** [location]
|
||||||
|
- **Recommendation:** [modern replacement]
|
||||||
|
|
||||||
|
### Domain-Specific Checklist
|
||||||
|
|
||||||
|
**For [Domain] Skills:**
|
||||||
|
- [x] [Specific requirement 1]
|
||||||
|
- [x] [Specific requirement 2]
|
||||||
|
- [ ] [Missing requirement]
|
||||||
|
|
||||||
|
**Compliance Score:** [X]% ([Y]/[Z] checks passed)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Resource Completeness
|
||||||
|
|
||||||
|
### Scripts Directory
|
||||||
|
|
||||||
|
**Scripts Mentioned in SKILL.md:**
|
||||||
|
| Script Name | Exists | Documented | Executable |
|
||||||
|
|-------------|--------|------------|------------|
|
||||||
|
| `[name].py` | [[OK]/[ERROR]] | [[OK]/[ERROR]] | [[OK]/[ERROR]] |
|
||||||
|
|
||||||
|
**Issues:**
|
||||||
|
- [ ] Script `[name]` mentioned but not found
|
||||||
|
- [ ] Script `[name]` missing usage documentation
|
||||||
|
|
||||||
|
### References Directory
|
||||||
|
|
||||||
|
**References Mentioned:**
|
||||||
|
| Reference Name | Exists | Size | Grep Patterns |
|
||||||
|
|----------------|--------|------|---------------|
|
||||||
|
| `[name].md` | [[OK]/[ERROR]] | [X]KB | [[OK]/[ERROR]] |
|
||||||
|
|
||||||
|
**Issues:**
|
||||||
|
- [ ] Reference `[name]` mentioned but not found
|
||||||
|
- [ ] Large reference (>10KB) without grep patterns
|
||||||
|
|
||||||
|
### Assets Directory
|
||||||
|
|
||||||
|
**Assets Mentioned:**
|
||||||
|
| Asset Name | Exists | Type | Documented |
|
||||||
|
|------------|--------|------|------------|
|
||||||
|
| `[name]` | [[OK]/[ERROR]] | [type] | [[OK]/[ERROR]] |
|
||||||
|
|
||||||
|
**Issues:**
|
||||||
|
- [ ] Asset `[name]` mentioned but not found
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Detailed Findings
|
||||||
|
|
||||||
|
### Critical Issues (Must Fix Before Production)
|
||||||
|
|
||||||
|
#### Issue 1: [Title]
|
||||||
|
**Severity:** Critical
|
||||||
|
**Location:** [Line number or section]
|
||||||
|
**Description:** [Detailed explanation]
|
||||||
|
|
||||||
|
**Current State:**
|
||||||
|
```[language]
|
||||||
|
[Code or text showing the problem]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Required Fix:**
|
||||||
|
```[language]
|
||||||
|
[Code or text showing the solution]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Impact:** [How this affects skill functionality or user experience]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Warnings (Should Fix)
|
||||||
|
|
||||||
|
#### Warning 1: [Title]
|
||||||
|
**Severity:** Medium
|
||||||
|
**Location:** [Line number or section]
|
||||||
|
**Description:** [Detailed explanation]
|
||||||
|
|
||||||
|
**Recommendation:** [Suggested improvement]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Suggestions (Nice to Have)
|
||||||
|
|
||||||
|
#### Suggestion 1: [Title]
|
||||||
|
**Priority:** Low
|
||||||
|
**Description:** [Enhancement opportunity]
|
||||||
|
|
||||||
|
**Benefit:** [How this would improve the skill]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Code Quality Assessment
|
||||||
|
|
||||||
|
### Examples Provided
|
||||||
|
- [x] Clear code examples
|
||||||
|
- [x] Examples follow best practices
|
||||||
|
- [x] Examples are complete and runnable
|
||||||
|
- [ ] Issue: [Description]
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
- [x] Includes troubleshooting section
|
||||||
|
- [x] Common errors documented
|
||||||
|
- [x] Solutions provided
|
||||||
|
- [ ] Missing: [What's missing]
|
||||||
|
|
||||||
|
### Completeness
|
||||||
|
- [x] Prerequisites listed
|
||||||
|
- [x] Dependencies documented
|
||||||
|
- [x] Configuration steps clear
|
||||||
|
- [x] Edge cases covered
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Modernization Opportunities
|
||||||
|
|
||||||
|
### Framework Updates Needed
|
||||||
|
1. **[Framework name]**: Update from [old] to [new]
|
||||||
|
- Impact: [High / Medium / Low]
|
||||||
|
- Effort: [High / Medium / Low]
|
||||||
|
- Benefits: [List benefits]
|
||||||
|
|
||||||
|
### Pattern Modernization
|
||||||
|
1. **[Pattern name]**: Replace [old pattern] with [new pattern]
|
||||||
|
- Affected sections: [List sections]
|
||||||
|
- Migration path: [Brief steps]
|
||||||
|
|
||||||
|
### Tooling Updates
|
||||||
|
1. **[Tool name]**: Replace [old tool] with [new tool]
|
||||||
|
- Rationale: [Why update]
|
||||||
|
- Changes required: [List changes]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Automated Fixes Available
|
||||||
|
|
||||||
|
The following fixes can be applied automatically:
|
||||||
|
|
||||||
|
- [ ] Convert second-person to imperative form ([count] instances)
|
||||||
|
- [ ] Update frontmatter format
|
||||||
|
- [ ] Fix name hyphenation
|
||||||
|
- [ ] Add missing `allowed-tools` field
|
||||||
|
- [ ] Update framework version references
|
||||||
|
- [ ] Replace deprecated API patterns
|
||||||
|
|
||||||
|
**Estimated Time:** [X] minutes
|
||||||
|
**Apply fixes?** [Yes / No / Review First]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Manual Improvements Required
|
||||||
|
|
||||||
|
The following require manual review:
|
||||||
|
|
||||||
|
1. **[Improvement title]**
|
||||||
|
- Complexity: [High / Medium / Low]
|
||||||
|
- Estimated effort: [time]
|
||||||
|
- Priority: [High / Medium / Low]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Comparison to Best-in-Class
|
||||||
|
|
||||||
|
### Strengths
|
||||||
|
- [OK] [Strength 1]
|
||||||
|
- [OK] [Strength 2]
|
||||||
|
|
||||||
|
### Areas for Improvement
|
||||||
|
- [TIP] [Improvement area 1]
|
||||||
|
- [TIP] [Improvement area 2]
|
||||||
|
|
||||||
|
### Benchmarking
|
||||||
|
Compared to similar skills in the repository:
|
||||||
|
- **Structure:** [Better / Similar / Worse]
|
||||||
|
- **Documentation:** [Better / Similar / Worse]
|
||||||
|
- **Best Practices:** [Better / Similar / Worse]
|
||||||
|
- **Resource Completeness:** [Better / Similar / Worse]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Action Plan
|
||||||
|
|
||||||
|
### Immediate Actions (This Week)
|
||||||
|
1. [ ] [Action item with high priority]
|
||||||
|
2. [ ] [Action item with high priority]
|
||||||
|
|
||||||
|
### Short-Term Actions (This Month)
|
||||||
|
1. [ ] [Action item with medium priority]
|
||||||
|
2. [ ] [Action item with medium priority]
|
||||||
|
|
||||||
|
### Long-Term Improvements (This Quarter)
|
||||||
|
1. [ ] [Action item with low priority but high value]
|
||||||
|
2. [ ] [Action item with low priority but high value]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Testing & Validation
|
||||||
|
|
||||||
|
### Pre-Enhancement Validation
|
||||||
|
```bash
|
||||||
|
python scripts/quick_validate.py skills/[category]/[skill-name]
|
||||||
|
```
|
||||||
|
**Result:** [PASS / FAIL]
|
||||||
|
|
||||||
|
### Post-Enhancement Validation
|
||||||
|
After applying fixes:
|
||||||
|
- [ ] Run validation script
|
||||||
|
- [ ] Test resource references
|
||||||
|
- [ ] Verify code examples
|
||||||
|
- [ ] Check resource file paths
|
||||||
|
- [ ] Test scripts (if applicable)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Approval & Sign-Off
|
||||||
|
|
||||||
|
**Recommended Next Steps:**
|
||||||
|
1. [Step 1]
|
||||||
|
2. [Step 2]
|
||||||
|
3. [Step 3]
|
||||||
|
|
||||||
|
**Skill Ready for:**
|
||||||
|
- [ ] Immediate deployment (Grade A, no critical issues)
|
||||||
|
- [ ] Deployment after minor fixes (Grade B)
|
||||||
|
- [ ] Significant revision needed (Grade C or below)
|
||||||
|
|
||||||
|
**Reviewer Notes:**
|
||||||
|
[Any additional context or recommendations]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Appendix: Detailed Metrics
|
||||||
|
|
||||||
|
### Skill Statistics
|
||||||
|
- **Total Lines:** [count]
|
||||||
|
- **Code Examples:** [count]
|
||||||
|
- **Resource References:** [count]
|
||||||
|
- **Second-Person Instances:** [count]
|
||||||
|
- **External Links:** [count]
|
||||||
|
|
||||||
|
### Readability
|
||||||
|
- **Estimated Reading Time:** [X] minutes
|
||||||
|
- **Complexity Level:** [Beginner / Intermediate / Advanced]
|
||||||
|
- **Target Audience:** [Description]
|
||||||
|
|
||||||
|
### Maintenance
|
||||||
|
- **Last Updated:** [Date if available]
|
||||||
|
- **Update Frequency Required:** [How often skill needs updates]
|
||||||
|
- **Maintenance Burden:** [Low / Medium / High]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**End of Report**
|
||||||
|
|
||||||
|
*Generated by skill-reviewer-and-enhancer*
|
||||||
|
*For questions or clarifications, review the skill at `[skill-path]`*
|
||||||
@@ -0,0 +1,377 @@
|
|||||||
|
# Anthropic Skill Standards
|
||||||
|
|
||||||
|
Official standards for creating Claude Code skills based on Anthropic's skill-creator best practices.
|
||||||
|
|
||||||
|
## Core Principles
|
||||||
|
|
||||||
|
Skills should follow these fundamental principles:
|
||||||
|
1. **Progressive Disclosure**: Metadata → SKILL.md → Resources (load only what's needed)
|
||||||
|
2. **Imperative Voice**: Use verb-first instructions, not second-person
|
||||||
|
3. **Third-Person Descriptions**: Describe what the skill does, not what "you" do
|
||||||
|
4. **Resource Bundling**: Scripts, references, and assets organized in standard directories
|
||||||
|
|
||||||
|
## Skill Structure
|
||||||
|
|
||||||
|
### Required Files
|
||||||
|
|
||||||
|
```
|
||||||
|
skill-name/
|
||||||
|
├── SKILL.md (required)
|
||||||
|
└── Optional bundled resources:
|
||||||
|
├── scripts/ - Executable code
|
||||||
|
├── references/ - Documentation loaded as needed
|
||||||
|
└── assets/ - Templates and output files
|
||||||
|
```
|
||||||
|
|
||||||
|
### SKILL.md Format
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
name: skill-name-in-hyphen-case
|
||||||
|
description: Third-person description with trigger terms. Max 1024 chars.
|
||||||
|
allowed-tools: Read, Grep, Glob # Optional, for read-only skills
|
||||||
|
---
|
||||||
|
|
||||||
|
# Skill Title
|
||||||
|
|
||||||
|
Brief overview paragraph.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
To accomplish [goal], this skill [explains approach].
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
|
||||||
|
Use this skill when:
|
||||||
|
- [Specific scenario 1]
|
||||||
|
- [Specific scenario 2]
|
||||||
|
|
||||||
|
## Implementation Steps
|
||||||
|
|
||||||
|
### Step 1: [Action]
|
||||||
|
|
||||||
|
To [accomplish substep], do:
|
||||||
|
|
||||||
|
1. [Specific instruction]
|
||||||
|
2. [Specific instruction]
|
||||||
|
|
||||||
|
[Code example if applicable]
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- `scripts/[name].py` - Description
|
||||||
|
- `references/[name].md` - Description
|
||||||
|
- `assets/[name]` - Description
|
||||||
|
```
|
||||||
|
|
||||||
|
## Frontmatter Standards
|
||||||
|
|
||||||
|
### Name Field
|
||||||
|
|
||||||
|
**Format**: `hyphen-case` (lowercase letters, digits, hyphens only)
|
||||||
|
|
||||||
|
**Valid:**
|
||||||
|
- `nextjs-fullstack-scaffold`
|
||||||
|
- `api-contracts-and-zod-validation`
|
||||||
|
- `form-generator-rhf-zod`
|
||||||
|
|
||||||
|
**Invalid:**
|
||||||
|
- `NextjsScaffold` (PascalCase)
|
||||||
|
- `nextjs_scaffold` (snake_case)
|
||||||
|
- `nextjsScaffold` (camelCase)
|
||||||
|
- `-nextjs-scaffold` (starts with hyphen)
|
||||||
|
- `nextjs--scaffold` (consecutive hyphens)
|
||||||
|
|
||||||
|
### Description Field
|
||||||
|
|
||||||
|
**Requirements:**
|
||||||
|
- Use third-person voice
|
||||||
|
- Include WHAT the skill does
|
||||||
|
- Include WHEN to use it
|
||||||
|
- Include specific trigger terms users would search
|
||||||
|
- Maximum 1024 characters
|
||||||
|
- No angle brackets (< or >)
|
||||||
|
|
||||||
|
**Format:**
|
||||||
|
```yaml
|
||||||
|
description: This skill should be used when [scenarios]. Apply when [cases]. Trigger terms include [keywords, phrases, terms].
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```yaml
|
||||||
|
description: This skill should be used when generating React forms with React Hook Form, Zod validation, and shadcn/ui components. Applies when creating entity forms, character editors, location forms, or any form requiring client and server validation. Trigger terms include create form, generate form, React Hook Form, RHF, Zod validation, form component.
|
||||||
|
```
|
||||||
|
|
||||||
|
### allowed-tools Field (Optional)
|
||||||
|
|
||||||
|
**When to use**: For read-only or analysis skills that don't modify code
|
||||||
|
|
||||||
|
**Format:**
|
||||||
|
```yaml
|
||||||
|
allowed-tools: Read, Grep, Glob, Bash
|
||||||
|
```
|
||||||
|
|
||||||
|
**Common combinations:**
|
||||||
|
- Analysis only: `Read, Grep, Glob, Bash`
|
||||||
|
- File operations: `Read, Edit, Write, Bash`
|
||||||
|
- Full access: Omit field entirely
|
||||||
|
|
||||||
|
## Instruction Voice Standards
|
||||||
|
|
||||||
|
### Imperative/Infinitive Form
|
||||||
|
|
||||||
|
**Always use verb-first instructions:**
|
||||||
|
|
||||||
|
[OK] **Correct Examples:**
|
||||||
|
- "To create a form, use the generator script"
|
||||||
|
- "Generate schemas using Zod"
|
||||||
|
- "Install dependencies with npm"
|
||||||
|
- "Configure the database connection"
|
||||||
|
- "Use the template from assets/form.tsx"
|
||||||
|
- "Consult references/api-patterns.md for details"
|
||||||
|
|
||||||
|
[ERROR] **Incorrect Examples:**
|
||||||
|
- "You should create forms using the generator"
|
||||||
|
- "You can generate schemas with Zod"
|
||||||
|
- "You need to install dependencies"
|
||||||
|
- "You have to configure the database"
|
||||||
|
- "You will use the template"
|
||||||
|
- "You should consult the reference"
|
||||||
|
|
||||||
|
### Description Voice
|
||||||
|
|
||||||
|
**Use third-person for descriptions:**
|
||||||
|
|
||||||
|
[OK] **Correct:**
|
||||||
|
- "This skill should be used when..."
|
||||||
|
- "Apply this skill when creating..."
|
||||||
|
- "Use for generating..."
|
||||||
|
|
||||||
|
[ERROR] **Incorrect:**
|
||||||
|
- "Use this skill when you need to..."
|
||||||
|
- "You should use this when..."
|
||||||
|
- "This helps you create..."
|
||||||
|
|
||||||
|
## Resource Organization
|
||||||
|
|
||||||
|
### scripts/ Directory
|
||||||
|
|
||||||
|
**Purpose**: Executable code for deterministic operations
|
||||||
|
|
||||||
|
**When to include:**
|
||||||
|
- Code is repeatedly rewritten in skill usage
|
||||||
|
- Deterministic operations needed
|
||||||
|
- Complex logic that benefits from separate execution
|
||||||
|
|
||||||
|
**Format:**
|
||||||
|
- Python: `script_name.py` (executable, #!/usr/bin/env python3)
|
||||||
|
- Bash: `script_name.sh` (executable, #!/bin/bash)
|
||||||
|
|
||||||
|
**Documentation in SKILL.md:**
|
||||||
|
```markdown
|
||||||
|
### scripts/generate_form.py
|
||||||
|
|
||||||
|
Generates form component from field specification.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
\`\`\`bash
|
||||||
|
python scripts/generate_form.py --fields fields.json --output components/forms
|
||||||
|
\`\`\`
|
||||||
|
```
|
||||||
|
|
||||||
|
### references/ Directory
|
||||||
|
|
||||||
|
**Purpose**: Documentation loaded into context as needed
|
||||||
|
|
||||||
|
**When to include:**
|
||||||
|
- Detailed information too long for SKILL.md (>5k words)
|
||||||
|
- API reference documentation
|
||||||
|
- Pattern catalogs
|
||||||
|
- Specification documents
|
||||||
|
|
||||||
|
**Format**: Markdown files with descriptive names
|
||||||
|
|
||||||
|
**For large files (>10k words):**
|
||||||
|
Include grep patterns in SKILL.md:
|
||||||
|
```markdown
|
||||||
|
Consult `references/api-reference.md` for API details.
|
||||||
|
|
||||||
|
To find specific endpoints:
|
||||||
|
\`\`\`bash
|
||||||
|
Grep: pattern="GET /api/entities" path="references/api-reference.md"
|
||||||
|
\`\`\`
|
||||||
|
```
|
||||||
|
|
||||||
|
### assets/ Directory
|
||||||
|
|
||||||
|
**Purpose**: Files used in output (not loaded into context)
|
||||||
|
|
||||||
|
**When to include:**
|
||||||
|
- Templates
|
||||||
|
- Boilerplate code
|
||||||
|
- Starter files
|
||||||
|
- Configuration examples
|
||||||
|
|
||||||
|
**Format**: Any file type appropriate for the skill
|
||||||
|
|
||||||
|
**Reference in SKILL.md:**
|
||||||
|
```markdown
|
||||||
|
Use template from `assets/form-template.tsx` as starting point.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Trigger Terms
|
||||||
|
|
||||||
|
Include specific terms users would search for:
|
||||||
|
|
||||||
|
### Technical Terms
|
||||||
|
- Framework names: Next.js, React, Vitest, Playwright
|
||||||
|
- Library names: Zod, React Hook Form, Prisma
|
||||||
|
- Tool names: ESLint, Prettier, Husky
|
||||||
|
- Pattern names: Server Actions, RLS, CSP
|
||||||
|
|
||||||
|
### Action Terms
|
||||||
|
- create, generate, build, scaffold, initialize
|
||||||
|
- setup, configure, install, integrate
|
||||||
|
- test, validate, check, audit, review
|
||||||
|
- optimize, improve, refactor, enhance
|
||||||
|
|
||||||
|
### Domain Terms (for worldbuilding)
|
||||||
|
- entity, character, location, item, faction
|
||||||
|
- relationship, timeline, event, lore
|
||||||
|
- world, map, narrative, description
|
||||||
|
|
||||||
|
### Problem Terms
|
||||||
|
- error, issue, bug, vulnerability
|
||||||
|
- performance, slow, bottleneck
|
||||||
|
- inconsistent, duplicate, broken
|
||||||
|
|
||||||
|
## Skill Categories
|
||||||
|
|
||||||
|
### development
|
||||||
|
Code generation, refactoring, debugging, setup, configuration, frameworks, libraries, tooling
|
||||||
|
|
||||||
|
### data-modeling
|
||||||
|
Schemas, types, validation, database design, entity relationships, migrations
|
||||||
|
|
||||||
|
### ui-components
|
||||||
|
React components, styling, responsive design, accessibility, user interface
|
||||||
|
|
||||||
|
### documentation
|
||||||
|
API docs, guides, comments, changelogs, ADRs, PRDs
|
||||||
|
|
||||||
|
### testing
|
||||||
|
Unit tests, integration tests, E2E tests, accessibility tests, coverage, mocks
|
||||||
|
|
||||||
|
### utilities
|
||||||
|
Helpers, formatters, converters, validators, analyzers, auditors
|
||||||
|
|
||||||
|
## Quality Checklist
|
||||||
|
|
||||||
|
Before finalizing a skill, verify:
|
||||||
|
|
||||||
|
- [ ] Name is hyphen-case (lowercase, hyphens only)
|
||||||
|
- [ ] Description is third-person voice
|
||||||
|
- [ ] Description includes trigger terms
|
||||||
|
- [ ] Description explains WHAT and WHEN
|
||||||
|
- [ ] Description is under 1024 characters
|
||||||
|
- [ ] Instructions use imperative form (no "you")
|
||||||
|
- [ ] Section structure is logical
|
||||||
|
- [ ] Resources are properly referenced
|
||||||
|
- [ ] Scripts have usage examples
|
||||||
|
- [ ] Large references include grep patterns
|
||||||
|
- [ ] Assets are described in context
|
||||||
|
- [ ] Examples are clear and complete
|
||||||
|
- [ ] Code follows current best practices
|
||||||
|
- [ ] Validation passes: `python scripts/quick_validate.py`
|
||||||
|
|
||||||
|
## Common Mistakes
|
||||||
|
|
||||||
|
### Mistake 1: Second-Person Voice
|
||||||
|
|
||||||
|
[ERROR] "You should install the dependencies"
|
||||||
|
[OK] "Install the dependencies"
|
||||||
|
|
||||||
|
[ERROR] "You can use the template"
|
||||||
|
[OK] "Use the template" or "To use the template"
|
||||||
|
|
||||||
|
### Mistake 2: Wrong Name Format
|
||||||
|
|
||||||
|
[ERROR] `FormGenerator` (PascalCase)
|
||||||
|
[OK] `form-generator`
|
||||||
|
|
||||||
|
[ERROR] `form_generator` (snake_case)
|
||||||
|
[OK] `form-generator`
|
||||||
|
|
||||||
|
### Mistake 3: Vague Description
|
||||||
|
|
||||||
|
[ERROR] "This skill helps with forms and validation stuff."
|
||||||
|
[OK] "This skill should be used when generating React forms with React Hook Form and Zod validation. Use for creating entity forms, data entry interfaces, or any form requiring validation."
|
||||||
|
|
||||||
|
### Mistake 4: Missing Trigger Terms
|
||||||
|
|
||||||
|
[ERROR] "This skill generates forms." (no searchable terms)
|
||||||
|
[OK] "This skill generates React Hook Form components with Zod validation. Trigger terms include create form, generate form, RHF, React Hook Form, Zod validation, form component."
|
||||||
|
|
||||||
|
### Mistake 5: Incomplete Resource References
|
||||||
|
|
||||||
|
[ERROR] "Use the script to generate forms." (which script?)
|
||||||
|
[OK] "Use `scripts/generate_form.py` to generate form components."
|
||||||
|
|
||||||
|
## Version Guidelines
|
||||||
|
|
||||||
|
Keep skills current with latest versions:
|
||||||
|
|
||||||
|
### Framework Versions
|
||||||
|
- Next.js: 15/16 (App Router, Server Components)
|
||||||
|
- React: 19 (Server Components, Actions)
|
||||||
|
- TypeScript: 5.x
|
||||||
|
- Node.js: 20+
|
||||||
|
|
||||||
|
### Testing Tools
|
||||||
|
- Vitest (not Jest)
|
||||||
|
- React Testing Library
|
||||||
|
- Playwright
|
||||||
|
- axe-core for a11y
|
||||||
|
|
||||||
|
### Build Tools
|
||||||
|
- Vite (for fast builds)
|
||||||
|
- ESM (not CommonJS)
|
||||||
|
- Modern bundlers
|
||||||
|
|
||||||
|
### Deprecated Patterns to Avoid
|
||||||
|
- Next.js Pages Router (use App Router)
|
||||||
|
- Jest (use Vitest)
|
||||||
|
- getServerSideProps (use Server Components)
|
||||||
|
- API routes for mutations (use Server Actions)
|
||||||
|
- Class components (use function components)
|
||||||
|
|
||||||
|
## Maintenance
|
||||||
|
|
||||||
|
### Regular Updates
|
||||||
|
- Review skills quarterly for outdated patterns
|
||||||
|
- Update framework versions
|
||||||
|
- Replace deprecated APIs
|
||||||
|
- Add new best practices
|
||||||
|
- Improve trigger terms based on usage
|
||||||
|
|
||||||
|
### Validation
|
||||||
|
Run validation regularly:
|
||||||
|
```bash
|
||||||
|
python scripts/quick_validate.py skills/[category]/[skill-name]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Quality Audits
|
||||||
|
Use skill-reviewer-and-enhancer to audit:
|
||||||
|
```bash
|
||||||
|
# Trigger the skill
|
||||||
|
"Review and enhance the [skill-name] skill"
|
||||||
|
```
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- Official Anthropic skill-creator skill
|
||||||
|
- Claude Code documentation
|
||||||
|
- Community best practices
|
||||||
|
- Framework official documentation
|
||||||
@@ -0,0 +1,589 @@
|
|||||||
|
# Next.js Best Practices (2025)
|
||||||
|
|
||||||
|
Current best practices for Next.js 15/16 development with App Router, Server Components, and modern patterns.
|
||||||
|
|
||||||
|
## Framework Versions
|
||||||
|
|
||||||
|
### Current Versions
|
||||||
|
- **Next.js**: 15.x or 16.x (latest stable)
|
||||||
|
- **React**: 19.x (Server Components, Actions)
|
||||||
|
- **TypeScript**: 5.x
|
||||||
|
- **Node.js**: 20 LTS or higher
|
||||||
|
|
||||||
|
### Deprecated Versions to Flag
|
||||||
|
- Next.js 12.x or older (Pages Router era)
|
||||||
|
- React 17.x or older
|
||||||
|
- TypeScript 4.x or older
|
||||||
|
|
||||||
|
## App Router vs Pages Router
|
||||||
|
|
||||||
|
### [OK] Modern (App Router)
|
||||||
|
|
||||||
|
```
|
||||||
|
app/
|
||||||
|
├── layout.tsx # Root layout
|
||||||
|
├── page.tsx # Homepage
|
||||||
|
├── entities/
|
||||||
|
│ ├── layout.tsx # Nested layout
|
||||||
|
│ ├── page.tsx # Entity list
|
||||||
|
│ └── [id]/
|
||||||
|
│ └── page.tsx # Entity detail
|
||||||
|
└── api/ # API route handlers (minimal)
|
||||||
|
└── webhook/
|
||||||
|
└── route.ts # External webhooks only
|
||||||
|
```
|
||||||
|
|
||||||
|
### [ERROR] Deprecated (Pages Router)
|
||||||
|
|
||||||
|
```
|
||||||
|
pages/
|
||||||
|
├── _app.tsx # Old pattern
|
||||||
|
├── _document.tsx # Old pattern
|
||||||
|
├── index.tsx # Old pattern
|
||||||
|
├── entities/
|
||||||
|
│ ├── index.tsx
|
||||||
|
│ └── [id].tsx
|
||||||
|
└── api/ # Old API routes
|
||||||
|
└── entities.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
**Migration Check**: Flag any skills using `pages/` directory or `getServerSideProps`
|
||||||
|
|
||||||
|
## Server Components vs Client Components
|
||||||
|
|
||||||
|
### Default: Server Components
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// app/entities/page.tsx
|
||||||
|
// No 'use client' directive = Server Component (default)
|
||||||
|
|
||||||
|
import { db } from '@/lib/db'
|
||||||
|
|
||||||
|
export default async function EntitiesPage() {
|
||||||
|
// Direct database access - only in Server Components
|
||||||
|
const entities = await db.entity.findMany()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>Entities</h1>
|
||||||
|
{entities.map(entity => (
|
||||||
|
<EntityCard key={entity.id} entity={entity} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Client Components (When Needed)
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// components/EntityForm.tsx
|
||||||
|
'use client' // Required for hooks, events, browser APIs
|
||||||
|
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { useForm } from 'react-hook-form'
|
||||||
|
|
||||||
|
export function EntityForm() {
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||||
|
const form = useForm()
|
||||||
|
|
||||||
|
return <form>...</form>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**When to use 'use client':**
|
||||||
|
- useState, useEffect, other hooks
|
||||||
|
- Event handlers (onClick, onChange)
|
||||||
|
- Browser APIs (localStorage, window)
|
||||||
|
- Third-party libraries requiring browser
|
||||||
|
|
||||||
|
**Best Practice**: Keep 'use client' boundary as low as possible in component tree
|
||||||
|
|
||||||
|
## Data Fetching
|
||||||
|
|
||||||
|
### [OK] Modern: Server Components + fetch
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// app/characters/page.tsx
|
||||||
|
export default async function CharactersPage() {
|
||||||
|
// Fetch in Server Component
|
||||||
|
const characters = await fetch('https://api.example.com/characters', {
|
||||||
|
next: { revalidate: 3600 } // Cache for 1 hour
|
||||||
|
}).then(res => res.json())
|
||||||
|
|
||||||
|
return <CharacterList characters={characters} />
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### [OK] Modern: Server Components + Database
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// app/locations/page.tsx
|
||||||
|
import { db } from '@/lib/db'
|
||||||
|
|
||||||
|
export default async function LocationsPage() {
|
||||||
|
// Direct database access
|
||||||
|
const locations = await db.location.findMany({
|
||||||
|
include: { region: true }
|
||||||
|
})
|
||||||
|
|
||||||
|
return <LocationList locations={locations} />
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### [ERROR] Deprecated: getServerSideProps
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// pages/characters.tsx - OLD PATTERN
|
||||||
|
export async function getServerSideProps() {
|
||||||
|
const characters = await fetchCharacters()
|
||||||
|
return { props: { characters } }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Migration Check**: Replace `getServerSideProps`, `getStaticProps`, `getInitialProps` with Server Component async data fetching
|
||||||
|
|
||||||
|
## Server Actions
|
||||||
|
|
||||||
|
### [OK] Modern: Server Actions for Mutations
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// app/actions/character.ts
|
||||||
|
'use server'
|
||||||
|
|
||||||
|
import { db } from '@/lib/db'
|
||||||
|
import { revalidatePath } from 'next/cache'
|
||||||
|
|
||||||
|
export async function createCharacter(formData: FormData) {
|
||||||
|
const name = formData.get('name') as string
|
||||||
|
|
||||||
|
const character = await db.character.create({
|
||||||
|
data: { name }
|
||||||
|
})
|
||||||
|
|
||||||
|
revalidatePath('/characters')
|
||||||
|
return { success: true, character }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// app/characters/CreateForm.tsx
|
||||||
|
'use client'
|
||||||
|
|
||||||
|
import { createCharacter } from '@/app/actions/character'
|
||||||
|
|
||||||
|
export function CreateForm() {
|
||||||
|
return (
|
||||||
|
<form action={createCharacter}>
|
||||||
|
<input name="name" />
|
||||||
|
<button type="submit">Create</button>
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### [ERROR] Deprecated: API Routes for Simple Mutations
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// pages/api/characters.ts - OLD PATTERN
|
||||||
|
export default async function handler(req, res) {
|
||||||
|
if (req.method === 'POST') {
|
||||||
|
const character = await createCharacter(req.body)
|
||||||
|
res.json({ character })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**When to still use API Routes:**
|
||||||
|
- Webhooks from external services
|
||||||
|
- OAuth callbacks
|
||||||
|
- Third-party integrations requiring public endpoints
|
||||||
|
|
||||||
|
**Migration Check**: Replace API routes used for form submissions with Server Actions
|
||||||
|
|
||||||
|
## Metadata and SEO
|
||||||
|
|
||||||
|
### [OK] Modern: generateMetadata
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// app/characters/[id]/page.tsx
|
||||||
|
import { Metadata } from 'next'
|
||||||
|
|
||||||
|
export async function generateMetadata({ params }): Promise<Metadata> {
|
||||||
|
const character = await db.character.findUnique({
|
||||||
|
where: { id: params.id }
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: character.name,
|
||||||
|
description: character.bio,
|
||||||
|
openGraph: {
|
||||||
|
title: character.name,
|
||||||
|
description: character.bio,
|
||||||
|
images: [character.image],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### [ERROR] Deprecated: next/head
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// OLD PATTERN
|
||||||
|
import Head from 'next/head'
|
||||||
|
|
||||||
|
<Head>
|
||||||
|
<title>{character.name}</title>
|
||||||
|
</Head>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Routing and Navigation
|
||||||
|
|
||||||
|
### [OK] Modern: Link from next/link
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
|
<Link href="/characters/123">View Character</Link>
|
||||||
|
```
|
||||||
|
|
||||||
|
### [OK] Modern: useRouter from next/navigation
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
'use client'
|
||||||
|
import { useRouter } from 'next/navigation' // Not 'next/router'!
|
||||||
|
|
||||||
|
export function BackButton() {
|
||||||
|
const router = useRouter()
|
||||||
|
return <button onClick={() => router.back()}>Back</button>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### [ERROR] Deprecated: useRouter from next/router
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// OLD PATTERN
|
||||||
|
import { useRouter } from 'next/router' // Pages Router only
|
||||||
|
```
|
||||||
|
|
||||||
|
## Loading States
|
||||||
|
|
||||||
|
### [OK] Modern: loading.tsx
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// app/characters/loading.tsx
|
||||||
|
export default function Loading() {
|
||||||
|
return <div>Loading characters...</div>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### [OK] Modern: Suspense Boundaries
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// app/characters/page.tsx
|
||||||
|
import { Suspense } from 'react'
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
return (
|
||||||
|
<Suspense fallback={<LoadingSkeleton />}>
|
||||||
|
<CharacterList />
|
||||||
|
</Suspense>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
### [OK] Modern: error.tsx
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// app/characters/error.tsx
|
||||||
|
'use client'
|
||||||
|
|
||||||
|
export default function Error({
|
||||||
|
error,
|
||||||
|
reset,
|
||||||
|
}: {
|
||||||
|
error: Error & { digest?: string }
|
||||||
|
reset: () => void
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>Something went wrong!</h2>
|
||||||
|
<button onClick={() => reset()}>Try again</button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Caching and Revalidation
|
||||||
|
|
||||||
|
### [OK] Modern: Revalidation Strategies
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// Time-based revalidation
|
||||||
|
fetch(url, { next: { revalidate: 3600 } }) // 1 hour
|
||||||
|
|
||||||
|
// On-demand revalidation
|
||||||
|
import { revalidatePath } from 'next/cache'
|
||||||
|
revalidatePath('/characters')
|
||||||
|
|
||||||
|
// Tag-based revalidation
|
||||||
|
fetch(url, { next: { tags: ['characters'] } })
|
||||||
|
import { revalidateTag } from 'next/cache'
|
||||||
|
revalidateTag('characters')
|
||||||
|
|
||||||
|
// Disable caching
|
||||||
|
fetch(url, { cache: 'no-store' })
|
||||||
|
```
|
||||||
|
|
||||||
|
## Middleware
|
||||||
|
|
||||||
|
### [OK] Modern: middleware.ts
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// middleware.ts
|
||||||
|
import { NextResponse } from 'next/server'
|
||||||
|
import type { NextRequest } from 'next/server'
|
||||||
|
|
||||||
|
export function middleware(request: NextRequest) {
|
||||||
|
// Auth check
|
||||||
|
const token = request.cookies.get('token')
|
||||||
|
|
||||||
|
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
|
||||||
|
return NextResponse.redirect(new URL('/login', request.url))
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
matcher: '/dashboard/:path*',
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## TypeScript Patterns
|
||||||
|
|
||||||
|
### [OK] Type-Safe Server Components
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
interface Character {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
bio: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function CharacterPage({
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
params: { id: string }
|
||||||
|
}) {
|
||||||
|
const character: Character = await db.character.findUnique({
|
||||||
|
where: { id: params.id }
|
||||||
|
})
|
||||||
|
|
||||||
|
return <div>{character.name}</div>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### [OK] Type-Safe Server Actions
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
'use server'
|
||||||
|
|
||||||
|
import { z } from 'zod'
|
||||||
|
|
||||||
|
const createCharacterSchema = z.object({
|
||||||
|
name: z.string().min(2),
|
||||||
|
bio: z.string().max(5000),
|
||||||
|
})
|
||||||
|
|
||||||
|
export async function createCharacter(
|
||||||
|
data: z.infer<typeof createCharacterSchema>
|
||||||
|
) {
|
||||||
|
const validated = createCharacterSchema.parse(data)
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
### [OK] Modern: Type-Safe Env
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// env.mjs
|
||||||
|
import { createEnv } from "@t3-oss/env-nextjs"
|
||||||
|
import { z } from "zod"
|
||||||
|
|
||||||
|
export const env = createEnv({
|
||||||
|
server: {
|
||||||
|
DATABASE_URL: z.string().url(),
|
||||||
|
NEXTAUTH_SECRET: z.string().min(1),
|
||||||
|
},
|
||||||
|
client: {
|
||||||
|
NEXT_PUBLIC_API_URL: z.string().url(),
|
||||||
|
},
|
||||||
|
runtimeEnv: {
|
||||||
|
DATABASE_URL: process.env.DATABASE_URL,
|
||||||
|
NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,
|
||||||
|
NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### [OK] Modern: next.config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
/** @type {import('next').NextConfig} */
|
||||||
|
const nextConfig = {
|
||||||
|
experimental: {
|
||||||
|
serverActions: {
|
||||||
|
bodySizeLimit: '2mb',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
images: {
|
||||||
|
remotePatterns: [
|
||||||
|
{
|
||||||
|
protocol: 'https',
|
||||||
|
hostname: 'example.com',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default nextConfig
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Anti-Patterns to Flag
|
||||||
|
|
||||||
|
### [ERROR] Fetching in Client Components
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
'use client'
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
// BAD: Fetch in client component
|
||||||
|
export function Characters() {
|
||||||
|
const [data, setData] = useState([])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch('/api/characters').then(r => r.json()).then(setData)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return <div>{data.map(...)}</div>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[OK] **Fix**: Move data fetching to Server Component or use React Query for client-side data management
|
||||||
|
|
||||||
|
### [ERROR] Unnecessary 'use client'
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
'use client' // Not needed!
|
||||||
|
|
||||||
|
export function StaticCard({ title, description }) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h3>{title}</h3>
|
||||||
|
<p>{description}</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[OK] **Fix**: Remove 'use client' if component doesn't use hooks, events, or browser APIs
|
||||||
|
|
||||||
|
### [ERROR] Mixing Data Fetching Methods
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
// BAD: Using both old and new patterns
|
||||||
|
export async function getServerSideProps() { ... }
|
||||||
|
|
||||||
|
export default async function Page() {
|
||||||
|
const data = await fetch(...)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
[OK] **Fix**: Use only Server Component data fetching in App Router
|
||||||
|
|
||||||
|
## Performance Best Practices
|
||||||
|
|
||||||
|
### Image Optimization
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import Image from 'next/image'
|
||||||
|
|
||||||
|
<Image
|
||||||
|
src="/character.jpg"
|
||||||
|
alt="Character portrait"
|
||||||
|
width={800}
|
||||||
|
height={600}
|
||||||
|
priority // For LCP images
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Font Optimization
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { Inter } from 'next/font/google'
|
||||||
|
|
||||||
|
const inter = Inter({ subsets: ['latin'] })
|
||||||
|
|
||||||
|
export default function RootLayout({ children }) {
|
||||||
|
return (
|
||||||
|
<html lang="en" className={inter.className}>
|
||||||
|
{children}
|
||||||
|
</html>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Splitting
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import dynamic from 'next/dynamic'
|
||||||
|
|
||||||
|
const HeavyComponent = dynamic(() => import('@/components/HeavyComponent'), {
|
||||||
|
loading: () => <p>Loading...</p>,
|
||||||
|
ssr: false, // Client-only if needed
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validation Checklist
|
||||||
|
|
||||||
|
When reviewing Next.js skills, check for:
|
||||||
|
|
||||||
|
- [ ] Uses App Router (app/ directory), not Pages Router
|
||||||
|
- [ ] Server Components for data fetching
|
||||||
|
- [ ] Server Actions for mutations
|
||||||
|
- [ ] Proper 'use client' boundaries
|
||||||
|
- [ ] generateMetadata for SEO
|
||||||
|
- [ ] useRouter from 'next/navigation'
|
||||||
|
- [ ] Modern caching with fetch options
|
||||||
|
- [ ] loading.tsx and error.tsx files
|
||||||
|
- [ ] Type-safe environment variables
|
||||||
|
- [ ] Image and font optimization
|
||||||
|
- [ ] No deprecated APIs (getServerSideProps, etc.)
|
||||||
|
- [ ] Proper middleware usage
|
||||||
|
- [ ] Correct import paths
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
### Must Use (Modern)
|
||||||
|
- [OK] `app/` directory
|
||||||
|
- [OK] Server Components (default)
|
||||||
|
- [OK] Server Actions ('use server')
|
||||||
|
- [OK] `generateMetadata`
|
||||||
|
- [OK] `useRouter` from 'next/navigation'
|
||||||
|
- [OK] `revalidatePath`/`revalidateTag`
|
||||||
|
|
||||||
|
### Must Avoid (Deprecated)
|
||||||
|
- [ERROR] `pages/` directory
|
||||||
|
- [ERROR] `getServerSideProps`
|
||||||
|
- [ERROR] `getStaticProps`
|
||||||
|
- [ERROR] `getInitialProps`
|
||||||
|
- [ERROR] API routes for mutations
|
||||||
|
- [ERROR] `useRouter` from 'next/router'
|
||||||
|
- [ERROR] `next/head`
|
||||||
@@ -0,0 +1,674 @@
|
|||||||
|
# Testing Best Practices (2025)
|
||||||
|
|
||||||
|
Modern testing patterns using Vitest, React Testing Library, Playwright, and accessibility testing.
|
||||||
|
|
||||||
|
## Testing Stack
|
||||||
|
|
||||||
|
### Current Tools (2025)
|
||||||
|
- **Unit/Integration**: Vitest (not Jest)
|
||||||
|
- **Component Testing**: React Testing Library
|
||||||
|
- **E2E Testing**: Playwright
|
||||||
|
- **Accessibility**: axe-core + @axe-core/playwright
|
||||||
|
- **Coverage**: Vitest with v8 provider
|
||||||
|
|
||||||
|
### Deprecated Tools to Flag
|
||||||
|
- [ERROR] Jest (replaced by Vitest)
|
||||||
|
- [ERROR] Enzyme (replaced by React Testing Library)
|
||||||
|
- [ERROR] Karma, Jasmine (outdated)
|
||||||
|
|
||||||
|
## Vitest Configuration
|
||||||
|
|
||||||
|
### [OK] Modern: vitest.config.ts
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { defineConfig } from 'vitest/config'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
test: {
|
||||||
|
globals: true,
|
||||||
|
environment: 'jsdom',
|
||||||
|
setupFiles: './test/setup.ts',
|
||||||
|
coverage: {
|
||||||
|
provider: 'v8',
|
||||||
|
reporter: ['text', 'json', 'html'],
|
||||||
|
exclude: [
|
||||||
|
'node_modules/',
|
||||||
|
'test/',
|
||||||
|
'**/*.config.{ts,js}',
|
||||||
|
'**/*.d.ts',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': path.resolve(__dirname, './src'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### [ERROR] Deprecated: jest.config.js
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// OLD PATTERN - Don't use Jest
|
||||||
|
module.exports = {
|
||||||
|
testEnvironment: 'jsdom',
|
||||||
|
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Unit Testing Patterns
|
||||||
|
|
||||||
|
### [OK] Modern: Vitest Imports
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
||||||
|
|
||||||
|
describe('validateEntity', () => {
|
||||||
|
it('validates entity with required fields', () => {
|
||||||
|
const result = validateEntity({
|
||||||
|
name: 'Character',
|
||||||
|
type: 'character'
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.valid).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('rejects entity with missing name', () => {
|
||||||
|
const result = validateEntity({
|
||||||
|
type: 'character'
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.valid).toBe(false)
|
||||||
|
expect(result.errors).toContain('Name is required')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Organization
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Good structure: Arrange, Act, Assert (AAA)
|
||||||
|
describe('CharacterService', () => {
|
||||||
|
describe('createCharacter', () => {
|
||||||
|
it('creates character with valid data', async () => {
|
||||||
|
// Arrange
|
||||||
|
const characterData = {
|
||||||
|
name: 'Aria',
|
||||||
|
class: 'Rogue',
|
||||||
|
}
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = await createCharacter(characterData)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result.success).toBe(true)
|
||||||
|
expect(result.character).toMatchObject(characterData)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component Testing with RTL
|
||||||
|
|
||||||
|
### [OK] Modern: React Testing Library
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { render, screen, waitFor } from '@testing-library/react'
|
||||||
|
import userEvent from '@testing-library/user-event'
|
||||||
|
import { describe, it, expect, vi } from 'vitest'
|
||||||
|
import { CharacterForm } from './CharacterForm'
|
||||||
|
|
||||||
|
describe('CharacterForm', () => {
|
||||||
|
it('renders form fields', () => {
|
||||||
|
render(<CharacterForm />)
|
||||||
|
|
||||||
|
expect(screen.getByLabelText(/name/i)).toBeInTheDocument()
|
||||||
|
expect(screen.getByLabelText(/class/i)).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('submits form with valid data', async () => {
|
||||||
|
const user = userEvent.setup()
|
||||||
|
const onSubmit = vi.fn()
|
||||||
|
|
||||||
|
render(<CharacterForm onSubmit={onSubmit} />)
|
||||||
|
|
||||||
|
await user.type(screen.getByLabelText(/name/i), 'Aria')
|
||||||
|
await user.selectOptions(screen.getByLabelText(/class/i), 'rogue')
|
||||||
|
await user.click(screen.getByRole('button', { name: /submit/i }))
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(onSubmit).toHaveBeenCalledWith({
|
||||||
|
name: 'Aria',
|
||||||
|
class: 'rogue',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows validation errors', async () => {
|
||||||
|
const user = userEvent.setup()
|
||||||
|
render(<CharacterForm />)
|
||||||
|
|
||||||
|
await user.click(screen.getByRole('button', { name: /submit/i }))
|
||||||
|
|
||||||
|
expect(await screen.findByText(/name is required/i)).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### [ERROR] Deprecated: Enzyme
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// OLD PATTERN - Don't use Enzyme
|
||||||
|
import { shallow } from 'enzyme'
|
||||||
|
|
||||||
|
const wrapper = shallow(<CharacterForm />)
|
||||||
|
wrapper.find('input').simulate('change')
|
||||||
|
```
|
||||||
|
|
||||||
|
## Custom Render Function
|
||||||
|
|
||||||
|
### [OK] Create Test Utils
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// test/utils/render.tsx
|
||||||
|
import { render, RenderOptions } from '@testing-library/react'
|
||||||
|
import { ReactElement } from 'react'
|
||||||
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
|
||||||
|
|
||||||
|
const queryClient = new QueryClient({
|
||||||
|
defaultOptions: {
|
||||||
|
queries: { retry: false },
|
||||||
|
mutations: { retry: false },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
function AllProviders({ children }: { children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<QueryClientProvider client={queryClient}>
|
||||||
|
{children}
|
||||||
|
</QueryClientProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function renderWithProviders(
|
||||||
|
ui: ReactElement,
|
||||||
|
options?: Omit<RenderOptions, 'wrapper'>
|
||||||
|
) {
|
||||||
|
return render(ui, { wrapper: AllProviders, ...options })
|
||||||
|
}
|
||||||
|
|
||||||
|
export * from '@testing-library/react'
|
||||||
|
export { renderWithProviders as render }
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mocking Patterns
|
||||||
|
|
||||||
|
### [OK] Vitest Mocks
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { vi } from 'vitest'
|
||||||
|
|
||||||
|
// Mock module
|
||||||
|
vi.mock('@/lib/db', () => ({
|
||||||
|
db: {
|
||||||
|
character: {
|
||||||
|
findMany: vi.fn(),
|
||||||
|
create: vi.fn(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
// Mock function
|
||||||
|
const mockFetch = vi.fn()
|
||||||
|
global.fetch = mockFetch
|
||||||
|
|
||||||
|
// Mock implementation
|
||||||
|
mockFetch.mockResolvedValue({
|
||||||
|
ok: true,
|
||||||
|
json: async () => ({ name: 'Test' }),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Spy on method
|
||||||
|
const spy = vi.spyOn(console, 'error').mockImplementation(() => {})
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
afterEach(() => {
|
||||||
|
vi.clearAllMocks()
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### [ERROR] Deprecated: Jest Mocks
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// OLD PATTERN
|
||||||
|
jest.mock('./module')
|
||||||
|
jest.fn()
|
||||||
|
jest.spyOn()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Playwright E2E Testing
|
||||||
|
|
||||||
|
### [OK] Modern: Playwright Config
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// playwright.config.ts
|
||||||
|
import { defineConfig, devices } from '@playwright/test'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: './test/e2e',
|
||||||
|
fullyParallel: true,
|
||||||
|
forbidOnly: !!process.env.CI,
|
||||||
|
retries: process.env.CI ? 2 : 0,
|
||||||
|
workers: process.env.CI ? 1 : undefined,
|
||||||
|
reporter: 'html',
|
||||||
|
|
||||||
|
use: {
|
||||||
|
baseURL: 'http://localhost:3000',
|
||||||
|
trace: 'on-first-retry',
|
||||||
|
screenshot: 'only-on-failure',
|
||||||
|
},
|
||||||
|
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: 'chromium',
|
||||||
|
use: { ...devices['Desktop Chrome'] },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'firefox',
|
||||||
|
use: { ...devices['Desktop Firefox'] },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'webkit',
|
||||||
|
use: { ...devices['Desktop Safari'] },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
webServer: {
|
||||||
|
command: 'npm run dev',
|
||||||
|
url: 'http://localhost:3000',
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### [OK] E2E Test Patterns
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
|
||||||
|
test.describe('Character Creation', () => {
|
||||||
|
test('creates new character', async ({ page }) => {
|
||||||
|
await page.goto('/characters')
|
||||||
|
|
||||||
|
// Navigate to create form
|
||||||
|
await page.getByRole('button', { name: /create character/i }).click()
|
||||||
|
|
||||||
|
// Fill form
|
||||||
|
await page.getByLabel(/name/i).fill('Aria Shadowblade')
|
||||||
|
await page.getByLabel(/class/i).selectOption('rogue')
|
||||||
|
await page.getByLabel(/level/i).fill('5')
|
||||||
|
|
||||||
|
// Submit
|
||||||
|
await page.getByRole('button', { name: /save/i }).click()
|
||||||
|
|
||||||
|
// Verify success
|
||||||
|
await expect(page.getByText('Aria Shadowblade')).toBeVisible()
|
||||||
|
await expect(page).toHaveURL(/\/characters\/\d+/)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('shows validation errors', async ({ page }) => {
|
||||||
|
await page.goto('/characters/create')
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: /save/i }).click()
|
||||||
|
|
||||||
|
await expect(page.getByText(/name is required/i)).toBeVisible()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Accessibility Testing
|
||||||
|
|
||||||
|
### [OK] Component-Level A11y
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { render } from '@testing-library/react'
|
||||||
|
import { axe, toHaveNoViolations } from 'jest-axe'
|
||||||
|
import { CharacterCard } from './CharacterCard'
|
||||||
|
|
||||||
|
expect.extend(toHaveNoViolations)
|
||||||
|
|
||||||
|
describe('CharacterCard Accessibility', () => {
|
||||||
|
it('has no accessibility violations', async () => {
|
||||||
|
const { container } = render(
|
||||||
|
<CharacterCard
|
||||||
|
character={{
|
||||||
|
name: 'Test',
|
||||||
|
class: 'Warrior',
|
||||||
|
level: 5,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
const results = await axe(container)
|
||||||
|
expect(results).toHaveNoViolations()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### [OK] E2E A11y with Playwright
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { test, expect } from '@playwright/test'
|
||||||
|
import AxeBuilder from '@axe-core/playwright'
|
||||||
|
|
||||||
|
test.describe('Accessibility', () => {
|
||||||
|
test('homepage meets WCAG 2.1 AA', async ({ page }) => {
|
||||||
|
await page.goto('/')
|
||||||
|
|
||||||
|
const accessibilityScanResults = await new AxeBuilder({ page })
|
||||||
|
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
|
||||||
|
.analyze()
|
||||||
|
|
||||||
|
expect(accessibilityScanResults.violations).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('character form is accessible', async ({ page }) => {
|
||||||
|
await page.goto('/characters/create')
|
||||||
|
|
||||||
|
const results = await new AxeBuilder({ page })
|
||||||
|
.exclude('#third-party-widget') // Exclude external widgets
|
||||||
|
.analyze()
|
||||||
|
|
||||||
|
expect(results.violations).toEqual([])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Coverage
|
||||||
|
|
||||||
|
### [OK] Coverage Configuration
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// vitest.config.ts
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
coverage: {
|
||||||
|
provider: 'v8',
|
||||||
|
reporter: ['text', 'json', 'html', 'lcov'],
|
||||||
|
exclude: [
|
||||||
|
'node_modules/',
|
||||||
|
'test/',
|
||||||
|
'**/*.config.{ts,js}',
|
||||||
|
'**/*.d.ts',
|
||||||
|
'**/types.ts',
|
||||||
|
],
|
||||||
|
thresholds: {
|
||||||
|
lines: 80,
|
||||||
|
functions: 80,
|
||||||
|
branches: 80,
|
||||||
|
statements: 80,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running Coverage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run with coverage
|
||||||
|
vitest run --coverage
|
||||||
|
|
||||||
|
# Watch mode with coverage
|
||||||
|
vitest --coverage --watch
|
||||||
|
|
||||||
|
# Coverage for specific files
|
||||||
|
vitest run --coverage --changed
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Best Practices
|
||||||
|
|
||||||
|
### 1. Query Priority (React Testing Library)
|
||||||
|
|
||||||
|
Use queries in this priority order:
|
||||||
|
|
||||||
|
1. **Accessible to everyone**:
|
||||||
|
- `getByRole`
|
||||||
|
- `getByLabelText`
|
||||||
|
- `getByPlaceholderText`
|
||||||
|
- `getByText`
|
||||||
|
|
||||||
|
2. **Semantic queries**:
|
||||||
|
- `getByAltText`
|
||||||
|
- `getByTitle`
|
||||||
|
|
||||||
|
3. **Test IDs** (last resort):
|
||||||
|
- `getByTestId`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// [OK] Good
|
||||||
|
screen.getByRole('button', { name: /submit/i })
|
||||||
|
screen.getByLabelText(/email/i)
|
||||||
|
|
||||||
|
// [ERROR] Avoid
|
||||||
|
screen.getByTestId('submit-button')
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Async Testing
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { waitFor, screen } from '@testing-library/react'
|
||||||
|
|
||||||
|
// [OK] Use waitFor for async assertions
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText(/success/i)).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
// [OK] Use findBy queries (combines getBy + waitFor)
|
||||||
|
const element = await screen.findByText(/success/i)
|
||||||
|
|
||||||
|
// [ERROR] Don't use arbitrary timeouts
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. User Interactions
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import userEvent from '@testing-library/user-event'
|
||||||
|
|
||||||
|
// [OK] Use userEvent (more realistic)
|
||||||
|
const user = userEvent.setup()
|
||||||
|
await user.type(input, 'text')
|
||||||
|
await user.click(button)
|
||||||
|
|
||||||
|
// [ERROR] Avoid fireEvent
|
||||||
|
import { fireEvent } from '@testing-library/react'
|
||||||
|
fireEvent.click(button)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Test Independence
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// [OK] Each test is independent
|
||||||
|
describe('CharacterList', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
// Fresh data for each test
|
||||||
|
mockCharacters = [...]
|
||||||
|
})
|
||||||
|
|
||||||
|
it('displays characters', () => {
|
||||||
|
render(<CharacterList characters={mockCharacters} />)
|
||||||
|
// Test logic
|
||||||
|
})
|
||||||
|
|
||||||
|
it('filters characters', () => {
|
||||||
|
render(<CharacterList characters={mockCharacters} />)
|
||||||
|
// Test logic
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// [ERROR] Tests depend on each other
|
||||||
|
let sharedState
|
||||||
|
|
||||||
|
it('creates character', () => {
|
||||||
|
sharedState = createCharacter()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('updates character', () => {
|
||||||
|
updateCharacter(sharedState) // Depends on previous test
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. What to Test
|
||||||
|
|
||||||
|
**[OK] Do Test:**
|
||||||
|
- User interactions and workflows
|
||||||
|
- Component rendering with different props
|
||||||
|
- Conditional logic and edge cases
|
||||||
|
- Form validation
|
||||||
|
- Error handling
|
||||||
|
- Accessibility
|
||||||
|
|
||||||
|
**[ERROR] Don't Test:**
|
||||||
|
- Implementation details
|
||||||
|
- Third-party library internals
|
||||||
|
- Exact CSS values
|
||||||
|
- Component internal state
|
||||||
|
- Trivial code
|
||||||
|
|
||||||
|
## Common Anti-Patterns
|
||||||
|
|
||||||
|
### [ERROR] Testing Implementation Details
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// BAD: Testing state directly
|
||||||
|
const { result } = renderHook(() => useCounter())
|
||||||
|
expect(result.current.count).toBe(0)
|
||||||
|
|
||||||
|
// GOOD: Testing behavior
|
||||||
|
render(<Counter />)
|
||||||
|
expect(screen.getByText(/count: 0/i)).toBeInTheDocument()
|
||||||
|
```
|
||||||
|
|
||||||
|
### [ERROR] Snapshot Testing Overuse
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// BAD: Large snapshots
|
||||||
|
expect(container).toMatchSnapshot()
|
||||||
|
|
||||||
|
// GOOD: Specific assertions
|
||||||
|
expect(screen.getByRole('heading')).toHaveTextContent('Characters')
|
||||||
|
```
|
||||||
|
|
||||||
|
### [ERROR] Not Cleaning Up
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// BAD: No cleanup
|
||||||
|
afterEach(() => {
|
||||||
|
// Missing cleanup
|
||||||
|
})
|
||||||
|
|
||||||
|
// GOOD: Proper cleanup
|
||||||
|
afterEach(() => {
|
||||||
|
vi.clearAllMocks()
|
||||||
|
cleanup()
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Package Scripts
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"test": "vitest",
|
||||||
|
"test:ui": "vitest --ui",
|
||||||
|
"test:coverage": "vitest run --coverage",
|
||||||
|
"test:watch": "vitest --watch",
|
||||||
|
"test:e2e": "playwright test",
|
||||||
|
"test:e2e:ui": "playwright test --ui",
|
||||||
|
"test:e2e:debug": "playwright test --debug",
|
||||||
|
"test:a11y": "playwright test a11y.spec.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validation Checklist
|
||||||
|
|
||||||
|
When reviewing testing skills, check for:
|
||||||
|
|
||||||
|
- [ ] Uses Vitest (not Jest)
|
||||||
|
- [ ] Uses React Testing Library (not Enzyme)
|
||||||
|
- [ ] Uses Playwright for E2E
|
||||||
|
- [ ] Includes accessibility testing with axe-core
|
||||||
|
- [ ] Proper query priority (role, label, text)
|
||||||
|
- [ ] userEvent instead of fireEvent
|
||||||
|
- [ ] Async testing with waitFor/findBy
|
||||||
|
- [ ] Proper mocking with vi.*
|
||||||
|
- [ ] Coverage configuration
|
||||||
|
- [ ] Independent tests
|
||||||
|
- [ ] Tests behavior, not implementation
|
||||||
|
- [ ] Cleanup in afterEach
|
||||||
|
- [ ] Descriptive test names
|
||||||
|
- [ ] AAA pattern (Arrange, Act, Assert)
|
||||||
|
|
||||||
|
## Migration Guide
|
||||||
|
|
||||||
|
### Jest → Vitest
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Jest
|
||||||
|
import { jest } from '@jest/globals'
|
||||||
|
jest.fn()
|
||||||
|
jest.spyOn()
|
||||||
|
jest.mock()
|
||||||
|
|
||||||
|
// Vitest
|
||||||
|
import { vi } from 'vitest'
|
||||||
|
vi.fn()
|
||||||
|
vi.spyOn()
|
||||||
|
vi.mock()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update Imports
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Old
|
||||||
|
import { describe, it, expect } from '@jest/globals'
|
||||||
|
|
||||||
|
// New
|
||||||
|
import { describe, it, expect } from 'vitest'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update Config
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Remove
|
||||||
|
npm uninstall jest @types/jest
|
||||||
|
|
||||||
|
# Install
|
||||||
|
npm install -D vitest @vitejs/plugin-react jsdom
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
### Must Use (Modern)
|
||||||
|
- [OK] Vitest (not Jest)
|
||||||
|
- [OK] React Testing Library
|
||||||
|
- [OK] Playwright
|
||||||
|
- [OK] axe-core for a11y
|
||||||
|
- [OK] userEvent for interactions
|
||||||
|
- [OK] waitFor/findBy for async
|
||||||
|
- [OK] getByRole queries
|
||||||
|
|
||||||
|
### Must Avoid (Deprecated)
|
||||||
|
- [ERROR] Jest
|
||||||
|
- [ERROR] Enzyme
|
||||||
|
- [ERROR] fireEvent
|
||||||
|
- [ERROR] getByTestId (overuse)
|
||||||
|
- [ERROR] Snapshot tests (overuse)
|
||||||
|
- [ERROR] Testing implementation details
|
||||||
|
- [ERROR] Arbitrary timeouts
|
||||||
@@ -0,0 +1,279 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Analyze skill structure and validate against Anthropic standards.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python analyze_skill_structure.py --skill skills/category/skill-name
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import io
|
||||||
|
import re
|
||||||
|
import argparse
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List, Tuple
|
||||||
|
|
||||||
|
# Configure stdout for UTF-8 encoding (prevents Windows encoding errors)
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||||||
|
|
||||||
|
def parse_frontmatter(content: str) -> Tuple[Dict[str, str], str]:
|
||||||
|
"""Extract and parse YAML frontmatter from SKILL.md"""
|
||||||
|
if not content.startswith('---'):
|
||||||
|
return {}, content
|
||||||
|
|
||||||
|
match = re.match(r'^---\n(.*?)\n---\n(.*)$', content, re.DOTALL)
|
||||||
|
if not match:
|
||||||
|
return {}, content
|
||||||
|
|
||||||
|
frontmatter_text = match.group(1)
|
||||||
|
body = match.group(2)
|
||||||
|
|
||||||
|
# Parse frontmatter
|
||||||
|
frontmatter = {}
|
||||||
|
for line in frontmatter_text.split('\n'):
|
||||||
|
if ':' in line:
|
||||||
|
key, value = line.split(':', 1)
|
||||||
|
frontmatter[key.strip()] = value.strip()
|
||||||
|
|
||||||
|
return frontmatter, body
|
||||||
|
|
||||||
|
def check_name_convention(name: str) -> List[str]:
|
||||||
|
"""Check if name follows hyphen-case convention"""
|
||||||
|
issues = []
|
||||||
|
|
||||||
|
if not re.match(r'^[a-z0-9-]+$', name):
|
||||||
|
issues.append(f"Name '{name}' should use only lowercase letters, digits, and hyphens")
|
||||||
|
|
||||||
|
if name.startswith('-') or name.endswith('-'):
|
||||||
|
issues.append(f"Name '{name}' cannot start or end with hyphen")
|
||||||
|
|
||||||
|
if '--' in name:
|
||||||
|
issues.append(f"Name '{name}' cannot contain consecutive hyphens")
|
||||||
|
|
||||||
|
return issues
|
||||||
|
|
||||||
|
def check_description(description: str) -> List[str]:
|
||||||
|
"""Check description quality"""
|
||||||
|
issues = []
|
||||||
|
|
||||||
|
if len(description) > 1024:
|
||||||
|
issues.append(f"Description is {len(description)} characters (max 1024)")
|
||||||
|
|
||||||
|
if '<' in description or '>' in description:
|
||||||
|
issues.append("Description cannot contain angle brackets")
|
||||||
|
|
||||||
|
# Check for third-person voice
|
||||||
|
first_person_patterns = [
|
||||||
|
r'\bI\s+',
|
||||||
|
r'\bwe\s+',
|
||||||
|
r'\bour\s+',
|
||||||
|
r'\bmy\s+',
|
||||||
|
]
|
||||||
|
for pattern in first_person_patterns:
|
||||||
|
if re.search(pattern, description, re.IGNORECASE):
|
||||||
|
issues.append("Description should use third-person voice, not first-person")
|
||||||
|
break
|
||||||
|
|
||||||
|
# Check if it includes trigger terms
|
||||||
|
if 'trigger' not in description.lower() and 'include' not in description.lower():
|
||||||
|
issues.append("Description should explicitly mention trigger terms")
|
||||||
|
|
||||||
|
return issues
|
||||||
|
|
||||||
|
def check_second_person_usage(body: str) -> List[Tuple[int, str]]:
|
||||||
|
"""Find second-person usage in skill body"""
|
||||||
|
violations = []
|
||||||
|
|
||||||
|
patterns = [
|
||||||
|
r'\byou\s+should\b',
|
||||||
|
r'\byou\s+can\b',
|
||||||
|
r'\byou\s+need\s+to\b',
|
||||||
|
r'\byou\s+have\s+to\b',
|
||||||
|
r'\byou\s+must\b',
|
||||||
|
r'\byou\s+will\b',
|
||||||
|
r'\byour\b',
|
||||||
|
]
|
||||||
|
|
||||||
|
lines = body.split('\n')
|
||||||
|
for i, line in enumerate(lines, start=1):
|
||||||
|
# Skip code blocks
|
||||||
|
if line.strip().startswith('```') or line.strip().startswith(' '):
|
||||||
|
continue
|
||||||
|
|
||||||
|
for pattern in patterns:
|
||||||
|
if re.search(pattern, line, re.IGNORECASE):
|
||||||
|
violations.append((i, line.strip()))
|
||||||
|
break
|
||||||
|
|
||||||
|
return violations
|
||||||
|
|
||||||
|
def check_emoji_usage(content: str) -> List[Tuple[int, str]]:
|
||||||
|
"""Find emoji usage in skill content"""
|
||||||
|
violations = []
|
||||||
|
|
||||||
|
# Common emojis that should be replaced with ASCII
|
||||||
|
# Using Unicode ranges to avoid encoding issues
|
||||||
|
emoji_pattern = re.compile(
|
||||||
|
r'[\U0001F300-\U0001F9FF' # Miscellaneous Symbols and Pictographs, Supplemental
|
||||||
|
r'\u2600-\u26FF' # Miscellaneous Symbols
|
||||||
|
r'\u2700-\u27BF' # Dingbats
|
||||||
|
r'\U0001F600-\U0001F64F' # Emoticons
|
||||||
|
r'\U0001F680-\U0001F6FF' # Transport and Map
|
||||||
|
r'\U0001F1E0-\U0001F1FF]' # Regional Indicator Symbols
|
||||||
|
)
|
||||||
|
|
||||||
|
lines = content.split('\n')
|
||||||
|
for i, line in enumerate(lines, start=1):
|
||||||
|
if emoji_pattern.search(line):
|
||||||
|
violations.append((i, line.strip()[:100])) # Limit to 100 chars
|
||||||
|
|
||||||
|
return violations
|
||||||
|
|
||||||
|
def analyze_skill(skill_path: Path) -> Dict:
|
||||||
|
"""Analyze skill structure and return findings"""
|
||||||
|
results = {
|
||||||
|
'valid': True,
|
||||||
|
'critical_issues': [],
|
||||||
|
'warnings': [],
|
||||||
|
'suggestions': [],
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if SKILL.md exists
|
||||||
|
skill_md = skill_path / 'SKILL.md'
|
||||||
|
if not skill_md.exists():
|
||||||
|
results['valid'] = False
|
||||||
|
results['critical_issues'].append('SKILL.md not found')
|
||||||
|
return results
|
||||||
|
|
||||||
|
# Read SKILL.md
|
||||||
|
content = skill_md.read_text(encoding='utf-8')
|
||||||
|
|
||||||
|
# Parse frontmatter
|
||||||
|
frontmatter, body = parse_frontmatter(content)
|
||||||
|
|
||||||
|
if not frontmatter:
|
||||||
|
results['valid'] = False
|
||||||
|
results['critical_issues'].append('No YAML frontmatter found')
|
||||||
|
return results
|
||||||
|
|
||||||
|
# Check required fields
|
||||||
|
if 'name' not in frontmatter:
|
||||||
|
results['valid'] = False
|
||||||
|
results['critical_issues'].append('Missing "name" field in frontmatter')
|
||||||
|
else:
|
||||||
|
name = frontmatter['name']
|
||||||
|
name_issues = check_name_convention(name)
|
||||||
|
if name_issues:
|
||||||
|
results['valid'] = False
|
||||||
|
results['critical_issues'].extend(name_issues)
|
||||||
|
|
||||||
|
if 'description' not in frontmatter:
|
||||||
|
results['valid'] = False
|
||||||
|
results['critical_issues'].append('Missing "description" field in frontmatter')
|
||||||
|
else:
|
||||||
|
description = frontmatter['description']
|
||||||
|
desc_issues = check_description(description)
|
||||||
|
if desc_issues:
|
||||||
|
for issue in desc_issues:
|
||||||
|
if 'angle brackets' in issue or 'characters' in issue:
|
||||||
|
results['valid'] = False
|
||||||
|
results['critical_issues'].append(issue)
|
||||||
|
else:
|
||||||
|
results['warnings'].append(issue)
|
||||||
|
|
||||||
|
# Check for second-person usage
|
||||||
|
second_person = check_second_person_usage(body)
|
||||||
|
if second_person:
|
||||||
|
results['warnings'].append(f"Found {len(second_person)} instances of second-person usage")
|
||||||
|
results['second_person_violations'] = second_person[:5] # First 5
|
||||||
|
|
||||||
|
# Check for emoji usage (critical issue)
|
||||||
|
emoji_violations = check_emoji_usage(content)
|
||||||
|
if emoji_violations:
|
||||||
|
results['valid'] = False
|
||||||
|
results['critical_issues'].append(f"Found {len(emoji_violations)} emoji characters (must use ASCII alternatives)")
|
||||||
|
results['emoji_violations'] = emoji_violations[:5] # First 5
|
||||||
|
|
||||||
|
# Check resource directories
|
||||||
|
resources = {
|
||||||
|
'scripts': skill_path / 'scripts',
|
||||||
|
'references': skill_path / 'references',
|
||||||
|
'assets': skill_path / 'assets',
|
||||||
|
}
|
||||||
|
|
||||||
|
mentioned_resources = {}
|
||||||
|
for resource_type, resource_path in resources.items():
|
||||||
|
pattern = f'{resource_type}/'
|
||||||
|
if pattern in body:
|
||||||
|
mentioned_resources[resource_type] = resource_path.exists()
|
||||||
|
|
||||||
|
for resource_type, exists in mentioned_resources.items():
|
||||||
|
if not exists:
|
||||||
|
results['suggestions'].append(
|
||||||
|
f"Skill mentions {resource_type}/ but directory doesn't exist"
|
||||||
|
)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description='Analyze skill structure')
|
||||||
|
parser.add_argument('--skill', required=True, help='Path to skill directory')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
skill_path = Path(args.skill)
|
||||||
|
|
||||||
|
if not skill_path.exists():
|
||||||
|
print(f"Error: Skill directory not found: {skill_path}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(f"Analyzing skill: {skill_path}")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
results = analyze_skill(skill_path)
|
||||||
|
|
||||||
|
# Print results
|
||||||
|
if results['valid']:
|
||||||
|
print("[PASS] Skill structure is valid!")
|
||||||
|
else:
|
||||||
|
print("[FAIL] Skill structure has critical issues")
|
||||||
|
|
||||||
|
print()
|
||||||
|
|
||||||
|
if results['critical_issues']:
|
||||||
|
print("CRITICAL ISSUES:")
|
||||||
|
for issue in results['critical_issues']:
|
||||||
|
print(f" [ERROR] {issue}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
if results['warnings']:
|
||||||
|
print("WARNINGS:")
|
||||||
|
for warning in results['warnings']:
|
||||||
|
print(f" [WARN] {warning}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
if 'second_person_violations' in results:
|
||||||
|
print("SECOND-PERSON USAGE (first 5):")
|
||||||
|
for line_num, line in results['second_person_violations']:
|
||||||
|
print(f" Line {line_num}: {line}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
if 'emoji_violations' in results:
|
||||||
|
print("EMOJI USAGE DETECTED (first 5):")
|
||||||
|
print(" Emojis must be replaced with ASCII alternatives:")
|
||||||
|
print(" [OK]/[PASS], [ERROR]/[FAIL], [WARN], [TIP], [INFO], [X]")
|
||||||
|
for line_num, line in results['emoji_violations']:
|
||||||
|
print(f" Line {line_num}: {line}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
if results['suggestions']:
|
||||||
|
print("SUGGESTIONS:")
|
||||||
|
for suggestion in results['suggestions']:
|
||||||
|
print(f" [TIP] {suggestion}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
sys.exit(0 if results['valid'] else 1)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user