feat: show multiple options (#64)
This commit is contained in:
61
src/cli.ts
61
src/cli.ts
@@ -1,10 +1,12 @@
|
||||
import { execa } from 'execa';
|
||||
import {
|
||||
black, green, red, bgCyan,
|
||||
black, dim, green, red, bgCyan,
|
||||
} from 'kolorist';
|
||||
import {
|
||||
intro, outro, spinner, confirm, isCancel,
|
||||
intro, outro, spinner, select, confirm, isCancel,
|
||||
} from '@clack/prompts';
|
||||
import { cli } from 'cleye';
|
||||
import { description, version } from '../package.json';
|
||||
import {
|
||||
getConfig,
|
||||
assertGitRepo,
|
||||
@@ -13,6 +15,25 @@ import {
|
||||
generateCommitMessage,
|
||||
} from './utils.js';
|
||||
|
||||
const argv = cli({
|
||||
name: 'aicommits',
|
||||
|
||||
version,
|
||||
|
||||
flags: {
|
||||
generate: {
|
||||
type: Number,
|
||||
description: 'Number of messages to generate. (Warning: generating multiple costs more)',
|
||||
alias: 'g',
|
||||
default: 1,
|
||||
},
|
||||
},
|
||||
|
||||
help: {
|
||||
description,
|
||||
},
|
||||
});
|
||||
|
||||
(async () => {
|
||||
intro(bgCyan(black(' aicommits ')));
|
||||
|
||||
@@ -38,16 +59,36 @@ import {
|
||||
|
||||
const s = spinner();
|
||||
s.start('The AI is analyzing your changes');
|
||||
const message = await generateCommitMessage(OPENAI_KEY, staged.diff);
|
||||
s.stop('The commit message is ready for review');
|
||||
const messages = await generateCommitMessage(
|
||||
OPENAI_KEY,
|
||||
staged.diff,
|
||||
argv.flags.generate,
|
||||
);
|
||||
s.stop('Changes analyzed');
|
||||
|
||||
const confirmed = await confirm({
|
||||
message: `Would you like to commit with this message:\n\n ${message}\n`,
|
||||
});
|
||||
let message;
|
||||
if (messages.length === 1) {
|
||||
[message] = messages;
|
||||
const confirmed = await confirm({
|
||||
message: `Use this commit message?\n\n ${message}\n`,
|
||||
});
|
||||
|
||||
if (!confirmed || isCancel(confirmed)) {
|
||||
outro('Commit cancelled');
|
||||
return;
|
||||
if (!confirmed || isCancel(confirmed)) {
|
||||
outro('Commit cancelled');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const selected = await select({
|
||||
message: `Pick a commit message to use: ${dim('(Ctrl+c to exit)')}`,
|
||||
options: messages.map(value => ({ label: value, value })),
|
||||
});
|
||||
|
||||
if (isCancel(selected)) {
|
||||
outro('Commit cancelled');
|
||||
return;
|
||||
}
|
||||
|
||||
message = selected;
|
||||
}
|
||||
|
||||
await execa('git', ['commit', '-m', message]);
|
||||
|
||||
@@ -60,11 +60,14 @@ export const getStagedDiff = async () => {
|
||||
|
||||
export const getDetectedMessage = (files: string[]) => `Detected ${files.length.toLocaleString()} staged file${files.length > 1 ? 's' : ''}`;
|
||||
|
||||
const sanitizeMessage = (message: string) => message.trim().replace(/[\n\r]/g, '').replace(/(\w)\.$/, '$1');
|
||||
|
||||
const promptTemplate = 'Write an insightful but concise Git commit message in a complete sentence in present tense for the following diff without prefacing it with anything:';
|
||||
|
||||
export const generateCommitMessage = async (
|
||||
apiKey: string,
|
||||
diff: string,
|
||||
completions: number,
|
||||
) => {
|
||||
const prompt = `${promptTemplate}\n${diff}`;
|
||||
|
||||
@@ -84,10 +87,12 @@ export const generateCommitMessage = async (
|
||||
presence_penalty: 0,
|
||||
max_tokens: 200,
|
||||
stream: false,
|
||||
n: 1,
|
||||
n: completions,
|
||||
});
|
||||
|
||||
return completion.data.choices[0].text!.trim().replace(/[\n\r]/g, '').replace(/(\w)\.$/, '$1');
|
||||
return completion.data.choices
|
||||
.filter(choice => choice.text)
|
||||
.map(choice => sanitizeMessage(choice.text!));
|
||||
} catch (error) {
|
||||
const errorAsAny = error as any;
|
||||
errorAsAny.message = `OpenAI API Error: ${errorAsAny.message} - ${errorAsAny.response.statusText}`;
|
||||
|
||||
Reference in New Issue
Block a user