ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

VSCode插件开发:离线规则引擎+AI增强的Git提交信息生成器

2026/10/7 22:55:47 拓冰建站 浏览量
VSCode插件开发:离线规则引擎+AI增强的Git提交信息生成器 1. 为什么我要自己做一个提交信息生成插件每次写完代码打开源代码管理面板看到那一排待提交的文件然后要在输入框里憋出一句像样的提交信息——这件事我忍了很久了。fix bug、update、修改这种提交记录我自己看着都脸红但忙起来的时候真的没精力去想什么feat: 新增用户登录态校验逻辑。团队里 code review 的时候翻 git log 找某个改动满屏的update和fix那感觉就像在一堆没贴标签的罐头里找一颗特定的豆子。市面上不是没有类似工具但要么是独立客户端要么需要把代码推到某个平台才能用要么配置起来一堆依赖。我就想能不能在 VSCode 里装一个插件点一下按钮它读一下我暂存区的 diff然后直接给我生成一条规范的提交信息最好是离线的规则引擎加可选的 AI 增强不强制联网不强制注册账号装完就能用。这个项目就是干这个的。它是一个 VSCode 扩展打包成.vsix离线包双击安装重启编辑器就能在源代码管理面板看到一个小图标。点它它做三件事读取当前暂存区的变更、分析变更类型和范围、生成一条符合 Conventional Commits 规范的提交信息并填入输入框。如果你配置了 OpenAI 的 API Key它还能调用模型生成更自然、更贴合业务语义的描述不配置也没关系内置的规则引擎覆盖了常见的增删改场景准确率在日常使用中够用。适合谁看如果你每天都在用 Git 做版本控制又不想在提交信息上花太多心思或者你是一个团队的技术负责人想统一团队的提交规范但推不动人这个插件的思路和实现细节都值得参考。下面我会从整体设计、核心实现、实操步骤到踩坑记录完整拆一遍。2. 整体设计与技术选型拆解2.1 为什么是 VSCode 扩展而不是独立 CLI一开始我考虑过写一个 Node.js 脚本通过 git hook 在 commit 之前自动生成信息。但很快放弃了原因有三个。第一git hook 的触发时机是prepare-commit-msg这时候暂存区已经确定了但用户往往还没想好要提交什么。如果生成的信息不符合预期用户需要中断提交、修改、重新提交体验很割裂。而在 VSCode 里用户可以在提交之前就看到生成的信息不满意直接改改完再点提交流程顺畅得多。第二VSCode 扩展能直接访问编辑器的 API比如获取当前工作区的根路径、读取配置、显示通知、操作源代码管理面板的输入框。这些能力用 CLI 实现起来要绕很多弯比如通过git config读取配置、通过标准输出传递信息维护成本高。第三分发方便。.vsix文件发给团队成员双击安装不需要每个人去配环境变量、装 Node 依赖。对于不熟悉命令行的同事来说这个门槛低得多。2.2 规则引擎加 AI 增强的双层架构核心设计上我把生成逻辑分成了两层规则引擎层和AI 增强层。规则引擎层负责处理绝大多数常见场景。它的输入是git diff --cached的输出输出是一条结构化的提交信息。具体来说它会解析 diff 中的文件路径、变更类型新增、修改、删除、重命名、变更行数然后根据一套预设的映射规则生成信息。比如检测到新增了.ts文件且内容包含export function就归类为feat检测到修改了测试文件就归类为test检测到只改了.md文件就归类为docs。AI 增强层是可选的。当用户在配置里填了 API Key 和模型名称后插件会把 diff 内容截断到一定长度加上一段精心设计的 prompt发给模型让模型生成一条更自然的提交信息。如果 API 调用失败或者超时自动回退到规则引擎的结果保证功能始终可用。这个双层架构的好处是离线可用、在线增强、失败降级。用户不会因为网络问题或者 API 配额用完就卡住。2.3 为什么选择 Conventional Commits 作为输出规范提交信息的格式有很多种我最终选了 Conventional Commits。原因很简单它有明确的类型前缀feat、fix、docs、style、refactor、perf、test、chore有可选的范围scope有描述主体还支持 breaking change 标记。这套规范被大量工具链支持比如自动生成 changelog、自动决定版本号、自动触发 CI 流程。对于团队协作来说统一的格式意味着 git log 可读性大幅提升也意味着可以用工具自动分析提交历史。我在插件里内置了一个类型映射表根据文件路径和变更内容自动推断类型用户也可以在设置里覆盖这个映射。2.4 技术栈与依赖选择插件本身用 TypeScript 写编译目标是 ES2020运行在 VSCode 的扩展宿主进程里。依赖方面我刻意保持精简simple-git封装 git 命令调用比直接child_process.exec更安全能处理路径转义和错误捕获。openai官方 Node SDK用于调用兼容 OpenAI 接口的模型服务。types/vscodeVSCode 扩展 API 的类型定义。没有引入任何 UI 框架所有交互都通过 VSCode 原生的window.showInputBox、window.showQuickPick、window.showInformationMessage实现。这样打包出来的.vsix体积很小安装快启动也不拖慢编辑器。3. 核心细节解析与实操要点3.1 暂存区 diff 的读取与解析读取暂存区 diff 是整个流程的第一步。我用simple-git的diff方法传入[--cached]参数拿到的是标准 unified diff 格式的文本。这个文本包含了每个文件的变更块每个块以开头后面跟着行号范围和变更内容。解析的时候我主要提取三类信息文件路径从diff --git a/xxx b/xxx这一行提取同时处理重命名的情况rename from和rename to。变更类型新增文件会有new file mode删除文件会有deleted file mode重命名会有rename from/to其余归为修改。变更内容摘要统计每个文件新增和删除了多少行以及是否包含特定关键词比如test、docs、config。这里有个细节要注意diff 文本可能非常大尤其是首次提交或者大规模重构的时候。如果直接把整个 diff 发给 AI 模型token 消耗会很高而且可能超出上下文限制。我的做法是只取每个文件的前 50 行变更内容并且总长度超过 8000 字符时截断优先保留新增行。实测下来这个策略在保证生成质量的同时把 token 消耗控制在了合理范围内。注意如果你的项目里有大文件比如图片、二进制文件git diff --cached可能会输出乱码或者极长的文本。建议在插件设置里加一个文件类型过滤把.png、.jpg、.zip这类扩展名排除掉。3.2 规则引擎的类型推断逻辑规则引擎的核心是一张映射表我把它设计成了可配置的 JSON 结构。默认配置大致如下{ typeRules: [ { pattern: \\.(test|spec)\\.(ts|js|tsx|jsx)$, type: test }, { pattern: \\.(md|mdx|txt)$, type: docs }, { pattern: \\.(css|scss|less)$, type: style }, { pattern: package\\.json$, type: chore }, { pattern: \\.(yml|yaml|json|toml)$, type: chore } ], keywordRules: [ { keyword: fix, type: fix }, { keyword: bug, type: fix }, { keyword: refactor, type: refactor }, { keyword: optimize, type: perf } ] }推断流程是先按文件路径匹配typeRules如果所有文件都匹配到同一个类型就用这个类型如果匹配到多个类型取优先级最高的featfixrefactorperftestdocsstylechore。如果路径没匹配上再看变更内容里有没有keywordRules里的关键词。最后如果什么都没匹配到默认用chore。范围scope的推断稍微简单一些取变更文件所在的最深层公共目录名。比如改了src/components/Button.tsx和src/components/Modal.tsxscope 就是components。如果改了多个不同目录的文件scope 留空。3.3 AI 增强层的 prompt 设计调用 AI 模型的时候prompt 的质量直接决定了生成结果的好坏。我试过很多版本最终稳定下来的 prompt 结构是这样的你是一个 Git 提交信息生成助手。请根据以下暂存区的变更内容生成一条符合 Conventional Commits 规范的提交信息。 要求 1. 格式为 type(scope): description 2. type 从 feat/fix/docs/style/refactor/perf/test/chore 中选择 3. description 用中文不超过 50 个字动词开头说明做了什么 4. 如果变更涉及多个不相关的改动用分号分隔 5. 只输出提交信息本身不要任何解释 变更内容 { diff 摘要 }这个 prompt 的关键点在于明确输出格式、限制长度、要求中文、禁止解释。早期版本我没有加“只输出提交信息本身”这句话结果模型经常返回“好的根据您的变更我建议的提交信息是...”这种废话还得额外写代码去提取。另外我把temperature设成了 0.3让输出更稳定。max_tokens设成 100因为提交信息本身很短不需要太多 token。3.4 配置项的设计与默认值插件暴露了以下配置项用户可以在 VSCode 设置里搜索commitAi找到配置项类型默认值说明commitAi.enableAIbooleanfalse是否启用 AI 增强commitAi.apiKeystringAPI Key存储在 VSCode 的 SecretStorage 里commitAi.baseUrlstringhttps://api.openai.com/v1API 基础地址支持兼容接口commitAi.modelstringgpt-4o-mini模型名称commitAi.maxDiffLengthnumber8000diff 截断长度commitAi.languagestringzh-CN生成信息的语言commitAi.customTypeRulesarray[]自定义类型映射规则API Key 我特意存在了SecretStorage里而不是普通的workspaceConfiguration。因为普通配置会明文写在settings.json里如果不小心把配置文件提交到仓库Key 就泄露了。SecretStorage是 VSCode 提供的加密存储只有扩展本身能读取。提示如果你用的是兼容 OpenAI 接口的第三方服务只需要改baseUrl和model两个配置就行。但要注意不同服务对 prompt 的响应格式可能有差异建议先用curl测试一下接口是否正常返回。4. 实操过程与核心环节实现4.1 从零搭建扩展项目骨架先确保你本地有 Node.js 18 以上版本和 npm。然后安装 VSCode 扩展开发脚手架npm install -g yo generator-code yo code在交互式界面里选择New Extension (TypeScript)输入扩展名称commit-ai其余选项保持默认。生成的项目结构里核心文件是src/extension.ts这是扩展的入口。接下来安装依赖npm install simple-git openai npm install --save-dev types/vscode然后在package.json里注册命令和配置项。命令的command字段填commitAi.generatetitle填生成提交信息。配置项按照上一节的表格逐个填入contributes.configuration.properties。4.2 注册命令与激活事件在package.json的activationEvents里加上onCommand:commitAi.generate这样用户第一次点击按钮时扩展才会激活不会拖慢编辑器启动。然后在extension.ts的activate函数里注册命令import * as vscode from vscode; import { generateCommitMessage } from ./generator; export function activate(context: vscode.ExtensionContext) { const disposable vscode.commands.registerCommand(commitAi.generate, async () { const message await generateCommitMessage(context); if (message) { const gitExtension vscode.extensions.getExtension(vscode.git)?.exports; const git gitExtension?.getAPI(1); const repo git?.repositories[0]; if (repo) { repo.inputBox.value message; vscode.window.showInformationMessage(提交信息已生成); } } }); context.subscriptions.push(disposable); }这里用到了 VSCode 内置的 Git 扩展 API。repo.inputBox.value就是源代码管理面板那个输入框的值直接赋值就能填入生成的信息。4.3 实现 diff 读取与规则引擎新建src/generator.ts核心逻辑如下import * as vscode from vscode; import simpleGit from simple-git; import { inferType, inferScope } from ./rules; import { callAI } from ./ai; export async function generateCommitMessage(context: vscode.ExtensionContext): Promisestring | undefined { const workspaceFolders vscode.workspace.workspaceFolders; if (!workspaceFolders || workspaceFolders.length 0) { vscode.window.showWarningMessage(请先打开一个 Git 仓库); return; } const rootPath workspaceFolders[0].uri.fsPath; const git simpleGit(rootPath); const isRepo await git.checkIsRepo(); if (!isRepo) { vscode.window.showWarningMessage(当前目录不是 Git 仓库); return; } const diff await git.diff([--cached]); if (!diff.trim()) { vscode.window.showWarningMessage(暂存区没有变更请先 git add); return; } const config vscode.workspace.getConfiguration(commitAi); const enableAI config.getboolean(enableAI, false); if (enableAI) { const apiKey await context.secrets.get(commitAi.apiKey); if (apiKey) { try { const aiMessage await callAI(diff, apiKey, config); if (aiMessage) return aiMessage; } catch (err) { console.error(AI 生成失败回退到规则引擎, err); } } } const type inferType(diff); const scope inferScope(diff); const description buildDescription(diff); return scope ? ${type}(${scope}): ${description} : ${type}: ${description}; }buildDescription函数负责从 diff 里提取一个简短的中文描述。我的做法是统计新增和删除的文件数如果只有一个文件就用文件名加动作如果有多个文件就用“更新多个文件”加主要变更类型。这个描述不算完美但作为规则引擎的兜底够用了。4.4 打包成 .vsix 离线包开发调试完成后安装vsce打包工具npm install -g vscode/vsce在项目根目录执行vsce package如果提示缺少repository字段在package.json里补上一个即可。打包成功后会生成commit-ai-0.0.1.vsix文件。把这个文件发给团队成员他们在 VSCode 里按CtrlShiftP打开命令面板输入Install from VSIX选择文件就能安装。注意打包之前记得把package.json里的publisher字段填上否则vsce会报错。这个字段可以随便填一个你的名字或团队名不影响本地安装使用。4.5 配置 API Key 与测试 AI 生成安装完插件后按Ctrl,打开设置搜索commitAi把enableAI勾上。然后按CtrlShiftP输入Commit AI: 设置 API Key在弹出的输入框里粘贴你的 Key。这个命令是我额外注册的专门用来往SecretStorage里写 Key。配置完成后随便改一个文件git add之后点击源代码管理面板的生成按钮。如果一切正常输入框里会出现一条类似feat(components): 新增按钮组件的加载状态的信息。如果 AI 调用失败控制台会输出错误日志。你可以按CtrlShiftP输入Developer: Toggle Developer Tools打开开发者工具在 Console 面板里看到具体的报错信息。5. 常见问题与排查技巧实录5.1 生成的信息不符合预期怎么办这是最常见的问题。首先要区分是规则引擎的问题还是 AI 的问题。如果没开 AI那问题出在类型映射规则上。你可以打开设置找到commitAi.customTypeRules添加自己的规则。比如你的项目里api目录下的文件都应该归为feat就加一条{ pattern: api/, type: feat }。如果开了 AI 但生成的信息还是不对大概率是 diff 截断导致的。模型只看到了部分变更自然推断不准确。你可以把commitAi.maxDiffLength调大比如改成 16000但要注意 token 消耗会相应增加。还有一种情况是模型本身的能力问题。gpt-4o-mini在简单场景下够用但如果你的变更涉及复杂的业务逻辑可能需要换成更强的模型。在设置里改commitAi.model即可。5.2 API 调用超时或报错API 调用失败的原因很多我整理了一个排查表现象可能原因解决方法提示 401 UnauthorizedAPI Key 错误或过期重新设置 Key提示 429 Too Many Requests请求频率超限降低使用频率或升级配额提示 timeout网络不通或服务不可达检查baseUrl是否正确提示 model not found模型名称错误确认服务商支持的模型列表返回内容为空prompt 被过滤或模型拒绝检查 diff 是否包含敏感内容我遇到最多的是baseUrl配置错误。很多人复制地址的时候会多带一个/或者少写/v1导致请求路径不对。建议直接用curl测试一下curl -X POST https://api.openai.com/v1/chat/completions \ -H Authorization: Bearer YOUR_KEY \ -H Content-Type: application/json \ -d {model:gpt-4o-mini,messages:[{role:user,content:test}]}如果这条命令能正常返回说明配置没问题问题出在插件代码里。5.3 暂存区没有变更时的处理用户可能忘记git add就直接点生成按钮。这时候git diff --cached返回空字符串插件会提示“暂存区没有变更”。但有些用户不理解什么是暂存区所以我后来把提示改成了“请先在源代码管理面板中暂存要提交的文件”。另外如果用户改了文件但没保存VSCode 的 Git 面板可能显示有变更但git diff --cached读不到内容。这种情况需要先保存文件再暂存。我在插件的 README 里专门写了这一点但实际使用中还是有人踩坑。5.4 多人协作时的配置同步团队使用的时候每个人的 API Key 不一样不能共享。但类型映射规则、语言偏好这些是可以统一的。我的做法是在项目根目录放一个.vscode/settings.json把commitAi.customTypeRules和commitAi.language写进去提交到仓库。这样新成员拉取代码后规则自动生效只需要自己配 Key 就行。提示不要把 API Key 写进.vscode/settings.json那个文件是明文存储的。Key 只能通过SecretStorage或者环境变量传递。5.5 扩展与其他 Git 工具的冲突有些团队用husky加commitlint做提交信息校验。如果插件生成的信息不符合commitlint的规则提交会被拦截。解决办法是确保插件的输出格式和commitlint的配置一致。比如commitlint要求subject不能为空且不超过 72 个字符那就在 prompt 里明确加上这个限制。还有一种情况是用户同时装了其他 Git 增强插件比如 GitLens。这些插件可能会修改源代码管理面板的 UI导致生成按钮的位置变化。但功能本身不冲突因为我是通过命令注册的不依赖 UI 位置。6. 一些实操心得与后续扩展方向这个插件我从有这个想法到跑通第一个可用版本大概花了两个周末。最大的感受是规则引擎的覆盖度比想象中重要。一开始我太依赖 AI 了结果发现很多同事根本不配 Key或者配了之后因为网络问题经常失败。后来我把规则引擎打磨了一遍现在即使完全离线生成的提交信息也能达到“能用”的水平。另一个心得是关于 prompt 的。我试过让模型直接输出 JSON 格式然后解析 JSON 拿字段但模型经常在 JSON 外面包一层 markdown 代码块解析起来很麻烦。后来改成让模型直接输出纯文本反而更稳定。有时候简单的方案比复杂的方案更可靠。后续我打算加两个功能一是支持自定义模板让用户决定输出格式比如有些人喜欢[类型] 描述而不是类型: 描述二是加一个提交历史分析统计最近一段时间各类型的提交占比帮团队发现是不是fix太多了需要还技术债了。如果你也想自己改这个插件代码结构很清晰generator.ts负责主流程rules.ts负责规则引擎ai.ts负责 API 调用。改起来不复杂关键是理解 VSCode 扩展的激活机制和 Git 扩展 API 的用法。踩过几次坑之后你会发现这套东西比想象中好上手。