# Yy Commit

> 帮助用户创建规范的 Git 提交。 当用户想要提交代码、创建 commit、保存改动并提交时触发。 不适用于：仅暂存文件、仅查看 git 状态或历史、执行 push/pull/merge 等非提交操作、修改代码或实现功能。

- Skill: `bulls-cows/yy-commit` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add bulls-cows/yy-commit`
- Raw SKILL.md: https://api.skillmd.com/api/skills/bulls-cows/yy-commit/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: bulls-cows (https://skillmd.com/u/bulls-cows)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/bulls-cows/yy-commit

---


# yy-commit

## 描述

帮助用户创建规范的 Git 提交，生成符合项目自定义规范的提交信息或生成符合 Conventional Commits 规范的中文提交信息。

## 使用场景

- 用户说"提交代码"、"commit"、"创建提交"
- 用户说"提交一下"、"commit 一下"
- 用户说"保存已完成的改动并提交"
- 用户完成某个功能或修复后需要提交
- 用户说"提交 src/a.ts src/b.ts"、"提交 src/ 目录"（指定具体文件/目录）
- 用户说"提交全部文件"、"全部提交"（语义等价于提交所有改动）

不应触发：

- 用户只想暂存文件或执行 `git add`
- 用户只是查看 git 状态或历史
- 用户要求执行 git push、pull、merge 等其他操作
- 用户要求修改代码或实现功能

## 指令

### 步骤 1. 分析当前状态与运行模式

#### 参数解析

检测用户是否在调用时指定了文件/目录路径（如"提交 src/a.ts src/b.ts"、"提交 src/ 目录"、“提交全部变更”）。

**决策分支**：

- **用户指定了文件/目录路径**（包括"全部文件"、"所有文件"等语义等价表达）：标记为**用户指定提交范围模式**，仅对指定路径运行下方命令；跳过步骤 2（不判断原子性、不划定分组），直接以指定文件为提交范围，进入步骤 3
- **用户未指定文件/目录路径**：保持常规流程，对全部改动运行下方命令，后续进入步骤 2

并行运行以下命令（用户指定文件/目录时，`git diff` 和 `git diff --staged` 仅针对指定路径）：

```bash
git status
git diff
git diff --staged
git log --oneline -5
```

**git status 输出解读规则**：

- `Changes not staged for commit` + `modified:` 开头的行 → 有已修改文件，需要提交
- `Untracked files:` + 文件名 → 有新增文件，需要提交
- `Changes to be committed:` + 文件名 → 有已暂存文件，需要提交
- `nothing to commit, working tree clean` → 无改动，告知用户并终止流程
- `no changes added to commit` → 仅表示暂存区为空，不代表无改动；需结合上述条目判断

收集以下信息供后续步骤使用：

- 哪些文件已修改
- 变更的具体内容
- **当前是否已有暂存内容**（`git diff --staged` 非空时记录已暂存文件清单，用于步骤 4 的副作用提示）
- 最近的提交记录（用于步骤 3 推断项目提交风格）

**快速提交模式判定**：在本步骤一次性判定是否启用快速模式，避免后续步骤再做重复判断。

- 用户请求中包含"直接提交"、"立即提交"、"马上提交"、"无需确认"、"不用确认"、"跳过确认"、"直接 commit" 等明确表达 → 标记为快速提交模式
- 用户表达虽不在上述关键词内但语义等价（如"别再问我了"、"go ahead"）→ 同样标记为快速提交模式
- 其他情况 → 常规模式

### 步骤 2. 理解改动并划定提交范围

> **前置条件**：仅在步骤 1 未检测到用户指定文件/目录时执行本步骤。用户已指定提交范围时，跳过本步骤直接进入步骤 3。

#### 2.1 理解改动意图

综合以下信息理解此次提交的目的：

1. **代码变更分析**：从 diff 中识别新增功能、修复问题、优化重构、文档更新等
2. **对话上下文**：提取用户想要实现的功能、提到的问题或需求
3. **变更范围**：确定影响的模块或功能区域
4. **深层理解**：不仅理解"做了什么"，更要理解"为什么要这么做"

**改动归属与范围规则**：

- 以当前 `git status`、`git diff` 和 `git diff --staged` 为提交范围的完整事实来源；已暂存、未暂存和未跟踪的改动均默认属于用户
- 对话上下文只用于理解改动意图和生成提交信息，不得据此排除、覆盖或还原工作区中未在对话里提及的改动
- 无法将某项改动对应到对话需求时，展示该项并询问用户如何分组；不得将其认定为多余改动或跳过重新暂存
- 最终选定的每个文件均按当前完整内容暂存；不得仅保留其中与对话需求相关的代码块，或自动还原其余代码块

#### 2.2 划定本次提交分组

以原子性为主要标准判断所有改动内容是否适合一次性提交。

**本技能一次只处理一个原子单元的提交**。如不适合一次性提交，告知用户哪些改动适合作为分组，让用户从以下选项中选择：

1. 选择单个分组提交（单选）
2. 强制一次性提交全部改动
3. 自动分组提交（分别提交每个分组）

**选项处理规则**：

- **选择单个分组**：以选定的分组为提交范围，进入步骤 3
- **强制一次性提交**：以全部改动为提交范围，进入步骤 3
- **自动分组提交**：进入循环流程，为每个分组依次执行步骤 3-5，直到所有分组提交完成

**原子性判断标准**：

- 核心标准：一次提交是否构成一个原子逻辑单元——所有改动围绕同一意图、共享同一回滚边界，单独取出任一部分都不影响其余部分的可工作性
- 如果根据核心标准仍无法明确如何分组，参考 [resources/atomicity-criteria.md](./resources/atomicity-criteria.md) 中的具体参考信号进行分组判断

### 步骤 3. 生成提交信息

**确定提交信息生成规范**：

- **项目已定义提交规范**（在 AGENTS.md、README 或其他明确规则中声明）：必须优先使用项目约定的规范
- **项目未定义提交规范**：使用[定制版 Conventional Commits 规范](./resources/conventional-commits.md)

**复杂改动提供候选选项：**

当单次改动涉及多个维度（如同时包含功能新增和重构），或改动意图可以从不同角度描述时，提供 2 个候选提交信息供用户选择，帮助用户找到最准确的表达。简单改动（意图明确、变更单一）只需生成 1 个即可。

**候选选项展示与选择交互**：

- 在候选列表中提供"重新生成"和"自填提交信息"两个出口
- 用户选定候选后进入步骤 4 的最终确认；用户选择"重新生成"时回到本步骤重新生成候选；用户选择"自填提交信息"时直接收集用户输入并跳到步骤 4
- **快速提交模式（步骤 1 已判定）**：直接生成 1 个候选并进入步骤 4 展示，跳过候选展示与选择交互

### 步骤 4. 展示并确认

向用户展示：

1. **暂存区副作用提示**（仅当步骤 1 检测到 `git diff --staged` 非空，且本次提交的暂存范围与已有暂存内容不一致时）：明确告知"执行提交前会先 `git reset HEAD` 重建暂存区，再按以下文件的当前完整内容重新暂存；该操作不会还原工作区文件"，并在用户确认前等待用户明确同意
2. 将要暂存的文件列表，确认其中包含用户要求提交的全部手动改动
3. 敏感文件警告（如暂存列表中存在敏感命名特征文件，详见 [resources/sensitive-file-patterns.md](./resources/sensitive-file-patterns.md)；新增文件触发强制阻塞，修改/删除文件仅警告）
4. 关键变更摘要（从 diff 提取的主要改动）
5. 生成的完整提交信息（含 body）
6. **主分支保护提醒**（当步骤 1 检测到 `git status` 输出包含 `On branch main` 或 `On branch master` 时，始终执行）：提示用户当前位于主分支，建议在功能分支工作

**快速提交模式的限制：**

- 主分支提醒不受快速提交模式影响，始终执行
- **暂存区副作用强制阻塞**：步骤 1 检测到已有暂存内容且与本次提交范围不一致时，即使命中快速提交模式也必须停止流程，回退到常规确认
- **新增敏感文件强制阻塞**：暂存列表中存在新增的敏感命名特征文件时，即使命中快速提交模式也必须停止流程，回退到常规确认；用户须明确豁免该文件或将其从暂存列表移除后才能继续
- **修改/删除敏感文件警告**：暂存列表中存在修改或删除的敏感命名特征文件时，展示警告提示但不阻塞流程

**常规模式：** 以编号列表向用户展示确认选项

```text
1. 确认提交
0. 取消提交
```

**用户选择处理**：

- **选 `1`**：进入步骤 5 执行提交
- **选 `0`**：终止本次提交流程，输出"已取消"
- **用户表达修改意见**：按"修改提交信息规则"重新生成，回到步骤 4 重新展示确认选项
- **用户直接给出完整提交信息**：以用户提供的内容为准，回到步骤 4 展示并等待最终确认
- **用户回复内容意图不明确**（既非确认/取消编号，也非可识别的修改意见或完整提交信息）：主动询问具体哪里不满意，引导用户表达修改方向

**修改提交信息规则：**

- 默认将用户反馈视为对冒号后 `description` 语义的修正，不要求用户手工重写前缀
- 基于新 description、代码改动、对话上下文与项目规范，重新评估前缀是否匹配；按以下优先级处理：
  - **项目有固定前缀规范**：前缀必须优先适配到项目规范（如 `功能:`、`重构:`、`文档:`）
  - **项目无固定规范且当前前缀与改动性质匹配**：保留前缀，只调整 description
  - **项目无固定规范但前缀与改动性质不匹配**：自动重新推导 type/scope
- 用户只指出"标题不合适"、"这不是修复"、"这其实是需求"等模糊反馈 → 视为允许 AI 根据真实改动自动重算前缀
- 仅当用户明确指定前缀类型时，才将该前缀作为强约束保留
- 如果提交信息包含 body（默认情况），优先保留 body，只更新受用户反馈直接影响的部分
- 仅在用户明确要求重新生成整个提交信息时，才重新生成完整的提交信息

展示格式（项目使用中文前缀规范时，将示例中的 `feat(auth):` 替换为对应的中文前缀，如 `功能:`）：

```text
即将提交以下更改：

