Slash Command 进阶:让指令读懂上下文

用 ! 前缀在 command 中执行 shell 命令,自动注入 diff、文件内容、测试结果,让 Claude 拿到真实信息而非模糊描述


大多数人的 slash command 长这样:

审查当前改动的代码质量,给出具体建议。

这能用,但有个根本限制:Claude 需要自己去推断"当前改动是什么"。如果你的 command 能主动把上下文注入进去,效果会完全不同。

本文讲的是如何让 command 变成"有眼睛"的指令——在触发时自动读取文件内容、git 状态、项目信息,把这些塞进提示词里,让 Claude 不用猜。


核心机制:!`命令` 注入 Shell 输出

自定义 slash command 支持在提示词里内嵌 shell 命令,语法是用反引号包裹命令并在前面加感叹号:!`命令`。触发时命令先执行,输出替换掉占位符,Claude 收到的是最终拼好的提示词。

以下是当前的 git diff:

!`git diff HEAD`

请审查这些改动,重点关注逻辑错误和安全问题。

多行命令用围栏代码块形式,开头写 ```!

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

触发 /review 时,Claude 收到的实际内容是:

以下是当前的 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
...

请审查这些改动,重点关注逻辑错误和安全问题。

不需要你手动复制粘贴,command 触发的瞬间,diff 已经在里面了。


实用模式

注入当前文件内容

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

以下是当前文件的完整内容:

!`cat $ARGUMENTS`

找出这个文件里所有潜在的性能问题,给出具体的行号和改法。

用法:/perf app/models/order.rb

$ARGUMENTS 接收文件路径,cat 读取内容注入进来。Claude 拿到的是真实代码,不是"请看当前文件"这种模糊描述。

allowed-tools 在 frontmatter 里预授权了 cat 命令,触发时不会弹权限确认。没有这一行的话,每次运行都要手动点"允许"。下面的例子都需要按同样方式声明。

注入 git 状态

当前分支和改动状态:

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

根据以上信息,生成一份简洁的 PR 描述,包含改动内容和动机。

这个 /pr command 不需要你描述"我改了什么",它自己去读。

注入项目特有信息

项目技术栈:

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

当前数据库结构(关键表):

!`head -100 db/schema.rb`

根据以上背景,为 $ARGUMENTS 编写符合项目规范的迁移脚本。

把项目背景信息提前写进 .claude/context/ 目录,command 触发时按需读取,不用每次重复描述项目情况。

动态读取测试结果

最近一次测试运行结果:

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

以上是失败的测试。分析根本原因,给出修复方案,不要改测试本身。

/fix-tests 触发时直接跑测试、拿结果,Claude 看到的是真实的报错信息。


完整示例:上下文感知的代码审查

把上面这些组合起来,/review 可以做到相当精准:

---
description: 审查当前改动,自动注入 diff 和相关背景
allowed-tools: Bash(git diff:*), Bash(cat:*)
---

## 本次改动

!`git diff HEAD`

## 改动涉及的文件列表

!`git diff HEAD --name-only`

## 项目编码规范摘要

!`cat .claude/context/coding-standards.md 2>/dev/null || echo "(无规范文件)"`

---

请审查以上改动:

1. 逻辑正确性:有没有边界条件没处理、逻辑错误
2. 安全问题:SQL 注入、权限校验、敏感信息暴露
3. 规范符合度:是否符合项目编码规范
4. 可读性:命名、注释、结构

每个问题给出文件名、行号、具体建议。没有问题就说没有,不要凑数。

这个 command 不需要你做任何准备——触发时,diff、文件列表、编码规范全部自动注入。


处理命令失败的情况

Shell 命令可能失败(文件不存在、命令不在 PATH 里等)。用 || 提供默认值,避免 command 因此中断:

!`git diff HEAD 2>/dev/null || echo "(无 git 改动或不在 git 仓库中)"`
!`cat .env.example 2>/dev/null || echo "(无 .env.example 文件)"`
!`which rspec > /dev/null 2>&1 && bundle exec rspec --dry-run 2>&1 | head -20 || echo "(未检测到 RSpec)"`

命令失败时,Claude 收到的是说明文字而不是空白,它可以据此调整回答策略。


性能考虑:不要注入太多

! 命令的输出全部进入 context window。几个常见的坑:

不要 cat 整个大文件

# 危险:可能注入几万行
!`cat db/schema.rb`

# 更好:只取需要的部分
!`grep -A 5 "create_table \"orders\"" db/schema.rb`

日志文件加行数限制

!`tail -50 log/development.log`

测试输出截断

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

context 越精准,Claude 的回答越好。把整个代码库塞进去不会让它更聪明,只会让它迷失在噪音里。


与 CLAUDE.md 的分工

常见的问题:项目背景信息应该放 CLAUDE.md 还是用 ! 动态注入?

判断标准:

信息类型 放哪里
每次都需要的项目背景(技术栈、目录结构、规范) CLAUDE.md
随时间变化的状态(当前 diff、测试结果、文件内容) ! 动态注入
特定任务才需要的背景(某个模块的设计说明) !cat 按需读取

CLAUDE.md 是持久的背景知识,! 是任务时的即时快照。两者互补,不要重复。


一个完整的 .claude/commands/ 目录

.claude/
├── commands/
│   ├── review.md        # 审查:注入 diff + 规范
│   ├── test.md          # 写测试:注入目标文件
│   ├── fix-tests.md     # 修测试:注入失败结果
│   ├── pr.md            # PR 描述:注入 git log + status
│   ├── explain.md       # 解释代码:注入文件内容
│   └── migrate.md       # 写迁移:注入 schema 片段
├── context/
│   ├── stack.md         # 技术栈说明
│   └── coding-standards.md  # 编码规范
└── settings.json

context/ 目录存放静态背景文件,供各个 command 按需读取。这套结构可以直接提交进 git,团队共享。


效果对比

静态 command

请审查当前代码改动。

Claude 需要先问"改动在哪里",或者自己去找,结果不稳定。

上下文感知 command

这是 diff(附实际内容)、这是相关规范(附实际内容),请审查。

Claude 直接开始分析,回答精准,不废话,不需要来回确认。

区别不在于 Claude 的能力,而在于你给了它多少真实信息。