我给 AI 编程助手加了一道 Git 提交门

📦 本文所有代码与配置均来自真实开源项目:

  • GitHub:https://github.com/Fengj0n/-Bookkeeping
  • Gitee:https://gitee.com/fengj0n/bookkeeping

引言

我用 Cursor 开发了一个本地记账应用,技术栈是 Electron、Vue 3 和 SQLite。项目进入持续修改阶段后,我遇到的主要问题不是代码生成,而是交付确认:测试是否真的执行过,质量检查是否有统一标准,提交的代码是否就是刚刚检查过的版本。

为了解决这几个问题,我在项目中加入了三层机制:用 Skill 固定检查规则,用 Subagent 执行测试和审计,用 Git Hook 拦截未经检查的提交。

本文记录这套方案的完整落地过程:规则如何设计、代理如何分工、Hook 如何拦截、验证结果如何,以及它适合什么场景。

目录

  • 一、从聊天约定到项目规则
    • 1.1 口头要求无法形成稳定流程
    • 1.2 Skill 固定检查协议
    • 1.3 规则需要区分阻断项和提醒项
  • 二、让代理执行检查,让 Git 负责拦截
    • 2.1 三个 Subagent 的职责
    • 2.2 pre-commit 是最后一道门
    • 2.3 为什么必须检查通行证是否过期
    • 2.4 Windows 环境的 Hook 注意事项
  • 三、验证结果与适用边界
    • 3.1 三种提交场景
    • 3.2 一次真实的安全修复
    • 3.3 适用场景与局限
  • 结语

一、从聊天约定到项目规则

1.1 口头要求无法形成稳定流程

最初我通过对话告诉 AI:“跑一下测试”“检查一下安全问题”。这种方式有两个缺点。

第一,要求不会自动继承。每次新开会话,都要重新说明测试文件、运行命令和报告格式。第二,结果难以核验。AI 说"已经检查完成",并不等于命令真的执行过,也不等于检查覆盖了预期范围。

因此,问题不在于提示词写得够不够长,而在于项目缺少固定的检查协议:检查什么、怎样算通过、失败后如何处理,都没有被保存为项目文件。

1.2 Skill 固定检查协议

我把协议写入 .cursor/skills/

.cursor/skills/
├── unit-test/SKILL.md
├── comment-check/SKILL.md
└── security-audit/SKILL.md

unit-test 规定测试文件和运行方式:

