Building a Claude Code skill from scratch — the complete guide
A step-by-step tutorial with real code for building a Claude Code skill that triggers correctly, loads efficiently, and actually helps. Not abstract guidance — working examples.
We've reviewed hundreds of skills for RuleSell's marketplace. Most fail for the same three reasons: the description doesn't trigger when users need it, the skill loads too much context, or it tells Claude things Claude already knows. This guide exists to fix all three.
By the end, you'll have a working skill with proper frontmatter, progressive disclosure, a reference file, and a validation step. We're using a real example — a database migration skill for Prisma — because abstract guidance produces abstract output.
How skills actually work (the 30-second version)
A Claude Code skill is a directory containing a SKILL.md file. At startup, Claude sees only the name and description from your frontmatter — about 250 characters per skill. When Claude's language model decides your skill matches the user's intent, it reads the full SKILL.md. If your skill references other files, Claude reads those on demand.
This is progressive disclosure: the skill system shows Claude just enough to decide, then reveals more as needed. A deep dive by Lee Han Chung confirmed the mechanics — skill names and descriptions are pre-loaded into the system prompt (approximately 15,000 characters budget across all skills), and the full SKILL.md loads only on invocation.
The practical implication: your description is an ad for your skill. If the description doesn't convince Claude to load the skill, the skill's content never runs. And once loaded, every token in SKILL.md competes with the conversation history, other skills, and the user's request.
Step 1: Choose the right scope
Before writing anything, decide what your skill covers. The most common failure mode is skills that are too broad.
Too broad (will fail):"Helps with database stuff"
This matches everything and nothing. Claude can't distinguish when this skill is useful versus when the user just wants a SQL query.
Too narrow (won't trigger):
"Runs prisma migrate deploy on production PostgreSQL databases running on Supabase"
This only matches one very specific scenario.
Right-sized (will work):
"Creates and runs Prisma database migrations including schema changes, data
migrations, and rollbacks. Use when the user asks to add or change a database
table, create a migration, modify a Prisma schema, or needs to roll back a
failed migration."
The formula from Anthropic's skill authoring guide: What it does + When to use it + Key capabilities. Write in third person ("Creates and runs..." not "I can help you..."), because the description is injected into the system prompt and first-person creates confusion.
Step 2: Create the directory structure
Skills live in one of three places:
| Location | Path | Scope |
|---|---|---|
| Personal | ~/.claude/skills/ | All your projects |
| Project | .claude/skills/ | This project only |
| Plugin | | Where plugin is enabled |
mkdir -p ~/.claude/skills/prisma-migrate
mkdir -p ~/.claude/skills/prisma-migrate/references
The directory structure will be:
prisma-migrate/
├── SKILL.md # Main instructions (loaded on trigger)
├── references/
│ ├── migration-types.md # Loaded when Claude needs migration details
│ └── rollback-guide.md # Loaded only for rollback scenarios
Step 3: Write the frontmatter
The YAML frontmatter between --- markers controls discovery and behavior. Here's what each field does:
---
name: prisma-migrate
description: >-
Creates and runs Prisma database migrations including schema changes,
data migrations, seed scripts, and rollbacks. Use when the user asks
to add, change, or remove database tables or columns, create a migration,
modify a Prisma schema, resolve migration drift, or roll back a failed
migration. Not for raw SQL queries or non-Prisma ORMs.
allowed-tools: Bash(npx prisma ) Read Grep Glob Edit
---
Breaking this down:
name: Must be lowercase letters, numbers, and hyphens. Max 64 characters. This becomes the /prisma-migrate slash command. Cannot contain "anthropic" or "claude" as reserved words.
description: Max 1024 characters, but front-load the key use case because descriptions longer than 250 characters get truncated in the skill listing. Notice we included:
- What it does ("Creates and runs Prisma database migrations")
- Specific trigger phrases ("add, change, or remove database tables")
- A negative case ("Not for raw SQL queries") — this helps Claude NOT trigger the skill on unrelated database requests
allowed-tools: Pre-approves these tools so Claude doesn't ask for permission every time. Bash(npx prisma ) allows any Prisma CLI command. Without this, Claude would prompt the user for approval on every prisma migrate command, which is tedious for a migration workflow.
Other frontmatter options worth knowing
# Prevent Claude from auto-triggering (user must type /prisma-migrate)
disable-model-invocation: true
# Run in isolated subagent context context: fork agent: Explore
# Limit to projects that have Prisma paths: "/prisma/schema.prisma"
The paths field is underused and powerful. Setting paths: "/prisma/schema.prisma" means Claude only loads this skill when working with files in a project that has a Prisma schema. No false triggers on non-Prisma projects.
Step 4: Write the SKILL.md body
The body is the prompt Claude receives when the skill activates. The official guidance says keep it under 500 lines. We aim for under 100 in the main file, pushing details to references.
---
name: prisma-migrate
description: >-
Creates and runs Prisma database migrations including schema changes,
data migrations, seed scripts, and rollbacks. Use when the user asks
to add, change, or remove database tables or columns, create a migration,
modify a Prisma schema, resolve migration drift, or roll back a failed
migration. Not for raw SQL queries or non-Prisma ORMs.
allowed-tools: Bash(npx prisma *) Read Grep Glob Edit
# Prisma Migration Workflow
Before any migration
- Read the current schema:
prisma/schema.prisma - Check migration status:
npx prisma migrate status - If drift detected, resolve before proceeding
Creating a migration
- Edit
prisma/schema.prisma with the requested changes - Run:
npx prisma migrate dev --name <descriptive-name> - Review the generated SQL in
prisma/migrations/ - If the migration includes data loss (dropping columns/tables),
warn the user explicitly before proceeding
Migration naming
Use descriptive kebab-case names that explain the change:
add-user-email-verificationremove-legacy-auth-columnscreate-order-items-table
Never use generic names like update or fix.
Data migrations
For migrations that need to transform existing data, create a separate migration script. See references/migration-types.md for the three migration patterns (schema-only, data-transform, seed).
Rollbacks
If a migration fails or needs reverting: See references/rollback-guide.md
Validation
After every migration:
bash npx prisma migrate status npx prisma validate If either command fails, fix the issue before marking the task complete. Do not skip validation.
Notice what we're doing:
- Concrete commands — not "run the migration tool" but
npx prisma migrate dev --name - Progressive disclosure — migration types and rollback details live in reference files, loaded only when needed
- Validation loop — the skill ends with a verification step that forces Claude to confirm success
- Opinionated naming — we tell Claude exactly how to name migrations instead of letting it decide
- Safety rail — explicit warning requirement before data-loss migrations
Step 5: Write the reference files
Reference files contain the detail that would bloat SKILL.md. Claude reads them only when the conversation requires it.
references/migration-types.md:
# Prisma Migration Types
Schema-only migration
Changes the database schema without touching existing data.
Example: adding a new optional column.
prisma model User { id Int @id @default(autoincrement()) email String @unique name String? bio String? // new optional field — no data migration needed } Run: npx prisma migrate dev --name add-user-bio
Data-transform migration
Changes schema AND needs existing data moved or transformed.
Example: splitting a name field into firstName and lastName.
Steps:
- Add new columns (nullable):
npx prisma migrate dev --name add-name-fields - Write a data script to populate new columns from old:
bash npx prisma db execute --file ./prisma/scripts/split-names.sql 3. Make new columns required, drop old: npx prisma migrate dev --name finalize-name-split
Always do this in multiple migrations, not one. Each step is independently reversible.
Seed migration
Populates reference data (countries, categories, roles) that the app needs to function.
Run after migration: npx prisma db seed
Seed scripts live in prisma/seed.ts (or seed.js).
references/rollback-guide.md:
# Prisma Migration Rollback
Development (local database)
Reset and replay:
bash npx prisma migrate reset This drops the database, recreates it, and replays all migrations. Only use in development. Never in production.
Production
Prisma does not have a built-in rollback command for production. Options:
- Forward migration: Create a new migration that undoes the changes.
This is the safest approach and creates a clear audit trail.
- Manual SQL: Write reversal SQL and apply with:
bash npx prisma db execute --file ./prisma/rollbacks/undo-
- Resolve and mark: If a migration partially applied:
bash npx prisma migrate resolve --rolled-back
After any rollback
- Run
npx prisma migrate status to verify state - Run
npx prisma validate to check schema consistency - Run
npx prisma generate to regenerate the client
Always verify the application starts and passes tests after rollback.
Keep references one level deep from SKILL.md. Anthropic's best practices explicitly warn against nested references (reference files that link to other reference files) because Claude may only partially read deeply-nested files.
Step 6: Test it
Testing is where most skill authors stop too early. You need to verify three things:
Does it trigger?
Start a fresh Claude Code session and try natural language that should activate your skill:
"I need to add an email verification field to the User table"
"Create a migration for the new orders feature"
"The last migration failed, help me roll it back"
If Claude doesn't activate your skill, the description needs work. Add more trigger phrases, be more specific about the user scenarios.
Does it NOT trigger when it shouldn't?
Try requests that are database-related but not Prisma migrations:
"Write a raw SQL query to count users"
"Help me set up a MongoDB connection"
"What's the best ORM for Go?"
If your skill triggers on these, the description is too broad. Add explicit negative cases.
Does the output match your expectations?
When the skill activates, check:
- Does Claude run
prisma migrate statusfirst? (It should — you told it to.) - Does Claude use descriptive migration names? (It should — you gave it a naming convention.)
- Does Claude validate after the migration? (It should — you made validation mandatory.)
- Does Claude load the rollback guide only when you ask about rollbacks? (It should — that's progressive disclosure.)
Step 7: The iterative refinement loop
The best way to improve a skill is the two-Claude method from Anthropic's docs:
- Claude A (your development session) — helps you write and refine the skill
- Claude B (a fresh session with the skill installed) — tests the skill on real tasks
- You observe Claude B's behavior and bring insights back to Claude A
- Does Claude read files in an unexpected order? Your structure might not be intuitive.
- Does Claude repeatedly read the same reference file? That content might belong in SKILL.md.
- Does Claude never read a reference file? It might be unnecessary, or it might be poorly signaled.
- Does Claude ignore a specific instruction? Strengthen the language — "always" and "never" are stronger signals than "should" and "consider."
Common mistakes to avoid
1. Explaining things Claude already knows.Bad:
Prisma is an ORM (Object-Relational Mapping) tool that helps developers interact with databases using a type-safe query API...
Claude knows what Prisma is. Every token spent explaining basics is a token not available for conversation context.
2. Offering too many options.Bad:
You can use prisma migrate dev, or prisma db push, or write raw SQL, or use a third-party migration tool...
Good:
Use prisma migrate dev for all migrations. Use prisma db push only for prototyping (warn the user it skips migration history).
Pick a default. Provide an escape hatch only when necessary.
3. Using Windows-style paths.Always use forward slashes: references/guide.md, not references\guide.md. Forward slashes work everywhere. Backslashes fail on Unix.
Every skill that makes changes should end with a verification step. Without it, Claude will report success based on the absence of errors, even if the result is wrong.
5. Descriptions in the wrong voice.Write in third person: "Creates and runs Prisma migrations." Not "I help you create Prisma migrations" and not "Use this to create Prisma migrations." The description is part of the system prompt, and inconsistent point-of-view causes discovery problems.
Publishing on RuleSell
Once your skill works locally, publishing it on RuleSell gives it distribution and quality validation. Our automated pipeline checks the six signals that matter:
- Trigger reliability — we test your description against our corpus of real user phrases
- Token efficiency — we measure the total context cost of loading and using your skill
- Schema cleanliness — we validate your frontmatter against the spec
- Install success rate — we test first-run installation on clean environments
- Freshness — we track update frequency
- Security scan — we check for credential access, command injection, and unexpected network calls
If this is your first skill, start with a free listing. You need 50 verified installs before you can publish paid items — this is intentional. We want creators to prove their work before we let them charge for it.
The complete file listing
For reference, here's everything we built:
~/.claude/skills/prisma-migrate/
├── SKILL.md # 45 lines of frontmatter + instructions
├── references/
│ ├── migration-types.md # ~50 lines, loaded on demand
│ └── rollback-guide.md # ~40 lines, loaded on demand
Total: ~135 lines across 3 files. The skill handles migrations, rollbacks, data transforms, and validation. It triggers on natural language, doesn't trigger on unrelated database requests, and validates its own output.
That's what a good skill looks like. Not 800 lines of inlined instructions. Not a wrapper around a command Claude already knows. A focused, well-structured, progressive-disclosure prompt that makes Claude better at one specific job.
Build your first skill and publish it at rulesell.com. Before you submit, check our list of 16 anti-patterns we reject — every one of them is a real failure we've seen in submissions. Once your skill is live, see how the Quality Score grades it across six dimensions.
For step-by-step publishing instructions, read How to publish your first Claude Code skill. Or browse existing Claude Code skills to see what's already been built.