Atlas
TechnicalExplainer

Claude Code Skills: Turn Repeated Prompts into Commands

Claude Code skills turn a checklist you keep pasting into a reusable /command. Covers SKILL.md, arguments, shell injection and tool permissions.

The short version

  • A Claude Code skill is a folder with a SKILL.md file; create one when you keep pasting the same checklist or when a CLAUDE.md section has turned into a procedure.
  • A skill body loads only when it is used, so long procedures cost little context compared with CLAUDE.md, which is always loaded.
  • Frontmatter controls who can start a skill: disable-model-invocation keeps risky actions like deploys manual, and allowed-tools pre-approves the commands you list for that turn without restricting other tools.
  • Shell injection with !command puts live data such as a git diff into the prompt before Claude sees it, but a failing command aborts the whole invocation. context: fork runs the skill in a subagent.
  • Old .claude/commands/ files still work; commands and skills have been merged, so new work should use the skills layout.
On this page
  1. The short version
  2. What is a Claude Code skill?
  3. Where do skills live?
  4. Your first skill: summarize and review a diff
  5. How do I pass arguments to a skill?
  6. Who can run a skill: you, Claude, or both?
  7. How do I pre-approve tools for a skill?
  8. How do I keep a skill from bloating context?
  9. Running a skill in a subagent
  10. A practical checklist
  11. Conclusion
  12. Questions people ask

If you keep pasting the same instructions into Claude Code, make it a skill. A skill is a directory with a SKILL.md file; Claude can load it when it is relevant, or you can run it yourself as /skill-name. This article shows how to write one, pass arguments, inject live data, limit permissions and share it with your team.

Everything below follows the Claude Code documentation on skills, linked at the end.

What is a Claude Code skill?

A skill is a Markdown file with YAML frontmatter between --- markers, followed by instructions. The frontmatter must start on the first line of the file, or it is not read. The key property is when the body loads: unlike CLAUDE.md content, a skill's body loads only when it is used.

That is the whole design argument. CLAUDE.md holds facts that must always be in context: build commands, conventions, where things live. Procedures, such as "how we cut a release" or "how we review a migration", belong in skills, where they cost little until needed. If you are deciding what goes where, see CLAUDE.md best practices.

Are skills the same as slash commands?

Custom commands have been merged into skills. A file at .claude/commands/deploy.md and one at .claude/skills/deploy/SKILL.md both create /deploy, and existing command files keep working. If a skill and a command file share a name, the skill wins. Skills add a directory for supporting files, frontmatter that controls who can invoke them, and automatic loading by Claude. Command files accept the same frontmatter, except name and paths.

Where do skills live?

Location decides who gets the skill:

LocationPathScope
Enterprise.claude/skills/ inside the managed settings directoryMachines where your organization deploys it
Personal~/.claude/skills/<skill-name>/SKILL.mdAll your projects
Project.claude/skills/<skill-name>/SKILL.mdThis repository
Nested<subdir>/.claude/skills/<skill-name>/SKILL.mdSessions in or below that subdirectory
Plugin<plugin>/skills/<skill-name>/SKILL.mdWhere the plugin is enabled, invoked as /plugin-name:skill-name

When names collide, enterprise beats personal, and personal beats project. Commit project skills to the repository and the whole team gets them. Nested skills load the first time Claude reads or edits a file in that subdirectory, which suits monorepos.

Your first skill: summarize and review a diff

Create .claude/skills/diff-review/SKILL.md:

yaml
---
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.
allowed-tools: Bash(git diff *)
---

## Current changes

!`git diff HEAD`

## Instructions

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

Two things happen here. The description tells Claude when to load the skill, so write it as "what it does, plus when to use it". The combined description and when_to_use text is truncated at 1,536 characters in the skill listing, so put the key use case first.

The line starting with ! followed by a backtick-wrapped command is shell injection. Claude Code runs git diff HEAD before Claude sees the skill and replaces the placeholder with the output. Claude sees the real diff, not an instruction to go and fetch it. The inline form is recognized only at the start of a line or after whitespace. Edits to existing skill files take effect in the current session without a restart; if you create a brand-new top-level skills directory mid-session, run /reload-skills.

Two rules from the docs matter in practice. First, if an injected command fails, the whole invocation aborts and Claude never sees the skill. Any non-zero exit counts, apart from exit code 1 from search and comparison commands, so append || true to commands that may fail. Second, injected commands are checked against your permission rules and do not prompt. A deny match aborts, and outside auto mode anything other than an allow also aborts, which is why the example pre-approves the command with allowed-tools. For multi-line commands, use a fenced block opened with three backticks and a !.

How do I pass arguments to a skill?

$ARGUMENTS expands to everything after the skill name:

yaml
---
name: fix-issue
description: Fix a GitHub issue
disable-model-invocation: true
---

Fix GitHub issue $ARGUMENTS following our coding standards.

Running /fix-issue 123 makes Claude receive "Fix GitHub issue 123 following our coding standards." For several values, use $ARGUMENTS[N] or the shorthand $0, $1, $2 (indexes start at 0):

yaml
---
name: migrate-component
description: Migrate a component from one language to another
---

Migrate the $0 component from $1 to $2.
Preserve all existing behavior and tests.

Quote multi-word values: /my-skill "hello world" second makes $0 equal hello world. If the skill text contains no placeholder, Claude Code appends ARGUMENTS: <value> to it instead.

