Advanced Slash Commands: Making Your Commands Context-Aware

Use the ! prefix to run shell commands inside slash commands, automatically injecting diffs, file contents, and test results so Claude gets real information instead of vague descriptions


Most people's slash commands look like this:

Review the quality of the current code changes and give specific suggestions.

It works, but there's a fundamental limitation: Claude has to figure out on its own what "the current changes" actually are. If your command can proactively inject the context, the result is completely different.

This post is about turning a command into one that "has eyes" — it automatically reads file contents, git status, and project info at trigger time, stuffs them into the prompt, and saves Claude from guessing.


The core mechanism: !`command` injects shell output

Custom slash commands let you embed shell commands inside the prompt. The syntax is to wrap the command in backticks and prefix it with an exclamation mark: !`command`. When triggered, the command runs first, its output replaces the placeholder, and what Claude receives is the fully assembled prompt.

Here is the current git diff:

!`git diff HEAD`

Please review these changes, focusing on logic errors and security issues.

For multi-line commands, use a fenced code block starting with ```!:

```!
node --version
git status --short
```

When you trigger /review, what Claude actually sees is:

Here is the current git diff:

diff --git a/app/models/user.rb b/app/models/user.rb
index 3a2f1c8..9b4e2d1 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -12,6 +12,9 @@ class User < ApplicationRecord
...

Please review these changes, focusing on logic errors and security issues.

No manual copy-paste. The moment the command fires, the diff is already inside.


Useful patterns

Inject the contents of a file

---
allowed-tools: Bash(cat:*)
---

Here is the full content of the current file:

!`cat $ARGUMENTS`

Find every potential performance issue in this file and give specific line numbers and fixes.

Usage: /perf app/models/order.rb

$ARGUMENTS receives the file path, and cat reads the content into the prompt. Claude gets the actual code, not a vague "please look at the current file."

allowed-tools in the frontmatter pre-authorizes cat, so triggering the command doesn't pop up a permission prompt. Without this line, you'd have to click "Allow" every time. The examples below all need the same kind of declaration.

Inject git status

Current branch and change status:

!`git status --short`
!`git log --oneline -10`

Based on the above, generate a concise PR description that covers what changed and why.

This /pr command doesn't need you to describe "what I changed" — it reads it itself.

Inject project-specific info

Project tech stack:

!`cat .claude/context/stack.md`

Current database schema (key tables):

!`head -100 db/schema.rb`

Based on the above, write a migration script for $ARGUMENTS that follows the project's conventions.

Put background info into .claude/context/ ahead of time, and let the command pull it in on demand. No need to re-explain the project every time.

Pull in test results dynamically

Latest test run results:

!`bundle exec rspec --format progress 2>&1 | tail -30`

Above are the failing tests. Analyze the root cause and give a fix — don't change the tests themselves.

When /fix-tests fires, it runs the tests and grabs the output directly. Claude sees the real error messages.


Full example: a context-aware code review

Combine the above and /review becomes pretty sharp:

---
description: Review current changes, automatically injecting diff and relevant context
allowed-tools: Bash(git diff:*), Bash(cat:*)
---

## Current changes

!`git diff HEAD`

## Files touched

!`git diff HEAD --name-only`

## Project coding standards (summary)

!`cat .claude/context/coding-standards.md 2>/dev/null || echo "(no standards file)"`

---

Please review the changes above:

1. Correctness: any unhandled edge cases or logic errors
2. Security: SQL injection, authorization checks, exposed secrets
3. Standards: does it follow the project's coding standards
4. Readability: naming, comments, structure

For each issue, give the file, line number, and a specific suggestion. If there are no issues, say so — don't pad.

This command needs zero prep on your part — diff, file list, and coding standards are all injected automatically when it fires.


Handling command failures

Shell commands can fail (file missing, command not on PATH, etc.). Use || to provide a fallback so the command doesn't blow up:

!`git diff HEAD 2>/dev/null || echo "(no git changes or not in a git repo)"`
!`cat .env.example 2>/dev/null || echo "(no .env.example file)"`
!`which rspec > /dev/null 2>&1 && bundle exec rspec --dry-run 2>&1 | head -20 || echo "(RSpec not detected)"`

When a command fails, Claude receives an explanatory message instead of a blank, and can adjust its strategy accordingly.


Performance: don't inject too much

The output of ! commands all goes into the context window. A few common traps:

Don't cat a huge file

# Dangerous: could inject tens of thousands of lines
!`cat db/schema.rb`

# Better: only grab what you need
!`grep -A 5 "create_table \"orders\"" db/schema.rb`

Cap log files with line limits

!`tail -50 log/development.log`

Truncate test output

!`bundle exec rspec 2>&1 | tail -40`

The tighter your context, the better Claude's answers. Stuffing the entire codebase in doesn't make it smarter — it just buries it in noise.


How this divides labor with CLAUDE.md

A common question: should project background info live in CLAUDE.md, or get injected dynamically with !?

A simple rule:

Type of info Where it goes
Project background needed every time (tech stack, layout, conventions) CLAUDE.md
State that changes over time (current diff, test results, file contents) ! dynamic injection
Background only specific tasks need (design notes for a particular module) !cat on demand

CLAUDE.md is persistent background knowledge; ! is a real-time snapshot at task time. They complement each other — don't duplicate.


A complete .claude/commands/ directory

.claude/
├── commands/
│   ├── review.md        # Review: inject diff + standards
│   ├── test.md          # Write tests: inject target file
│   ├── fix-tests.md     # Fix tests: inject failures
│   ├── pr.md            # PR description: inject git log + status
│   ├── explain.md       # Explain code: inject file content
│   └── migrate.md       # Migrations: inject schema fragment
├── context/
│   ├── stack.md         # Tech stack notes
│   └── coding-standards.md  # Coding standards
└── settings.json

The context/ directory holds static background files for the various commands to read on demand. This whole structure can go straight into git and be shared across the team.


Side-by-side

Static command:

Please review the current code changes.

Claude has to ask "where are the changes?" or hunt for them itself, and the result is hit-or-miss.

Context-aware command:

Here's the diff (actual content), here are the relevant standards (actual content), please review.

Claude jumps straight into analysis. Answers are sharp, no fluff, no back-and-forth.

The difference isn't in Claude's ability — it's in how much real information you handed it.