Skip to content

Developing Custom Skills

What You'll Learn

After completing this lesson, you will be able to:

  • ✅ Create compliant skill directory structure
  • ✅ Write complete SKILL.md definition files (including Front Matter, How It Works, Usage, and other sections)
  • ✅ Write compliant Bash scripts (error handling, output format, cleanup mechanisms)
  • ✅ Package skills as Zip files for publishing
  • ✅ Optimize context efficiency for more precise skill activation by Claude

Your Current Challenges

You may have encountered these scenarios:

  • ✗ Frequently repeating complex operations (e.g., deploying to specific platforms, analyzing log formats), needing to explain to Claude every time
  • ✗ Claude not knowing when to activate certain features, causing you to input the same instructions repeatedly
  • ✗ Wanting to encapsulate team best practices into reusable tools but not knowing where to start
  • ✗ Skill files you wrote often being ignored by Claude, unsure what went wrong

When to Use This Approach

When you need to:

  • 📦 Encapsulate Repetitive Operations: Package frequently used command sequences for one-click execution
  • 🎯 Precise Triggering: Have Claude proactively activate skills in specific scenarios
  • 🏢 Standardized Workflows: Automate team standards (e.g., code checks, deployment workflows)
  • 🚀 Extend Capabilities: Add features to Claude that it doesn't natively support

🎒 Before You Start

Before starting, please confirm:

Prerequisites

  • Completed Agent Skills Getting Started
  • Installed Agent Skills and familiar with basic usage
  • Understood basic command-line operations (Bash scripts)
  • Have a GitHub repository (for publishing skills)

Core Concepts

How Agent Skills Works:

Claude only loads a skill's name and description at startup. When it detects related keywords, it reads the complete SKILL.md content. This on-demand loading mechanism minimizes Token consumption to the greatest extent.

Three Core Elements of Skill Development:

  1. Directory Structure: Folder layout conforming to naming conventions
  2. SKILL.md: Skill definition file that tells Claude when to activate and how to use
  3. Scripts: Bash code that actually executes, responsible for specific operations

[Image: Skill Activation Flow]


Follow Along: Create Your First Skill

Step 1: Create Directory Structure

Why Correct directory structure is prerequisite for Claude to recognize the skill.

Create a new skill under the skills/ directory:

bash
cd skills
mkdir my-custom-skill
cd my-custom-skill
mkdir scripts

Directory structure should be:

skills/
  my-custom-skill/
    SKILL.md           # Skill definition file (required)
    scripts/
      deploy.sh        # Executable script (required)

Note: After development, you need to package the entire skill directory into my-custom-skill.zip for publishing (see the "Packaging Skills" section below)

You should see:

  • my-custom-skill/ uses kebab-case naming (lowercase letters and hyphens)
  • SKILL.md filename is all uppercase, must match exactly
  • scripts/ directory stores executable scripts

Step 2: Write SKILL.md

WhySKILL.md is the core of the skill, defining trigger conditions, usage methods, and output formats.

Create SKILL.md file:

markdown
---
name: my-custom-skill
description: Deploy my app to custom platform. Use when user says "deploy", "production", or "custom deploy".
---

# Custom Deployment Skill

Deploy your application to a custom platform with zero-config setup.

## How It Works

1. Detect the framework from `package.json` (Next.js, Vue, Svelte, etc.)
2. Create a tarball of the project (excluding `node_modules` and `.git`)
3. Upload to the deployment API
4. Return preview URL and deployment ID

## Usage

