diff --git a/README.md b/README.md index 9feb3c3b..a017d49b 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,24 @@ After doing the two steps above, generate your commit by running `aicommits`. > Note: If you get a EACCESS error on mac/linux when running the first command, try running it with `sudo npm install -g aicommits`. +## Using the `--long` or `--verbose` Flag + +By default, the AI Commits CLI generates concise commit messages. However, if you want to generate a longer commit message that provides more comprehensive information about the changes, you can use the --long or --verbose flags. + +To use the --long or --verbose flag, simply add it to the end of the command: + +```bash +aicommits --long +``` + +You can also use the short forms -l and -v: + +```bash +aicommits -v +``` + +When the --long or --verbose flag is set, the AI Commits CLI will generate a more detailed commit message that provides comprehensive information about the changes. This can be useful when you want to provide more context for a particular commit. + ## How it works This CLI tool runs a `git diff` command to grab all the latest changes, sends this to OpenAI's GPT-3, then returns the AI generated commit message. I also want to note that it does cost money since GPT-3 generations aren't free. However, OpenAI gives folks $18 of free credits and commit message generations are cheap so it should be free for a long time. diff --git a/src/cli.ts b/src/cli.ts index 9b02e885..3d679c24 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -7,108 +7,128 @@ import { Configuration, OpenAIApi } from 'openai'; const OPENAI_KEY = process.env.OPENAI_KEY ?? process.env.OPENAI_API_KEY; -const generateCommitMessage = async ( - apiKey: string, - prompt: string, -) => { - const openai = new OpenAIApi(new Configuration({ apiKey })); - try { - const completion = await openai.createCompletion({ - model: 'text-davinci-003', - prompt, - temperature: 0.7, - top_p: 1, - frequency_penalty: 0, - presence_penalty: 0, - max_tokens: 200, - stream: false, - n: 1, - }); - - return completion.data.choices[0].text!.trim().replace(/[\n\r]/g, ''); - } catch (error) { - const errorAsAny = error as any; - errorAsAny.message = `OpenAI API Error: ${errorAsAny.message} - ${errorAsAny.response.statusText}`; - throw errorAsAny; - } +const generateCommitMessage = async (apiKey: string, prompt: string) => { + const openai = new OpenAIApi(new Configuration({ apiKey })); + try { + const completion = await openai.createCompletion({ + model: 'text-davinci-003', + prompt, + temperature: 0.7, + top_p: 1, + frequency_penalty: 0, + presence_penalty: 0, + max_tokens: 200, + stream: false, + n: 1, + }); + + return completion.data.choices[0].text!.trim().replace(/[\n\r]/g, ''); + } catch (error) { + const errorAsAny = error as any; + errorAsAny.message = `OpenAI API Error: ${errorAsAny.message} - ${errorAsAny.response.statusText}`; + throw errorAsAny; + } }; (async () => { - console.log(chalk.white('▲ ') + chalk.green('Welcome to AICommits!')); - - if (!OPENAI_KEY) { - console.error( - `${chalk.white('▲ ') - }Please save your OpenAI API key as an env variable by doing 'export OPENAI_KEY=YOUR_API_KEY'`, - ); - process.exit(1); - } - try { - execSync('git rev-parse --is-inside-work-tree', { - encoding: 'utf8', - stdio: 'ignore', - }); - } catch { - console.error(`${chalk.white('▲ ')}This is not a git repository`); - process.exit(1); - } - - const diff = execSync( - 'git diff --cached . ":(exclude)package-lock.json" ":(exclude)yarn.lock" ":(exclude)pnpm-lock.yaml"', - { - encoding: 'utf8', - }, - ); - - if (!diff) { - console.log( - `${chalk.white('▲ ') - }No staged changes found. Make sure there are changes and run \`git add .\``, - ); - process.exit(1); - } - - // Accounting for GPT-3's input req of 4k tokens (approx 8k chars) - if (diff.length > 8000) { - console.log( - `${chalk.white('▲ ')}The diff is too large to write a commit message.`, - ); - process.exit(1); - } - - const prompt = `I want you to act like a git commit message writer. I will input a git diff and your job is to convert it into a useful commit message. Do not preface the commit with anything, use the present tense, return a complete sentence, and do not repeat yourself: ${diff}`; - - console.log( - chalk.white('▲ ') + chalk.gray('Generating your AI commit message...\n'), - ); - - try { - const aiCommitMessage = await generateCommitMessage(OPENAI_KEY, prompt); - console.log( - `${chalk.white('▲ ') + chalk.bold('Commit message: ') + aiCommitMessage - }\n`, - ); - - const confirmationMessage = await inquirer.prompt([ - { - name: 'useCommitMessage', - message: 'Would you like to use this commit message? (Y / n)', - choices: ['Y', 'y', 'n'], - default: 'y', - }, - ]); - - if (confirmationMessage.useCommitMessage === 'n') { - console.log(`${chalk.white('▲ ')}Commit message has not been commited.`); - process.exit(1); - } - - execSync(`git commit -m "${aiCommitMessage}"`, { - stdio: 'inherit', - encoding: 'utf8', - }); - } catch (error) { - console.error(chalk.white('▲ ') + chalk.red(error.message)); - process.exit(1); - } + console.log(chalk.white('▲ ') + chalk.green('Welcome to AICommits!')); + + if (!OPENAI_KEY) { + console.error( + `${chalk.white( + '▲ ' + )}Please save your OpenAI API key as an env variable by doing 'export OPENAI_KEY=YOUR_API_KEY'` + ); + process.exit(1); + } + try { + execSync('git rev-parse --is-inside-work-tree', { + encoding: 'utf8', + stdio: 'ignore', + }); + } catch { + console.error(`${chalk.white('▲ ')}This is not a git repository`); + process.exit(1); + } + + const diff = execSync( + 'git diff --cached . ":(exclude)package-lock.json" ":(exclude)yarn.lock" ":(exclude)pnpm-lock.yaml"', + { + encoding: 'utf8', + } + ); + + if (!diff) { + console.log( + `${chalk.white( + '▲ ' + )}No staged changes found. Make sure there are changes and run \`git add .\`` + ); + process.exit(1); + } + + // Accounting for GPT-3's input req of 4k tokens (approx 8k chars) + if (diff.length > 8000) { + console.log( + `${chalk.white('▲ ')}The diff is too large to write a commit message.` + ); + process.exit(1); + } + + const CLI_ARGS = process.argv.slice(2); + let isLongArgSet = false; + + let prompt = `Write an insightful but concise Git commit message in a complete sentence in present tense for the following diff without prefacing it with anything: ${diff}`; + + if ( + CLI_ARGS.includes('--long') || + CLI_ARGS.includes('-l') || + CLI_ARGS.includes('--verbose') || + CLI_ARGS.includes('-v') + ) { + isLongArgSet = true; + prompt = `Write a verbose Git commit message in a complete sentence in the present tense for the following diff without any preamble, providing with comprehensive information about the changes: ${diff}`; + } + + console.log( + chalk.white('▲ ') + + chalk.gray( + `Generating your AI commit message ${ + isLongArgSet + ? 'with a verbose description' + : 'with a concise description' + }...\n` + ) + ); + + try { + const aiCommitMessage = await generateCommitMessage(OPENAI_KEY, prompt); + console.log( + `${ + chalk.white('▲ ') + chalk.bold('Commit message: ') + aiCommitMessage + }\n` + ); + + const confirmationMessage = await inquirer.prompt([ + { + name: 'useCommitMessage', + message: 'Would you like to use this commit message? (Y / n)', + choices: ['Y', 'y', 'n'], + default: 'y', + }, + ]); + + if (confirmationMessage.useCommitMessage === 'n') { + console.log(`${chalk.white('▲ ')}Commit message has not been commited.`); + process.exit(1); + } + + execSync(`git commit -m "${aiCommitMessage}"`, { + stdio: 'inherit', + encoding: 'utf8', + }); + } catch (error) { + console.error(chalk.white('▲ ') + chalk.red(error.message)); + process.exit(1); + } })();