Claude Code 工作流中的命令实现与自定义指南

Claude Code 工作流中的命令实现与自定义指南

本文基于 claude-code-rev 源码分析 Claude Code 工作流中的命令系统:命令从哪里加载、如何被识别、如何执行、能否自定义、如何编写自定义命令/技能,以及这些命令与模型、工具权限、插件、工作流和 hooks 的关系。

结论先行:Claude Code 的命令可以自定义。最推荐的方式是写 Markdown 形式的命令或技能;更深度的交互式命令需要插件或源码级 local / local-jsx 实现;工作流命令和 MCP skill 也能进入命令系统,但它们依赖对应特性或外部服务。

0. 整体流程图

命令工作流可以拆成四层:

  1. 命令来源:内置命令、.claude/skills、旧版 .claude/commands、插件、工作流;MCP skills 走独立的 MCP 命令集合。
  2. 加载过滤:loadAllCommands(cwd) 合并本地/插件/工作流来源,getCommands(cwd) 按可用性、开关和动态技能过滤。
  3. 用户输入解析:用户输入 /command args 后,processSlashCommand(...) 解析命令名和参数,并从当前命令表中查找命令。
  4. 执行分支:根据 Command.type 进入 promptlocallocal-jsx,其中 prompt 命令还可以通过 context: fork 进入子代理执行;MCP skills 主要通过单独筛选后进入模型可调用技能集合。

1. 命令的核心类型

源码中的命令统一抽象为 Command,定义在 src/types/command.ts。它由公共字段 CommandBase 加上三种执行形态之一组成。

命令类型 源码类型 主要用途 是否适合用户自定义 执行结果
prompt PromptCommand 把 Markdown/文本内容扩展成模型输入;可作为技能被用户或 Claude 调用 适合,最推荐 生成用户消息,通常继续让模型处理;也可 context: fork 由子代理执行
local LocalCommand 执行本地 TypeScript/JS 模块逻辑,不渲染交互 UI 不适合普通配置,需要源码或插件代码 返回 textcompactskip
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 命令来源,例如 skillscommands_DEPRECATEDpluginbundledmcp
kind CommandBase 当前主要用于标记 workflow
isSensitive CommandBase 对敏感参数进行历史记录脱敏
allowedTools PromptCommand Markdown 命令中内联 shell 片段可用的工具 allowlist
model PromptCommand 命令或技能指定的模型
hooks PromptCommand 技能被调用时临时注册的 hooks
context PromptCommand inlineforkfork 会使用子代理单独执行
agent PromptCommand context: fork 时指定子代理类型
effort PromptCommand 子代理或模型推理 effort
paths PromptCommand 条件技能匹配文件路径后才激活

2. 命令从哪里来

src/commands.ts 是命令聚合入口。内置命令由 COMMANDS() 返回,技能、插件和工作流再由 loadAllCommands(cwd) 合并。

2.1 来源总表

来源 典型目录/入口 加载函数 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 由文件触达或动态发现机制插入

2.2 合并顺序

loadAllCommands(cwd) 的合并顺序是:

kotlin 复制代码
return [
  ...bundledSkills,
  ...builtinPluginSkills,
  ...skillDirCommands,
  ...workflowCommands,
  ...pluginCommands,
  ...pluginSkills,
  ...COMMANDS(),
]

这意味着命令列表中,技能和插件命令会出现在内置命令之前。运行时查找由 findCommand(...) 按数组顺序返回第一个匹配项,因此同名命令可能受到加载顺序影响。实践中建议自定义命令避免与内置命令重名。

2.3 过滤逻辑

getCommands(cwd) 会在合并后做两类过滤:

过滤点 源码函数 作用
可用性过滤 meetsAvailabilityRequirement(cmd) 根据 availability 限制命令只对某类认证/服务提供方可见
开关过滤 isCommandEnabled(cmd) 根据 feature flag、环境变量或运行时状态决定命令是否启用
动态技能插入 getDynamicSkills() 把运行时发现的技能插到插件技能之后、内置命令之前

3. Markdown 命令和技能如何加载

自定义命令最重要的入口在 src/skills/loadSkillsDir.tssrc/utils/markdownConfigLoader.ts

3.1 推荐目录:.claude/skills

技能目录只支持这一种结构:

