clog-ai
Version:
Generate commit log with AI
187 lines (161 loc) • 5.36 kB
text/typescript
import axios from 'axios';
import child_process from 'child_process';
import loading from 'loading-cli';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import readline from 'readline';
//@ts-ignore
import TEMPLACE_CN from './template/template.zh.md';
//@ts-ignore
import TEMPLACE_EN from './template/template.en.md';
const homeDir = os.homedir();
const configFilePath = path.join(homeDir, '.config/clog-ai/config.json');
const command = process.argv[2]?.trim();
const isVerbose = process.argv.includes('--verbose')||process.argv.includes('-v');
if (command == 'init') {
if (fs.existsSync(configFilePath)) {
console.log(
'config file already exists, please edit config file: ' +
path.resolve(configFilePath)
);
process.exit(0);
}
const config = {
language: 'zh',
datasource: 'openai',
openai_api_key: '',
azure_api_key: '',
azure_deployment_id: '',
azure_base_url: '',
azure_model: '',
azure_api_version: '',
};
const dirPath = path.dirname(configFilePath);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
fs.writeFileSync(configFilePath, JSON.stringify(config, null, 2));
console.log(
'init success, please edit config file: ' + path.resolve(configFilePath)
);
process.exit(0);
}
if (!fs.existsSync(configFilePath)) {
console.log('please run this command first: clog-ai init');
process.exit(0);
}
const config = getConfig()
//valid_datasource: azure openai
const valid_datasource = ['azure', 'openai']
if(!config.datasource){
console.log('Please specify the datasource in the configuration file: ' + path.resolve(configFilePath));
process.exit(0);
}
if(!valid_datasource.includes(config.datasource)){
console.log('Invalid datasource. Please check the configuration file: ' + path.resolve(configFilePath));
process.exit(0);
}
if(config.datasource === 'openai' && (!config.openai_api_key)){
console.log('Data source is OpenAI, but the corresponding configuration is missing. Please add it in the configuration file: ' + path.resolve(configFilePath))
process.exit(0);
}
if (config.datasource === 'azure' && (!config.azure_api_key || !config.azure_deployment_id || !config.azure_base_url || !config.azure_model || !config.azure_api_version)) {
console.log('Data source is Azure, but the corresponding configuration is missing. Please add it in the configuration file: ' + path.resolve(configFilePath))
process.exit(0);
}
function getConfig() {
const content = fs.readFileSync(configFilePath).toString();
return JSON.parse(content || '{}');
}
async function gptRequestAzure(prompt: string) {
const res = await axios.post(
`${config.azure_base_url}/openai/deployments/${config.azure_deployment_id}/chat/completions?api-version=${config.azure_api_version}`,
{
model: 'gpt-3.5-turbo-16k',
messages: [{ role: 'user', content: prompt }],
temperature: 0,
top_p: 1,
frequency_penalty: 0,
presence_penalty: 0,
},
{
headers: {
'api-key': config.azure_api_key,
},
timeout: 100000,
}
);
return res.data.choices[0].message.content;
}
async function gptRequestOpenai(prompt: string) {
const res = await axios.post(
`https://api.openai.com/v1/chat/completions`,
{
model: 'gpt-3.5-turbo-16k',
messages: [{ role: 'user', content: prompt }],
temperature: 0,
top_p: 1,
frequency_penalty: 0,
presence_penalty: 0,
},
{
headers: {
Authorization: `Bearer ${config.openai_api_key}`,
},
timeout: 100000,
},
);
return res.data.choices[0].message.content;
}
export default async () => {
// 执行 git diff,获取变更的文件内容
const diff = child_process
.execSync('git diff HEAD')
.toString()
.substring(0, 8000);
const prompt = (config.language == 'zh' ? TEMPLACE_CN : TEMPLACE_EN).replace('{{diff}}', diff)
const load = loading({
text: 'Generating commit log...',
color: 'yellow',
interval: 100,
frames: ['◰', '◳', '◲', '◱'],
}).start();
try {
if (isVerbose) {
console.log('--------- Input Prompt ----------');
console.log(prompt);
}
const res = config.datasource === 'azure' ? await gptRequestAzure(prompt) : await gptRequestOpenai(prompt);
if (isVerbose) {
console.log('\n--------- Output ----------');
console.log(res);
console.log('-------------------');
}
const commitLog = res.match(/<output>([\s\S]*)<\/output>/)?.[1]?.trim();
if (!commitLog) {
throw new Error('No commit log generated');
}
load.stop();
load.succeed('Generate commit log success');
console.log('-------------------')
console.log(commitLog);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
console.log('-------------------')
rl.question('Submit git commit with the log? (yes/no) ', (answer) => {
if (answer.toLowerCase() === 'yes' || answer.toLowerCase() === 'y') {
child_process.execSync(`git commit -m "${commitLog}"`);
load.succeed('commit success');
} else {
}
rl.close();
});
} catch (e) {
load.stop();
load.fail('Generate commit log fail');
console.error(e.message);
}
};