⚠️ 检测到当前暂存区已有改动，提交前将先清空暂存区，仅保留以下文件（仅在副作用场景出现）

📝 暂存文件：
  - src/auth/login.ts
  - src/components/LoginForm.vue
  - vite.config.ts

⚠️ 敏感文件警告：
  - [新增] .env（环境变量文件）
  - [修改] config/credentials.json（凭据文件）

📊 主要变更：
  - 新增 JWT token 生成逻辑
  - 添加登录表单验证
  - 调整 vite 构建别名以支持新模块

💬 提交信息：
feat(auth): 添加 JWT 用户认证功能

- 新增 JWT token 生成逻辑
- 添加登录表单验证
- 补充登录失败场景的错误提示

⚠️ 当前位于 main 分支，建议在功能分支工作。

是否确认提交？
```

### 步骤 5. 执行提交

用户确认后，按顺序执行（不可并行）。

```bash
# 1. 清空暂存区，重建最终选定的暂存内容；不会修改工作区文件
git reset HEAD

# 2. 按当前完整内容暂存选定的文件，不得根据对话上下文筛除代码块
git add <file1> <file2> ...

# 3. 创建提交
# 默认：title + body
git commit -m "<title>" -m "<body>"

# 仅 title
git commit -m "<title>"
```

**提交命令约束：**

- 提交信息必须使用 `-m` 参数传递，禁止使用 heredoc（`cat <<'EOF'`）、here-string（`@'...'@`）或其他 shell 特定语法
- 多行提交信息使用多个 `-m` 参数：`git commit -m "<title>" -m "<body line 1>" -m "<body line 2>"`，此写法在 bash、pwsh、zsh 等主流 shell 中均兼容
- 提交信息中的引号与特殊字符处理：优先使用**单引号**包裹 `-m` 参数值（`-m '<title>'`），避免 shell 对 `$`、`` ` ``、`\` 等字符做变量展开或命令替换；若 message 包含单引号，则改用双引号包裹并对 `$`、`` ` ``、`\`、`"` 做反斜杠转义
- **构造 `git commit` 命令后，确认命令中的每个 `-m` 参数值完整、未被截断，再执行**