objectivec 复制代码
.claude/
  skills/
    my-skill/
      SKILL.md

my-skill 会成为命令名,用户可以输入:

bash 复制代码
/my-skill 参数

如果放在用户目录,则是:

javascript 复制代码
~/.claude/
  skills/
    my-skill/
      SKILL.md

3.2 旧版目录:.claude/commands

旧版命令目录仍被支持:

objectivec 复制代码
.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(...) 会把子目录转换成冒号命名空间。

3.3 加载范围和优先级

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>,会回退到主仓库对应目录。

4. Frontmatter 字段参考

Markdown 命令/技能通过 YAML frontmatter 定义元数据。字段解析主要在 src/utils/frontmatterParser.tssrc/skills/loadSkillsDir.ts

4.1 常用字段

字段 类型 用于 含义
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 inlinefork skill fork 会启动子代理执行
agent string fork skill 指定子代理类型
effort string 或 integer fork skill 指定推理 effort
paths string 或 string[] skill 条件技能,匹配文件路径后才激活
shell bashpowershell skill/command 控制 Markdown 内 ! shell 片段使用的 shell

4.2 最小自定义技能示例

yaml 复制代码
---
description: 生成当前改动的代码审查清单
argument-hint: "[重点区域]"
when_to_use: 用户希望快速审查当前分支或工作区改动时
---
​
请审查当前仓库的改动,重点关注:$ARGUMENTS。
​
输出:
1. 高风险问题
2. 可维护性问题
3. 建议补充的测试
4. 可以直接合并的理由或阻塞项

放到:

bash 复制代码
.claude/skills/review-changes/SKILL.md

调用:

bash 复制代码
/review-changes auth 模块

4.3 带命名参数的示例

yaml 复制代码
---
description: 根据 issue 编号生成修复计划
argument-hint: "<issue> <scope>"
arguments:
  - issue
  - scope
---

请为 issue $issue 生成修复计划。

范围:$scope

要求:
- 先定位相关文件
- 说明风险
- 给出测试计划

命令内容最终会通过 substituteArguments(...) 替换参数。

4.4 只能由 Claude 调用的技能

yaml 复制代码
---
description: 安全审计技能
when_to_use: 当任务涉及认证、权限、外部输入、密钥或命令执行时使用
user-invocable: false
---

对当前任务做安全审计,重点检查注入、权限绕过、敏感信息泄露和不安全默认值。

用户直接输入 /security-audit 时会收到提示:该技能只能由 Claude 调用。用户可以说"请使用 security-audit 技能"。

4.5 fork 子代理技能

yaml 复制代码
---
description: 在独立上下文中做大型代码审查
when_to_use: 当改动跨多个模块,需要独立上下文审查时使用
context: fork
agent: code-reviewer
effort: high
---

请审查当前分支的代码改动,输出关键问题和建议。

执行时不会把技能正文简单塞进当前对话,而是走 executeForkedSlashCommand(...),调用 runAgent(...) 在子代理中执行。

4.6 带 hooks 的技能

yaml 复制代码
---
description: 编辑 TypeScript 后自动检查
hooks:
  PostToolUse:
    - matcher: "Write|Edit|MultiEdit"
      hooks:
        - type: command
          command: "npm run typecheck"
          timeout: 60
---

请实现用户请求,并在修改 TypeScript 文件后自动触发类型检查。

源码中 getMessagesForPromptSlashCommand(...) 会在命令带有 hooks 时调用 registerSkillHooks(...),这些 hook 以会话级方式注册。

5. 用户输入 /command 后发生什么

src/utils/processUserInput/processSlashCommand.tsx 负责 slash command 的运行时处理。

5.1 解析与查找

流程如下:

步骤 源码函数 行为
解析输入 parseSlashCommand(inputString) 拆出 commandNameargsisMcp
判断存在 hasCommand(commandName, context.options.commands) 不存在时判断是否像文件路径;否则返回 Unknown skill
获取命令 getCommand(commandName, context.options.commands) nameuserFacingName()aliases 查找
检查调用权限 command.userInvocable === false 禁止用户直接调用,只允许 Claude 使用 SkillTool
分支执行 switch (command.type) 进入 local-jsxlocalprompt 分支

findCommand(...) 的匹配逻辑是:

