15-commit命令
15. commit 命令所属分组命令系统概述/commit是 Claude Code 中最具代表性的 prompt 类斜杠命令。它本身不含任何 git 操作代码——既不调用simple-git也不直接child_process.exec——而是通过一份精心设计的 prompt 模板把读 status、读 diff、读最近 commit、起草 message、执行 commit整条链路完全交给模型与 Bash 工具完成。这种prompt-as-command的设计哲学是 Claude Code 命令系统的核心命令作者只需要描述目标和约束执行细节由模型在运行时编排。本文拆解commands/commit.ts这 90 行源码看它如何用一段模板字符串、一份 allowedTools 白名单、以及executeShellCommandsInPrompt提供的内联 shell 占位符机制完成一次安全的 git 提交。源码位置[commands/commit.ts](file:///e:/2026plan/AI_Lab/claude-code-sourcemap-main/restored-src/src/commands/commit.ts)[utils/promptShellExecution.ts](file:///e:/2026plan/AI_Lab/claude-code-sourcemap-main/restored-src/src/utils/promptShellExecution.ts)[utils/attribution.ts](file:///e:/2026plan/AI_Lab/claude-code-sourcemap-main/restored-src/src/utils/attribution.ts)[utils/undercover.ts](file:///e:/2026plan/AI_Lab/claude-code-sourcemap-main/restored-src/src/utils/undercover.ts)核心实现分析1. 命令骨架极简的 PromptCommandcommit.ts导出的command对象是上一篇介绍的PromptCommand形态的典型样本constcommand{type:prompt,name:commit,description:Create a git commit,allowedTools:ALLOWED_TOOLS,contentLength:0,// Dynamic contentprogressMessage:creating commit,source:builtin,asyncgetPromptForCommand(_args,context){/* ... */},}satisfies Command几个关键字段的含义type: prompt声明这是 prompt 类命令REPL 不会直接执行它而是把getPromptForCommand返回的ContentBlockParam[]注入到模型上下文让模型用工具完成任务。allowedTools: ALLOWED_TOOLS本次命令执行期间模型只能使用这些工具其他工具一律拒绝。这是 prompt 命令的权限收敛机制。contentLength: 0注释写明 “Dynamic content”因为真实 prompt 内容包含git status、git diff等命令输出长度在运行时才确定。该字段用于 token 估算0 表示动态。progressMessage: creating commitUI 上显示的进度文案。source: builtin标记为内置命令影响 typeahead 中的来源标注参考formatDescriptionWithSource。2. allowedTools最小权限白名单ALLOWED_TOOLS是一个三元素数组constALLOWED_TOOLS[Bash(git add:*),Bash(git status:*),Bash(git commit:*),]字符串形如Bash(git add:*)是 Claude Code 工具权限语法中前缀匹配 通配的写法允许任何以git add开头的 Bash 命令但git push、git reset、rm等一律被拦截。这一白名单在两处生效getPromptForCommand中通过getAppState()临时覆盖toolPermissionContext.alwaysAllowRules.command让这些 git 子命令免确认直接放行REPL 在命令执行期间把allowedTools作为整体工具白名单应用模型即使想调用其他工具也调不动。这种双重锁定让/commit在便利性和安全性之间取得平衡用户输入/commit后无需为每一条git add、git status、git commit单独确认但又无法借/commit之名做git push --force之类的危险操作。3. Prompt 模板Context Safety Protocol TaskgetPromptContent()函数返回的模板是整个命令的灵魂。它由三部分组成Context上下文采集## Context-Current git status:!git status-Current gitdiff(staged and unstaged changes):!git diff HEAD-Current branch:!git branch --show-current-Recent commits:!git log --oneline -10注意!git status这种内联反引号语法——它是executeShellCommandsInPrompt提供的内联 shell 占位符。在 prompt 真正发给模型之前executeShellCommandsInPrompt会扫描整段文本把这些!...替换成对应命令的实际输出。这意味着模型看到的是已经填好的真实 git status、真实 diff、真实最近 10 条 commit而不是请你自己运行 git status的指令。这种占位符预执行模式有两个好处第一模型不需要消耗工具调用额度去采集上下文一次 commit 任务的工具调用次数大幅下降第二prompt 长度更可预测避免模型因为漏跑 status 而瞎猜。Git Safety Protocol安全约束- NEVER update the git config - NEVER skip hooks (--no-verify, --no-gpg-sign, etc) unless the user explicitly requests it - CRITICAL: ALWAYS create NEW commits. NEVER use git commit --amend, unless the user explicitly requests it - Do not commit files that likely contain secrets (.env, credentials.json, etc). Warn the user if they specifically request to commit those files - If there are no changes to commit (i.e., no untracked files and no modifications), do not create an empty commit - Never use git commands with the -i flag (like git rebase -i or git add -i) since they require interactive input which is not supported这段是给模型的硬性禁令。其中NEVER use--amend和NEVER skip hooks是两条最关键的红线——它们防止模型为了让 commit 看起来更整洁而改写历史或绕过 CI 检查。-iflag那条则是技术约束交互式 git 命令在 Claude Code 的非交互 Bash 工具下无法工作。Your task任务步骤1. Analyze all staged changes and draft a commit message: - Look at the recent commits above to follow this repositorys commit message style - Summarize the nature of the changes (new feature, enhancement, bug fix, refactoring, test, docs, etc.) - Ensure the message accurately reflects the changes and their purpose - Draft a concise (1-2 sentences) commit message that focuses on the why rather than the what 2. Stage relevant files and create the commit using HEREDOC syntax: git commit -m $(cat EOF Commit message here.${commitAttribution ? \n\n${commitAttribution} : } EOF )任务部分把 commit 拆成起草 message和执行 commit两步并显式要求用 HEREDOC 语法。HEREDOC 是为了避免多行 commit message 在 shell 中被截断或转义出错——这是模型生成 shell 命令时最容易翻车的地方所以模板直接给出固定模板让模型只填 message 内容。模板末尾的${commitAttribution}是动态拼接的署名文本由getAttributionTexts()返回。比如对外发行版可能附加Generated with Claude Code内部 undercover 模式则返回空字符串以隐藏来源。4.getPromptForCommand占位符预执行 权限注入getPromptForCommand是PromptCommand接口的实现它做了三件事asyncgetPromptForCommand(_args,context){constpromptContentgetPromptContent()constfinalContentawaitexecuteShellCommandsInPrompt(promptContent,{...context,getAppState(){constappStatecontext.getAppState()return{...appState,toolPermissionContext:{...appState.toolPermissionContext,alwaysAllowRules:{...appState.toolPermissionContext.alwaysAllowRules,command:ALLOWED_TOOLS,},},}},},/commit,)return[{type:text,text:finalContent}]}第一调用getPromptContent()拿到模板字符串。第二把模板交给executeShellCommandsInPrompt让它把!git status等占位符替换成真实输出。第三返回[{ type: text, text: finalContent }]这是ContentBlockParam[]格式REPL 会把它作为用户消息注入对话。值得注意的是中间那段getAppState()覆盖它不是修改全局 app state而是返回一个局部副本把alwaysAllowRules.command替换成ALLOWED_TOOLS。这样executeShellCommandsInPrompt在执行git status、git diff等占位符命令时就不会触发权限确认弹窗——因为占位符命令也是通过 Bash 工具跑的没有这道覆盖用户会被四次权限弹窗打断。executeShellCommandsInPrompt的实现位于utils/promptShellExecution.ts它用BLOCK_PATTERN与INLINE_PATTERN两个正则扫描文本把所有匹配到的命令通过BashTool执行再把输出回填到原文中。注释里还提到一个性能细节INLINE_PATTERN在大文本上比BLOCK_PATTERN慢 100 倍所以先用text.includes(!)做廉价预筛93% 没有 ! 的 skill 直接跳过昂贵扫描。5. Undercover 模式内部用户的隐藏身份getPromptContent()开头有一段条件分支letprefixif(process.env.USER_TYPEantisUndercover()){prefixgetUndercoverInstructions()\n}USER_TYPE ant表示 Anthropic 内部用户isUndercover()则是一种隐藏身份开关。开启时prompt 头部会拼上getUndercoverInstructions()返回的额外指令要求模型在 commit message、PR 描述等对外可见的产物中不暴露由 Claude 生成的信息。与之配合getAttributionTexts()在 undercover 模式下返回{ commit: , pr: }让模板里的${commitAttribution}拼接出空字符串。这套机制让 Anthropic 内部工程师在用 Claude Code 处理对外开源仓库的 commit 时不会自动带上Generated with Claude Code的署名避免污染开源项目的 commit 历史。关键设计要点Prompt-as-command/commit不写任何 git 逻辑只写一份 prompt 模板。命令作者专注于目标和约束执行细节由模型编排。这让命令极薄90 行、极容易维护也极容易复制改造用户可以写自己的/my-commitskill。占位符预执行!git status语法让上下文采集发生在 prompt 注入之前模型拿到的是已填好的真实数据省下大量工具调用也避免了模型忘记看 diff这类失误。最小权限白名单allowedTools收敛到git add/status/commit三个前缀加上alwaysAllowRules临时覆盖既免确认又防越权——git push、git reset --hard等危险操作在/commit期间根本调不动。HEREDOC 模板commit message 多行场景下shell 转义是模型最容易出错的地方。模板直接给出git commit -m $(cat EOF ... EOF)固定骨架模型只填消息正文把 shell 注入风险降到最低。Safety Protocol 写进 prompt除了工具白名单这种硬约束模板还把–amend/–no-verify 不许用secrets 文件要警告等软约束直接写进 prompt 文本。这是 prompt-engineering 视角的安全防线与工具白名单形成纵深防御。与其他模块的关系utils/promptShellExecution.ts/commit的占位符预执行完全依赖它的BLOCK_PATTERN/INLINE_PATTERN扫描与BashTool调用。同一套机制也是所有 skill 模板的执行基础。utils/attribution.ts提供commit、pr两种署名文本决定 commit message 末尾是否附加Generated with Claude Code。utils/undercover.ts内部 undercover 模式的开关与指令来源被commit.ts与attribution.ts共同消费。commands.tscommit被列入INTERNAL_ONLY_COMMANDS意味着它仅在USER_TYPE ant的内部构建中可见外部发行版不会出现/commit命令。tools/BashTool/占位符命令与最终的git commit都通过 BashTool 执行allowedTools的Bash(git add:*)语法正是 BashTool 权限规则的消费方。commands/commit-push-pr.ts、commands/autofix-pr/同属 git 工作流命令家族复用相同的 prompt 模式与 attribution 机制。小结/commit是理解 Claude Code 命令系统设计哲学的最佳样本。它用 90 行代码展示了prompt-as-command的三件套一份带占位符的模板、一份 allowedTools 白名单、一段写进 prompt 的安全约束。命令本身不碰 git却能让模型安全地完成看 diff → 仿风格 → 写 message → 跑 commit全流程。这种把命令作者从写执行逻辑解放到写目标和约束的范式是后续/review、/plan、/security-review等命令的共同底座也是用户自定义 skill 的标准范式。