# How to Make Custom Slash Commands in Claude Code: A Complete Guide to the Skills Architecture

> Learn how to make custom slash commands in Claude Code by building Skills. This guide covers the Skills architecture and implementation for custom commands, enhancing your workflow.

- Repository: [Luong NGUYEN/claude-howto](https://github.com/luongnv89/claude-howto)
- Tags: how-to-guide
- Published: 2026-03-30

---

**Custom slash commands in Claude Code are implemented as Skills—directories containing a [`SKILL.md`](https://github.com/luongnv89/claude-howto/blob/main/SKILL.md) file with YAML front-matter and instructions—stored in `.claude/skills/<name>/` and invoked by typing `/<command-name>`.**

Claude Code supports custom slash commands through a unified Skills architecture that replaced the legacy `.claude/commands/` directory system. According to the `luongnv89/claude-howto` repository, these user-defined Skills allow you to package scripts, templates, and reusable instructions into directory-based modules that Claude can invoke automatically or on demand. This guide covers how to create, configure, and migrate custom slash commands using the modern Skills system.

## Understanding the Skills Architecture

Claude Code resolves slash commands through a hierarchical search order defined in [`01-slash-commands/README.md`](https://github.com/luongnv89/claude-howto/blob/main/01-slash-commands/README.md). When you type `/command-name`, the system checks:

1. **Built-in commands** — Native functionality implemented in Claude Code.
2. **Skills** — Looks for `.claude/skills/<name>/SKILL.md`.
3. **Legacy commands** — Falls back to `.claude/commands/<name>.md` for backward compatibility.
4. **Plugins and MCP prompts** — Loads external extensions.

The **Skills** architecture offers significant advantages over the legacy system. Because Skills are directory-based, you can bundle auxiliary files—such as scripts, templates, or reference documentation—alongside the main [`SKILL.md`](https://github.com/luongnv89/claude-howto/blob/main/SKILL.md) file. Skills also support **auto-invocation**, where Claude automatically calls a Skill when its description matches the current context, and **isolated execution** through sub-agents.

## Creating Your First Custom Skill

To create a custom slash command, create a directory under `.claude/skills/` and add a [`SKILL.md`](https://github.com/luongnv89/claude-howto/blob/main/SKILL.md) file with YAML front-matter followed by plain-text instructions.

```bash

# Create the skill directory structure

mkdir -p .claude/skills/hello-world

# Create the SKILL.md file

cat > .claude/skills/hello-world/SKILL.md <<'EOF'
---
name: hello-world
description: Say hello to the user
---

# Hello World

When invoked, simply respond with:

```

Hello, Claude Code user! 👋

```

EOF

```

Invoke the command by typing `/hello-world` in the Claude Code interface. The `name` field in the front-matter determines the command trigger, while the `description` helps Claude decide when to suggest the Skill during auto-invocation scenarios.

## Configuring Skill Behavior with Front-Matter

The [`SKILL.md`](https://github.com/luongnv89/claude-howto/blob/main/SKILL.md) front-matter controls execution permissions, argument handling, and runtime environment. According to the reference table in [`01-slash-commands/README.md`](https://github.com/luongnv89/claude-howto/blob/main/01-slash-commands/README.md), these fields configure custom slash commands:

| Field | Purpose | Default |
|-------|---------|---------|
| **name** | Command trigger that becomes `/<name>` | Directory name |
| **description** | Human-readable hint for auto-invocation | First paragraph of content |
| **argument-hint** | Template shown during auto-completion | None |
| **allowed-tools** | Tool whitelist (e.g., `Bash(git *)`) | Inherits global permissions |
| **model** | Explicit model override | Inherits session model |
| **disable-model-invocation** | When `true`, only users can run the command | `false` |
| **user-invocable** | Hide from the `/` menu when `false` | `true` |
| **context** | Set to `fork` to execute in an isolated sub-agent | None |
| **agent** | Agent type used when `context: fork` | `general-purpose` |

Configure these fields at the top of your [`SKILL.md`](https://github.com/luongnv89/claude-howto/blob/main/SKILL.md) file between triple dashes:

```yaml
---
name: review-pr
description: Review a pull request with specified priority
argument-hint: "<pr-number> <priority-level>"
allowed-tools: Bash(gh *), Read()
---

```

## Handling Arguments and Dynamic Context

Skills consume arguments through placeholder variables. Use **positional arguments** (`$0`, `$1`, `$2`) to capture specific tokens, or **`$ARGUMENTS`** to receive the entire argument string as a single value.

```yaml
---
name: assign-issue
description: Assign a GitHub issue to a user
argument-hint: "<issue-id> <github-username>"
allowed-tools: Bash(gh *)
---
Assign issue #$0 to @$1 using the GitHub CLI:

!`gh issue edit $0 --assignee $1`

```

When invoked as `/assign-issue 1234 alice`, `$0` resolves to `"1234"` and `$1` resolves to `"alice"`.

For dynamic context, embed shell command outputs directly into the prompt using the back-tick syntax: `` !`command` ``. Claude injects the command result before processing the Skill.

```yaml
---
name: commit
allowed-tools: Bash(git *)
---

## Context

- Current git status: !`git status`
- Current branch:   !`git branch --show-current`

## Your task

Create a single commit based on the above changes.

```

You can also reference repository files with the `@` prefix (e.g., `Review the implementation in @src/utils/helpers.js`).

## Isolated Execution and Security Controls

To prevent a custom command from modifying your main session's state, set **`context: fork`** in the front-matter. This executes the Skill in a separate sub-agent with its own file system view.

```yaml
---
name: run-tests
description: Execute the test suite in a fresh environment
context: fork
agent: general-purpose
allowed-tools: Bash(npm test)
---
Run the project's test suite and report any failures:

!`npm test --silent`

```

Use **`allowed-tools`** to restrict which tools the Skill can access—such as `Bash(git *)` to permit only Git commands—or set **`disable-model-invocation: true`** to ensure only human users (not Claude itself) can trigger the command.

## Migrating from Legacy Commands

If you have existing custom commands in `.claude/commands/<name>.md`, migrate them to the Skills architecture by moving the file to a directory-based structure.

Migration steps from [`01-slash-commands/README.md`](https://github.com/luongnv89/claude-howto/blob/main/01-slash-commands/README.md):

```bash

# Create the new skill directory

mkdir -p .claude/skills/optimize

# Copy the legacy command file

cp .claude/commands/optimize.md .claude/skills/optimize/SKILL.md

```

After migration, `/optimize` resolves to the Skill version. If both locations exist, the Skills version takes precedence in the search order.

## Summary

- **Custom slash commands** in Claude Code are created as Skills in `.claude/skills/<name>/SKILL.md`.
- **Front-matter fields** like `name`, `description`, `allowed-tools`, and `context` control execution behavior and permissions.
- **Arguments** are passed via `$0`, `$1`, or `$ARGUMENTS` placeholders, while dynamic context uses `` !`command` `` syntax.
- **Isolated execution** is achieved with `context: fork`, running the Skill in a separate sub-agent.
- **Legacy commands** from `.claude/commands/` should be migrated to the Skills architecture for full feature support.

## Frequently Asked Questions

### Where should I store custom slash commands in Claude Code?

Store custom slash commands in `.claude/skills/<command-name>/SKILL.md`. This directory-based structure replaces the legacy `.claude/commands/<name>.md` format and supports bundling additional files, isolated execution, and advanced front-matter configuration.

### What is the difference between Skills and legacy commands?

**Skills** are directory-based packages that support front-matter metadata, auto-invocation, isolated sub-agents via `context: fork`, and bundled resources. **Legacy commands** are single Markdown files in `.claude/commands/` that lack these advanced features but remain supported for backward compatibility.

### How do I pass arguments to a custom slash command?

Use positional placeholders like `$0` and `$1` in your [`SKILL.md`](https://github.com/luongnv89/claude-howto/blob/main/SKILL.md) content, or `$ARGUMENTS` to capture the entire input string. Define the expected format in the `argument-hint` front-matter field to provide auto-completion guidance when users type the command.

### Can I restrict which tools a custom command can access?

Yes. Set the `allowed-tools` front-matter field to a comma-separated list of permitted tools, such as `Bash(git *), Read()` to allow only Git operations and file reading. This creates a sandboxed execution environment for the specific slash command.