You can also declare named positional arguments in an arguments field and use $issue or $branch in the body, and add argument-hint (for example [issue-number]) for autocomplete. Other substitutions include ${CLAUDE_SKILL_DIR}, the directory containing SKILL.md, which is useful for pointing at a bundled script. To write a literal dollar sign before a digit, escape it as \$1.00.

Who can run a skill: you, Claude, or both?

Two frontmatter fields control invocation:

FrontmatterYou can invokeClaude can invoke
(default)YesYes
disable-model-invocation: trueYesNo
user-invocable: falseNoYes

Use disable-model-invocation: true for anything with side effects: deploys, commits, messages sent outside. You do not want the model deciding on its own that now is a good time to deploy. Use user-invocable: false for background knowledge Claude should pull in on its own but that makes no sense as a command; it hides the skill from the / menu.

You can also restrict skills through permission rules. Deny the Skill tool in /permissions to disable all of them, or use rules such as Skill(commit) for an exact match and Skill(deploy *) for a prefix match to allow or deny specific ones.

How do I pre-approve tools for a skill?

allowed-tools lets Claude use the listed tools without prompting during the turn that invokes the skill:

yaml
---
name: commit
description: Stage and commit the current changes
disable-model-invocation: true
allowed-tools: Bash(git add *) Bash(git commit *) Bash(git status *)
---

Note the limits stated in the docs: it does not restrict other tools, which still follow your normal permission settings, and the grant clears when you send your next message. The value can be space- or comma-separated, or a YAML list. If you want to remove tools instead, disallowed-tools takes them out of Claude's pool while the skill is active. For unattended runs, read Claude Code headless in CI before relying on any of this.

How do I keep a skill from bloating context?

Keep SKILL.md under 500 lines and move detail into supporting files that Claude loads when needed:

text
my-skill/
├── SKILL.md (required - overview and navigation)
├── reference.md (detailed API docs - loaded when needed)
├── examples.md (usage examples - loaded when needed)
└── scripts/
    └── helper.py (utility script - executed, not loaded)

Reference the files from SKILL.md so Claude knows they exist. A script that is executed rather than read costs no context for its source, which makes scripts a good home for deterministic steps such as formatting a report.

Running a skill in a subagent

Add context: fork and the skill content becomes the prompt for a new subagent. That subagent does not see your conversation history, so the instructions must be self-contained and contain an explicit task; guidelines alone produce no output:

yaml
---
name: pr-summary
description: Summarize changes in a pull request
context: fork
agent: Explore
allowed-tools: Bash(gh *)
---

## Pull request context
- PR diff: !`gh pr diff`
- PR comments: !`gh pr view --comments`
- Changed files: !`gh pr diff --name-only`

## Your task
Summarize this pull request...

The agent field picks the subagent type: Explore, Plan, general-purpose (the default) or a custom agent. Forked skills run in the background by default, so you can keep working; set background: false to wait for the result. In -p and SDK mode the result is waited for anyway. Forking keeps noisy exploration out of your main context. For when splitting work is worth it, see Claude Code subagents. Skills can also set model and effort overrides while active, register hooks, and limit automatic activation with paths globs.

A practical checklist

  • Start from a real repetition: the third time you paste the same prompt.
  • Write the description for the model: what it does, and when to use it.
  • Put live facts in with ! injection instead of asking Claude to run commands, and guard commands that may exit non-zero.
  • Mark side-effecting skills disable-model-invocation: true and pre-approve only the exact commands needed.
  • Field names must match exactly. Claude Code ignores unrecognized fields without an error, so a typo like allowed_tools silently does nothing. Malformed YAML loads the skill with no fields set. Test the skill after writing it.
  • Commit it to .claude/skills/ and review changes like code.

Conclusion

Skills are the right home for procedures: loaded on demand, shareable through the repository, and controllable down to who may start them and which commands run without a prompt. Keep CLAUDE.md for the facts that always apply, and move every recurring checklist into a skill.

Atlas is a team of AI engineers built on Claude Code, where each engineer has a written role. Whether Atlas lets you attach your own skills to those roles is covered below.

Questions people ask

What is a skill in Claude Code?

A skill is a directory containing a SKILL.md file with YAML frontmatter and Markdown instructions. Claude loads it when it is relevant, or you run it as /skill-name. Its body loads only when used.

What is the difference between a skill and CLAUDE.md?

CLAUDE.md holds facts that are always loaded. A skill's body loads only when it is used, so long procedures cost little context until needed.

Do custom slash commands still work?

Yes. Commands have been merged into skills, and a file in .claude/commands/ still creates the same command as the matching skill. If both share a name, the skill wins. Skills add supporting files and invocation controls.

How do I stop Claude from running a skill on its own?

Set disable-model-invocation: true in the frontmatter. You can still run it with /skill-name, but Claude cannot load it automatically.

How do I pass a value to a skill?

Use $ARGUMENTS for everything after the skill name, or $0, $1 and so on for individual values. Running /fix-issue 123 substitutes 123 into the text.

Sources

  1. Extend Claude with skills (Claude Code docs)
  2. Custom commands and skills (Claude Code docs, slash-commands page)

Researched and drafted with Claude, then checked against the sources above. Atlas is an independent product, not affiliated with Anthropic or OpenAI. Claude and Claude Code are trademarks of Anthropic; Codex is a product of OpenAI.