From CLAUDE.md overflow to on-demand knowledge
By now you know the problem with stuffing task-specific knowledge into CLAUDE.md — it loads every session, whether relevant or not. Skills are the solution. A skill is a markdown file that Claude loads only when it detects relevance to the current task or when you explicitly invoke it with a slash command.
The mental model: a skill is a recipe. It describes how to do a specific job — the ingredients, the steps, the techniques. But the recipe does not cook. When a skill loads, its content injects into your current conversation. Claude follows the instructions using the tools and context already available in your session.
Skills live in a required directory structure:
.claude/skills/<skill-name>/
├── SKILL.md # Required — main instructions
├── reference.md # Optional — detailed reference docs
├── examples.md # Optional — usage examples
└── scripts/
└── helper.sh # Optional — executable scriptsThe SKILL.md is the entry point. Supporting files like reference docs and examples load on-demand when Claude needs them — they do not consume tokens at startup.
Skills exist at three scopes:
- Project:
.claude/skills/— available in this project, version-controlled with your code - Personal:
~/.claude/skills/— available in all your projects - Enterprise: managed settings — deployed by organisation admins
Which of these tasks in your workflow would benefit most from being a skill?
Anatomy of a skill file
A SKILL.md has YAML frontmatter followed by markdown instructions. Here is a complete example:
---
name: review-pr
description: Reviews pull request changes against team coding standards, security checklist, and test coverage requirements
user-invocable: true
allowed-tools: Read Grep Glob
argument-hint: [branch-or-pr-number]
---
# PR Review Procedure
Review the current branch's changes against main. For each file changed:
1. Check naming conventions match project standards
2. Verify error handling follows structured logging pattern
3. Confirm new functions have corresponding test coverage
4. Flag any hardcoded secrets, API keys, or credentials
5. Check for N+1 query patterns in database access
6. Verify TypeScript types are explicit (no implicit any)
## Output Format
Structure your review as:
- **Critical** — must fix before merge
- **Warning** — should fix, but not blocking
- **Suggestion** — optional improvements
Include file path and line number for each finding.The frontmatter fields control how the skill behaves. Let us walk through the important ones.
name — The skill's identifier. Becomes the slash command: /review-pr. Lowercase, hyphens allowed, max 64 characters.
description — This is the most important field. Claude reads every skill description at session startup and uses them to decide which skills to auto-invoke. Front-load the key use case in the first sentence. Descriptions beyond roughly 250 characters are truncated.
user-invocable — If true (the default), the skill appears in the / autocomplete menu. Set to false for skills that only Claude should invoke — background knowledge that helps Claude but is not useful as a manual command.
disable-model-invocation — If true, Claude cannot auto-invoke this skill. Only manual /skill-name works. Use this for skills with side effects like deployment, where you want explicit human intent.
allowed-tools — Tools listed here skip the per-use permission prompt when the skill is active. If your PR review skill needs to read files and search code, listing Read Grep Glob means Claude can do that without asking you to approve each tool call.
argument-hint — Displayed during autocomplete to show what arguments the skill expects. Example: [branch-name] or [from-lang] [to-lang].
Model, context, paths, and shell
Four more frontmatter fields unlock advanced skill patterns.
model — Override the session model for this skill. If your session runs on Sonnet but a specific skill benefits from Opus-level reasoning, set model: claude-opus-4-6. The override applies only while the skill is active.
context: fork — This is a critical field. When set, the skill runs in an isolated subagent context instead of your main conversation. It does not see your session history. This is the bridge between skills and agents — a skill with context: fork behaves like a lightweight agent.
Use context: fork for skills that generate a lot of intermediate output (exploring files, running analysis) where you want the result but not the process in your conversation.
paths — Glob patterns that scope when the skill auto-loads:
paths: "src/api/**/*.ts,src/api/**/*.test.ts"With this field, the skill only enters Claude's consideration when you are working with files matching these patterns. Without it, the skill description is always in context. For project-specific skills, paths keep descriptions lean.
shell — Sets the shell for executable command blocks: bash (default) or powershell. Only relevant if you use shell command injection, covered next.
Dynamic skills with shell commands
Skills can execute shell commands before sending content to Claude. The syntax is !`command` — backtick-wrapped commands prefixed with !. The command runs, and its output replaces the placeholder in the skill content.
This turns static recipes into dynamic, context-aware workflows:
---
name: pr-summary
description: Summarises the current pull request changes with diff context
user-invocable: true
allowed-tools: Bash
---
# PR Summary
## Changed files
!`git diff --name-only main...HEAD`
## Full diff
!`git diff main...HEAD`
## Recent commits
!`git log --oneline main...HEAD`
Summarise these changes. Group by:
1. What changed (features, fixes, refactoring)
2. Risk areas (complex logic, breaking changes, security)
3. Missing (tests, docs, migration steps)When you invoke /pr-summary, the shell commands execute first. Claude sees the actual diff, the actual file list, and the actual commit history — not the commands. This is far more reliable than asking Claude to run those commands itself because the data is available from the first token of processing.
String substitutions let you parameterise skills:
---
name: migrate-component
argument-hint: [component-name] [from-framework] [to-framework]
---
Migrate the `$0` component from $1 to $2.
Current implementation:
!`find src -name "$0*" -type f`
Preserve all existing tests and behaviour.Invoking /migrate-component SearchBar React Vue replaces $0 with SearchBar, $1 with React, $2 with Vue.
Available substitutions:
$ARGUMENTS— all arguments as a single string$0,$1,$2— positional arguments${CLAUDE_SESSION_ID}— unique session identifier${CLAUDE_SKILL_DIR}— directory containing this SKILL.md
How skills activate
Skills trigger in two ways, and understanding the difference is key to designing them well.
Auto-invocation: At session startup, Claude reads the description field of every skill. During your conversation, when Claude encounters a task that matches a skill's description, it loads the full SKILL.md content into context and follows the instructions. You do not need to do anything — Claude decides a skill is relevant and uses it.
This is why the description field matters so much. A description like "reviews code" is too vague — Claude might load it during unrelated tasks. A description like "reviews pull request changes against team coding standards, security checklist, and test coverage requirements" tells Claude exactly when this skill applies.
Manual invocation: You type /skill-name in the prompt. The skill loads immediately, regardless of what Claude thinks about relevance. Use this for skills where you want explicit control: deployment, database migrations, anything with side effects.
Controlling invocation:
| Setting | Auto-invoke | Manual /command | Use when |
|---|---|---|---|
| Default (both false) | Yes | Yes | Most skills — Claude and you can both trigger |
disable-model-invocation: true | No | Yes | Side-effect skills — deploy, migrate, publish |
user-invocable: false | Yes | No | Background knowledge — Claude uses it, you do not need to |
| Both true | No | No | Effectively disabled — do not do this |
You have a skill that generates database migration files. It creates actual files and modifies your schema. How should you configure its invocation?
Three skills you should build today
Here are three skills that provide immediate value in almost any project. Adapt the instructions to match your team's conventions.
Skill 1: Changelog generator
---
name: changelog
description: Generates a structured changelog entry from recent git commits
user-invocable: true
allowed-tools: Bash
---
# Generate Changelog
## Recent changes
!`git log --oneline --no-merges $(git describe --tags --abbrev=0 2>/dev/null || echo HEAD~20)..HEAD`
Categorise each commit into:
- **Added** — new features
- **Changed** — modifications to existing features
- **Fixed** — bug fixes
- **Removed** — removed features or deprecated items
Output in Keep a Changelog format. Skip merge commits and
version bumps. Group related commits into single entries.Skill 2: Security audit
---
name: security-check
description: Scans changed files for common security vulnerabilities including hardcoded secrets, SQL injection, XSS, and insecure dependencies
user-invocable: true
allowed-tools: Read Grep Glob Bash
---
# Security Audit
## Changed files
!`git diff --name-only main...HEAD`
For each changed file, check for:
1. Hardcoded secrets (API keys, passwords, tokens, connection strings)
2. SQL injection vectors (string concatenation in queries)
3. XSS vulnerabilities (unescaped user input in HTML/JSX)
4. Insecure randomness (Math.random for security-sensitive values)
5. Missing input validation on user-facing endpoints
6. Overly permissive CORS or CSP headers
Report findings with file path, line number, severity (critical/high/medium/low),
and a specific fix recommendation.Skill 3: API endpoint scaffold
---
name: new-endpoint
description: Scaffolds a new API endpoint with route handler, validation schema, tests, and documentation following project conventions
user-invocable: true
argument-hint: [method] [path] [description]
paths: "src/api/**"
---
# New API Endpoint
Create a new $0 endpoint at $1.
Purpose: $ARGUMENTS
Follow the existing patterns in src/api/ for:
- Route handler structure
- Input validation using the project's validation library
- Error response format: { error: string, code: string }
- Success response wrapping
- Test file with happy path and error cases
- JSDoc comment on the handler functionEach of these skills encodes institutional knowledge that would otherwise live in a developer's head or a Notion page. As a skill, it is version-controlled, team-shared, and available in every Claude Code session where it is relevant.
Module 4 — Final Assessment
A skill's description field says: 'helps with code'. Why is this problematic?
You write a skill with shell command injection: !`git diff main...HEAD`. When does this command execute?
Your team has a deployment procedure that involves 12 steps including database migrations and cache invalidation. Where should it live?
What does the paths field in a skill's frontmatter control?