cd /news/developer-tools/custom-slash-commands-in-claude-code… · home topics developer-tools article
[ARTICLE · art-69460] src=dev.to ↗ pub= topic=developer-tools verified=true sentiment=· neutral

Custom slash commands in Claude Code: how they work now that commands are skills

Anthropic has merged custom slash commands into skills in Claude Code, making commands a subset of the skills system. The change means commands like `/fix-issue` now support argument expansion, tool grants that last only one turn, and shell command substitution via `!` syntax. A developer explains the new behavior, including precedence rules where skills override commands, and practical tips for using `$ARGUMENTS`, indexed access, and `allowed-tools`.

read4 min views1 publishedJul 23, 2026

You type /fix-issue 123

and Claude Code expands it into a full prompt with your team's standards attached. Custom slash commands are the cheapest automation in Claude Code — one markdown file, zero config — and they changed in a way that's easy to miss: custom commands have been merged into skills. Your .claude/commands/

files keep working, but the merged model explains several things that look like bugs (arguments not expanding, tool grants vanishing mid-task) and adds features commands never had.

Here is the current behavior, verified against the official docs as of late July 2026.

Drop a markdown file into your project:

<!-- .claude/commands/fix-issue.md -->
---
description: "Fix a GitHub issue by number"
---
Fix GitHub issue $ARGUMENTS following our coding standards.
Read the issue with `gh issue view`, locate the relevant code,
implement the fix, and add a regression test.

Typing /fix-issue 123

sends that content with $ARGUMENTS

replaced by 123

.

The merge means this file and a skill at .claude/skills/fix-issue/SKILL.md

both create /fix-issue

and behave the same way. What a skill adds on top:

If a command and a skill share a name, the skill takes precedence. My practical rule: throwaway personal shortcuts can stay in commands/

; anything you will grow or share belongs in skills/

. (How to write a description that actually triggers is its own topic — I covered it in the SKILL.md guide.)

$ARGUMENTS

, $0

, and the quoting rules Three expansion rules cover almost every confusion I see:

1. $ARGUMENTS is the whole string as typed.

/fix-issue 123 high-priority

$ARGUMENTS

becomes 123 high-priority

.2. Indexed access is zero-based and shell-quoted. $ARGUMENTS[0]

(or the shorthand $0

) is the first argument. Quoting groups words:

/migrate-component "search bar" React Vue
→ $0 = search bar, $1 = React, $2 = Vue

3. Missing values behave differently by placeholder type. An indexed placeholder with no matching argument ($2

when you passed one arg) stays in the text unchanged. A named placeholder declared in frontmatter expands to an empty string. If you need a literal dollar-digit in prose ($1.00

), escape it: \$1.00

.

One more safety net: if you pass arguments but the file contains no $ARGUMENTS

at all, Claude Code appends ARGUMENTS: <your input>

to the end, so your input is never silently dropped.

! command``

This is the feature that turns a canned prompt into a grounded one. Lines like:

## Current diff
!`git diff HEAD`

run the shell command before Claude sees anything, and the output replaces the placeholder. Claude receives the actual diff, not an instruction to go fetch it — one fewer tool round-trip, and no chance of the model "summarizing" a diff it never read.

Substitution is a single pass: a command's output cannot emit another !…``

placeholder for a second expansion. Treat it as data, not macros.

allowed-tools

is a one-turn grant, not a session setting The most common surprise in the merged model. Frontmatter like:

allowed-tools: Bash(git add:*), Bash(git commit:*)

pre-approves those tools for the turn that invokes the skill. The grant clears when you send your next message — even though the skill's content stays in context. So "why is Claude asking permission again for the same command?" is usually this: the instructions persisted, the grant did not. Re-invoking the skill re-applies it.

Two related notes:

allowed-tools

doesn't restrict anything. Unlisted tools remain available under your normal permission settings.${CLAUDE_SKILL_DIR}

expands in both the body and allowed-tools

, so a skill can ship a script and pre-approve exactly that script's invocation — no prompt, no wildcard.If you want a grant that survives the whole session, that belongs in your permission rules instead — the allow/deny/ask matching logic is its own minefield.

By default both you and Claude can run any skill. Two frontmatter switches change that:

disable-model-invocation: true

— only you can trigger it. Use for anything with side effects where timing matters: /deploy

, /commit

, /send-release-notes

. You don't want the model deploying because the code "looks ready".user-invocable: false

— only Claude can load it. Use for background knowledge that isn't a meaningful action, like a legacy-system-context

skill.Skill descriptions are always in context so Claude knows what exists; the body loads on invocation.

Once invoked, the rendered content stays in the conversation for the rest of the session (re-invoking an identical skill adds a short "already loaded" note rather than a duplicate). Two consequences:

After context compaction, recent skills are re-attached within a fixed token budget, so a long session can silently drop older ones — details in what survives compaction.

$0

is the $N

stays literal; unmatched named args become empty! command``

runs before Claude reads anything; single passallowed-tools

clears on your next messagecode-review

) .claude/commands/

still works; skills are the recommended pathI maintain Rulestack — versioned rules & skills packs for Claude Code, Cursor, and Codex, kept in sync with changes like this one: https://rulestack.gumroad.com?ref=devto

Daily notes on AI coding workflows live on Bluesky: https://bsky.app/profile/ai-shop.bsky.social

── more in #developer-tools 4 stories · sorted by recency
── more on @anthropic 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/custom-slash-command…] indexed:0 read:4min 2026-07-23 ·