Compare commits

..

4 Commits

Author SHA1 Message Date
史悦
8e1f3d8259 优化 generatePrompt 函数,通过注释添加提示,以确保仅输出Git提交信息。
Some checks failed
Test / Test (ubuntu-latest) (push) Has been cancelled
Test / Test (windows-latest) (push) Has been cancelled
2025-10-21 10:12:03 +08:00
史悦
35372013a1 ``refactor(prompt): 优化并规范化生成提交信息的提示词- 将 conventional 类型的提示词从 JSON 格式更改为更简洁的列表格式。- 将通用规则格式化为项目列表,并增加“使用动词开头”的祈使句要求,以提高生成消息的一致性和质量。``
Some checks failed
Test / Test (ubuntu-latest) (push) Has been cancelled
Test / Test (windows-latest) (push) Has been cancelled
2025-10-21 10:02:56 +08:00
史悦
65da1dc301 feat: 在aicommits中添加提交、推送、重写和取消操作选项
Some checks failed
Test / Test (ubuntu-latest) (push) Has been cancelled
Test / Test (windows-latest) (push) Has been cancelled
2025-08-28 10:53:49 +08:00
史悦
69d3ef5040 文档: 在README.md中增加baseURL配置项说明 2025-08-27 17:50:39 +08:00
4 changed files with 146 additions and 50 deletions

View File

@@ -239,6 +239,18 @@ aicommits config set type=conventional
aicommits config set type= aicommits config set type=
``` ```
#### baseURL
默认值:`https://api.openai.com`
用于设置 OpenAI API 的基础 URL。如果您使用的是 OpenAI 的代理服务或其他兼容的 API 端点,可以通过此选项进行配置:
```sh
aicommits config set baseURL=https://your-proxy-url.com
```
这对于使用自定义 API 端点或代理服务的用户非常有用。
## 工作原理 ## 工作原理
这个 CLI 工具运行 `git diff` 来获取您所有的最新代码更改,将它们发送到 OpenAI 的 GPT-3然后返回 AI 生成的提交消息。 这个 CLI 工具运行 `git diff` 来获取您所有的最新代码更改,将它们发送到 OpenAI 的 GPT-3然后返回 AI 生成的提交消息。

View File

