每个 Claude Code 用户都有几段反复输入的提示词——"看一下失败的测试告诉我哪错了""基于 diff 开个 PR""跑 lint,能自动修的修掉,修不了的留着"。这些都是成段的话,每周会被重打、改写或从笔记里复制几十次。自定义斜杠命令(Custom Slash Commands)就是为消灭这些重复而生:把一段保存好的提示词写成 Markdown 文件,用 /名字 一键调用,让同样的工作每次都以同一种方式发生。本教程适合已跑通过 Claude Code 首个项目的开发者,目标是你读完就能在自己的项目里发布 /review、/pr、/release:notes 等可复用命令,并理解它和 Skill、Subagent、Hook 的边界。
/clear 等 SDK 命令需 v2.1.117+)并能完成一轮对话;README.md);.md 文件;key: value)。剥掉营销话术,一个斜杠命令就是放在特定目录下的一个 Markdown 文件。文件名去掉 .md 后缀就是命令名,文件正文就是被注入会话的提示词。
没有 DSL,没有插件清单,没有编译步骤。如果你能在文本编辑器里写一段话,你就能发布一个斜杠命令。
两个目录决定了一切:
| 作用域 | 路径 | 行为 |
|---|---|---|
| 项目级 | .claude/commands/ |
随仓库进入 Git,团队每个克隆者都能用 |
| 个人级 | ~/.claude/commands/ |
存在家目录,跨所有项目跟随你,仅你可见 |
放在 .claude/commands/ship-it.md 的文件,在任何打开于该仓库的会话里都会显示为 /ship-it;放在 ~/.claude/commands/standup.md 的文件,则到处都显示为 /standup。
子目录会变成命名空间:.claude/commands/release/notes.md 会变成 /release:notes。当命令超过十个、自动补全开始拥挤时,这一点很关键。
趋势提示:官方推荐的新格式是
.claude/skills/<name>/SKILL.md,它既支持/名字调用,也支持 Claude 自主触发。.claude/commands/是经典格式,CLI 仍完全支持,本文聚焦经典格式因为它最轻量、最适合"一个命令一个动作"的场景。两者并不冲突,可共存。
最小可用的命令只有一行。在你的项目根目录创建 .claude/commands/recap.md,内容如下:
对照 main 分支总结 diff,写成 3 条要点形式的 PR 描述。不要表情符号,不要营销腔。
在任何会话里输入 /recap,Claude 会把这段指令当作一条全新的用户消息处理:它运行 git diff、读取改动、按你偏好的风格写出 PR 描述。
这就是全部形状。本教程剩下的一切都是对它的变形。
预期结果:输入 / 后弹出命令选择器,能看到 recap 条目;选中或输入 /recap 回车,Claude 立即开始分析当前分支与 main 的差异并产出三条要点。
斜杠命令文件顶部可写一段 YAML frontmatter,用来微调命令行为。真正会用到的字段如下:
---
description: 为当前分支开一个带摘要的 PR
argument-hint: [可选的 reviewer 账号]
allowed-tools: Bash(git:*), Bash(gh:*)
model: claude-sonnet-4-6
---
逐项说明:
description:在斜杠命令选择器里显示的一行说明。务必写——若省略,选择器会显示正文第一行,而那往往不是你想要的。argument-hint:在输入框显示的占位提示。纯属外观,但它告诉用户命令需要什么形状的输入。allowed-tools:命令被允许无需逐次询问即可调用的工具白名单。请收窄到命令真正需要的范围。Bash(git:*) 允许运行任意 git 子命令;Bash(git status) 只允许运行那条精确命令。model:为本命令覆盖会话模型。把廉价机械的命令路由到 Haiku,把重活留给 Opus。对于个人命令你可以全部忽略,对项目命令大多数也用不上。但有两个字段稳定地物有所值:description(让团队成员能读懂选择器)和 allowed-tools(让命令不必每次运行都弹权限)。
$ARGUMENTS 与位置占位符一个命令从"便签"升级为"工具",始于它接受输入。斜杠命令名字之后的任何内容,在命令正文里都可作为 $ARGUMENTS 访问:
---
description: 开 PR 并指定 reviewer
argument-hint: <github-handle>
---
为当前分支开一个 PR,标题清晰、正文三段摘要,然后请求 @$ARGUMENTS 审查。
调用 /pr alice 会把 alice 替换进提示词,再交给 Claude。/pr alice bob 则替换为 alice bob。没有位置解析——$ARGUMENTS 是整个尾部字符串,你把它当成一个整体来处理。
如果命令需要结构化输入,最佳做法是让提示词声明它期望的结构、交给 Claude 去解析,而不是发明一套语法:
"把
$ARGUMENTS当作以逗号分隔的文件路径列表。对每一个……"
这比硬造一套参数语法更耐久。
需要位置参数时,可用 $0、$1 编号占位符,并通过 argument-hint 声明形状:
---
argument-hint: [issue-number] [priority]
description: 修复一个 GitHub issue
---
修复 issue #$0,优先级为 $1。先查 issue 描述,再实现必要的改动。
/fix-issue 123 high 会让 $0="123"、$1="high"。
除了 $ARGUMENTS,命令正文还支持三种让提示词"活起来"的机制,这是把命令写出工程感的关键。
!反引号用 ! 加反引号可在提示词里内联执行 bash 并把输出嵌入正文:
---
allowed-tools: Bash(git add *), Bash(git status *), Bash(git commit *)
description: 创建一个 git commit
---
## 上下文
- 当前状态:!`git status`
- 当前 diff:!`git diff HEAD`
## 任务
基于上述改动创建一个 message 恰当的 commit。
Claude 看到的是已经把 git status、git diff HEAD 真实输出展开后的文本,等于命令一启动就自带了"现场快照"。
@前缀用 @ 前缀可直接把文件内容纳入提示词:
---
description: 审查配置文件
---
审查以下配置文件存在的问题:
- 包配置:@package.json
- TS 配置:@tsconfig.json
- 环境配置:@.env
检查安全问题、过期依赖和错误配置。
allowed-tools 不只是权限便利,更是意图声明。一个 /lint 命令只列出 Bash(npm run lint:*) 就不可能意外编辑文件;一个 /deploy-check 只列出 Bash(gh:*) 和 Read 就不可能推送代码。表面越窄,命令越安全,用户越不用纠结是否放行每一次调用。
这是新手最容易混淆的地方。斜杠命令坐落在一个很窄的带状区域里,它擅长一件事,对另几件事很糟。用一张决策表收束:
flowchart TD
Start[我有一段重复工作] --> Q1{是否每次都是同一段提示词<br/>最多1-2个变量槽?}
Q1 -->|是| Q2{是否希望用户主动输入才触发?}
Q1 -->|否,需附带模板/脚本/领域知识| Skill[用 Skill<br/>.claude/skills/SKILL.md]
Q2 -->|是,希望主动触发| Cmd[✅ 用斜杠命令<br/>.claude/commands/x.md]
Q2 -->|否,希望Claude自主判断触发| Skill
Start --> Q3{是否需要独立上下文窗口<br/>和专职人格?}
Q3 -->|是| Sub[用 Subagent<br/>有独立allowlist、返回报告]
Start --> Q4{是否应在harness事件上自动触发<br/>如保存文件/会话停止?}
Q4 -->|是,机械动作无需模型| Hook[用 Hook<br/>settings.json]
Q4 -->{需要模型在环} --> Q2
style Cmd fill:#d4edda,stroke:#28a745,stroke-width:2px
一句话启发式:
真实团队里被长期保留下来的命令,通常有四个共同点。
最强的命令在结尾有一个单一、可观测的产物。/pr 开 PR,/release-notes 写发版说明,/triage 给 issue 打标签。试图当"万金油"的命令(/dev——你想干啥就干啥)会漂移:没人记得它干嘛用,提示词膨胀到想覆盖每种情况,最终沦为比直接打字更差的版本。
把命令正文当作"成功长什么样"的契约,写清楚:
"审查这个 PR"会产生十种不同的审查;"只针对安全问题审查此 PR,忽略风格与命名,在发现可利用漏洞处发布行内评论"会产生接近一致的审查结果。
/lint 只允许 Bash(npm run lint:*),/deploy-check 只允许 Bash(gh:*) 和 Read。窄表面 = 安全 = 少打扰。
机械工作(格式化 commit message、从 git log 生成 changelog、给 issue 分类)在 Haiku 上跑得很好,成本只是 Sonnet/Opus 的零头:
---
model: claude-haiku-4-5-20251001
---
设计类工作(提议架构、解开棘手 bug、重写 API)则应钉到 Opus,避免用户意外在降级模型上调用而拿到更差的答案。
下面是完整的、可运行的示例。目标:在一个项目里建三个相互配合的命令,覆盖"发版"全流程。最终目录结构如下:
.claude/
└── commands/
└── release/
├── notes.md → /release:notes
├── cut.md → /release:cut
└── verify.md → /release:verify
依次写入三个文件。
.claude/commands/release/notes.md —— 生成发版说明:
---
description: 为自上次 tag 以来的提交生成发版说明
allowed-tools: Bash(git:*)
model: claude-haiku-4-5-20251001
---
生成覆盖自最近 tag 以来提交的发版说明。
1. 用 `git describe --tags --abbrev=0` 找到最新 tag。
2. 读取该 tag 与 HEAD 之间的 commit log。
3. 把提交归入 ## 新功能、## 修复、## 内部 三组。
4. 每行写成面向用户的句子,而非原始 commit subject。
5. 跳过 merge 提交和依赖版本号 bump。
6. 输出到 stdout。不要写文件。
.claude/commands/release/cut.md —— 切发版 tag:
---
description: 基于发版说明切一个新版本 tag
argument-hint: <semver 比如 1.4.0>
allowed-tools: Bash(git:*)
---
## 当前状态
- 最新 tag:!`git describe --tags --abbrev=0`
- 未发布提交数:!`git rev-list --count $(git describe --tags --abbrev=0)..HEAD`
## 任务
为版本 $ARGUMENTS 切 tag。
1. 先运行 /release:notes 确认要发布的内容。
2. 确认工作区干净(`git status` 无未提交改动)。
3. 用 `git tag -a v$ARGUMENTS -m "<发版说明首行>"` 打带注解的 tag。
4. 提示用户用 `git push origin v$ARGUMENTS` 推送。
不要直接 push,把推送决定权留给用户。
.claude/commands/release/verify.md —— 校验发版产物:
---
description: 校验发版前置条件是否就绪
allowed-tools: Bash(git:*), Bash(npm:*), Read
---
## 上下文
- package.json 版本:!`node -p "require('./package.json').version"`
- 最新 tag:!`git describe --tags --abbrev=0`
- main 分支状态:!`git status -sb`
## 校验清单
检查发版前置条件:
1. package.json 版本号是否与即将切的 tag 一致;
2. 工作区是否干净;
3. 是否所有改动都已推送到远端 main;
4. 依赖是否有已知高危漏洞(`npm audit`)。
按 ✅/⚠️/❌ 三级逐条汇报,最后给一个 GO / NO-GO 结论。
在项目根目录启动 Claude Code 会话,输入 /,选择器里应出现 release:notes、release:cut、release:verify 三个条目,描述与你写的 description 一致。
/release:notes
Claude 会调用 Haiku,读 git log,在终端打印三段式发版说明。确认无误后:
/release:cut 1.4.0
Claude 先确认工作区干净,再打带注解的 tag,最后只提示你推送、不替你 push——这是把"不可逆的外向动作"决定权留给用户的关键设计。
/release:verify
Claude 汇总版本号、tag、工作区、依赖审计,给出 GO/NO-GO。
| 命令 | 模型 | 产出物 | 副作用 |
|---|---|---|---|
/release:notes |
Haiku | stdout 三段式说明 | 无(只读 git) |
/release:cut 1.4.0 |
继承会话模型 | 本地 annotated tag | 不推送,留给用户 |
/release:verify |
继承会话模型 | GO/NO-GO 报告 | 无(只读 + npm audit) |
/release:notes 报 "no tags found":仓库还没有任何 tag。先手动 git tag v0.1.0 打一个基线 tag。.claude/commands/release/ 下、扩展名是 .md、没有把 release 目录建成了文件。用 find .claude/commands -type f 复核。allowed-tools 写成了 Bash(git) 而非 Bash(git:*)。前者只匹配裸 git,后者匹配所有 git 子命令。model 临时换成 claude-sonnet-4-6 排查是模型能力问题还是提示词问题。若是提示词问题,补充"先读什么/输出什么格式"。组织命令的成熟做法:相关命令收进同一个子目录。常见命名空间:release:、pr:、db:、docs:、triage:、test:。一旦超过十个命令就该开始分组,让选择器保持清爽、相关命令排在一起。
会话内输入 / 即可看到全部可用命令(内置、项目、个人)及其描述——这是你的健全性检查。删除一个命令只需删掉对应 .md 文件,或把它移到 ~/.claude/commands/ 之外。
当你想把一组命令分享到仓库之外——比如一套跨十几个服务使用的发版命令——下一步是把它们打包成插件。插件是一个包含命令(以及 Skill、Agent、Hook)的目录,任何 Claude Code 用户都能用一条命令安装,更新自动流转。
不必从插件起步。自然的演进路径是:
~/.claude/commands/)——一个用户,所有项目;.claude/commands/)——本仓库所有用户;每一步都等量增加摩擦与耐久度。别在命令还没"挣到"之前就提前升级它。
.claude/commands/ 迁移到 .claude/skills/<name>/SKILL.md 的官方推荐路径,支持自主触发。claude /release:cut 1.4.0 在 -p 无头模式下调用自定义命令,把它接入 CI——这是从"手动一键"走向"自动流水线"的下一站。/review 命令并通过"换一个仓库仍能用"的迁移测试。argument-hint、allowed-tools、model 三字段的命令;能用子目录命名空间组织 3 个以上命令;能口述命令与 Skill/Hook 的边界。$ARGUMENTS、!bash、@文件引用、命名空间的完整规范。/pr /triage /release:notes 范例。.claude/commands/ 经典格式与 .claude/skills/ 新格式的迁移说明。本文由微信公众号「51学通信」原创制作,站长「爱卫生」(微信:gprshome201101)。聚焦 5G/6G 核心网协议与 AI 辅助开发的交叉实战,每日一篇可落地的硬核技术拆解。
[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
