用途
commit 技能用于把工作区(working tree)中尚未提交的改动,整理成逻辑清晰、原子化的 Conventional Commits,但只处理属于当前分支的文件。它特别强调一个不那么显而易见的价值:越界防护(out-of-scope guard)。在多 worktree 与多智能体(multi-agent)的开发环境中,工作区里很容易残留属于其他分支的杂散文件,而该技能绝不会把它们顺手扫进提交里。
该技能是提交分组契约(commit-grouping contract)的唯一事实来源(single source of truth),有两种调用方式:
- 独立调用(/commit):“把我的 WIP 整理成提交。”它在当前分支上创建提交后即停止。它从不推送、不开 PR、不写 changelog,也不触碰 Linear。
- 嵌入 ship 流程(例如 /send-it):它是 lint 关口之前运行的提交步骤,使分支在 changelog/PR 工作开始前保持干净。提交之后的一切(lint、changelog、push、PR、Linear)由 ship 流程负责;本技能只负责暂存决策与提交本身。
配置
本技能旁边有一个 config.json(随附一份中性的 config.example.json 作为模板),其中只有一个配置项:
- 键:baseBranch
- 含义:用于计算分支 diff 的主干(origin/<baseBranch>),并据此计算合并基点以做范围分类。
- 默认值:"main"
在文档中 <base> 即 baseBranch 的值。
当 ship 流程委派给本技能时(例如 /send-it),它可以针对该次运行指定不同的 base 进行分类——例如 send-it 的 --base 用于堆叠式 PR(stacked PR)。应遵守调用方解析出的 base;只有在独立的 /commit 运行时,才回退到 config.json 中的 baseBranch。
流程
-
执行 git status --porcelain。若工作区干净,则无可提交内容——说明情况并停止。
-
检查未提交文件:用 git status --porcelain 获取清单,用 git diff 和 git diff --cached 查看具体改动块(hunks)。
-
过滤分支相关性,判断哪些未提交文件在范围内:
- 计算合并基点:git merge-base HEAD origin/<base>。
- 查看分支已直接改动过的文件:git diff --name-only <merge-base>...HEAD。
- 默认在范围内:未提交文件的路径出现在上述“分支已改动列表”中。
- 范围外(不确定):一旦分支已有自己的提交,其余所有文件都属此类。这刻意包括——仅位于分支改动过的目录中、但本身不是该分支改动路径的文件。共享目录不足以认领一个文件。来自其他分支或 worktree 的杂散文件很容易落进本分支恰好编辑过的目录里,而悄悄把它扫进去正是该防护要阻止的越界泄漏。除非用户明确确认,否则仅目录匹配的情况一律视为范围外。
-
lint 表面(开发者决定)。与范围判断分开,标记任何对 lint/format 配置文件的未提交改动(eslint.config.、.eslintrc、.markdownlint*、.prettierrc* / .prettierignore、.yamllint*、.shellcheckrc、.github/actionlint.yaml、eslint-suppressions.json),以及 diff 新增的任何忽略/禁用指令(eslint-disable*、markdownlint-disable*、# yamllint disable、prettier-ignore、shellcheck disable=)。这些由开发者定夺,详见 references/lint-surfaces.md。绝不要为了通过钩子(hook)而自行编写这类指令。当它已存在于工作区中时,可能是有意为之,因此不要还原它,也不要悄悄暂存它。
-
新分支(尚无自己的提交):没有可对比的“分支已改动列表”,因此无法区分自己的改动与其他分支或 worktree 留下的杂散文件。不要自动把每个未提交文件都提升为在范围内——那正是该防护要捕获的泄漏。把所有未提交文件视为不确定,在暂存任何文件前先让用户确认哪些属于本分支。
-
向用户展示暂存计划:按提议的提交分组列出在范围内文件,另附一份明确的“范围外/不确定”文件清单,标注为“uncertain — possibly from another branch/worktree”;再列出第 3 步中的任何 lint 表面改动,每项注明其文件(若是忽略指令则一并注明指令),标注为“developer decision — confirm”。询问:“Stage in-scope files and create the commits below? (yes / no / customise)”。不确定文件绝不自动暂存。确认范围同样覆盖所列的 lint 表面改动:计划已列明它们,开发者会在提交前看到。在新分支上,不确定清单即全部未提交文件(见第 3 步):请用户确认哪些属于本分支,只把已确认的文件视为在范围内。
-
把在范围内文件归入逻辑原子提交:
- 一个连贯单元一个提交(一个功能、一个修复、一次重构、一次文档变更、一次工具微调)。不要捆绑无关改动。
- 使用 Conventional Commits 主题(feat:、fix:、chore:、docs:、refactor:、perf:、test:),当 scope 明显时带上(feat(commit): …)。
- 标题(即整个第一行:类型、可选的 (scope)、可选的 !、:,然后是主题文本)保持在 72 字符以内。这是 Git 常见的主题上限,并非 Conventional Commits 规范的一部分。feat(commit): add out-of-scope guard 会计入类型、scope、冒号和空格——不只是冒号后的文本。若自然标题会超长,则重写直到符合:宁可先去掉可选的 scope,也不要丢失含义。不要从词中间用省略号截断,绝不丢弃类型前缀,也不要把标题折到第二行来规避上限(那会变成正文,而不是更短的标题)。
- 破坏性变更要如实标注:在类型/scope 后加 !(feat(api)!: …),和/或在正文中加 BREAKING CHANGE: 脚注。ship 流程会从这些提交信息中读回版本提升信号,因此标记必须准确。
-
确认后,用 git add <具体文件>(绝不 git add -A)和 git commit 创建提交。只暂存计划中列明的文件;范围外文件保留在工作区中,原封不动。用每个块一个 -m 来添加主题之外的正文或脚注——git commit -m "<subject>" -m "<body>";对于破坏性变更,用 git commit -m "feat(api)!: <subject>" -m "BREAKING CHANGE: <what changed and the migration>"(或 git commit -F <message-file> 用于更长正文)。无需正文时,单条 git commit -m "<subject>" 即可。
-
如果 pre-commit 钩子重排了文件,提交仍以格式化后的内容成功。
lint 表面
改变 linter 的配置方式,或让它视而不见,都是开发者的决定。规则(覆盖的表面、优先级顺序、报告内容)与 preflight 和 triage-pr 共享,位于 references/lint-surfaces.md,由仓库的 lib/lint-surfaces.md 引入。对本技能而言,暂存计划(第 4 步)是自然的停顿点:在那里列明每项 lint 表面改动;绝不要在提交中途停下来询问某项。
提交信息措辞
提交主题与正文使用消费仓库所记录的散文语言。在这一整套仓库中即英式英语(colour、behaviour、-ise/-yse)。这只管散文——绝不涉及标识符、依赖名或上游 API 字段名。
Conventional Commits 标题——整个第一行——必须 ≤ 72 字符。计入类型、可选 scope、可选 !、冒号与空格、以及主题。超长标题要重写;不要为了规避上限把它折进正文。正文与脚注的行长不在本技能范围内。
分组粒度——按组件拆分暂缓
/commit 按意图分组(每个连贯变更一个提交),不按组件或包边界分组。按组件做原子提交拆分(把文件归属到某个包并对每个包切一个提交)暂缓(R-374)——目前没有任何 estate 仓库做按组件路径归属。只有当出现一个公开的多包 monorepo、基于 squash 做按组件版本管理并进入 estate 时,才重新考虑。
参数
$ARGUMENTS