**禁止事项：**

- 禁止添加 `Co-Authored-By:` 等署名备注
- 禁止使用 heredoc、here-string 或其他 shell 特定语法传递提交信息，必须使用 `-m` 参数

### 步骤 6. 验证提交结果

提交后立即验证 commit message 是否完整写入，发现截断立即修复。

1. 运行以下命令获取实际提交的 message：

   ```bash
   git log -1 --format="%H%n%s%n%b"
   ```

2. 将实际 message 与预期 title 和 body 逐字对比：
   - 检查 title 是否完整，未在冒号后或其他位置截断
   - 检查 body 是否完整，每行内容是否完整
3. 若发现截断或不一致，使用 `git commit --amend` 修复：

   ```bash
   git commit --amend -m "<title>" -m "<body line 1>" -m "<body line 2>"
   ```

4. 修复后再次运行 `git log -1 --format="%H%n%s%n%b"` 确认 message 完整
5. 在输出结果中展示最终的 commit message

**验证规则：**

- 必须逐字对比，不能只检查长度或部分内容
- 发现截断后必须修复，不能跳过
- 修复后必须再次验证，直到确认完整

### 步骤 7. 输出结果

输出提交结果，包括：

- 提交的文件列表
- 提交信息
- 提交哈希值

输出内容参考示例如下：

