From d6e7f042788a4046b98011f1e0c3fefd5b822e75 Mon Sep 17 00:00:00 2001 From: Hassan El Mghari Date: Mon, 13 Feb 2023 17:32:23 -0500 Subject: [PATCH] completed migration to typescript --- .gitignore | 1 + .npmignore | 3 ++ aicommits.ts | 104 +++++++++++++++++++++++++++++++++++++++++++++++ index.ts | 110 +++----------------------------------------------- package.json | 3 +- tsconfig.json | 10 ++--- 6 files changed, 120 insertions(+), 111 deletions(-) create mode 100644 .npmignore create mode 100644 aicommits.ts diff --git a/.gitignore b/.gitignore index 5e52727..f7acd32 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /node_modules package-lock.json +bin diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..e3e70fc --- /dev/null +++ b/.npmignore @@ -0,0 +1,3 @@ +screenshot.png +index.ts +aicommits.ts diff --git a/aicommits.ts b/aicommits.ts new file mode 100644 index 0000000..0dc3a9c --- /dev/null +++ b/aicommits.ts @@ -0,0 +1,104 @@ +#!/usr/bin/env node + +import chalk from "chalk"; +import { execSync } from "child_process"; +import inquirer from "inquirer"; +import fetch from "node-fetch"; + +let OPENAI_API_KEY = process.env.OPENAI_API_KEY; + +export async function main() { + console.log(chalk.white("▲ ") + chalk.green("Welcome to AICommit!")); + + if (!OPENAI_API_KEY) { + console.error( + chalk.white("▲ ") + + "Please specify an OpenAI key using export OPEN_AI_KEY='YOUR_API_KEY'" + ); + process.exit(1); + } + try { + execSync("git rev-parse --is-inside-work-tree", { + encoding: "utf8", + stdio: "ignore", + }); + } catch (e) { + console.error(chalk.white("▲ ") + "This is not a git repository"); + process.exit(1); + } + + const diff = execSync("git diff --cached", { 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); + } + + 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. Do not preface the commit with anything, return a complete sentence, and do not repeat yourself: ${diff}`; + + console.log( + chalk.white("▲ ") + chalk.gray("Generating your AI commit message...\n") + ); + const aiCommitMessage = await generateCommitMessage(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", + }); +} + +async function generateCommitMessage(prompt: string) { + 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: any = await response.json(); + const aiCommit = json.choices[0].text; + + return aiCommit.replace(/(\r\n|\n|\r)/gm, ""); +} diff --git a/index.ts b/index.ts index 686e6fe..69ba6a4 100644 --- a/index.ts +++ b/index.ts @@ -1,106 +1,6 @@ -#!/usr/bin/env node +#! /usr/bin/env node +import { main } from "./aicommits"; -import chalk from "chalk"; -import { execSync } from "child_process"; -import inquirer from "inquirer"; -import fetch from "node-fetch"; - -let OPENAI_API_KEY = process.env.OPENAI_API_KEY; - -async function main() { - console.log(chalk.white("▲ ") + chalk.green("Welcome to AICommit!")); - - if (!OPENAI_API_KEY) { - console.error( - chalk.white("▲ ") + - "Please specify an OpenAI key using export OPEN_AI_KEY='YOUR_API_KEY'" - ); - process.exit(1); - } - try { - execSync("git rev-parse --is-inside-work-tree", { - encoding: "utf8", - stdio: "ignore", - }); - } catch (e) { - console.error(chalk.white("▲ ") + "This is not a git repository"); - process.exit(1); - } - - const diff = execSync("git diff --cached", { 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); - } - - 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. Do not preface the commit with anything, return a complete sentence, and do not repeat yourself: ${diff}`; - - console.log( - chalk.white("▲ ") + chalk.gray("Generating your AI commit message...\n") - ); - const aiCommitMessage = await generateCommitMessage(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", - }); -} - -async function generateCommitMessage(prompt: string) { - 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: any = await response.json(); - const aiCommit = json.choices[0].text; - - return aiCommit.replace(/(\r\n|\n|\r)/gm, ""); -} - -await main(); +(async () => { + await main(); +})(); diff --git a/package.json b/package.json index 893cd98..68169b3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aicommits", - "version": "0.1.6", + "version": "0.1.7", "description": "Writes your git commit messages for you with AI", "main": "bin/index.js", "bin": { @@ -10,6 +10,7 @@ "author": "Hassan El Mghari (@nutlope)", "license": "MIT", "devDependencies": { + "@types/node": "^18.13.0", "typescript": "^4.9.5" }, "scripts": { diff --git a/tsconfig.json b/tsconfig.json index b79262b..76dc155 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,10 @@ { "compilerOptions": { - "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, - "module": "commonjs" /* Specify what module code is generated. */, - "outDir": "./bin" /* Specify an output folder for all emitted files. */, - "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */, - "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, + "target": "es2017", + "module": "commonjs", + "outDir": "./bin", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, "skipLibCheck": true /* Skip type checking all .d.ts files. */ }, "exclude": ["node_modules"]