# 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]: - [Service 2]: - [Escalation tree]: ``` **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