```text
✓ 提交成功

提交文件：
  - skills/yy-create-rule/SKILL.md
  - skills/yy-create-rule/resources/rule-best-practices.md

提交哈希：7660360

提交信息：refactor(yy-create-rule): 添加规则内容通用性约束

变更统计：2 files changed, 2 insertions(+)
```

## 特殊情况处理

### 无变更内容

如果 git status 显示没有变更，告知用户：

```text
当前工作区没有未提交的变更。

如果你期望有变更，可能的原因：
- 文件尚未保存
- 变更已经在之前的提交中
- .gitignore 忽略了这些文件
```

### 冲突或未推送的提交

在步骤 1 解析 `git status` 输出时识别以下信号并提醒用户：

- **合并冲突**：状态中出现 `UU`、`AA`、`DD`、`AU`、`UA`、`DU`、`UD` 标记的文件 → 提醒用户先解决冲突再提交
- **未推送的本地提交**：状态中出现 `Your branch is ahead of 'origin/...' by N commit(s)` → 仅提示存在未推送提交，不阻断本次提交
- **本地落后远端**：状态中出现 `Your branch is behind 'origin/...'` → 仅提示，不阻断本次提交

## 安全边界

**禁止主动执行：**

- 跳过 hook：不使用 `--no-verify` 等标志
- 强制操作：不使用 `--force` 或 `--amend`（除非用户明确要求，或属于步骤 6 验证流程中的自动修复）
- 添加署名：不添加 `Co-Authored-By:` 等署名备注
- 还原用户改动：不因改动未在对话中提及而使用 `git restore`、`git checkout --`、`git clean` 或补丁覆盖工作区文件

**主分支保护：**

- 当前位于 `main/master` 分支时，提醒用户考虑在功能分支工作，不得添加额外确认交互阻塞提交流程

**排除来源权威性：**

- 以 `.gitignore` 为权威排除来源，不在其之上叠加二级黑名单
- 未被忽略的文件视为允许提交，对敏感命名特征文件始终给出展示警告
- 新增敏感文件触发强制阻塞，修改/删除敏感文件仅警告不阻塞

## 最佳实践

- 提交信息应该回答"这个提交做了什么，以及为什么要这么做"
- 优先说明改动的目的/原因，而不只是描述改动本身
- 保持提交信息的精确性：小改动要有具体的描述，避免过于宽泛
- 认真对待用户反馈：如果用户认为提交信息不够好，立即调整优化
- 优先使用对话上下文理解真实意图
- 当不确定变更意图时，询问用户
- 保持提交小而聚焦
- 使用有意义的 scope 帮助快速定位
- 提交信息涉及具体文件名时，必须保留原文件名，不得翻译

## 路径格式规范

- 在文档中提及文件路径时，优先使用相对路径
- 在终端中提及文件路径时，优先使用绝对路径
- 使用正斜杠作为路径分隔符，路径包含空格时使用引号包裹

