UNPKG

scai

Version:

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

110 lines (108 loc) • 4.13 kB
import { execSync } from 'child_process'; import readline from 'readline'; function askUserToChoose(suggestions) { return new Promise((resolve) => { console.log('\nšŸ’” AI-suggested commit messages:\n'); suggestions.forEach((msg, i) => { console.log(`${i + 1}) ${msg}`); }); console.log(`${suggestions.length + 1}) šŸ” Regenerate suggestions`); console.log(`${suggestions.length + 2}) āœļø Write your own commit message`); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); rl.question(`\nšŸ‘‰ Choose a commit message [1-${suggestions.length + 2}]: `, (answer) => { rl.close(); const choice = parseInt(answer, 10); if (isNaN(choice) || choice < 1 || choice > suggestions.length + 2) { console.log('āš ļø Invalid selection. Using the first suggestion by default.'); resolve(0); } else if (choice === suggestions.length + 2) { resolve('custom'); } else { resolve(choice - 1); // Return 0-based index (0 to 3) } }); }); } 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(); if (!response || typeof response !== 'string') { throw new Error('Invalid LLM response'); } const lines = response.trim().split('\n').filter(line => /^\d+\.\s+/.test(line)); const messages = lines.map(line => line.replace(/^\d+\.\s+/, '').replace(/^"(.*)"$/, '$1').trim()); if (messages.length === 0) { throw new Error('No valid commit messages found in LLM response.'); } return messages; } 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()); }); }); } export async function suggestCommitMessage(options) { try { let diff = execSync("git diff", { encoding: "utf-8" }).trim(); if (!diff) { diff = execSync("git diff --cached", { encoding: "utf-8" }).trim(); } if (!diff) { 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}`; let message = null; while (message === null) { const suggestions = await generateSuggestions(prompt); const choice = await askUserToChoose(suggestions); if (choice === suggestions.length) { // User chose "Regenerate" console.log('\nšŸ”„ Regenerating suggestions...\n'); continue; } if (choice === 'custom') { message = await promptCustomMessage(); break; } message = suggestions[choice]; } console.log(`\nāœ… Selected commit message:\n${message}\n`); if (options.commit) { const commitDiff = execSync("git diff", { encoding: "utf-8" }).trim(); if (commitDiff) { execSync("git add .", { encoding: "utf-8" }); } execSync(`git commit -m "${message.replace(/"/g, '\\"')}"`, { stdio: 'inherit' }); console.log('āœ… Committed with selected message.'); } } catch (err) { console.error('āŒ Error in commit message suggestion:', err.message); } }