@elsikora/commitizen-plugin-commitlint-ai
Version:
AI-powered Commitizen adapter with Commitlint integration
177 lines (172 loc) • 8.17 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var chalk = require('chalk');
var commitConfirmation = require('./services/commitConfirmation.js');
var commitlintConfig = require('./services/commitlintConfig.js');
var commitlintValidator = require('./services/commitlintValidator.js');
var index = require('./services/llm/index.js');
var prompts = require('./store/prompts.js');
var config = require('./services/llm/config.js');
async function Process(rules, prompts$1, inquirer) {
prompts.setPromptConfig(prompts$1);
// First, ask for LLM provider and API key
await index.selectLLMProvider(inquirer);
// Extract context from commitlint config
const promptContext = commitlintConfig.extractLLMPromptContext(rules, prompts$1);
// Check if manual mode is enabled in config
const config$1 = config.getLLMConfig();
// If manual mode is enabled, skip AI generation and go straight to manual entry
if (config$1 && config$1.mode === "manual") {
console.log(chalk.blue("Using manual commit entry mode..."));
}
else {
try {
console.log(chalk.blue("Generating commit message with AI..."));
// Generate commit message using LLM
const commitConfig = await index.generateCommitMessage(promptContext);
// Validate the commit message with commitlint and fix if needed
const validatedCommitMessage = await commitlintValidator.validateAndFixCommitMessage(commitConfig, promptContext);
// If validation returned null, it means we should switch to manual mode
if (validatedCommitMessage === null) {
console.log(chalk.yellow("Switching to manual commit entry after failed validation attempts."));
}
else {
console.log(chalk.green("AI generated commit message successfully!"));
// Show the generated message to the user
console.log("\n" + chalk.yellow("Generated commit message:"));
console.log(chalk.white("-----------------------------------"));
console.log(chalk.white(validatedCommitMessage));
console.log(chalk.white("-----------------------------------\n"));
// Ask for confirmation
const { confirmCommit } = await inquirer.prompt([
{
// eslint-disable-next-line @elsikora-typescript/naming-convention
default: true,
message: "Do you want to proceed with this commit message?",
name: "confirmCommit",
type: "confirm",
},
]);
if (confirmCommit) {
return validatedCommitMessage;
}
else {
console.log(chalk.yellow("AI generated message rejected. Switching to commit edit mode."));
const confirmedCommitMessage = await commitConfirmation.commitConfirmation(promptContext, { ...commitConfig, scope: extractCommitScope(validatedCommitMessage) });
return confirmedCommitMessage;
// If user rejects the generated message, fall through to the manual entry option
}
}
}
catch (error) {
// Only show error for AI mode errors, not when manual mode is intentionally used
if (config$1?.mode !== "manual") {
console.error(chalk.red("Error generating commit with AI:"), error);
console.log(chalk.yellow("Falling back to manual commit entry..."));
}
}
}
// Fallback to regular prompts if LLM fails or in manual mode
const commitQuestions = [
{
choices: promptContext.typeEnum?.map((type) => {
const desc = promptContext.typeDescriptions?.[type]?.description ?? "";
const emoji = promptContext.typeDescriptions?.[type]?.emoji ?? "";
// Remove emoji from description if it already appears at the beginning
let cleanDesc = desc;
if (emoji && desc.startsWith(emoji)) {
cleanDesc = desc.slice(emoji.length).trim();
}
return {
name: type + (emoji ? " " + emoji : "") + ": " + cleanDesc,
value: type,
};
}) ?? [],
message: promptContext.typeDescription ?? "Select the type of change that you're committing:",
name: "type",
type: "list",
},
{
message: promptContext.scopeDescription ?? 'What is the scope of this change:\n - Use component, directory or area of codebase\n - Use comma-separated list for multiple areas\n - Type "global" for project-wide changes\n - Press enter to skip if scope is not applicable',
name: "scope",
type: "input",
},
{
message: promptContext.subject.description ?? "Write a short, imperative mood description of the change:",
name: "subject",
type: "input",
// eslint-disable-next-line @elsikora-sonar/function-return-type
validate: (input) => {
if (!input)
return "Subject is required";
return true;
},
},
{
message: promptContext.body?.description ?? "Provide a longer description of the change: (press enter to skip)",
name: "body",
type: "input",
},
{
// eslint-disable-next-line @elsikora-typescript/naming-convention
default: false,
message: "Are there any breaking changes?",
name: "isBreaking",
type: "confirm",
},
{
message: "Describe the breaking changes:",
name: "breakingBody",
type: "input",
// eslint-disable-next-line @elsikora-typescript/no-unsafe-return
when: (answers) => answers.isBreaking,
},
];
// First get all commit details
// @ts-ignore
const answers = await inquirer.prompt(commitQuestions);
// Construct message from manual answers
// eslint-disable-next-line @elsikora-typescript/no-unsafe-argument,@elsikora-typescript/restrict-plus-operands
const header = "".concat(answers.type, answers.scope ? "(" + answers.scope + ")" : "", ": ", answers.subject);
let body = "";
if (answers.isBreaking) {
// eslint-disable-next-line @elsikora-typescript/restrict-template-expressions
body = `BREAKING CHANGE: ${answers.breakingBody || "This commit introduces breaking changes."}\n\n`;
}
if (answers.body) {
// eslint-disable-next-line @elsikora-typescript/restrict-plus-operands
body += answers.body;
}
const commitMessage = [header, body].filter(Boolean).join("\n\n");
// Display the commit message to the user
console.log("\n" + chalk.yellow("Your commit message:"));
console.log(chalk.white("-----------------------------------"));
console.log(chalk.white(commitMessage));
console.log(chalk.white("-----------------------------------\n"));
// Now ask for confirmation
const { confirmCommit } = await inquirer.prompt([
{
// eslint-disable-next-line @elsikora-typescript/naming-convention
default: true,
message: "Are you sure you want to proceed with the commit above?",
name: "confirmCommit",
type: "confirm",
},
]);
// Check confirmation
if (!confirmCommit) {
console.log(chalk.yellow("Commit canceled."));
throw new Error("User canceled the commit");
}
return commitMessage;
}
function extractCommitScope(commitMessage) {
const scopeRegex = /^[^(\n]+(\([^)\n]+\))?:/;
const match = scopeRegex.exec(commitMessage);
if (match?.[1]) {
return match[1].slice(1, -1);
}
return "";
}
exports.default = Process;
//# sourceMappingURL=Process.js.map