测试对象 文件位置 运行命令
JavaScript 工具函数 tests/*.test.js node tests/xxx.test.js
Electron 模块 tests/electron/*-test.js npx electron tests/xxx-test.js
Python 游戏 games/test_*.py python games/test_xxx.py

它还规定每条用例输出 PASSFAIL!,全部通过时退出码必须为 0,并在项目根目录生成 test-report.md

security-audit 的检查范围包括密码、Token、私钥、SQL 拼接、路径穿越、XSS、Electron 隔离配置和明文配置。comment-check 则检查函数注释、核心逻辑注释,以及注释是否与实际代码一致。

这些规则的作用是把"注意测试"和"检查安全"转换成可执行的检查项。检查结果有了固定格式,后续才能被代理和 Git Hook 使用。

1.3 规则需要区分阻断项和提醒项

如果所有问题都阻止提交,质量门禁会变得难以使用。因此我把检查结果分成两类。

阻断项包括:测试失败、严重或高危安全问题、注释与代码不一致。注释数量不足、表达不够通俗等问题仍然写入报告,但暂不阻止提交。

这是一个针对个人项目的取舍:先拦截会影响功能和安全的错误,再逐步改善代码可读性。团队项目可以根据风险等级调整这个标准。

二、让代理执行检查,让 Git 负责拦截

2.1 三个 Subagent 的职责

项目中配置了三个代理:

.cursor/agents/
├── tester.md
├── quality-engineer.md
└── gitcommit-agent.md

tester 只负责单元测试。它读取 unit-test 技能,执行现有测试,生成报告。只有所有测试退出码为 0 且没有 FAIL! 时,才生成:

.quality-gate/test-passed.flag

quality-engineer 负责安全审计和注释检查。达到项目规定的阻断标准后,生成:

.quality-gate/quality-passed.flag

gitcommit-agent 是流程编排者,不重新实现测试和审计逻辑。用户调用:

/gitcommit-agent

它按以下顺序工作:

删除旧通行证
    ↓
运行 tester
    ↓
运行 quality-engineer
    ↓
确认两张通行证有效
    ↓
git add + git commit

每轮检查开始前删除旧通行证,是为了避免上一轮的结果被误用于本轮代码。

2.2 pre-commit 是最后一道门

Subagent 解决了"如何完成检查",但不能阻止用户绕过代理直接提交:

git add .
git commit -m "保存修改"

所以我配置了 Git 的 pre-commit Hook:

hooks/pre-commit
scripts/check-quality-gate.js

Hook 文件只调用 Node 脚本:

#!/bin/sh
node scripts/check-quality-gate.js

通过下面的命令启用:

git config core.hooksPath hooks

脚本检查两张通行证是否存在,以及通行证是否对应当前暂存区的代码。

2.3 为什么必须检查通行证是否过期

只判断标记文件存在会产生错误放行:

周一:测试通过,生成通行证
周二:修改代码
周三:直接提交

周一的通行证不能证明周二的修改没有问题。因此脚本读取暂存区文件:

const staged = execSync(
  'git diff --cached --name-only --diff-filter=ACMR',
  { encoding: 'utf8' }
).split('\n').filter(Boolean)

然后比较文件时间。如果任一暂存文件比通行证更新,提交就会被拒绝:

if (newestSourceMs > oldestMarkerMs + 2000) {
  console.error('❌ 提交被拦截:通行证已过期')
  process.exit(1)
}

2000 毫秒是 Windows 文件时间精度的容差。这个方案绑定的是文件修改时间,不是密码学签名,因此不能防止手工伪造通行证;它的目标是防止正常开发中的流程遗漏。如果需要更强的可信度,应在 CI 中重新执行测试和审计,而不是信任本地标记文件。

2.4 Windows 环境的 Hook 注意事项

Windows 编辑器可能把 Hook 保存为 CRLF,而 Hook 通常由 sh 执行,换行符错误会导致脚本运行失败。项目使用 .gitattributes 固定脚本使用 LF:

hooks/pre-commit text eol=lf

Hook 还需要保留可执行权限。换电脑或重新克隆项目后,需要再次执行:

git config core.hooksPath hooks

三、验证结果与适用边界

3.1 三种提交场景

我对门禁做了三次验证:

场景 结果
没有通行证直接提交 拒绝提交
使用早于代码修改时间的旧通行证 拒绝提交,并提示通行证过期
重新运行测试和质量检查后提交 允许提交

项目实际检查结果如下:

  • JavaScript 测试:19/19 通过
  • Python 测试:12/12 通过
  • 严重安全问题:0
  • 高危安全问题:0
  • 注释与代码不一致:0
  • 正式提交:e31071a

提交时,Hook 输出了:

[质量门禁] ✅ 单元测试 + 质量检查两张通行证有效,放行

3.2 一次真实的安全修复

在质量检查过程中发现,项目原本试图通过 Electron 的 onHeadersReceived 为正式版页面注入 CSP。但正式版使用 loadFile() 加载 file:// 页面时,这种拦截方式不会生效。

后来改为在 index.html 中直接声明:

<meta http-equiv="Content-Security-Policy"
      content="default-src 'self'; style-src 'self' 'unsafe-inline'" />

这个问题说明,安全审计不能只搜索"有没有安全配置",还要确认配置是否适用于实际运行环境。

3.3 适用场景与局限

对个人项目或小型项目,这套方案的成本较低,适合解决以下问题:

  • 测试经常被忘记执行
  • 多个检查流程需要重复说明
  • 提交结果依赖人工记忆
  • 希望把项目规范随代码一起保存

它不适合被当作完整的安全系统。--no-verify 可以绕过本地 Hook,通行证也可以被手工伪造。因此团队项目仍应在 CI 中配置独立检查,把远程测试结果作为最终依据。

结语

这次实践的结果不是增加了几个 AI 配置文件,而是把一次提交拆成了几个可验证的步骤:Skill 保存标准,Subagent 执行标准,Git Hook 验证结果。

对我来说,最有用的变化是:提交代码前不再依赖"应该已经检查过了"这种判断,而是必须拿出测试结果和质量检查结果。

如果你也想在自己的项目中尝试类似的质量门禁,欢迎参考本文开头给出的开源仓库,.cursor/agents/.hooks/ 目录可以直接复用。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