Custom Slash Commands in Claude Code With Skills

There is a prompt you type into Claude Code every day. “Review the staged changes, list anything risky, suggest a commit message.” You have it in a notes file and you paste it. A skill turns that into /review-staged, with the git diff already pulled in before Claude reads a word.

This post covers writing a skill, where the file goes for one project or all of them, arguments, injecting live command output, the 2 switches that decide whether you or Claude can invoke it, and the Windows-specific shell field. The format is from the current docs, linked at the end, and if you have an older .claude\commands\ folder, it still works and the last section says how.

Write One

A skill is a folder with a SKILL.md inside. The folder name becomes the command. For a personal skill that works in every project:

New-Item -ItemType Directory -Force "$env:USERPROFILE\.claude\skills\review-staged"
code "$env:USERPROFILE\.claude\skills\review-staged\SKILL.md"
---
description: Reviews the staged changes and flags anything risky. Use when the user asks what is staged, wants a commit message, or asks to review their diff.
---

## Staged changes

!`git diff --cached`

## Instructions

Summarise the changes above in 2 or 3 bullet points, then list any risks: missing error handling, hardcoded values, tests that need updating. If the diff is empty, say nothing is staged.

Save it and type /review-staged in a session. The description is what Claude reads to decide when to load the skill on its own, so the docs’ advice is to put the trigger phrases in it. The body loads only when the skill runs, so a long skill costs nothing until it is used.

Where Skills Live

  • Personal: ~\.claude\skills\<name>\SKILL.md, every project on this machine.
  • Project: .claude\skills\<name>\SKILL.md, this repo; commit it to share.
  • Nested: <subdir>\.claude\skills\..., sessions started in or below that folder.
  • Plugin: bundled with a plugin and invoked as /plugin-name:skill-name.

Claude Code watches the personal and project folders, so edits apply in the running session. If you create a top-level skills folder that did not exist when the session started, run /reload-skills. /skills lists what is loaded, with the token cost of each, and lets you hide one from Claude or from the menu.

Arguments

Whatever you type after the command is available as $ARGUMENTS, or by position as $0, $1 and so on:

---
name: fix-issue
description: Fix a GitHub issue by number
disable-model-invocation: true
argument-hint: [issue-number]
---

Fix GitHub issue $ARGUMENTS following our coding standards. Read the issue with `gh issue view $ARGUMENTS` first.

/fix-issue 123 gives Claude the text with 123 in place. argument-hint is what the menu shows next to the command. If you pass arguments and nothing in the body uses them, Claude Code appends them as ARGUMENTS: ... at the end rather than dropping them.

Pull Live Output In With !

The !`command` form in the body runs the command before the skill content is sent, and the output replaces the placeholder. Claude receives the data, never the command. For more than 1 line, a fenced block opened with ```! does the same:

## Environment
```!
git status --short
git log --oneline -5
```

A command that exits non-zero aborts the whole invocation, and Claude never sees the skill. Append || true to anything that may legitimately fail, such as a grep with no matches.

On Windows: those commands run in Bash by default, through Git Bash. If a skill needs PowerShell, set shell: powershell in the frontmatter and write the commands in PowerShell. Without Git for Windows installed, a skill that uses the default shell fails before any command runs with requires bash (shell: bash in frontmatter) but Git Bash was not found; How to Install Git on Windows fixes that, or the shell field does.

---
description: Summarise what changed in the current folder today, Windows version
shell: powershell
---

## Files changed today
!`Get-ChildItem -Recurse -File | Where-Object LastWriteTime -gt (Get-Date).Date | Select-Object -ExpandProperty FullName`

Summarise what was worked on today from the list above.

Who Can Invoke It

By default both you and Claude can run a skill, and its description sits in context all the time so Claude knows it exists. 2 frontmatter switches change that:

  • disable-model-invocation: true: only you can run it. The docs’ examples are /commit, /deploy, anything with side effects whose timing you want to control. The description leaves context too, which saves tokens.
  • user-invocable: false: only Claude can run it, and it is hidden from the / menu. For background knowledge that is not a command.

If Claude tries to invoke a skill marked disable-model-invocation, Claude Code blocks the call. You can also control which skills Claude may use with Skill(name) rules in permissions, as in the permissions post.

The Fields Worth Knowing

  • allowed-tools: tools Claude can use without asking during the turn that invoked the skill, for example Bash(gh *). The grant ends with your next message.
  • model and effort: override for the rest of the turn.
  • context: fork with agent:: run the skill in a subagent so its work stays out of your context. The subagents post explains why you would.
  • paths: only activate when Claude is working with files matching the globs.
  • hooks: hooks that register when the skill is invoked and stay for the session.

Field names are lowercase words separated by hyphens, exactly as the docs list them. Keep SKILL.md under 500 lines and put reference material in files beside it.

If You Have .claude\commands\ Files

Custom commands were merged into skills. A file at .claude\commands\deploy.md and a skill at .claude\skills\deploy\SKILL.md both create /deploy and work the same way, and the old files keep working. A command file in a subfolder maps with a colon: .claude\commands\frontend\component.md is /frontend:component. The docs recommend the skill folder for new work because it can carry supporting files; there is no need to move what already works.

Other Considerations

Skills and CLAUDE.md are not the same job. CLAUDE.md loads every session and holds facts and rules; a skill loads on demand and holds a procedure. The docs say to move a multi-step process out of CLAUDE.md into a skill, which is also how you keep the memory file under 200 lines.

Skills work in claude -p. Put /review-staged in the prompt string and Claude Code expands it before the run. Run Claude Code From a PowerShell Script With claude -p covers scripting it.

Skills synced from claude.ai land in ~\.claude\skills\synced\. Do not edit them there; the next sync overwrites them, and synced is a reserved folder name.

Frequently Asked Questions

What is the difference between a skill and a custom slash command?

None now. Commands were merged into skills. A .claude\commands\name.md file and a .claude\skills\name\SKILL.md both give you /name.

My skill does not appear in the / menu.

Check the folder name and that SKILL.md is spelt exactly so. If the skills folder is new since the session started, run /reload-skills. A skill with user-invocable: false is hidden by design.

Can Claude run my skill without me asking?

Yes, by default, when the description matches what you asked for. Set disable-model-invocation: true on anything you want to trigger yourself only.

How do I pass more than 1 argument?

Space-separated, then $0, $1, $2 in the body, or $ARGUMENTS for the whole string. You can also declare named ones with the arguments field.

Does the !`command` output cost tokens?

Yes, whatever the command prints goes into context as part of the skill. Keep the commands tight; git diff --cached rather than the whole log.


Related

If SQL Server is part of your day job, my current work lives over at sqldba.blog: production DBA scripts, an error library and the SSMS guide.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Looking for SQL Server?

This site started as a SQL Server blog in 2017 and the archive is still here. The new SQL Server writing, and all the scripts, moved to a site of their own.

sqldba.blogScripts, error library, wait types, SSMS
Browse by topic