git-coco 0.22.2 → 0.22.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.esm.mjs +219 -12
- package/dist/index.js +219 -12
- package/package.json +2 -3
package/dist/index.esm.mjs
CHANGED
|
@@ -47,7 +47,7 @@ import { pathToFileURL } from 'url';
|
|
|
47
47
|
/**
|
|
48
48
|
* Current build version from package.json
|
|
49
49
|
*/
|
|
50
|
-
const BUILD_VERSION = "0.22.
|
|
50
|
+
const BUILD_VERSION = "0.22.4";
|
|
51
51
|
|
|
52
52
|
const isInteractive = (config) => {
|
|
53
53
|
return config?.mode === 'interactive' || !!config?.interactive;
|
|
@@ -6219,7 +6219,7 @@ function getLlm(provider, model, config) {
|
|
|
6219
6219
|
});
|
|
6220
6220
|
case 'openai':
|
|
6221
6221
|
return new ChatOpenAI({
|
|
6222
|
-
|
|
6222
|
+
apiKey: apiKey,
|
|
6223
6223
|
model,
|
|
6224
6224
|
temperature: config.service.temperature || 0.2,
|
|
6225
6225
|
});
|
|
@@ -11476,9 +11476,34 @@ ${schema.description}
|
|
|
11476
11476
|
// Load commitlint rules context if available
|
|
11477
11477
|
const hasCommitLintConfig = await hasCommitlintConfig();
|
|
11478
11478
|
let commitlint_rules_context = '';
|
|
11479
|
+
let shouldSkipCommitlintValidation = false;
|
|
11479
11480
|
if (USE_CONVENTIONAL_COMMITS || hasCommitLintConfig) {
|
|
11480
|
-
const { getCommitlintRulesContext } = await Promise.resolve().then(function () { return commitlintValidator; });
|
|
11481
|
-
|
|
11481
|
+
const { getCommitlintRulesContext, checkCommitlintAvailability } = await Promise.resolve().then(function () { return commitlintValidator; });
|
|
11482
|
+
// Check if commitlint packages are available
|
|
11483
|
+
const availability = checkCommitlintAvailability();
|
|
11484
|
+
if (!availability.available) {
|
|
11485
|
+
const { handleMissingCommitlintDeps } = await Promise.resolve().then(function () { return handleMissingCommitlintDeps$1; });
|
|
11486
|
+
const result = await handleMissingCommitlintDeps({
|
|
11487
|
+
logger,
|
|
11488
|
+
interactive: INTERACTIVE,
|
|
11489
|
+
missingPackages: availability.missingPackages,
|
|
11490
|
+
});
|
|
11491
|
+
switch (result.action) {
|
|
11492
|
+
case 'continue':
|
|
11493
|
+
shouldSkipCommitlintValidation = true;
|
|
11494
|
+
logger.log('Continuing without commitlint validation...', { color: 'yellow' });
|
|
11495
|
+
break;
|
|
11496
|
+
case 'setup':
|
|
11497
|
+
logger.log('\nPlease run `coco init` to set up commitlint, then try again.', { color: 'blue' });
|
|
11498
|
+
process.exit(0);
|
|
11499
|
+
case 'abort':
|
|
11500
|
+
logger.log('\nAborting commit operation.', { color: 'red' });
|
|
11501
|
+
process.exit(1);
|
|
11502
|
+
}
|
|
11503
|
+
}
|
|
11504
|
+
else {
|
|
11505
|
+
commitlint_rules_context = await getCommitlintRulesContext();
|
|
11506
|
+
}
|
|
11482
11507
|
}
|
|
11483
11508
|
// Get variables for the prompt
|
|
11484
11509
|
const variables = {
|
|
@@ -11525,13 +11550,33 @@ ${schema.description}
|
|
|
11525
11550
|
const ticketId = extractTicketIdFromBranchName(branchName);
|
|
11526
11551
|
const ticketFooter = argv.appendTicket && ticketId ? `\n\nPart of **${ticketId}**` : '';
|
|
11527
11552
|
const fullMessage = `${commitMsg.title}\n\n${commitMsg.body}${appendedText}${ticketFooter}`;
|
|
11528
|
-
// If commitlint validation is needed, validate the message
|
|
11529
|
-
if (USE_CONVENTIONAL_COMMITS || hasCommitLintConfig) {
|
|
11553
|
+
// If commitlint validation is needed and not skipped, validate the message
|
|
11554
|
+
if ((USE_CONVENTIONAL_COMMITS || hasCommitLintConfig) && !shouldSkipCommitlintValidation) {
|
|
11530
11555
|
const { validateCommitMessage, CommitlintValidationError } = await Promise.resolve().then(function () { return commitlintValidator; });
|
|
11531
11556
|
const validationResult = await validateCommitMessage(fullMessage);
|
|
11532
11557
|
logger.verbose(`Validation result: ${JSON.stringify(validationResult)}`, {
|
|
11533
11558
|
color: 'yellow',
|
|
11534
11559
|
});
|
|
11560
|
+
// Handle missing dependencies gracefully
|
|
11561
|
+
if (validationResult.missingDependencies && validationResult.missingDependencies.length > 0) {
|
|
11562
|
+
const { handleMissingCommitlintDeps } = await Promise.resolve().then(function () { return handleMissingCommitlintDeps$1; });
|
|
11563
|
+
const result = await handleMissingCommitlintDeps({
|
|
11564
|
+
logger,
|
|
11565
|
+
interactive: INTERACTIVE,
|
|
11566
|
+
missingPackages: validationResult.missingDependencies,
|
|
11567
|
+
});
|
|
11568
|
+
switch (result.action) {
|
|
11569
|
+
case 'continue':
|
|
11570
|
+
logger.log('Skipping commitlint validation...', { color: 'yellow' });
|
|
11571
|
+
return fullMessage;
|
|
11572
|
+
case 'setup':
|
|
11573
|
+
logger.log('\nPlease run `coco init` to set up commitlint, then try again.', { color: 'blue' });
|
|
11574
|
+
process.exit(0);
|
|
11575
|
+
case 'abort':
|
|
11576
|
+
logger.log('\nAborting commit due to missing dependencies.', { color: 'red' });
|
|
11577
|
+
process.exit(1);
|
|
11578
|
+
}
|
|
11579
|
+
}
|
|
11535
11580
|
if (!validationResult.valid) {
|
|
11536
11581
|
retryCount++;
|
|
11537
11582
|
// Format validation errors for next attempt
|
|
@@ -11575,7 +11620,7 @@ ${schema.description}
|
|
|
11575
11620
|
};
|
|
11576
11621
|
// Use retry wrapper for commitlint validation with up to 4 total attempts
|
|
11577
11622
|
// (2 automatic retries + 2 more if user chooses "Try again")
|
|
11578
|
-
|
|
11623
|
+
const result = await withRetry(generateCommitMessage, {
|
|
11579
11624
|
maxAttempts: 6, // Allow for multiple user retry requests
|
|
11580
11625
|
shouldRetry: shouldRetryCommitlint,
|
|
11581
11626
|
backoffMs: 0, // No delay needed for commitlint retries
|
|
@@ -11587,6 +11632,15 @@ ${schema.description}
|
|
|
11587
11632
|
logger.verbose(`Retrying commit message generation (attempt ${attempt}): ${error.message}`, { color: 'yellow' });
|
|
11588
11633
|
},
|
|
11589
11634
|
});
|
|
11635
|
+
// Ensure we always return a formatted string, not a JSON object
|
|
11636
|
+
if (typeof result === 'object' && result !== null && 'title' in result && 'body' in result) {
|
|
11637
|
+
const commitMsgObj = result;
|
|
11638
|
+
const appendedText = argv.append ? `\n\n${argv.append}` : '';
|
|
11639
|
+
const ticketId = extractTicketIdFromBranchName(branchName);
|
|
11640
|
+
const ticketFooter = argv.appendTicket && ticketId ? `\n\nPart of **${ticketId}**` : '';
|
|
11641
|
+
return `${commitMsgObj.title}\n\n${commitMsgObj.body}${appendedText}${ticketFooter}`;
|
|
11642
|
+
}
|
|
11643
|
+
return result;
|
|
11590
11644
|
},
|
|
11591
11645
|
noResult: async () => {
|
|
11592
11646
|
await noResult$2({ git, logger });
|
|
@@ -11969,14 +12023,21 @@ const questions = {
|
|
|
11969
12023
|
},
|
|
11970
12024
|
],
|
|
11971
12025
|
}),
|
|
12026
|
+
setupCommitlint: async () => await confirm({
|
|
12027
|
+
message: 'set up commitlint for conventional commits support?',
|
|
12028
|
+
default: true,
|
|
12029
|
+
}),
|
|
11972
12030
|
};
|
|
11973
12031
|
|
|
11974
12032
|
const handler$2 = async (argv, logger) => {
|
|
11975
12033
|
const options = loadConfig(argv);
|
|
11976
12034
|
logger.log(LOGO);
|
|
11977
12035
|
let scope = options?.scope;
|
|
12036
|
+
let shouldSetupCommitlint = false;
|
|
11978
12037
|
if (!scope) {
|
|
11979
12038
|
scope = await questions.whatScope();
|
|
12039
|
+
// Ask about commitlint setup after scope selection
|
|
12040
|
+
shouldSetupCommitlint = await questions.setupCommitlint();
|
|
11980
12041
|
const llmProvider = await questions.selectLLMProvider();
|
|
11981
12042
|
const llmModel = await questions.selectLLMModel(llmProvider);
|
|
11982
12043
|
const service = getDefaultServiceConfigFromAlias(llmProvider, llmModel);
|
|
@@ -12093,6 +12154,10 @@ const handler$2 = async (argv, logger) => {
|
|
|
12093
12154
|
}
|
|
12094
12155
|
// After config is written, check for package installation
|
|
12095
12156
|
await checkAndHandlePackageInstallation({ global: scope === 'global', logger });
|
|
12157
|
+
// Install commitlint packages if user requested
|
|
12158
|
+
if (shouldSetupCommitlint) {
|
|
12159
|
+
await installCommitlintPackages(scope, logger);
|
|
12160
|
+
}
|
|
12096
12161
|
logger.log(`\ninit successful! 🦾🤖🎉`, { color: 'green' });
|
|
12097
12162
|
}
|
|
12098
12163
|
else {
|
|
@@ -12100,6 +12165,39 @@ const handler$2 = async (argv, logger) => {
|
|
|
12100
12165
|
}
|
|
12101
12166
|
}
|
|
12102
12167
|
};
|
|
12168
|
+
/**
|
|
12169
|
+
* Install commitlint packages based on scope (global or project)
|
|
12170
|
+
*/
|
|
12171
|
+
async function installCommitlintPackages(scope, logger) {
|
|
12172
|
+
const packages = ['@commitlint/config-conventional', '@commitlint/cli'];
|
|
12173
|
+
try {
|
|
12174
|
+
if (scope === 'global') {
|
|
12175
|
+
logger.startSpinner('Installing commitlint packages globally...', { color: 'blue' });
|
|
12176
|
+
for (const pkg of packages) {
|
|
12177
|
+
await installNpmPackage({ name: pkg, flags: ['-g'] });
|
|
12178
|
+
}
|
|
12179
|
+
logger.stopSpinner('Installed commitlint packages globally');
|
|
12180
|
+
}
|
|
12181
|
+
else {
|
|
12182
|
+
logger.startSpinner('Installing commitlint packages in project...', { color: 'blue' });
|
|
12183
|
+
for (const pkg of packages) {
|
|
12184
|
+
await installNpmPackage({ name: pkg, flags: ['--save-dev'] });
|
|
12185
|
+
}
|
|
12186
|
+
logger.stopSpinner('Installed commitlint packages in project');
|
|
12187
|
+
}
|
|
12188
|
+
}
|
|
12189
|
+
catch (error) {
|
|
12190
|
+
logger.stopSpinner('Failed to install commitlint packages');
|
|
12191
|
+
logger.log(`Error installing commitlint packages: ${error.message}`, { color: 'red' });
|
|
12192
|
+
logger.log('You can install them manually later:', { color: 'yellow' });
|
|
12193
|
+
if (scope === 'global') {
|
|
12194
|
+
logger.log('npm install -g @commitlint/config-conventional @commitlint/cli', { color: 'gray' });
|
|
12195
|
+
}
|
|
12196
|
+
else {
|
|
12197
|
+
logger.log('npm install --save-dev @commitlint/config-conventional @commitlint/cli', { color: 'gray' });
|
|
12198
|
+
}
|
|
12199
|
+
}
|
|
12200
|
+
}
|
|
12103
12201
|
|
|
12104
12202
|
var init = {
|
|
12105
12203
|
command: command$2,
|
|
@@ -12786,6 +12884,31 @@ class CommitlintValidationError extends Error {
|
|
|
12786
12884
|
this.commitMessage = commitMessage;
|
|
12787
12885
|
}
|
|
12788
12886
|
}
|
|
12887
|
+
/**
|
|
12888
|
+
* Check if commitlint config packages are available
|
|
12889
|
+
* We only check for config-conventional since @commitlint/core is bundled with git-coco
|
|
12890
|
+
*/
|
|
12891
|
+
function checkCommitlintAvailability() {
|
|
12892
|
+
const requiredPackages = ['@commitlint/config-conventional'];
|
|
12893
|
+
const missingPackages = [];
|
|
12894
|
+
for (const pkg of requiredPackages) {
|
|
12895
|
+
try {
|
|
12896
|
+
// Try to resolve the package from the current working directory
|
|
12897
|
+
require.resolve(pkg, { paths: [process.cwd(), ...module.paths] });
|
|
12898
|
+
}
|
|
12899
|
+
catch (error) {
|
|
12900
|
+
missingPackages.push(pkg);
|
|
12901
|
+
}
|
|
12902
|
+
}
|
|
12903
|
+
// If config-conventional is missing, also suggest installing CLI
|
|
12904
|
+
if (missingPackages.length > 0) {
|
|
12905
|
+
missingPackages.push('@commitlint/cli');
|
|
12906
|
+
}
|
|
12907
|
+
return {
|
|
12908
|
+
available: missingPackages.length === 0,
|
|
12909
|
+
missingPackages,
|
|
12910
|
+
};
|
|
12911
|
+
}
|
|
12789
12912
|
/**
|
|
12790
12913
|
* Load commitlint configuration
|
|
12791
12914
|
*/
|
|
@@ -12825,10 +12948,35 @@ async function loadCommitlintConfig() {
|
|
|
12825
12948
|
catch (error) {
|
|
12826
12949
|
// Could be an error parsing, or just not found. Fall through to default.
|
|
12827
12950
|
}
|
|
12828
|
-
//
|
|
12829
|
-
|
|
12830
|
-
|
|
12831
|
-
|
|
12951
|
+
// Try to fallback to conventional config, but handle missing dependencies gracefully
|
|
12952
|
+
try {
|
|
12953
|
+
return await load({
|
|
12954
|
+
extends: ['@commitlint/config-conventional'],
|
|
12955
|
+
});
|
|
12956
|
+
}
|
|
12957
|
+
catch (error) {
|
|
12958
|
+
// If @commitlint/config-conventional is not available, return a basic conventional config
|
|
12959
|
+
if (error instanceof Error && error.message.includes('Cannot find module "@commitlint/config-conventional"')) {
|
|
12960
|
+
return await load({
|
|
12961
|
+
rules: {
|
|
12962
|
+
'header-max-length': [2, 'always', 72],
|
|
12963
|
+
'header-min-length': [2, 'always', 8],
|
|
12964
|
+
'subject-empty': [2, 'never'],
|
|
12965
|
+
'subject-full-stop': [2, 'never', '.'],
|
|
12966
|
+
'subject-case': [2, 'always', ['sentence-case', 'start-case', 'pascal-case', 'upper-case', 'lower-case']],
|
|
12967
|
+
'type-empty': [2, 'never'],
|
|
12968
|
+
'type-case': [2, 'always', 'lower-case'],
|
|
12969
|
+
'type-enum': [2, 'always', [
|
|
12970
|
+
'build', 'chore', 'ci', 'docs', 'feat', 'fix',
|
|
12971
|
+
'perf', 'refactor', 'revert', 'style', 'test'
|
|
12972
|
+
]],
|
|
12973
|
+
'body-max-line-length': [2, 'always', 100],
|
|
12974
|
+
'scope-case': [2, 'always', 'lower-case'],
|
|
12975
|
+
},
|
|
12976
|
+
});
|
|
12977
|
+
}
|
|
12978
|
+
throw error;
|
|
12979
|
+
}
|
|
12832
12980
|
}
|
|
12833
12981
|
/**
|
|
12834
12982
|
* Format commitlint rules into a human-readable string for AI prompts
|
|
@@ -12937,9 +13085,19 @@ async function validateCommitMessage(message, options = {}) {
|
|
|
12937
13085
|
};
|
|
12938
13086
|
}
|
|
12939
13087
|
catch (error) {
|
|
13088
|
+
const errorMessage = error.message;
|
|
13089
|
+
// Check if this is a missing dependency error
|
|
13090
|
+
if (errorMessage.includes('Cannot find module "@commitlint/config-conventional"')) {
|
|
13091
|
+
return {
|
|
13092
|
+
valid: false,
|
|
13093
|
+
errors: ['Commitlint configuration requires @commitlint/config-conventional to be installed'],
|
|
13094
|
+
warnings: [],
|
|
13095
|
+
missingDependencies: ['@commitlint/config-conventional', '@commitlint/cli'],
|
|
13096
|
+
};
|
|
13097
|
+
}
|
|
12940
13098
|
return {
|
|
12941
13099
|
valid: false,
|
|
12942
|
-
errors: [
|
|
13100
|
+
errors: [errorMessage],
|
|
12943
13101
|
warnings: [],
|
|
12944
13102
|
};
|
|
12945
13103
|
}
|
|
@@ -12948,12 +13106,61 @@ async function validateCommitMessage(message, options = {}) {
|
|
|
12948
13106
|
var commitlintValidator = /*#__PURE__*/Object.freeze({
|
|
12949
13107
|
__proto__: null,
|
|
12950
13108
|
CommitlintValidationError: CommitlintValidationError,
|
|
13109
|
+
checkCommitlintAvailability: checkCommitlintAvailability,
|
|
12951
13110
|
formatCommitlintRulesForPrompt: formatCommitlintRulesForPrompt,
|
|
12952
13111
|
getCommitlintRulesContext: getCommitlintRulesContext,
|
|
12953
13112
|
loadCommitlintConfig: loadCommitlintConfig,
|
|
12954
13113
|
validateCommitMessage: validateCommitMessage
|
|
12955
13114
|
});
|
|
12956
13115
|
|
|
13116
|
+
/**
|
|
13117
|
+
* Handle missing commitlint dependencies with user-friendly options
|
|
13118
|
+
*/
|
|
13119
|
+
async function handleMissingCommitlintDeps(options) {
|
|
13120
|
+
const { logger, interactive, missingPackages } = options;
|
|
13121
|
+
if (!interactive) {
|
|
13122
|
+
logger.log('\nCommitlint packages not found. Skipping commit message validation.', {
|
|
13123
|
+
color: 'yellow',
|
|
13124
|
+
});
|
|
13125
|
+
logger.log('Run `coco init` to set up commitlint for conventional commits.', {
|
|
13126
|
+
color: 'gray',
|
|
13127
|
+
});
|
|
13128
|
+
return { action: 'continue' };
|
|
13129
|
+
}
|
|
13130
|
+
logger.log('\nCommitlint configuration requires additional packages:', { color: 'yellow' });
|
|
13131
|
+
missingPackages.forEach((pkg) => {
|
|
13132
|
+
logger.log(` • ${pkg}`, { color: 'gray' });
|
|
13133
|
+
});
|
|
13134
|
+
logger.log('');
|
|
13135
|
+
const choice = await select({
|
|
13136
|
+
message: 'How would you like to proceed?',
|
|
13137
|
+
choices: [
|
|
13138
|
+
{
|
|
13139
|
+
name: 'Continue without commitlint validation',
|
|
13140
|
+
value: 'continue',
|
|
13141
|
+
description: 'Generate commit message without validation rules',
|
|
13142
|
+
},
|
|
13143
|
+
{
|
|
13144
|
+
name: 'Set up commitlint (run coco init)',
|
|
13145
|
+
value: 'setup',
|
|
13146
|
+
description: 'Exit and run the init command to install required packages',
|
|
13147
|
+
},
|
|
13148
|
+
{
|
|
13149
|
+
name: 'Abort',
|
|
13150
|
+
value: 'abort',
|
|
13151
|
+
description: 'Cancel the commit operation',
|
|
13152
|
+
},
|
|
13153
|
+
],
|
|
13154
|
+
default: 'continue',
|
|
13155
|
+
});
|
|
13156
|
+
return { action: choice };
|
|
13157
|
+
}
|
|
13158
|
+
|
|
13159
|
+
var handleMissingCommitlintDeps$1 = /*#__PURE__*/Object.freeze({
|
|
13160
|
+
__proto__: null,
|
|
13161
|
+
handleMissingCommitlintDeps: handleMissingCommitlintDeps
|
|
13162
|
+
});
|
|
13163
|
+
|
|
12957
13164
|
/**
|
|
12958
13165
|
* Handle commit message validation results with user interaction
|
|
12959
13166
|
*/
|
package/dist/index.js
CHANGED
|
@@ -69,7 +69,7 @@ var readline__namespace = /*#__PURE__*/_interopNamespaceDefault(readline);
|
|
|
69
69
|
/**
|
|
70
70
|
* Current build version from package.json
|
|
71
71
|
*/
|
|
72
|
-
const BUILD_VERSION = "0.22.
|
|
72
|
+
const BUILD_VERSION = "0.22.4";
|
|
73
73
|
|
|
74
74
|
const isInteractive = (config) => {
|
|
75
75
|
return config?.mode === 'interactive' || !!config?.interactive;
|
|
@@ -6241,7 +6241,7 @@ function getLlm(provider, model, config) {
|
|
|
6241
6241
|
});
|
|
6242
6242
|
case 'openai':
|
|
6243
6243
|
return new openai.ChatOpenAI({
|
|
6244
|
-
|
|
6244
|
+
apiKey: apiKey,
|
|
6245
6245
|
model,
|
|
6246
6246
|
temperature: config.service.temperature || 0.2,
|
|
6247
6247
|
});
|
|
@@ -11498,9 +11498,34 @@ ${schema.description}
|
|
|
11498
11498
|
// Load commitlint rules context if available
|
|
11499
11499
|
const hasCommitLintConfig = await hasCommitlintConfig();
|
|
11500
11500
|
let commitlint_rules_context = '';
|
|
11501
|
+
let shouldSkipCommitlintValidation = false;
|
|
11501
11502
|
if (USE_CONVENTIONAL_COMMITS || hasCommitLintConfig) {
|
|
11502
|
-
const { getCommitlintRulesContext } = await Promise.resolve().then(function () { return commitlintValidator; });
|
|
11503
|
-
|
|
11503
|
+
const { getCommitlintRulesContext, checkCommitlintAvailability } = await Promise.resolve().then(function () { return commitlintValidator; });
|
|
11504
|
+
// Check if commitlint packages are available
|
|
11505
|
+
const availability = checkCommitlintAvailability();
|
|
11506
|
+
if (!availability.available) {
|
|
11507
|
+
const { handleMissingCommitlintDeps } = await Promise.resolve().then(function () { return handleMissingCommitlintDeps$1; });
|
|
11508
|
+
const result = await handleMissingCommitlintDeps({
|
|
11509
|
+
logger,
|
|
11510
|
+
interactive: INTERACTIVE,
|
|
11511
|
+
missingPackages: availability.missingPackages,
|
|
11512
|
+
});
|
|
11513
|
+
switch (result.action) {
|
|
11514
|
+
case 'continue':
|
|
11515
|
+
shouldSkipCommitlintValidation = true;
|
|
11516
|
+
logger.log('Continuing without commitlint validation...', { color: 'yellow' });
|
|
11517
|
+
break;
|
|
11518
|
+
case 'setup':
|
|
11519
|
+
logger.log('\nPlease run `coco init` to set up commitlint, then try again.', { color: 'blue' });
|
|
11520
|
+
process.exit(0);
|
|
11521
|
+
case 'abort':
|
|
11522
|
+
logger.log('\nAborting commit operation.', { color: 'red' });
|
|
11523
|
+
process.exit(1);
|
|
11524
|
+
}
|
|
11525
|
+
}
|
|
11526
|
+
else {
|
|
11527
|
+
commitlint_rules_context = await getCommitlintRulesContext();
|
|
11528
|
+
}
|
|
11504
11529
|
}
|
|
11505
11530
|
// Get variables for the prompt
|
|
11506
11531
|
const variables = {
|
|
@@ -11547,13 +11572,33 @@ ${schema.description}
|
|
|
11547
11572
|
const ticketId = extractTicketIdFromBranchName(branchName);
|
|
11548
11573
|
const ticketFooter = argv.appendTicket && ticketId ? `\n\nPart of **${ticketId}**` : '';
|
|
11549
11574
|
const fullMessage = `${commitMsg.title}\n\n${commitMsg.body}${appendedText}${ticketFooter}`;
|
|
11550
|
-
// If commitlint validation is needed, validate the message
|
|
11551
|
-
if (USE_CONVENTIONAL_COMMITS || hasCommitLintConfig) {
|
|
11575
|
+
// If commitlint validation is needed and not skipped, validate the message
|
|
11576
|
+
if ((USE_CONVENTIONAL_COMMITS || hasCommitLintConfig) && !shouldSkipCommitlintValidation) {
|
|
11552
11577
|
const { validateCommitMessage, CommitlintValidationError } = await Promise.resolve().then(function () { return commitlintValidator; });
|
|
11553
11578
|
const validationResult = await validateCommitMessage(fullMessage);
|
|
11554
11579
|
logger.verbose(`Validation result: ${JSON.stringify(validationResult)}`, {
|
|
11555
11580
|
color: 'yellow',
|
|
11556
11581
|
});
|
|
11582
|
+
// Handle missing dependencies gracefully
|
|
11583
|
+
if (validationResult.missingDependencies && validationResult.missingDependencies.length > 0) {
|
|
11584
|
+
const { handleMissingCommitlintDeps } = await Promise.resolve().then(function () { return handleMissingCommitlintDeps$1; });
|
|
11585
|
+
const result = await handleMissingCommitlintDeps({
|
|
11586
|
+
logger,
|
|
11587
|
+
interactive: INTERACTIVE,
|
|
11588
|
+
missingPackages: validationResult.missingDependencies,
|
|
11589
|
+
});
|
|
11590
|
+
switch (result.action) {
|
|
11591
|
+
case 'continue':
|
|
11592
|
+
logger.log('Skipping commitlint validation...', { color: 'yellow' });
|
|
11593
|
+
return fullMessage;
|
|
11594
|
+
case 'setup':
|
|
11595
|
+
logger.log('\nPlease run `coco init` to set up commitlint, then try again.', { color: 'blue' });
|
|
11596
|
+
process.exit(0);
|
|
11597
|
+
case 'abort':
|
|
11598
|
+
logger.log('\nAborting commit due to missing dependencies.', { color: 'red' });
|
|
11599
|
+
process.exit(1);
|
|
11600
|
+
}
|
|
11601
|
+
}
|
|
11557
11602
|
if (!validationResult.valid) {
|
|
11558
11603
|
retryCount++;
|
|
11559
11604
|
// Format validation errors for next attempt
|
|
@@ -11597,7 +11642,7 @@ ${schema.description}
|
|
|
11597
11642
|
};
|
|
11598
11643
|
// Use retry wrapper for commitlint validation with up to 4 total attempts
|
|
11599
11644
|
// (2 automatic retries + 2 more if user chooses "Try again")
|
|
11600
|
-
|
|
11645
|
+
const result = await withRetry(generateCommitMessage, {
|
|
11601
11646
|
maxAttempts: 6, // Allow for multiple user retry requests
|
|
11602
11647
|
shouldRetry: shouldRetryCommitlint,
|
|
11603
11648
|
backoffMs: 0, // No delay needed for commitlint retries
|
|
@@ -11609,6 +11654,15 @@ ${schema.description}
|
|
|
11609
11654
|
logger.verbose(`Retrying commit message generation (attempt ${attempt}): ${error.message}`, { color: 'yellow' });
|
|
11610
11655
|
},
|
|
11611
11656
|
});
|
|
11657
|
+
// Ensure we always return a formatted string, not a JSON object
|
|
11658
|
+
if (typeof result === 'object' && result !== null && 'title' in result && 'body' in result) {
|
|
11659
|
+
const commitMsgObj = result;
|
|
11660
|
+
const appendedText = argv.append ? `\n\n${argv.append}` : '';
|
|
11661
|
+
const ticketId = extractTicketIdFromBranchName(branchName);
|
|
11662
|
+
const ticketFooter = argv.appendTicket && ticketId ? `\n\nPart of **${ticketId}**` : '';
|
|
11663
|
+
return `${commitMsgObj.title}\n\n${commitMsgObj.body}${appendedText}${ticketFooter}`;
|
|
11664
|
+
}
|
|
11665
|
+
return result;
|
|
11612
11666
|
},
|
|
11613
11667
|
noResult: async () => {
|
|
11614
11668
|
await noResult$2({ git, logger });
|
|
@@ -11991,14 +12045,21 @@ const questions = {
|
|
|
11991
12045
|
},
|
|
11992
12046
|
],
|
|
11993
12047
|
}),
|
|
12048
|
+
setupCommitlint: async () => await prompts.confirm({
|
|
12049
|
+
message: 'set up commitlint for conventional commits support?',
|
|
12050
|
+
default: true,
|
|
12051
|
+
}),
|
|
11994
12052
|
};
|
|
11995
12053
|
|
|
11996
12054
|
const handler$2 = async (argv, logger) => {
|
|
11997
12055
|
const options = loadConfig(argv);
|
|
11998
12056
|
logger.log(LOGO);
|
|
11999
12057
|
let scope = options?.scope;
|
|
12058
|
+
let shouldSetupCommitlint = false;
|
|
12000
12059
|
if (!scope) {
|
|
12001
12060
|
scope = await questions.whatScope();
|
|
12061
|
+
// Ask about commitlint setup after scope selection
|
|
12062
|
+
shouldSetupCommitlint = await questions.setupCommitlint();
|
|
12002
12063
|
const llmProvider = await questions.selectLLMProvider();
|
|
12003
12064
|
const llmModel = await questions.selectLLMModel(llmProvider);
|
|
12004
12065
|
const service = getDefaultServiceConfigFromAlias(llmProvider, llmModel);
|
|
@@ -12115,6 +12176,10 @@ const handler$2 = async (argv, logger) => {
|
|
|
12115
12176
|
}
|
|
12116
12177
|
// After config is written, check for package installation
|
|
12117
12178
|
await checkAndHandlePackageInstallation({ global: scope === 'global', logger });
|
|
12179
|
+
// Install commitlint packages if user requested
|
|
12180
|
+
if (shouldSetupCommitlint) {
|
|
12181
|
+
await installCommitlintPackages(scope, logger);
|
|
12182
|
+
}
|
|
12118
12183
|
logger.log(`\ninit successful! 🦾🤖🎉`, { color: 'green' });
|
|
12119
12184
|
}
|
|
12120
12185
|
else {
|
|
@@ -12122,6 +12187,39 @@ const handler$2 = async (argv, logger) => {
|
|
|
12122
12187
|
}
|
|
12123
12188
|
}
|
|
12124
12189
|
};
|
|
12190
|
+
/**
|
|
12191
|
+
* Install commitlint packages based on scope (global or project)
|
|
12192
|
+
*/
|
|
12193
|
+
async function installCommitlintPackages(scope, logger) {
|
|
12194
|
+
const packages = ['@commitlint/config-conventional', '@commitlint/cli'];
|
|
12195
|
+
try {
|
|
12196
|
+
if (scope === 'global') {
|
|
12197
|
+
logger.startSpinner('Installing commitlint packages globally...', { color: 'blue' });
|
|
12198
|
+
for (const pkg of packages) {
|
|
12199
|
+
await installNpmPackage({ name: pkg, flags: ['-g'] });
|
|
12200
|
+
}
|
|
12201
|
+
logger.stopSpinner('Installed commitlint packages globally');
|
|
12202
|
+
}
|
|
12203
|
+
else {
|
|
12204
|
+
logger.startSpinner('Installing commitlint packages in project...', { color: 'blue' });
|
|
12205
|
+
for (const pkg of packages) {
|
|
12206
|
+
await installNpmPackage({ name: pkg, flags: ['--save-dev'] });
|
|
12207
|
+
}
|
|
12208
|
+
logger.stopSpinner('Installed commitlint packages in project');
|
|
12209
|
+
}
|
|
12210
|
+
}
|
|
12211
|
+
catch (error) {
|
|
12212
|
+
logger.stopSpinner('Failed to install commitlint packages');
|
|
12213
|
+
logger.log(`Error installing commitlint packages: ${error.message}`, { color: 'red' });
|
|
12214
|
+
logger.log('You can install them manually later:', { color: 'yellow' });
|
|
12215
|
+
if (scope === 'global') {
|
|
12216
|
+
logger.log('npm install -g @commitlint/config-conventional @commitlint/cli', { color: 'gray' });
|
|
12217
|
+
}
|
|
12218
|
+
else {
|
|
12219
|
+
logger.log('npm install --save-dev @commitlint/config-conventional @commitlint/cli', { color: 'gray' });
|
|
12220
|
+
}
|
|
12221
|
+
}
|
|
12222
|
+
}
|
|
12125
12223
|
|
|
12126
12224
|
var init = {
|
|
12127
12225
|
command: command$2,
|
|
@@ -12808,6 +12906,31 @@ class CommitlintValidationError extends Error {
|
|
|
12808
12906
|
this.commitMessage = commitMessage;
|
|
12809
12907
|
}
|
|
12810
12908
|
}
|
|
12909
|
+
/**
|
|
12910
|
+
* Check if commitlint config packages are available
|
|
12911
|
+
* We only check for config-conventional since @commitlint/core is bundled with git-coco
|
|
12912
|
+
*/
|
|
12913
|
+
function checkCommitlintAvailability() {
|
|
12914
|
+
const requiredPackages = ['@commitlint/config-conventional'];
|
|
12915
|
+
const missingPackages = [];
|
|
12916
|
+
for (const pkg of requiredPackages) {
|
|
12917
|
+
try {
|
|
12918
|
+
// Try to resolve the package from the current working directory
|
|
12919
|
+
require.resolve(pkg, { paths: [process.cwd(), ...module.paths] });
|
|
12920
|
+
}
|
|
12921
|
+
catch (error) {
|
|
12922
|
+
missingPackages.push(pkg);
|
|
12923
|
+
}
|
|
12924
|
+
}
|
|
12925
|
+
// If config-conventional is missing, also suggest installing CLI
|
|
12926
|
+
if (missingPackages.length > 0) {
|
|
12927
|
+
missingPackages.push('@commitlint/cli');
|
|
12928
|
+
}
|
|
12929
|
+
return {
|
|
12930
|
+
available: missingPackages.length === 0,
|
|
12931
|
+
missingPackages,
|
|
12932
|
+
};
|
|
12933
|
+
}
|
|
12811
12934
|
/**
|
|
12812
12935
|
* Load commitlint configuration
|
|
12813
12936
|
*/
|
|
@@ -12847,10 +12970,35 @@ async function loadCommitlintConfig() {
|
|
|
12847
12970
|
catch (error) {
|
|
12848
12971
|
// Could be an error parsing, or just not found. Fall through to default.
|
|
12849
12972
|
}
|
|
12850
|
-
//
|
|
12851
|
-
|
|
12852
|
-
|
|
12853
|
-
|
|
12973
|
+
// Try to fallback to conventional config, but handle missing dependencies gracefully
|
|
12974
|
+
try {
|
|
12975
|
+
return await load({
|
|
12976
|
+
extends: ['@commitlint/config-conventional'],
|
|
12977
|
+
});
|
|
12978
|
+
}
|
|
12979
|
+
catch (error) {
|
|
12980
|
+
// If @commitlint/config-conventional is not available, return a basic conventional config
|
|
12981
|
+
if (error instanceof Error && error.message.includes('Cannot find module "@commitlint/config-conventional"')) {
|
|
12982
|
+
return await load({
|
|
12983
|
+
rules: {
|
|
12984
|
+
'header-max-length': [2, 'always', 72],
|
|
12985
|
+
'header-min-length': [2, 'always', 8],
|
|
12986
|
+
'subject-empty': [2, 'never'],
|
|
12987
|
+
'subject-full-stop': [2, 'never', '.'],
|
|
12988
|
+
'subject-case': [2, 'always', ['sentence-case', 'start-case', 'pascal-case', 'upper-case', 'lower-case']],
|
|
12989
|
+
'type-empty': [2, 'never'],
|
|
12990
|
+
'type-case': [2, 'always', 'lower-case'],
|
|
12991
|
+
'type-enum': [2, 'always', [
|
|
12992
|
+
'build', 'chore', 'ci', 'docs', 'feat', 'fix',
|
|
12993
|
+
'perf', 'refactor', 'revert', 'style', 'test'
|
|
12994
|
+
]],
|
|
12995
|
+
'body-max-line-length': [2, 'always', 100],
|
|
12996
|
+
'scope-case': [2, 'always', 'lower-case'],
|
|
12997
|
+
},
|
|
12998
|
+
});
|
|
12999
|
+
}
|
|
13000
|
+
throw error;
|
|
13001
|
+
}
|
|
12854
13002
|
}
|
|
12855
13003
|
/**
|
|
12856
13004
|
* Format commitlint rules into a human-readable string for AI prompts
|
|
@@ -12959,9 +13107,19 @@ async function validateCommitMessage(message, options = {}) {
|
|
|
12959
13107
|
};
|
|
12960
13108
|
}
|
|
12961
13109
|
catch (error) {
|
|
13110
|
+
const errorMessage = error.message;
|
|
13111
|
+
// Check if this is a missing dependency error
|
|
13112
|
+
if (errorMessage.includes('Cannot find module "@commitlint/config-conventional"')) {
|
|
13113
|
+
return {
|
|
13114
|
+
valid: false,
|
|
13115
|
+
errors: ['Commitlint configuration requires @commitlint/config-conventional to be installed'],
|
|
13116
|
+
warnings: [],
|
|
13117
|
+
missingDependencies: ['@commitlint/config-conventional', '@commitlint/cli'],
|
|
13118
|
+
};
|
|
13119
|
+
}
|
|
12962
13120
|
return {
|
|
12963
13121
|
valid: false,
|
|
12964
|
-
errors: [
|
|
13122
|
+
errors: [errorMessage],
|
|
12965
13123
|
warnings: [],
|
|
12966
13124
|
};
|
|
12967
13125
|
}
|
|
@@ -12970,12 +13128,61 @@ async function validateCommitMessage(message, options = {}) {
|
|
|
12970
13128
|
var commitlintValidator = /*#__PURE__*/Object.freeze({
|
|
12971
13129
|
__proto__: null,
|
|
12972
13130
|
CommitlintValidationError: CommitlintValidationError,
|
|
13131
|
+
checkCommitlintAvailability: checkCommitlintAvailability,
|
|
12973
13132
|
formatCommitlintRulesForPrompt: formatCommitlintRulesForPrompt,
|
|
12974
13133
|
getCommitlintRulesContext: getCommitlintRulesContext,
|
|
12975
13134
|
loadCommitlintConfig: loadCommitlintConfig,
|
|
12976
13135
|
validateCommitMessage: validateCommitMessage
|
|
12977
13136
|
});
|
|
12978
13137
|
|
|
13138
|
+
/**
|
|
13139
|
+
* Handle missing commitlint dependencies with user-friendly options
|
|
13140
|
+
*/
|
|
13141
|
+
async function handleMissingCommitlintDeps(options) {
|
|
13142
|
+
const { logger, interactive, missingPackages } = options;
|
|
13143
|
+
if (!interactive) {
|
|
13144
|
+
logger.log('\nCommitlint packages not found. Skipping commit message validation.', {
|
|
13145
|
+
color: 'yellow',
|
|
13146
|
+
});
|
|
13147
|
+
logger.log('Run `coco init` to set up commitlint for conventional commits.', {
|
|
13148
|
+
color: 'gray',
|
|
13149
|
+
});
|
|
13150
|
+
return { action: 'continue' };
|
|
13151
|
+
}
|
|
13152
|
+
logger.log('\nCommitlint configuration requires additional packages:', { color: 'yellow' });
|
|
13153
|
+
missingPackages.forEach((pkg) => {
|
|
13154
|
+
logger.log(` • ${pkg}`, { color: 'gray' });
|
|
13155
|
+
});
|
|
13156
|
+
logger.log('');
|
|
13157
|
+
const choice = await prompts.select({
|
|
13158
|
+
message: 'How would you like to proceed?',
|
|
13159
|
+
choices: [
|
|
13160
|
+
{
|
|
13161
|
+
name: 'Continue without commitlint validation',
|
|
13162
|
+
value: 'continue',
|
|
13163
|
+
description: 'Generate commit message without validation rules',
|
|
13164
|
+
},
|
|
13165
|
+
{
|
|
13166
|
+
name: 'Set up commitlint (run coco init)',
|
|
13167
|
+
value: 'setup',
|
|
13168
|
+
description: 'Exit and run the init command to install required packages',
|
|
13169
|
+
},
|
|
13170
|
+
{
|
|
13171
|
+
name: 'Abort',
|
|
13172
|
+
value: 'abort',
|
|
13173
|
+
description: 'Cancel the commit operation',
|
|
13174
|
+
},
|
|
13175
|
+
],
|
|
13176
|
+
default: 'continue',
|
|
13177
|
+
});
|
|
13178
|
+
return { action: choice };
|
|
13179
|
+
}
|
|
13180
|
+
|
|
13181
|
+
var handleMissingCommitlintDeps$1 = /*#__PURE__*/Object.freeze({
|
|
13182
|
+
__proto__: null,
|
|
13183
|
+
handleMissingCommitlintDeps: handleMissingCommitlintDeps
|
|
13184
|
+
});
|
|
13185
|
+
|
|
12979
13186
|
/**
|
|
12980
13187
|
* Handle commit message validation results with user interaction
|
|
12981
13188
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-coco",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.4",
|
|
4
4
|
"description": "zero-effort git commits with coco.",
|
|
5
5
|
"author": "gfargo <ghfargo@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"coco:esm": "node dist/index.esm.mjs"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
+
"@commitlint/types": "^19.8.0",
|
|
48
49
|
"@rollup/plugin-commonjs": "^28.0.0",
|
|
49
50
|
"@rollup/plugin-eslint": "^9.0.5",
|
|
50
51
|
"@rollup/plugin-json": "^6.0.0",
|
|
@@ -73,9 +74,7 @@
|
|
|
73
74
|
"typescript": "^5.4.5"
|
|
74
75
|
},
|
|
75
76
|
"dependencies": {
|
|
76
|
-
"@commitlint/config-conventional": "^19.8.0",
|
|
77
77
|
"@commitlint/core": "^19.8.0",
|
|
78
|
-
"@commitlint/types": "^19.8.0",
|
|
79
78
|
"@inquirer/prompts": "3.3.0",
|
|
80
79
|
"@langchain/anthropic": "^0.3.14",
|
|
81
80
|
"@langchain/community": "^0.3.32",
|