UNPKG

scai

Version:

> AI-powered CLI tools for smart commit messages, auto generated comments, and readme files — all powered by local models.

107 lines (105 loc) • 6.26 kB
import { execSync } from 'child_process'; import readline from 'readline'; async function askUserToChoose(suggestions) { return new Promise((resolve) => { console.log('\nšŸ’” AI-suggested commit messages:\n'); // Display all suggestions to the user suggestions.forEach((msg, i) => { console.log(`${i + 1}) ${msg}`); }); console.log(`${suggestions.length + 1}) šŸ” Regenerate suggestions`); // Allow the user to regenerate suggestions if they want console.log(`${suggestions.length + 2}) āœļø Write your own commit message`); // Allow the user to write their own commit message const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); rl.question(`\nšŸ‘‰ Choose a commit message [1-${suggestions.length + 2}]: `, // Ask the user to choose from the suggestions or write their own (answer) => { rl.close(); const choice = parseInt(answer, 10); // Parse the user's input as a number if (isNaN(choice) || choice < 1 || choice > suggestions.length + 2) { // Check that the user has entered a valid number console.log('āš ļø Invalid selection. Using the first suggestion by default.'); // Default to the first suggestion if the input is invalid resolve(0); // Resolve the promise with the 0-based index (0 to 3) } else if (choice === suggestions.length + 2) { // If the user has chosen "Write your own commit message" resolve('custom'); // Return 'custom' to indicate that the user wants to write their own commit message } else { resolve(choice - 1); // Return the 0-based index (0 to 3) of the selected suggestion } }); }); } async function generateSuggestions(prompt) { const res = await fetch("http://localhost:11434/api/generate", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ model: "llama3", prompt, stream: false, }), }); const { response } = await res.json(); // Parse the response from the API as JSON if (!response || typeof response !== 'string') { // Check that the response is valid throw new Error('Invalid LLM response'); // Throw an error if the response is invalid } const lines = response.trim().split('\n').filter(line => /^\d+\.\s+/.test(line)); // Split the response into individual lines and filter out any lines that don't start with a number followed by a period const messages = lines.map(line => // Map each line to its commit message suggestion line.replace(/^\d+\.\s+/, '').replace(/^"(.*)"$/, '$1').trim()); if (messages.length === 0) { // Check that there are any commit message suggestions throw new Error('No valid commit messages found in LLM response.'); // Throw an error if there are no valid commit message suggestions } return messages; // Return the array of commit message suggestions } function promptCustomMessage() { return new Promise((resolve) => { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); rl.question('\nšŸ“ Enter your custom commit message:\n> ', (input) => { rl.close(); resolve(input.trim()); // Resolve the promise with the trimmed input string }); }); } export async function suggestCommitMessage(options) { try { let diff = execSync("git diff", { encoding: "utf-8" }).trim(); // Get the current Git diff if (!diff) { // Check that there are any changes to stage diff = execSync("git diff --cached", { encoding: "utf-8" }).trim(); // If there are no staged changes, check for unstaged changes } if (!diff) { // If there are still no changes, display a message and return early console.log('āš ļø No staged changes to suggest a message for.'); return; } const prompt = `Suggest 3 concise, conventional Git commit message options for this diff. Return ONLY the commit messages, numbered 1 to 3, like so: 1. ... 2. ... 3. ... Here is the diff: ${diff}`; // Create a prompt string for the user to enter their own commit message suggestions let message = null; while (message === null) { // Loop until the user has entered a valid commit message or chosen to write their own const suggestions = await generateSuggestions(prompt); // Generate commit message suggestions based on the diff output from Git const choice = await askUserToChoose(suggestions); // Ask the user to choose from the suggestions or write their own if (choice === 'custom') { // If the user has chosen "Write your own commit message" message = await promptCustomMessage(); // Prompt the user for their custom commit message break; // Break out of the loop and use the custom commit message } message = suggestions[choice]; // Use the selected suggestion as the commit message } console.log(`\nāœ… Selected commit message:\n${message}\n`); // Display the selected commit message to the user if (options.commit) { // If the user has specified that they want to commit the changes const commitDiff = execSync("git diff", { encoding: "utf-8" }).trim(); // Get the current Git diff again, in case anything has changed since the last call to `generateSuggestions` if (commitDiff) { // Check that there are any staged changes execSync("git add .", { encoding: "utf-8" }); // Stage all changes before committing } execSync(`git commit -m "${message.replace(/"/g, '\\"')}"`, { stdio: 'inherit' }); // Commit the changes with the selected commit message console.log('āœ… Committed with selected message.'); // Display a success message to the user } } catch (err) { // If there is an error in the code console.error('āŒ Error in commit message suggestion:', err); // Log the error to the console } }