Claude命令作为参数化工作流模板,执行分加载、预处理、注入、模型执行四阶段。13个前端字段分身份、可见性、运行时资源、上下文边界、动态行为五类。动态注入包括!shell命令输出、@文件内容、$ARGUMENTS用户参数,三者组合使命令从按钮进化为可编程入口。
!、@、$ARGUMENTS上一篇把 release-notes 生成器拆完,最后留下一句“下一篇把 Commands 单独拎出来细讲”——这一篇就来还这个债。

长期稳定更新的攒劲资源: >>>点此立即查看<<<
但开讲之前,得先解释一件事:为什么 Command 这个看起来“简单”的东西,值得单开一篇?
原因在于,当你真的用它来写工作流时,会发现 Command 一半的能力都藏在它如何把外部世界拼接进提示词这件事上:
!shell-command`` 把命令输出灌进来@filepath 把文件内容灌进来$ARGUMENTS 把用户参数拼进来paths 让它只对某些文件激活context: fork 让它去开子上下文这些字段每一个都不复杂,但组合起来,却能让 Command 从一个“按钮”进化为“参数化的工作流模板”。这正是上一篇没展开的部分。
资深架构师看 Command,视角应该跟看 Makefile target 或者 shell script with frontmatter 一样——它是一种 declarative pipeline 的入口描述符,而不是一段聊天提示。这一篇,我们就按这个视角来拆解。
我们先画出 Command 的执行链路。理解了这条链路,再看字段时,每个字段在哪一步起作用就一目了然了。
sequenceDiagram
actor User as 用户
participant CLI as claude CLI
participant Loader as Command Loader
participant Tools as 内置工具
(Bash/Read/...)
participant Model as Claude 模型
participant Tool2 as 模型调度的工具
User->>CLI: 输入 /release-notes-crafter
CLI->>Loader: 解析 / 触发
Loader->>Loader: 1. 读 .claude/commands/release-notes-crafter.md
Loader->>Loader: 2. 解析 frontmatter
Loader->>Tools: 3. 执行 ! 块 (shell)
Tools-->>Loader: stdout 结果
Loader->>Loader: 4. 内联 @ 文件引用
Loader->>Loader: 5. 替换 $ARGUMENTS / $0..$N
Loader->>Model: 拼好的最终 prompt + frontmatter (model/effort/allowed-tools)
Model->>Tool2: 按 prompt 指示调用 AskUserQuestion / Agent / Skill
Tool2-->>Model: 工具结果
Model-->>User: 最终响应
这条链路里有四个关键的时机,需要区分清楚:
| 阶段 | 谁干 | 关键动作 |
|---|---|---|
| 加载 | Loader | 读取文件、解析 frontmatter |
| 预处理 | Loader | 执行 !、内联 @、替换 $ARGUMENTS |
| 注入 | CLI | 把整段提示词作为用户消息塞进当前会话 |
| 执行 | Model | 按提示词调度工具 |
这里有一个很多人不知道的关键规则:! 块是在 Loader 阶段由本机执行的,$ARGUMENTS 也是在这里被替换的。这意味着,模型看到的现场信息,是已经由 Loader “预处理”过的。而模型自己再去调用 Bash() 拿到的东西,在输出格式或环境上可能就跟 Loader 直接灌进来的不一样。
打个比方,如果你在 command 里用 !git log`` 拿到了带颜色的日志,然后模型再自己跑 Bash("git log"),它拿到的可能是纯文本。这个差异在某些场景下(比如解析输出)会直接导致结果出错。很多人在写多个 command 后,都会在某个莫名其妙的 bug 里撞上这个点,才真正意识到“原来 Loader 和模型不是同一个执行者”。
在最新版(v2.1.96)中,Command 的 frontmatter 共有 13 个字段。一个完整的 YAML 示意如下:
---
name: my-command # 显示名 + /slash 标识,缺省取目录/文件名
description: 做某件有用的事 # 自动补全 + 语义匹配(如果允许)
argument-hint: [issue-number] # 自动补全时的参数 placeholder
disable-model-invocation: false # 是否禁止模型自动触发
user-invocable: true # 是否在 / 菜单出现
paths: "src/**/*.ts" # glob,命中文件才激活(可逗号分隔或列表)
allowed-tools: Read, Edit, Bash(gh *) # 激活时不需要权限确认的工具
model: sonnet # 运行时模型
effort: high # 运行时 effort 等级
context: fork # 设为 fork 则跑在独立子上下文
agent: general-purpose # context: fork 时的子 agent 类型
shell: bash # !`cmd` 块用什么 shell(bash/powershell)
hooks: # 仅作用于本 command 的 lifecycle hooks
PostToolUse:
- matcher: "Edit"
hooks: [...]
---
这 13 个字段不是平铺的,按“控制什么”可以分成五类:
root((Command 13 字段))
身份
name
description
argument-hint
可见性与触发
user-invocable
disable-model-invocation
paths
运行时资源
model
effort
allowed-tools
上下文边界
context
agent
动态行为
shell
hooks
下面挑几个容易踩雷的单独说。
name 缺省、显式哪个对?name 缺省时会取目录或文件名。比如 .claude/commands/release-notes-crafter.md 没写 name,slash 自动就是 /release-notes-crafter。正常情况下省着写就好,只有在 plugin marketplace 命名冲突,或中文目录名撞到 unicode 时才需要显式覆盖。
真正不可省的字段是 description——它直接影响自动补全是否展示,以及模型是否能匹配到。
description 决定生死---
description: 提交并 push 当前分支,再开 PR
---
description 是用户在 / 菜单里看到的一行字,也是模型进行语义匹配(如果 disable-model-invocation 没关)的依据。没写 description 的 command 实际上是“半盲”状态——用户能通过 /name 触发,但永远不会被自动建议。
写法上有两个常见的反模式:
| 反模式 | 例子 | 问题 |
|---|---|---|
| 太短 | description: commit |
用户看不懂、模型匹配不准 |
| 太长 | description: A comprehensive workflow that takes... |
占自动补全空间,反而不想点 |
一个折中的写法是:一个动词 + 一个目标 + 一个约束。例如“提交并 push 当前分支并开 PR”——动词“提交+push+开”,目标“PR”,约束“当前分支”,清晰明了。
disable-model-invocation:把按钮锁死成“只能手按”默认情况下,Command 的 description 会进入模型的可调用列表。但 Command 的设计哲学本身就是“用户主动触发的入口”。因此,很多团队会把所有 commands 默认设置 disable-model-invocation: true:
---
description: 提交并开 PR
disable-model-invocation: true
---
它的效果是:
/commit-and-pr,正常触发。这个开关跟 user-invocable: false 是一对镜像:
| 字段 | 用户能 / 触发? |
模型能自动触发? | |
|---|---|---|---|
| 默认 | |||
user-invocable: false |
|||
disable-model-invocation: true |
|||
| 都开 | (这种一般是 paths 触发的“知识背景”) |
这四个象限互不矛盾,根据需求选择即可。
allowed-tools:本地小作用域的权限控制allowed-tools: Read, Edit, Bash(gh *)
这里列出的工具,在该 command 激活期间不会触发权限确认。语法支持精确到子命令:
Bash(gh *):只放行 gh 开头的 bash 命令Bash(npm test):只放行字面 npm testBash:所有 Bash(权限最大,尽量少用)
Read、Edit、Write:纯工具名需要注意的是allowed-tools 是叠加在全局 settings 之上的本地白名单。它不会扩展全局 deny 规则——deny 永远是最高优先级。
model 和 effort:分层用模型省钱上一篇讲过 release-notes-crafter 用 model: haiku 是因为 command 只编排不算账。这里再补充一点:
---
description: 起草 release notes 草稿
model: opus
effort: high
---
某些写作类 command 则反过来——内容质量重要,那就让 opus 以高 effort 上。 effort 等级 low/medium/high/max 之间是 token 消耗倍数关系(max 大概是 low 的 8x),所以默认值不写就好,只在特殊场景才显式拉满。
!、@、$ARGUMENTS这是 Command 区别于“普通提示词”的核心所在。三种注入分别处理“shell 输出、文件内容、用户参数”。
!shell-command`` —— shell 输出注入---
description: 提交工作流——基于当前 diff 生成 commit message
allowed-tools: Bash(git *), Read, Edit
---
# 提交工作流
## 当前 git status
!`git status --short`
## 暂存区 diff
!`git diff --staged`
## 最近 5 次提交
!`git log --oneline -5`
请基于以上信息:
1. 写一条 conventional commits 风格的 commit message
2. 展示给我确认
3. 确认后执行 git commit
加载这个 command 时,三个 ! 块会被先执行,stdout 替换原位置,再把整段内容塞给模型。模型看到的是已经填好的现场,省了它再去 Bash 一遍。
这里有几个要点:
! 块里只能跑一行,多行需要用 && 连接。shell: powershell 时,语法会变成 PowerShell 表达式。! 块的输出不计入 allowed-tools 检查,因为它是在 Loader 阶段执行的。一个常见的错误是把交互式命令放进去,比如 !vim file.md``,这会让 Loader 卡住。Loader 期望的是一个“跑完即返回 stdout”的一次性命令。
@filepath —— 文件内容注入---
description: 根据规范 review 当前 PR
---
请按照以下规范检查暂存改动:
@docs/CODE_REVIEW_CHECKLIST.md
当前改动:
!`git diff origin/main`
@ 在 Loader 阶段把文件逐字内联进 prompt——它不是让模型去 Read,而是预先填好。
高级工程师立刻会关心的几件事:
@ 不截断、不压缩、不做 summary,100KB 的规范会原样塞进来,token 全计费。大文件更适合让模型自己去 Read——它读到一半发现不相关会自己退出;@ 更适合 <10KB 的稳定规范文件。@import 语义一致,很多人会记错。Read 比真正省了什么? 不是 token(进了 prompt 都要算),是那一轮 tool call 的延迟,以及“模型先读再决定怎么用”这层认知步骤。对于每次都要读、且都按同样方式理解的规范类文件,直接 @ 内联,模型的注意力从第一个 token 起就落在规范上。@ 只接受一个路径。想拉多个文件就写多条 @ 行;想动态展开,就走 !cat src/**/*.ts`` 之类的 shell 路径。到这里,@ 和 ! 的分工就清晰了:规范用 @(稳定、反复引用),现场用 !(动态、每次不同)。
$ARGUMENTS 和 $0..$N —— 参数最后一种是用户传参。
---
description: 详细 review 一个 PR
argument-hint: [reviewer]
---
# Review PR #$0
主审人:$1
详情:
!`gh pr view $0 --json title,body,files`
按以上规范展开 review,并把发现 @ 给 $1。
调用:
> /pr-review 1234 alice
替换规则:
| 占位符 | 含义 |
|---|---|
$ARGUMENTS |
整个参数串原样(“1234 alice”) |
$0 |
第 1 个参数 |
$1 |
第 2 个参数 |
$N |
第 N+1 个参数 |
把三种注入叠起来,一个 command 就能做到:
graph LR
U[用户 /pr-review 1234] --> L[Loader]
L -->|"$0=1234"| P1[替换参数]
L -->|"!`gh pr view 1234`"| P2[拉 PR 元信息]
L -->|"@CHECKLIST.md"| P3[内联规范]
P1 --> M[最终 prompt]
P2 --> M
P3 --> M
M --> Claude[模型执行 review]
style L fill:#ff9
style M fill:#afa
这正是 Command 不只是“按钮”的原因——它是一个会自己去抓现场的按钮。就像 Makefile 比“双击 .sh 脚本”的优势,在于 $(shell ...) 这种东西。Command 的 !/@/$ARGUMENTS 也是同样的道理。
paths 是个低调但很有用的字段:
---
description: 检查 React 组件的 a11y
paths:
- "src/components/**/*.tsx"
- "src/pages/**/*.tsx"
---
它的效果是:当当前对话的焦点文件匹配 paths 时,这个 command 才进入 Claude 的可用列表。其他时候,它就是隐形的。
它与 disable-model-invocation 的区别在于:
disable-model-invocation: true —— 永远不让模型自动选。paths: "..." —— 只在工作上下文相关时才作为候选。paths 模式允许你为一个仓库写几十个超细分的 command,而不会污染补全。React 组件场景下才出现 React 相关的 command,写 SQL 时它们就自动隐身。它与 Skill 的 paths 字段是同一回事,共享同一套 glob 匹配引擎。
最后一个值得展开的字段是 context: fork:
---
description: 跨整个 monorepo 找出所有未使用的依赖
context: fork
agent: general-purpose
allowed-tools: Read, Glob, Grep, Bash(npm *)
---
# 找出未使用依赖
1. 找所有 package.json
2. 对比每个的 dependencies 和 src 引用
3. 列出未引用的依赖
加上 context: fork 之后,这个 command 不再“内联到主对话”——它跑在一个临时子上下文里,主对话只看到最终结果。
graph TB
subgraph Main[主对话]
M1[用户消息]
M2[Claude 响应]
Trig[/find-unused-deps 触发]
R[只看到最终结果
'5 个未使用依赖:A B C D E']
end
subgraph Forked[fork 出的临时上下文]
F1[读 100 个 package.json]
F2[grep src 引用]
F3[计算差集]
end
Trig -.fork.-> Forked
Forked -.返回结果.-> R
style Forked fill:#fcc
style R fill:#cfc
这就把 Command 升级成了“带 UI 的 Subagent 入口”——用户主动触发,行为却像个 agent。
什么时候用?凡是这个 command 内部需要读大量文件、做大量推理、产生大量中间输出的场景:
不用 context: fork 的话,几百个文件读下来,会直接撑爆主对话的上下文。
讲完自定义 command,我们来看看“官方已经准备好的”。Claude Code v2.1.96 内置了 65 个 slash command,按功能聚类:
root((65 个内置 10 大类))
上下文管理 最值钱
/context
/compact
/clear
模型控制
/plan
/model
/effort
会话管理
/rename
/resume
/rewind
/btw
扩展管理
/agents
/skills
/mcp
/memory
调试兜底
/doctor
/diff
其他 5 类 一年两次
认证 / 配置
项目 / 远程 / 导出
65 个全记得完吗?记不完,也不需要。其中 5 个分类(认证、配置、项目管理、远程、导出)一年用不上两次。完整清单可以查官方的 Slash Commands 文档。下面只挑架构师工作流里影响最大的 5 个分类详细说,也就是上面 mindmap 上半截那五支。
| Command | 实战价值 |
|---|---|
/context |
用色块可视化看上下文是什么被“塞满”的——Read 占多少、Tool 输出占多少。看完就知道下一步该 /compact 哪部分 |
/compact [instructions] |
手动压缩对话,可带 instructions 指导压缩重点。当上下文占用达到 50% 时,手动 compact 是最佳时机 |
/clear |
直接清空。/reset、/new 是它的别名 |
/cost |
看本会话消耗了多少钱 |
/usage |
看当前套餐还剩多少额度 |
/insights |
跑一份分析报告,看你最常用的工具、最花时间的环节 |
/stats |
日常使用统计图 |
/compact 的 instructions 参数被严重低估了。举个例子:
/compact 保留所有对架构决策的讨论,删除工具输出
压缩后剩下的就是决策脉络,工具反复读文件的 token 全没了。在长对话里,这一招经常能救命。
| Command | 实战价值 |
|---|---|
/plan |
进入 plan mode——只读不写,强制 Claude 先出方案再执行 |
/ultraplan |
在云端起草计划,浏览器里审,再回到本地执行 |
/model |
切换模型,左右箭头调 effort |
/effort |
直接改 effort 不换模型 |
/fast |
fast mode 切换 |
复杂任务有一个标准流程:/plan 进 plan mode → 跟 Claude 来回打磨方案 → 满意了退出 plan mode → 开干。这个流程可以避免 Claude “上来就动手”造成的浪费。
| Command | 实战价值 |
|---|---|
/rename [name] |
给会话取个有语义的名字,列表里能找到 |
/resume [session] |
恢复指定会话;不带参数则进入 picker |
/rewind |
回退到任意之前的 checkpoint,包括代码!别名 /checkpoint |
/branch |
从当前点 fork 一份对话——同一个起点试两条路径 |
/btw |
“顺便问一下”——临时旁支问题不污染主线 |
/rewind 是 Claude Code 比传统 IDE 更强的地方——不仅会话能回退,代码也能回到那一刻。Claude 跑偏了,不要修修补补,直接 rewind 到岔路口重选方向,比一行行 revert 干净得多。
| Command | 实战价值 |
|---|---|
/agents |
管理你的 subagents |
/skills |
列出可用的 skills |
/hooks |
看 hook 配置(不能改,要改去 settings.json) |
/mcp |
管 MCP 服务器连接和 OAuth |
/plugin |
管插件 |
/memory |
编辑 CLAUDE.md,开关 auto-memory |
/reload-plugins |
不重启进程加载新插件改动 |
后面的 06、07、08、10 篇会分别深入这些,这里只是认个门牌号。
如果只能记 10 个,按重要度排序如下:
| 优先 | Command | 一句话用法 |
|---|---|---|
| 1 | /compact |
上下文 50% 时手动压缩,永远比让它自动压缩要聪明 |
| 2 | /plan |
复杂任务永远先 plan mode 谈方案 |
| 3 | /rewind |
Claude 跑偏立刻回退,别在烂上下文里继续修 |
| 4 | /model |
切换模型/effort——plan 用 opus、coding 用 sonnet 是基操 |
| 5 | /context |
看上下文构成,决定怎么 compact |
| 6 | /permissions |
把常用工具预批准,比 --dangerously-skip-permissions 安全得多 |
| 7 | /resume |
重要会话先 /rename,之后能 /resume 找回 |
| 8 | /clear |
真的不需要历史就清,不要让上下文涨着浪费钱 |
| 9 | /doctor |
任何“诶它怎么不工作”的场景,先跑一下 |
| 10 | /diff |
看 Claude 改了啥,比直接看 git diff 多了个“per turn”的维度 |
这 10 个里,有 7 个是上下文/会话/模型管理类的——这不是巧合。LLM agent 工具的瓶颈从来不在能力,而在上下文管理。有十年传统软件经验的人,上来用 Claude Code,最容易踩的坑就是把它当成“无限上下文的助手”,不停地喂它信息——结果越用越笨,token 越烧越多。这 10 个命令里,7 个是治这个病的。
最后回到实战。Command 在实际项目里通常长成三种形态:
---
description: 把 React 组件改成 hooks 写法
---
把当前选中的 React 组件从 class 写法改成 functional + hooks。
保持 props 接口、保持测试通过、保持 PropTypes。
零字段,纯 prompt。适合重复出现的“按规范做某件事”。
上一篇的 release-notes-crafter.md 就是这种:
---
description: Craft release notes for a given version — fetches commits between tags, categorizes them, writes a polished RELEASE_NOTES.md
model: haiku
---
正文用结构化 markdown 描述 step by step:哪步用 AskUserQuestion、哪步 Agent、哪步 Skill。Command 自己不干活,调度其他扩展。
---
description: 提交并 PR
allowed-tools: Bash(git *), Bash(gh *)
---
# 当前现场
!`git status --short`
# 暂存改动
!`git diff --staged`
# 项目 commit 规范
@docs/COMMIT_CONVENTIONS.md
请基于现场和规范,生成 commit message 并提交,然后 gh pr create。
把“上下文准备”自动化掉,模型一来就有完整快照。
写第一个 command 时,大家通常从形态一开始(容易),但真正起作用的 command 大多是形态二和形态三。
最后列几条踩过的坑,专挑文档里没强调的:
Backtick 不是单引号
!git status`` 用的是反引号(U+0060),不是单引号(U+0027)。VS Code 默认的 markdown 智能引号会把它偷换成 curly quote,结果命令不执行、没有报错,Loader 直接把那一行当成纯文本塞给模型。这个设计实在不够友好——连个 warning 都没有。
$ARGUMENTS 不会做 shell escape
如果用户传入的参数里有 ; | &,原样替换进 ! 块就是命令注入。永远不要把 $ARGUMENTS 直接拼到 ! 块里,要用就拼到自然语言提示里,让模型决定怎么用。
paths 是匹配“工作焦点”,不是匹配“会话历史”
测试 paths 是否生效:用 /add-dir 加目录,让你的 command 出现在补全里。只是打开一个 tab 不算“工作焦点”。
context: fork 不能套娃
fork 出的子上下文里,子 agent 不能再启动一个 fork。设计上是有意为之——为了避免无限递归。
hooks 字段的 matcher 是工具名,不是字符串
hooks:
PostToolUse:
- matcher: "Edit" # 正确:匹配 Edit 工具
- matcher: "edit*" # 错误:不会匹配
完整的 matcher 语法,留到第 07 篇 hooks 系统里再展开。
Command 这个看起来最朴素的扩展点,背后其实有一整套“prompt 编译期”——! 跑 shell、@ 内联文件、$ARGUMENTS 拼参数、paths 限定激活范围、context: fork 切换执行模式。这些字段的组合,让一个 markdown 文件变成了一个会自动抓取现场的工作流入口。
13 个 frontmatter 字段、3 种动态注入语法、65 个内置 command——这些细节背下来意义不大,真正有用的是知道哪个能力对应哪个字段。下次想“我要让一个 command 在 .ts 文件里才出现”,能立刻定位到 paths,就够了。
Skills 是这趟旅程的下一站。Command 是按钮,按下去执行;Skill 是工具箱里的工具,模型自己判断什么时候开柜子拿哪一把。下一篇,我们把 Skill 的 9 种类型、渐进式揭露的设计哲学,以及 Skill 工具的调用机制全部摊开讲。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述