@@ -61,7 +61,7 @@ export default async (
}); });
const s = spinner(); const s = spinner();
s.start('The AI is analyzing your changes'); s.start('正在分析您的更改');
let messages: string[]; let messages: string[];
try { try {
messages = await generateCommitMessage( messages = await generateCommitMessage(
@@ -84,34 +84,126 @@ export default async (
throw new KnownError('No commit messages were generated. Try again.'); throw new KnownError('No commit messages were generated. Try again.');
} }
let message: string; // 处理单个或多个提交消息的函数
if (messages.length === 1) { const handleCommitAction = async (msg: string, isSingleMessage: boolean = true) => {
[message] = messages; const action = await select({
const confirmed = await confirm({ message: isSingleMessage ? `使用此提交消息?\n\n ${msg}\n` : '选择操作:',
message: `Use this commit message?\n\n ${message}\n`, options: [
{ label: '提交', value: 'commit' },
{ label: '提交并推送', value: 'commit-and-push' },
{ label: '重写', value: 'rewrite' },
{ label: '取消', value: 'cancel' },
],
}); });
if (!confirmed || isCancel(confirmed)) { if (isCancel(action) || action === 'cancel') {
outro('Commit cancelled'); outro('提交已取消');
return false;
}
if (action === 'rewrite') {
return 'rewrite';
}
if (action === 'commit') {
await execa('git', ['commit', '-m', msg, ...rawArgv]);
outro(`${green('✔')} 提交成功!`);
return true;
} else if (action === 'commit-and-push') {
await execa('git', ['commit', '-m', msg, ...rawArgv]);
const pushSpinner = spinner();
pushSpinner.start('正在推送到远程仓库...');
try {
await execa('git', ['push']);
pushSpinner.stop('推送成功');
outro(`${green('✔')} 提交并推送成功!`);
return true;
} catch (error) {
pushSpinner.stop('推送失败');
outro(`${red('✖')} 提交成功但推送失败: ${(error as Error).message}`);
return true;
}
}
return false;
};
// 重新生成提交消息的函数
const regenerateMessages = async () => {
const s = spinner();
s.start('正在重新生成提交消息...');
try {
const config = await getConfig({
OPENAI_KEY: env.OPENAI_KEY || env.OPENAI_API_KEY,
proxy:
env.https_proxy || env.HTTPS_PROXY || env.http_proxy || env.HTTP_PROXY,
generate: generate?.toString(),
type: commitType?.toString(),
});
const newMessages = await generateCommitMessage(
config.OPENAI_KEY,
config.model,
config.locale,
staged.diff,
config.generate,
config['max-length'],
config.type,
config.timeout,
config.proxy,
config.baseURL
);
s.stop('已重新生成提交消息');
if (newMessages.length === 0) {
throw new KnownError('没有重新生成提交消息。请重试。');
}
return newMessages;
} catch (error) {
s.stop('重新生成失败');
throw error;
}
};
// 处理消息选择和提交的主循环
let currentMessages = messages;
while (true) {
let message: string;
if (currentMessages.length === 1) {
[message] = currentMessages;
const result = await handleCommitAction(message, true);
if (result === 'rewrite') {
currentMessages = await regenerateMessages();
continue;
} else if (result === true || result === false) {
return; return;
} }
} else { } else {
const selected = await select({ const selected = await select({
message: `Pick a commit message to use: ${dim('(Ctrl+c to exit)')}`, message: `选择一个提交消息: ${dim('(Ctrl+c 退出)')}`,
options: messages.map((value) => ({ label: value, value })), options: currentMessages.map((value) => ({ label: value, value })),
}); });
if (isCancel(selected)) { if (isCancel(selected)) {
outro('Commit cancelled'); outro('提交已取消');
return; return;
} }
message = selected as string; message = selected as string;
const result = await handleCommitAction(message, false);
if (result === 'rewrite') {
currentMessages = await regenerateMessages();
continue;
} else if (result === true || result === false) {
return;
}
}
} }
await execa('git', ['commit', '-m', message, ...rawArgv]);
outro(`${green('✔')} Successfully committed!`);
})().catch((error) => { })().catch((error) => {
outro(`${red('✖')} ${error.message}`); outro(`${red('✖')} ${error.message}`);
handleCliError(error); handleCliError(error);

View File

@@ -36,7 +36,7 @@ export default () =>
}); });
const s = spinner(); const s = spinner();
s.start('The AI is analyzing your changes'); s.start('正在分析您的更改');
let messages: string[]; let messages: string[];
try { try {
messages = await generateCommitMessage( messages = await generateCommitMessage(

View File

@@ -18,24 +18,14 @@ const commitTypes: Record<CommitType, string> = {
* Conventional Changelog: * Conventional Changelog:
* https://github.com/conventional-changelog/conventional-changelog/blob/d0e5d5926c8addba74bc962553dd8bcfba90e228/packages/conventional-changelog-conventionalcommits/writer-opts.js#L182-L193 * https://github.com/conventional-changelog/conventional-changelog/blob/d0e5d5926c8addba74bc962553dd8bcfba90e228/packages/conventional-changelog-conventionalcommits/writer-opts.js#L182-L193
*/ */
conventional: `从下面的类型到描述的JSON中选择最能描述git差异的类型\n${JSON.stringify( conventional: `从下面的类型到描述的JSON中选择最能描述git差异的类型(中文即可,注意后面加冒号)\n
{ \n- feat: 新功能
docs: '仅文档更改', \n- fix: 修复 bug
style: \n- docs: 更新文档
'不影响代码含义的更改(空格、格式、缺少分号等)', \n- style: 格式调整(不影响代码执行)
refactor: '既不修复错误也不添加功能的代码更改', \n- refactor: 重构(非功能更新)
perf: '提高性能的代码更改', \n- test: 测试相关内容
test: '添加缺失的测试或更正现有测试', \n- chore: 构建或工具变更`,
build: '影响构建系统或外部依赖的更改',
ci: '对我们的CI配置文件和脚本的更改',
chore: '不修改src或测试文件的其他更改',
revert: '恢复之前的提交',
feat: '新功能',
fix: '错误修复',
},
null,
2
)}`,
}; };
export const generatePrompt = ( export const generatePrompt = (
@@ -45,14 +35,16 @@ export const generatePrompt = (
) => ) =>
[ [
'为以下代码差异生成一个简洁的、使用现在时态的中文git提交消息并遵循以下规范', '为以下代码差异生成一个简洁的、使用现在时态的中文git提交消息并遵循以下规范',
`提交消息使用中文编写。`, `- 提交消息使用中文编写。`,
`提交消息最多${maxLength}个字符。`, `- 提交消息最多${maxLength}个字符。`,
'提交消息包含具体的类名、方法名或其他关键信息,不能过于笼统。', `- 使用动词作开头(祈使句形式)`,
'对于功能添加,应该指明具体的类或模块,如"在UserController中添加了用户权限验证功能"。', '- 提交消息包含具体的类名、方法名或其他关键信息,不能过于笼统。',
'对于代码重构,应该指明重构的具体类或方法,如"重构了PaymentProcessor类的金额计算逻辑"。', '- 对于功能添加,应该指明具体类或模块,如"在UserController中添加了用户权限验证功能"。',
'排除任何不必要的内容如翻译。您的整个响应将直接传递到git提交中。', '- 对于代码重构,应该指明重构的具体类或方法,如"重构了PaymentProcessor类的金额计算逻辑"。',
'- 排除任何不必要的内容如翻译。您的整个响应将直接传递到git提交中。',
commitTypes[type], commitTypes[type],
specifyCommitFormat(type), specifyCommitFormat(type),
//`只输出Git的提交信息即可不要输出其他任何信息`,
] ]
.filter(Boolean) .filter(Boolean)
.join('\n'); .join('\n');