```bash
bash /mnt/skills/user/my-custom-skill/scripts/deploy.sh [path]

Arguments:

  • path - Directory path or .tgz file to deploy (defaults to current directory)

Examples:

Deploy current directory:

bash
bash /mnt/skills/user/my-custom-skill/scripts/deploy.sh .

Deploy specific directory:

bash
bash /mnt/skills/user/my-custom-skill/scripts/deploy.sh ./my-app

Output

You'll see:

✓ Deployed to https://my-app-abc123.custom-platform.io
✓ Deployment ID: deploy_12345
✓ Claim URL: https://custom-platform.io/claim?code=...

JSON format (for machine-readable output):

json
{
  "previewUrl": "https://my-app-abc123.custom-platform.io",
  "deploymentId": "deploy_12345",
  "claimUrl": "https://custom-platform.io/claim?code=..."
}

Present Results to User

When presenting results to the user, use this format:

🎉 Deployment successful!

**Preview URL**: https://my-app-abc123.custom-platform.io

To transfer ownership:
1. Visit the claim URL
2. Sign in to your account
3. Confirm the transfer

**Deployment ID**: deploy_12345

Troubleshooting

Network Error

  • Check your internet connection
  • Verify the deployment API is accessible

Permission Error

  • Ensure the directory is readable
  • Check file permissions on the script

Framework Not Detected

  • Ensure package.json exists in the project root
  • Verify the framework is supported

**You should see**:
- Front Matter contains `name` and `description` fields
- `description` includes trigger keywords (e.g., "deploy", "production")
- Includes sections like `How It Works`, `Usage`, `Output`, `Present Results to User`, `Troubleshooting`, etc.

### Step 3: Write Bash Script

**Why**
The script is the part that actually executes operations and needs to conform to Claude's input/output conventions.

Create `scripts/deploy.sh`:

```bash
#!/bin/bash
set -e  # Exit immediately on error

# Configuration
DEPLOY_API="${DEPLOY_API:-https://deploy.example.com/api}"

# Parse parameters
INPUT_PATH="${1:-.}"

# Cleanup function
cleanup() {
  if [ -n "$TEMP_TARBALL" ] && [ -f "$TEMP_TARBALL" ]; then
    rm -f "$TEMP_TARBALL" >&2 || true
  fi
}
trap cleanup EXIT

# Detect framework
detect_framework() {
  local path="$1"
  local framework=""

  if [ -f "${path}/package.json" ]; then
    if grep -q '"next"' "${path}/package.json"; then
      framework="nextjs"
    elif grep -q '"vue"' "${path}/package.json"; then
      framework="vue"
    elif grep -q '"@sveltejs/kit"' "${path}/package.json"; then
      framework="sveltekit"
    fi
  fi

  echo "$framework"
}

# Main flow
FRAMEWORK=$(detect_framework "$INPUT_PATH")
echo "Detected framework: ${FRAMEWORK:-unknown}" >&2

# Create tarball
TEMP_TARBALL=$(mktemp -t deploy-XXXXXX.tgz)
echo "Creating tarball..." >&2
tar -czf "$TEMP_TARBALL" \
  --exclude='node_modules' \
  --exclude='.git' \
  -C "$INPUT_PATH" . >&2 || true

# Upload
echo "Uploading..." >&2
RESULT=$(curl -s -X POST "$DEPLOY_API" \
  -F "file=@$TEMP_TARBALL" \
  -F "framework=$FRAMEWORK")

# Check for errors
if echo "$RESULT" | grep -q '"error"'; then
  ERROR_MSG=$(echo "$RESULT" | grep -o '"error":"[^"]*"' | cut -d'"' -f4)
  echo "Deployment failed: $ERROR_MSG" >&2
  exit 1
fi

# Output result
echo "$RESULT"
echo "Deployment completed successfully" >&2

You should see:

  • Script uses #!/bin/bash shebang
  • Uses set -e for error handling
  • Status messages output to stderr (>&2)
  • Machine-readable output (JSON) outputs to stdout
  • Includes cleanup trap

Step 4: Set Execution Permissions

bash
chmod +x scripts/deploy.sh

You should see:

  • Script becomes executable (ls -l scripts/deploy.sh shows -rwxr-xr-x)

Step 5: Test Skill

Test the skill in Claude Code:

bash
# Activate skill
"Activate my-custom-skill"

# Trigger deployment
"Deploy my current directory using my-custom-skill"

