UNPKG

nyegosh

Version:

AI generated commit messages, like Njegoš wrote them.

147 lines (129 loc) 4.67 kB
#!/usr/bin/env node require("colors"); const simpleGit = require("simple-git"); const { exec } = require("child_process"); const { promisify } = require("util"); const path = require("path"); const dotenv = require("dotenv"); const { conventionalCommitsDoc } = require("./conventional-commits"); const { initializeModelConnection } = require("./auth"); const { writeToGitCommitEditmsg } = require("./commit-editmsg"); const { promptUser } = require("./lib/readline"); const envPath = path.join(require("os").homedir(), ".nyegosh.env"); dotenv.config({ path: envPath }); const aiClient = initializeModelConnection(); const gitHandler = simpleGit(); const execAsync = promisify(exec); var messages = [ { role: "system", content: `You are a helpful coding assistant for generating meaningful commit messages no longer than 300 characters based on the user provided code diff.`, }, { role: "system", content: `Specification for generating commit message: ${conventionalCommitsDoc}.`, }, ]; async function getStagedDiff() { try { const diff = await gitHandler.diff(["--staged"]); if (!diff.trim()) { console.log("No staged changes to commit.".red); process.exit(1); } return diff; } catch (error) { console.error("Error getting staged diff:", error.message.red); throw error; } } function getPrompt(diff) { const prompt = ` Given the following git diff generate a single commit message: \n\n ${diff} `; return prompt; } async function generateCommitMessage(refinementPrompt = undefined) { if (refinementPrompt) { messages.push(refinementPrompt); } try { const response = await aiClient.chat.completions.create({ model: process.env.NYEGOSH_GENERATOR_DEPLOYMENT, stream: false, messages: messages, }); const generatedCommitMessage = response.choices?.[0]?.message?.content?.trim(); if (!generatedCommitMessage) { throw new Error("AI did not generate a commit message."); } messages.push({ role: "assistant", content: generatedCommitMessage }); return generatedCommitMessage; } catch (error) { // TODO: Handle token limit exceeded console.error("Error generating commit message:", error.message.red); throw error; } } async function writeCommitMessage(message) { console.log("AI-Suggested Commit Message:".blue); console.log(`\n${message}\n`); writeToGitCommitEditmsg(message); const userResponse = await promptUser( "Do you want to (c)ommit suggested message, (w)rite your own, (r)efine the current message or (a)bort? (c/w/r/a): " .blue, ); switch (userResponse.toLowerCase()) { case "c": await commitChanges(message); break; case "w": const customMessage = await promptUser("\nEnter your commit message:\n".cyan); if (customMessage.trim()) { await commitChanges(customMessage); } else { console.log("Commit message is empty. Aborting.".red); } break; case "a": writeToGitCommitEditmsg(""); console.log("Commit aborted.".yellow); break; case "r": let userPrompt = await promptUser("\nHow can I make the message better?:\n".cyan); let refinementPrompt = { role: "user", content: userPrompt }; let refinedMessage = await generateCommitMessage(refinementPrompt); writeCommitMessage(refinedMessage); break; default: writeToGitCommitEditmsg(""); console.log("Invalid option. Commit aborted.".red); } } async function commitChanges(message) { try { const escapedMessage = message.replace(/`/g, "\\`"); await execAsync(`git commit -m "${escapedMessage}"`); console.info("Changes committed successfully.".green); } catch (error) { console.error("Error committing changes:", error.message.red); throw error; } } (async () => { try { const diff = await getStagedDiff(); if (!diff) { console.log("No staged changes to commit.".yellow); process.exit(1); } const prompt = getPrompt(diff); messages.push({ role: "user", content: prompt }); const commitMessage = await generateCommitMessage(); writeCommitMessage(commitMessage); } catch (error) { process.exit(1); } })();