Learn Claude Code Mastery Building Your Own Skills Library: Reusable Workflows for Every Project

Building Your Own Skills Library: Reusable Workflows for Every Project

Intermediate 🕐 30 min Lesson 14 of 15
What you'll learn
  • Explain why skills are better than long CLAUDE.md files for procedures and reference material
  • Create a SKILL.md file with proper frontmatter and invoke it with a slash command
  • Use dynamic context injection (!`command`) to pull live data into a skill before Claude sees it
  • Pass arguments to skills using $ARGUMENTS and control invocation with disable-model-invocation
  • Share skills across a team by committing .claude/skills/ to version control

The Problem with CLAUDE.md Growth

CLAUDE.md starts small. A few conventions, a deploy command, some project notes. But over time it grows: you add a section about the PR process, then one about the test framework, then step-by-step instructions for the staging deployment procedure. Eventually the file is hundreds of lines, Claude loads all of it into every session, and the most important parts get diluted by everything else.

The Anthropic recommendation is to keep CLAUDE.md to facts Claude needs in every session — the things that, if missing, would cause mistakes immediately. Procedures, reference material, and reusable workflows belong in skills. A skill's content only enters the context when you invoke it, costing nothing when it's not needed.

The Anatomy of a Skill

Every skill is a directory containing a SKILL.md file. The directory name becomes the slash command. A skill at ~/.claude/skills/deploy/SKILL.md is invoked with /deploy.

The SKILL.md file has two parts: YAML frontmatter between --- markers, and the markdown instructions Claude follows when the skill runs:

---
name: deploy
description: Deploy the application to the staging environment
disable-model-invocation: true
---

Deploy the application to staging:

1. Run the test suite with `npm test` and stop if any tests fail
2. Build the production bundle with `npm run build`
3. Deploy to staging with `npm run deploy:staging`
4. Verify the deployment succeeded by hitting the health check endpoint
5. Report the deployed version and any warnings from the deploy output

Skills live in three scopes:

  • Personal~/.claude/skills/<name>/SKILL.md — available across every project you open
  • Project.claude/skills/<name>/SKILL.md — this project only; commit to git to share with your team
  • Plugin — packaged with a Claude Code plugin for distribution to others

Claude Code watches skill directories for changes. Edit a skill and it takes effect in the current session without restarting. In monorepos, Claude also auto-discovers .claude/skills/ directories inside subdirectories when you work with files in those packages.

Controlling Who Invokes a Skill

By default, both you and Claude can invoke any skill. Two frontmatter fields let you restrict this:

  • disable-model-invocation: true — only you can invoke it with /skill-name. Use this for workflows with side effects you want to control: deployments, git pushes, Slack notifications. You don't want Claude deciding to deploy because your code looks ready.
  • user-invocable: false — only Claude can load it automatically when relevant. Use this for background reference material that isn't a meaningful action for a user to trigger directly.

Dynamic Context Injection

One of the most powerful skill features is the !`command` syntax. Any line in your SKILL.md that starts with !` runs a shell command before Claude sees the skill content. The command output replaces the line, so Claude receives actual live data instead of static text.

This skill summarizes uncommitted changes. The diff is fetched fresh every time the skill runs:

---
name: summarize-changes
description: Summarizes uncommitted changes and flags anything risky. Use when the user asks what changed, wants a commit message, or asks to review their diff.
---

## Current diff

!`git diff HEAD`

## Instructions

Summarize the changes above in two or three bullet points. Then list any risks you notice: missing error handling, hardcoded values, tests that need updating, or anything that looks incomplete. If the diff is empty, say there are no uncommitted changes.

Other useful injections:

!`git log --oneline -10`          # Last 10 commits
!`git branch --show-current`      # Current branch name
!`cat package.json | jq .version` # Current package version
!`gh pr list --state open`        # Open pull requests (requires gh CLI)

This is preprocessing — it runs before Claude sees anything. Claude only sees the final rendered output.

Arguments and String Substitution

Skills accept arguments via the $ARGUMENTS placeholder. When you type /fix-issue 247, Claude receives the skill content with $ARGUMENTS replaced by 247:

---
name: fix-issue
description: Fix a GitHub issue by number
disable-model-invocation: true
allowed-tools: Bash(gh *) Read Grep Edit Write
---

Fix GitHub issue $ARGUMENTS following our coding standards.

1. Read the issue with `gh issue view $ARGUMENTS`
2. Understand the requirements
3. Search the codebase for the relevant code
4. Implement the fix
5. Write a test that would have caught this bug
6. Create a descriptive commit message referencing the issue number

For skills that need multiple distinct arguments, use $ARGUMENTS[0], $ARGUMENTS[1], or the shorthand $0, $1:

---
name: migrate-component
description: Migrate a UI component from one framework to another
---
Migrate the $0 component from $1 to $2. Preserve all existing behavior and tests.

The allowed-tools frontmatter field pre-approves specific tools for this skill without affecting your global permission settings. Claude can use the listed tools when running this skill without asking for approval each time.

Supporting Files and Running in a Subagent

Skills can include multiple files in their directory. Keep SKILL.md under 500 lines — move detailed reference material to reference.md, usage examples to examples/, and runnable scripts to scripts/. Reference them from SKILL.md so Claude knows they exist:

my-skill/
├── SKILL.md            # Main instructions (required)
├── reference.md        # Detailed API docs — loaded when needed
├── examples/
│   └── sample.md       # Example output format
└── scripts/
    └── validate.sh     # Script Claude can execute

For skills that should run completely in isolation — without access to your conversation history — add context: fork to the frontmatter. The skill content becomes the prompt for a new subagent, and results are summarized back to your main session:

---
name: deep-research
description: Research a topic thoroughly without filling the main context
context: fork
agent: Explore
---

Research $ARGUMENTS thoroughly:
1. Find relevant files using Grep and Glob
2. Read and analyze the code
3. Summarize findings with specific file references and line numbers

Four Skills Worth Building Today

Start with these four. Each solves a repeated friction point that most developers hit within days of using Claude Code:

  • /summarize-changes — with !`git diff HEAD` injection. Use before every commit. Catches risky changes you've lost context on.
  • /fix-issue — takes an issue number, reads the full issue, searches the codebase, implements and tests the fix. Replaces 10+ minutes of manual context-gathering.
  • /pre-review — runs before you open a PR: checks for hardcoded values, missing tests, and obvious logic issues. Use disable-model-invocation: true so Claude doesn't trigger it uninvited.
  • /onboard — for new team members: reads CLAUDE.md, lists available skills, explains the dev workflow. Commit this to .claude/skills/ so it's available to everyone who clones the repo.

Skills compound. Each one you build reduces the friction of a task you do repeatedly. Commit your project skills to .claude/skills/ in git, and your entire team gets the same productivity improvements without any individual effort.

Key takeaways
  • Skills load only when invoked — they cost nothing in context when not in use, unlike CLAUDE.md which loads every session
  • The !`command` syntax runs a shell command before Claude sees the skill content, so Claude receives live data not stale text
  • disable-model-invocation: true prevents Claude from auto-triggering skills with side effects like deployments or git pushes
  • allowed-tools in frontmatter pre-approves specific tools for a skill without changing your global permission settings
  • Project skills in .claude/skills/ committed to git give every team member the same reusable workflows automatically