You should see:

  • Claude activates the skill
  • Executes the deploy.sh script
  • Outputs deployment result (including previewUrl and deploymentId)

Checkpoint ✅

Now check if your skill complies with conventions:

  • [ ] Directory name uses kebab-case (e.g., my-custom-skill)
  • [ ] SKILL.md filename is all uppercase, correct
  • [ ] Front Matter contains name and description fields
  • [ ] description includes trigger keywords
  • [ ] Script uses #!/bin/bash shebang
  • [ ] Script uses set -e for error handling
  • [ ] Status messages output to stderr (>&2)
  • [ ] JSON outputs to stdout
  • [ ] Script includes cleanup trap

Common Pitfalls

Pitfall 1: Skill Not Activated

Symptom: You say "Deploy my app", but Claude doesn't activate the skill.

Cause: description doesn't include trigger keywords.

Solution:

markdown
# ❌ Wrong
description: A tool for deploying applications

# ✅ Correct
description: Deploy my app to production. Use when user says "deploy", "production", or "push to live".

Pitfall 2: Script Output Confusion

Symptom: Claude cannot parse JSON output.

Cause: Status messages and JSON output mixed together.

Solution:

bash
# ❌ Wrong: All output to stdout
echo "Creating tarball..."
echo '{"previewUrl": "..."}'

# ✅ Correct: Status messages use stderr
echo "Creating tarball..." >&2
echo '{"previewUrl": "..."}'

Pitfall 3: Temporary Files Not Cleaned

Symptom: Disk space gradually fills up.

Cause: Script doesn't clean up temporary files when exiting.

Solution:

bash
# ✅ Correct: Use cleanup trap
cleanup() {
  rm -f "$TEMP_TARBALL" >&2 || true
}
trap cleanup EXIT

Pitfall 4: SKILL.md Too Long

Symptom: Skill consumes too much context, affecting performance.

Cause: SKILL.md exceeds 500 lines.

Solution:

  • Put detailed reference documentation in separate files
  • Use Progressive Disclosure
  • Prioritize scripts over inline code

Lesson Summary

Core Points:

  1. Directory Structure: Uses kebab-case, includes SKILL.md and scripts/ directory
  2. SKILL.md Format: Front Matter + How It Works + Usage + Output + Present Results to User + Troubleshooting
  3. Script Conventions: #!/bin/bash, set -e, stderr for status messages, stdout for JSON, cleanup trap
  4. Context Efficiency: Keep SKILL.md < 500 lines, use progressive disclosure, prioritize scripts
  5. Package and Publish: Use zip -r command to package as {skill-name}.zip

Best Practices Mnemonic:

Descriptions be specific, triggers clear Use stderr for status, stdout for JSON Remember to trap, clean temp files Don't make files too long, scripts take space


Next Lesson Preview

In the next lesson, we learn Writing React Best Practices Rules.

You will learn:

  • How to write compliant rule files
  • Use _template.md template to generate rules
  • Define impact levels and tags
  • Write Incorrect/Correct code example comparisons
  • Add references and validate rules

Appendix: Source Code Reference

Click to expand source code locations

Updated: 2026-01-25

FeatureFile PathLine Numbers
Skill Development ConventionsAGENTS.md:9-699-69
Directory Structure DefinitionAGENTS.md:11-2011-20
Naming ConventionsAGENTS.md:22-2722-27
SKILL.md FormatAGENTS.md:29-6829-68
Context Efficiency Best PracticesAGENTS.md:70-7870-78
Script RequirementsAGENTS.md:80-8780-87
Zip PackagingAGENTS.md:89-9689-96
User Installation MethodsAGENTS.md:98-11098-110

Key Constants:

  • SKILL.md filename: Must be all uppercase, exact match
  • /mnt/skills/user/{skill-name}/scripts/{script}.sh: Script path format

Key Functions:

  • Script cleanup function cleanup(): For deleting temporary files
  • Framework detection function detect_framework(): Infers framework type from package.json