From aecf99d1ab7736436b15ced0c1aa4ff98a98c263 Mon Sep 17 00:00:00 2001 From: Hassan El Mghari Date: Mon, 13 Feb 2023 15:28:08 -0500 Subject: [PATCH] rewrite v1 --- .gitignore | 2 ++ README.md | 9 +++-- aicommit.js | 2 -- cli.js | 6 ++++ lib.js | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 23 +++++-------- 6 files changed, 114 insertions(+), 21 deletions(-) create mode 100644 cli.js create mode 100644 lib.js diff --git a/.gitignore b/.gitignore index b7869dd..25c4eb0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .env.json +/node_modules +package-lock.json diff --git a/README.md b/README.md index 4f5c2ab..99e1835 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ AI Commits is a tool that writes your git commit messages for you. Never write a [![AI Commit Screenshot](https://github.com/Nutlope/aicommits/blob/main/screenshot.png)](https://twitter.com/nutlope/status/1624646872890589184) -## How to install +## Installation and usage -1. `npm install -g autocommit` -2. `export OPENAIKEY=sk-xxxxxxxxxxxxxxxx` -3. `autocommit` after you run `git add .` +1. `npm install -g autocommit` to install the CLI +2. `export OPENAI_KEY=sk-xxxxxxxxxxxxxxxx` to specify your OpenAI API Key +3. `autocommit` after you run `git add .` to generate your commit ## How it works @@ -23,7 +23,6 @@ It currently can only support git diffs of up to 200 lines of code. I'm working Now: - Rewrite this in node to publish as an npm package - - Figure out how to fail gracefully instead of exit 1 Future tasks: diff --git a/aicommit.js b/aicommit.js index 473aa1a..e4a761d 100755 --- a/aicommit.js +++ b/aicommit.js @@ -3,8 +3,6 @@ void (async function () { $.verbose = false; - // TODO: Figure out how to fail gracefully instead of exit 1 - let conventionalCommit = false; console.log(chalk.white("▲ ") + chalk.green("Welcome to AICommit!")); diff --git a/cli.js b/cli.js new file mode 100644 index 0000000..d04edce --- /dev/null +++ b/cli.js @@ -0,0 +1,6 @@ +#! /usr/bin/env node +import { main } from "./lib"; + +(async () => { + await main(process.cwd()); +})(); diff --git a/lib.js b/lib.js new file mode 100644 index 0000000..03ae2aa --- /dev/null +++ b/lib.js @@ -0,0 +1,93 @@ +#!/usr/bin/env node +const { execSync, spawn } = require("child_process"); +let OPENAI_API_KEY = process.env.OPENAI_API_KEY; +import inquirer from "inquirer"; + +export async function main() { + console.log("Welcome to AICommit!"); + if (!OPENAI_API_KEY) { + console.error( + "Please specify an OpenAI key using export OPEN_AI_KEY='YOUR_API_KEY'" + ); + process.exit(1); + } + // Check to see if the user is in a git repo + try { + execSync("git rev-parse --is-inside-work-tree", { + encoding: "utf8", + stdio: "ignore", + }); + } catch (e) { + console.error("This is not a git repository"); + process.exit(1); + } + + let conventionalCommit = false; + + const diff = execSync("git diff --cached", { encoding: "utf8" }); + + if (!diff) { + console.log( + "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("The diff is too large to write a commit message."); + process.exit(1); + } + + let 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. ${ + conventionalCommit + ? "Preface the commit with 'feat:' if it is a feature or 'fix:' if it is a bug." + : "Do not preface the commit with anything." + } Return a complete sentence and do not repeat yourself: ${diff}`; + + const aiCommitMessage = await generateCommitMessage(prompt); + + console.log(aiCommitMessage); + + 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 !== "n") { + execSync(`git commit -m "${cleanedUpAiCommit}"`, { + encoding: "utf8", + }); + } +} + +async function generateCommitMessage(prompt) { + const payload = { + model: "text-davinci-003", + prompt, + temperature: 0.7, + top_p: 1, + frequency_penalty: 0, + presence_penalty: 0, + max_tokens: 200, + stream: false, + n: 1, + }; + const response = await fetch("https://api.openai.com/v1/completions", { + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${OPENAI_API_KEY ?? ""}`, + }, + method: "POST", + body: JSON.stringify(payload), + }); + + const json = await response.json(); + const aiCommit = json.choices[0].text; + + return aiCommit.replace(/(\r\n|\n|\r)/gm, ""); +} diff --git a/package.json b/package.json index d24dd05..f7d63be 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,15 @@ { "name": "aicommits", - "version": "0.0.3", - "description": "generates ai commit", - "main": "aicommit.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "start": "./aicommit.js" - }, + "version": "0.0.4", + "description": "Writes your git commit messages for you with AI", + "main": "cli.js", "bin": { - "gpt-commit": "./aicommit.js" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/Nutlope/aicommits.git" + "gpt-commit": "./index.js" }, + "repository": "https://github.com/Nutlope/aicommits", "author": "Hassan El Mghari (@nutlope)", - "license": "ISC", - "homepage": "https://github.com/Nutlope/aicommits#readme" + "license": "MIT", + "dependencies": { + "inquirer": "^9.1.4" + } }