Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:51:34 +08:00
commit acde81dcfe
59 changed files with 22282 additions and 0 deletions

View File

@@ -0,0 +1,226 @@
---
name: skill-builder
description: Build efficient, scalable Claude Code skills using progressive disclosure and token optimization. Use when creating new skills, optimizing existing skills, or learning skill development patterns. Provides templates, checklists, and working examples.
version: 1.0.0
---
# Build Skills Using Progressive Disclosure
## When to Use
- Creating a new Claude Code skill from scratch
- Optimizing an existing skill for token efficiency
- Learning progressive disclosure, dynamic manifests, or deferred loading patterns
- Need templates, checklists, or troubleshooting for skill development
- Want to understand the three-level loading pattern (metadata → body → bundled)
## What This Skill Does
**Guides you through building efficient Claude Code skills** that follow best practices:
- **Progressive Disclosure**: Structure skills in * levels (metadata, body, bundled, *)
- **Token Optimization**: Keep metadata ~100 tokens, body <2k tokens, details in bundled files
- **Templates**: Copy-paste ready SKILL.md templates and structure examples
- **Process**: Step-by-step guide from planning to deployment
- **Patterns**: Deep dives into progressive disclosure, dynamic manifests, deferred loading
## 🎯 Core Principle: The 3-Level Pattern
**Every skill loads in three levels:**
| Level | File | Loaded When | Token Limit | What Goes Here |
|-------|------|-------------|-------------|----------------|
| **1** | SKILL.md Metadata (YAML) | Always | ~100 | Name, description, version |
| **2** | SKILL.md Body (Markdown) | Skill triggers | <5k (<2k recommended) | Quick ref, core instructions, links to Level 3 |
| **3** | Bundled files in `/reference/` | As-needed by Claude | Unlimited | Detailed docs, scripts, examples, specs |
**Key rules**:
1. SKILL.md is a **table of contents**, not a comprehensive manual
2. **ALL reference .md files MUST be in `/reference/` folder** (not root!)
3. Link to them as `./reference/filename.md` from SKILL.md
4. Move details to Level 3 files that Claude loads only when referenced
**Critical Structure:**
```
skill-name/
├── SKILL.md # ✅ Level 1+2: Only .md in root
├── reference/ # ✅ REQUIRED: All reference .md files HERE
│ ├── detail1.md
│ └── detail2.md
└── scripts/ # Executable tools
```
## Quick Start
### Building Your First Skill (Recommended)
1. **Read the 3-level table above** (30 seconds)
2. **Scan the template**: [Quick Reference](./reference/quick-reference.md) (3 min)
3. **Follow the process**: [Skill Creation Process](./reference/skill-creation-process.md) (3-5 hours)
- Phase 1: Planning (30 min)
- Phase 2: Structure (15 min)
- Phase 3: Implementation (2-4 hours) with full `incident-triage` example
- Phase 4: Testing (30 min)
- Phase 5: Refinement (ongoing)
**Result**: A working skill following best practices
### Quick Lookup (While Building)
Need a template or checklist right now?
→ [Quick Reference](./reference/quick-reference.md) - Templates, checklists, common pitfalls
### Learn the Patterns (Deep Dive)
Want to understand the architectural patterns?
1. [Philosophy](./reference/philosophy.md) - Why these patterns matter (10 min)
2. [Progressive Disclosure](./reference/progressive-disclosure.md) - Reveal info gradually (~1.4k tokens)
3. [Dynamic Manifests](./reference/dynamic-manifests.md) - Runtime capability discovery (~1.9k tokens)
4. [Deferred Loading](./reference/deferred-loading.md) - Lazy initialization (~2.2k tokens)
## Inputs
This skill doesn't require specific inputs. Use it when:
- User asks to "create a skill" or "build a skill"
- User mentions "progressive disclosure" or "token optimization"
- User needs help with SKILL.md structure
- User asks about best practices for Claude Code skills
## Outputs
Provides guidance, templates, and examples for:
- SKILL.md metadata and body structure
- **Folder layout with REQUIRED `/reference/` folder** for all reference .md files
- Token budgets per level
- Copy-paste templates
- Working code examples (incident-triage)
- Structure validation checklist
- Troubleshooting common issues
## ⚠️ Critical Requirement: /reference/ Folder
**Before creating any skill, understand this:**
**CORRECT Structure:**
```
skill-name/
├── SKILL.md # Only .md in root
├── reference/ # ALL reference docs go HERE
│ ├── api-spec.md
│ ├── examples.md
│ └── advanced.md
└── scripts/
```
**WRONG Structure:**
```
skill-name/
├── SKILL.md
├── api-spec.md # ❌ Should be in reference/
├── examples.md # ❌ Should be in reference/
└── scripts/
```
**This hierarchy is MANDATORY for:**
1. Token optimization (Claude loads only what's needed)
2. Consistency across all skills
3. Clear separation of concerns (Level 2 vs Level 3)
## Available Reference Files
All detailed content lives in bundled files (Level 3):
- **[Quick Reference](./reference/quick-reference.md)** (~1k tokens) - Templates, checklists, metadata examples
- **[Philosophy](./reference/philosophy.md)** (~700 tokens) - Why patterns matter, learning paths
- **[Skill Creation Process](./reference/skill-creation-process.md)** (~5.5k tokens) - Complete step-by-step guide
- **[Progressive Disclosure](./reference/progressive-disclosure.md)** (~1.4k tokens) - Pattern deep dive
- **[Dynamic Manifests](./reference/dynamic-manifests.md)** (~1.9k tokens) - Runtime discovery pattern
- **[Deferred Loading](./reference/deferred-loading.md)** (~2.2k tokens) - Lazy loading pattern
## Common Questions
**Q: Where do I start?**
A: Read the [3-level table](#-core-principle-the-3-level-pattern) above, then follow [Skill Creation Process](./reference/skill-creation-process.md)
**Q: My SKILL.md is too long. What do I do?**
A: Move details to `reference/*.md` files (Level 3). Keep SKILL.md body <2k tokens.
**Q: How do I make my skill trigger correctly?**
A: Use specific keywords in the description (Level 1 metadata). See [Quick Reference](./reference/quick-reference.md#metadata-best-practices)
**Q: Can I see a complete working example?**
A: Yes! See the `incident-triage` example in [Skill Creation Process](./reference/skill-creation-process.md)
## Guardrails
- **ALWAYS enforce `/reference/` folder structure** - reference .md files MUST NOT be in root
- **Validate folder structure** before considering a skill complete
- Focus on **creating skills**, not using existing skills
- Emphasize **token optimization**: ~100 (L1), <2k (L2), unlimited (L3)
- Always recommend **scripts for deterministic logic** instead of generated code
- Remind about **environment variables** for credentials (never hardcode)
- Point to **working examples** (incident-triage) rather than abstract explanations
- **Catch and fix** skills with reference files in root directory
## Triggers
This skill should activate when user mentions:
- "create a skill" or "build a skill"
- "progressive disclosure"
- "token optimization" or "token limits"
- "SKILL.md" or "skill structure"
- "best practices" for Claude Code skills
- "how to organize a skill"
- "skill creation process"
## Validation
**NEW: Automated Skill Validator**
Use the included validation script to check your skill before deployment:
```bash
# Install dependencies (first time only)
cd .claude/skill-builder/scripts
npm install
# Validate your skill
node validate-skill.js /path/to/your/skill
node validate-skill.js . # validate current directory
```
The validator checks:
- ✓ YAML frontmatter format and syntax
- ✓ Required fields (name, description)
- ✓ Description specificity and triggers
- ✓ Token budgets (metadata ~100, body <2k)
- ✓ File structure (/reference/ folder compliance)
- ✓ No stray .md files in root
- ✓ Path format (forward slashes)
- ✓ Referenced files exist
See: [Validation Script](./scripts/validate-skill.js)
## Testing
To test this skill:
```bash
# Ask Claude:
"Help me create a new Claude Code skill for incident triage"
"What are best practices for skill token optimization?"
"Show me a SKILL.md template"
```
Verify the skill:
- [ ] Provides the 3-level table
- [ ] Links to appropriate reference files
- [ ] Emphasizes token limits
- [ ] Shows working examples
- [ ] Guides through the process
- [ ] Passes automated validation (run validate-skill.js)
---
**Last Updated**: 2025-10-20

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,272 @@
# Best Practices Guide
**Progressive Disclosure Applied**: This guide uses a hierarchical structure where you start with high-level concepts and progressively drill down into technical details.
**Token-Optimized Structure**:
- This file: ~628 tokens (overview & navigation)
- [best-practices.md](./best-practices.md): ~920 tokens (quick reference for building skills)
- Topic files: 1.4k-2.2k tokens each (deep dives loaded as-needed)
**📑 Navigation**: See [INDEX.md](./INDEX.md) for complete file reference and navigation patterns.
---
## 🎯 Quick Start (Level 1)
### Building a New Skill?
**[Skill Creation Process](./reference/skill-creation-process.md)** - Follow this step-by-step guide
### Learning Patterns?
Choose your learning path:
- **[Progressive Disclosure](./topics/progressive-disclosure.md)** - Learn the core UX/architectural pattern
- **[Dynamic Manifests](./topics/dynamic-manifests.md)** - Implement runtime capability discovery
- **[Deferred Loading](./topics/deferred-loading.md)** - Optimize resource initialization
### Need Quick Reference?
**[best-practices.md](./best-practices.md)** - Checklists, templates, and common pitfalls
---
## 📚 Concept Map
```
Best Practices
├─── Progressive Disclosure ◄──┐
│ (Design Pattern) │
│ │ │
│ └─── Influences ────────────┤
│ │
├─── Dynamic Manifests ◄────────┤
│ (Runtime Discovery) │
│ │ │
│ └─── Enables ───────────────┤
│ │
└─── Deferred Loading ◄─────────┘
(Lazy Initialization)
```
---
## 🚀 Why These Patterns Matter
### The Problem
Traditional systems load everything at startup:
- ❌ Slow initialization
- ❌ High memory consumption
- ❌ Wasted resources on unused features
- ❌ Poor scalability
### The Solution
Progressive Disclosure + Dynamic Manifests + Deferred Loading:
- ✅ Fast startup (load on-demand)
- ✅ Efficient resource usage
- ✅ Adaptive capabilities
- ✅ Context-aware feature availability
---
## 📖 Learning Path
### For Beginners
1. Start with **[Progressive Disclosure](./topics/progressive-disclosure.md#what-is-it)** - Understand the philosophy
2. See **[Simple Examples](./topics/progressive-disclosure.md#simple-examples)**
3. Review **[Quick Start](./topics/dynamic-manifests.md#quick-start)**
### For Practitioners
1. Read **[Implementation Patterns](./topics/progressive-disclosure.md#implementation-patterns)**
2. Configure **[Dynamic Manifests](./topics/dynamic-manifests.md#configuration)**
3. Optimize with **[Deferred Loading](./topics/deferred-loading.md#strategies)**
### For Architects
1. Study **[Architectural Principles](./topics/progressive-disclosure.md#architectural-principles)**
2. Design **[Capability Systems](./topics/dynamic-manifests.md#capability-systems)**
3. Implement **[Advanced Optimization](./topics/deferred-loading.md#advanced-techniques)**
---
## 🔗 Topic Relationships
### Progressive Disclosure → Dynamic Manifests
Progressive disclosure provides the **design philosophy**: show users only what they need, when they need it.
Dynamic manifests provide the **technical implementation**: systems query capabilities at runtime, enabling features progressively.
**Example**: A chat interface starts with basic tools (Level 1), then reveals advanced tools (Level 2) as the user demonstrates expertise → The system's dynamic manifest adjusts which tools are available based on context.
### Dynamic Manifests → Deferred Loading
Dynamic manifests tell you **what's available**.
Deferred loading determines **when to initialize it**.
**Example**: Dynamic manifest says "Tool X is available" → Deferred loading ensures Tool X's code isn't loaded until first use → Saves memory and startup time.
---
## 🎓 Real-World Applications
### MCP (Model Context Protocol) Skills
```
User opens Claude Code
[Progressive Disclosure]
→ Only basic skills shown initially
User works with project files
[Dynamic Manifests]
→ System detects project type
→ New relevant skills appear
User invokes advanced skill
[Deferred Loading]
→ Skill code loaded on first use
→ Subsequent calls use cached version
```
### Web Applications
```
User visits page
[Progressive Disclosure]
→ Core UI loads first
User navigates to dashboard
[Dynamic Manifests]
→ Check user permissions
→ Build feature menu dynamically
User clicks "Export Data"
[Deferred Loading]
→ Load export library on demand
→ Initialize only when needed
```
---
## 🛠️ Implementation Checklist
Use this as a quick reference when implementing these patterns:
- [ ] Design information hierarchy (Progressive Disclosure)
- [ ] Identify capability tiers (Basic → Intermediate → Advanced)
- [ ] Implement runtime discovery endpoints (Dynamic Manifests)
- [ ] Create `.well-known/mcp/manifest.json` (MCP specific)
- [ ] Enable lazy initialization (Deferred Loading)
- [ ] Add caching strategies (Optimization)
- [ ] Implement change notifications (Dynamic updates)
- [ ] Test without system restart (Validation)
---
## 📊 Performance Metrics
Track these to measure success:
| Metric | Before | Target | Pattern |
|--------|--------|--------|---------|
| Initial Load Time | 5s | < 1s | Progressive Disclosure |
| Memory at Startup | 500MB | < 100MB | Deferred Loading |
| Feature Discovery | Static | Dynamic | Dynamic Manifests |
| Context Tokens Used | 10k | < 2k | Progressive Loading |
---
## 🔍 Deep Dive Topics
Ready to go deeper? Click any topic:
1. **[Progressive Disclosure](./topics/progressive-disclosure.md)**
- Design philosophy
- UX patterns
- Information architecture
- Cognitive load management
2. **[Dynamic Manifests](./topics/dynamic-manifests.md)**
- Configuration guide
- Endpoint implementation
- Registry patterns
- MCP-specific setup
3. **[Deferred Loading](./topics/deferred-loading.md)**
- Lazy initialization
- Code splitting
- Resource optimization
- Caching strategies
---
## 🎯 Quick Wins
Want immediate improvements? Start here:
### 5-Minute Win: Enable Dynamic Discovery
```json
// claude_desktop_config.json
{
"mcpServers": {
"your-server": {
"dynamicDiscovery": true,
"discoveryInterval": 5000
}
}
}
```
See [Dynamic Manifests: Quick Start](./topics/dynamic-manifests.md#quick-start)
### 15-Minute Win: Implement Lazy Loading
```python
from functools import lru_cache
@lru_cache(maxsize=128)
def load_expensive_resource():
# Only loads on first call
return initialize_resource()
```
See [Deferred Loading: Basic Patterns](./topics/deferred-loading.md#basic-patterns)
### 30-Minute Win: Progressive Disclosure UI
```markdown
# Level 1: Essentials (always visible)
## Getting Started
# Level 2: Intermediate (click to expand)
<details>
<summary>Advanced Features</summary>
...
</details>
# Level 3: Expert (separate page)
See [Advanced Guide](./advanced.md)
```
See [Progressive Disclosure: UI Patterns](./topics/progressive-disclosure.md#ui-patterns)
---
## 📚 Additional Resources
- [MCP Official Spec](https://spec.modelcontextprotocol.io/)
- [Progressive Disclosure (Nielsen Norman Group)](https://www.nngroup.com/articles/progressive-disclosure/)
- [Lazy Loading Best Practices](https://web.dev/lazy-loading/)
---
## 🆘 Troubleshooting
**Problem**: Changes not appearing without restart
**Solution**: Check [Dynamic Manifests: Configuration](./topics/dynamic-manifests.md#configuration)
**Problem**: High memory usage at startup
**Solution**: Review [Deferred Loading: Strategies](./topics/deferred-loading.md#strategies)
**Problem**: Users overwhelmed by options
**Solution**: Apply [Progressive Disclosure: Principles](./topics/progressive-disclosure.md#principles)
---
**Last Updated**: 2025-10-20
**Version**: 1.0.0

View File

@@ -0,0 +1,620 @@
# Progressive Disclosure
> **Definition**: A design pattern that sequences information and actions across multiple screens to reduce cognitive load and improve user experience.
**Navigation**: [← Back to Best Practices](../README.md) | [Next: Dynamic Manifests →](./dynamic-manifests.md)
---
## Table of Contents
- [What Is It?](#what-is-it) ← Start here
- [Why Use It?](#why-use-it)
- [Simple Examples](#simple-examples)
- [Implementation Patterns](#implementation-patterns) ← For practitioners
- [Architectural Principles](#architectural-principles) ← For architects
- [UI Patterns](#ui-patterns)
- [Related Concepts](#related-concepts)
---
## What Is It?
Progressive disclosure is revealing information **gradually** rather than all at once.
### The Core Idea
```
❌ Bad: Show everything immediately
User sees: [100 buttons] [50 options] [20 menus]
Result: Overwhelmed, confused
✅ Good: Show essentials, reveal more as needed
User sees: [5 core actions]
User clicks "More": [15 additional options appear]
User clicks "Advanced": [Advanced features panel opens]
Result: Focused, confident
```
### Real-World Analogy
**Restaurant Menu**
```
1. Main categories (Appetizers, Entrees, Desserts) ← Level 1
└─ Click "Entrees"
2. Entree types (Pasta, Seafood, Steak) ← Level 2
└─ Click "Pasta"
3. Specific dishes with details ← Level 3
```
This prevents menu overwhelm while still providing complete information.
---
## Why Use It?
### Benefits
| Benefit | Description | Impact |
|---------|-------------|--------|
| **Reduced Cognitive Load** | Users process less information at once | Less confusion, faster decisions |
| **Improved Discoverability** | Users find relevant features easier | Better feature adoption |
| **Faster Performance** | Load only what's needed now | Quicker startup, less memory |
| **Adaptive Complexity** | Beginners see simple, experts see advanced | Serves all skill levels |
### When to Use
**Use progressive disclosure when:**
- Users don't need all features/info immediately
- Feature set is large or complex
- Users have varying skill levels
- Performance/load time matters
**Don't use when:**
- All information is equally critical
- Users need to compare all options at once
- Feature set is small (< 7 items)
- Extra clicks harm the experience
---
## Simple Examples
### Example 1: Settings Panel
**Traditional Approach:**
```
Settings
├── Profile Name: _______
├── Email: _______
├── Password: _______
├── Theme: [ Dark | Light ]
├── Language: [ English ▼ ]
├── Timezone: [ UTC-5 ▼ ]
├── Date Format: [ MM/DD/YYYY ▼ ]
├── Currency: [ USD ▼ ]
├── API Keys: _______
├── Webhook URL: _______
├── Debug Mode: [ ]
├── Log Level: [ Info ▼ ]
└── ... (20 more settings)
```
Result: Users scrolls, scans, feels lost.
**Progressive Disclosure:**
```
Settings
├── Profile Name: _______
├── Email: _______
├── Theme: [ Dark | Light ]
├── [▼ Advanced Settings]
│ └── (collapsed by default)
└── [▼ Developer Settings]
└── (collapsed by default)
```
Click "Advanced Settings":
```
Advanced Settings
├── Language: [ English ▼ ]
├── Timezone: [ UTC-5 ▼ ]
├── Date Format: [ MM/DD/YYYY ▼ ]
└── Currency: [ USD ▼ ]
```
### Example 2: MCP Skills
**Traditional: All Skills Loaded**
```python
# Load everything at startup
available_skills = [
"basic-search",
"file-operations",
"web-scraping",
"data-analysis",
"machine-learning",
"blockchain-analysis",
"video-processing",
# ... 50 more skills
]
```
Result: Slow startup, high memory, overwhelming list.
**Progressive Disclosure:**
```python
# Level 1: Always available
tier_1_skills = ["basic-search", "file-operations"]
# Level 2: Loaded when project type detected
if is_data_project():
tier_2_skills = ["data-analysis", "visualization"]
# Level 3: Loaded on explicit request
if user_requests("machine-learning"):
tier_3_skills = ["ml-training", "model-deployment"]
```
### Example 3: Command Line Tool
**Traditional:**
```bash
$ mytool --help
Usage: mytool [OPTIONS] COMMAND [ARGS]...
Options:
--config PATH Configuration file path
--verbose Verbose output
--debug Debug mode
--log-file PATH Log file path
--log-level LEVEL Logging level
--timeout SECONDS Operation timeout
--retry-count N Number of retries
--parallel N Parallel workers
--cache-dir PATH Cache directory
--no-cache Disable caching
--format FORMAT Output format
... (30 more options)
Commands:
init Initialize project
build Build project
deploy Deploy project
test Run tests
... (20 more commands)
```
**Progressive Disclosure:**
```bash
$ mytool --help
Usage: mytool [OPTIONS] COMMAND
Common Commands:
init Initialize project
build Build project
deploy Deploy project
Run 'mytool COMMAND --help' for command-specific options
Run 'mytool --help-all' for complete documentation
$ mytool build --help
Usage: mytool build [OPTIONS]
Essential Options:
--output PATH Output directory (default: ./dist)
--watch Watch for changes
Advanced Options (mytool build --help-advanced):
--parallel N Parallel workers
--cache-dir PATH Cache directory
... (more advanced options)
```
---
## Implementation Patterns
### Pattern 1: Tiered Information Architecture
Organize content into logical tiers:
```
Tier 1: Essentials (80% of users need this)
├── Core functionality
├── Most common tasks
└── Critical information
Tier 2: Intermediate (30% of users need this)
├── Advanced features
├── Customization options
└── Detailed documentation
Tier 3: Expert (5% of users need this)
├── Edge cases
├── Debug/diagnostic tools
└── API reference
```
**Implementation:**
```markdown
# My API Documentation
## Quick Start (Tier 1)
Basic usage examples that work for most cases.
<details>
<summary>Advanced Usage (Tier 2)</summary>
## Authentication Options
Detailed authentication flows...
## Rate Limiting
How to handle rate limits...
</details>
[Expert Guide](./expert-guide.md) (Tier 3) →
```
### Pattern 2: Context-Aware Disclosure
Show features based on user context:
```python
class FeatureDisclosure:
def get_available_features(self, user_context):
features = ["core_feature_1", "core_feature_2"] # Always available
# Intermediate features
if user_context.skill_level >= "intermediate":
features.extend(["advanced_search", "bulk_operations"])
# Expert features
if user_context.has_permission("admin"):
features.extend(["system_config", "user_management"])
# Contextual features
if user_context.project_type == "data_science":
features.extend(["ml_tools", "visualization"])
return features
```
### Pattern 3: Progressive Enhancement
Start minimal, add capabilities:
```javascript
// Level 1: Basic functionality works everywhere
function saveData(data) {
localStorage.setItem('data', JSON.stringify(data));
}
// Level 2: Enhanced with sync (if available)
if (navigator.onLine && hasCloudSync()) {
function saveData(data) {
localStorage.setItem('data', JSON.stringify(data));
cloudSync.upload(data); // Progressive enhancement
}
}
// Level 3: Real-time collaboration (if enabled)
if (hasFeature('realtime_collaboration')) {
function saveData(data) {
localStorage.setItem('data', JSON.stringify(data));
cloudSync.upload(data);
websocket.broadcast(data); // Further enhancement
}
}
```
### Pattern 4: Lazy Loading
Defer initialization until needed:
```python
class SkillManager:
def __init__(self):
self._skills = {}
self._skill_registry = {
'basic': ['search', 'files'],
'advanced': ['ml', 'data_analysis'],
'expert': ['custom_models']
}
def get_skill(self, skill_name):
# Progressive disclosure: Load on first access
if skill_name not in self._skills:
self._skills[skill_name] = self._load_skill(skill_name)
return self._skills[skill_name]
def _load_skill(self, skill_name):
# Deferred loading happens here
module = import_module(f'skills.{skill_name}')
return module.SkillClass()
```
---
## Architectural Principles
### Principle 1: Information Hierarchy
Design with clear levels:
```
Level 0: Critical (always visible, < 5 items)
└─ Things users MUST see/do immediately
Level 1: Primary (visible by default, < 10 items)
└─ Core functionality, 80% use case
Level 2: Secondary (behind 1 click, < 20 items)
└─ Advanced features, configuration
Level 3: Tertiary (behind 2+ clicks, unlimited)
└─ Expert features, detailed docs, edge cases
```
### Principle 2: Cognitive Load Management
**Miller's Law**: Humans can hold 7±2 items in working memory.
**Application:**
- Level 1 UI: Show ≤ 7 primary actions
- Menus: Group into ≤ 7 categories
- Forms: Break into ≤ 7 fields per step
**Bad Example:**
```
[Button1] [Button2] [Button3] [Button4] [Button5]
[Button6] [Button7] [Button8] [Button9] [Button10]
[Button11] [Button12] [Button13] [Button14] [Button15]
```
**Good Example:**
```
[Common Actions ▼]
├─ Action 1
├─ Action 2
└─ Action 3
[Advanced ▼]
├─ Action 4
└─ Action 5
[Expert ▼]
└─ More...
```
### Principle 3: Discoverability vs. Visibility
Balance showing enough vs. hiding too much:
```
High Discoverability
│ Ideal Zone:
│ Core features visible,
│ Advanced features discoverable
│ ┌─────────────┐
│ │ ✓ Sweet │
│ │ Spot │
│ └─────────────┘
└──────────────────────────→ High Visibility
Hidden features Feature overload
```
**Techniques:**
- Visual cues: "▼ More options" "⚙ Advanced"
- Tooltips: Hint at hidden features
- Progressive help: "New features available!"
- Analytics: Track if users find features
### Principle 4: Reversible Disclosure
Users should control disclosure:
```
✅ Good: User-controlled
[▼ Show Advanced Options] ← User clicks to expand
[▲ Hide Advanced Options] ← User clicks to collapse
❌ Bad: Forced progression
Step 1 → Step 2 → Step 3 (can't go back)
```
**Implementation:**
- Persistent state: Remember user's disclosure preferences
- Keyboard shortcuts: Power users want quick access
- Breadcrumbs: Show where user is in hierarchy
---
## UI Patterns
### Pattern: Accordion/Collapsible Sections
```html
<details>
<summary>Basic Configuration</summary>
<p>Essential settings here...</p>
</details>
<details>
<summary>Advanced Configuration</summary>
<p>Advanced settings here...</p>
</details>
```
### Pattern: Tabs
```
┌─────────┬──────────┬──────────┐
│ Basic │ Advanced │ Expert │
├─────────┴──────────┴──────────┤
│ │
│ [Content for selected tab] │
│ │
└────────────────────────────────┘
```
### Pattern: Modal/Dialog
```
Main Screen (Simple)
[Click "Advanced Settings" button]
┌─────────────────────────┐
│ Advanced Settings │
│ │
│ [Complex options here] │
│ │
│ [Cancel] [Apply] │
└─────────────────────────┘
```
### Pattern: Progressive Form
```
Step 1: Basic Info Step 2: Details Step 3: Preferences
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Name: _______ │ → │ Address: ____ │ → │ Theme: [ ] │
│ Email: ______ │ │ Phone: ______ │ │ Notifications: │
│ │ │ │ │ [ ] Email │
│ [Next] │ │ [Back] [Next] │ │ [ ] SMS │
└─────────────────┘ └─────────────────┘ │ [Back] [Finish] │
└─────────────────┘
```
### Pattern: Contextual Help
```
Setting Name [?] ← Hover shows basic help
Hover: "Controls the display theme"
Click [?]: Opens detailed documentation
```
---
## Related Concepts
### Progressive Disclosure → [Dynamic Manifests](./dynamic-manifests.md)
Progressive disclosure = **design philosophy**
Dynamic manifests = **technical implementation**
Example:
- Progressive disclosure says: "Show basic tools first"
- Dynamic manifests implement: Runtime query of available tools based on context
See: [Dynamic Manifests: Configuration](./dynamic-manifests.md#configuration)
### Progressive Disclosure → [Deferred Loading](./deferred-loading.md)
Progressive disclosure = **what to show**
Deferred loading = **when to load**
Example:
- Progressive disclosure: "Advanced feature hidden until clicked"
- Deferred loading: "Advanced feature code loaded on first access"
See: [Deferred Loading: Strategies](./deferred-loading.md#strategies)
### Progressive Disclosure in MCP
MCP Skills use progressive disclosure:
```
User starts → Basic skills available
User works with Python files → Python skills appear
User requests ML feature → ML skills loaded
```
Implemented via:
- Metadata scanning (what's available)
- Lazy loading (when to load)
- Context awareness (what to show)
See: [Best Practices: MCP Applications](../README.md#real-world-applications)
---
## Measurement & Testing
### Key Metrics
Track these to validate progressive disclosure:
| Metric | Good | Bad |
|--------|------|-----|
| Time to first action | < 5s | > 30s |
| Feature discovery rate | > 70% | < 30% |
| User confusion (support tickets) | Decreasing | Increasing |
| Task completion rate | > 85% | < 60% |
### A/B Testing
```
Group A: Everything visible (control)
Group B: Progressive disclosure (test)
Measure:
- Time to complete common task
- Number of clicks
- Error rate
- User satisfaction
```
---
## Anti-Patterns
### ❌ Hiding Critical Information
```
❌ Bad: Hide error messages in collapsed section
✅ Good: Show errors prominently, hide resolution steps
```
### ❌ Too Many Levels
```
❌ Bad: Menu → Submenu → Submenu → Submenu → Action
✅ Good: Menu → Submenu → Action (max 3 levels)
```
### ❌ Inconsistent Disclosure
```
❌ Bad: Some settings in tabs, others in accordions, others in modals
✅ Good: Consistent pattern throughout app
```
### ❌ No Visual Cues
```
❌ Bad: Hidden features with no hint they exist
✅ Good: "⚙ Advanced settings" or "▼ Show more"
```
---
## Further Reading
- [Jakob Nielsen: Progressive Disclosure](https://www.nngroup.com/articles/progressive-disclosure/)
- [Information Architecture Basics](https://www.usability.gov/what-and-why/information-architecture.html)
- [Cognitive Load Theory](https://en.wikipedia.org/wiki/Cognitive_load)
---
**Navigation**: [← Back to Best Practices](../README.md) | [Next: Dynamic Manifests →](./dynamic-manifests.md)
**Last Updated**: 2025-10-20

View File

@@ -0,0 +1,362 @@
# Agent Skills Best Practices - Quick Reference
> **Quick access guide** for building efficient, maintainable Claude Code skills. For detailed architectural patterns, see [README.md](./README.md).
**📑 Navigation**: [INDEX.md](./INDEX.md) | [README.md](./README.md) | [Skill Creation Process](./reference/skill-creation-process.md)
---
## 🎯 Progressive Disclosure: Core Principle
**Progressive disclosure is the core design principle that makes Agent Skills flexible and scalable.** Like a well-organized manual that starts with a table of contents, then specific chapters, and finally a detailed appendix, skills let Claude load information only as needed:
| Level | File | Context Window | # Tokens |
|-------|------|----------------|----------|
| **1** | SKILL.md Metadata (YAML) | Always loaded | ~100 |
| **2** | SKILL.md Body (Markdown) | Loaded when Skill triggers | <5k |
| **3+** | Bundled files (text files, scripts, data) | Loaded as-needed by Claude | unlimited* |
**Key takeaways:**
- **Level 1 (Metadata)**: ~100 tokens, always in context - make it count!
- **Level 2 (Body)**: <5k tokens, loaded on trigger - keep focused
- **Level 3+ (Bundled)**: Unlimited, loaded as needed - reference from Level 2
**This means:** Your SKILL.md should be a **table of contents and quick reference**, not a comprehensive manual. Link to detailed files that Claude loads only when needed.
---
## 📑 Navigation
- **[README.md](./README.md)** - Comprehensive guide with architectural patterns
- **[Progressive Disclosure](./topics/progressive-disclosure.md)** - Design philosophy & UX patterns
- **[Dynamic Manifests](./topics/dynamic-manifests.md)** - Runtime capability discovery
- **[Deferred Loading](./topics/deferred-loading.md)** - Lazy initialization & optimization
---
## ⚡ Quick Start Checklist
Building a new skill? Follow this checklist:
- [ ] **Metadata (Level 1)**: Clear `name` and `description` (~100 tokens total)
- [ ] **Body (Level 2)**: Core instructions under 5k tokens (aim for <2k)
- [ ] **Bundled files (Level 3+)**: Complex details in separate files
- [ ] Move deterministic logic to executable scripts (not generated code)
- [ ] Extract shared utilities to reusable modules
- [ ] Add environment variable support for credentials
- [ ] Include error messages with troubleshooting steps
- [ ] Test with actual Claude usage
---
## 🎯 Core Principles (Summary)
### 1. Progressive Disclosure
Structure in layers:
- **Metadata** (always loaded) → **SKILL.md body** (on trigger) → **Linked files** (as needed)
### 2. Code > Tokens
Use scripts for deterministic tasks (API calls, data processing, calculations)
### 3. Keep SKILL.md Focused
<5k tokens (<2k recommended), scannable, action-oriented
### 4. Reusable Components
Extract shared logic to prevent duplication
### 5. Clear Metadata
Specific description helps Claude know when to trigger
### 6. Error Handling
Provide actionable feedback and troubleshooting steps
### 7. Logical Structure (Respecting Token Limits)
**⚠️ CRITICAL: Reference files MUST be in `/reference/` folder, NOT in root!**
```
skill-name/
├── SKILL.md # Level 1+2: Metadata (~100) + Body (<5k tokens)
├── reference/ # ✅ REQUIRED: Level 3 detailed docs (loaded as-needed)
│ ├── detail1.md # ✅ All .md reference files go HERE
│ └── detail2.md # ✅ NOT in root directory
├── scripts/ # Level 3: Executable code
└── shared/ # Level 3: Reusable utilities
```
**❌ WRONG - Reference files in root:**
```
skill-name/
├── SKILL.md
├── detail1.md # ❌ WRONG! Should be in reference/
├── detail2.md # ❌ WRONG! Should be in reference/
└── scripts/
```
**✅ CORRECT - Reference files in /reference/ folder:**
```
skill-name/
├── SKILL.md
├── reference/
│ ├── detail1.md # ✅ CORRECT!
│ └── detail2.md # ✅ CORRECT!
└── scripts/
```
### 8. Iterate
Test → Monitor → Refine based on actual usage
### 9. Security
No hardcoded secrets, audit third-party skills
### 10. Test
Smoke test scripts, verify with Claude, check error messages
---
## 📝 SKILL.md Template (Token-Aware)
```markdown
---
# Level 1: Metadata (~100 tokens) - Always loaded
name: skill-name
description: Specific description of what this does (triggers skill selection)
version: 1.0.0
---
# Level 2: Body (<5k tokens, <2k recommended) - Loaded on trigger
## When to Use
- Trigger condition 1
- Trigger condition 2
## Quick Start
1. Run `scripts/main.py --arg value`
2. Review output
## Advanced Usage
For complex scenarios, see [reference/advanced.md](./reference/advanced.md)
For API details, see [reference/api-spec.md](./reference/api-spec.md)
# Level 3: Bundled files - Loaded as-needed by Claude
# (Don't embed large content here - link to it!)
```
**Token budget guide:**
- Metadata: ~100 tokens
- Body target: <2k tokens (max 5k)
- If approaching 2k, move details to bundled files
---
## 🚫 Common Pitfalls
| ❌ Don't | ✅ Do |
|----------|-------|
| **Put reference files in root** | **Put reference files in /reference/ folder** |
| Put everything in SKILL.md | Split into focused files (Level 3) |
| Generate code via tokens | Write executable scripts |
| Vague names ("helper-skill") | Specific names ("pdf-form-filler") |
| Hardcode credentials | Use environment variables |
| >5k token SKILL.md body | Keep under 2k tokens (max 5k) |
| >100 token metadata | Concise name + description (~100) |
| Duplicate logic | Extract to shared modules |
| Generic descriptions | Specific trigger keywords |
---
## 🔧 Recommended Structure (Token-Optimized)
**⚠️ MANDATORY: All reference .md files MUST be in `/reference/` folder!**
```
my-skill/
├── SKILL.md # Level 1+2: Metadata (~100) + Body (<2k tokens)
│ # Quick reference + links to Level 3
├── README.md # Human documentation (optional, not loaded)
├── reference/ # ✅ REQUIRED: Level 3 detailed docs (loaded as-needed)
│ ├── api_spec.md # ✅ All detailed .md files go HERE
│ ├── examples.md # ✅ NOT in root directory!
│ └── advanced.md # ✅ Link from SKILL.md as ./reference/file.md
├── scripts/ # Level 3: Executable tools (loaded as-needed)
│ ├── main_tool.py
│ └── helper.py
└── shared/ # Level 3: Reusable components
├── __init__.py
├── config.py # Centralized config
├── api_client.py # API wrapper
└── formatters.py # Output formatting
```
**Key principles:**
1. SKILL.md is the table of contents. Details go in Level 3 files.
2. **ALL reference .md files MUST be in `/reference/` folder**
3. Link to them as `./reference/filename.md` from SKILL.md
---
## 🎨 Metadata Best Practices
### Good Metadata
```yaml
---
name: pdf-form-filler
description: Fill out PDF forms by extracting fields and inserting values
---
```
- Specific about function
- Contains keywords Claude might see
- Clear trigger conditions
### Poor Metadata
```yaml
---
name: pdf-skill
description: A skill for working with PDFs
---
```
- Too generic
- Vague purpose
- Unclear when to trigger
---
## 🛡️ Error Handling Pattern
```python
class AuthenticationError(Exception):
"""Raised when API authentication fails"""
pass
try:
client.authenticate()
except AuthenticationError:
print("❌ Authentication failed")
print("\nTroubleshooting:")
print("1. Verify API_KEY environment variable is set")
print("2. Check API endpoint is accessible")
print("3. Ensure network connectivity")
```
**Include:**
- Custom exception types
- Clear error messages with context
- Numbered troubleshooting steps
- Graceful degradation when possible
---
## 🔍 When to Use Each Pattern
### Use Progressive Disclosure When:
- Skill has optional advanced features
- Documentation is extensive
- Users have varying expertise levels
- See: [topics/progressive-disclosure.md](./topics/progressive-disclosure.md)
### Use Dynamic Manifests When:
- Capabilities change based on context
- Features depend on user permissions
- Tools should appear/disappear dynamically
- See: [topics/dynamic-manifests.md](./topics/dynamic-manifests.md)
### Use Deferred Loading When:
- Skill has heavy dependencies
- Not all features used every time
- Startup time matters
- See: [topics/deferred-loading.md](./topics/deferred-loading.md)
---
## ✅ Skill Structure Validation Checklist
**Run this checklist BEFORE considering a skill complete:**
- [ ] **Folder Structure**:
- [ ] `/reference/` folder exists
- [ ] ALL .md reference files are IN `/reference/` folder
- [ ] NO .md files in root (except SKILL.md and optional README.md)
- [ ] `/scripts/` folder exists (if scripts needed)
- [ ] `/shared/` folder exists (if shared utilities needed)
- [ ] **SKILL.md Structure**:
- [ ] Metadata section exists (~100 tokens)
- [ ] Body is <2k tokens (max 5k)
- [ ] Links to reference files use `./reference/filename.md` format
- [ ] No large content blocks embedded (moved to /reference/)
- [ ] **Progressive Disclosure**:
- [ ] Level 1 (metadata) is concise
- [ ] Level 2 (body) is a table of contents
- [ ] Level 3 (reference files) contains details
## 📊 Optimization Checklist
- [ ] **Token Efficiency**:
- Metadata ~100 tokens
- Body <2k tokens (max 5k)
- Detailed content in Level 3 files IN `/reference/` folder
- [ ] **Code Execution**: Deterministic tasks in scripts
- [ ] **Lazy Loading**: Heavy imports deferred (Level 3)
- [ ] **Caching**: Results cached when appropriate
- [ ] **Shared Utilities**: Common code extracted
- [ ] **Environment Config**: Credentials via env vars
- [ ] **Error Recovery**: Graceful failure handling
- [ ] **Progressive Disclosure**: SKILL.md links to details in `/reference/`, doesn't embed them
- [ ] **Folder Hierarchy**: All reference .md files in `/reference/` folder
---
## 🧪 Testing Workflow
```bash
# 1. Manual smoke test
cd skill-name/scripts
python main_tool.py --test-mode
# 2. Test with Claude
"Use the my-skill to process test data"
# 3. Verify checklist
✓ Works on first try?
✓ Error messages helpful?
✓ Claude understands how to use it?
✓ No credentials in code?
```
---
## 🛠️ Step-by-Step Process
**Building a new skill?** Follow the systematic process:
**[Skill Creation Process Guide](./reference/skill-creation-process.md)** - Complete walkthrough from planning to deployment
Includes:
- 5-phase process (Planning → Structure → Implementation → Testing → Refinement)
- Full working example: `incident-triage` skill
- Copy-paste templates for all components
- Token optimization at every step
- Adaptation checklist for your use case
---
## 📚 Additional Resources
- [Skill Creation Process](./reference/skill-creation-process.md) - Step-by-step guide with example
- [Anthropic: Equipping Agents with Skills](https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills)
- [Skills Documentation](https://docs.claude.com/en/docs/agents-and-tools/agent-skills/overview)
- [Skills Cookbook](https://github.com/anthropics/claude-cookbooks/tree/main/skills)
- [MCP Official Spec](https://spec.modelcontextprotocol.io/)
---
## 🗺️ Full Documentation
For comprehensive guides on architectural patterns, implementation details, and advanced techniques, see:
**[README.md](./README.md)** - Start here for the complete best practices guide
**Last Updated**: 2025-10-20

View File

@@ -0,0 +1,834 @@
# Skill Creation Process: Step-by-Step Guide
> **Use this guide** to systematically build a new Claude Code skill following progressive disclosure principles and token optimization.
**Example Used**: `incident-triage` skill (adapt for your use case)
---
## 📋 Process Overview
```
Phase 1: Planning → Phase 2: Structure → Phase 3: Implementation → Phase 4: Testing → Phase 5: Refinement
(30 min) (15 min) (2-4 hours) (30 min) (ongoing)
```
---
## Phase 1: Planning (30 minutes)
### Step 1.1: Define the Core Problem
**Questions to answer:**
- [ ] What specific, repeatable task does this solve?
- [ ] When should Claude invoke this skill?
- [ ] What are the inputs and outputs?
- [ ] What's the 1-sentence description?
**Example (incident-triage):**
- **Task**: Triage incidents by extracting facts, enriching with data, proposing severity/priority
- **Triggers**: "triage", "new incident", "assign severity", "prioritize ticket"
- **Inputs**: Free text or JSON ticket payload
- **Outputs**: Summary, severity/priority, next steps, assignment hint
- **Description**: "Triage incidents by extracting key facts, enriching with CMDB/log data, and proposing severity, priority, and next actions."
### Step 1.2: Identify the Three Levels
**Level 1: Metadata** (~100 tokens, always loaded)
- [ ] Skill name (kebab-case)
- [ ] Description (triggers Claude's router)
- [ ] Version
**Level 2: SKILL.md Body** (<2k tokens, loaded on trigger)
- [ ] When to Use (2-3 bullet points)
- [ ] What It Does (high-level flow)
- [ ] Inputs/Outputs (contract)
- [ ] Quick Start (1-3 commands)
- [ ] Links to Level 3 docs
**Level 3: Bundled Files** (unlimited, loaded as-needed)
- [ ] Detailed documentation
- [ ] Executable scripts
- [ ] API specs, examples, decision matrices
- [ ] Shared utilities
### Step 1.3: Token Budget Plan
Fill out this table:
| Component | Target Tokens | What Goes Here |
|-----------|--------------|----------------|
| Metadata | ~100 | Name, description, version |
| SKILL.md Body | <2k (aim for 1.5k) | Quick ref, links to Level 3 |
| reference/*.md | 500-1000 each | Detailed docs (as many files as needed) |
| scripts/*.py | n/a | Executable code (not loaded unless run) |
---
## Phase 2: Structure (15 minutes)
### Step 2.1: Create Folder Layout
**⚠️ CRITICAL: Create `/reference/` folder and put ALL reference .md files there!**
```bash
# Navigate to skills directory
cd .claude/skills
# Create skill structure
mkdir -p incident-triage/{scripts,reference,shared}
touch incident-triage/SKILL.md
touch incident-triage/scripts/{triage_main.py,enrich_ticket.py,suggest_priority.py,common.py}
touch incident-triage/reference/{inputs-and-prompts.md,decision-matrix.md,runbook-links.md,api-specs.md,examples.md}
touch incident-triage/shared/{config.py,api_client.py,formatters.py}
```
**Verify structure matches this EXACT pattern:**
```
incident-triage/
├── SKILL.md # ✅ Level 1+2 (≤2k tokens) - ONLY .md in root
├── reference/ # ✅ REQUIRED: Level 3 docs folder
│ ├── inputs-and-prompts.md # ✅ All reference .md files go HERE
│ ├── decision-matrix.md # ✅ NOT in root!
│ ├── runbook-links.md
│ ├── api-specs.md
│ └── examples.md
├── scripts/ # Level 3: executable code
│ ├── triage_main.py
│ ├── enrich_ticket.py
│ ├── suggest_priority.py
│ └── common.py
└── shared/ # Level 3: utilities
├── config.py
├── api_client.py
└── formatters.py
```
**❌ WRONG - DO NOT DO THIS:**
```
incident-triage/
├── SKILL.md
├── inputs-and-prompts.md # ❌ WRONG! Should be in reference/
├── decision-matrix.md # ❌ WRONG! Should be in reference/
└── scripts/
```
### Step 2.2: Stub Out Files
Create minimal stubs for each file to establish contracts:
**SKILL.md** (copy template from best-practices.md)
**reference/*.md** (headers only for now)
**scripts/*.py** (function signatures with pass)
**shared/*.py** (class/function signatures)
### Step 2.3: Validate Folder Structure
**Run this validation BEFORE moving to Phase 3:**
```bash
# Check structure
ls -la incident-triage/
# Verify:
# ✅ SKILL.md exists in root
# ✅ reference/ folder exists
# ✅ NO .md files in root except SKILL.md
# ✅ scripts/ folder exists (if needed)
# ✅ shared/ folder exists (if needed)
# Check reference folder
ls -la incident-triage/reference/
# Verify:
# ✅ All .md reference files are HERE
# ✅ inputs-and-prompts.md
# ✅ decision-matrix.md
# ✅ api-specs.md
# ✅ examples.md
```
**Checklist:**
- [ ] `/reference/` folder created
- [ ] All reference .md files in `/reference/` (not root)
- [ ] SKILL.md links use `./reference/filename.md` format
- [ ] No .md files in root except SKILL.md
---
## Phase 3: Implementation (2-4 hours)
Work in this order to maintain focus and avoid scope creep:
### Step 3.1: Write Level 1 (Metadata) - 5 minutes
Open `SKILL.md` and write the frontmatter:
```yaml
---
name: incident-triage
description: Triage incidents by extracting key facts, enriching with CMDB/log data, and proposing severity, priority, and next actions.
version: 1.0.0
---
```
**Checklist:**
- [ ] Name is clear and specific (not "helper" or "utility")
- [ ] Description contains trigger keywords
- [ ] Description explains what it does (not what it is)
- [ ] Total metadata ≤100 tokens
### Step 3.2: Write Level 2 (SKILL.md Body) - 30 minutes
Follow this exact structure:
```markdown
# Level 2: Body (<2k tokens recommended) — Loaded when the skill triggers
## When to Use
- [Trigger condition 1]
- [Trigger condition 2]
- [Trigger condition 3]
## What It Does (at a glance)
- **[Action 1]**: [brief description]
- **[Action 2]**: [brief description]
- **[Action 3]**: [brief description]
- **[Action 4]**: [brief description]
## Inputs
- [Input format 1]
- [Input format 2]
Details: see [reference/inputs-and-prompts.md](./reference/inputs-and-prompts.md).
## Quick Start
1. **Dry-run** (no external calls):
```bash
python scripts/main.py --example --dry-run
```
2. **With enrichment**:
```bash
python scripts/main.py --ticket-id 12345 --include-logs
```
3. Review output
Examples: [reference/examples.md](./reference/examples.md)
## Decision Logic (high-level)
[2-3 sentences on how decisions are made]
Full details: [reference/decision-matrix.md](./reference/decision-matrix.md)
## Outputs (contract)
- `field1`: [description]
- `field2`: [description]
- `field3`: [description]
## Guardrails
- [Security consideration 1]
- [Token budget note]
- [Error handling approach]
## Links (Level 3, loaded only when needed)
- Prompts: [reference/inputs-and-prompts.md](./reference/inputs-and-prompts.md)
- Decision logic: [reference/decision-matrix.md](./reference/decision-matrix.md)
- Examples: [reference/examples.md](./reference/examples.md)
- API specs: [reference/api-specs.md](./reference/api-specs.md)
## Triggers (help the router)
Keywords: [keyword1], [keyword2], [keyword3]
Inputs containing: [field1], [field2]
## Security & Config
Set environment variables:
- `VAR1_API_KEY`
- `VAR2_API_KEY`
Centralized in `shared/config.py`. Never echo secrets.
## Testing
```bash
# Smoke test
python scripts/main.py --fixture reference/examples.md
# End-to-end
python scripts/main.py --text "Example input" --dry-run
```
```
**Checklist:**
- [ ] <2k tokens (aim for 1.5k)
- [ ] Links to Level 3 for details
- [ ] Quick Start is copy-paste ready
- [ ] Output contract is clear
- [ ] No extensive examples or specs embedded
### Step 3.3: Write Level 3 Reference Docs - 45 minutes
Create each reference file systematically:
#### reference/inputs-and-prompts.md
```markdown
# Inputs and Prompt Shapes
## Input Format 1: Free Text
- Description
- Example
## Input Format 2: Structured JSON
```json
{
"field": "value"
}
```
## Prompt Snippets
- Extraction goals
- Summarization style
- Redaction rules
```
#### reference/decision-matrix.md
```markdown
# Decision Matrix
[Full decision logic with tables, formulas, edge cases]
## Base Matrix
| Dimension 1 \ Dimension 2 | Value A | Value B | Value C |
|---|---|---|---|
| Low | Result | Result | Result |
| Med | Result | Result | Result |
| High | Result | Result | Result |
## Adjustments
- Adjustment rule 1
- Adjustment rule 2
## Rationale
[Why this matrix, examples, edge cases]
```
#### reference/api-specs.md
```markdown
# API Specs & Schemas
## API 1: CMDB
- Base URL: `{SERVICE_MAP_URL}`
- Auth: Header `X-API-Key: {CMDB_API_KEY}`
- Endpoints:
- GET `/service/{name}/dependencies`
- Response schema: [...]
## API 2: Logs
- Base URL: [...]
- Endpoints: [...]
```
#### reference/examples.md
```markdown
# Examples
## Example 1: [Scenario Name]
**Input:**
```
[Example input]
```
**Output:**
```
[Example output with all fields]
```
**Explanation:** [Why these decisions were made]
## Example 2: [Another Scenario]
[...]
```
#### reference/runbook-links.md
```markdown
# Runbook Links
- [Service 1]: <URL>
- [Service 2]: <URL>
- [Escalation tree]: <URL>
```
**Checklist for all reference docs:**
- [ ] Each file focuses on one aspect
- [ ] 500-1000 tokens per file (can be more if needed)
- [ ] Referenced from SKILL.md but not embedded
- [ ] Includes examples where helpful
### Step 3.4: Write Shared Utilities - 30 minutes
#### shared/config.py
```python
"""Centralized configuration from environment variables."""
import os
class Config:
"""Config object - never logs secrets"""
CMDB_API_KEY = os.getenv("CMDB_API_KEY")
LOGS_API_KEY = os.getenv("LOGS_API_KEY")
SERVICE_MAP_URL = os.getenv("SERVICE_MAP_URL")
DASHBOARD_BASE_URL = os.getenv("DASHBOARD_BASE_URL")
@classmethod
def validate(cls):
"""Check required env vars are set"""
missing = []
for key in ["CMDB_API_KEY", "LOGS_API_KEY"]:
if not getattr(cls, key):
missing.append(key)
if missing:
raise ValueError(f"Missing required env vars: {missing}")
cfg = Config()
```
#### shared/api_client.py
```python
"""API client wrappers."""
import requests
from .config import cfg
class CMDBClient:
def __init__(self):
self.base_url = cfg.SERVICE_MAP_URL
self.headers = {"X-API-Key": cfg.CMDB_API_KEY}
def get_service_dependencies(self, service_name):
"""Fetch service dependencies"""
try:
resp = requests.get(
f"{self.base_url}/service/{service_name}/dependencies",
headers=self.headers,
timeout=5
)
resp.raise_for_status()
return resp.json()
except requests.RequestException as e:
raise ConnectionError(f"CMDB API failed: {e}")
class LogsClient:
def __init__(self):
self.base_url = cfg.LOGS_API_URL
self.headers = {"Authorization": f"Bearer {cfg.LOGS_API_KEY}"}
def recent_errors(self, service_name, last_minutes=15):
"""Fetch recent error logs"""
# Implementation
pass
def cmdb_client():
return CMDBClient()
def logs_client():
return LogsClient()
```
#### shared/formatters.py
```python
"""Output formatting helpers."""
def format_output(enriched, severity, priority, rationale, next_steps):
"""Format triage result as markdown."""
lines = [
"### Incident Triage Result",
f"**Severity**: {severity} | **Priority**: {priority}",
f"**Rationale**: {rationale}",
"",
"**Summary**:",
enriched.get("summary", "N/A"),
"",
"**Next Steps**:",
]
for i, step in enumerate(next_steps, 1):
lines.append(f"{i}. {step}")
if "evidence" in enriched:
lines.extend(["", "**Evidence**:"])
for link in enriched["evidence"]:
lines.append(f"- {link}")
return "\n".join(lines)
```
### Step 3.5: Write Main Scripts - 1 hour
#### scripts/triage_main.py (entry point)
```python
#!/usr/bin/env python3
"""Main entry point for incident triage."""
import argparse
import json
import sys
from pathlib import Path
# Add parent to path for imports
sys.path.insert(0, str(Path(__file__).parent.parent))
from shared.config import cfg
from shared.formatters import format_output
from scripts.enrich_ticket import enrich
from scripts.suggest_priority import score
def main():
parser = argparse.ArgumentParser(description="Triage an incident")
parser.add_argument("--text", help="Free-text incident description")
parser.add_argument("--ticket-id", help="Ticket ID to enrich")
parser.add_argument("--include-logs", action="store_true")
parser.add_argument("--include-cmdb", action="store_true")
parser.add_argument("--dry-run", action="store_true",
help="Skip external API calls")
args = parser.parse_args()
# Validate inputs
if not args.text and not args.ticket_id:
print("Error: Provide --text or --ticket-id")
sys.exit(1)
# Build payload
payload = {
"text": args.text,
"ticket_id": args.ticket_id
}
try:
# Enrich (respects --dry-run)
enriched = enrich(
payload,
include_logs=args.include_logs and not args.dry_run,
include_cmdb=args.include_cmdb and not args.dry_run
)
# Score (deterministic)
severity, priority, rationale = score(enriched)
# Generate next steps
next_steps = generate_next_steps(enriched, severity)
# Format output
output = format_output(enriched, severity, priority, rationale, next_steps)
print(output)
except Exception as e:
print(f"❌ Triage failed: {e}")
print("\nTroubleshooting:")
print("1. Check environment variables are set")
print("2. Verify API endpoints are accessible")
print("3. Run with --dry-run to test without external calls")
sys.exit(1)
def generate_next_steps(enriched, severity):
"""Generate action items based on enrichment and severity"""
steps = []
if severity in ["SEV1", "SEV2"]:
steps.append("Page on-call immediately")
if "dashboard_url" in enriched:
steps.append(f"Review dashboard: {enriched['dashboard_url']}")
steps.append("Compare last 15m vs 24h baseline")
if enriched.get("recent_deploy"):
steps.append("Consider rollback if error budget breached")
return steps
if __name__ == "__main__":
main()
```
#### scripts/enrich_ticket.py
```python
"""Enrich ticket with external data."""
from shared.config import cfg
from shared.api_client import cmdb_client, logs_client
def enrich(payload, include_logs=False, include_cmdb=False):
"""
Enrich ticket payload with CMDB/logs data.
Args:
payload: Dict with 'text' and/or 'ticket_id'
include_logs: Fetch recent logs
include_cmdb: Fetch CMDB dependencies
Returns:
Dict with original payload + enrichment
"""
result = {"input": payload}
# Extract service name from text or ticket
service = extract_service(payload)
if service:
result["service"] = service
# Enrich with CMDB
if include_cmdb and service:
try:
cmdb_data = cmdb_client().get_service_dependencies(service)
result["cmdb"] = cmdb_data
result["blast_radius"] = cmdb_data.get("dependent_services", [])
except Exception as e:
result["cmdb_error"] = str(e)
# Enrich with logs
if include_logs and service:
try:
logs = logs_client().recent_errors(service)
result["logs"] = logs
except Exception as e:
result["logs_error"] = str(e)
# Derive scope/impact hints
result["scope"] = derive_scope(result)
result["impact"] = derive_impact(result)
return result
def extract_service(payload):
"""Extract service name from payload."""
# Check explicit service field
if "service" in payload:
return payload["service"]
# Parse from text (simple keyword matching)
text = payload.get("text", "").lower()
known_services = ["checkout", "payments", "inventory", "auth"]
for service in known_services:
if service in text:
return service
return None
def derive_scope(enriched):
"""Determine blast radius scope."""
blast_radius = len(enriched.get("blast_radius", []))
if blast_radius == 0:
return "single-service"
elif blast_radius < 3:
return "few-services"
else:
return "multi-service"
def derive_impact(enriched):
"""Estimate user impact level."""
# Check for explicit impact data
if "impact" in enriched.get("input", {}):
pct = enriched["input"]["impact"].get("users_affected_pct", 0)
if pct > 50:
return "high"
elif pct > 10:
return "medium"
else:
return "low"
# Infer from service criticality
service = enriched.get("service", "")
critical_services = ["checkout", "payments", "auth"]
if service in critical_services:
return "medium" # Default to medium for critical services
return "low"
```
#### scripts/suggest_priority.py
```python
"""Deterministic severity/priority scoring."""
DECISION_MATRIX = {
# (impact, scope) -> (severity, priority)
("low", "single-service"): ("SEV4", "P4"),
("low", "few-services"): ("SEV3", "P3"),
("low", "multi-service"): ("SEV3", "P3"),
("medium", "single-service"): ("SEV3", "P3"),
("medium", "few-services"): ("SEV2", "P2"),
("medium", "multi-service"): ("SEV2", "P2"),
("high", "single-service"): ("SEV2", "P2"),
("high", "few-services"): ("SEV1", "P1"),
("high", "multi-service"): ("SEV1", "P1"),
}
def score(enriched):
"""
Score incident severity and priority.
Args:
enriched: Dict from enrich_ticket()
Returns:
Tuple of (severity, priority, rationale)
"""
impact = enriched.get("impact", "medium")
scope = enriched.get("scope", "single-service")
# Base score from matrix
key = (impact, scope)
if key not in DECISION_MATRIX:
# Default fallback
severity, priority = "SEV3", "P3"
rationale = f"Default scoring (impact={impact}, scope={scope})"
else:
severity, priority = DECISION_MATRIX[key]
rationale = f"{impact.title()} impact, {scope} scope"
# Apply adjustments
if should_escalate(enriched):
severity, priority = escalate(severity, priority)
rationale += " (escalated: long recovery expected)"
return severity, priority, rationale
def should_escalate(enriched):
"""Check if incident should be escalated."""
# Check for long recovery indicators
logs = enriched.get("logs", {})
if logs.get("error_rate_increasing"):
return True
# Check for repeated incidents
if enriched.get("recent_incidents_count", 0) > 3:
return True
return False
def escalate(severity, priority):
"""Escalate severity/priority by one level."""
sev_map = {"SEV4": "SEV3", "SEV3": "SEV2", "SEV2": "SEV1", "SEV1": "SEV1"}
pri_map = {"P4": "P3", "P3": "P2", "P2": "P1", "P1": "P1"}
return sev_map.get(severity, severity), pri_map.get(priority, priority)
```
---
## Phase 4: Testing (30 minutes)
### Step 4.1: Create Test Fixtures
Create `reference/test-fixtures.json`:
```json
{
"test1": {
"text": "Checkout API seeing 500 errors at 12%; started 15:05Z",
"expected_severity": "SEV2",
"expected_priority": "P2"
},
"test2": {
"text": "Single user reports login issue on mobile app",
"expected_severity": "SEV4",
"expected_priority": "P4"
}
}
```
### Step 4.2: Run Tests
```bash
# 1. Smoke test deterministic components
python scripts/suggest_priority.py --test
# 2. Dry-run end-to-end
python scripts/triage_main.py --text "API timeouts on checkout" --dry-run
# 3. With enrichment (requires env vars)
export CMDB_API_KEY="test_key"
export LOGS_API_KEY="test_key"
python scripts/triage_main.py --ticket-id 12345 --include-logs --include-cmdb
```
### Step 4.3: Test with Claude
Ask Claude:
```
"I have a new incident: checkout API showing 500 errors affecting 15% of users in EU region. Can you triage this?"
```
Verify:
- [ ] Skill triggers correctly
- [ ] Output is well-formatted
- [ ] Severity/priority makes sense
- [ ] Next steps are actionable
- [ ] Links work
---
## Phase 5: Refinement (Ongoing)
### Step 5.1: Token Count Audit
```bash
# Count tokens in SKILL.md body (exclude metadata)
wc -w incident-triage/SKILL.md
# Multiply by 0.75 for rough token count
```
**Checklist:**
- [ ] Metadata ~100 tokens
- [ ] Body <2k tokens
- [ ] If over, move content to reference/*.md
### Step 5.2: Real-World Usage Monitoring
Track these metrics:
- [ ] Does Claude trigger the skill appropriately?
- [ ] Are users getting helpful results?
- [ ] What questions/errors come up?
- [ ] Which Level 3 docs are never used?
### Step 5.3: Iterate Based on Feedback
**If skill triggers too often:**
→ Make description more specific
**If skill triggers too rarely:**
→ Add more trigger keywords
**If output is unhelpful:**
→ Improve decision logic or examples
**If token limit exceeded:**
→ Move more content to Level 3
---
## 🎓 Adaptation Checklist
To create YOUR skill from this template:
- [ ] **Folder Structure** (CRITICAL):
- [ ] Create `/reference/` folder
- [ ] Put ALL reference .md files IN `/reference/` folder
- [ ] NO .md files in root except SKILL.md
- [ ] Links in SKILL.md use `./reference/filename.md` format
- [ ] **Rename**: Replace "incident-triage" with your skill name
- [ ] **Metadata**: Write name/description with your trigger keywords
- [ ] **Triggers**: List all keywords/patterns that should invoke your skill
- [ ] **Inputs/Outputs**: Define your specific contract
- [ ] **Scripts**: Replace enrichment/scoring with your logic
- [ ] **Reference docs**: Create docs for your domain (decision matrices, API specs, etc.)
- [ ] **Config**: Add your required environment variables
- [ ] **Examples**: Create 3-5 realistic examples
- [ ] **Test**: Dry-run → with real data → with Claude
- [ ] **Validate Structure**: Run structure validation checklist
- [ ] **Refine**: Monitor usage, iterate based on feedback
---
## 📚 Related Resources
- [Agent Skills Best Practices](../best-practices.md) - Quick reference
- [Progressive Disclosure](../topics/progressive-disclosure.md) - Design philosophy
- [Token Optimization](../README.md#token-optimized-structure) - Token limits explained
---
**Last Updated**: 2025-10-20
**Version**: 1.0.0

View File

@@ -0,0 +1,147 @@
# Claude Code Skill Validator
A comprehensive validation tool for Claude Code skills that checks structure, format, and best practices compliance.
## Installation
```bash
cd scripts
npm install
```
## Usage
### Validate a skill directory
```bash
node validate-skill.js /path/to/skill-directory
# Examples:
node validate-skill.js ~/.claude/skills/my-skill
node validate-skill.js .claude/skills/my-skill
node validate-skill.js . # validate current directory
```
### Help
```bash
node validate-skill.js --help
```
## What It Validates
### ✓ YAML Frontmatter
- Opening `---` on line 1
- Closing `---` before content
- Valid YAML syntax
- No tabs, proper indentation
### ✓ Required Fields
- `name`: Skill name
- `description`: What the skill does and when to use it
### ✓ Optional Fields
- `allowed-tools`: Tool restrictions (if present)
- `version`: Skill version
### ✓ Description Quality
- Specificity (not too vague)
- Usage triggers ("when to use")
- Length (20-150 tokens recommended)
### ✓ Token Budgets
- Metadata: ~100 tokens (warning at 150+)
- Body: <2k tokens recommended (error at 5k+)
- Provides estimates for all sections
### ✓ File Structure
- Only `SKILL.md` in root directory
- No stray `.md` files in root
- `/reference/` folder for detailed docs
- `/scripts/` folder detection
### ✓ Path Format
- All paths use forward slashes (cross-platform)
- No Windows-style backslashes
### ✓ Reference Links
- All `./reference/*.md` links are valid
- Referenced files actually exist
## Exit Codes
- `0`: Validation passed (may have warnings)
- `1`: Validation failed with errors
## Output
The validator provides:
- **✓ Success messages** (green): Validation passed
- **⚠ Warnings** (yellow): Best practice recommendations
- **✗ Errors** (red): Must be fixed before deployment
- ** Info** (cyan): Informational messages
## Example Output
```
╔════════════════════════════════════════════╗
║ Claude Code Skill Validator v1.0.0 ║
╚════════════════════════════════════════════╝
Validating skill at: /path/to/skill
[1/8] Validating directory...
✓ Skill directory exists
[2/8] Checking for SKILL.md...
✓ SKILL.md exists
[3/8] Validating YAML frontmatter...
✓ Valid YAML frontmatter delimiters found
✓ YAML syntax is valid
...
═══════════════════════════════════════════
VALIDATION REPORT
═══════════════════════════════════════════
✓ All validations passed! Skill structure is excellent.
```
## Integration with Skill Development
This validator is designed to work with the skill-builder skill's development process:
1. **During development**: Run validation frequently to catch issues early
2. **Before testing**: Ensure structure is correct
3. **Before deployment**: Final validation check
4. **CI/CD integration**: Use exit codes for automated checks
## Common Issues and Fixes
### Error: "Markdown files found in root"
**Fix**: Move all `.md` files (except `SKILL.md`) to `/reference/` folder
### Warning: "Description may be too vague"
**Fix**: Add specific triggers like "Use when..." or "For [specific use case]"
### Warning: "Body is over 2k tokens"
**Fix**: Move detailed content to files in `/reference/` folder
### Error: "Referenced file does not exist"
**Fix**: Ensure all linked files exist at the specified path
## Based on Official Documentation
This validator implements all requirements from:
- https://anthropic.mintlify.app/en/docs/claude-code/skills
## Version
v1.0.0 - Initial release
## License
MIT

View File

@@ -0,0 +1,23 @@
{
"name": "claude-skill-validator",
"version": "1.0.0",
"description": "Validation tool for Claude Code skills",
"main": "validate-skill.js",
"scripts": {
"validate": "node validate-skill.js"
},
"bin": {
"validate-skill": "./validate-skill.js"
},
"dependencies": {
"yaml": "^2.3.4"
},
"keywords": [
"claude",
"claude-code",
"skill",
"validator"
],
"author": "",
"license": "MIT"
}

View File

@@ -0,0 +1,451 @@
#!/usr/bin/env node
/**
* Claude Code Skill Validator
*
* Validates skill structure according to Claude Code documentation:
* - YAML frontmatter format and required fields
* - File structure and /reference/ folder requirements
* - Token budget estimates for metadata and body
* - Path format validation (forward slashes)
* - Description specificity checks
*
* Usage: node validate-skill.js <path-to-skill-directory>
*/
const fs = require('fs');
const path = require('path');
const yaml = require('yaml');
// ANSI color codes for terminal output
const colors = {
reset: '\x1b[0m',
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
cyan: '\x1b[36m',
};
class SkillValidator {
constructor(skillPath) {
this.skillPath = path.resolve(skillPath);
this.errors = [];
this.warnings = [];
this.info = [];
this.skillMdPath = path.join(this.skillPath, 'SKILL.md');
}
// Color helper methods
error(msg) {
this.errors.push(msg);
console.error(`${colors.red}✗ ERROR: ${msg}${colors.reset}`);
}
warn(msg) {
this.warnings.push(msg);
console.warn(`${colors.yellow}⚠ WARNING: ${msg}${colors.reset}`);
}
success(msg) {
console.log(`${colors.green}${msg}${colors.reset}`);
}
log(msg) {
this.info.push(msg);
console.log(`${colors.cyan} ${msg}${colors.reset}`);
}
// Token estimation (rough approximation: ~4 chars per token)
estimateTokens(text) {
return Math.ceil(text.length / 4);
}
// Validate skill directory exists
validateDirectory() {
if (!fs.existsSync(this.skillPath)) {
this.error(`Skill directory not found: ${this.skillPath}`);
return false;
}
if (!fs.statSync(this.skillPath).isDirectory()) {
this.error(`Path is not a directory: ${this.skillPath}`);
return false;
}
this.success('Skill directory exists');
return true;
}
// Validate SKILL.md exists
validateSkillMdExists() {
if (!fs.existsSync(this.skillMdPath)) {
this.error('SKILL.md not found in skill directory');
return false;
}
this.success('SKILL.md exists');
return true;
}
// Parse and validate YAML frontmatter
validateFrontmatter(content) {
// Check for frontmatter delimiters
if (!content.startsWith('---\n') && !content.startsWith('---\r\n')) {
this.error('SKILL.md must start with "---" on line 1');
return null;
}
const lines = content.split('\n');
let closingIndex = -1;
for (let i = 1; i < lines.length; i++) {
if (lines[i].trim() === '---') {
closingIndex = i;
break;
}
}
if (closingIndex === -1) {
this.error('SKILL.md frontmatter missing closing "---"');
return null;
}
this.success('Valid YAML frontmatter delimiters found');
// Extract and parse YAML
const yamlContent = lines.slice(1, closingIndex).join('\n');
let metadata;
try {
metadata = yaml.parse(yamlContent);
} catch (e) {
this.error(`Invalid YAML syntax: ${e.message}`);
return null;
}
this.success('YAML syntax is valid');
return { metadata, bodyStartLine: closingIndex + 1 };
}
// Validate required frontmatter fields
validateRequiredFields(metadata) {
const required = ['name', 'description'];
let allPresent = true;
required.forEach(field => {
if (!metadata[field]) {
this.error(`Required frontmatter field missing: "${field}"`);
allPresent = false;
} else {
this.success(`Required field present: ${field}`);
}
});
return allPresent;
}
// Validate description specificity
validateDescription(description) {
if (!description) return false;
const tokens = this.estimateTokens(description);
this.log(`Description length: ${description.length} chars (~${tokens} tokens)`);
// Check for vague terms
const vagueTerms = ['helps', 'assists', 'provides', 'enables', 'allows'];
const foundVague = vagueTerms.filter(term =>
description.toLowerCase().includes(term)
);
if (foundVague.length > 2) {
this.warn(`Description may be too vague. Contains: ${foundVague.join(', ')}`);
this.warn('Consider adding specific triggers or use cases');
}
// Check for "when to use" indicators
const hasWhenIndicators = /when|use when|trigger|for \w+ing/i.test(description);
if (!hasWhenIndicators) {
this.warn('Description should include "when to use" indicators');
} else {
this.success('Description includes usage triggers');
}
// Check length
if (tokens > 150) {
this.warn(`Description is long (~${tokens} tokens). Consider keeping under 150 tokens`);
} else if (tokens < 20) {
this.warn(`Description is short (~${tokens} tokens). Add more specificity about when to use`);
} else {
this.success(`Description length is good (~${tokens} tokens)`);
}
return true;
}
// Validate allowed-tools field if present
validateAllowedTools(metadata) {
if (!metadata['allowed-tools']) {
this.log('No allowed-tools restriction (skill has access to all tools)');
return true;
}
const allowedTools = metadata['allowed-tools'];
if (typeof allowedTools === 'string') {
const tools = allowedTools.split(',').map(t => t.trim());
this.success(`Tool restrictions defined: ${tools.join(', ')}`);
} else {
this.warn('allowed-tools should be a comma-separated string');
}
return true;
}
// Validate markdown body
validateBody(content, bodyStartLine) {
const lines = content.split('\n');
const bodyContent = lines.slice(bodyStartLine).join('\n').trim();
if (!bodyContent) {
this.error('SKILL.md body is empty');
return false;
}
const tokens = this.estimateTokens(bodyContent);
this.log(`Body length: ${bodyContent.length} chars (~${tokens} tokens)`);
if (tokens > 5000) {
this.error(`Body exceeds 5k tokens (~${tokens} tokens). Move content to /reference/ files`);
} else if (tokens > 2000) {
this.warn(`Body is over 2k tokens (~${tokens} tokens). Consider moving details to /reference/`);
} else {
this.success(`Body token count is optimal (~${tokens} tokens, under 2k recommended)`);
}
return true;
}
// Validate file structure
validateFileStructure() {
const files = fs.readdirSync(this.skillPath);
// Check for markdown files in root (only SKILL.md allowed)
const rootMdFiles = files.filter(f =>
f.endsWith('.md') && f !== 'SKILL.md'
);
if (rootMdFiles.length > 0) {
this.error(`Markdown files found in root (should be in /reference/): ${rootMdFiles.join(', ')}`);
this.error('Move these files to /reference/ folder');
} else {
this.success('No stray markdown files in root directory');
}
// Check for /reference/ folder
const referencePath = path.join(this.skillPath, 'reference');
if (fs.existsSync(referencePath) && fs.statSync(referencePath).isDirectory()) {
const referenceFiles = fs.readdirSync(referencePath);
const mdFiles = referenceFiles.filter(f => f.endsWith('.md'));
if (mdFiles.length > 0) {
this.success(`/reference/ folder exists with ${mdFiles.length} file(s): ${mdFiles.join(', ')}`);
} else {
this.warn('/reference/ folder exists but contains no markdown files');
}
} else {
this.warn('/reference/ folder not found (optional, but recommended for detailed docs)');
}
// Check for scripts folder
const scriptsPath = path.join(this.skillPath, 'scripts');
if (fs.existsSync(scriptsPath)) {
const scriptFiles = fs.readdirSync(scriptsPath);
this.log(`/scripts/ folder exists with ${scriptFiles.length} file(s)`);
}
return rootMdFiles.length === 0;
}
// Validate paths in content (should use forward slashes)
validatePaths(content) {
const backslashPaths = content.match(/\]\([^)]*\\/g);
if (backslashPaths && backslashPaths.length > 0) {
this.warn('Found paths with backslashes. Use forward slashes for cross-platform compatibility');
backslashPaths.forEach(match => {
this.warn(` Found: ${match}`);
});
} else {
this.success('All paths use forward slashes');
}
// Check for reference links
const referenceLinks = content.match(/\[.*?\]\(\.\/reference\/.*?\.md\)/g);
if (referenceLinks && referenceLinks.length > 0) {
this.success(`Found ${referenceLinks.length} links to /reference/ files`);
// Validate that referenced files exist
referenceLinks.forEach(link => {
const match = link.match(/\(\.\/reference\/(.*?\.md)\)/);
if (match) {
const filename = match[1];
const filepath = path.join(this.skillPath, 'reference', filename);
if (!fs.existsSync(filepath)) {
this.error(`Referenced file does not exist: ./reference/${filename}`);
} else {
this.success(` ✓ ./reference/${filename} exists`);
}
}
});
}
return true;
}
// Validate metadata token budget (~100 tokens)
validateMetadataTokens(metadata) {
const metadataStr = yaml.stringify(metadata);
const tokens = this.estimateTokens(metadataStr);
this.log(`Metadata token estimate: ~${tokens} tokens`);
if (tokens > 150) {
this.warn(`Metadata is large (~${tokens} tokens). Aim for ~100 tokens`);
} else if (tokens > 100) {
this.log('Metadata is slightly over 100 tokens but acceptable');
} else {
this.success(`Metadata token budget is optimal (~${tokens} tokens)`);
}
return true;
}
// Run all validations
async validate() {
console.log(`\n${colors.blue}╔════════════════════════════════════════════╗${colors.reset}`);
console.log(`${colors.blue}║ Claude Code Skill Validator v1.0.0 ║${colors.reset}`);
console.log(`${colors.blue}╚════════════════════════════════════════════╝${colors.reset}\n`);
console.log(`Validating skill at: ${colors.cyan}${this.skillPath}${colors.reset}\n`);
// Step 1: Directory validation
console.log(`${colors.blue}[1/8]${colors.reset} Validating directory...`);
if (!this.validateDirectory()) return this.generateReport();
// Step 2: SKILL.md existence
console.log(`\n${colors.blue}[2/8]${colors.reset} Checking for SKILL.md...`);
if (!this.validateSkillMdExists()) return this.generateReport();
// Read SKILL.md
const content = fs.readFileSync(this.skillMdPath, 'utf-8');
// Step 3: Frontmatter parsing
console.log(`\n${colors.blue}[3/8]${colors.reset} Validating YAML frontmatter...`);
const parsed = this.validateFrontmatter(content);
if (!parsed) return this.generateReport();
const { metadata, bodyStartLine } = parsed;
// Step 4: Required fields
console.log(`\n${colors.blue}[4/8]${colors.reset} Checking required fields...`);
this.validateRequiredFields(metadata);
// Step 5: Description quality
console.log(`\n${colors.blue}[5/8]${colors.reset} Validating description...`);
this.validateDescription(metadata.description);
// Step 6: Optional fields
console.log(`\n${colors.blue}[6/8]${colors.reset} Checking optional fields...`);
this.validateAllowedTools(metadata);
this.validateMetadataTokens(metadata);
// Step 7: Body validation
console.log(`\n${colors.blue}[7/8]${colors.reset} Validating body content...`);
this.validateBody(content, bodyStartLine);
// Step 8: File structure
console.log(`\n${colors.blue}[8/8]${colors.reset} Validating file structure...`);
this.validateFileStructure();
this.validatePaths(content);
return this.generateReport();
}
// Generate final report
generateReport() {
console.log(`\n${colors.blue}═══════════════════════════════════════════${colors.reset}`);
console.log(`${colors.blue}VALIDATION REPORT${colors.reset}`);
console.log(`${colors.blue}═══════════════════════════════════════════${colors.reset}\n`);
if (this.errors.length === 0 && this.warnings.length === 0) {
console.log(`${colors.green}✓ All validations passed! Skill structure is excellent.${colors.reset}\n`);
return 0;
}
if (this.errors.length > 0) {
console.log(`${colors.red}Errors: ${this.errors.length}${colors.reset}`);
this.errors.forEach((err, i) => {
console.log(` ${i + 1}. ${err}`);
});
console.log('');
}
if (this.warnings.length > 0) {
console.log(`${colors.yellow}Warnings: ${this.warnings.length}${colors.reset}`);
this.warnings.forEach((warn, i) => {
console.log(` ${i + 1}. ${warn}`);
});
console.log('');
}
if (this.errors.length > 0) {
console.log(`${colors.red}✗ Validation failed. Please fix errors before deploying.${colors.reset}\n`);
return 1;
} else {
console.log(`${colors.yellow}⚠ Validation passed with warnings. Consider addressing warnings for best practices.${colors.reset}\n`);
return 0;
}
}
}
// CLI entry point
async function main() {
const args = process.argv.slice(2);
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
console.log(`
${colors.cyan}Claude Code Skill Validator${colors.reset}
${colors.blue}Usage:${colors.reset}
node validate-skill.js <path-to-skill-directory>
${colors.blue}Examples:${colors.reset}
node validate-skill.js ./my-skill
node validate-skill.js ~/.claude/skills/my-skill
node validate-skill.js . (validate current directory)
${colors.blue}What it validates:${colors.reset}
✓ YAML frontmatter format (opening/closing ---)
✓ Required fields: name, description
✓ Description specificity and triggers
✓ Token budgets (metadata ~100, body <2k recommended)
✓ File structure (/reference/ folder for docs)
✓ No stray .md files in root (except SKILL.md)
✓ Path format (forward slashes)
✓ Referenced files exist
`);
process.exit(0);
}
const skillPath = args[0];
const validator = new SkillValidator(skillPath);
const exitCode = await validator.validate();
process.exit(exitCode);
}
// Run if called directly
if (require.main === module) {
main().catch(err => {
console.error(`${colors.red}Fatal error: ${err.message}${colors.reset}`);
process.exit(1);
});
}
module.exports = { SkillValidator };