{"id":517,"date":"2026-07-29T02:13:15","date_gmt":"2026-07-29T02:13:15","guid":{"rendered":"https:\/\/abrarqasim.com\/blog\/claude-code-slash-commands\/"},"modified":"2026-07-29T02:13:15","modified_gmt":"2026-07-29T02:13:15","slug":"claude-code-slash-commands","status":"publish","type":"post","link":"https:\/\/abrarqasim.com\/blog\/claude-code-slash-commands\/","title":{"rendered":"Claude Code Slash Commands: The Ones That Earn Their Place"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><em>Last updated: July 2026. Reflects Claude Code 2.1.x, the <code>.claude\/skills\/<\/code> format that replaced <code>.claude\/commands\/<\/code>, and the namespacing bug that is still open.<\/em><\/p>\n\n\n\n<div class=\"wp-block-group tldr-box has-pale-cyan-blue-background-color has-background\" style=\"border-width:1px;border-radius:8px;padding-top:1.5em;padding-right:1.5em;padding-bottom:1.5em;padding-left:1.5em\"><div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\">\n<h3 class=\"wp-block-heading has-medium-font-size\">TL;DR<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Claude Code slash commands are two different things wearing one name: built-in session controls like <code>\/clear<\/code>, <code>\/compact<\/code>, <code>\/context<\/code>, and <code>\/model<\/code>, and custom commands you write yourself as Markdown files. The custom ones used to live in <code>.claude\/commands\/name.md<\/code>; Anthropic now calls that the legacy format and points you at <code>.claude\/skills\/name\/SKILL.md<\/code> instead. Both still produce <code>\/name<\/code>, both take the same frontmatter (<code>description<\/code>, <code>argument-hint<\/code>, <code>allowed-tools<\/code>, <code>model<\/code>, <code>disable-model-invocation<\/code>), and both support <code>$ARGUMENTS<\/code>, <code>!<\/code> for injecting shell output, and <code>@<\/code> for injecting files. The single highest-value trick is bash injection: a command that runs <code>!`git diff --cached`<\/code> before Claude thinks is worth more than a command with a beautifully worded prompt. The thing that will waste your afternoon is subdirectory namespacing, which the docs describe and the CLI does not implement the way they describe it.<\/p>\n<\/div><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">The word &#8220;slash command&#8221; covers two unrelated things<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This is the source of most of the confusion I see, so it goes first.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When you type <code>\/<\/code> in a Claude Code session, the autocomplete list mixes two categories that have nothing in common except the prefix. Built-in commands are session controls compiled into the CLI. They manipulate the conversation, the model, or your config. You cannot write one, override one, or change what one does. Custom commands are Markdown files on your disk that expand into a prompt. They are text. Claude reads them the same way it reads anything else you type.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The practical consequence: <code>\/compact<\/code> is not a prompt, it is a mechanism, and you cannot ask Claude to run it for you. Your <code>\/deploy<\/code> command <em>is<\/em> a prompt, so Claude can invoke it on its own if you let it. Those two facts drive most of the design decisions later in this post.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The built-in commands worth memorizing<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">There are a lot of built-ins. I use maybe eight of them with any regularity, and three of those are context management, which is where the actual leverage is.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Command<\/th><th>What it does<\/th><th>When I reach for it<\/th><\/tr><\/thead><tbody><tr><td><code>\/clear<\/code><\/td><td>Wipes conversation history, starts from empty context<\/td><td>New task in the same repo. Stops the last task&#8217;s decisions bleeding into this one.<\/td><\/tr><tr><td><code>\/compact<\/code><\/td><td>Summarizes older messages and replaces them with the summary<\/td><td>Mid-task, when context is filling but I still need the history.<\/td><\/tr><tr><td><code>\/context<\/code><\/td><td>Shows what is currently eating your context window<\/td><td>Before I decide between <code>\/clear<\/code> and <code>\/compact<\/code>.<\/td><\/tr><tr><td><code>\/model<\/code><\/td><td>Switches the model for the session<\/td><td>Dropping to a cheaper model for mechanical work.<\/td><\/tr><tr><td><code>\/cost<\/code><\/td><td>Token usage and spend for the session<\/td><td>After a long agentic run, to see what it cost.<\/td><\/tr><tr><td><code>\/rewind<\/code><\/td><td>Restores earlier conversation state<\/td><td>When a run went sideways and I want the state from four turns ago.<\/td><\/tr><tr><td><code>\/agents<\/code><\/td><td>Manages subagent definitions<\/td><td>Setting up a repo, rarely after.<\/td><\/tr><tr><td><code>\/init<\/code><\/td><td>Generates a CLAUDE.md from the codebase<\/td><td>First run in an unfamiliar repo. The output always needs editing.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The ordering advice everyone gives is &#8220;run <code>\/compact<\/code> when you get the warning.&#8221; That is backwards. By the time the warning fires, the compaction has to summarize a context that is already 90% full, and the summary it produces is correspondingly lossy. Check <code>\/context<\/code> around the halfway mark and compact then, while there is still room for a summary that keeps detail. I wrote more about how context windows actually degrade in <a href=\"https:\/\/abrarqasim.com\/blog\/llm-context-window-anti-hallucination-prompt-backfired\/\">the post about the anti-hallucination prompt that backfired on me<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One more built-in note: <code>\/clear<\/code> does not destroy anything. The old conversation stays on disk and you can resume it by session ID. It is not a delete, it is a fresh window.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Where custom commands live, and why there are now two answers<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Every guide written before 2026 tells you the same thing: drop a Markdown file in <code>.claude\/commands\/<\/code>, the filename becomes the command name, done. <code>review.md<\/code> gives you <code>\/review<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That still works. Anthropic&#8217;s current docs also label it the legacy format and point you at <code>.claude\/skills\/&lt;name&gt;\/SKILL.md<\/code> instead. The two formats produce the same slash command and accept the same frontmatter. The difference is that a skill can also be invoked autonomously by Claude when it decides the skill is relevant, whereas the mental model for a command file is that you type it.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Location<\/th><th>Scope<\/th><th>Committed to git?<\/th><\/tr><\/thead><tbody><tr><td><code>.claude\/commands\/x.md<\/code><\/td><td>This project, everyone on the repo<\/td><td>Yes<\/td><\/tr><tr><td><code>.claude\/skills\/x\/SKILL.md<\/code><\/td><td>This project, everyone on the repo (current format)<\/td><td>Yes<\/td><\/tr><tr><td><code>~\/.claude\/commands\/x.md<\/code><\/td><td>You, every project<\/td><td>No, it is your home dir<\/td><\/tr><tr><td><code>~\/.claude\/skills\/x\/SKILL.md<\/code><\/td><td>You, every project (current format)<\/td><td>No<\/td><\/tr><tr><td>Plugin<\/td><td>Distributed to other people, namespaced <code>\/plugin__command<\/code><\/td><td>Separate repo<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">My rule after a year of this: anything that encodes how <em>this repo<\/em> works goes in the project directory and gets committed. Anything that encodes how <em>I<\/em> work goes in <code>~\/.claude\/<\/code>. The mistake I made early was putting a <code>\/test<\/code> command in my home directory that assumed Vitest. It fired in a Go repo and Claude confidently went looking for a <code>package.json<\/code>. Personal commands need to be framework-agnostic or they are landmines.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Anatomy of a custom command<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A command file is YAML frontmatter plus a prompt body. Here is a complete one:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">---\ndescription: Create a git commit with a conventional message\nallowed-tools: Bash(git add:*), Bash(git commit:*), Bash(git diff:*)\nargument-hint: [optional message]\nmodel: haiku\n---\n\n## Staged changes\n!`git diff --cached`\n\n## Recent commit style\n!`git log --oneline -10`\n\nWrite a Conventional Commits message for the staged changes above.\nMatch the style of the recent commits. If $ARGUMENTS is non-empty,\nuse it as the subject line instead of generating one.\nCommit. Do not push.<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The frontmatter fields, in order of how much they matter:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Field<\/th><th>Purpose<\/th><th>Default if omitted<\/th><\/tr><\/thead><tbody><tr><td><code>description<\/code><\/td><td>Shown in autocomplete and <code>\/help<\/code>. Also required for Claude to invoke the command itself.<\/td><td>First line of the prompt body<\/td><\/tr><tr><td><code>allowed-tools<\/code><\/td><td>Restricts which tools the command can use<\/td><td>Inherits from the conversation<\/td><\/tr><tr><td><code>argument-hint<\/code><\/td><td>Shows expected args in autocomplete<\/td><td>None<\/td><\/tr><tr><td><code>model<\/code><\/td><td><code>sonnet<\/code>, <code>haiku<\/code>, a full model ID, or <code>inherit<\/code><\/td><td>Inherits from the conversation<\/td><\/tr><tr><td><code>disable-model-invocation<\/code><\/td><td><code>true<\/code> means only a human typing the command can trigger it<\/td><td>false<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><code>description<\/code> looks like documentation and is actually load-bearing. Claude can invoke your custom commands on its own through the SlashCommand tool, but only ones that have a <code>description<\/code> populated. Leave it out and your command becomes type-only. That is sometimes what you want, and there is a cleaner way to say so.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>disable-model-invocation: true<\/code> is the cleaner way. Put it on anything with side effects you would not want an agent triggering mid-run: <code>\/deploy<\/code>, <code>\/commit<\/code>, anything that talks to production. The failure mode without it is not dramatic, it is quiet. Claude decides your <code>\/deploy<\/code> command is a reasonable next step in a long agentic loop, and you find out from the notification. Related reading on why I do not let agents run unsupervised: <a href=\"https:\/\/abrarqasim.com\/blog\/ai-coding-agents-poisoned-issue-prompt-injection\/\">AI coding agents will run a poisoned issue for you<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>model: haiku<\/code> on mechanical commands is the cheapest win in this whole post. Linting, formatting, generating a commit message from a diff \u2014 none of that needs a frontier model. The model reverts when the command finishes; it does not stick to your session or your settings.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Bash injection is the feature that actually matters<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Everything above is plumbing. This is the part that changes what your commands can do.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Prefix a backtick-wrapped shell command with <code>!<\/code> inside a command file and Claude Code runs it <em>before<\/em> the model sees the prompt, then substitutes the output inline. The model does not decide to run <code>git diff<\/code>; the diff is already sitting in its context when it starts.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">## Context\n- Branch: !`git rev-parse --abbrev-ref HEAD`\n- Status: !`git status --short`\n- Failing tests: !`npm test 2>&amp;1 | tail -40`\n\nFix the failing tests above. Change the implementation, not the assertions,\nunless the assertion is provably wrong.<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The difference in reliability is not subtle. A prompt that says &#8220;check the test output and fix the failures&#8221; gives the model a choice about how to gather context, and it will sometimes run the wrong command, sometimes run a narrower subset, sometimes decide it already knows. A prompt with the output pre-injected removes the choice. This is the same principle I keep landing on across every agentic setup: constrain the input, do not rely on a better model. I made that argument at length in <a href=\"https:\/\/abrarqasim.com\/blog\/agentic-coding-why-the-feedback-loop-beats-a-smarter-model\/\">why the feedback loop beats a smarter model<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Two things that bit me. First, whatever you inject counts against your context window, so <code>!`git diff`<\/code> on a 4,000-line refactor will eat the session before Claude writes a line. Pipe through <code>head<\/code> or <code>--stat<\/code>. Second, the shell command has to be in <code>allowed-tools<\/code> or the whole thing fails at expansion time, and the error is easy to misread as the command not existing.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">File references work the same way with <code>@<\/code>. <code>Review @src\/auth\/session.ts against @docs\/auth.md<\/code> pulls both files into the prompt. Useful when the file set is fixed; useless when it is not, because you cannot glob.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Arguments, and a docs inconsistency to watch<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><code>$ARGUMENTS<\/code> captures everything the user typed after the command name. That is the safe one and it behaves the way you expect.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Positional arguments are where I would slow down. Community guides and most examples use <code>$1<\/code> for the first argument and <code>$2<\/code> for the second. Anthropic&#8217;s own SDK page shows an example where <code>\/fix-issue 123 high<\/code> resolves to <code>$0=\"123\"<\/code> and <code>$1=\"high\"<\/code> \u2014 zero-indexed. I have not found a version note explaining the difference, so I stopped guessing: write a throwaway command whose entire body is <code>first=$1 second=$2 zero=$0<\/code>, run it with two known arguments, and read what comes back on your version. Thirty seconds, and it beats debugging a command that silently interpolates the wrong string into a <code>gh<\/code> call.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Worth knowing regardless: if you reference more placeholders than the user supplied, the unmatched ones stay in the prompt as literal text. So a missing argument does not error, it hands Claude a prompt containing the characters <code>$2<\/code>. If your command has optional arguments, say so in the body \u2014 &#8220;if <code>$2<\/code> is empty or still reads as <code>$2<\/code>, default to X.&#8221;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Namespacing: the documented behavior and the real behavior<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You can organize commands into subdirectories:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">.claude\/commands\/\n\u251c\u2500\u2500 frontend\/\n\u2502   \u251c\u2500\u2500 component.md\n\u2502   \u2514\u2500\u2500 style-check.md\n\u251c\u2500\u2500 backend\/\n\u2502   \u2514\u2500\u2500 db-migrate.md\n\u2514\u2500\u2500 review.md<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">What the current docs say happens: the subdirectory shows up in the command&#8217;s description, but the command name is unchanged. <code>frontend\/component.md<\/code> gives you <code>\/component<\/code>, not <code>\/frontend:component<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Older documentation described a colon-namespaced form, <code>\/project:frontend:component<\/code>, and there is an open issue on the Claude Code repo (#2422) from people who tried it, got &#8220;command not found,&#8221; and reasonably assumed they had misconfigured something. If you read a guide that promises colon namespacing, that guide is describing documentation rather than behavior.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Which leaves you with a flat namespace, so two commands with the same filename in different subdirectories collide. Prefix the filenames instead \u2014 <code>fe-component.md<\/code>, <code>be-migrate.md<\/code>. Ugly, works today.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Plugin commands are the exception and are properly namespaced as <code>\/pluginname__commandname<\/code>, which is why installing two plugins that both ship a <code>\/review<\/code> does not break anything.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One more collision to know about: Claude Code ships bundled skills including <code>code-review<\/code> and <code>verify<\/code>. Create <code>.claude\/commands\/code-review.md<\/code> and yours shadows the bundled one silently. The command list shows the name once and you have no indication which one you are running. If you want your own review workflow, give it a different name. I go through what I actually run before merging agent-written code in <a href=\"https:\/\/abrarqasim.com\/blog\/ai-code-review-tools-what-i-run-before-merging-ai-written-prs\/\">this post on AI code review tools<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Restricting tools, and the allowed-tools syntax question<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><code>allowed-tools<\/code> is the guardrail that makes a read-only command actually read-only. <code>allowed-tools: Read, Grep, Glob<\/code> on an audit command means it physically cannot edit a file, which matters more than a prompt that politely asks it not to.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Anthropic&#8217;s documentation examples use comma-separated lists, including for scoped bash permissions like <code>Bash(git add *), Bash(git status *)<\/code>. Some community guides insist the field is space-separated and that commas silently whitelist nothing. I have not been able to reproduce the silent-failure claim, and the official examples use commas, so commas are what I write. But if you ever hit a command that asks for permission on a tool you explicitly allowed, that is the first line to test \u2014 swap the separator before you go looking for anything more exotic.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Scoped bash patterns are the part worth getting right. <code>Bash(git:*)<\/code> allows every git subcommand, including <code>git push --force<\/code>. <code>Bash(git add:*)<\/code> and <code>Bash(git commit:*)<\/code> as separate entries is more typing and considerably narrower. If you want enforcement that survives a command file someone edits later, that belongs in hooks rather than frontmatter \u2014 I covered that setup in <a href=\"https:\/\/abrarqasim.com\/blog\/claude-code-hooks-the-guardrails-i-actually-ship\/\">Claude Code hooks: the guardrails I actually ship<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Running commands without the interactive session<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You do not have to open a session to fire a command. <code>claude -p '\/lint'<\/code> runs it headless and exits, which makes commands usable from aliases, git hooks, and CI.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">alias clint=\"claude -p '\/lint'\"\nalias ccommit=\"claude -p '\/commit'\"<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is where <code>model: haiku<\/code> compounds. A pre-commit alias that runs a Haiku-backed lint-and-fix costs close to nothing and finishes in the time it takes to switch windows. The same alias on a frontier model is a habit you will drop within a week because of the latency.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Two cautions. Headless runs still hit whatever permissions your settings enforce, so a command that needs approval will hang waiting for an answer nobody is there to give \u2014 <code>allowed-tools<\/code> is not optional in this mode. And if you are wiring this into CI, remember the output is a full model response, not a exit code you can branch on, unless your command explicitly instructs the model to end with a machine-readable line.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What I would actually build first<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">If you are starting from zero, resist writing fifteen commands in an afternoon. Most of them will never fire. The ones that stuck for me share a shape: repetitive, mechanical, and dependent on context I would otherwise have to paste in by hand.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>A commit command.<\/strong> Injects <code>!`git diff --cached`<\/code> and <code>!`git log --oneline -10`<\/code>, writes a message matching your repo&#8217;s existing style. Haiku. This alone justifies the setup.<\/li><li><strong>A test-fix command.<\/strong> Injects the failing test output, tells Claude to fix implementation rather than assertions. Sonnet, because this needs reasoning.<\/li><li><strong>A read-only audit command.<\/strong> <code>allowed-tools: Read, Grep, Glob<\/code>, pointed at whatever you care about \u2014 dependency drift, missing error handling, unbounded queries.<\/li><li><strong>A CI-failure command.<\/strong> Injects <code>!`gh run view --log-failed`<\/code>. The value is entirely in the injection; a model that has the actual failure log behaves completely differently from one that is guessing.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Notice that three of the four are mostly bash injection and barely any prompt. That is the pattern. The commands that failed for me were the ones where I tried to write a clever prompt and gave the model no fresh context to work from. If you find yourself polishing prose in a command file, you are probably optimizing the wrong half of it. The same lesson shows up in how I trimmed model output generally, which I wrote about in <a href=\"https:\/\/abrarqasim.com\/blog\/i-stopped-claude-from-rambling-and-cut-my-output-tokens-by-75\/\">how I stopped Claude from rambling<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Frequently asked questions<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Where do I put custom slash commands in Claude Code?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Project commands go in <code>.claude\/commands\/name.md<\/code> and are shared with everyone on the repo. Personal commands go in <code>~\/.claude\/commands\/name.md<\/code> and follow you across projects. Anthropic now treats both as the legacy format and recommends <code>.claude\/skills\/name\/SKILL.md<\/code> and <code>~\/.claude\/skills\/name\/SKILL.md<\/code> instead. Both formats produce the same <code>\/name<\/code> command and the CLI still supports both.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What is the difference between a slash command and a skill?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Functionally they have converged. A file at <code>.claude\/commands\/deploy.md<\/code> and a skill at <code>.claude\/skills\/deploy\/SKILL.md<\/code> both create <code>\/deploy<\/code> and both accept the same frontmatter. The skill format is the current recommendation and carries the expectation that Claude may invoke it autonomously when it judges the skill relevant, while a command file reads more like something you type. If you want the type-only behavior explicitly, set <code>disable-model-invocation: true<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Can Claude run my custom slash commands by itself?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes, through the SlashCommand tool, but only for user-defined commands that have the <code>description<\/code> frontmatter field populated. Built-in commands like <code>\/compact<\/code> and <code>\/init<\/code> are not available to it. To see exactly which of your commands are eligible on your version, run <code>claude --debug<\/code> and trigger a query. To block a specific command from autonomous invocation, add <code>disable-model-invocation: true<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How do I pass arguments to a Claude Code slash command?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use <code>$ARGUMENTS<\/code> in the command body to capture everything typed after the command name. Positional placeholders also work, but the indexing is inconsistent between sources: community guides use <code>$1<\/code> for the first argument, while Anthropic&#8217;s SDK documentation shows an example resolving the first argument to <code>$0<\/code>. Test it on your version with a throwaway command before relying on it. Placeholders with no matching argument are left in the prompt as literal text rather than erroring.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why is my namespaced slash command not found?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Because the colon-namespaced form does not work as older documentation described it. A file at <code>.claude\/commands\/frontend\/component.md<\/code> creates <code>\/component<\/code>, not <code>\/project:frontend:component<\/code>. The subdirectory affects the description shown in autocomplete, not the command name. This is tracked as an open issue on the Claude Code repository. Since the namespace is effectively flat, prefix filenames to avoid collisions between subdirectories.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How do I run a shell command inside a slash command?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Prefix a backtick-wrapped command with <code>!<\/code> in the command body, so that a status check is written as an exclamation mark followed by <code>git status<\/code> wrapped in backticks. Claude Code executes it before the model sees the prompt and substitutes the output inline. The command must be permitted by <code>allowed-tools<\/code> or expansion fails. Keep the output small, because injected text counts against your context window \u2014 a full <code>git diff<\/code> on a large change can consume the session before any work happens.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Can I use a cheaper model for a specific slash command?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Yes. Set <code>model<\/code> in the frontmatter to <code>haiku<\/code>, <code>sonnet<\/code>, a full model ID, or <code>inherit<\/code>. The setting applies only while the command runs and reverts afterward; it is not written to your settings. Mechanical commands like linting, formatting, and commit-message generation run well on the cheapest tier, and the latency difference is what determines whether you keep using them.<\/p>\n\n\n\n<script type=\"application\/ld+json\">\n{\n  \"@context\": \"https:\/\/schema.org\",\n  \"@type\": \"FAQPage\",\n  \"mainEntity\": [\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Where do I put custom slash commands in Claude Code?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Project commands go in .claude\/commands\/name.md and are shared with everyone on the repo. Personal commands go in ~\/.claude\/commands\/name.md and follow you across projects. Anthropic now treats both as the legacy format and recommends .claude\/skills\/name\/SKILL.md and ~\/.claude\/skills\/name\/SKILL.md instead. Both formats produce the same \/name command and the CLI still supports both.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"What is the difference between a slash command and a skill?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Functionally they have converged. A file at .claude\/commands\/deploy.md and a skill at .claude\/skills\/deploy\/SKILL.md both create \/deploy and both accept the same frontmatter. The skill format is the current recommendation and carries the expectation that Claude may invoke it autonomously when it judges the skill relevant, while a command file reads more like something you type. If you want the type-only behavior explicitly, set disable-model-invocation: true.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Can Claude run my custom slash commands by itself?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Yes, through the SlashCommand tool, but only for user-defined commands that have the description frontmatter field populated. Built-in commands like \/compact and \/init are not available to it. To see exactly which of your commands are eligible on your version, run claude --debug and trigger a query. To block a specific command from autonomous invocation, add disable-model-invocation: true.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"How do I pass arguments to a Claude Code slash command?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Use $ARGUMENTS in the command body to capture everything typed after the command name. Positional placeholders also work, but the indexing is inconsistent between sources: community guides use $1 for the first argument, while Anthropic's SDK documentation shows an example resolving the first argument to $0. Test it on your version with a throwaway command before relying on it. Placeholders with no matching argument are left in the prompt as literal text rather than erroring.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Why is my namespaced slash command not found?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Because the colon-namespaced form does not work as older documentation described it. A file at .claude\/commands\/frontend\/component.md creates \/component, not \/project:frontend:component. The subdirectory affects the description shown in autocomplete, not the command name. This is tracked as an open issue on the Claude Code repository. Since the namespace is effectively flat, prefix filenames to avoid collisions between subdirectories.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"How do I run a shell command inside a slash command?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Prefix a backtick-wrapped command with an exclamation mark in the command body. Claude Code executes it before the model sees the prompt and substitutes the output inline. The command must be permitted by allowed-tools or expansion fails. Keep the output small, because injected text counts against your context window: a full git diff on a large change can consume the session before any work happens.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"Can I use a cheaper model for a specific slash command?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Yes. Set model in the frontmatter to haiku, sonnet, a full model ID, or inherit. The setting applies only while the command runs and reverts afterward; it is not written to your settings. Mechanical commands like linting, formatting, and commit-message generation run well on the cheapest tier, and the latency difference is what determines whether you keep using them.\"\n      }\n    }\n  ]\n}\n<\/script>\n\n","protected":false},"excerpt":{"rendered":"<p>The built-in commands worth memorizing, how custom commands actually work in 2026, and the gotchas nobody documents: namespacing, argument indexing, and the SlashCommand tool.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Claude Code Slash Commands (2026 Guide)","rank_math_description":"Claude code slash commands in 2026: the built-ins worth memorizing, custom command frontmatter, bash injection, and the namespacing bug that wastes an hour.","rank_math_focus_keyword":"claude code slash commands","rank_math_canonical_url":"","rank_math_robots":"","footnotes":""},"categories":[4],"tags":[],"class_list":["post-517","post","type-post","status-publish","format-standard","hentry","category-ai"],"_links":{"self":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts\/517","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/comments?post=517"}],"version-history":[{"count":0,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts\/517\/revisions"}],"wp:attachment":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/media?parent=517"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/categories?post=517"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/tags?post=517"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}