
Skill Coach
- 139 installs
- 178 repo stars
- Updated July 14, 2026
- erichowens/some_claude_skills
Coach practitioners through creating, debugging, and improving Claude skills via iterative feedback on triggers, instructions, examples, and real task outcomes.
About
Provides hands-on mentorship for Claude Code skill authors: diagnosing misfires, sharpening activation rules, improving examples, and building confidence through practice tasks so custom agent capabilities become reliable extensions of everyday development work.
- Iterative skill improvement loops
- Trigger tuning guidance
- Instruction clarity reviews
- Real-task outcome debriefs
- Beginner-to-advanced skill mentoring
Skill Coach by the numbers
- 139 all-time installs (skills.sh)
- Ranked #219 of 782 Skill Development skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/erichowens/some_claude_skills --skill skill-coachAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 139 |
|---|---|
| repo stars | ★ 178 |
| Last updated | July 14, 2026 |
| Repository | erichowens/some_claude_skills ↗ |
What it does
Coach practitioners through creating, debugging, and improving Claude skills via iterative feedback on triggers, instructions, examples, and real task outcomes.
Files
Skill Coach: Creating Expert-Level Agent Skills
Encode real domain expertise, not just surface-level instructions. Focus on shibboleths - the deep knowledge that separates novices from experts.
When to Use This Skill
Use for:
- Creating new Agent Skills from scratch
- Reviewing/auditing existing skills
- Improving skill activation rates
- Adding domain expertise to skills
- Debugging why skills don't activate
NOT for:
- General Claude Code features (slash commands, MCPs)
- Non-skill coding advice
- Debugging runtime errors (use domain skills)
Quick Wins
Immediate improvements for existing skills: 1. Add NOT clause to description → Prevents false activation 2. Add 1-2 anti-patterns → Prevents common mistakes 3. Check line count (run validator) → Should be fewer than 500 lines 4. Remove dead files → Delete unreferenced scripts/references 5. Test activation → Questions that should/shouldn't trigger it
What Makes a Great Skill
Great skills are progressive disclosure machines that: 1. Activate precisely - Specific keywords + NOT clause 2. Encode shibboleths - Expert knowledge that separates novice from expert 3. Surface anti-patterns - "If you see X, that's wrong because Y, use Z" 4. Capture temporal knowledge - "Pre-2024: X. 2024+: Y" 5. Know their limits - "Use for A, B, C. NOT for D, E, F" 6. Provide decision trees - Not templates, but "If X then A, if Y then B" 7. Stay under 500 lines - Core in SKILL.md, deep dives in /references
Core Principles
Progressive Disclosure
- Phase 1 (~100 tokens): Metadata - "Should I activate?"
- Phase 2 (<5k tokens): SKILL.md - "How do I do this?"
- Phase 3 (as needed): References - "Show me the details"
Critical: Keep SKILL.md under 500 lines. Split details into /references.
Description Formula
[What] [Use for] [Keywords] NOT for [Exclusions]
❌ Bad: "Helps with images"
⚠️ Better: "Image processing with CLIP"
✅ Good: "CLIP semantic search. Use for image-text matching.
Activate on 'CLIP', 'embeddings'. NOT for counting, spatial reasoning."SKILL.md Template
---
name: your-skill-name
description: [What] [When] [Triggers]. NOT for [Exclusions].
allowed-tools: Read,Write # Minimal only
---
# Skill Name
[One sentence purpose]
## When to Use
✅ Use for: [A, B, C]
❌ NOT for: [D, E, F]
## Core Instructions
[Step-by-step, decision trees, not templates]
## Common Anti-Patterns
### [Pattern]
**Symptom**: [Recognition]
**Problem**: [Why wrong]
**Solution**: [Better approach]Frontmatter Rules (CRITICAL)
Only these frontmatter keys are allowed by Claude's skill marketplace:
| Key | Required | Purpose |
|---|---|---|
name | ✅ | Lowercase-hyphenated identifier |
description | ✅ | Activation keywords + NOT clause |
allowed-tools | ⚠️ | Comma-separated tool names |
license | ❌ | e.g., "MIT" |
metadata | ❌ | Custom key-value pairs |
Invalid keys that will FAIL upload:
# ❌ WRONG - These will break skill upload
integrates_with:
- orchestrator
triggers:
- "activate on this"
tools: Read,Write
outputs: formatted text
coordinates_with: other-skill
python_dependencies:
- numpyMove custom info to the body:
## Integrations
Works with: orchestrator, team-builder
## Activation Triggers
Responds to: "create skill", "review skill", "skill quality"Validation command:
# Find invalid frontmatter keys
for skill in .claude/skills/*/SKILL.md; do
sed -n '/^---$/,/^---$/p' "$skill" | grep -E "^[a-zA-Z_-]+:" | cut -d: -f1 | \
grep -vE "^(name|description|license|allowed-tools|metadata)$" && \
echo " ^ in $(basename $(dirname $skill))"
doneSkill Structure
Mandatory:
your-skill/
└── SKILL.md # Core instructions (max 500 lines)Strongly Recommended (self-contained skills):
├── scripts/ # Working code - NOT templates
├── mcp-server/ # Custom MCP if external APIs needed
├── agents/ # Subagent definitions if orchestration needed
├── references/ # Deep dives on domain knowledge
└── CHANGELOG.md # Version historySelf-Contained Skills (RECOMMENDED)
Skills with working tools are immediately useful. See references/self-contained-tools.md for full patterns.
Quick decision: External APIs? → MCP. Multi-step workflow? → Subagents. Repeatable operations? → Scripts.
Decision Trees
When to create a NEW skill?
- ✅ Domain expertise not in existing skills
- ✅ Pattern repeats across 3+ projects
- ✅ Anti-patterns you want to prevent
- ❌ One-time task → Just do it directly
- ❌ Existing skill could be extended → Improve that one
Skill vs Subagent vs MCP?
- Skill: Domain expertise, decision trees (no runtime state)
- Subagent: Multi-step workflows needing tool orchestration
- MCP: External APIs, auth, stateful connections
Skill Creation Process (6 Steps)
Follow these steps in order when creating a new skill:
Step 1: Understand with Concrete Examples
Skip only if usage patterns are already clear. Ask:
- "What functionality should this skill support?"
- "Can you give examples of how it would be used?"
- "What would a user say that should trigger this skill?"
Step 2: Plan Reusable Contents
For each example, analyze: 1. How to execute from scratch 2. What scripts, references, assets would help with repeated execution
Example analyses:
pdf-editorfor "rotate this PDF" → Needsscripts/rotate_pdf.pyfrontend-webapp-builder→ Needsassets/hello-world/templatebig-queryskill → Needsreferences/schema.mdfor table schemas
Step 3: Initialize the Skill
Create the skill directory structure:
your-skill/
├── SKILL.md # Core instructions (max 500 lines)
├── scripts/ # Working code - NOT templates
├── references/ # Deep dives on domain knowledge
└── assets/ # Files used in output (templates, icons)Step 4: Write SKILL.md
- Write in imperative/infinitive form ("To accomplish X, do Y")
- Answer: Purpose? When to use? How to use bundled resources?
- Reference all scripts/references so Claude knows they exist
Step 5: Validate and Package
# Validate skill structure and content
python scripts/validate_skill.py <path>
# Check for self-contained tool completeness
python scripts/check_self_contained.py <path>Step 6: Iterate
After real-world use: 1. Notice struggles or inefficiencies 2. Identify how SKILL.md or bundled resources should be updated 3. Implement changes and test again
---
Common Workflows
Create Skill from Expertise: 1. Define scope: What expertise? What keywords? What NOT to handle? 2. Write description with keywords and NOT clause 3. Add anti-patterns you've observed 4. Test activation thoroughly
Debug Activation Issues (flowchart):
Skill not activating when expected?
├── Check description has specific keywords
│ ├── NO → Add "Activate on: keyword1, keyword2"
│ └── YES → Check if query contains those keywords
│ ├── NO → Add missing keyword variations
│ └── YES → Check for conflicting NOT clause
│ ├── YES → Narrow exclusion scope
│ └── NO → Check file structure
│ ├── SKILL.md missing → Create it
│ └── Wrong location → Move to .claude/skills/
Skill activating when it shouldn't?
├── Missing NOT clause?
│ ├── YES → Add "NOT for: exclusion1, exclusion2"
│ └── NO → NOT clause too narrow
│ └── Expand exclusions based on false positive queriesRun python scripts/test_activation.py <path> to validate
Recursive Self-Improvement (use this skill to improve skills): 1. Run python scripts/validate_skill.py <path> → Get validation report 2. Run python scripts/check_self_contained.py <path> → Check tool completeness 3. Address ERRORS first, then WARNINGS, then SUGGESTIONS 4. Re-run validation until clean 5. Update CHANGELOG.md with improvements made
Tool Permissions
Guidelines:
- Read-only skill:
Read,Grep,Glob - File modifier:
Read,Write,Edit - Build integration:
Read,Write,Bash(npm:*,git:*) - ⚠️ Never: Unrestricted
Bashfor untrusted skills
Success Metrics
| Metric | Target |
|---|---|
| Correct activation | >90% |
| False positive rate | <5% |
| Token usage | <5k typical |
Reference Files
| File | Contents |
|---|---|
references/antipatterns.md | Domain shibboleths and anti-pattern catalog with case studies |
references/shibboleths.md | Expert vs novice knowledge patterns |
references/validation-checklist.md | Complete review and testing guide |
references/self-contained-tools.md | Scripts, MCP servers, and subagent implementation patterns |
references/scoring-rubric.md | Quantitative skill evaluation (0-10 scoring) |
references/skill-composition.md | Cross-skill dependencies and composition patterns |
references/skill-lifecycle.md | Maintenance, versioning, and deprecation guidance |
references/mcp_vs_scripts.md | Architectural decision guide: Skills vs Agents vs MCPs vs Scripts |
---
This skill guides: Skill creation | Skill auditing | Anti-pattern detection | Progressive disclosure | Domain expertise encoding
Changelog
All notable changes to the skill-coach skill will be documented in this file.
[2.2.0] - 2025-12-04
Added
scripts/test_activation.py- Automated activation testing with keyword extraction- Extracts positive/negative keywords from description
- Generates test queries automatically
- Reports pass/fail rate against 90% target
references/scoring-rubric.md- Quantitative skill evaluation (0-10 scoring)- 5 scoring categories: Activation Precision, Domain Expertise, Progressive Disclosure, Self-Containment, Maintainability
- Composite score formula with grade mapping (A-F)
references/skill-composition.md- Cross-skill dependency patterns- Sequential, Parallel, Hierarchical dependency types
- Composition anti-patterns with fixes
- Example photo analysis pipeline
references/skill-lifecycle.md- Maintenance and versioning guidance- 5 lifecycle stages: DRAFT → ACTIVE → MATURE → DEPRECATED → ARCHIVED
- Maintenance checklists (monthly, quarterly, annually)
- Health indicators table
- 4 real-world failure case studies added to
references/antipatterns.md: - Photo Expert Explosion (Everything Skill anti-pattern)
- Phantom MCP (Reference Illusion anti-pattern)
- Time Bomb (stale temporal knowledge)
- Activation Black Hole (generic description)
- Activation debugging flowchart in SKILL.md (ASCII decision tree)
- Recursive self-improvement workflow in SKILL.md
Changed
- Description now uses third person ("Activates for..." not "when users mention...")
- Bash permissions scoped from
BashtoBash(python:*,wc:*,find:*,grep:*) - Added "improve skill" to activation keywords
- Consolidated duplicate antipatterns files (merged
anti-patterns.mdintoantipatterns.md) - Updated reference table with 4 new files
Fixed
- Validation warning: Description no longer uses first/second person
- Validation warning: Bash tool is now scoped (was unrestricted)
Metrics
- Activation precision: 100% (12/12 tests passed)
- SKILL.md: 240 lines (within 500 line limit)
- New reference files: 4 (scoring-rubric, skill-composition, skill-lifecycle, test_activation.py)
[2.1.1] - 2025-12-01
Added
scripts/check_self_contained.py- Validates skills ship working tools, not just instructions- Detects Phantom Tools (referenced files that don't exist)
- Detects Template Soup (scripts with TODO/FIXME markers)
- Validates MCP server completeness (package.json, dependencies, source)
- Checks agent definition completeness
- Reports "instructions only" vs "self-contained with tools"
Changed
- skill-coach now practices what it preaches (eats its own dogfood)
[2.1.0] - 2025-12-01
Added
- Self-Contained Skills section (RECOMMENDED) - strongly advocates shipping working tools
references/self-contained-tools.md- Complete implementation patterns for:- Working scripts (not templates)
- MCP server implementations
- Subagent definitions and orchestration
- Decision tree: "What tools does my skill need?"
- Anti-patterns: Phantom Tools, Template Soup, Dependency Hell, MCP Without Purpose
- Self-contained checklist for skill authors
Changed
- Skill structure now shows scripts/mcp-server/agents as Strongly Recommended
- Philosophy shift: "Skills with working tools are immediately useful"
Why This Matters
Skills that only provide instructions require users to implement everything themselves. Skills that ship working tools let users be productive immediately.
[2.0.0] - 2025-11-29
Changed
- SKILL.md restructured for progressive disclosure (471 → ~161 lines)
- Content organized into quick reference format
Added
references/anti-patterns.md- 12 documented anti-patterns with fixesreferences/shibboleths.md- 9 expert vs novice indicatorsreferences/validation-checklist.md- 30+ validation criteria organized by category- Decision tree format for common scenarios
- Integration guide with other skills
Migration
- No changes to frontmatter or activation triggers
- Validation checklist now available for systematic review
- Anti-patterns guide helps avoid common mistakes
[1.2.0] - 2025-11-26
Added
- MCP & Tool Research (MANDATORY) section - comprehensive guide for researching MCPs
- Research process with 4 steps: Web Search, Check Registries, Evaluate Quality, Add to Skill
- Domain-Specific MCP Examples table
- Anti-pattern: Assuming No MCPs Exist
- Anti-pattern: Adding MCPs Without Testing
- MCP research added to Quick Start workflow (step 2)
- MCP research added to Review Checklist (CRITICAL section)
Changed
- Updated Review Checklist:
allowed-toolsguidance now emphasizes including relevant MCPs - Quick Start now has 6 steps instead of 5 (added MCP research step)
[1.1.0] - 2025-11-26
Added
- Versioning Skills section with complete guidance
- CHANGELOG.md format template
- Version numbering explanation (MAJOR/MINOR/PATCH)
- "Why version skills?" rationale
- Recommended structure now includes CHANGELOG.md
- CHANGELOG.md tracking added to Review Checklist (HIGH PRIORITY)
[1.0.0] - 2025-01-01
Added
- Initial skill creation
- Progressive disclosure architecture
- Description field design patterns
- Anti-pattern detection framework
- Temporal knowledge capture
- Domain-specific shibboleths
- Skill review checklist
- Testing guidelines
- Decision trees for skill creation
DEPRECATED
This skill has been superseded by skill-architect as of 2026-01-14.
Why Deprecated
skill-coach and skill-creator have been unified into a single authoritative meta-skill that combines:
- Systematic workflow from skill-creator
- Domain expertise encoding from skill-coach
Migration
Use /skill-architect instead of this skill.
All functionality from skill-coach has been preserved and enhanced in skill-architect.
Location
New skill: /Users/erichowens/.claude/skills/skill-architect/
#!/usr/bin/env python3
"""
CLIP Usage Validator - Checks if CLIP is appropriate for a given query
This demonstrates domain-specific validation that encodes expert knowledge.
"""
import sys
import re
from enum import Enum
from dataclasses import dataclass
from typing import Optional
class TaskType(Enum):
SEMANTIC_SEARCH = "semantic_search"
COUNTING = "counting"
FINE_GRAINED = "fine_grained"
SPATIAL = "spatial"
COMPOSITIONAL = "compositional"
ZERO_SHOT = "zero_shot"
@dataclass
class ValidationResult:
is_appropriate: bool
task_type: TaskType
confidence: float
reason: str
alternative: Optional[str] = None
class CLIPValidator:
"""Validates whether CLIP is appropriate for a given task."""
# Keywords that indicate specific task types
COUNTING_KEYWORDS = [
'how many', 'count', 'number of', 'total', 'quantity',
'several', 'few', 'multiple'
]
SPATIAL_KEYWORDS = [
'left', 'right', 'above', 'below', 'next to', 'beside',
'between', 'in front', 'behind', 'under', 'over', 'near'
]
FINE_GRAINED_DOMAINS = [
'celebrity', 'celebrities', 'actor', 'actress',
'car model', 'vehicle model', 'car make',
'flower species', 'bird species', 'dog breed',
'person', 'face', 'people'
]
COMPOSITIONAL_PATTERNS = [
r'(\w+)\s+(\w+)\s+and\s+(\w+)\s+(\w+)', # "red car and blue truck"
r'both\s+',
r'neither\s+',
r'either\s+',
]
GOOD_USE_CASES = [
'find images', 'search for', 'similar to', 'looks like',
'classify', 'categorize', 'what is this', 'identify',
'semantic', 'concept', 'theme'
]
def validate(self, query: str) -> ValidationResult:
"""
Validate if CLIP is appropriate for the query.
Args:
query: Natural language query
Returns:
ValidationResult with recommendation
"""
query_lower = query.lower()
# Check for counting tasks
if any(kw in query_lower for kw in self.COUNTING_KEYWORDS):
return ValidationResult(
is_appropriate=False,
task_type=TaskType.COUNTING,
confidence=0.95,
reason="Query requires counting objects. CLIP cannot preserve spatial information needed for counting.",
alternative="Use object detection models: DETR, Faster R-CNN, YOLO"
)
# Check for spatial reasoning
if any(kw in query_lower for kw in self.SPATIAL_KEYWORDS):
return ValidationResult(
is_appropriate=False,
task_type=TaskType.SPATIAL,
confidence=0.90,
reason="Query requires spatial understanding. CLIP's embeddings lose spatial topology.",
alternative="Use spatial reasoning models: GQA, SWIG, Visual Genome models"
)
# Check for fine-grained classification
if any(domain in query_lower for domain in self.FINE_GRAINED_DOMAINS):
return ValidationResult(
is_appropriate=False,
task_type=TaskType.FINE_GRAINED,
confidence=0.85,
reason="Query requires fine-grained classification. CLIP trained on coarse categories.",
alternative="Use specialized models: Fine-tuned ResNet/EfficientNet for the specific domain"
)
# Check for compositional reasoning
if any(re.search(pattern, query_lower) for pattern in self.COMPOSITIONAL_PATTERNS):
return ValidationResult(
is_appropriate=False,
task_type=TaskType.COMPOSITIONAL,
confidence=0.80,
reason="Query requires attribute binding. CLIP cannot bind attributes to specific objects.",
alternative="Use compositional models: DCSMs (Dense Cosine Similarity Maps), PC-CLIP"
)
# Check if it's a good CLIP use case
if any(use_case in query_lower for use_case in self.GOOD_USE_CASES):
return ValidationResult(
is_appropriate=True,
task_type=TaskType.SEMANTIC_SEARCH,
confidence=0.90,
reason="Query is appropriate for CLIP: semantic search or broad categorization.",
alternative=None
)
# Default: probably okay but lower confidence
return ValidationResult(
is_appropriate=True,
task_type=TaskType.ZERO_SHOT,
confidence=0.60,
reason="Query appears suitable for CLIP, but verify results carefully.",
alternative="If results are poor, consider task-specific models"
)
def print_result(query: str, result: ValidationResult):
"""Pretty-print validation results."""
print("\n" + "="*70)
print(f"CLIP USAGE VALIDATION")
print("="*70)
print(f"\nQuery: {query}")
print(f"Task Type: {result.task_type.value}")
print(f"Confidence: {result.confidence:.0%}")
print()
if result.is_appropriate:
print("✅ CLIP IS APPROPRIATE")
print(f"\nReason: {result.reason}")
if result.alternative:
print(f"\n💡 Note: {result.alternative}")
else:
print("❌ CLIP IS NOT APPROPRIATE")
print(f"\nReason: {result.reason}")
print(f"\n💡 Use Instead: {result.alternative}")
print("\n" + "="*70 + "\n")
def run_examples():
"""Run validation on example queries."""
examples = [
"Find images of beaches at sunset",
"How many cars are in this image?",
"Identify which celebrity this is",
"Is the cat to the left or right of the dog?",
"Find images with a red car and a blue truck",
"Classify this image as indoor or outdoor",
]
validator = CLIPValidator()
print("\n" + "="*70)
print("EXAMPLE VALIDATIONS")
print("="*70)
for query in examples:
result = validator.validate(query)
print(f"\n{query}")
print(f" → {'✅ CLIP' if result.is_appropriate else '❌ Alternative'}: {result.task_type.value}")
if not result.is_appropriate:
print(f" → {result.alternative}")
print("\n" + "="*70 + "\n")
def main():
if len(sys.argv) < 2:
print("Usage:")
print(" python validate_clip_usage.py 'your query here'")
print(" python validate_clip_usage.py --examples")
print("\nExample:")
print(" python validate_clip_usage.py 'Find images of mountains'")
sys.exit(1)
if sys.argv[1] == '--examples':
run_examples()
return
query = ' '.join(sys.argv[1:])
validator = CLIPValidator()
result = validator.validate(query)
print_result(query, result)
# Exit code: 0 if appropriate, 1 if not
sys.exit(0 if result.is_appropriate else 1)
if __name__ == '__main__':
main()
CLIP-Aware Image Embeddings
Smart image-text matching that knows when CLIP works and when to use alternatives.
Quick Decision Tree
Your task:
├─ Semantic search ("find beach images") → CLIP ✓
├─ Zero-shot classification (broad categories) → CLIP ✓
├─ Counting objects → DETR, Faster R-CNN ✗
├─ Fine-grained ID (celebrities, car models) → Specialized model ✗
├─ Spatial relations ("cat left of dog") → GQA, SWIG ✗
└─ Compositional ("red car AND blue truck") → DCSMs, PC-CLIP ✗When to Use This Skill
✅ Use for:
- Semantic image search
- Broad category classification
- Image similarity matching
- Zero-shot tasks on new categories
❌ Do NOT use for:
- Counting objects in images
- Fine-grained classification
- Spatial understanding
- Attribute binding
- Negation handling
Installation
pip install transformers pillow torch sentence-transformers --break-system-packagesValidation: Run python scripts/validate_setup.py
Basic Usage
Image Search
from transformers import CLIPProcessor, CLIPModel
from PIL import Image
model = CLIPModel.from_pretrained("openai/clip-vit-large-patch14")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-large-patch14")
# Embed images
images = [Image.open(f"img{i}.jpg") for i in range(10)]
inputs = processor(images=images, return_tensors="pt")
image_features = model.get_image_features(**inputs)
# Search with text
text_inputs = processor(text=["a beach at sunset"], return_tensors="pt")
text_features = model.get_text_features(**text_inputs)
# Compute similarity
similarity = (image_features @ text_features.T).softmax(dim=0)Common Anti-Patterns
Anti-Pattern 1: "CLIP for Everything"
❌ Wrong:
# Using CLIP to count cars in an image
prompt = "How many cars are in this image?"
# CLIP cannot count - it will give nonsense resultsWhy wrong: CLIP's architecture collapses spatial information into a single vector. It literally cannot count.
✓ Right:
from transformers import DetrImageProcessor, DetrForObjectDetection
processor = DetrImageProcessor.from_pretrained("facebook/detr-resnet-50")
model = DetrForObjectDetection.from_pretrained("facebook/detr-resnet-50")
# Detect objects
results = model(**processor(images=image, return_tensors="pt"))
# Filter for cars and count
car_detections = [d for d in results if d['label'] == 'car']
count = len(car_detections)How to detect: If query contains "how many", "count", or numeric questions → Use object detection
---
Anti-Pattern 2: Fine-Grained Classification
❌ Wrong:
# Trying to identify specific celebrities with CLIP
prompts = ["Tom Hanks", "Brad Pitt", "Morgan Freeman"]
# CLIP will perform poorly - not trained for fine-grained face IDWhy wrong: CLIP trained on coarse categories. Fine-grained faces, car models, flower species require specialized models.
✓ Right:
# Use a fine-tuned face recognition model
from transformers import AutoFeatureExtractor, AutoModelForImageClassification
model = AutoModelForImageClassification.from_pretrained(
"microsoft/resnet-50" # Then fine-tune on celebrity dataset
)
# Or use dedicated face recognition: ArcFace, CosFaceHow to detect: If query asks to distinguish between similar items in same category → Use specialized model
---
Anti-Pattern 3: Spatial Understanding
❌ Wrong:
# CLIP cannot understand spatial relationships
prompts = [
"cat to the left of dog",
"cat to the right of dog"
]
# Will give nearly identical scoresWhy wrong: CLIP embeddings lose spatial topology. "Left" and "right" are treated as bag-of-words.
✓ Right:
# Use a spatial reasoning model
# Examples: GQA models, Visual Genome models, SWIG
from swig_model import SpatialRelationModel
model = SpatialRelationModel()
result = model.predict_relation(image, "cat", "dog")
# Returns: "left", "right", "above", "below", etc.How to detect: If query contains directional words (left, right, above, under, next to) → Use spatial model
---
Anti-Pattern 4: Attribute Binding
❌ Wrong:
prompts = [
"red car and blue truck",
"blue car and red truck"
]
# CLIP often gives similar scores for bothWhy wrong: CLIP cannot bind attributes to objects. It sees "red, blue, car, truck" as a bag of concepts.
✓ Right - Use PC-CLIP or DCSMs:
# PC-CLIP: Fine-tuned for pairwise comparisons
from pc_clip import PCCLIPModel
model = PCCLIPModel.from_pretrained("pc-clip-vit-l")
# Or use DCSMs (Dense Cosine Similarity Maps)How to detect: If query has multiple objects with different attributes → Use compositional model
---
Evolution Timeline
2021: CLIP Released
- Revolutionary: zero-shot, 400M image-text pairs
- Widely adopted for everything
- Limitations not yet understood
2022-2023: Limitations Discovered
- Cannot count objects
- Poor at fine-grained classification
- Fails spatial reasoning
- Can't bind attributes
2024: Alternatives Emerge
- DCSMs: Preserve patch/token topology
- PC-CLIP: Trained on pairwise comparisons
- SpLiCE: Sparse interpretable embeddings
2025: Current Best Practices
- Use CLIP for what it's good at
- Task-specific models for limitations
- Compositional models for complex queries
LLM Mistake: LLMs trained on 2021-2023 data will suggest CLIP for everything because limitations weren't widely known. This skill corrects that.
---
Validation Script
Before using CLIP, check if it's appropriate:
python scripts/validate_clip_usage.py \
--query "your query here" \
--check-allReturns:
- ✅ CLIP is appropriate
- ❌ Use alternative (with suggestion)
Task-Specific Guidance
Image Search (CLIP ✓)
# Good use of CLIP
queries = ["beach", "mountain", "city skyline"]
# Works well for broad semantic conceptsZero-Shot Classification (CLIP ✓)
# Good: Broad categories
categories = ["indoor", "outdoor", "nature", "urban"]
# CLIP excels at thisObject Counting (CLIP ✗)
# Use object detection instead
from transformers import DetrImageProcessor, DetrForObjectDetection
# See /references/object_detection.mdFine-Grained Classification (CLIP ✗)
# Use specialized models
# See /references/fine_grained_models.mdSpatial Reasoning (CLIP ✗)
# Use spatial relation models
# See /references/spatial_models.md---
Troubleshooting
Issue: CLIP gives unexpected results
Check: 1. Is this a counting task? → Use object detection 2. Fine-grained classification? → Use specialized model 3. Spatial query? → Use spatial model 4. Multiple objects with attributes? → Use compositional model
Validation:
python scripts/diagnose_clip_issue.py --image path/to/image --query "your query"Issue: Low similarity scores
Possible causes: 1. Query too specific (CLIP works better with broad concepts) 2. Fine-grained task (not CLIP's strength) 3. Need to adjust threshold
Solution: Try broader query or use alternative model
---
Model Selection Guide
| Model | Best For | Avoid For |
|---|---|---|
| CLIP ViT-L/14 | Semantic search, broad categories | Counting, fine-grained, spatial |
| DETR | Object detection, counting | Semantic similarity |
| DINOv2 | Fine-grained features | Text-image matching |
| PC-CLIP | Attribute binding, comparisons | General embedding |
| DCSMs | Compositional reasoning | Simple similarity |
Performance Notes
CLIP models:
- ViT-B/32: Fast, lower quality
- ViT-L/14: Balanced (recommended)
- ViT-g-14: Highest quality, slower
Inference time (single image, CPU):
- ViT-B/32: ~100ms
- ViT-L/14: ~300ms
- ViT-g-14: ~1000ms
Further Reading
/references/clip_limitations.md- Detailed analysis of CLIP's failures/references/alternatives.md- When to use what model/references/compositional_reasoning.md- DCSMs and PC-CLIP deep dive/scripts/validate_clip_usage.py- Pre-flight validation tool/scripts/diagnose_clip_issue.py- Debug unexpected results
Changelog
v1.2.0 (2025-03-15)
- Added DCSMs and PC-CLIP alternatives
- Updated for 2025 best practices
- Improved validation scripts
v1.1.0 (2024-06-10)
- Added anti-pattern detection
- Expanded troubleshooting
v1.0.0 (2024-01-15)
- Initial release
Skill-Coach: Overview
What This Is
A meta-skill that guides creation of expert-level Agent Skills - the kind that encode real domain knowledge and shibboleths, not just surface-level instructions.
Status: Iteratively self-improved 5 times using its own guidance (Nov 2025), demonstrating the improvement loop it teaches.
Key Innovation: Encoding the Shibboleths
Most skills say: "Here's how to use X"
This teaches: "Here's how to use X, and here's where everyone gets it wrong, and why, and what to use instead"
Structure
skill-coach/
├── README.md # Start here
├── SKILL.md # The coach skill itself
├── scripts/
│ └── validate_skill.py # ✅ Validates skill structure & quality
├── references/
│ ├── antipatterns.md # 🎯 Domain-specific shibboleths
│ └── mcp_vs_scripts.md # When to use MCP vs Scripts
└── examples/
└── good/
└── clip-aware-embeddings/ # 🌟 Exemplary skill
├── SKILL.md
└── scripts/
└── validate_clip_usage.py # Domain-specific validatorThe CLIP Example: Why This Matters
Look at examples/good/clip-aware-embeddings/SKILL.md - it doesn't just say "use CLIP for image-text matching."
It says:
Novice knowledge (what LLMs trained on 2021-2023 data know):
"CLIP is pre-trained on 400M image-text pairs! Use it for all image tasks!"
Expert knowledge (the shibboleth):
"CLIP has fundamental geometric limitations. It CANNOT:
- Count objects (use DETR instead)
- Do fine-grained classification (use specialized models)
- Understand spatial relationships (use GQA models)
- Bind attributes ('red car AND blue truck' → use DCSMs)"
This is the knowledge gap that separates "it compiles" from "it's correct."
What Makes This Different
1. Anti-Patterns Catalog
references/antipatterns.md documents:
- CLIP's actual limitations (with research citations)
- Framework evolution (Next.js Pages → App Router)
- Architecture decisions (MCP vs Scripts philosophy)
- Temporal context (when things changed and why)
2. Validation Tooling
General validation (scripts/validate_skill.py):
- Checks structure (YAML, required fields)
- Validates description quality
- Ensures progressive disclosure
- Checks line count (<500)
- Verifies allowed-tools scope
Domain-specific validation (examples/.../validate_clip_usage.py):
- Detects counting queries → suggests object detection
- Identifies spatial queries → suggests spatial models
- Catches fine-grained tasks → suggests specialized models
This is executable domain knowledge.
3. Progressive Disclosure Done Right
The CLIP skill is 380 lines but FEELS concise because:
- Quick decision tree upfront
- Anti-patterns clearly marked
- References to deep dives (not inline)
- Validation scripts (run, don't read)
4. Temporal Knowledge
Every anti-pattern includes:
- Timeline: "2021: CLIP released, 2023: limitations discovered"
- Why LLMs get it wrong: "Training data predates the research"
- Migration path: "If you're doing X, use Y instead"
Test It Out
Validate Your Skills
cd skill-coach
python scripts/validate_skill.py /path/to/your-skill/See Domain Validation
cd examples/good/clip-aware-embeddings
python scripts/validate_clip_usage.py "How many cars are in this image?"
# → ❌ Use object detection: DETR, Faster R-CNN, YOLO
python scripts/validate_clip_usage.py "Find images of beaches"
# → ✅ CLIP is appropriateThe MCP vs Scripts Philosophy
From references/mcp_vs_scripts.md:
"MCP's job isn't to abstract reality for the agent; it's to manage the auth, networking, and security boundaries and then get out of the way."
Use Scripts for:
- Local file operations
- Stateless transformations
- CLI wrappers
- Batch processing
Use MCPs for:
- External APIs with auth
- Stateful connections
- Real-time data
- Multiple related operations
The guide includes decision matrix, evolution path, and anti-examples.
Key Shibboleths Encoded
ML/AI
- CLIP's geometric impossibilities
- Embedding model selection by task
- Model versioning and temporal changes
Frameworks
- Next.js: Pages Router → App Router (Oct 2022)
- React: Class Components → Hooks (Feb 2019)
- State: Redux → Zustand/Context (2020+)
Architecture
- When complexity justifies MCP over scripts
- Security via least-privilege tool access
- Performance vs simplicity tradeoffs
What You Can Do With This
1. Use it as-is: Ask Claude to apply skill-coach when creating skills 2. Study the example: See all principles in action 3. Add your shibboleths: Contribute domain knowledge you've learned 4. Validate existing skills: Run the validator on skills you have
Recent Improvements (5 Iterations)
The skill-coach has been iteratively improved using its own guidance:
Iteration 1: Foundation
- Added 5 skill-specific anti-patterns (Reference Illusion, Description Soup, Template Theater, Everything Skill, Orphaned Sections)
- Added Evolution Timeline (2024-2025 skill framework best practices)
- Created comprehensive Skill Review Checklist
- Removed all references to non-existent files
Iteration 2: Actionability
- Added 3 Common Workflows (create, debug activation, reduce false positives)
- Made iteration strategy actionable with specific prompts
- Explained why THIS skill uses each tool
- Condensed validation patterns to concepts
Iteration 3: Expert Knowledge
- Added Skill Creation Shibboleths (novice vs expert skill creator)
- Enhanced "What Makes a Great Skill" (5→7 items)
- Condensed domain examples
- Added meta-note about self-improvement
Iteration 4: Usability
- Added "Quick Wins" - 5 immediate improvements
- Simplified skill structure (honest about what's needed)
- Description progression (Bad→Better→Good)
- Realistic file structure (SKILL.md only is mandatory)
Iteration 5: Decision Support
- Added Decision Trees (when to create new skill, Skill vs Subagent vs MCP)
- Prioritized checklist (CRITICAL/HIGH PRIORITY/NICE TO HAVE)
- Final polish and consistency
Result: 482 → 470 lines, more concise yet more comprehensive.
The Meta Point
This skill practices what it preaches:
- ✅ Progressive disclosure (SKILL.md → references/)
- ✅ Anti-patterns specific to skill creation
- ✅ Validation tooling (validate_skill.py)
- ✅ Working examples (CLIP skill)
- ✅ Temporal knowledge (2024-2025 evolution)
- ✅ Clear decision trees (when to create, Skill vs MCP)
- ✅ Iteratively improved using its own guidance
It's not just teaching - it's demonstrating.
Start Here
1. Read README.md for getting started 2. Check SKILL.md for Quick Wins (immediate improvements) 3. Study examples/good/clip-aware-embeddings/SKILL.md 4. Review references/antipatterns.md for domain shibboleths 5. Use skill-coach when creating/improving your own skills
The Philosophy
"Great skills don't just say 'here's how' - they say 'here's how, and here's where everyone gets it wrong, and why, and what to use instead.'"
This is about encoding expertise and shibboleths, not just instructions.
---
Created: 2025-11-23 Last Improved: 2025-11-24 (5 iterations) Version: 2.0.0
Skill Coach: Master Agent Skills Development
A comprehensive guide and toolkit for creating expert-level Agent Skills that encode real domain knowledge, not just surface-level instructions.
Latest: Iteratively self-improved 5 times (Nov 2024), demonstrating the improvement loop it teaches.
What This Skill Does
Skill Coach helps you build skills that:
- Activate precisely - Specific keywords + NOT clause prevents false activation
- Encode shibboleths - Domain knowledge separating experts from novices
- Surface anti-patterns - "If you see X, that's wrong because Y, use Z"
- Capture temporal knowledge - "Pre-2024: X. 2024+: Y. Watch for LLMs suggesting X"
- Know their limits - "Use this for A, B, C. NOT for D, E, F"
- Provide decision trees - Not templates, but "If X then A, if Y then B, never C"
- Include validation - Pre-flight checks catching errors early
Quick Start
1. Install and Use
Copy this folder to your skills directory:
# For Claude Code
cp -r skill-coach ~/.claude/skills/
# For Claude.ai
# Upload via the Skills interface2. Validate Your Skills
cd skill-coach
python scripts/validate_skill.py /path/to/your-skill/3. Study Examples
Look at /examples/good/clip-aware-embeddings/ to see all principles in action.
What's Inside
skill-coach/
├── SKILL.md # Main skill instructions
├── scripts/
│ └── validate_skill.py # Skill validation tool
├── references/
│ ├── antipatterns.md # Domain shibboleths catalog
│ └── mcp_vs_scripts.md # Architecture decisions
└── examples/
├── good/
│ └── clip-aware-embeddings/ # Exemplary skill
└── bad/
└── (anti-examples)Key Concepts
1. Progressive Disclosure
Skills load in three phases:
- Phase 1 (~100 tokens): Metadata - "Should I activate?"
- Phase 2 (<5k tokens): Instructions - "How do I do this?"
- Phase 3 (as needed): Details - "Show me more"
2. The Shibboleths
Deep knowledge that reveals expertise:
Example - CLIP Embeddings:
- Novice: "CLIP is great for image-text tasks!"
- Expert: "CLIP fails at counting, fine-grained classification, spatial reasoning, and attribute binding. Use DETR for counting, specialized models for fine-grained, DCSMs for compositional."
3. Anti-Pattern Detection
Great skills actively warn about mistakes:
### Anti-Pattern: Using CLIP to Count Objects
**Why wrong**: CLIP's architecture cannot preserve spatial information
**What to do**: Use DETR or Faster R-CNN
**How to detect**: If query contains "how many" or "count"4. Temporal Knowledge
Capture what changed and when:
## Evolution Timeline
- Pre-2024: Redux for all state management
- 2024+: Zustand/Jotai for global state, Context for simple cases
- Watch for: LLMs suggesting Redux by defaultCreating Your First Skill
Step 1: Define Scope
---
name: your-skill-name
description: [What it does] [When to use] [Specific triggers]. NOT for [What it's NOT for].
---Step 2: Add Instructions
# Your Skill
## When to Use
✅ Use for: ...
❌ Do NOT use for: ...
## Quick Start
[Minimal working example]
## Common Anti-Patterns
[What looks right but is wrong]Step 3: Include Validation
# scripts/validate.py
def validate_setup():
# Check environment, dependencies, config
passStep 4: Test
python scripts/validate_skill.py your-skill/Quick Wins (Improve Existing Skills Fast)
Apply these immediately to existing skills:
1. Add NOT clause to description → Prevents false activation 2. Add 1-2 anti-patterns → Prevents common mistakes 3. Check line count (wc -l) → Should be <500 4. Remove dead files → Delete unreferenced scripts/references 5. Test activation → Ask questions that should/shouldn't trigger it
Validation Checklist (Prioritized)
CRITICAL (must-have):
- [ ] Description has keywords AND NOT clause
- [ ] SKILL.md under 500 lines
- [ ] All referenced files exist
- [ ] Test activation: Does it activate when it should?
- [ ] Test non-activation: Doesn't activate when it shouldn't?
HIGH PRIORITY (should-have):
- [ ] Has "When to Use" and "When NOT to Use" sections
- [ ] Includes 1-3 anti-patterns with "Why it's wrong"
- [ ] Encodes domain shibboleths (expert vs novice knowledge)
- [ ]
allowed-toolsis minimal
NICE TO HAVE (polish):
- [ ] Temporal knowledge (what changed when)
- [ ] Working code examples (not just templates)
- [ ] References for deep dives
- [ ] Bash restrictions if applicable
Real Examples
Good: CLIP-Aware Embeddings
See /examples/good/clip-aware-embeddings/ for a skill that:
- Knows when CLIP works and when it doesn't
- Provides alternatives for each limitation
- Includes validation scripts
- Documents evolution (2021 → 2025)
- Has clear anti-patterns
Study This Example
It demonstrates: 1. ✅ Progressive disclosure 2. ✅ Anti-pattern detection 3. ✅ Temporal knowledge 4. ✅ Task-specific guidance 5. ✅ Validation tooling 6. ✅ Clear alternatives
Domain-Specific Shibboleths
These are the knowledge gaps where skills add most value:
ML/AI Models
- CLIP limitations (counting, fine-grained, spatial)
- When to use specialized models
- Embedding model selection by task
Framework Evolution
- Next.js: Pages Router → App Router (2022)
- React: Class Components → Hooks (2019)
- State Management: Redux → Zustand (2020+)
Architecture
- When to use MCP vs Scripts
- Evolution from scripts → library → MCP
- Security and performance tradeoffs
See `/references/antipatterns.md` for comprehensive catalog
Best Practices
Description Field
Good:
description: Semantic image search with CLIP. Use for finding similar images, zero-shot classification. NOT for counting objects, fine-grained classification, or spatial reasoning. Mention CLIP, embeddings, image similarity.Bad:
description: Helps with imagesProgressive Structure
Good:
# Skill Name
## Quick Decision Tree
[Fast decision making]
## Common Anti-Patterns
[What to avoid]
## Validation
[How to check]
See /references/deep_dive.md for detailed theoryBad:
# Skill Name
[50 pages of comprehensive tutorial]Validation
Good:
# scripts/validate.py
def check_environment():
"""Specific, actionable errors"""
if not has_model():
raise Error("Model X not found. Install: pip install x")
def check_task_appropriate(query):
"""Task-specific validation"""
if "count" in query.lower():
raise Error("Use object detection for counting, not CLIP")Bad:
# No validation script
# Or generic "check passed/failed" with no guidanceTools & Scripts
Validate Skill Structure
python scripts/validate_skill.py your-skill/Checks:
- Required files and structure
- Description quality
- Line count (<500)
- Progressive disclosure
- Anti-patterns section
- allowed-tools scope
Create New Skill
Ask Claude:
Using the skill-coach skill, help me create a new skill for [your domain].
Focus on anti-patterns where novices get it wrong.Common Mistakes
❌ Skill as Documentation Dump
Don't create a 500-line tutorial. Create actionable instructions with references.
❌ Missing "NOT for"
Without negative triggers, skills activate on false positives.
❌ No Temporal Context
LLMs suggest outdated patterns. Document what changed and when.
❌ Overly Permissive Tools
allowed-tools: Bash # Can execute ANYTHINGBetter:
allowed-tools: Bash(git:*,npm:run),Read,Write❌ No Validation
Skills should include scripts to check if environment is correct.
Integration with Other Tools
Works with MCP
Skills can reference MCPs:
## Requirements
- GitHub MCP (for API access)
- Scripts for local validation
Install: `/plugin marketplace add github-mcp`Works with Subagents
Subagents can use skills for domain expertise:
Skill provides knowledge → Subagent executes with toolsWorks with Projects
Skills available across all conversations in a project.
Contributing Patterns
When you discover a new anti-pattern:
1. Document what looks right but is wrong 2. Explain WHY it's wrong (fundamental reason) 3. Show the correct approach 4. Add temporal context (when did this change?) 5. Note why LLMs make this mistake 6. Include detection/validation if possible
Resources
In This Skill
/references/antipatterns.md- Comprehensive anti-pattern catalog/references/mcp_vs_scripts.md- When to use what/examples/good/- Exemplary skills to study/scripts/validate_skill.py- Validation tool
External
Version History
v2.0.0 (2025-11-24)
5 Iterations of Self-Improvement:
- Iteration 1: Added 5 skill-specific anti-patterns, Evolution Timeline, removed non-existent file references
- Iteration 2: Added Common Workflows, Tool Permissions explanation, actionable iteration strategy
- Iteration 3: Added Skill Creation Shibboleths, enhanced "What Makes a Great Skill" (5→7 items)
- Iteration 4: Added Quick Wins, simplified structure, Description progression (Bad→Better→Good)
- Iteration 5: Added Decision Trees (when to create, Skill vs MCP), prioritized checklist
- Result: 482 → 470 lines, more concise yet comprehensive
v1.0.0 (2025-11-23)
- Initial release
- Comprehensive anti-patterns catalog
- CLIP-aware embeddings example
- Validation tooling
- MCP vs Scripts guide
---
Get Started
1. Read SKILL.md Quick Wins for immediate improvements 2. Study /examples/good/clip-aware-embeddings/ 3. Run validation on your existing skills 4. Use this skill when creating new skills 5. Share your domain-specific shibboleths
Remember: Great skills don't just say "here's how" - they say "here's how, and here's where everyone gets it wrong, and why, and what to use instead."
Skill Anti-Patterns: The Shibboleths
This document catalogs domain-specific knowledge that separates novices from experts - the things LLMs get wrong because their training data includes outdated patterns, oversimplified tutorials, or cargo-culted code.
Table of Contents
1. ML/AI Model Selection 2. Framework Evolution 3. Tool Architecture 4. Skill Design
---
ML/AI Model Selection
Anti-Pattern: CLIP for Everything
Novice thinking: "CLIP is pre-trained on 400M image-text pairs and does zero-shot classification. Use it for all image-text tasks!"
Reality: CLIP has fundamental geometric limitations. Research from 2023-2025 proves it cannot simultaneously handle:
1. Basic descriptions 2. Attribute binding ("red car AND blue truck" vs "blue car AND red truck") 3. Spatial relationships ("cat left of dog" vs "dog left of cat") 4. Negation ("not a cat")
What CLIP fails at:
- ❌ Counting objects in images
- ❌ Fine-grained classification (celebrity ID, car models, flower species)
- ❌ Compositional reasoning
- ❌ Spatial understanding
- ❌ Handwritten text (MNIST-style)
When to use alternatives:
| Task | Use Instead | Why |
|---|---|---|
| Counting objects | DETR, Faster R-CNN | Object detection models built for counting |
| Fine-grained classification | EfficientNet + task head | Transfer learning on specific domain |
| Compositional reasoning | DCSMs, PC-CLIP | Preserve patch/token topology |
| Spatial relationships | GQA models, SWIG | Built for spatial understanding |
| Attribute binding | PC-CLIP (pairwise) | Trained on comparative data |
Timeline:
- 2021: Original CLIP released
- 2022-2023: Limitations discovered in research
- 2024: DCSMs (Dense Cosine Similarity Maps) paper
- 2024: PC-CLIP (Pairwise Comparison CLIP)
- 2025: SpLiCE (Sparse Linear Concept Embeddings)
LLM mistake: LLMs trained on 2021-2023 data will suggest CLIP for everything because limitations weren't widely known yet.
---
Anti-Pattern: Single Embedding Model
Novice thinking: "Pick one embedding model and use it everywhere"
Expert knowledge: Different tasks need different models:
Text embeddings:
- Semantic search:
text-embedding-3-large,voyage-2 - Code search:
voyage-code-2,text-embedding-ada-002 - Multi-lingual:
multilingual-e5-large - Long documents:
jina-embeddings-v2(8k tokens)
Image embeddings:
- General: CLIP ViT-L/14
- Fine-grained: DINOv2
- Medical: BiomedCLIP
- Faces: ArcFace, CosFace
Multi-modal:
- Image-text: CLIP, BLIP-2
- Video: X-CLIP, VideoCLIP
- 3D: ULIP, PointCLIP
Why this matters: Embedding quality directly impacts retrieval accuracy. Using the wrong model can drop accuracy by 20-40%.
---
Anti-Pattern: Ignoring Model Versioning
Problem: "We're using text-embedding-ada-002" (doesn't specify when)
Why wrong: Models evolve:
text-embedding-ada-002(Dec 2022) vstext-embedding-3-small(Jan 2024)- CLIP ViT-B/32 vs ViT-L/14 vs ViT-g-14
- Different training data, different capabilities
Best practice: Pin versions, document when you adopted them:
# embeddings.py
MODEL = "text-embedding-3-large" # Adopted: 2024-03-15
MODEL_DIMENSIONS = 3072
TRAINING_CUTOFF = "2023-09" # Approximate---
Framework Evolution
Anti-Pattern: Pages Router in App Router Projects
Context: Next.js 13 (Oct 2022) introduced App Router, fundamentally changing architecture.
Outdated pattern (Pages Router):
// pages/api/users.js
export default function handler(req, res) {
res.json({ users: [] })
}
// pages/users.js
export async function getServerSideProps() {
return { props: { users: [] } }
}Current pattern (App Router):
// app/api/users/route.js
export async function GET() {
return Response.json({ users: [] })
}
// app/users/page.js
async function UsersPage() {
const users = await fetchUsers() // Server Component
return <UserList users={users} />
}Why it matters: Pages Router patterns don't work in App Router and vice versa.
LLM mistake: Training data from 2020-2023 overwhelmingly shows Pages Router. LLMs will default to old patterns unless specifically prompted.
Timeline:
- 2016-2022: Pages Router only
- Oct 2022: App Router introduced (beta)
- May 2023: App Router stable
- 2024+: App Router is default
---
Anti-Pattern: Redux for Everything
Novice thinking: "Global state needs Redux"
Timeline:
- 2015-2020: Redux dominated
- 2019: Context API improved in React 16.3
- 2020: Zustand, Jotai emerged
- 2023: React Server Components changed the game
Current wisdom:
- Local UI state:
useState,useReducer - Derived state:
useMemo, selectors - Global state (simple): Context API
- Global state (complex): Zustand, Jotai
- Server state: React Query, SWR
- URL state: Next.js searchParams
- Redux: Only if you need time-travel debugging or complex middleware
Why Redux fell out of favor:
- Boilerplate heavy
- Server Components make much state "server-native"
- Simpler alternatives emerged
LLM mistake: LLMs will suggest Redux by default because 80% of training data predates alternatives.
---
Anti-Pattern: Class Components
Timeline:
- 2013-2018: Class components only
- Feb 2019: Hooks introduced (React 16.8)
- 2020+: Functional components are standard
Outdated:
class UserProfile extends React.Component {
state = { user: null }
componentDidMount() {
fetchUser().then(user => this.setState({ user }))
}
render() {
return <div>{this.state.user?.name}</div>
}
}Current:
function UserProfile() {
const [user, setUser] = useState(null)
useEffect(() => {
fetchUser().then(setUser)
}, [])
return <div>{user?.name}</div>
}When class components are still valid:
- Error boundaries (no hook equivalent yet)
- Legacy codebases
LLM mistake: Will generate class components for complex state management
---
Tool Architecture
Anti-Pattern: MCP for Everything
Novice thinking: "MCP is the new standard, make everything an MCP!"
Expert reality: MCPs have overhead. Use them strategically.
Use MCP when:
- ✅ External API with authentication
- ✅ Stateful connections (WebSocket, database)
- ✅ Real-time data streams
- ✅ Security boundaries (credentials, OAuth)
Use Scripts when:
- ✅ Local file operations
- ✅ Batch transformations
- ✅ Stateless computations
- ✅ CLI wrappers
Example - Wrong:
# mcp_server_for_json_parsing.py - OVERKILL!
@mcp.tool()
def parse_json(file_path: str):
with open(file_path) as f:
return json.load(f)Example - Right:
# scripts/parse_json.py - Simple script!
import json
import sys
with open(sys.argv[1]) as f:
data = json.load(f)
print(json.dumps(data, indent=2))Philosophy: "MCP's job isn't to abstract reality for the agent; its job is to manage the auth, networking, and security boundaries and then get out of the way."
---
Anti-Pattern: Premature Abstraction
Problem: Building a complex MCP before understanding the use case
Better approach: Start with scripts, graduate to MCP when you need: 1. Auth/security boundaries 2. Multiple tools in same domain 3. State management 4. Error handling standardization
Evolution path:
Script → Multiple Scripts → Helper Library → MCP ServerOnly promote to MCP when complexity justifies it.
---
Skill Design
Anti-Pattern: Skill as Documentation Dump
Bad:
---
name: react-guide
description: Everything about React
---
# React Guide
React is a JavaScript library for building user interfaces...
[50 pages of tutorial content]Why wrong: Not progressive disclosure, not actionable, not targeted.
Good:
---
name: react-server-components
description: Use React Server Components correctly. Use when working with Next.js App Router, async components, or server-side data fetching.
---
# React Server Components
## Quick Decision Tree
Is your component:
- Fetching data? → Server Component
- Using hooks/events? → Client Component
- Both? → Server Component wrapper + Client Component child
## Common Anti-Pattern: Everything is 'use client'
❌ **Wrong**:'use client' async function Page() { // This doesn't work! const data = await fetch(...) return <div>{data}</div> }
✅ **Right**:// Server Component (default) async function Page() { const data = await fetchData() return <ClientComponent data={data} /> }
// client-component.jsx 'use client' function ClientComponent({ data }) { const [count, setCount] = useState(0) return <div onClick={() => setCount(count + 1)}>{data}</div> }
## When This Pattern Changed
- Pre-Next.js 13: All components are client-side
- Next.js 13+: Server Components by default
- LLM confusion: Will add 'use client' everywhere because older patterns
See /references/server-components-deep-dive.md for more.---
Anti-Pattern: Missing "When NOT to Use"
Problem: Skills activate on false positives
Example - Without negatives:
description: Processes images using computer vision techniquesActivates for: image resizing, image generation, image editing, OCR, face detection, etc.
Example - With negatives:
description: Semantic image search using CLIP embeddings. Use for finding similar images, zero-shot classification. NOT for image generation, editing, or OCR. NOT for counting objects or fine-grained classification.Pattern: Always include "NOT for X, Y, Z" to prevent false activation.
---
Anti-Pattern: No Validation Script
Problem: Skill gives instructions but no way to check correctness
Better: Include validation
# scripts/validate.py
def validate_setup():
"""Check if environment is configured correctly."""
checks = {
"Node version": check_node_version(),
"Dependencies": check_dependencies(),
"API keys": check_api_keys(),
}
for name, passed in checks.items():
print(f"{'✅' if passed else '❌'} {name}")
return all(checks.values())---
Anti-Pattern: Overly Permissive Tools
Bad:
allowed-tools: BashWhy: Can execute ANY bash command
Better:
allowed-tools: Bash(git:*,npm:run,npm:install),Read,WritePrinciple: Least privilege - only grant what's needed
---
Temporal Knowledge Patterns
When documenting anti-patterns, always include:
1. Timeline: When was this practice common? 2. Why deprecated: What replaced it and why? 3. LLM confusion: Why will LLMs suggest the old pattern? 4. Migration path: How to update from old to new?
Template:
### Anti-Pattern: [Pattern Name]
**Used**: [Date range]
**Replaced by**: [New approach]
**Why deprecated**: [Reason]
**Old way**:
[code example]
**New way**:
[code example]
**LLM mistake**: [Why LLM suggests old pattern]
**How to detect**: [Validation rule]---
---
Real-World Failure Case Studies
Case Study 1: The Photo Expert Explosion
Skill: photo-expert (v1.0) Problem: Single skill for ALL photo operations
Symptoms:
- Activated on "photo" anywhere in query
- 800+ lines of instructions
- Slow loading, high token usage
- Wrong advice given (composition advice when user wanted color theory)
Root Cause: Everything Skill anti-pattern
Resolution: Split into 5 focused skills:
clip-aware-embeddings- semantic searchphoto-composition-critic- aesthetic analysiscolor-theory-palette-harmony-expert- color sciencecollage-layout-expert- arrangement algorithmsevent-detection-temporal-intelligence-expert- clustering
Lesson: One domain ≠ one skill. Split by expertise type.
---
Case Study 2: The Phantom MCP
Skill: github-workflow-helper (v1.1) Problem: Referenced MCP server that didn't exist
SKILL.md said:
Use the included MCP server for GitHub API access.
Run: `npx github-helper-mcp`Reality: No mcp-server/ directory existed
Symptoms:
- Claude confidently told users to run non-existent commands
- Users filed bug reports
- Trust in skill ecosystem damaged
Root Cause: Reference Illusion anti-pattern
Resolution: 1. Added check_self_contained.py to detect phantom tools 2. Either create the MCP or remove the reference 3. Added validation to CI
Lesson: Don't promise tools you don't deliver.
---
Case Study 3: The Time Bomb
Skill: react-hooks-expert (v2.0) Problem: Temporal knowledge became stale
Original content (2023):
Use useEffect with empty deps for componentDidMount behaviorBy 2024: This caused issues with React 18 Strict Mode double-mounting
Symptoms:
- Users followed advice → got bugs
- Skill became actively harmful
- No CHANGELOG to track when content was written
Root Cause: Missing temporal knowledge markers
Resolution:
## Temporal Context
- **Pre-React 18**: useEffect with [] = componentDidMount
- **React 18+**: useEffect with [] runs TWICE in dev (Strict Mode)
- **Current best practice**: Use refs for "run once" patternsLesson: Date your knowledge. Update quarterly.
---
Case Study 4: The Activation Black Hole
Skill: api-design-expert (v1.0) Problem: Never activated when needed
Description:
description: Expert guidance for API designSymptoms:
- User: "How should I structure my REST endpoints?"
- Skill: silence
- User confused why skill existed but never helped
Root Cause: Missing Exclusions + no keywords
Resolution:
description: REST/GraphQL API design patterns. Activate on "API design",
"endpoint structure", "REST architecture", "GraphQL schema".
NOT for API implementation, SDK generation, or documentation.Lesson: Generic descriptions = zero activations
---
Contributing
When you discover a new anti-pattern:
1. Document what looks right but is wrong 2. Explain the fundamental reason it's wrong 3. Show the correct approach 4. Include temporal context (when did this change?) 5. Note why LLMs make this mistake 6. Add detection/validation if possible
Remember: The goal is to encode the knowledge that separates "it compiles" from "it's correct" - the shibboleths that reveal expertise.
Skills vs Agents vs MCPs vs Scripts: An Architectural Decision Guide
TL;DR
Use Skills for: Domain expertise, anti-patterns, decision trees (no runtime state) Use Agents for: Multi-step workflows needing tool orchestration and autonomy Use MCPs for: External APIs, auth boundaries, stateful connections Use Scripts for: Local, stateless operations with no auth
The Philosophy
"MCP's job isn't to abstract reality for the agent; it's to manage the auth, networking, and security boundaries and then get out of the way."
>
— Shrivu Shankar, "How I Use Every Claude Code Feature"
Each tool serves a distinct purpose:
- Skills encode domain expertise and decision trees without runtime state
- Agents orchestrate multi-step workflows with tool autonomy
- MCPs manage auth boundaries and external service connections
- Scripts handle local, stateless operations
None is inherently "better" - they solve different problems at different layers.
Decision Matrix
│ Expertise │ Multi-step │ Runtime │ Local │ Remote │ Auth │ Decision
───────────────────────────┼───────────┼────────────┼─────────┼───────┼────────┼──────┼──────────
CLIP anti-patterns │ ✓ │ │ │ │ │ │ Skill
Code review workflow │ │ ✓ │ ✓ │ │ │ │ Agent
JSON parsing │ │ │ │ ✓ │ │ │ Script
AWS S3 operations │ │ │ │ │ ✓ │ ✓ │ MCP
Database queries │ │ │ ✓ │ │ ✓ │ ✓ │ MCP
PR creation workflow │ │ ✓ │ ✓ │ │ │ │ Agent
Git operations │ │ │ │ ✓ │ │ │ Script
Jira API │ │ │ │ │ ✓ │ ✓ │ MCP
Framework evolution guide │ ✓ │ │ │ │ │ │ Skill
Image resizing │ │ │ │ ✓ │ │ │ Script
Testing pipeline │ │ ✓ │ ✓ │ │ │ │ Agent
WebSocket client │ │ │ ✓ │ │ ✓ │ ✓ │ MCP
PDF generation │ │ │ │ ✓ │ │ │ Script
GitHub API │ │ │ │ │ ✓ │ ✓ │ MCP
File organization │ │ │ │ ✓ │ │ │ ScriptLegend:
- Expertise: Domain knowledge, anti-patterns, decision trees
- Multi-step: Orchestrates multiple operations autonomously
- Runtime: Maintains state across operations
- Local: File system operations
- Remote: External service calls
- Auth: Requires authentication/authorization
Use Skills When...
✅ Domain Expertise Needed
# .claude/skills/clip-aware-embeddings/SKILL.md
---
name: clip-aware-embeddings
description: CLIP semantic search expertise. Use for image-text matching, zero-shot classification. NOT for counting, fine-grained classification, spatial reasoning.
---
## When NOT to Use CLIP
### Anti-Pattern: Using CLIP to Count Objects
**Why wrong**: CLIP's architecture cannot preserve spatial information
**What to do**: Use DETR or Faster R-CNN for object detection
**How to detect**: If query contains "how many" or "count"Why Skill:
- No runtime state needed
- Encodes expert knowledge (shibboleths)
- Prevents common mistakes via anti-patterns
- Available across all conversations
✅ Framework Evolution Knowledge
# .claude/skills/react-performance-expert/SKILL.md
---
name: react-performance-expert
description: React performance optimization expertise. Pre-2024 patterns vs modern best practices. NOT for Vue/Angular.
---
## Evolution Timeline
- Pre-2019: Class components + shouldComponentUpdate
- 2019-2023: Hooks + React.memo
- 2024+: React Compiler (automatic memoization)
## Watch For
LLMs may suggest manual useMemo/useCallback when React Compiler handles it automatically.Why Skill:
- Captures temporal knowledge
- Warns about deprecated patterns
- No execution needed, just guidance
✅ Architectural Decision Trees
# .claude/skills/state-management-advisor/SKILL.md
---
name: state-management-advisor
description: State management decision guidance. Use when choosing Redux vs Zustand vs Context. NOT for implementation.
---
## Decision Tree
- Simple boolean/string state shared by 2-3 components → Context
- Complex state with actions (todo list, shopping cart) → Zustand
- Time-travel debugging required → Redux Toolkit
- NEVER: Redux for simple stateWhy Skill:
- Decision logic, not code templates
- Prevents overengineering
- No tools needed, just expertise
Use Agents When...
✅ Multi-Step Workflows with Tool Orchestration
# Agents orchestrate multiple tools autonomously
# Example: Code Review Agent
from anthropic import Agent
review_agent = Agent(
name="code-reviewer",
instructions="""
1. Read modified files
2. Run linter and tests
3. Check for security issues
4. Generate review comments
5. Create summary report
""",
tools=["Read", "Bash", "Grep", "Write"]
)
# Agent autonomously:
# - Decides which files to read
# - Runs appropriate tests
# - Generates contextual feedbackWhy Agent:
- Multiple steps requiring decisions
- Needs tool access (Read, Bash, etc.)
- Maintains context across operations
- Autonomy in execution order
✅ Task Decomposition and Parallel Execution
# Example: Testing Pipeline Agent
testing_agent = Agent(
name="test-runner",
instructions="""
1. Identify all test files
2. Run unit tests in parallel
3. Run integration tests
4. Generate coverage report
5. Fail fast on critical errors
""",
tools=["Bash", "Read", "Write"]
)
# Agent manages:
# - Parallel test execution
# - State aggregation (pass/fail counts)
# - Conditional logic (fail fast)Why Agent:
- Orchestrates multiple bash commands
- Maintains state (test results)
- Makes runtime decisions (fail fast)
✅ Complex Debugging Workflows
# Example: Bug Investigation Agent
debug_agent = Agent(
name="debugger",
instructions="""
1. Search codebase for error patterns
2. Read relevant files
3. Identify root cause
4. Propose fixes
5. Test proposed solutions
""",
tools=["Grep", "Read", "Edit", "Bash"]
)
# Agent autonomously:
# - Searches strategically
# - Follows leads based on findings
# - Iterates on hypothesesWhy Agent:
- Non-linear investigation path
- Requires multiple tool types
- Runtime decision-making
- Iterative refinement
❌ When NOT to Use Agents
Don't use Agent for:
- Single operations (just use tool directly)
- Pure expertise (use Skill instead)
- External API calls (use MCP)
- Simple scripts (use Script)
Anti-Pattern: Agent for Static Knowledge
# BAD: Agent that just returns information
Agent(
name="python-docs",
instructions="Answer Python questions",
tools=[]
)
# BETTER: Use a Skill with /references/ to documentationUse Scripts When...
✅ Local File Operations
# scripts/organize_photos.py
import os
import shutil
from datetime import datetime
def organize_by_date(source_dir):
for file in os.listdir(source_dir):
if file.lower().endswith(('.jpg', '.png')):
creation_time = os.path.getctime(os.path.join(source_dir, file))
date = datetime.fromtimestamp(creation_time).strftime('%Y-%m')
os.makedirs(f"{source_dir}/{date}", exist_ok=True)
shutil.move(f"{source_dir}/{file}", f"{source_dir}/{date}/{file}")Why script: No auth, no external APIs, pure file operations.
✅ Stateless Transformations
# scripts/convert_markdown.py
import markdown
import sys
with open(sys.argv[1]) as f:
html = markdown.markdown(f.read())
print(html)Why script: Input → Output, no state, no network.
✅ CLI Wrappers
# scripts/git_summary.py
import subprocess
import json
def get_commit_summary(since="1 week ago"):
result = subprocess.run(
['git', 'log', f'--since={since}', '--oneline'],
capture_output=True,
text=True
)
return result.stdout.split('\n')
print(json.dumps(get_commit_summary()))Why script: Wrapping existing CLI tools, no auth needed.
✅ Batch Processing
# scripts/batch_resize.sh
#!/bin/bash
for img in *.jpg; do
convert "$img" -resize 800x600 "resized_$img"
doneWhy script: Simple, local, no coordination needed.
Use MCPs When...
✅ External APIs with Auth
# Good MCP example
from mcp.server import Server
from anthropic import Anthropic
app = Server("claude-api")
client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
@app.tool()
async def ask_claude(prompt: str) -> str:
"""Query Claude API with authentication."""
message = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}]
)
return message.content[0].textWhy MCP:
- Authentication (API key)
- External service
- Standardized error handling
- Rate limiting concerns
✅ Stateful Connections
# Database MCP
from mcp.server import Server
import psycopg2
app = Server("postgres-mcp")
conn = None # Persistent connection
@app.tool()
async def connect_db(connection_string: str):
"""Establish database connection."""
global conn
conn = psycopg2.connect(connection_string)
return "Connected"
@app.tool()
async def query_db(sql: str):
"""Execute query on open connection."""
if not conn:
raise Exception("Not connected")
cursor = conn.cursor()
cursor.execute(sql)
return cursor.fetchall()Why MCP:
- Maintains state (connection)
- Multiple related operations
- Connection pooling
- Transaction management
✅ Real-Time Data
# Stock price MCP
from mcp.server import Server
import websocket
import json
app = Server("stock-prices")
ws = None
@app.tool()
async def subscribe_stock(ticker: str):
"""Subscribe to real-time stock updates."""
global ws
ws = websocket.WebSocketApp(
f"wss://api.example.com/stocks/{ticker}",
on_message=handle_message
)
ws.run_forever()Why MCP: WebSocket connection, real-time updates, persistent connection.
✅ Multiple Related Tools
# GitHub MCP
from mcp.server import Server
import requests
app = Server("github-api")
BASE = "https://api.github.com"
TOKEN = os.getenv("GITHUB_TOKEN")
HEADERS = {"Authorization": f"token {TOKEN}"}
@app.tool()
async def list_repos(org: str):
"""List organization repositories."""
r = requests.get(f"{BASE}/orgs/{org}/repos", headers=HEADERS)
return r.json()
@app.tool()
async def create_issue(repo: str, title: str, body: str):
"""Create GitHub issue."""
r = requests.post(
f"{BASE}/repos/{repo}/issues",
headers=HEADERS,
json={"title": title, "body": body}
)
return r.json()
@app.tool()
async def get_pr(repo: str, pr_number: int):
"""Get pull request details."""
r = requests.get(f"{BASE}/repos/{repo}/pulls/{pr_number}", headers=HEADERS)
return r.json()Why MCP:
- All tools share auth
- Related domain (GitHub)
- Standardized error handling
- Single configuration
Anti-Patterns
❌ Skill for Runtime Execution
Bad:
# .claude/skills/file-organizer/SKILL.md
---
name: file-organizer
description: Organizes files by date
allowed-tools: Bash,Read,Write
---
Run this script to organize files:
python scripts/organize.py /path/to/filesWhy it's wrong: Skills provide expertise, not execution. Use Script or Agent for actual work.
What to do instead:
- Skill: Provide decision tree ("When to organize by date vs by type")
- Script: Do the actual organizing
- Agent: Orchestrate multiple organization strategies
❌ Agent for Static Knowledge
Bad:
# Agent that just returns information
Agent(
name="python-syntax-helper",
instructions="Answer Python syntax questions",
tools=[]
)Why it's wrong: No tools needed, no multi-step workflow, just knowledge lookup.
What to do instead: Use a Skill with /references/ to documentation.
❌ Agent for Single Operations
Bad:
# Agent that just runs one command
Agent(
name="test-runner",
instructions="Run pytest on the codebase",
tools=["Bash"]
)Why it's wrong: Single bash command doesn't justify agent overhead.
What to do instead: Just run pytest directly via Bash tool.
❌ MCP for Local Operations
Bad:
# mcp_server_json.py - OVERKILL
from mcp.server import Server
import json
app = Server("json-parser")
@app.tool()
async def parse_json(file_path: str):
with open(file_path) as f:
return json.load(f)
@app.tool()
async def write_json(file_path: str, data: dict):
with open(file_path, 'w') as f:
json.dump(data, f, indent=2)Better:
# scripts/json_utils.py
import json
import sys
# Parse
with open(sys.argv[1]) as f:
print(json.dumps(json.load(f), indent=2))Why: No auth, no network, no state. Script is simpler.
❌ Script for Authenticated APIs
Bad:
# scripts/query_jira.py
import requests
# API key hardcoded or in environment - not great!
response = requests.get(
"https://company.atlassian.net/rest/api/3/issue/PROJ-123",
auth=("user@example.com", os.getenv("JIRA_TOKEN"))
)
print(response.json())Why problematic:
- Credentials in script or environment
- No error handling
- No rate limiting
- Can't compose with other Jira operations
- Each agent invocation re-authenticates
Better: MCP with proper auth flow, connection reuse, error handling.
❌ Overengineered MCP
Bad:
# mcp_server_calculator.py - TOO SIMPLE FOR MCP
from mcp.server import Server
app = Server("calculator")
@app.tool()
async def add(a: float, b: float) -> float:
return a + b
@app.tool()
async def subtract(a: float, b: float) -> float:
return a - bBetter: Claude can do this natively. No tool needed.
Evolution Path
Good architecture evolves through layers of abstraction:
Stage 0: Direct Tool Use
# Just use Claude's tools directly
Read file → Edit file → Bash commandStage 1: Single Script
# fetch_data.py
import requests
data = requests.get("https://api.example.com/data").json()
print(data)Use when: One-off operation, local, no auth
Stage 2: Multiple Scripts
scripts/
├── fetch_data.py
├── process_data.py
└── upload_results.pyUse when: Related operations, still local, no orchestration needed
Stage 3: Skill for Expertise
# .claude/skills/data-pipeline-expert/SKILL.md
---
name: data-pipeline-expert
description: Data pipeline anti-patterns and decision trees
---
## When to Batch Process
- Data size > 100MB → Batch with pagination
- Real-time updates needed → Use streaming instead
## Anti-Patterns
- Processing entire dataset in memory → OOM errorsUse when: You have domain expertise to encode, prevent common mistakes
Stage 4: Agent for Orchestration
# When scripts need coordination
Agent(
name="pipeline-runner",
instructions="""
1. Validate data format
2. Run fetch_data.py
3. If fetch succeeds, run process_data.py
4. If validation passes, run upload_results.py
5. Generate summary report
""",
tools=["Bash", "Read", "Write"]
)Use when: Multi-step workflow with runtime decisions, state management
Stage 5: Helper Library
# lib/data_client.py
class DataClient:
def fetch(self): ...
def process(self): ...
def upload(self): ...
# scripts/run_pipeline.py
from lib.data_client import DataClient
client = DataClient()
client.fetch()
client.process()
client.upload()Use when: Shared logic across multiple scripts, testability needed
Stage 6: MCP Server
# Only when you need:
# - Auth management
# - Multiple agents using it
# - External API access
# - Connection pooling
from mcp.server import Server
from lib.data_client import DataClient
app = Server("data-pipeline")
client = DataClient()
@app.tool()
async def fetch_data():
return client.fetch()
# ... etcUse when: External APIs, auth boundaries, stateful connections
The Rule: Start simple. Each stage adds complexity - only evolve when the complexity pays for itself.
Performance Considerations
Skills
- ✅ Zero runtime overhead (loaded as context)
- ✅ Prevents mistakes before execution
- ✅ Cached across conversations
- ❌ Takes up context window
- ❌ No execution capability
Agents
- ✅ Autonomy reduces back-and-forth
- ✅ Parallel tool execution
- ✅ Context maintained across steps
- ❌ Higher token usage
- ❌ More complex debugging
Scripts
- ✅ Zero latency overhead
- ✅ Simple debugging
- ✅ No network calls for local ops
- ❌ No connection reuse
- ❌ No shared state across calls
MCPs
- ✅ Connection pooling
- ✅ Shared state
- ✅ Standardized errors
- ❌ Network overhead
- ❌ More complex debugging
Security Considerations
Scripts
- ✅ No credential management complexity
- ✅ Run in user context
- ❌ Credentials in environment or hardcoded
- ❌ Each invocation re-authenticates
MCPs
- ✅ Centralized credential management
- ✅ OAuth flows
- ✅ Connection reuse (fewer auth requests)
- ❌ More attack surface
- ❌ Requires secure credential storage
Testing
Scripts
# Easy to test
python scripts/process_data.py test_input.jsonMCPs
# MCP Inspector or custom client needed
from mcp.client import Client
async def test():
async with Client("http://localhost:8000") as client:
result = await client.call_tool("process_data", {"input": "test"})
assert result == expectedDocumentation Recommendations
In your skill's SKILL.md:
## Tools Required
This skill uses:
- **Scripts** for local processing: `/scripts/validate.py`
- **MCP** for GitHub API access: Requires `github-mcp` installed
### Setup MCP
/plugin marketplace add github-mcp
Or use CLI directly if GitHub MCP unavailable:gh issue list
Decision Flowchart
Do you need to encode expertise/anti-patterns?
├─ Yes → Skill (with decision trees, no execution)
└─ No → Is it multi-step with runtime decisions?
├─ Yes → Agent (orchestrates tools autonomously)
└─ No → Is it a local operation?
├─ Yes → Script (stateless, no auth)
└─ No → Does it require auth/external API?
├─ Yes → MCP Server (manages auth boundaries)
└─ No → Script (with curl/CLI)Key Questions: 1. Expertise? → Skill (anti-patterns, decision trees) 2. Multi-step + decisions? → Agent (tool orchestration) 3. External API + auth? → MCP (connection management) 4. Everything else? → Script (simple execution)
Real-World Examples
Good: CLIP Limitations as Skill
- Domain expertise (what NOT to use CLIP for)
- Anti-patterns with alternatives
- No runtime execution needed
- Prevents common mistakes before coding
Good: Code Review as Agent
- Multi-step workflow (read → lint → test → summarize)
- Runtime decisions (which files to read)
- Tool orchestration (Read, Bash, Grep, Write)
- Autonomous execution
Good: Git as Script
- CLI wrapper
- Local operations
- No auth needed
- Simple subprocess calls
Good: Playwright as MCP
- Complex browser automation
- Stateful (browser context)
- Multiple related operations
- Security boundaries (sandbox)
Good: Framework Evolution as Skill
- Temporal knowledge (pre-2024 vs 2024+)
- Warns about deprecated patterns
- Decision trees for migration
- No execution needed
Good: Testing Pipeline as Agent
- Orchestrates test suite
- Parallel execution management
- State aggregation (pass/fail counts)
- Fail-fast logic
Good: Image Processing as Script
- Local file operations
- Stateless transformations
- No network needed
- Simple input/output
Good: AWS SDK as MCP
- Many related services
- Auth required
- Connection pooling
- Error handling standardization
Summary
Skills win on:
- Domain expertise encoding
- Anti-pattern prevention
- Zero runtime overhead
- Context persistence
- Decision tree guidance
Agents win on:
- Multi-step orchestration
- Tool autonomy
- Runtime decision-making
- Workflow automation
- Parallel execution
Scripts win on:
- Simplicity
- Local operations
- No dependencies
- Easy testing
- Zero overhead
MCPs win on:
- Auth management
- Connection reuse
- Multiple related operations
- Stateful interactions
- Standardization
The Hierarchy: 1. Start with: Direct tool use (Read, Edit, Bash) 2. Extract to: Script (when operation repeats) 3. Add: Skill (when expertise/anti-patterns emerge) 4. Coordinate with: Agent (when multi-step workflows appear) 5. Promote to: MCP (when auth/external APIs needed)
The Rule: Each layer adds complexity. Only add layers when the value justifies the cost.
---
Further Reading
/references/antipatterns.md- "MCP for Everything" anti-pattern/examples/good/mcp-vs-script-comparison/- Side-by-side examples- Model Context Protocol docs: https://modelcontextprotocol.io/
Skill Scoring Rubric
Quantitative metrics for evaluating skill quality. Score each category 0-10.
Scoring Categories
1. Activation Precision (0-10)
How accurately does the skill activate?
| Score | Criteria |
|---|---|
| 0-2 | No keywords, vague description, activates randomly |
| 3-4 | Some keywords, missing NOT clause, many false positives |
| 5-6 | Good keywords + NOT clause, occasional misfires |
| 7-8 | Precise activation, clear boundaries, <10% false positives |
| 9-10 | Perfect activation, comprehensive exclusions, <2% false positives |
Quick check: Count (correct activations) / (total queries) for 10 test queries
2. Domain Expertise Depth (0-10)
How much expert knowledge is encoded?
| Score | Criteria |
|---|---|
| 0-2 | Generic advice, could be Googled easily |
| 3-4 | Some domain knowledge, no anti-patterns |
| 5-6 | Good expertise, 1-2 anti-patterns, some shibboleths |
| 7-8 | Deep expertise, 3+ anti-patterns, decision trees |
| 9-10 | Expert-level with temporal knowledge, edge cases, and shibboleths |
Quick check: Count shibboleths + anti-patterns + decision trees
3. Progressive Disclosure (0-10)
How well is information layered?
| Score | Criteria |
|---|---|
| 0-2 | Everything in one file, >500 lines, no structure |
| 3-4 | Some structure, still too dense |
| 5-6 | Core in SKILL.md, some refs, ~300-500 lines |
| 7-8 | Clean SKILL.md <300 lines, refs for deep dives |
| 9-10 | Optimal: <200 line core, refs load on-demand |
Quick check: wc -l SKILL.md → Target <300
4. Self-Containment (0-10)
Does the skill ship working tools?
| Score | Criteria |
|---|---|
| 0-2 | Instructions only, user must implement everything |
| 3-4 | Mentions tools but doesn't include them |
| 5-6 | Has scripts but they're templates |
| 7-8 | Working scripts, no phantom tools |
| 9-10 | Complete tooling: scripts, validation, maybe MCP |
Quick check: Run check_self_contained.py
5. Maintainability (0-10)
How easy is the skill to update?
| Score | Criteria |
|---|---|
| 0-2 | No CHANGELOG, no versioning, no structure |
| 3-4 | Basic structure, no versioning |
| 5-6 | Has CHANGELOG, some documentation |
| 7-8 | Versioned, documented, modular references |
| 9-10 | SemVer, complete changelog, validation scripts |
Quick check: Has CHANGELOG.md? Uses semantic versioning?
---
Composite Score
Formula: (Σ category scores) / 5
| Total | Grade | Meaning |
|---|---|---|
| 9-10 | A | Production-ready, exemplary |
| 7-8.9 | B | Good quality, minor improvements possible |
| 5-6.9 | C | Functional but needs work |
| 3-4.9 | D | Significant issues, needs revision |
| 0-2.9 | F | Not ready for use |
Example Evaluation
Skill: clip-aware-embeddings
------------------------------
Activation Precision: 9/10 (clear NOT clause, specific triggers)
Domain Expertise Depth: 8/10 (shibboleths, anti-patterns, temporal)
Progressive Disclosure: 8/10 (~150 line core, refs for details)
Self-Containment: 7/10 (working scripts, no MCP)
Maintainability: 7/10 (versioned, has CHANGELOG)
------------------------------
Composite Score: 7.8/10 (Grade: B)Automation
Run validation scripts for automated scoring:
# Structure + content checks
python scripts/validate_skill.py /path/to/skill
# Self-containment check
python scripts/check_self_contained.py /path/to/skillSelf-Contained Tools
Implementation patterns for scripts, MCP servers, and subagents that make skills immediately useful.
Philosophy
The best skill is one where the user can start working immediately.
| Approach | Result |
|---|---|
| "Here's how to build a CLIP embedder" | User spends 2 hours implementing |
| "Here's a working CLIP embedder, run it" | User is productive in 2 minutes |
Skills should encode expertise AND provide working tools to apply that expertise.
---
Scripts
When to Include Scripts
- Skill describes repeatable operations (analysis, validation, transformation)
- Domain has specific algorithms that should be implemented correctly
- Pre-flight checks would prevent common errors
Script Requirements
1. Actually work - Not templates, not pseudocode 2. Minimal dependencies - Prefer stdlib, document any pip/npm installs 3. Clear interface - CLI args or stdin/stdout 4. Error handling - Graceful failures with helpful messages 5. README - How to install and run
Example: Domain Analysis Script
#!/usr/bin/env python3
"""
Photo Composition Analyzer
Analyzes images for composition quality using rule of thirds,
visual weight distribution, and color harmony.
Usage: python analyze_composition.py <image_path>
Dependencies: pip install pillow numpy
"""
import sys
from pathlib import Path
def analyze_composition(image_path: str) -> dict:
"""Analyze composition and return scores."""
# Import here to give helpful error if missing
try:
from PIL import Image
import numpy as np
except ImportError:
print("Install dependencies: pip install pillow numpy")
sys.exit(1)
img = Image.open(image_path)
# ... actual implementation ...
return {
"rule_of_thirds": 0.85,
"visual_balance": 0.72,
"color_harmony": 0.91,
"overall": 0.83
}
if __name__ == "__main__":
if len(sys.argv) != 2:
print(f"Usage: {sys.argv[0]} <image_path>")
sys.exit(1)
result = analyze_composition(sys.argv[1])
for metric, score in result.items():
print(f"{metric}: {score:.2f}")Example: Validation Script
#!/bin/bash
# validate_skill.sh - Pre-flight checks for skill quality
# Usage: ./validate_skill.sh /path/to/skill
SKILL_DIR="$1"
if [ -z "$SKILL_DIR" ]; then
echo "Usage: $0 <skill_directory>"
exit 1
fi
errors=0
# Check SKILL.md exists
if [ ! -f "$SKILL_DIR/SKILL.md" ]; then
echo "❌ Missing SKILL.md"
((errors++))
else
echo "✅ SKILL.md exists"
fi
# Check line count
lines=$(wc -l < "$SKILL_DIR/SKILL.md")
if [ "$lines" -gt 500 ]; then
echo "⚠️ SKILL.md is $lines lines (target: <500)"
else
echo "✅ SKILL.md is $lines lines"
fi
# Check for NOT clause in description
if grep -q "NOT for" "$SKILL_DIR/SKILL.md"; then
echo "✅ Description has NOT clause"
else
echo "❌ Missing NOT clause in description"
((errors++))
fi
exit $errors---
MCP Servers
When to Build an MCP
- Skill needs external API access (GitHub, Figma, databases, etc.)
- OAuth or API key authentication required
- Stateful connections (websockets, streaming)
- Rate limiting or caching needed
MCP Server Structure
mcp-server/
├── src/
│ └── index.ts # Server implementation
├── package.json # Dependencies and scripts
├── tsconfig.json # TypeScript config
└── README.md # Installation instructionsExample: Minimal MCP Server
// src/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new Server(
{ name: "my-skill-mcp", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
// Define tools
server.setRequestHandler("tools/list", async () => ({
tools: [
{
name: "analyze_repo",
description: "Analyze a GitHub repository structure",
inputSchema: {
type: "object",
properties: {
repo: { type: "string", description: "owner/repo format" }
},
required: ["repo"]
}
}
]
}));
server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
if (name === "analyze_repo") {
// Actual implementation
const result = await analyzeRepo(args.repo);
return { content: [{ type: "text", text: JSON.stringify(result) }] };
}
throw new Error(`Unknown tool: ${name}`);
});
// Start server
const transport = new StdioServerTransport();
await server.connect(transport);package.json
{
"name": "my-skill-mcp",
"version": "1.0.0",
"type": "module",
"bin": { "my-skill-mcp": "dist/index.js" },
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0"
},
"devDependencies": {
"typescript": "^5.0.0"
}
}README Template
# My Skill MCP Server
MCP server for [domain] operations.
## Installation
\`\`\`bash
cd mcp-server
npm install
npm run build
\`\`\`
## Configuration
Add to your Claude Code MCP settings:
\`\`\`json
{
"mcpServers": {
"my-skill": {
"command": "node",
"args": ["/path/to/mcp-server/dist/index.js"],
"env": {
"API_KEY": "your-api-key"
}
}
}
}
\`\`\`
## Tools
- `analyze_repo` - Analyze a GitHub repository structure
- `fetch_issues` - Get open issues with labels---
Subagents
When to Define Subagents
- Skill involves multi-step workflows
- Different phases need different tool access
- Orchestration logic is complex enough to warrant isolation
Subagent Definition Format
# agents/research-workflow.md
## Agent: Research Coordinator
### Purpose
Orchestrate multi-source research with synthesis.
### System Prompt
You are a research coordinator. Your job is to:
1. Break down research questions into searchable queries
2. Dispatch searches to appropriate sources
3. Synthesize findings into coherent answers
### Tools Required
- WebSearch
- WebFetch
- Read
- Write
### Workflow
1. Receive research question
2. Generate 3-5 search queries
3. Execute searches in parallel
4. Read and extract relevant content
5. Synthesize into final answer
### Success Criteria
- All claims have citations
- Multiple sources corroborate findings
- Contradictions are explicitly notedMulti-Agent Orchestration Pattern
# agents/orchestrator.md
## Pipeline: Code Review
### Agents
1. **security-scanner** - Check for vulnerabilities
2. **style-checker** - Verify code style
3. **architecture-reviewer** - Assess design patterns
### Orchestration
\`\`\`
parallel:
- security-scanner → security_report
- style-checker → style_report
then:
- architecture-reviewer(security_report, style_report) → final_review
\`\`\`
### Handoff Protocol
Each agent produces structured output:
- `status`: pass | warn | fail
- `findings`: list of issues
- `recommendations`: suggested fixes---
Anti-Patterns
Phantom Tools
What it looks like: SKILL.md references scripts/analyze.py but file doesn't exist
Why it's wrong: Users try to run non-existent code, lose trust in skill
Fix: Only reference tools that actually exist and work
Template Soup
What it looks like: Scripts are templates with # TODO: implement comments
Why it's wrong: User still has to do the implementation work
Fix: Ship working code or don't ship at all
Dependency Hell
What it looks like: Script requires 15 pip packages, specific Python version, system libraries
Why it's wrong: Most users won't complete setup
Fix: Minimize dependencies, prefer stdlib, document clearly
MCP Without Purpose
What it looks like: MCP server for operations that could be a simple script
Why it's wrong: Over-engineering; MCP has setup overhead
Fix: Use MCP only when you need: auth, state, external APIs, or caching
---
Checklist: Is My Skill Self-Contained?
□ Can a user start using this skill immediately?
□ Are all referenced scripts/tools actually present and working?
□ Do scripts have clear installation instructions?
□ Do scripts handle errors gracefully?
□ If MCP needed, is server implementation complete?
□ If subagents needed, are prompts and workflows defined?
□ Is there a validation script to check environment?
□ Does README explain how to set everything up?---
Examples of Self-Contained Skills
| Skill | Tools Included |
|---|---|
| clip-aware-embeddings | scripts/validate_clip_usage.py |
| site-reliability-engineer | scripts/validate-brackets.js, scripts/validate-liquid.js |
| skill-coach | scripts/validate_skill.py |
Goal: Every skill with repeatable operations should have working tools.
Domain Shibboleths
Expert knowledge that separates novices from experts.
What Are Shibboleths?
Deep knowledge markers that reveal true expertise. Great skills encode these to prevent Claude from giving novice-level advice.
Skill Creation Shibboleths
Novice skill creator:
- "I'll make a comprehensive skill that handles everything related to X"
- Focuses on templates and examples
- Description: "Helps with many things"
- Thinks more tools = better
Expert skill creator:
- "I'll create a focused skill that encodes THIS specific expertise about X"
- Focuses on decision trees and anti-patterns
- Description: "Does A, B, C. Activate on keywords X, Y. NOT for D, E, F."
- Minimal tools, knows when NOT to use the skill
- Encodes temporal knowledge: "Pre-2024 pattern X was common, now use Y"
Domain Example Shibboleths
CLIP Embeddings
Novice: "CLIP is great for image-text matching"
Expert: "CLIP fails at:
- Counting objects
- Fine-grained classification (specific dog breeds, car models)
- Attribute binding ('red cube' vs 'blue sphere')
- Spatial relationships ('left of', 'above')
- Negation ('no dogs in image')
Use instead:
- DCSMs for compositional queries
- PC-CLIP for geometric reasoning
- Specialized counting models
- Task-specific fine-tuned models"
MCPs vs Scripts
Novice: "MCPs are better because they're more powerful"
Expert: "MCP for auth/external APIs. Script for local/stateless.
- MCP: OAuth tokens, rate-limited APIs, persistent connections
- Script: File processing, transformations, local tools
- Building an MCP when a script would suffice = over-engineering"
React Performance
Novice: "Use useMemo and useCallback everywhere"
Expert: "Profile first. Premature optimization causes:
- Complexity without benefit
- Memory overhead from closures
- Harder debugging
Only memoize when:
- Measured re-render cost > 16ms
- Referential equality matters (deps of other hooks)
- Expensive computations"
API Design
Novice: "Use REST for everything"
Expert: "Match API style to use case:
- REST: CRUD resources, cacheable, simple clients
- GraphQL: Complex queries, mobile/low-bandwidth, rapidly evolving
- gRPC: Internal services, high throughput, typed contracts
- WebSocket: Real-time bidirectional
Anti-pattern: GraphQL for simple CRUD = overengineering"
Encoding Shibboleths in Skills
Structure
## Expert Knowledge: [Domain]
### Novice Approach
[What beginners do/think]
### Expert Insight
[What experienced practitioners know]
### Implications for This Skill
[How this affects the skill's guidance]Example Encoding
## Expert Knowledge: Caching
### Novice Approach
"Cache everything for performance"
### Expert Insight
Cache invalidation is the hard problem:
- Stale data causes user confusion
- Thundering herd on expiry
- Memory pressure under load
### Implications
This skill recommends cache-aside pattern with:
- Short TTLs (5min default)
- Explicit invalidation hooks
- Graceful degradation on cache missSkill Composition Patterns
How skills work together, depend on each other, and compose into workflows.
Dependency Types
1. Sequential Dependency
One skill's output feeds another's input.
clip-aware-embeddings → collage-layout-expert
[generates embeddings] [uses embeddings for layout]Implementation: Reference the upstream skill in your description:
description: "...Requires embeddings from clip-aware-embeddings skill..."2. Parallel Composition
Multiple skills apply simultaneously to different aspects.
┌── color-theory-expert ──────┐
│ │
Photo ───┼── photo-composition-critic ─┼── Combined Analysis
│ │
└── event-detection-expert ───┘Implementation: Each skill operates independently; user/orchestrator combines results.
3. Hierarchical (Meta-Skills)
A skill that orchestrates other skills.
design-archivist (meta)
├── vibe-matcher
├── color-theory-expert
└── competitive-cartographerImplementation: Define subagent that invokes other skills.
---
Composition Anti-Patterns
Circular Dependency
Wrong: Skill A depends on B, B depends on A
skill-coach → skill-documentarian → skill-coach (cycle!)Fix: Make dependencies unidirectional or extract shared functionality.
Implicit Dependency
Wrong: Skill assumes another is present but doesn't document it
description: "Uses CLIP embeddings for search"
# But doesn't mention clip-aware-embeddings skillFix: Explicit dependencies in description or README.
Monolithic Anti-Composition
Wrong: One skill tries to do everything
name: photo-everything-expert
description: "Handles composition, color, events, layout, embeddings..."Fix: Split into focused, composable skills.
---
Best Practices
1. Document Dependencies
In your SKILL.md or README:
## Dependencies
- **Required**: `clip-aware-embeddings` for vector search
- **Optional**: `color-theory-expert` for palette analysis2. Use Consistent Data Formats
When skills pass data:
- Embeddings: Float arrays or paths to .npy files
- Color palettes: Hex arrays or LAB tuples
- Scores: 0.0-1.0 normalized floats
3. Fail Gracefully Without Dependencies
def analyze_with_optional_color():
try:
# Try using color-theory skill output
palette = load_palette()
except FileNotFoundError:
# Degrade gracefully
palette = extract_basic_colors(image)4. Composition Keywords
Add to description for discovery:
- "Composes with X, Y, Z"
- "Extends X with Y capabilities"
- "Downstream of X"
- "Input for Y workflows"
---
Example: Photo Analysis Pipeline
Input Photo
│
├─[1]── clip-aware-embeddings ────────────────┐
│ → semantic embedding │
│ │
├─[2]── photo-composition-critic ─────────────┤
│ → aesthetic scores │
│ ├──→ collage-layout-expert
├─[3]── color-theory-palette-harmony ─────────┤ → optimal grid
│ → palette + harmony score │
│ │
└─[4]── event-detection-temporal ─────────────┘
→ event clustersEach skill:
- Works independently
- Has clear input/output contract
- Can be used standalone or composed
- Documents what it depends on
Skill Lifecycle Management
From creation to deprecation - how to maintain skills over time.
Lifecycle Stages
DRAFT → ACTIVE → MATURE → DEPRECATED → ARCHIVED
│ │ │ │ │
v v v v v
Testing In use Stable Phasing out Read-only---
Stage 1: DRAFT
Characteristics:
- New skill under development
- May have incomplete features
- Not yet validated
Tasks:
- [ ] Write initial SKILL.md
- [ ] Add description with keywords + NOT clause
- [ ] Create at least 1 anti-pattern
- [ ] Run validation scripts
- [ ] Test activation with 10+ queries
Version: 0.x.x (pre-release)
---
Stage 2: ACTIVE
Characteristics:
- Validated and working
- Being actively used and refined
- Accepting feedback
Tasks:
- [ ] Monitor activation precision
- [ ] Add anti-patterns as discovered
- [ ] Update temporal knowledge
- [ ] Respond to user feedback
- [ ] Keep CHANGELOG updated
Version: 1.0.0+
---
Stage 3: MATURE
Characteristics:
- Stable, well-documented
- Comprehensive anti-patterns
- Working tools included
- Rarely needs changes
Tasks:
- [ ] Periodic review (quarterly)
- [ ] Check temporal knowledge freshness
- [ ] Validate against latest Claude behavior
- [ ] Consider extracting sub-skills if grown too large
Version: 2.0.0+ (stable API)
---
Stage 4: DEPRECATED
Characteristics:
- Being phased out
- Better alternative exists
- Still functional but not recommended
Tasks:
- [ ] Add deprecation notice to SKILL.md header
- [ ] Document migration path
- [ ] Point to replacement skill
- [ ] Set end-of-support date
Example deprecation notice:
> ⚠️ **DEPRECATED**: This skill is deprecated as of v2.3.0.
> Use `new-skill-name` instead. Migration guide: `references/migration.md`
> End of support: 2025-06-01---
Stage 5: ARCHIVED
Characteristics:
- No longer maintained
- Kept for historical reference
- May still work but unsupported
Tasks:
- [ ] Move to
/archived/directory - [ ] Update any references
- [ ] Document why archived
---
Maintenance Checklist
Monthly
- [ ] Check activation logs for issues
- [ ] Review user feedback
- [ ] Update any broken links
Quarterly
- [ ] Validate temporal knowledge
- [ ] Run full test suite
- [ ] Check for new anti-patterns
- [ ] Update dependencies (if MCP/scripts)
Annually
- [ ] Full skill audit
- [ ] Consider restructuring
- [ ] Evaluate if still needed
---
Versioning
Follow Semantic Versioning (SemVer):
| Change Type | Version Bump | Example |
|---|---|---|
| Bug fix, typo | PATCH (0.0.X) | 1.0.0 → 1.0.1 |
| New feature, anti-pattern | MINOR (0.X.0) | 1.0.1 → 1.1.0 |
| Breaking change, restructure | MAJOR (X.0.0) | 1.1.0 → 2.0.0 |
CHANGELOG format:
## [1.2.0] - 2025-01-15
### Added
- New anti-pattern: Template Theater
- Script for activation testing
### Changed
- Updated NOT clause for better precision
### Fixed
- Typo in example code---
When to Create vs Extend
| Scenario | Action |
|---|---|
| New domain expertise | Create new skill |
| Extension of existing | Extend that skill |
| Skill > 500 lines | Split into focused skills |
| Cross-domain | Create composition pattern |
| Experiment | Create in DRAFT, delete if fails |
---
Skill Health Indicators
| Indicator | Healthy | Warning | Critical |
|---|---|---|---|
| Activation precision | >90% | 70-90% | <70% |
| SKILL.md lines | <300 | 300-500 | >500 |
| Anti-patterns | 3+ | 1-2 | 0 |
| Last update | <3 months | 3-6 months | >6 months |
| Validation errors | 0 | 1-2 | >2 |
Skill Validation Checklist
Complete guide to reviewing and testing skills.
Review Checklist
CRITICAL (must-have)
- [ ] Description has keywords AND NOT clause
- [ ] SKILL.md under 500 lines (
wc -l SKILL.md) - [ ] All referenced files exist (
find skill-dir/ -type f) - [ ] Test activation: Does it activate when it should?
- [ ] Test non-activation: Does it NOT activate when it shouldn't?
HIGH PRIORITY (should-have)
- [ ] Has "When to Use" and "When NOT to Use" sections
- [ ] Includes 1-3 anti-patterns with "Why it's wrong"
- [ ] Encodes domain shibboleths (expert vs novice knowledge)
- [ ]
allowed-toolsis minimal
NICE TO HAVE (polish)
- [ ] Temporal knowledge (what changed when)
- [ ] Working code examples (not just templates)
- [ ] References for deep dives
- [ ] Bash restrictions if applicable
Activation Testing
Positive Tests (SHOULD activate)
Ask Claude questions that should trigger the skill:
# Example for a React skill:
"Help me optimize this React component's re-renders"
"Why is my useEffect running twice?"
"How do I prevent unnecessary renders?"
# Check: Did the skill activate?Negative Tests (SHOULD NOT activate)
Ask questions that should NOT trigger the skill:
# Example for a React skill:
"Help me write a Python script"
"What's the best database for my project?"
"How do I set up nginx?"
# Check: Did it correctly NOT activate?Edge Cases
Test ambiguous queries at the boundary:
# For a React skill - might or might not be React-specific:
"How do I handle forms?"
"What's the best state management?"Integration Testing
- [ ] Test with related skills (do they conflict or complement?)
- [ ] Test with MCPs (does skill guide MCP usage?)
- [ ] Test in different project contexts
Security Audit
- [ ] Read all scripts before enabling skill
- [ ] Check for network calls / data exfiltration
- [ ] Verify allowed-tools are minimal
- [ ] Test in isolated project first
File Structure Validation
# Check for orphaned references
grep -r "references/" skill-dir/SKILL.md | \
sed 's/.*references\//references\//' | \
while read ref; do
[ -f "skill-dir/$ref" ] || echo "Missing: $ref"
done
# Check line count
wc -l skill-dir/SKILL.md
# Should be < 500
# List all files
find skill-dir/ -type f -name "*.md" -o -name "*.py" -o -name "*.sh"Description Quality Check
Good description formula:
[What it does]. [Use cases]. Activate on [keywords]. NOT for [exclusions].Red flags:
- No keywords → Won't activate correctly
- No NOT clause → False positives
- "Helps with many things" → Too vague
- Over 200 chars → Consider splitting
Success Metrics
| Metric | Target |
|---|---|
| Correct activation | >90% |
| False positive rate | <5% |
| Token usage | <5k typical |
| Error prevention | Measurable reduction |
Common Issues
| Issue | Symptom | Fix |
|---|---|---|
| Won't activate | Missing keywords | Add specific trigger words |
| Activates too much | No exclusions | Add NOT clause |
| Claude ignores sections | Buried too deep | Move to main SKILL.md |
| Missing files | Reference errors | Remove refs or create files |
| Token bloat | Slow loading | Extract to /references |
#!/usr/bin/env python3
"""
Self-Contained Skill Checker
Verifies that a skill ships working tools, not just instructions.
Detects: Phantom Tools, Template Soup, Incomplete MCPs, Missing Agents.
Usage: python check_self_contained.py <skill_path>
"""
import os
import sys
import re
import json
from pathlib import Path
from dataclasses import dataclass, field
from enum import Enum
from typing import List, Optional, Set
class Status(Enum):
PASS = "PASS"
WARN = "WARN"
FAIL = "FAIL"
SKIP = "SKIP"
@dataclass
class Check:
name: str
status: Status
message: str
details: List[str] = field(default_factory=list)
class SelfContainedChecker:
"""Check if a skill ships working tools."""
# Markers that indicate template/placeholder code
# Must be actual placeholders, not descriptions of anti-patterns
TEMPLATE_MARKERS = [
r'#\s*TODO:', # TODO: with colon (action item)
r'//\s*TODO:', # JS TODO:
r'#\s*FIXME:', # FIXME: with colon
r'//\s*FIXME:', # JS FIXME:
r'raise\s+NotImplementedError\(\)', # Unimplemented function
r'throw\s+new\s+Error\(["\']Not implemented',
r'^\s*pass\s*$', # bare pass (placeholder)
r'YOUR_[A-Z_]+_HERE', # YOUR_API_KEY_HERE
r'REPLACE_THIS',
r'^\s*\.\.\.\s*$', # bare ... placeholder
]
def __init__(self, skill_path: str):
self.skill_path = Path(skill_path)
self.skill_md = self.skill_path / "SKILL.md"
self.checks: List[Check] = []
self.skill_content = ""
def run(self) -> List[Check]:
"""Run all self-contained checks."""
if not self.skill_path.exists():
self.checks.append(Check(
"Skill exists",
Status.FAIL,
f"Skill directory not found: {self.skill_path}"
))
return self.checks
if self.skill_md.exists():
self.skill_content = self.skill_md.read_text()
self.check_scripts()
self.check_mcp_server()
self.check_agents()
self.check_referenced_files()
self.summarize()
return self.checks
def check_scripts(self):
"""Check scripts/ directory for working code."""
scripts_dir = self.skill_path / "scripts"
if not scripts_dir.exists():
# Check if skill mentions scripts but doesn't have them
if re.search(r'scripts/', self.skill_content):
self.checks.append(Check(
"Scripts directory",
Status.FAIL,
"SKILL.md references scripts/ but directory doesn't exist",
["This is a Phantom Tools anti-pattern"]
))
else:
self.checks.append(Check(
"Scripts directory",
Status.SKIP,
"No scripts/ directory (not required)"
))
return
# Find all script files
scripts = list(scripts_dir.glob("*.py")) + \
list(scripts_dir.glob("*.sh")) + \
list(scripts_dir.glob("*.js")) + \
list(scripts_dir.glob("*.ts"))
if not scripts:
self.checks.append(Check(
"Scripts directory",
Status.WARN,
"scripts/ exists but contains no script files"
))
return
# Check each script for template markers
template_scripts = []
working_scripts = []
for script in scripts:
content = script.read_text()
lines = content.split('\n')
is_template = False
markers_found = []
for pattern in self.TEMPLATE_MARKERS:
# Check each line for patterns with ^ anchors
for line in lines:
# Skip lines that are defining string patterns (like in this checker)
stripped = line.strip()
if stripped.startswith(("r'", 'r"', "'", '"')):
continue
if re.search(pattern, line, re.IGNORECASE | re.MULTILINE):
is_template = True
if pattern not in markers_found:
markers_found.append(pattern)
break
if is_template:
template_scripts.append((script.name, markers_found))
else:
working_scripts.append(script.name)
if template_scripts and not working_scripts:
self.checks.append(Check(
"Scripts quality",
Status.FAIL,
f"All {len(template_scripts)} scripts are templates, not working code",
[f" {name}: contains {markers}" for name, markers in template_scripts]
))
elif template_scripts:
self.checks.append(Check(
"Scripts quality",
Status.WARN,
f"{len(template_scripts)} of {len(scripts)} scripts are templates",
[f" Template: {name}" for name, _ in template_scripts] +
[f" Working: {name}" for name in working_scripts]
))
else:
self.checks.append(Check(
"Scripts quality",
Status.PASS,
f"All {len(working_scripts)} scripts appear to be working code",
[f" {name}" for name in working_scripts]
))
def check_mcp_server(self):
"""Check mcp-server/ directory for complete implementation."""
mcp_dir = self.skill_path / "mcp-server"
if not mcp_dir.exists():
# Only flag if skill claims to SHIP an MCP (path reference), not just discusses them
# Look for: `mcp-server/`, "See mcp-server/", "Run mcp-server/"
ships_mcp = re.search(r'`mcp-server/', self.skill_content) or \
re.search(r'[Ss]ee\s+mcp-server/', self.skill_content) or \
re.search(r'[Rr]un\s+.*mcp-server/', self.skill_content)
if ships_mcp:
self.checks.append(Check(
"MCP Server",
Status.FAIL,
"SKILL.md references mcp-server/ path but directory doesn't exist",
["This is a Phantom Tools anti-pattern"]
))
else:
self.checks.append(Check(
"MCP Server",
Status.SKIP,
"No mcp-server/ directory (not required)"
))
return
issues = []
passes = []
# Check for package.json
package_json = mcp_dir / "package.json"
if not package_json.exists():
issues.append("Missing package.json")
else:
passes.append("Has package.json")
try:
pkg = json.loads(package_json.read_text())
if "dependencies" not in pkg and "devDependencies" not in pkg:
issues.append("package.json has no dependencies")
if "@modelcontextprotocol/sdk" not in str(pkg):
issues.append("Missing @modelcontextprotocol/sdk dependency")
else:
passes.append("Has MCP SDK dependency")
except json.JSONDecodeError:
issues.append("package.json is invalid JSON")
# Check for source files
src_files = list(mcp_dir.glob("src/*.ts")) + \
list(mcp_dir.glob("src/*.js")) + \
list(mcp_dir.glob("*.ts")) + \
list(mcp_dir.glob("*.js"))
if not src_files:
issues.append("No source files found")
else:
passes.append(f"Has {len(src_files)} source file(s)")
# Check for template markers in source
for src in src_files:
content = src.read_text()
for pattern in self.TEMPLATE_MARKERS:
if re.search(pattern, content, re.IGNORECASE):
issues.append(f"{src.name} contains template markers")
break
# Check for README
readme = mcp_dir / "README.md"
if not readme.exists():
issues.append("Missing README.md with installation instructions")
else:
passes.append("Has README.md")
# Determine status
if len(issues) >= 3:
status = Status.FAIL
msg = "MCP server is incomplete"
elif issues:
status = Status.WARN
msg = "MCP server has issues"
else:
status = Status.PASS
msg = "MCP server appears complete"
self.checks.append(Check(
"MCP Server",
status,
msg,
[f" ✓ {p}" for p in passes] + [f" ✗ {i}" for i in issues]
))
def check_agents(self):
"""Check agents/ directory for complete definitions."""
agents_dir = self.skill_path / "agents"
if not agents_dir.exists():
# Only flag if skill claims to SHIP agents (path reference), not just discusses them
ships_agents = re.search(r'`agents/', self.skill_content) or \
re.search(r'[Ss]ee\s+agents/', self.skill_content) or \
re.search(r'[Rr]un\s+.*agents/', self.skill_content)
if ships_agents:
self.checks.append(Check(
"Agents",
Status.FAIL,
"SKILL.md references agents/ path but directory doesn't exist",
["This is a Phantom Tools anti-pattern"]
))
else:
self.checks.append(Check(
"Agents",
Status.SKIP,
"No agents/ directory (not required)"
))
return
# Find agent definitions
agent_files = list(agents_dir.glob("*.md")) + \
list(agents_dir.glob("*.yaml")) + \
list(agents_dir.glob("*.yml"))
if not agent_files:
self.checks.append(Check(
"Agents",
Status.WARN,
"agents/ exists but contains no definition files"
))
return
# Check each agent definition
complete = []
incomplete = []
required_sections = ['purpose', 'prompt', 'tools', 'workflow']
for agent_file in agent_files:
content = agent_file.read_text().lower()
missing = []
for section in required_sections:
if section not in content:
missing.append(section)
if missing:
incomplete.append((agent_file.name, missing))
else:
complete.append(agent_file.name)
if incomplete and not complete:
self.checks.append(Check(
"Agents",
Status.FAIL,
f"All {len(incomplete)} agent definitions are incomplete",
[f" {name}: missing {missing}" for name, missing in incomplete]
))
elif incomplete:
self.checks.append(Check(
"Agents",
Status.WARN,
f"{len(incomplete)} of {len(agent_files)} agent definitions incomplete",
[f" Incomplete: {name}" for name, _ in incomplete] +
[f" Complete: {name}" for name in complete]
))
else:
self.checks.append(Check(
"Agents",
Status.PASS,
f"All {len(complete)} agent definitions appear complete",
[f" {name}" for name in complete]
))
def check_referenced_files(self):
"""Check that files referenced in SKILL.md actually exist."""
if not self.skill_content:
return
# Find file references like `scripts/foo.py` or `/references/bar.md`
patterns = [
r'`(scripts/[^`]+)`',
r'`(references/[^`]+)`',
r'`(agents/[^`]+)`',
r'`(mcp-server/[^`]+)`',
]
referenced = set()
for pattern in patterns:
matches = re.findall(pattern, self.skill_content)
referenced.update(matches)
if not referenced:
self.checks.append(Check(
"Referenced files",
Status.SKIP,
"No specific file paths referenced in SKILL.md"
))
return
missing = []
found = []
for ref in referenced:
full_path = self.skill_path / ref
if full_path.exists():
found.append(ref)
else:
missing.append(ref)
if missing:
self.checks.append(Check(
"Referenced files",
Status.FAIL,
f"{len(missing)} referenced files don't exist (Phantom Tools)",
[f" Missing: {f}" for f in missing] +
[f" Found: {f}" for f in found]
))
else:
self.checks.append(Check(
"Referenced files",
Status.PASS,
f"All {len(found)} referenced files exist",
[f" {f}" for f in found]
))
def summarize(self):
"""Add summary check."""
fails = sum(1 for c in self.checks if c.status == Status.FAIL)
warns = sum(1 for c in self.checks if c.status == Status.WARN)
passes = sum(1 for c in self.checks if c.status == Status.PASS)
skips = sum(1 for c in self.checks if c.status == Status.SKIP)
# Determine if skill is self-contained
has_tools = any(
c.status in (Status.PASS, Status.WARN)
for c in self.checks
if c.name in ("Scripts quality", "MCP Server", "Agents")
)
if fails > 0:
status = Status.FAIL
msg = f"Skill has {fails} critical issues"
elif warns > 0:
status = Status.WARN
msg = f"Skill has {warns} warnings to address"
elif has_tools:
status = Status.PASS
msg = "Skill is self-contained with working tools"
else:
status = Status.WARN
msg = "Skill has no tools (instructions only)"
self.checks.append(Check(
"SUMMARY",
status,
msg,
[f" {passes} passed, {warns} warnings, {fails} failed, {skips} skipped"]
))
def print_report(checks: List[Check]):
"""Print formatted report."""
status_icons = {
Status.PASS: "✅",
Status.WARN: "⚠️ ",
Status.FAIL: "❌",
Status.SKIP: "⏭️ ",
}
print(f"\n{'='*60}")
print("SELF-CONTAINED SKILL CHECK")
print(f"{'='*60}\n")
for check in checks:
icon = status_icons[check.status]
print(f"{icon} {check.name}: {check.message}")
for detail in check.details:
print(f" {detail}")
print()
print(f"{'='*60}\n")
def main():
if len(sys.argv) < 2:
print("Usage: python check_self_contained.py <skill_path>")
print("\nChecks if a skill ships working tools or just instructions.")
print("\nDetects:")
print(" - Phantom Tools: Referenced files that don't exist")
print(" - Template Soup: Scripts with TODO/FIXME markers")
print(" - Incomplete MCPs: Missing package.json, dependencies, etc.")
print(" - Missing Agents: Referenced but undefined subagents")
print("\nExample:")
print(" python check_self_contained.py ~/.claude/skills/my-skill/")
sys.exit(1)
skill_path = sys.argv[1]
print(f"Checking self-contained status: {skill_path}")
checker = SelfContainedChecker(skill_path)
checks = checker.run()
print_report(checks)
# Exit code based on failures
fails = sum(1 for c in checks if c.status == Status.FAIL)
sys.exit(1 if fails else 0)
if __name__ == '__main__':
main()