本文基于 claude-code-rev 源码分析 Claude Code 工作流中的命令系统:命令从哪里加载、如何被识别、如何执行、能否自定义、如何编写自定义命令/技能,以及这些命令与模型、工具权限、插件、工作流和 hooks 的关系。
结论先行:Claude Code 的命令可以自定义。最推荐的方式是写 Markdown 形式的命令或技能;更深度的交互式命令需要插件或源码级 local / local-jsx 实现;工作流命令和 MCP skill 也能进入命令系统,但它们依赖对应特性或外部服务。
命令工作流可以拆成四层:

源码中的命令统一抽象为 Command,定义在 src/types/command.ts。它由公共字段 CommandBase 加上三种执行形态之一组成。
| 命令类型 | 源码类型 | 主要用途 | 是否适合用户自定义 | 执行结果 |
|---|---|---|---|---|
| prompt | PromptCommand | 把 Markdown/文本内容扩展成模型输入;可作为技能被用户或 Claude 调用 | 适合,最推荐 | 生成用户消息,通常继续让模型处理;也可 context: fork 由子代理执行 |
| local | LocalCommand | 执行本地 TypeScript/JS 模块逻辑,不渲染交互 UI | 不适合普通配置,需要源码或插件代码 | 返回 text、compact 或 skip |
| local-jsx | LocalJSXCommand | 渲染 Ink/React 交互界面,例如选择器、配置面板 | 不适合普通配置,需要源码或插件代码 | 通过 onDone(...) 回传结果、是否继续查询、下一条输入等 |
关键字段:
| 字段 | 出现位置 | 含义 |
|---|---|---|
| name | CommandBase | 命令内部名称,用户通常通过 /<name> 调用 |
| aliases | CommandBase | 命令别名,findCommand(...) 会匹配别名 |
| description | CommandBase | 命令说明,显示在补全、帮助或 SkillTool 中 |
| argumentHint | CommandBase | 参数提示,来自 frontmatter 的 argument-hint |
| whenToUse | CommandBase | 技能适用场景,来自 frontmatter 的 when_to_use |
| userInvocable | CommandBase | 是否允许用户直接输入 /name 调用;false 时只能让 Claude 通过 Skill 工具调用 |
| disableModelInvocation | CommandBase | 是否禁止模型通过 SkillTool 调用 |
| loadedFrom | CommandBase | 命令来源,例如 skills、commands_DEPRECATED、plugin、bundled、mcp |
| kind | CommandBase | 当前主要用于标记 workflow |
| isSensitive | CommandBase | 对敏感参数进行历史记录脱敏 |
| allowedTools | PromptCommand | Markdown 命令中内联 shell 片段可用的工具 allowlist |
| model | PromptCommand | 命令或技能指定的模型 |
| hooks | PromptCommand | 技能被调用时临时注册的 hooks |
| context | PromptCommand | inline 或 fork;fork 会使用子代理单独执行 |
| agent | PromptCommand | context: fork 时指定子代理类型 |
| effort | PromptCommand | 子代理或模型推理 effort |
| paths | PromptCommand | 条件技能匹配文件路径后才激活 |
src/commands.ts 是命令聚合入口。内置命令由 COMMANDS() 返回,技能、插件和工作流再由 loadAllCommands(cwd) 合并。
| 来源 | 典型目录/入口 | 加载函数 | loadedFrom / source | 说明 |
|---|---|---|---|---|
| 内置命令 | src/commands/* | COMMANDS() | source: builtin | 例如 /help、/clear、/model、/hooks、/status 等 |
| 用户/项目技能 | .claude/skills/<skill>/SKILL.md、~/.claude/skills/<skill>/SKILL.md | getSkillDirCommands(cwd) | loadedFrom: skills | 推荐的自定义方式,目录格式固定为 skill-name/SKILL.md |
| 旧版自定义命令 | .claude/commands/*.md、.claude/commands/**/SKILL.md | loadSkillsFromCommandsDir(cwd) | loadedFrom: commands_DEPRECATED | 仍支持,适合兼容旧用法;新内容建议迁移到 skills |
| bundled skills | 构建内置的技能包 | getBundledSkills() | loadedFrom: bundled | 随 Claude Code 打包分发 |
| built-in plugin skills | 内置插件提供 | getBuiltinPluginSkillCommands() | 取决于插件 | 来自启用的内置插件 |
| plugin commands | 插件命令 | getPluginCommands() | loadedFrom: plugin | 插件可提供命令能力 |
| plugin skills | 插件技能 | getPluginSkills() | loadedFrom: plugin | 插件提供的 prompt 型技能 |
| workflow commands | 工作流脚本 | getWorkflowCommands(cwd) | kind: workflow | 受 WORKFLOW_SCRIPTS feature gate 控制 |
| MCP skills | MCP 服务暴露 | getMcpSkillCommands(...) | loadedFrom: mcp | 不经过 loadAllCommands(cwd);从 AppState.mcp.commands 单独筛选后作为模型可调用 skill 进入系统 |
| dynamic skills | 运行时发现 | getDynamicSkills() | 通常仍是 prompt command | 由文件触达或动态发现机制插入 |
loadAllCommands(cwd) 的合并顺序是:
|
1 2 3 4 5 6 7 8 9 |
return [ ...bundledSkills, ...builtinPluginSkills, ...skillDirCommands, ...workflowCommands, ...pluginCommands, ...pluginSkills, ...COMMANDS(), ] |
这意味着命令列表中,技能和插件命令会出现在内置命令之前。运行时查找由 findCommand(...) 按数组顺序返回第一个匹配项,因此同名命令可能受到加载顺序影响。实践中建议自定义命令避免与内置命令重名。
getCommands(cwd) 会在合并后做两类过滤:
| 过滤点 | 源码函数 | 作用 |
|---|---|---|
| 可用性过滤 | meetsAvailabilityRequirement(cmd) | 根据 availability 限制命令只对某类认证/服务提供方可见 |
| 开关过滤 | isCommandEnabled(cmd) | 根据 feature flag、环境变量或运行时状态决定命令是否启用 |
| 动态技能插入 | getDynamicSkills() | 把运行时发现的技能插到插件技能之后、内置命令之前 |
自定义命令最重要的入口在 src/skills/loadSkillsDir.ts 和 src/utils/markdownConfigLoader.ts。
技能目录只支持这一种结构:
|
1 2 3 4 |
.claude/ skills/ my-skill/ SKILL.md |
my-skill 会成为命令名,用户可以输入:
|
1 |
/my-skill 参数 |
如果放在用户目录,则是:
|
1 2 3 4 |
~/.claude/ skills/ my-skill/ SKILL.md |
旧版命令目录仍被支持:
|
1 2 3 4 5 6 7 |
.claude/ commands/ review.md git/ pr.md deploy/ SKILL.md |
命名规则:
| 文件形态 | 命令名 |
|---|---|
| .claude/commands/review.md | /review |
| .claude/commands/git/pr.md | /git:pr |
| .claude/commands/deploy/SKILL.md | /deploy |
| .claude/commands/team/release/SKILL.md | /team:release |
源码中 buildNamespace(...) 会把子目录转换成冒号命名空间。
loadMarkdownFilesForSubdir(...) 会加载三类位置:
| 位置 | 路径 | 说明 |
|---|---|---|
| managed/policy | managed path 下的 .claude/<subdir> | 组织策略或托管配置,优先级最高 |
| user | ~/.claude/<subdir> | 用户全局命令/技能 |
| project | 从当前目录向上查找 .claude/<subdir>,直到 git root 或 home | 项目级命令/技能 |
Markdown 文件加载组合顺序是 managed > user > project。文件层面会按真实文件 identity 去重,技能层面也会按 realpath 去重,顺序靠前者保留。
项目目录向上查找会在 git root 停止,避免父目录的 .claude/commands 或 .claude/skills 意外泄露进无关仓库。worktree 场景下,如果 worktree 缺少 .claude/<subdir>,会回退到主仓库对应目录。
Markdown 命令/技能通过 YAML frontmatter 定义元数据。字段解析主要在 src/utils/frontmatterParser.ts 和 src/skills/loadSkillsDir.ts。
| 字段 | 类型 | 用于 | 含义 |
|---|---|---|---|
| description | string | 命令/技能 | 显示说明;缺失时会从正文第一行提取 |
| argument-hint | string | 命令/技能 | 参数提示,例如 [branch]、<issue-id> |
| arguments | string 或 string[] | 命令/技能 | 声明命名参数,供正文替换使用 |
| allowed-tools | string 或 string[] | prompt 命令 | 允许内联 shell 片段使用的工具;缺失默认为空 |
| when_to_use | string | skill | 给模型看的“什么时候使用”说明 |
| version | string | skill | 技能版本 |
| model | string | prompt 命令 | 指定模型;inherit 表示继承父上下文 |
| user-invocable | boolean-like string | skill | 是否允许用户直接 /name 调用;默认 true |
| disable-model-invocation | boolean-like string | skill | 是否禁止模型通过 SkillTool 调用 |
| hooks | HooksSettings | skill | 技能被调用时注册临时 hooks |
| context | inline 或 fork | skill | fork 会启动子代理执行 |
| agent | string | fork skill | 指定子代理类型 |
| effort | string 或 integer | fork skill | 指定推理 effort |
| paths | string 或 string[] | skill | 条件技能,匹配文件路径后才激活 |
| shell | bash 或 powershell | skill/command | 控制 Markdown 内 ! shell 片段使用的 shell |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
--- description: 生成当前改动的代码审查清单 argument-hint: "[重点区域]" when_to_use: 用户希望快速审查当前分支或工作区改动时 --- ? 请审查当前仓库的改动,重点关注:$ARGUMENTS。 ? 输出: 1. 高风险问题 2. 可维护性问题 3. 建议补充的测试 4. 可以直接合并的理由或阻塞项 |
放到:
|
1 |
.claude/skills/review-changes/SKILL.md |
调用:
|
1 |
/review-changes auth 模块 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
--- description: 根据 issue 编号生成修复计划 argument-hint: "<issue> <scope>" arguments: - issue - scope ---
请为 issue $issue 生成修复计划。
范围:$scope
要求: - 先定位相关文件 - 说明风险 - 给出测试计划 |
命令内容最终会通过 substituteArguments(...) 替换参数。
|
1 2 3 4 5 6 |
--- description: 安全审计技能 when_to_use: 当任务涉及认证、权限、外部输入、密钥或命令执行时使用 user-invocable: false --- 对当前任务做安全审计,重点检查注入、权限绕过、敏感信息泄露和不安全默认值。 |
用户直接输入 /security-audit 时会收到提示:该技能只能由 Claude 调用。用户可以说“请使用 security-audit 技能”。
|
1 2 3 4 5 6 7 8 |
--- description: 在独立上下文中做大型代码审查 when_to_use: 当改动跨多个模块,需要独立上下文审查时使用 context: fork agent: code-reviewer effort: high --- 请审查当前分支的代码改动,输出关键问题和建议。 |
执行时不会把技能正文简单塞进当前对话,而是走 executeForkedSlashCommand(...),调用 runAgent(...) 在子代理中执行。
|
1 2 3 4 5 6 7 8 9 10 11 |
--- description: 编辑 TypeScript 后自动检查 hooks: PostToolUse: - matcher: "Write|Edit|MultiEdit" hooks: - type: command command: "npm run typecheck" timeout: 60 --- 请实现用户请求,并在修改 TypeScript 文件后自动触发类型检查。 |
源码中 getMessagesForPromptSlashCommand(...) 会在命令带有 hooks 时调用 registerSkillHooks(...),这些 hook 以会话级方式注册。
src/utils/processUserInput/processSlashCommand.tsx 负责 slash command 的运行时处理。
流程如下:
| 步骤 | 源码函数 | 行为 |
|---|---|---|
| 解析输入 | parseSlashCommand(inputString) | 拆出 commandName、args、isMcp |
| 判断存在 | hasCommand(commandName, context.options.commands) | 不存在时判断是否像文件路径;否则返回 Unknown skill |
| 获取命令 | getCommand(commandName, context.options.commands) | 按 name、userFacingName()、aliases 查找 |
| 检查调用权限 | command.userInvocable === false | 禁止用户直接调用,只允许 Claude 使用 SkillTool |
| 分支执行 | switch (command.type) | 进入 local-jsx、local 或 prompt 分支 |
findCommand(...) 的匹配逻辑是:
|
1 2 3 |
_.name === commandName || getCommandName(_) === commandName || _.aliases?.includes(commandName) |
普通 prompt 命令会:
Markdown 技能的 getPromptForCommand(...) 还会做这些替换/处理:
| 处理 | 说明 |
|---|---|
| 添加 base directory | 有 baseDir 时,正文前会加 Base directory for this skill: ... |
| 参数替换 | 通过 $ARGUMENTS 或命名参数替换用户输入 |
| ${CLAUDE_SKILL_DIR} | 替换成技能目录,便于引用技能自带脚本 |
| ${CLAUDE_SESSION_ID} | 替换成当前 session id |
| 执行 Markdown 内 shell 片段 | 非 MCP 技能可以执行 ! shell 片段;MCP 技能禁止执行 |
当 PromptCommand.context === 'fork' 时,流程进入 executeForkedSlashCommand(...):
| 阶段 | 行为 |
|---|---|
| 准备上下文 | prepareForkedCommandContext(...) 生成技能内容、agent 定义、prompt messages |
| 创建 agent id | createAgentId() |
| 启动子代理 | runAgent(...) 使用指定 agent、model、effort 和可用工具 |
| 收集结果 | extractResultText(...) 提取子代理最终输出 |
| 返回结果 | 结果包装成 <local-command-stdout>,不再直接让主模型查询 |
在 KAIROS/assistant 模式下,fork 命令还可能以后台方式运行,完成后把结果重新放回消息队列。
local 命令是源码或插件中的 JS/TS 模块。执行逻辑:
先构造用户输入消息。
await command.load() 懒加载模块。
调用 mod.call(args, context)。
根据返回结果处理:
local-jsx 命令适合交互 UI,例如选择器、设置面板。执行逻辑:
command.load() 懒加载 JSX 模块。
调用 mod.call(onDone, context, args)。
返回 React 节点后通过 setToolJSX(...) 渲染。
UI 完成后调用 onDone(result, options)。
options 可控制:
可以,但不同层级能力不同。
| 自定义方式 | 是否需要写代码 | 能力 | 适用场景 | 推荐程度 |
|---|---|---|---|---|
| .claude/skills/<name>/SKILL.md | 否 | prompt 命令、模型技能、可 fork、可带 hooks、可指定模型和工具 | 项目/个人常用流程、审查、生成、分析、规范化任务 | 最高 |
| .claude/commands/*.md | 否 | 旧版 prompt 命令,支持命名空间 | 兼容旧项目或快速添加 /foo | 中等,建议新建用 skills |
| 插件命令/技能 | 是 | 可分发、可封装复杂能力 | 团队/生态共享命令 | 高,但复杂度更高 |
| 工作流命令 | 取决于工作流 | 标记为 workflow 的命令 | 可复用自动化工作流 | 取决于 feature 是否启用 |
| MCP skills | 外部 MCP 服务 | 远端服务暴露技能 | 外部系统集成 | 适合系统集成 |
| 修改 src/commands/* | 是 | 完整 local/local-jsx/prompt 能力 | fork 本项目或实现内置级交互命令 | 仅适合维护者 |
文件:.claude/skills/pr-description/SKILL.md
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
--- description: 根据当前分支生成 PR 描述 argument-hint: "[目标分支]" when_to_use: 用户准备创建 pull request 或需要总结当前分支改动时 allowed-tools: - Bash(git status:*) - Bash(git diff:*) - Bash(git log:*) --- 请基于当前分支相对 $ARGUMENTS 的改动生成 PR 描述。 要求: - 标题不超过 70 字符 - Summary 使用 1-3 个 bullet - Test plan 使用 checklist - 明确指出未验证项 |
调用:
|
1 |
/pr-description main |
文件:.claude/skills/db-review/SKILL.md
|
1 2 3 4 5 6 7 8 9 10 11 12 |
--- description: 数据库变更审查 when_to_use: 当任务涉及 SQL、migration、索引、事务或数据库性能时使用 disable-model-invocation: false user-invocable: false ---
请审查数据库相关改动,重点检查: - migration 是否可回滚 - 大表 DDL 是否会长时间锁表 - 查询是否有合适索引 - 是否存在 SQL 注入风险 |
用户不能直接 /db-review,但可以要求 Claude 使用该技能。
|
1 2 3 4 5 6 7 8 9 |
--- description: React 组件审查 when_to_use: 当修改 React/TSX 文件时检查组件结构和状态管理 paths: - "src/**/*.tsx" - "app/**/*.tsx" ---
当用户修改 React 组件时,检查 props 类型、状态管理、副作用和可访问性。 |
带 paths 的技能会先进入 conditional skill 存储,只有匹配路径被触达后才激活。
Claude Code 把很多 prompt 型命令也暴露给模型作为 SkillTool 可调用能力。
| 函数 | 作用 |
|---|---|
| getSkillToolCommands(cwd) | 返回模型可调用的 prompt commands,包括 .claude/skills、bundled skills 和旧版 .claude/commands |
| getSlashCommandToolSkills(cwd) | 更偏向“技能”列表,要求有 description 或 whenToUse,并来自 skills/plugin/bundled 等来源 |
| getMcpSkillCommands(mcpCommands) | 从 MCP 命令中筛出 prompt 型、模型可调用、loadedFrom: mcp 的技能 |
影响模型是否可调用的关键字段:
| 字段 | 效果 |
|---|---|
| disable-model-invocation: true | 不进入模型可调用技能列表 |
| user-invocable: false | 用户不能直接 /name 调用,但模型仍可调用,除非同时禁用模型调用 |
| description / when_to_use | 对插件/MCP 类技能尤其重要,决定是否展示给模型或工具列表 |
从 Markdown skill 文件到可执行命令,经历以下阶段:
8.1.1 加载阶段:Markdown → PromptCommand
|
1 2 3 4 5 6 7 8 9 10 11 |
.claude/skills/example.md ↓ loadSkillsDir.ts 读取文件 ↓ parseMarkdown() 解析 frontmatter + body ↓ 构建 PromptCommand 对象 { type: 'prompt', path: '/path/to/example.md', prompt: 'body 内容', allowedTools: frontmatter.allowed-tools, ... } |
源码位置:src/skills/loadSkillsDir.ts 的 getPromptForCommand() (344-405行)
8.1.2 用户输入/skill-name执行路径
|
1 2 3 4 5 6 |
用户输入: /example arg1 arg2 ↓ processSlashCommand.tsx 检测 "/" 开头 ↓ findMatchingCommand() 查找匹配的命令 ↓ getMessagesForPromptSlashCommand() 生成消息 ↓ getPromptForCommand() 处理 prompt 内容 ↓ 返回 { messages, allowedTools, hooks } |
关键函数:src/utils/processUserInput/processSlashCommand.tsx 第827行
8.1.3 getPromptForCommand() 详细处理步骤
| 步骤 | 操作 | 源码位置 |
|---|---|---|
| 1 | 添加 base dir prefix | 第352行 |
| 2 | 替换 $ARGUMENTS | 第360-375行 |
| 3 | 替换 ${CLAUDE_SKILL_DIR} | 第380行 |
| 4 | 替换 ${CLAUDE_SESSION_ID} | 第385行 |
| 5 | 执行内联 ! shell 片段 | 第390-405行 |
8.1.4 SkillTool 调用路径
模型通过 SkillTool 调用 skill 时:
|
1 2 3 4 5 |
模型输出: Skill({ skill: "example", args: "arg1" }) ↓ SkillTool.ts call() 函数 (580-841行) ↓ 判断 context: inline 还是 fork ↓ inline: 直接返回 prompt 消息 ↓ fork: 启动子 Agent 执行 |
8.1.5 Inline vs Fork 执行流程对比
| 类型 | 流程 | 适用场景 |
|---|---|---|
| inline | 模型收到 skill prompt → 在当前会话继续 | 简单指令、快速任务 |
| fork | 启动子 Agent → 子 Agent 执行 → 返回结果 | 复杂任务、隔离执行 |
8.1.6 关键源码位置表
| 功能 | 文件 | 行号 |
|---|---|---|
| Skill 加载 | src/skills/loadSkillsDir.ts | 344-405 |
| Slash 命令解析 | src/utils/processUserInput/processSlashCommand.tsx | 827 |
| SkillTool 调用 | src/tools/SkillTool/SkillTool.ts | 580-841 |
| SkillTool 系统提示 | src/tools/SkillTool/prompt.ts | 173-195 |
| Skills 列表注入 | src/messages/prompt/formatPrompt.ts | formatCommandsWithinBudget |
命令系统本身不等同于工具权限系统,但它会影响后续模型或内联 shell 的权限边界。
Markdown 命令 frontmatter 的 allowed-tools 会被解析为工具列表。对于 prompt 命令:
MCP skills 被视为远端/不可信来源,源码中明确禁止执行其 Markdown 正文里的内联 shell 片段。也就是说:
| 来源 | 是否执行 Markdown 内 ! shell 片段 |
|---|---|
| 本地 skills / commands | 可以,受权限与 allowed-tools 影响 |
| bundled / plugin skills | 取决于加载来源与策略 |
| MCP skills | 不执行 |
src/commands.ts 中还有远程模式限制:
| 限制 | 源码对象/函数 | 含义 |
|---|---|---|
| REMOTE_SAFE_COMMANDS | remote mode 预过滤 | 只保留不依赖本地文件系统、git、shell、IDE、MCP 的命令 |
| BRIDGE_SAFE_COMMANDS | bridge inbound allowlist | 手机/web 远程控制进入的 local 命令默认阻止,只有 allowlist 内可执行 |
| isBridgeSafeCommand(cmd) | bridge 判断 | prompt 命令默认安全,local-jsx 默认阻止,local 需要 allowlist |
命令和 hooks 是两套机制,但可以互相连接:
| 连接点 | 说明 |
|---|---|
| 技能 frontmatter 中声明 hooks | 技能被调用后,通过 registerSkillHooks(...) 注册会话级 hooks |
| 命令触发工具调用 | prompt 命令本身会变成模型输入,模型后续使用工具时仍会触发 hooks |
| allowed-tools 与 hooks 双重约束 | 命令可限定工具列表,hooks 可在工具真正执行前后审计或阻断 |
| fork 技能与 Subagent hooks | context: fork 会启动子代理,相关生命周期可能触发子代理 hooks |
建议:
| 目标 | 用命令还是 hook |
|---|---|
| 让用户主动触发某套流程 | 用命令/技能 |
| 在工具执行前后自动校验 | 用 hook |
| 给某个技能附带临时质量门禁 | 在技能 frontmatter 中声明 hooks |
| 对所有项目统一拦截危险行为 | 用全局或项目级 hook |
getCommands(cwd) 是命令表入口。它先调用 memoized 的 loadAllCommands(cwd),再做可用性过滤和动态技能插入。
核心链路:
|
1 2 3 4 5 6 7 8 9 10 |
getCommands(cwd) └─ loadAllCommands(cwd) ├─ getSkills(cwd) │ ├─ getSkillDirCommands(cwd) │ ├─ getPluginSkills() │ ├─ getBundledSkills() │ └─ getBuiltinPluginSkillCommands() ├─ getPluginCommands() ├─ getWorkflowCommands(cwd) └─ COMMANDS() |
getSkillDirCommands(cwd) 又会加载:
|
1 2 3 4 5 |
managed .claude/skills user ~/.claude/skills project .claude/skills additional --add-dir .claude/skills legacy .claude/commands |
SKILL.md 或旧版 .md 文件会经历:
|
1 2 3 4 5 |
读取 Markdown → parseFrontmatter(...) → parseSkillFrontmatterFields(...) → createSkillCommand(...) → 返回 type: 'prompt' 的 Command |
createSkillCommand(...) 生成的命令固定是 type: 'prompt'。因此 Markdown 自定义命令不是直接执行 JS 函数,而是生成一段 prompt 内容,让 Claude Code 把它作为用户消息或子代理任务处理。
用户输入 /name args 后:
|
1 2 3 4 5 6 7 8 9 10 11 |
processSlashCommand(...) → parseSlashCommand(...) → hasCommand(...) → getMessagesForSlashCommand(...) → getCommand(...) → switch command.type ├─ local-jsx: load().call(onDone, context, args) ├─ local: load().call(args, context) └─ prompt: ├─ context === 'fork' → executeForkedSlashCommand(...) └─ inline → getMessagesForPromptSlashCommand(...) |
Markdown 命令最终被转成 PromptCommand。它只有 getPromptForCommand(...),不能直接返回 React 节点,也不能直接实现 onDone 交互流程。需要交互 UI 的命令必须是 local-jsx,也就是源码或插件代码提供的命令。
skills 目录是当前更完整的能力模型:
旧版 .claude/commands 仍能用,但源码中标记为 commands_DEPRECATED,适合兼容,不建议作为新设计的首选。
| 场景 | 推荐做法 | 原因 |
|---|---|---|
| 团队共享常用流程 | 提交 .claude/skills/<name>/SKILL.md 到仓库 | 可版本化、可审查、随项目走 |
| 个人全局命令 | 放在 ~/.claude/skills/<name>/SKILL.md | 不污染项目仓库 |
| 旧项目已有 /commands | 可以继续用,但新命令迁移到 /skills | commands_DEPRECATED 仍支持但不是首选 |
| 需要交互选择器 | 写插件或源码 local-jsx 命令 | Markdown 无法渲染 UI |
| 需要执行本地脚本 | 优先用技能正文指导 Claude 调工具,必要时用 Markdown ! 片段并设置 allowed-tools | 保持权限边界清晰 |
| 涉及危险操作 | 不要只靠命令说明,配合 PreToolUse / PermissionRequest hooks | 命令是触发流程,hook 才是强约束点 |
| 需要模型自动选择 | 写清 description 和 when_to_use | 模型依赖这些字段判断是否使用技能 |
| 需要隔离上下文 | 使用 context: fork | 大任务不会污染主上下文,且有独立 token 预算 |
.claude/commands 是旧版 Markdown 命令目录;.claude/skills 是更完整的技能目录。两者最终都会被转成 type: 'prompt' 的 Command,但 skills 支持更明确的目录结构、资源引用、条件激活和模型技能语义。
命令合并时自定义技能在内置命令之前,查找时返回第一个匹配项,因此同名有可能影响解析结果。但不建议依赖覆盖行为,最好避免与内置命令重名。
可以使用 Markdown 内联 shell 片段,但要受权限和 allowed-tools 影响。MCP skills 不会执行内联 shell。涉及危险操作时,建议用 hooks 做强制约束。
用户不能直接输入 /skill-name 调用;如果模型可调用未禁用,Claude 仍可通过 SkillTool 使用它。
普通 prompt 命令会把内容加入当前对话;context: fork 会启动子代理在独立上下文中执行,最终把结果返回给主流程。
不一定。模型可调用技能通常要求是 prompt 类型、不是 builtin、没有 disableModelInvocation,并且有合适的 description 或 when_to_use。插件/MCP 类技能对显式描述要求更高。
| 文件 | 关键函数/类型 | 说明 |
|---|---|---|
| src/types/command.ts | Command, PromptCommand, LocalCommand, LocalJSXCommand | 命令类型系统 |
| src/commands.ts | COMMANDS() | 内置命令列表 |
| src/commands.ts | loadAllCommands(cwd) | 合并 bundled skills、plugin skills、skill dir commands、workflow commands、plugin commands、内置命令 |
| src/commands.ts | getCommands(cwd) | 过滤可用命令并插入动态技能 |
| src/commands.ts | findCommand(...), getCommand(...) | 命令查找逻辑,支持别名和 user-facing name |
| src/commands.ts | getSkillToolCommands(...), getSlashCommandToolSkills(...) | 生成模型可调用技能列表 |
| src/commands.ts | REMOTE_SAFE_COMMANDS, isBridgeSafeCommand(...) | 远程/bridge 模式命令安全过滤 |
| src/utils/processUserInput/processSlashCommand.tsx | processSlashCommand(...) | 用户 /command 输入解析入口 |
| src/utils/processUserInput/processSlashCommand.tsx | getMessagesForSlashCommand(...) | 按 command.type 分支执行 |
| src/utils/processUserInput/processSlashCommand.tsx | executeForkedSlashCommand(...) | context: fork 子代理执行逻辑 |
| src/skills/loadSkillsDir.ts | getSkillDirCommands(cwd) | 加载 .claude/skills 和旧版 .claude/commands |
| src/skills/loadSkillsDir.ts | createSkillCommand(...) | 把 Markdown 技能转换成 PromptCommand |
| src/skills/loadSkillsDir.ts | parseSkillFrontmatterFields(...) | 解析技能 frontmatter 字段 |
| src/utils/markdownConfigLoader.ts | loadMarkdownFilesForSubdir(...) | 加载 managed/user/project Markdown 配置文件 |
| src/utils/markdownConfigLoader.ts | getProjectDirsUpToHome(...) | 从 cwd 向上查找 .claude/<subdir>,到 git root 停止 |
| src/utils/frontmatterParser.ts | FrontmatterData, parseFrontmatter(...) | frontmatter 字段定义和 YAML 解析 |
| 你想做什么 | 应该用什么 |
|---|---|
| 增加一个 /review,让 Claude 按固定步骤审查代码 | .claude/skills/review/SKILL.md |
| 增加一个 /git:pr 命名空间命令 | .claude/commands/git/pr.md 或迁移为 .claude/skills/git-pr/SKILL.md |
| 让 Claude 在修改 TS 文件后自动 typecheck | 技能 frontmatter hooks,或项目级 PostToolUse hook |
| 做一个交互式模型选择面板 | local-jsx 命令,需要源码或插件 |
| 做一个纯本地状态查询命令 | local 命令,需要源码或插件 |
| 让某技能只在 React 文件被修改后出现 | paths: ["**/*.tsx"] 条件技能 |
| 把外部系统能力暴露给 Claude | MCP skill 或插件 skill |
| 团队统一分发命令 | 项目 .claude/skills 或插件 |