scss 复制代码
_.name === commandName ||
getCommandName(_) === commandName ||
_.aliases?.includes(commandName)

5.2 prompt 命令执行

普通 prompt 命令会:

  1. 调用 command.getPromptForCommand(args, context) 生成内容块。
  2. 注册技能 hooks(如果 frontmatter 中定义了 hooks)。
  3. 解析 allowedTools,把额外工具权限传给后续模型处理。
  4. 创建用户消息,通常设置 shouldQuery: true,让 Claude 接着处理。

Markdown 技能的 getPromptForCommand(...) 还会做这些替换/处理:

处理 说明
添加 base directory baseDir 时,正文前会加 Base directory for this skill: ...
参数替换 通过 $ARGUMENTS 或命名参数替换用户输入
${CLAUDE_SKILL_DIR} 替换成技能目录,便于引用技能自带脚本
${CLAUDE_SESSION_ID} 替换成当前 session id
执行 Markdown 内 shell 片段 非 MCP 技能可以执行 ! shell 片段;MCP 技能禁止执行

5.3 context: fork 的执行

PromptCommand.context === 'fork' 时,流程进入 executeForkedSlashCommand(...)

阶段 行为
准备上下文 prepareForkedCommandContext(...) 生成技能内容、agent 定义、prompt messages
创建 agent id createAgentId()
启动子代理 runAgent(...) 使用指定 agentmodeleffort 和可用工具
收集结果 extractResultText(...) 提取子代理最终输出
返回结果 结果包装成 <local-command-stdout>,不再直接让主模型查询

在 KAIROS/assistant 模式下,fork 命令还可能以后台方式运行,完成后把结果重新放回消息队列。

5.4 local 命令执行

local 命令是源码或插件中的 JS/TS 模块。执行逻辑:

  1. 先构造用户输入消息。

  2. await command.load() 懒加载模块。

  3. 调用 mod.call(args, context)

  4. 根据返回结果处理:

    • { type: 'skip' }:不写消息,不继续查询。
    • { type: 'text', value }:输出 <local-command-stdout>
    • { type: 'compact', compactionResult }:进入上下文压缩后的消息构造流程。

5.5 local-jsx 命令执行

local-jsx 命令适合交互 UI,例如选择器、设置面板。执行逻辑:

  1. command.load() 懒加载 JSX 模块。

  2. 调用 mod.call(onDone, context, args)

  3. 返回 React 节点后通过 setToolJSX(...) 渲染。

  4. UI 完成后调用 onDone(result, options)

  5. options 可控制:

    • display: 'skip' | 'system' | 'user'
    • shouldQuery
    • metaMessages
    • nextInput
    • submitNextInput

6. 命令是否可以自定义

可以,但不同层级能力不同。

自定义方式 是否需要写代码 能力 适用场景 推荐程度
.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 本项目或实现内置级交互命令 仅适合维护者

7. 自定义命令实战模板

7.1 项目级命令:生成 PR 描述

文件:.claude/skills/pr-description/SKILL.md

yaml 复制代码
---
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
- 明确指出未验证项

调用:

bash 复制代码
/pr-description main

7.2 只能模型调用的领域技能

文件:.claude/skills/db-review/SKILL.md

yaml 复制代码
---
description: 数据库变更审查
when_to_use: 当任务涉及 SQL、migration、索引、事务或数据库性能时使用
disable-model-invocation: false
user-invocable: false
---

请审查数据库相关改动,重点检查:
- migration 是否可回滚
- 大表 DDL 是否会长时间锁表
- 查询是否有合适索引
- 是否存在 SQL 注入风险

用户不能直接 /db-review,但可以要求 Claude 使用该技能。

7.3 按路径激活的条件技能

yaml 复制代码
---
description: React 组件审查
when_to_use: 当修改 React/TSX 文件时检查组件结构和状态管理
paths:
  - "src/**/*.tsx"
  - "app/**/*.tsx"
---

当用户修改 React 组件时,检查 props 类型、状态管理、副作用和可访问性。

paths 的技能会先进入 conditional skill 存储,只有匹配路径被触达后才激活。

8. 命令和 SkillTool 的关系

Claude Code 把很多 prompt 型命令也暴露给模型作为 SkillTool 可调用能力。

