git-coco 0.22.1 β 0.22.3
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 +211 -12
- package/dist/index.js +214 -15
- package/package.json +2 -3
package/dist/index.esm.mjs
CHANGED
|
@@ -40,7 +40,6 @@ import { minimatch } from 'minimatch';
|
|
|
40
40
|
import { encoding_for_model } from 'tiktoken';
|
|
41
41
|
import { exec } from 'child_process';
|
|
42
42
|
import * as readline from 'readline';
|
|
43
|
-
import { lint, load } from '@commitlint/core';
|
|
44
43
|
import { pathToFileURL } from 'url';
|
|
45
44
|
|
|
46
45
|
// This file is auto-generated - DO NOT EDIT
|
|
@@ -48,7 +47,7 @@ import { pathToFileURL } from 'url';
|
|
|
48
47
|
/**
|
|
49
48
|
* Current build version from package.json
|
|
50
49
|
*/
|
|
51
|
-
const BUILD_VERSION = "0.22.
|
|
50
|
+
const BUILD_VERSION = "0.22.3";
|
|
52
51
|
|
|
53
52
|
const isInteractive = (config) => {
|
|
54
53
|
return config?.mode === 'interactive' || !!config?.interactive;
|
|
@@ -6220,7 +6219,7 @@ function getLlm(provider, model, config) {
|
|
|
6220
6219
|
});
|
|
6221
6220
|
case 'openai':
|
|
6222
6221
|
return new ChatOpenAI({
|
|
6223
|
-
|
|
6222
|
+
apiKey: apiKey,
|
|
6224
6223
|
model,
|
|
6225
6224
|
temperature: config.service.temperature || 0.2,
|
|
6226
6225
|
});
|
|
@@ -11477,9 +11476,34 @@ ${schema.description}
|
|
|
11477
11476
|
// Load commitlint rules context if available
|
|
11478
11477
|
const hasCommitLintConfig = await hasCommitlintConfig();
|
|
11479
11478
|
let commitlint_rules_context = '';
|
|
11479
|
+
let shouldSkipCommitlintValidation = false;
|
|
11480
11480
|
if (USE_CONVENTIONAL_COMMITS || hasCommitLintConfig) {
|
|
11481
|
-
const { getCommitlintRulesContext } = await Promise.resolve().then(function () { return commitlintValidator; });
|
|
11482
|
-
|
|
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
|
+
}
|
|
11483
11507
|
}
|
|
11484
11508
|
// Get variables for the prompt
|
|
11485
11509
|
const variables = {
|
|
@@ -11526,13 +11550,33 @@ ${schema.description}
|
|
|
11526
11550
|
const ticketId = extractTicketIdFromBranchName(branchName);
|
|
11527
11551
|
const ticketFooter = argv.appendTicket && ticketId ? `\n\nPart of **${ticketId}**` : '';
|
|
11528
11552
|
const fullMessage = `${commitMsg.title}\n\n${commitMsg.body}${appendedText}${ticketFooter}`;
|
|
11529
|
-
// If commitlint validation is needed, validate the message
|
|
11530
|
-
if (USE_CONVENTIONAL_COMMITS || hasCommitLintConfig) {
|
|
11553
|
+
// If commitlint validation is needed and not skipped, validate the message
|
|
11554
|
+
if ((USE_CONVENTIONAL_COMMITS || hasCommitLintConfig) && !shouldSkipCommitlintValidation) {
|
|
11531
11555
|
const { validateCommitMessage, CommitlintValidationError } = await Promise.resolve().then(function () { return commitlintValidator; });
|
|
11532
11556
|
const validationResult = await validateCommitMessage(fullMessage);
|
|
11533
11557
|
logger.verbose(`Validation result: ${JSON.stringify(validationResult)}`, {
|
|
11534
11558
|
color: 'yellow',
|
|
11535
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
|
+
}
|
|
11536
11580
|
if (!validationResult.valid) {
|
|
11537
11581
|
retryCount++;
|
|
11538
11582
|
// Format validation errors for next attempt
|
|
@@ -11970,14 +12014,21 @@ const questions = {
|
|
|
11970
12014
|
},
|
|
11971
12015
|
],
|
|
11972
12016
|
}),
|
|
12017
|
+
setupCommitlint: async () => await confirm({
|
|
12018
|
+
message: 'set up commitlint for conventional commits support?',
|
|
12019
|
+
default: true,
|
|
12020
|
+
}),
|
|
11973
12021
|
};
|
|
11974
12022
|
|
|
11975
12023
|
const handler$2 = async (argv, logger) => {
|
|
11976
12024
|
const options = loadConfig(argv);
|
|
11977
12025
|
logger.log(LOGO);
|
|
11978
12026
|
let scope = options?.scope;
|
|
12027
|
+
let shouldSetupCommitlint = false;
|
|
11979
12028
|
if (!scope) {
|
|
11980
12029
|
scope = await questions.whatScope();
|
|
12030
|
+
// Ask about commitlint setup after scope selection
|
|
12031
|
+
shouldSetupCommitlint = await questions.setupCommitlint();
|
|
11981
12032
|
const llmProvider = await questions.selectLLMProvider();
|
|
11982
12033
|
const llmModel = await questions.selectLLMModel(llmProvider);
|
|
11983
12034
|
const service = getDefaultServiceConfigFromAlias(llmProvider, llmModel);
|
|
@@ -12094,6 +12145,10 @@ const handler$2 = async (argv, logger) => {
|
|
|
12094
12145
|
}
|
|
12095
12146
|
// After config is written, check for package installation
|
|
12096
12147
|
await checkAndHandlePackageInstallation({ global: scope === 'global', logger });
|
|
12148
|
+
// Install commitlint packages if user requested
|
|
12149
|
+
if (shouldSetupCommitlint) {
|
|
12150
|
+
await installCommitlintPackages(scope, logger);
|
|
12151
|
+
}
|
|
12097
12152
|
logger.log(`\ninit successful! π¦Ύπ€π`, { color: 'green' });
|
|
12098
12153
|
}
|
|
12099
12154
|
else {
|
|
@@ -12101,6 +12156,39 @@ const handler$2 = async (argv, logger) => {
|
|
|
12101
12156
|
}
|
|
12102
12157
|
}
|
|
12103
12158
|
};
|
|
12159
|
+
/**
|
|
12160
|
+
* Install commitlint packages based on scope (global or project)
|
|
12161
|
+
*/
|
|
12162
|
+
async function installCommitlintPackages(scope, logger) {
|
|
12163
|
+
const packages = ['@commitlint/config-conventional', '@commitlint/cli'];
|
|
12164
|
+
try {
|
|
12165
|
+
if (scope === 'global') {
|
|
12166
|
+
logger.startSpinner('Installing commitlint packages globally...', { color: 'blue' });
|
|
12167
|
+
for (const pkg of packages) {
|
|
12168
|
+
await installNpmPackage({ name: pkg, flags: ['-g'] });
|
|
12169
|
+
}
|
|
12170
|
+
logger.stopSpinner('Installed commitlint packages globally');
|
|
12171
|
+
}
|
|
12172
|
+
else {
|
|
12173
|
+
logger.startSpinner('Installing commitlint packages in project...', { color: 'blue' });
|
|
12174
|
+
for (const pkg of packages) {
|
|
12175
|
+
await installNpmPackage({ name: pkg, flags: ['--save-dev'] });
|
|
12176
|
+
}
|
|
12177
|
+
logger.stopSpinner('Installed commitlint packages in project');
|
|
12178
|
+
}
|
|
12179
|
+
}
|
|
12180
|
+
catch (error) {
|
|
12181
|
+
logger.stopSpinner('Failed to install commitlint packages');
|
|
12182
|
+
logger.log(`Error installing commitlint packages: ${error.message}`, { color: 'red' });
|
|
12183
|
+
logger.log('You can install them manually later:', { color: 'yellow' });
|
|
12184
|
+
if (scope === 'global') {
|
|
12185
|
+
logger.log('npm install -g @commitlint/config-conventional @commitlint/cli', { color: 'gray' });
|
|
12186
|
+
}
|
|
12187
|
+
else {
|
|
12188
|
+
logger.log('npm install --save-dev @commitlint/config-conventional @commitlint/cli', { color: 'gray' });
|
|
12189
|
+
}
|
|
12190
|
+
}
|
|
12191
|
+
}
|
|
12104
12192
|
|
|
12105
12193
|
var init = {
|
|
12106
12194
|
command: command$2,
|
|
@@ -12787,10 +12875,36 @@ class CommitlintValidationError extends Error {
|
|
|
12787
12875
|
this.commitMessage = commitMessage;
|
|
12788
12876
|
}
|
|
12789
12877
|
}
|
|
12878
|
+
/**
|
|
12879
|
+
* Check if commitlint config packages are available
|
|
12880
|
+
* We only check for config-conventional since @commitlint/core is bundled with git-coco
|
|
12881
|
+
*/
|
|
12882
|
+
function checkCommitlintAvailability() {
|
|
12883
|
+
const requiredPackages = ['@commitlint/config-conventional'];
|
|
12884
|
+
const missingPackages = [];
|
|
12885
|
+
for (const pkg of requiredPackages) {
|
|
12886
|
+
try {
|
|
12887
|
+
// Try to resolve the package from the current working directory
|
|
12888
|
+
require.resolve(pkg, { paths: [process.cwd(), ...module.paths] });
|
|
12889
|
+
}
|
|
12890
|
+
catch (error) {
|
|
12891
|
+
missingPackages.push(pkg);
|
|
12892
|
+
}
|
|
12893
|
+
}
|
|
12894
|
+
// If config-conventional is missing, also suggest installing CLI
|
|
12895
|
+
if (missingPackages.length > 0) {
|
|
12896
|
+
missingPackages.push('@commitlint/cli');
|
|
12897
|
+
}
|
|
12898
|
+
return {
|
|
12899
|
+
available: missingPackages.length === 0,
|
|
12900
|
+
missingPackages,
|
|
12901
|
+
};
|
|
12902
|
+
}
|
|
12790
12903
|
/**
|
|
12791
12904
|
* Load commitlint configuration
|
|
12792
12905
|
*/
|
|
12793
12906
|
async function loadCommitlintConfig() {
|
|
12907
|
+
const { load } = await import('@commitlint/core');
|
|
12794
12908
|
const projectRoot = findProjectRoot(process.cwd());
|
|
12795
12909
|
const cwd = projectRoot || process.cwd();
|
|
12796
12910
|
// @commitlint/load has issues with ESM configs (e.g. commitlint.config.js with `export default`).
|
|
@@ -12825,10 +12939,35 @@ async function loadCommitlintConfig() {
|
|
|
12825
12939
|
catch (error) {
|
|
12826
12940
|
// Could be an error parsing, or just not found. Fall through to default.
|
|
12827
12941
|
}
|
|
12828
|
-
//
|
|
12829
|
-
|
|
12830
|
-
|
|
12831
|
-
|
|
12942
|
+
// Try to fallback to conventional config, but handle missing dependencies gracefully
|
|
12943
|
+
try {
|
|
12944
|
+
return await load({
|
|
12945
|
+
extends: ['@commitlint/config-conventional'],
|
|
12946
|
+
});
|
|
12947
|
+
}
|
|
12948
|
+
catch (error) {
|
|
12949
|
+
// If @commitlint/config-conventional is not available, return a basic conventional config
|
|
12950
|
+
if (error instanceof Error && error.message.includes('Cannot find module "@commitlint/config-conventional"')) {
|
|
12951
|
+
return await load({
|
|
12952
|
+
rules: {
|
|
12953
|
+
'header-max-length': [2, 'always', 72],
|
|
12954
|
+
'header-min-length': [2, 'always', 8],
|
|
12955
|
+
'subject-empty': [2, 'never'],
|
|
12956
|
+
'subject-full-stop': [2, 'never', '.'],
|
|
12957
|
+
'subject-case': [2, 'always', ['sentence-case', 'start-case', 'pascal-case', 'upper-case', 'lower-case']],
|
|
12958
|
+
'type-empty': [2, 'never'],
|
|
12959
|
+
'type-case': [2, 'always', 'lower-case'],
|
|
12960
|
+
'type-enum': [2, 'always', [
|
|
12961
|
+
'build', 'chore', 'ci', 'docs', 'feat', 'fix',
|
|
12962
|
+
'perf', 'refactor', 'revert', 'style', 'test'
|
|
12963
|
+
]],
|
|
12964
|
+
'body-max-line-length': [2, 'always', 100],
|
|
12965
|
+
'scope-case': [2, 'always', 'lower-case'],
|
|
12966
|
+
},
|
|
12967
|
+
});
|
|
12968
|
+
}
|
|
12969
|
+
throw error;
|
|
12970
|
+
}
|
|
12832
12971
|
}
|
|
12833
12972
|
/**
|
|
12834
12973
|
* Format commitlint rules into a human-readable string for AI prompts
|
|
@@ -12927,6 +13066,7 @@ async function getCommitlintRulesContext() {
|
|
|
12927
13066
|
*/
|
|
12928
13067
|
async function validateCommitMessage(message, options = {}) {
|
|
12929
13068
|
try {
|
|
13069
|
+
const { lint } = await import('@commitlint/core');
|
|
12930
13070
|
const config = await loadCommitlintConfig();
|
|
12931
13071
|
const result = await lint(message, config.rules, options);
|
|
12932
13072
|
return {
|
|
@@ -12936,9 +13076,19 @@ async function validateCommitMessage(message, options = {}) {
|
|
|
12936
13076
|
};
|
|
12937
13077
|
}
|
|
12938
13078
|
catch (error) {
|
|
13079
|
+
const errorMessage = error.message;
|
|
13080
|
+
// Check if this is a missing dependency error
|
|
13081
|
+
if (errorMessage.includes('Cannot find module "@commitlint/config-conventional"')) {
|
|
13082
|
+
return {
|
|
13083
|
+
valid: false,
|
|
13084
|
+
errors: ['Commitlint configuration requires @commitlint/config-conventional to be installed'],
|
|
13085
|
+
warnings: [],
|
|
13086
|
+
missingDependencies: ['@commitlint/config-conventional', '@commitlint/cli'],
|
|
13087
|
+
};
|
|
13088
|
+
}
|
|
12939
13089
|
return {
|
|
12940
13090
|
valid: false,
|
|
12941
|
-
errors: [
|
|
13091
|
+
errors: [errorMessage],
|
|
12942
13092
|
warnings: [],
|
|
12943
13093
|
};
|
|
12944
13094
|
}
|
|
@@ -12947,12 +13097,61 @@ async function validateCommitMessage(message, options = {}) {
|
|
|
12947
13097
|
var commitlintValidator = /*#__PURE__*/Object.freeze({
|
|
12948
13098
|
__proto__: null,
|
|
12949
13099
|
CommitlintValidationError: CommitlintValidationError,
|
|
13100
|
+
checkCommitlintAvailability: checkCommitlintAvailability,
|
|
12950
13101
|
formatCommitlintRulesForPrompt: formatCommitlintRulesForPrompt,
|
|
12951
13102
|
getCommitlintRulesContext: getCommitlintRulesContext,
|
|
12952
13103
|
loadCommitlintConfig: loadCommitlintConfig,
|
|
12953
13104
|
validateCommitMessage: validateCommitMessage
|
|
12954
13105
|
});
|
|
12955
13106
|
|
|
13107
|
+
/**
|
|
13108
|
+
* Handle missing commitlint dependencies with user-friendly options
|
|
13109
|
+
*/
|
|
13110
|
+
async function handleMissingCommitlintDeps(options) {
|
|
13111
|
+
const { logger, interactive, missingPackages } = options;
|
|
13112
|
+
if (!interactive) {
|
|
13113
|
+
logger.log('\nCommitlint packages not found. Skipping commit message validation.', {
|
|
13114
|
+
color: 'yellow',
|
|
13115
|
+
});
|
|
13116
|
+
logger.log('Run `coco init` to set up commitlint for conventional commits.', {
|
|
13117
|
+
color: 'gray',
|
|
13118
|
+
});
|
|
13119
|
+
return { action: 'continue' };
|
|
13120
|
+
}
|
|
13121
|
+
logger.log('\nCommitlint configuration requires additional packages:', { color: 'yellow' });
|
|
13122
|
+
missingPackages.forEach((pkg) => {
|
|
13123
|
+
logger.log(` β’ ${pkg}`, { color: 'gray' });
|
|
13124
|
+
});
|
|
13125
|
+
logger.log('');
|
|
13126
|
+
const choice = await select({
|
|
13127
|
+
message: 'How would you like to proceed?',
|
|
13128
|
+
choices: [
|
|
13129
|
+
{
|
|
13130
|
+
name: 'Continue without commitlint validation',
|
|
13131
|
+
value: 'continue',
|
|
13132
|
+
description: 'Generate commit message without validation rules',
|
|
13133
|
+
},
|
|
13134
|
+
{
|
|
13135
|
+
name: 'Set up commitlint (run coco init)',
|
|
13136
|
+
value: 'setup',
|
|
13137
|
+
description: 'Exit and run the init command to install required packages',
|
|
13138
|
+
},
|
|
13139
|
+
{
|
|
13140
|
+
name: 'Abort',
|
|
13141
|
+
value: 'abort',
|
|
13142
|
+
description: 'Cancel the commit operation',
|
|
13143
|
+
},
|
|
13144
|
+
],
|
|
13145
|
+
default: 'continue',
|
|
13146
|
+
});
|
|
13147
|
+
return { action: choice };
|
|
13148
|
+
}
|
|
13149
|
+
|
|
13150
|
+
var handleMissingCommitlintDeps$1 = /*#__PURE__*/Object.freeze({
|
|
13151
|
+
__proto__: null,
|
|
13152
|
+
handleMissingCommitlintDeps: handleMissingCommitlintDeps
|
|
13153
|
+
});
|
|
13154
|
+
|
|
12956
13155
|
/**
|
|
12957
13156
|
* Handle commit message validation results with user interaction
|
|
12958
13157
|
*/
|
package/dist/index.js
CHANGED
|
@@ -39,7 +39,6 @@ var minimatch = require('minimatch');
|
|
|
39
39
|
var tiktoken = require('tiktoken');
|
|
40
40
|
var child_process = require('child_process');
|
|
41
41
|
var readline = require('readline');
|
|
42
|
-
var core$1 = require('@commitlint/core');
|
|
43
42
|
var url = require('url');
|
|
44
43
|
|
|
45
44
|
function _interopNamespaceDefault(e) {
|
|
@@ -70,7 +69,7 @@ var readline__namespace = /*#__PURE__*/_interopNamespaceDefault(readline);
|
|
|
70
69
|
/**
|
|
71
70
|
* Current build version from package.json
|
|
72
71
|
*/
|
|
73
|
-
const BUILD_VERSION = "0.22.
|
|
72
|
+
const BUILD_VERSION = "0.22.3";
|
|
74
73
|
|
|
75
74
|
const isInteractive = (config) => {
|
|
76
75
|
return config?.mode === 'interactive' || !!config?.interactive;
|
|
@@ -6242,7 +6241,7 @@ function getLlm(provider, model, config) {
|
|
|
6242
6241
|
});
|
|
6243
6242
|
case 'openai':
|
|
6244
6243
|
return new openai.ChatOpenAI({
|
|
6245
|
-
|
|
6244
|
+
apiKey: apiKey,
|
|
6246
6245
|
model,
|
|
6247
6246
|
temperature: config.service.temperature || 0.2,
|
|
6248
6247
|
});
|
|
@@ -11499,9 +11498,34 @@ ${schema.description}
|
|
|
11499
11498
|
// Load commitlint rules context if available
|
|
11500
11499
|
const hasCommitLintConfig = await hasCommitlintConfig();
|
|
11501
11500
|
let commitlint_rules_context = '';
|
|
11501
|
+
let shouldSkipCommitlintValidation = false;
|
|
11502
11502
|
if (USE_CONVENTIONAL_COMMITS || hasCommitLintConfig) {
|
|
11503
|
-
const { getCommitlintRulesContext } = await Promise.resolve().then(function () { return commitlintValidator; });
|
|
11504
|
-
|
|
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
|
+
}
|
|
11505
11529
|
}
|
|
11506
11530
|
// Get variables for the prompt
|
|
11507
11531
|
const variables = {
|
|
@@ -11548,13 +11572,33 @@ ${schema.description}
|
|
|
11548
11572
|
const ticketId = extractTicketIdFromBranchName(branchName);
|
|
11549
11573
|
const ticketFooter = argv.appendTicket && ticketId ? `\n\nPart of **${ticketId}**` : '';
|
|
11550
11574
|
const fullMessage = `${commitMsg.title}\n\n${commitMsg.body}${appendedText}${ticketFooter}`;
|
|
11551
|
-
// If commitlint validation is needed, validate the message
|
|
11552
|
-
if (USE_CONVENTIONAL_COMMITS || hasCommitLintConfig) {
|
|
11575
|
+
// If commitlint validation is needed and not skipped, validate the message
|
|
11576
|
+
if ((USE_CONVENTIONAL_COMMITS || hasCommitLintConfig) && !shouldSkipCommitlintValidation) {
|
|
11553
11577
|
const { validateCommitMessage, CommitlintValidationError } = await Promise.resolve().then(function () { return commitlintValidator; });
|
|
11554
11578
|
const validationResult = await validateCommitMessage(fullMessage);
|
|
11555
11579
|
logger.verbose(`Validation result: ${JSON.stringify(validationResult)}`, {
|
|
11556
11580
|
color: 'yellow',
|
|
11557
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
|
+
}
|
|
11558
11602
|
if (!validationResult.valid) {
|
|
11559
11603
|
retryCount++;
|
|
11560
11604
|
// Format validation errors for next attempt
|
|
@@ -11992,14 +12036,21 @@ const questions = {
|
|
|
11992
12036
|
},
|
|
11993
12037
|
],
|
|
11994
12038
|
}),
|
|
12039
|
+
setupCommitlint: async () => await prompts.confirm({
|
|
12040
|
+
message: 'set up commitlint for conventional commits support?',
|
|
12041
|
+
default: true,
|
|
12042
|
+
}),
|
|
11995
12043
|
};
|
|
11996
12044
|
|
|
11997
12045
|
const handler$2 = async (argv, logger) => {
|
|
11998
12046
|
const options = loadConfig(argv);
|
|
11999
12047
|
logger.log(LOGO);
|
|
12000
12048
|
let scope = options?.scope;
|
|
12049
|
+
let shouldSetupCommitlint = false;
|
|
12001
12050
|
if (!scope) {
|
|
12002
12051
|
scope = await questions.whatScope();
|
|
12052
|
+
// Ask about commitlint setup after scope selection
|
|
12053
|
+
shouldSetupCommitlint = await questions.setupCommitlint();
|
|
12003
12054
|
const llmProvider = await questions.selectLLMProvider();
|
|
12004
12055
|
const llmModel = await questions.selectLLMModel(llmProvider);
|
|
12005
12056
|
const service = getDefaultServiceConfigFromAlias(llmProvider, llmModel);
|
|
@@ -12116,6 +12167,10 @@ const handler$2 = async (argv, logger) => {
|
|
|
12116
12167
|
}
|
|
12117
12168
|
// After config is written, check for package installation
|
|
12118
12169
|
await checkAndHandlePackageInstallation({ global: scope === 'global', logger });
|
|
12170
|
+
// Install commitlint packages if user requested
|
|
12171
|
+
if (shouldSetupCommitlint) {
|
|
12172
|
+
await installCommitlintPackages(scope, logger);
|
|
12173
|
+
}
|
|
12119
12174
|
logger.log(`\ninit successful! π¦Ύπ€π`, { color: 'green' });
|
|
12120
12175
|
}
|
|
12121
12176
|
else {
|
|
@@ -12123,6 +12178,39 @@ const handler$2 = async (argv, logger) => {
|
|
|
12123
12178
|
}
|
|
12124
12179
|
}
|
|
12125
12180
|
};
|
|
12181
|
+
/**
|
|
12182
|
+
* Install commitlint packages based on scope (global or project)
|
|
12183
|
+
*/
|
|
12184
|
+
async function installCommitlintPackages(scope, logger) {
|
|
12185
|
+
const packages = ['@commitlint/config-conventional', '@commitlint/cli'];
|
|
12186
|
+
try {
|
|
12187
|
+
if (scope === 'global') {
|
|
12188
|
+
logger.startSpinner('Installing commitlint packages globally...', { color: 'blue' });
|
|
12189
|
+
for (const pkg of packages) {
|
|
12190
|
+
await installNpmPackage({ name: pkg, flags: ['-g'] });
|
|
12191
|
+
}
|
|
12192
|
+
logger.stopSpinner('Installed commitlint packages globally');
|
|
12193
|
+
}
|
|
12194
|
+
else {
|
|
12195
|
+
logger.startSpinner('Installing commitlint packages in project...', { color: 'blue' });
|
|
12196
|
+
for (const pkg of packages) {
|
|
12197
|
+
await installNpmPackage({ name: pkg, flags: ['--save-dev'] });
|
|
12198
|
+
}
|
|
12199
|
+
logger.stopSpinner('Installed commitlint packages in project');
|
|
12200
|
+
}
|
|
12201
|
+
}
|
|
12202
|
+
catch (error) {
|
|
12203
|
+
logger.stopSpinner('Failed to install commitlint packages');
|
|
12204
|
+
logger.log(`Error installing commitlint packages: ${error.message}`, { color: 'red' });
|
|
12205
|
+
logger.log('You can install them manually later:', { color: 'yellow' });
|
|
12206
|
+
if (scope === 'global') {
|
|
12207
|
+
logger.log('npm install -g @commitlint/config-conventional @commitlint/cli', { color: 'gray' });
|
|
12208
|
+
}
|
|
12209
|
+
else {
|
|
12210
|
+
logger.log('npm install --save-dev @commitlint/config-conventional @commitlint/cli', { color: 'gray' });
|
|
12211
|
+
}
|
|
12212
|
+
}
|
|
12213
|
+
}
|
|
12126
12214
|
|
|
12127
12215
|
var init = {
|
|
12128
12216
|
command: command$2,
|
|
@@ -12809,10 +12897,36 @@ class CommitlintValidationError extends Error {
|
|
|
12809
12897
|
this.commitMessage = commitMessage;
|
|
12810
12898
|
}
|
|
12811
12899
|
}
|
|
12900
|
+
/**
|
|
12901
|
+
* Check if commitlint config packages are available
|
|
12902
|
+
* We only check for config-conventional since @commitlint/core is bundled with git-coco
|
|
12903
|
+
*/
|
|
12904
|
+
function checkCommitlintAvailability() {
|
|
12905
|
+
const requiredPackages = ['@commitlint/config-conventional'];
|
|
12906
|
+
const missingPackages = [];
|
|
12907
|
+
for (const pkg of requiredPackages) {
|
|
12908
|
+
try {
|
|
12909
|
+
// Try to resolve the package from the current working directory
|
|
12910
|
+
require.resolve(pkg, { paths: [process.cwd(), ...module.paths] });
|
|
12911
|
+
}
|
|
12912
|
+
catch (error) {
|
|
12913
|
+
missingPackages.push(pkg);
|
|
12914
|
+
}
|
|
12915
|
+
}
|
|
12916
|
+
// If config-conventional is missing, also suggest installing CLI
|
|
12917
|
+
if (missingPackages.length > 0) {
|
|
12918
|
+
missingPackages.push('@commitlint/cli');
|
|
12919
|
+
}
|
|
12920
|
+
return {
|
|
12921
|
+
available: missingPackages.length === 0,
|
|
12922
|
+
missingPackages,
|
|
12923
|
+
};
|
|
12924
|
+
}
|
|
12812
12925
|
/**
|
|
12813
12926
|
* Load commitlint configuration
|
|
12814
12927
|
*/
|
|
12815
12928
|
async function loadCommitlintConfig() {
|
|
12929
|
+
const { load } = await import('@commitlint/core');
|
|
12816
12930
|
const projectRoot = findProjectRoot(process.cwd());
|
|
12817
12931
|
const cwd = projectRoot || process.cwd();
|
|
12818
12932
|
// @commitlint/load has issues with ESM configs (e.g. commitlint.config.js with `export default`).
|
|
@@ -12827,7 +12941,7 @@ async function loadCommitlintConfig() {
|
|
|
12827
12941
|
(Object.keys(module.default.rules || {}).length > 0 ||
|
|
12828
12942
|
(module.default.extends && module.default.extends.length > 0))) {
|
|
12829
12943
|
// We found a config, now let commitlint process it (for extends etc)
|
|
12830
|
-
return await
|
|
12944
|
+
return await load(module.default, { cwd });
|
|
12831
12945
|
}
|
|
12832
12946
|
}
|
|
12833
12947
|
catch (error) {
|
|
@@ -12838,7 +12952,7 @@ async function loadCommitlintConfig() {
|
|
|
12838
12952
|
}
|
|
12839
12953
|
try {
|
|
12840
12954
|
// Let @commitlint/load try to find the config. This works for CJS, JSON, and YAML.
|
|
12841
|
-
const config = await
|
|
12955
|
+
const config = await load({}, { cwd });
|
|
12842
12956
|
// Check if a real config was loaded.
|
|
12843
12957
|
if (config.extends.length > 0 || Object.keys(config.rules).length > 0) {
|
|
12844
12958
|
return config;
|
|
@@ -12847,10 +12961,35 @@ async function loadCommitlintConfig() {
|
|
|
12847
12961
|
catch (error) {
|
|
12848
12962
|
// Could be an error parsing, or just not found. Fall through to default.
|
|
12849
12963
|
}
|
|
12850
|
-
//
|
|
12851
|
-
|
|
12852
|
-
|
|
12853
|
-
|
|
12964
|
+
// Try to fallback to conventional config, but handle missing dependencies gracefully
|
|
12965
|
+
try {
|
|
12966
|
+
return await load({
|
|
12967
|
+
extends: ['@commitlint/config-conventional'],
|
|
12968
|
+
});
|
|
12969
|
+
}
|
|
12970
|
+
catch (error) {
|
|
12971
|
+
// If @commitlint/config-conventional is not available, return a basic conventional config
|
|
12972
|
+
if (error instanceof Error && error.message.includes('Cannot find module "@commitlint/config-conventional"')) {
|
|
12973
|
+
return await load({
|
|
12974
|
+
rules: {
|
|
12975
|
+
'header-max-length': [2, 'always', 72],
|
|
12976
|
+
'header-min-length': [2, 'always', 8],
|
|
12977
|
+
'subject-empty': [2, 'never'],
|
|
12978
|
+
'subject-full-stop': [2, 'never', '.'],
|
|
12979
|
+
'subject-case': [2, 'always', ['sentence-case', 'start-case', 'pascal-case', 'upper-case', 'lower-case']],
|
|
12980
|
+
'type-empty': [2, 'never'],
|
|
12981
|
+
'type-case': [2, 'always', 'lower-case'],
|
|
12982
|
+
'type-enum': [2, 'always', [
|
|
12983
|
+
'build', 'chore', 'ci', 'docs', 'feat', 'fix',
|
|
12984
|
+
'perf', 'refactor', 'revert', 'style', 'test'
|
|
12985
|
+
]],
|
|
12986
|
+
'body-max-line-length': [2, 'always', 100],
|
|
12987
|
+
'scope-case': [2, 'always', 'lower-case'],
|
|
12988
|
+
},
|
|
12989
|
+
});
|
|
12990
|
+
}
|
|
12991
|
+
throw error;
|
|
12992
|
+
}
|
|
12854
12993
|
}
|
|
12855
12994
|
/**
|
|
12856
12995
|
* Format commitlint rules into a human-readable string for AI prompts
|
|
@@ -12949,8 +13088,9 @@ async function getCommitlintRulesContext() {
|
|
|
12949
13088
|
*/
|
|
12950
13089
|
async function validateCommitMessage(message, options = {}) {
|
|
12951
13090
|
try {
|
|
13091
|
+
const { lint } = await import('@commitlint/core');
|
|
12952
13092
|
const config = await loadCommitlintConfig();
|
|
12953
|
-
const result = await
|
|
13093
|
+
const result = await lint(message, config.rules, options);
|
|
12954
13094
|
return {
|
|
12955
13095
|
valid: result.valid,
|
|
12956
13096
|
errors: result.errors.map((error) => error.message),
|
|
@@ -12958,9 +13098,19 @@ async function validateCommitMessage(message, options = {}) {
|
|
|
12958
13098
|
};
|
|
12959
13099
|
}
|
|
12960
13100
|
catch (error) {
|
|
13101
|
+
const errorMessage = error.message;
|
|
13102
|
+
// Check if this is a missing dependency error
|
|
13103
|
+
if (errorMessage.includes('Cannot find module "@commitlint/config-conventional"')) {
|
|
13104
|
+
return {
|
|
13105
|
+
valid: false,
|
|
13106
|
+
errors: ['Commitlint configuration requires @commitlint/config-conventional to be installed'],
|
|
13107
|
+
warnings: [],
|
|
13108
|
+
missingDependencies: ['@commitlint/config-conventional', '@commitlint/cli'],
|
|
13109
|
+
};
|
|
13110
|
+
}
|
|
12961
13111
|
return {
|
|
12962
13112
|
valid: false,
|
|
12963
|
-
errors: [
|
|
13113
|
+
errors: [errorMessage],
|
|
12964
13114
|
warnings: [],
|
|
12965
13115
|
};
|
|
12966
13116
|
}
|
|
@@ -12969,12 +13119,61 @@ async function validateCommitMessage(message, options = {}) {
|
|
|
12969
13119
|
var commitlintValidator = /*#__PURE__*/Object.freeze({
|
|
12970
13120
|
__proto__: null,
|
|
12971
13121
|
CommitlintValidationError: CommitlintValidationError,
|
|
13122
|
+
checkCommitlintAvailability: checkCommitlintAvailability,
|
|
12972
13123
|
formatCommitlintRulesForPrompt: formatCommitlintRulesForPrompt,
|
|
12973
13124
|
getCommitlintRulesContext: getCommitlintRulesContext,
|
|
12974
13125
|
loadCommitlintConfig: loadCommitlintConfig,
|
|
12975
13126
|
validateCommitMessage: validateCommitMessage
|
|
12976
13127
|
});
|
|
12977
13128
|
|
|
13129
|
+
/**
|
|
13130
|
+
* Handle missing commitlint dependencies with user-friendly options
|
|
13131
|
+
*/
|
|
13132
|
+
async function handleMissingCommitlintDeps(options) {
|
|
13133
|
+
const { logger, interactive, missingPackages } = options;
|
|
13134
|
+
if (!interactive) {
|
|
13135
|
+
logger.log('\nCommitlint packages not found. Skipping commit message validation.', {
|
|
13136
|
+
color: 'yellow',
|
|
13137
|
+
});
|
|
13138
|
+
logger.log('Run `coco init` to set up commitlint for conventional commits.', {
|
|
13139
|
+
color: 'gray',
|
|
13140
|
+
});
|
|
13141
|
+
return { action: 'continue' };
|
|
13142
|
+
}
|
|
13143
|
+
logger.log('\nCommitlint configuration requires additional packages:', { color: 'yellow' });
|
|
13144
|
+
missingPackages.forEach((pkg) => {
|
|
13145
|
+
logger.log(` β’ ${pkg}`, { color: 'gray' });
|
|
13146
|
+
});
|
|
13147
|
+
logger.log('');
|
|
13148
|
+
const choice = await prompts.select({
|
|
13149
|
+
message: 'How would you like to proceed?',
|
|
13150
|
+
choices: [
|
|
13151
|
+
{
|
|
13152
|
+
name: 'Continue without commitlint validation',
|
|
13153
|
+
value: 'continue',
|
|
13154
|
+
description: 'Generate commit message without validation rules',
|
|
13155
|
+
},
|
|
13156
|
+
{
|
|
13157
|
+
name: 'Set up commitlint (run coco init)',
|
|
13158
|
+
value: 'setup',
|
|
13159
|
+
description: 'Exit and run the init command to install required packages',
|
|
13160
|
+
},
|
|
13161
|
+
{
|
|
13162
|
+
name: 'Abort',
|
|
13163
|
+
value: 'abort',
|
|
13164
|
+
description: 'Cancel the commit operation',
|
|
13165
|
+
},
|
|
13166
|
+
],
|
|
13167
|
+
default: 'continue',
|
|
13168
|
+
});
|
|
13169
|
+
return { action: choice };
|
|
13170
|
+
}
|
|
13171
|
+
|
|
13172
|
+
var handleMissingCommitlintDeps$1 = /*#__PURE__*/Object.freeze({
|
|
13173
|
+
__proto__: null,
|
|
13174
|
+
handleMissingCommitlintDeps: handleMissingCommitlintDeps
|
|
13175
|
+
});
|
|
13176
|
+
|
|
12978
13177
|
/**
|
|
12979
13178
|
* Handle commit message validation results with user interaction
|
|
12980
13179
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-coco",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.3",
|
|
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",
|