函数 作用
getSkillToolCommands(cwd) 返回模型可调用的 prompt commands,包括 .claude/skills、bundled skills 和旧版 .claude/commands
getSlashCommandToolSkills(cwd) 更偏向"技能"列表,要求有 descriptionwhenToUse,并来自 skills/plugin/bundled 等来源
getMcpSkillCommands(mcpCommands) 从 MCP 命令中筛出 prompt 型、模型可调用、loadedFrom: mcp 的技能

影响模型是否可调用的关键字段:

字段 效果
disable-model-invocation: true 不进入模型可调用技能列表
user-invocable: false 用户不能直接 /name 调用,但模型仍可调用,除非同时禁用模型调用
description / when_to_use 对插件/MCP 类技能尤其重要,决定是否展示给模型或工具列表

8.1 Skill 变成命令的完整流程

从 Markdown skill 文件到可执行命令,经历以下阶段:

8.1.1 加载阶段:Markdown → PromptCommand
bash 复制代码
.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.tsgetPromptForCommand() (344-405行)

8.1.2 用户输入 /skill-name 执行路径
css 复制代码
用户输入: /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 时:

scss 复制代码
模型输出: 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

9. 命令和权限的关系

命令系统本身不等同于工具权限系统,但它会影响后续模型或内联 shell 的权限边界。

9.1 allowed-tools

Markdown 命令 frontmatter 的 allowed-tools 会被解析为工具列表。对于 prompt 命令:

  • 会传到 slash command 结果的 allowedTools
  • 在 Markdown 内联 shell 执行时,会被写入 alwaysAllowRules.command,让该命令内部的 shell 片段按命令级 allowlist 执行。

9.2 MCP 技能的限制

MCP skills 被视为远端/不可信来源,源码中明确禁止执行其 Markdown 正文里的内联 shell 片段。也就是说:

来源 是否执行 Markdown 内 ! shell 片段
本地 skills / commands 可以,受权限与 allowed-tools 影响
bundled / plugin skills 取决于加载来源与策略
MCP skills 不执行

9.3 Remote / Bridge 模式限制

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

10. 与 hooks 的关系

命令和 hooks 是两套机制,但可以互相连接:

连接点 说明
技能 frontmatter 中声明 hooks 技能被调用后,通过 registerSkillHooks(...) 注册会话级 hooks
命令触发工具调用 prompt 命令本身会变成模型输入,模型后续使用工具时仍会触发 hooks
allowed-tools 与 hooks 双重约束 命令可限定工具列表,hooks 可在工具真正执行前后审计或阻断
fork 技能与 Subagent hooks context: fork 会启动子代理,相关生命周期可能触发子代理 hooks

建议:

目标 用命令还是 hook
让用户主动触发某套流程 用命令/技能
在工具执行前后自动校验 用 hook
给某个技能附带临时质量门禁 在技能 frontmatter 中声明 hooks
对所有项目统一拦截危险行为 用全局或项目级 hook

11. 源码级原理说明

11.1 加载原理

getCommands(cwd) 是命令表入口。它先调用 memoized 的 loadAllCommands(cwd),再做可用性过滤和动态技能插入。

核心链路:

scss 复制代码
getCommands(cwd)
  └─ loadAllCommands(cwd)
      ├─ getSkills(cwd)
      │   ├─ getSkillDirCommands(cwd)
      │   ├─ getPluginSkills()
      │   ├─ getBundledSkills()
      │   └─ getBuiltinPluginSkillCommands()
      ├─ getPluginCommands()
      ├─ getWorkflowCommands(cwd)
      └─ COMMANDS()

getSkillDirCommands(cwd) 又会加载:

bash 复制代码
managed .claude/skills
user ~/.claude/skills
project .claude/skills
additional --add-dir .claude/skills
legacy .claude/commands

11.2 Markdown 转 Command 的原理

SKILL.md 或旧版 .md 文件会经历:

scss 复制代码
读取 Markdown
  → parseFrontmatter(...)
  → parseSkillFrontmatterFields(...)
  → createSkillCommand(...)
  → 返回 type: 'prompt' 的 Command

createSkillCommand(...) 生成的命令固定是 type: 'prompt'。因此 Markdown 自定义命令不是直接执行 JS 函数,而是生成一段 prompt 内容,让 Claude Code 把它作为用户消息或子代理任务处理。

11.3 执行原理

用户输入 /name args 后:

scss 复制代码
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(...)

11.4 为什么 Markdown 命令不能直接做任意 UI

Markdown 命令最终被转成 PromptCommand。它只有 getPromptForCommand(...),不能直接返回 React 节点,也不能直接实现 onDone 交互流程。需要交互 UI 的命令必须是 local-jsx,也就是源码或插件代码提供的命令。

11.5 为什么自定义命令推荐写成 skills

skills 目录是当前更完整的能力模型:

  • 支持 SKILL.md 目录结构和技能资源文件。
  • 支持 ${CLAUDE_SKILL_DIR} 引用技能目录。
  • 支持 paths 条件激活。
  • 支持 when_to_use 给模型选择技能。
  • 支持 context: fork 启动子代理。
  • 支持技能级 hooks

旧版 .claude/commands 仍能用,但源码中标记为 commands_DEPRECATED,适合兼容,不建议作为新设计的首选。

12. 使用建议与最佳实践

场景 推荐做法 原因
团队共享常用流程 提交 .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 才是强约束点
需要模型自动选择 写清 descriptionwhen_to_use 模型依赖这些字段判断是否使用技能
需要隔离上下文 使用 context: fork 大任务不会污染主上下文,且有独立 token 预算

13. 常见问题

13.1 /commands/skills 有什么区别

.claude/commands 是旧版 Markdown 命令目录;.claude/skills 是更完整的技能目录。两者最终都会被转成 type: 'prompt'Command,但 skills 支持更明确的目录结构、资源引用、条件激活和模型技能语义。

13.2 自定义命令能覆盖内置命令吗

命令合并时自定义技能在内置命令之前,查找时返回第一个匹配项,因此同名有可能影响解析结果。但不建议依赖覆盖行为,最好避免与内置命令重名。

13.3 Markdown 命令能直接执行 shell 吗

可以使用 Markdown 内联 shell 片段,但要受权限和 allowed-tools 影响。MCP skills 不会执行内联 shell。涉及危险操作时,建议用 hooks 做强制约束。

13.4 user-invocable: false 是什么效果

用户不能直接输入 /skill-name 调用;如果模型可调用未禁用,Claude 仍可通过 SkillTool 使用它。

13.5 context: fork 和普通命令区别是什么

普通 prompt 命令会把内容加入当前对话;context: fork 会启动子代理在独立上下文中执行,最终把结果返回给主流程。

13.6 命令会自动出现在模型可用技能里吗

不一定。模型可调用技能通常要求是 prompt 类型、不是 builtin、没有 disableModelInvocation,并且有合适的 descriptionwhen_to_use。插件/MCP 类技能对显式描述要求更高。

14. 关键源码位置

文件 关键函数/类型 说明
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 解析

15. 快速决策表

你想做什么 应该用什么
增加一个 /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 或插件

16. 参考文献

名称 地址 说明
claude-code-rev github.com/kunge2013/c... 本文源码分析参考的 Claude Code 恢复版仓库
Hooks 文档 ./claude-code-hooks.md 本文关联的 hooks 生命周期与配置说明
相关推荐
明月_清风1 小时前
Go 没有 `class`,如何实现面向对象三要素?与传统 OOP 的深度对比
后端·go
一切皆是因缘际会1 小时前
人工智能从对话工具向自主生产力跃迁
人工智能·深度学习·ai·重构
搬砖的小码农_Sky2 小时前
如何用Nvidia Geforce RTX 5060 Ti显卡进行本地Whisper语音转文字任务?
人工智能·ai·whisper·gpu算力
波动几何2 小时前
工作流重构方法技能workflow-refactor
人工智能
nix.gnehc2 小时前
从范式到工程:Plan & Execute + Nacos MCP 构建 AI Agent 的实践之路
人工智能·agent·mcp
xixingzhe22 小时前
spring构造函数注入对比@Resource
java·后端·spring
工一木子2 小时前
Browser MCP:让 Cursor 直接操控你的真实浏览器
人工智能
程序员牛奶2 小时前
[Algo-2]双指针技巧:你真的学懂双指针了吗?
后端
测试员周周2 小时前
【Appium 系列】第17节-XMind用例转换 — 从思维导图到 YAML
java·服务器·人工智能·单元测试·appium·测试用例·xmind