git-coco 0.20.1 → 0.21.0
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/README.md +15 -1
- package/dist/index.esm.mjs +473 -128
- package/dist/index.js +472 -127
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -18,6 +18,8 @@ var anthropic = require('@langchain/anthropic');
|
|
|
18
18
|
var ollama = require('@langchain/ollama');
|
|
19
19
|
var openai = require('@langchain/openai');
|
|
20
20
|
var output_parsers = require('@langchain/core/output_parsers');
|
|
21
|
+
require('@langchain/core/utils/json_schema');
|
|
22
|
+
require('@langchain/core/utils/types');
|
|
21
23
|
var base = require('@langchain/core/language_models/base');
|
|
22
24
|
var runnables = require('@langchain/core/runnables');
|
|
23
25
|
var outputs = require('@langchain/core/outputs');
|
|
@@ -39,6 +41,8 @@ var child_process = require('child_process');
|
|
|
39
41
|
var readline = require('node:readline');
|
|
40
42
|
var require$$0 = require('stream');
|
|
41
43
|
var readline$1 = require('readline');
|
|
44
|
+
var core$1 = require('@commitlint/core');
|
|
45
|
+
var url = require('url');
|
|
42
46
|
|
|
43
47
|
function _interopNamespaceDefault(e) {
|
|
44
48
|
var n = Object.create(null);
|
|
@@ -68,7 +72,7 @@ var readline__namespace = /*#__PURE__*/_interopNamespaceDefault(readline$1);
|
|
|
68
72
|
/**
|
|
69
73
|
* Current build version from package.json
|
|
70
74
|
*/
|
|
71
|
-
const BUILD_VERSION = "0.
|
|
75
|
+
const BUILD_VERSION = "0.21.0";
|
|
72
76
|
|
|
73
77
|
const isInteractive = (config) => {
|
|
74
78
|
return config?.mode === 'interactive' || !!config?.interactive;
|
|
@@ -1200,6 +1204,8 @@ const schema$1 = {
|
|
|
1200
1204
|
"text-davinci-edit-001",
|
|
1201
1205
|
"code-davinci-edit-001",
|
|
1202
1206
|
"text-embedding-ada-002",
|
|
1207
|
+
"text-embedding-3-small",
|
|
1208
|
+
"text-embedding-3-large",
|
|
1203
1209
|
"text-similarity-davinci-001",
|
|
1204
1210
|
"text-similarity-curie-001",
|
|
1205
1211
|
"text-similarity-babbage-001",
|
|
@@ -1235,10 +1241,54 @@ const schema$1 = {
|
|
|
1235
1241
|
"gpt-4-vision-preview",
|
|
1236
1242
|
"gpt-4o",
|
|
1237
1243
|
"gpt-4o-2024-05-13",
|
|
1244
|
+
"gpt-4o-2024-08-06",
|
|
1245
|
+
"gpt-4o-2024-11-20",
|
|
1246
|
+
"gpt-4o-mini-2024-07-18",
|
|
1238
1247
|
"gpt-4o-mini",
|
|
1248
|
+
"gpt-4o-search-preview",
|
|
1249
|
+
"gpt-4o-search-preview-2025-03-11",
|
|
1250
|
+
"gpt-4o-mini-search-preview",
|
|
1251
|
+
"gpt-4o-mini-search-preview-2025-03-11",
|
|
1252
|
+
"gpt-4o-audio-preview",
|
|
1253
|
+
"gpt-4o-audio-preview-2024-12-17",
|
|
1254
|
+
"gpt-4o-audio-preview-2024-10-01",
|
|
1255
|
+
"gpt-4o-mini-audio-preview",
|
|
1256
|
+
"gpt-4o-mini-audio-preview-2024-12-17",
|
|
1257
|
+
"o1",
|
|
1258
|
+
"o1-2024-12-17",
|
|
1259
|
+
"o1-mini",
|
|
1260
|
+
"o1-mini-2024-09-12",
|
|
1261
|
+
"o1-preview",
|
|
1262
|
+
"o1-preview-2024-09-12",
|
|
1263
|
+
"o1-pro",
|
|
1264
|
+
"o1-pro-2025-03-19",
|
|
1265
|
+
"o3",
|
|
1266
|
+
"o3-2025-04-16",
|
|
1267
|
+
"o3-mini",
|
|
1268
|
+
"o3-mini-2025-01-31",
|
|
1269
|
+
"o4-mini",
|
|
1270
|
+
"o4-mini-2025-04-16",
|
|
1271
|
+
"chatgpt-4o-latest",
|
|
1272
|
+
"gpt-4o-realtime",
|
|
1273
|
+
"gpt-4o-realtime-preview-2024-10-01",
|
|
1274
|
+
"gpt-4o-realtime-preview-2024-12-17",
|
|
1275
|
+
"gpt-4o-mini-realtime-preview",
|
|
1276
|
+
"gpt-4o-mini-realtime-preview-2024-12-17",
|
|
1239
1277
|
"gpt-4.1",
|
|
1278
|
+
"gpt-4.1-2025-04-14",
|
|
1240
1279
|
"gpt-4.1-mini",
|
|
1241
|
-
"gpt-4.1-
|
|
1280
|
+
"gpt-4.1-mini-2025-04-14",
|
|
1281
|
+
"gpt-4.1-nano",
|
|
1282
|
+
"gpt-4.1-nano-2025-04-14",
|
|
1283
|
+
"gpt-4.5-preview",
|
|
1284
|
+
"gpt-4.5-preview-2025-02-27",
|
|
1285
|
+
"gpt-5",
|
|
1286
|
+
"gpt-5-2025-08-07",
|
|
1287
|
+
"gpt-5-nano",
|
|
1288
|
+
"gpt-5-nano-2025-08-07",
|
|
1289
|
+
"gpt-5-mini",
|
|
1290
|
+
"gpt-5-mini-2025-08-07",
|
|
1291
|
+
"gpt-5-chat-latest"
|
|
1242
1292
|
]
|
|
1243
1293
|
},
|
|
1244
1294
|
"OllamaModel": {
|
|
@@ -1500,6 +1550,13 @@ const schema$1 = {
|
|
|
1500
1550
|
"ChatModel": {
|
|
1501
1551
|
"type": "string",
|
|
1502
1552
|
"enum": [
|
|
1553
|
+
"gpt-5",
|
|
1554
|
+
"gpt-5-mini",
|
|
1555
|
+
"gpt-5-nano",
|
|
1556
|
+
"gpt-5-2025-08-07",
|
|
1557
|
+
"gpt-5-mini-2025-08-07",
|
|
1558
|
+
"gpt-5-nano-2025-08-07",
|
|
1559
|
+
"gpt-5-chat-latest",
|
|
1503
1560
|
"gpt-4.1",
|
|
1504
1561
|
"gpt-4.1-mini",
|
|
1505
1562
|
"gpt-4.1-nano",
|
|
@@ -6223,8 +6280,6 @@ function getPrompt({ template, variables, fallback }) {
|
|
|
6223
6280
|
throw new LangChainExecutionError('getPrompt: Unexpected execution path - neither template nor fallback available', { template, fallback, variables });
|
|
6224
6281
|
}
|
|
6225
6282
|
|
|
6226
|
-
new Set("ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789");
|
|
6227
|
-
|
|
6228
6283
|
/**
|
|
6229
6284
|
* Base interface that all chains must implement.
|
|
6230
6285
|
*/
|
|
@@ -6327,7 +6382,7 @@ class BaseChain extends base.BaseLangChain {
|
|
|
6327
6382
|
async run(
|
|
6328
6383
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6329
6384
|
input, config) {
|
|
6330
|
-
const inputKeys = this.inputKeys.filter((k) => !this.memory?.memoryKeys.includes(k)
|
|
6385
|
+
const inputKeys = this.inputKeys.filter((k) => !this.memory?.memoryKeys.includes(k));
|
|
6331
6386
|
const isKeylessInput = inputKeys.length <= 1;
|
|
6332
6387
|
if (!isKeylessInput) {
|
|
6333
6388
|
throw new Error(`Chain ${this._chainType()} expects multiple inputs, cannot use 'run' `);
|
|
@@ -6498,7 +6553,7 @@ function _getLanguageModel(llmLike) {
|
|
|
6498
6553
|
* import { ChatOpenAI } from "@langchain/openai";
|
|
6499
6554
|
*
|
|
6500
6555
|
* const prompt = ChatPromptTemplate.fromTemplate("Tell me a {adjective} joke");
|
|
6501
|
-
* const llm = new ChatOpenAI();
|
|
6556
|
+
* const llm = new ChatOpenAI({ model: "gpt-4o-mini" });
|
|
6502
6557
|
* const chain = prompt.pipe(llm);
|
|
6503
6558
|
*
|
|
6504
6559
|
* const response = await chain.invoke({ adjective: "funny" });
|
|
@@ -7114,6 +7169,8 @@ Please follow the guidelines below when writing your commit message:
|
|
|
7114
7169
|
|
|
7115
7170
|
{{branch_name_context}}
|
|
7116
7171
|
|
|
7172
|
+
{{commitlint_rules_context}}
|
|
7173
|
+
|
|
7117
7174
|
{{format_instructions}}
|
|
7118
7175
|
|
|
7119
7176
|
{{commit_history}}
|
|
@@ -7121,7 +7178,7 @@ Please follow the guidelines below when writing your commit message:
|
|
|
7121
7178
|
{{additional_context}}
|
|
7122
7179
|
`;
|
|
7123
7180
|
// Define the variables that will be passed to the prompt template
|
|
7124
|
-
const inputVariables$3 = ['summary', 'format_instructions', 'additional_context', 'commit_history', 'branch_name_context'];
|
|
7181
|
+
const inputVariables$3 = ['summary', 'format_instructions', 'additional_context', 'commit_history', 'branch_name_context', 'commitlint_rules_context'];
|
|
7125
7182
|
const COMMIT_PROMPT = new prompts$1.PromptTemplate({
|
|
7126
7183
|
template: template$4,
|
|
7127
7184
|
inputVariables: inputVariables$3,
|
|
@@ -7163,23 +7220,20 @@ Based on the following diff summary, generate a conventional commit message that
|
|
|
7163
7220
|
|
|
7164
7221
|
{{branch_name_context}}
|
|
7165
7222
|
|
|
7223
|
+
{{commitlint_rules_context}}
|
|
7224
|
+
|
|
7166
7225
|
{{format_instructions}}
|
|
7167
7226
|
|
|
7168
7227
|
{{commit_history}}
|
|
7169
7228
|
|
|
7170
|
-
{{additional_context}}
|
|
7171
|
-
|
|
7172
|
-
Remember:
|
|
7173
|
-
- Be concise and precise
|
|
7174
|
-
- Focus on WHAT and WHY, not HOW
|
|
7175
|
-
- Use imperative mood in both title and body
|
|
7176
|
-
- Ensure the title alone provides enough context to understand the change`;
|
|
7229
|
+
{{additional_context}}`;
|
|
7177
7230
|
const conventionalInputVariables = [
|
|
7178
7231
|
'summary',
|
|
7179
7232
|
'additional_context',
|
|
7180
7233
|
'commit_history',
|
|
7181
7234
|
'format_instructions',
|
|
7182
7235
|
'branch_name_context',
|
|
7236
|
+
'commitlint_rules_context',
|
|
7183
7237
|
];
|
|
7184
7238
|
const CONVENTIONAL_COMMIT_PROMPT = new prompts$1.PromptTemplate({
|
|
7185
7239
|
template: CONVENTIONAL_TEMPLATE,
|
|
@@ -7388,7 +7442,9 @@ async function generateAndReviewLoop({ label, factory, parser, noResult, agent,
|
|
|
7388
7442
|
continue;
|
|
7389
7443
|
}
|
|
7390
7444
|
// Only edit the result in interactive mode if approved
|
|
7391
|
-
|
|
7445
|
+
// Use custom edit function if provided, otherwise use default editResult
|
|
7446
|
+
const editFunction = options.review?.customEditFunction || editResult;
|
|
7447
|
+
result = await editFunction(result, options);
|
|
7392
7448
|
}
|
|
7393
7449
|
else {
|
|
7394
7450
|
// In non-interactive mode, we return the result as is to be output to stdout by the caller.
|
|
@@ -7572,17 +7628,17 @@ var changelog = {
|
|
|
7572
7628
|
const conventionalTypeRegex = /^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?:/;
|
|
7573
7629
|
// Regular commit message schema with basic validation
|
|
7574
7630
|
const CommitMessageResponseSchema = objectType({
|
|
7575
|
-
title: stringType(),
|
|
7576
|
-
body: stringType(),
|
|
7631
|
+
title: stringType().describe("Title of the commit message"),
|
|
7632
|
+
body: stringType().describe("Body of the commit message"),
|
|
7577
7633
|
});
|
|
7578
7634
|
// Conventional commit message schema with strict formatting rules
|
|
7579
7635
|
const ConventionalCommitMessageResponseSchema = objectType({
|
|
7580
7636
|
title: stringType()
|
|
7581
7637
|
.max(50, "Title must be 50 characters or less")
|
|
7582
|
-
.refine((title) => conventionalTypeRegex.test(title), "Title must follow Conventional Commits format (e.g., 'feat: add new feature' or 'fix(scope): fix bug')"),
|
|
7583
|
-
body: stringType()
|
|
7638
|
+
.refine((title) => conventionalTypeRegex.test(title), "Title must follow Conventional Commits format (e.g., 'feat: add new feature' or 'fix(scope): fix bug')").describe("Title of the commit message"),
|
|
7639
|
+
body: stringType().describe("Body of the commit message")
|
|
7584
7640
|
// .max(280, "Body must be 280 characters or less"),
|
|
7585
|
-
});
|
|
7641
|
+
}).describe("Conventional commit message schema with strict formatting rules");
|
|
7586
7642
|
const command$3 = 'commit';
|
|
7587
7643
|
/**
|
|
7588
7644
|
* Command line options via yargs
|
|
@@ -8241,12 +8297,12 @@ function formatSet(input) {
|
|
|
8241
8297
|
* const overallChain = new SequentialChain({
|
|
8242
8298
|
* chains: [
|
|
8243
8299
|
* new LLMChain({
|
|
8244
|
-
* llm: new ChatOpenAI({ temperature: 0 }),
|
|
8300
|
+
* llm: new ChatOpenAI({ model: "gpt-4o-mini", temperature: 0 }),
|
|
8245
8301
|
* prompt: promptTemplate,
|
|
8246
8302
|
* outputKey: "synopsis",
|
|
8247
8303
|
* }),
|
|
8248
8304
|
* new LLMChain({
|
|
8249
|
-
* llm: new OpenAI({ temperature: 0 }),
|
|
8305
|
+
* llm: new OpenAI({ model: "gpt-4o-mini", temperature: 0 }),
|
|
8250
8306
|
* prompt: reviewPromptTemplate,
|
|
8251
8307
|
* outputKey: "review",
|
|
8252
8308
|
* }),
|
|
@@ -8483,7 +8539,8 @@ class SimpleSequentialChain extends BaseChain {
|
|
|
8483
8539
|
/** @ignore */
|
|
8484
8540
|
_validateChains() {
|
|
8485
8541
|
for (const chain of this.chains) {
|
|
8486
|
-
if (chain.inputKeys.filter((k) => !chain.memory?.memoryKeys.includes(k)
|
|
8542
|
+
if (chain.inputKeys.filter((k) => !chain.memory?.memoryKeys.includes(k))
|
|
8543
|
+
.length !== 1) {
|
|
8487
8544
|
throw new Error(`Chains used in SimpleSequentialChain should all have one input, got ${chain.inputKeys.length} for ${chain._chainType()}.`);
|
|
8488
8545
|
}
|
|
8489
8546
|
if (chain.outputKeys.length !== 1) {
|
|
@@ -11037,6 +11094,75 @@ const getTokenCounter = async (modelName) => {
|
|
|
11037
11094
|
});
|
|
11038
11095
|
};
|
|
11039
11096
|
|
|
11097
|
+
const COMMITLINT_CONFIG_FILES = [
|
|
11098
|
+
'.commitlintrc',
|
|
11099
|
+
'.commitlintrc.json',
|
|
11100
|
+
'.commitlintrc.yaml',
|
|
11101
|
+
'.commitlintrc.yml',
|
|
11102
|
+
'.commitlintrc.js',
|
|
11103
|
+
'.commitlintrc.cjs',
|
|
11104
|
+
'commitlint.config.js',
|
|
11105
|
+
'commitlint.config.cjs',
|
|
11106
|
+
];
|
|
11107
|
+
|
|
11108
|
+
/**
|
|
11109
|
+
* Finds the project root directory starting from the given current directory.
|
|
11110
|
+
* It checks if the `.git` directory or `package.json` file exists in the current directory or any of its parent directories.
|
|
11111
|
+
* If found, it returns the path to the project root directory.
|
|
11112
|
+
* If not found, it throws an error.
|
|
11113
|
+
*
|
|
11114
|
+
* @param currentDir - The current directory to start searching from.
|
|
11115
|
+
* @returns The path to the project root directory.
|
|
11116
|
+
* @throws Error if the project root directory cannot be found.
|
|
11117
|
+
*/
|
|
11118
|
+
function findProjectRoot(currentDir) {
|
|
11119
|
+
const root = path.parse(currentDir).root;
|
|
11120
|
+
while (currentDir !== root) {
|
|
11121
|
+
if (fs.existsSync(path.join(currentDir, '.git')) ||
|
|
11122
|
+
fs.existsSync(path.join(currentDir, 'package.json'))) {
|
|
11123
|
+
return currentDir;
|
|
11124
|
+
}
|
|
11125
|
+
currentDir = path.dirname(currentDir);
|
|
11126
|
+
}
|
|
11127
|
+
throw new Error('Unable to find project root. Are you in the right directory?');
|
|
11128
|
+
}
|
|
11129
|
+
|
|
11130
|
+
/**
|
|
11131
|
+
* Check if a commitlint configuration exists in the project root.
|
|
11132
|
+
*/
|
|
11133
|
+
async function hasCommitlintConfig() {
|
|
11134
|
+
const projectRoot = findProjectRoot(process.cwd());
|
|
11135
|
+
if (!projectRoot) {
|
|
11136
|
+
return false;
|
|
11137
|
+
}
|
|
11138
|
+
// Check for dedicated commitlint config files
|
|
11139
|
+
for (const file of COMMITLINT_CONFIG_FILES) {
|
|
11140
|
+
if (fs.existsSync(path.join(projectRoot, file))) {
|
|
11141
|
+
return true;
|
|
11142
|
+
}
|
|
11143
|
+
}
|
|
11144
|
+
// Check for commitlint config in package.json
|
|
11145
|
+
const pkgPath = path.join(projectRoot, 'package.json');
|
|
11146
|
+
if (fs.existsSync(pkgPath)) {
|
|
11147
|
+
try {
|
|
11148
|
+
const pkgContent = fs.readFileSync(pkgPath, 'utf8');
|
|
11149
|
+
const pkg = JSON.parse(pkgContent);
|
|
11150
|
+
if (pkg.commitlint) {
|
|
11151
|
+
return true;
|
|
11152
|
+
}
|
|
11153
|
+
}
|
|
11154
|
+
catch (error) {
|
|
11155
|
+
// Ignore errors reading or parsing package.json
|
|
11156
|
+
}
|
|
11157
|
+
}
|
|
11158
|
+
return false;
|
|
11159
|
+
}
|
|
11160
|
+
|
|
11161
|
+
var hasCommitlintConfig$1 = /*#__PURE__*/Object.freeze({
|
|
11162
|
+
__proto__: null,
|
|
11163
|
+
hasCommitlintConfig: hasCommitlintConfig
|
|
11164
|
+
});
|
|
11165
|
+
|
|
11040
11166
|
async function noResult$2({ git, logger }) {
|
|
11041
11167
|
const { staged, unstaged, untracked } = await getChanges({ git });
|
|
11042
11168
|
const hasStaged = staged && staged.length > 0;
|
|
@@ -11091,12 +11217,18 @@ const handler$3 = async (argv, logger) => {
|
|
|
11091
11217
|
color: 'yellow',
|
|
11092
11218
|
});
|
|
11093
11219
|
}
|
|
11220
|
+
logger.verbose(`→ ${provider} (${model})`, {
|
|
11221
|
+
color: 'green',
|
|
11222
|
+
});
|
|
11223
|
+
const USE_CONVENTIONAL_COMMITS = config.conventionalCommits || argv.conventional;
|
|
11094
11224
|
async function factory() {
|
|
11095
11225
|
if (config.noDiff) {
|
|
11096
11226
|
const status = await git.status();
|
|
11097
|
-
return status.files.map(file => ({
|
|
11227
|
+
return status.files.map((file) => ({
|
|
11098
11228
|
filePath: file.path,
|
|
11099
|
-
status: (file.index === 'A' || file.index === '?'
|
|
11229
|
+
status: (file.index === 'A' || file.index === '?'
|
|
11230
|
+
? 'added'
|
|
11231
|
+
: 'modified'),
|
|
11100
11232
|
summary: file.path, // Simplified summary for noDiff
|
|
11101
11233
|
}));
|
|
11102
11234
|
}
|
|
@@ -11118,12 +11250,13 @@ const handler$3 = async (argv, logger) => {
|
|
|
11118
11250
|
options: { tokenizer, git, llm, logger },
|
|
11119
11251
|
});
|
|
11120
11252
|
}
|
|
11253
|
+
logger.log(`Generating commit message...${JSON.stringify(config.prompt)}`, { color: 'blue' });
|
|
11121
11254
|
const commitMsg = await generateAndReviewLoop({
|
|
11122
11255
|
label: 'commit message',
|
|
11123
11256
|
options: {
|
|
11124
11257
|
...config,
|
|
11125
11258
|
prompt: config.prompt ||
|
|
11126
|
-
(
|
|
11259
|
+
(USE_CONVENTIONAL_COMMITS
|
|
11127
11260
|
? CONVENTIONAL_COMMIT_PROMPT.template
|
|
11128
11261
|
: COMMIT_PROMPT.template),
|
|
11129
11262
|
logger,
|
|
@@ -11136,21 +11269,23 @@ const handler$3 = async (argv, logger) => {
|
|
|
11136
11269
|
retryMessageOnly: 'Restart the function execution from generating the commit message',
|
|
11137
11270
|
retryFull: 'Restart the function execution from the beginning, regenerating both the diff summary and commit message',
|
|
11138
11271
|
},
|
|
11272
|
+
customEditFunction: async (message, options) => {
|
|
11273
|
+
const { editCommitMessage } = await Promise.resolve().then(function () { return editCommitMessage$1; });
|
|
11274
|
+
return editCommitMessage(message, options);
|
|
11275
|
+
},
|
|
11139
11276
|
},
|
|
11140
11277
|
},
|
|
11141
11278
|
factory,
|
|
11142
11279
|
parser,
|
|
11143
11280
|
agent: async (context, options) => {
|
|
11144
|
-
// Check if conventional commits are enabled via config or CLI flag
|
|
11145
|
-
const useConventional = config.conventionalCommits || argv.conventional;
|
|
11146
11281
|
// Select the appropriate schema based on whether conventional commits are enabled
|
|
11147
|
-
const schema =
|
|
11282
|
+
const schema = USE_CONVENTIONAL_COMMITS
|
|
11148
11283
|
? ConventionalCommitMessageResponseSchema
|
|
11149
11284
|
: CommitMessageResponseSchema;
|
|
11150
11285
|
const formatInstructions = `You must always return valid JSON fenced by a markdown code block. Do not return any additional text. The JSON object you return should match the following schema:
|
|
11151
|
-
{
|
|
11286
|
+
${schema.description}`;
|
|
11152
11287
|
// Use conventional commit prompt if enabled
|
|
11153
|
-
const promptTemplate =
|
|
11288
|
+
const promptTemplate = USE_CONVENTIONAL_COMMITS ? CONVENTIONAL_COMMIT_PROMPT : COMMIT_PROMPT;
|
|
11154
11289
|
const prompt = getPrompt({
|
|
11155
11290
|
template: options.prompt,
|
|
11156
11291
|
variables: promptTemplate.inputVariables,
|
|
@@ -11180,6 +11315,13 @@ const handler$3 = async (argv, logger) => {
|
|
|
11180
11315
|
: config.includeBranchName !== false; // Default to true if not explicitly set to false
|
|
11181
11316
|
// Create branch name context string based on the configuration
|
|
11182
11317
|
const branchNameContext = includeBranchName ? `Current git branch name: ${branchName}` : '';
|
|
11318
|
+
// Load commitlint rules context if available
|
|
11319
|
+
const hasCommitLintConfig = await hasCommitlintConfig();
|
|
11320
|
+
let commitlint_rules_context = '';
|
|
11321
|
+
if (USE_CONVENTIONAL_COMMITS || hasCommitLintConfig) {
|
|
11322
|
+
const { getCommitlintRulesContext } = await Promise.resolve().then(function () { return commitlintValidator; });
|
|
11323
|
+
commitlint_rules_context = await getCommitlintRulesContext();
|
|
11324
|
+
}
|
|
11183
11325
|
// Get variables for the prompt
|
|
11184
11326
|
const variables = {
|
|
11185
11327
|
summary: context,
|
|
@@ -11187,59 +11329,106 @@ const handler$3 = async (argv, logger) => {
|
|
|
11187
11329
|
additional_context: additional_context,
|
|
11188
11330
|
commit_history: commit_history,
|
|
11189
11331
|
branch_name_context: branchNameContext,
|
|
11332
|
+
commitlint_rules_context: commitlint_rules_context,
|
|
11190
11333
|
};
|
|
11191
11334
|
const maxAttempts = config.service.provider === 'ollama' && 'maxParsingAttempts' in config.service
|
|
11192
11335
|
? config.service.maxParsingAttempts || 3
|
|
11193
11336
|
: 3;
|
|
11194
|
-
|
|
11195
|
-
|
|
11196
|
-
|
|
11197
|
-
|
|
11198
|
-
|
|
11337
|
+
// Custom retry logic for commitlint validation
|
|
11338
|
+
let retryCount = 0;
|
|
11339
|
+
let validationErrors = '';
|
|
11340
|
+
const generateCommitMessage = async () => {
|
|
11341
|
+
// Update variables with validation errors for retry attempts
|
|
11342
|
+
const currentVariables = {
|
|
11343
|
+
...variables,
|
|
11344
|
+
additional_context: validationErrors
|
|
11345
|
+
? `${variables.additional_context}\n\n## Validation Errors from Previous Attempt\nPlease fix the following issues:\n${validationErrors}`
|
|
11346
|
+
: variables.additional_context,
|
|
11347
|
+
};
|
|
11348
|
+
const commitMsg = await executeChainWithSchema(schema, llm, prompt, currentVariables, {
|
|
11349
|
+
retryOptions: {
|
|
11350
|
+
maxAttempts,
|
|
11351
|
+
onRetry: (attempt, error) => {
|
|
11352
|
+
logger.verbose(`Failed to parse commit message (attempt ${attempt}/${maxAttempts}): ${error.message}`, { color: 'yellow' });
|
|
11353
|
+
},
|
|
11199
11354
|
},
|
|
11200
|
-
|
|
11201
|
-
|
|
11202
|
-
|
|
11203
|
-
|
|
11204
|
-
|
|
11205
|
-
|
|
11206
|
-
|
|
11207
|
-
|
|
11355
|
+
fallbackParser: (text) => ({
|
|
11356
|
+
title: text.split('\n')[0] || 'Auto-generated commit',
|
|
11357
|
+
body: text.split('\n').slice(1).join('\n') || 'Generated commit message',
|
|
11358
|
+
}),
|
|
11359
|
+
onFallback: () => {
|
|
11360
|
+
logger.verbose('Max retry attempts reached. Falling back to simple text output.', {
|
|
11361
|
+
color: 'red',
|
|
11362
|
+
});
|
|
11363
|
+
},
|
|
11364
|
+
});
|
|
11365
|
+
// Construct the full commit message
|
|
11366
|
+
const appendedText = argv.append ? `\n\n${argv.append}` : '';
|
|
11367
|
+
const ticketId = extractTicketIdFromBranchName(branchName);
|
|
11368
|
+
const ticketFooter = argv.appendTicket && ticketId ? `\n\nPart of **${ticketId}**` : '';
|
|
11369
|
+
const fullMessage = `${commitMsg.title}\n\n${commitMsg.body}${appendedText}${ticketFooter}`;
|
|
11370
|
+
// If commitlint validation is needed, validate the message
|
|
11371
|
+
if (USE_CONVENTIONAL_COMMITS || hasCommitLintConfig) {
|
|
11372
|
+
const { validateCommitMessage, CommitlintValidationError } = await Promise.resolve().then(function () { return commitlintValidator; });
|
|
11373
|
+
const validationResult = await validateCommitMessage(fullMessage);
|
|
11374
|
+
logger.verbose(`Validation result: ${JSON.stringify(validationResult)}`, {
|
|
11375
|
+
color: 'yellow',
|
|
11208
11376
|
});
|
|
11377
|
+
if (!validationResult.valid) {
|
|
11378
|
+
retryCount++;
|
|
11379
|
+
// Format validation errors for next attempt
|
|
11380
|
+
validationErrors = validationResult.errors.map((error) => `- ${error}`).join('\n');
|
|
11381
|
+
// Auto-retry up to 2 times
|
|
11382
|
+
if (retryCount <= 2) {
|
|
11383
|
+
logger.verbose(`Commit message validation failed (attempt ${retryCount}/2). Retrying with error feedback...`, { color: 'yellow' });
|
|
11384
|
+
throw new CommitlintValidationError(`Validation failed: ${validationResult.errors.join('; ')}`, validationResult, fullMessage);
|
|
11385
|
+
}
|
|
11386
|
+
// After 2 failed attempts, let the user decide
|
|
11387
|
+
const { handleValidationErrors } = await Promise.resolve().then(function () { return commitValidationHandler; });
|
|
11388
|
+
const validationHandlerResult = await handleValidationErrors(fullMessage, validationResult, {
|
|
11389
|
+
logger,
|
|
11390
|
+
interactive: INTERACTIVE,
|
|
11391
|
+
openInEditor: config.openInEditor,
|
|
11392
|
+
});
|
|
11393
|
+
logger.verbose(`Validation handler result: ${JSON.stringify(validationHandlerResult)}`, {
|
|
11394
|
+
color: 'blue',
|
|
11395
|
+
});
|
|
11396
|
+
switch (validationHandlerResult.action) {
|
|
11397
|
+
case 'proceed':
|
|
11398
|
+
return validationHandlerResult.message;
|
|
11399
|
+
case 'edit':
|
|
11400
|
+
return validationHandlerResult.message;
|
|
11401
|
+
case 'regenerate':
|
|
11402
|
+
// Reset retry count and validation errors for fresh attempts
|
|
11403
|
+
retryCount = 0;
|
|
11404
|
+
validationErrors = '';
|
|
11405
|
+
throw new CommitlintValidationError('User requested regeneration', validationResult, fullMessage);
|
|
11406
|
+
case 'abort':
|
|
11407
|
+
logger.log('\nAborting commit due to validation errors.', { color: 'red' });
|
|
11408
|
+
process.exit(1);
|
|
11409
|
+
}
|
|
11410
|
+
}
|
|
11411
|
+
}
|
|
11412
|
+
return fullMessage;
|
|
11413
|
+
};
|
|
11414
|
+
// Custom shouldRetry function for commitlint errors
|
|
11415
|
+
const shouldRetryCommitlint = (error) => {
|
|
11416
|
+
return error.name === 'CommitlintValidationError';
|
|
11417
|
+
};
|
|
11418
|
+
// Use retry wrapper for commitlint validation with up to 4 total attempts
|
|
11419
|
+
// (2 automatic retries + 2 more if user chooses "Try again")
|
|
11420
|
+
return await withRetry(generateCommitMessage, {
|
|
11421
|
+
maxAttempts: 6, // Allow for multiple user retry requests
|
|
11422
|
+
shouldRetry: shouldRetryCommitlint,
|
|
11423
|
+
backoffMs: 0, // No delay needed for commitlint retries
|
|
11424
|
+
onRetry: (attempt, error) => {
|
|
11425
|
+
if (error.name === 'CommitlintValidationError' && attempt <= 2) {
|
|
11426
|
+
// Don't log for auto-retries, we already log in the function
|
|
11427
|
+
return;
|
|
11428
|
+
}
|
|
11429
|
+
logger.verbose(`Retrying commit message generation (attempt ${attempt}): ${error.message}`, { color: 'yellow' });
|
|
11209
11430
|
},
|
|
11210
11431
|
});
|
|
11211
|
-
// Construct the full commit message
|
|
11212
|
-
const appendedText = argv.append ? `\n\n${argv.append}` : '';
|
|
11213
|
-
const ticketId = extractTicketIdFromBranchName(branchName);
|
|
11214
|
-
const ticketFooter = argv.appendTicket && ticketId ? `\n\nPart of **${ticketId}**` : '';
|
|
11215
|
-
const fullMessage = `${commitMsg.title}\n\n${commitMsg.body}${appendedText}${ticketFooter}`;
|
|
11216
|
-
// If conventional commits are enabled, validate with commitlint
|
|
11217
|
-
if (useConventional) {
|
|
11218
|
-
const { validateCommitMessage } = await Promise.resolve().then(function () { return commitlintValidator; });
|
|
11219
|
-
const { handleValidationErrors } = await Promise.resolve().then(function () { return commitValidationHandler; });
|
|
11220
|
-
const validationResult = await validateCommitMessage(fullMessage);
|
|
11221
|
-
const validationHandlerResult = await handleValidationErrors(fullMessage, validationResult, {
|
|
11222
|
-
logger,
|
|
11223
|
-
interactive: INTERACTIVE,
|
|
11224
|
-
openInEditor: config.openInEditor,
|
|
11225
|
-
});
|
|
11226
|
-
switch (validationHandlerResult.action) {
|
|
11227
|
-
case 'proceed':
|
|
11228
|
-
// Validation passed, use the message as is
|
|
11229
|
-
return validationHandlerResult.message;
|
|
11230
|
-
case 'edit':
|
|
11231
|
-
// User edited the message, use the edited version
|
|
11232
|
-
return validationHandlerResult.message;
|
|
11233
|
-
case 'regenerate':
|
|
11234
|
-
// User wants to regenerate, throw special error to trigger regeneration
|
|
11235
|
-
throw new Error('REGENERATE_COMMIT_MESSAGE');
|
|
11236
|
-
case 'abort':
|
|
11237
|
-
// User wants to abort or validation failed in non-interactive mode
|
|
11238
|
-
logger.log('\nAborting commit due to validation errors.', { color: 'red' });
|
|
11239
|
-
process.exit(1);
|
|
11240
|
-
}
|
|
11241
|
-
}
|
|
11242
|
-
return fullMessage;
|
|
11243
11432
|
},
|
|
11244
11433
|
noResult: async () => {
|
|
11245
11434
|
await noResult$2({ git, logger });
|
|
@@ -11280,28 +11469,6 @@ const builder$2 = (yargs) => {
|
|
|
11280
11469
|
return yargs.options(options$2).usage(getCommandUsageHeader(command$2));
|
|
11281
11470
|
};
|
|
11282
11471
|
|
|
11283
|
-
/**
|
|
11284
|
-
* Finds the project root directory starting from the given current directory.
|
|
11285
|
-
* It checks if the `.git` directory or `package.json` file exists in the current directory or any of its parent directories.
|
|
11286
|
-
* If found, it returns the path to the project root directory.
|
|
11287
|
-
* If not found, it throws an error.
|
|
11288
|
-
*
|
|
11289
|
-
* @param currentDir - The current directory to start searching from.
|
|
11290
|
-
* @returns The path to the project root directory.
|
|
11291
|
-
* @throws Error if the project root directory cannot be found.
|
|
11292
|
-
*/
|
|
11293
|
-
function findProjectRoot(currentDir) {
|
|
11294
|
-
const root = path.parse(currentDir).root;
|
|
11295
|
-
while (currentDir !== root) {
|
|
11296
|
-
if (fs.existsSync(path.join(currentDir, '.git')) ||
|
|
11297
|
-
fs.existsSync(path.join(currentDir, 'package.json'))) {
|
|
11298
|
-
return currentDir;
|
|
11299
|
-
}
|
|
11300
|
-
currentDir = path.dirname(currentDir);
|
|
11301
|
-
}
|
|
11302
|
-
throw new Error('Unable to find project root. Are you in the right directory?');
|
|
11303
|
-
}
|
|
11304
|
-
|
|
11305
11472
|
/**
|
|
11306
11473
|
* Executes a command as a Promise and returns the result.
|
|
11307
11474
|
*
|
|
@@ -13221,11 +13388,13 @@ function isObservable(obj) {
|
|
|
13221
13388
|
return !!obj && (obj instanceof Observable || (isFunction(obj.lift) && isFunction(obj.subscribe)));
|
|
13222
13389
|
}
|
|
13223
13390
|
|
|
13224
|
-
var EmptyError = createErrorClass(function (_super) {
|
|
13225
|
-
|
|
13226
|
-
|
|
13227
|
-
|
|
13228
|
-
|
|
13391
|
+
var EmptyError = createErrorClass(function (_super) {
|
|
13392
|
+
return function EmptyErrorImpl() {
|
|
13393
|
+
_super(this);
|
|
13394
|
+
this.name = 'EmptyError';
|
|
13395
|
+
this.message = 'no elements in sequence';
|
|
13396
|
+
};
|
|
13397
|
+
});
|
|
13229
13398
|
|
|
13230
13399
|
function lastValueFrom(source, config) {
|
|
13231
13400
|
var hasConfig = typeof config === 'object';
|
|
@@ -14512,23 +14681,183 @@ y.command(review.command, review.desc, review.builder, review.handler);
|
|
|
14512
14681
|
y.command(init.command, init.desc, init.builder, init.handler);
|
|
14513
14682
|
y.help().parse(process.argv.slice(2));
|
|
14514
14683
|
|
|
14684
|
+
/**
|
|
14685
|
+
* Edit a commit message with commitlint validation if config exists
|
|
14686
|
+
*/
|
|
14687
|
+
async function editCommitMessage(message, options) {
|
|
14688
|
+
// First, let the user edit the message
|
|
14689
|
+
const editedMessage = await editResult(message, options);
|
|
14690
|
+
// Then validate it against commitlint if config exists
|
|
14691
|
+
const { hasCommitlintConfig } = await Promise.resolve().then(function () { return hasCommitlintConfig$1; });
|
|
14692
|
+
const hasConfig = await hasCommitlintConfig();
|
|
14693
|
+
if (hasConfig) {
|
|
14694
|
+
const { validateCommitMessage } = await Promise.resolve().then(function () { return commitlintValidator; });
|
|
14695
|
+
const { handleValidationErrors } = await Promise.resolve().then(function () { return commitValidationHandler; });
|
|
14696
|
+
const validationResult = await validateCommitMessage(editedMessage);
|
|
14697
|
+
if (!validationResult.valid) {
|
|
14698
|
+
// Show validation errors and get user action
|
|
14699
|
+
const validationHandlerResult = await handleValidationErrors(editedMessage, validationResult, {
|
|
14700
|
+
logger: options.logger,
|
|
14701
|
+
interactive: options.interactive,
|
|
14702
|
+
openInEditor: options.openInEditor,
|
|
14703
|
+
});
|
|
14704
|
+
// Return the result from the validation handler
|
|
14705
|
+
return validationHandlerResult.message;
|
|
14706
|
+
}
|
|
14707
|
+
}
|
|
14708
|
+
return editedMessage;
|
|
14709
|
+
}
|
|
14710
|
+
|
|
14711
|
+
var editCommitMessage$1 = /*#__PURE__*/Object.freeze({
|
|
14712
|
+
__proto__: null,
|
|
14713
|
+
editCommitMessage: editCommitMessage
|
|
14714
|
+
});
|
|
14715
|
+
|
|
14716
|
+
/**
|
|
14717
|
+
* Custom error for commitlint validation failures
|
|
14718
|
+
* This allows the retry system to identify these errors specifically
|
|
14719
|
+
*/
|
|
14720
|
+
class CommitlintValidationError extends Error {
|
|
14721
|
+
constructor(message, validationResult, commitMessage) {
|
|
14722
|
+
super(message);
|
|
14723
|
+
this.name = 'CommitlintValidationError';
|
|
14724
|
+
this.validationResult = validationResult;
|
|
14725
|
+
this.commitMessage = commitMessage;
|
|
14726
|
+
}
|
|
14727
|
+
}
|
|
14515
14728
|
/**
|
|
14516
14729
|
* Load commitlint configuration
|
|
14517
14730
|
*/
|
|
14518
14731
|
async function loadCommitlintConfig() {
|
|
14519
|
-
|
|
14520
|
-
const
|
|
14521
|
-
|
|
14732
|
+
const projectRoot = findProjectRoot(process.cwd());
|
|
14733
|
+
const cwd = projectRoot || process.cwd();
|
|
14734
|
+
// @commitlint/load has issues with ESM configs (e.g. commitlint.config.js with `export default`).
|
|
14735
|
+
// Let's try to load them manually first.
|
|
14736
|
+
const esmConfigCandidates = COMMITLINT_CONFIG_FILES.filter((file) => file.endsWith('.js'));
|
|
14737
|
+
for (const configFile of esmConfigCandidates) {
|
|
14738
|
+
const configPath = path.join(cwd, configFile);
|
|
14739
|
+
if (fs.existsSync(configPath)) {
|
|
14740
|
+
try {
|
|
14741
|
+
const module = await import(url.pathToFileURL(configPath).href);
|
|
14742
|
+
if (module.default &&
|
|
14743
|
+
(Object.keys(module.default.rules || {}).length > 0 ||
|
|
14744
|
+
(module.default.extends && module.default.extends.length > 0))) {
|
|
14745
|
+
// We found a config, now let commitlint process it (for extends etc)
|
|
14746
|
+
return await core$1.load(module.default, { cwd });
|
|
14747
|
+
}
|
|
14748
|
+
}
|
|
14749
|
+
catch (error) {
|
|
14750
|
+
// Failed to import, maybe not an ESM file after all or syntax error.
|
|
14751
|
+
// We will let the standard load take a chance.
|
|
14752
|
+
}
|
|
14753
|
+
}
|
|
14754
|
+
}
|
|
14522
14755
|
try {
|
|
14523
|
-
//
|
|
14524
|
-
const config = await load();
|
|
14525
|
-
|
|
14756
|
+
// Let @commitlint/load try to find the config. This works for CJS, JSON, and YAML.
|
|
14757
|
+
const config = await core$1.load({}, { cwd });
|
|
14758
|
+
// Check if a real config was loaded.
|
|
14759
|
+
if (config.extends.length > 0 || Object.keys(config.rules).length > 0) {
|
|
14760
|
+
return config;
|
|
14761
|
+
}
|
|
14526
14762
|
}
|
|
14527
|
-
catch {
|
|
14528
|
-
//
|
|
14529
|
-
|
|
14530
|
-
|
|
14531
|
-
|
|
14763
|
+
catch (error) {
|
|
14764
|
+
// Could be an error parsing, or just not found. Fall through to default.
|
|
14765
|
+
}
|
|
14766
|
+
// If nothing worked, fallback to conventional config
|
|
14767
|
+
return core$1.load({
|
|
14768
|
+
extends: ['@commitlint/config-conventional'],
|
|
14769
|
+
});
|
|
14770
|
+
}
|
|
14771
|
+
/**
|
|
14772
|
+
* Format commitlint rules into a human-readable string for AI prompts
|
|
14773
|
+
*/
|
|
14774
|
+
function formatCommitlintRulesForPrompt(config) {
|
|
14775
|
+
if (!config.rules || Object.keys(config.rules).length === 0) {
|
|
14776
|
+
return '';
|
|
14777
|
+
}
|
|
14778
|
+
const ruleDescriptions = [];
|
|
14779
|
+
// Add information about extends if present
|
|
14780
|
+
if (config.extends && config.extends.length > 0) {
|
|
14781
|
+
ruleDescriptions.push(`Following ${config.extends.join(', ')} configuration`);
|
|
14782
|
+
}
|
|
14783
|
+
// Process key rules that affect commit message format
|
|
14784
|
+
const rules = config.rules;
|
|
14785
|
+
// Header length rules
|
|
14786
|
+
if (rules['header-max-length']) {
|
|
14787
|
+
const [level, , maxLength] = rules['header-max-length'];
|
|
14788
|
+
if (level > 0) {
|
|
14789
|
+
ruleDescriptions.push(`Header (title) must be ${maxLength} characters or less (including spaces)`);
|
|
14790
|
+
}
|
|
14791
|
+
}
|
|
14792
|
+
if (rules['header-min-length']) {
|
|
14793
|
+
const [level, , minLength] = rules['header-min-length'];
|
|
14794
|
+
if (level > 0) {
|
|
14795
|
+
ruleDescriptions.push(`Header (title) must be at least ${minLength} characters (including spaces)`);
|
|
14796
|
+
}
|
|
14797
|
+
}
|
|
14798
|
+
// Body length rules
|
|
14799
|
+
if (rules['body-max-line-length']) {
|
|
14800
|
+
const [level, , maxLength] = rules['body-max-line-length'];
|
|
14801
|
+
if (level > 0) {
|
|
14802
|
+
ruleDescriptions.push(`Body lines must be ${maxLength} characters or less (including spaces)`);
|
|
14803
|
+
}
|
|
14804
|
+
}
|
|
14805
|
+
// Type rules
|
|
14806
|
+
if (rules['type-enum']) {
|
|
14807
|
+
const [level, , allowedTypes] = rules['type-enum'];
|
|
14808
|
+
if (level > 0 && Array.isArray(allowedTypes)) {
|
|
14809
|
+
ruleDescriptions.push(`Allowed types: ${allowedTypes.join(', ')}`);
|
|
14810
|
+
}
|
|
14811
|
+
}
|
|
14812
|
+
// Case rules
|
|
14813
|
+
if (rules['type-case']) {
|
|
14814
|
+
const [level, , caseType] = rules['type-case'];
|
|
14815
|
+
if (level > 0) {
|
|
14816
|
+
ruleDescriptions.push(`Type must be ${caseType} case`);
|
|
14817
|
+
}
|
|
14818
|
+
}
|
|
14819
|
+
if (rules['subject-case']) {
|
|
14820
|
+
const [level, , caseType] = rules['subject-case'];
|
|
14821
|
+
if (level > 0) {
|
|
14822
|
+
ruleDescriptions.push(`Subject must be ${caseType} case`);
|
|
14823
|
+
}
|
|
14824
|
+
}
|
|
14825
|
+
// Scope rules
|
|
14826
|
+
if (rules['scope-enum']) {
|
|
14827
|
+
const [level, , allowedScopes] = rules['scope-enum'];
|
|
14828
|
+
if (level > 0 && Array.isArray(allowedScopes)) {
|
|
14829
|
+
ruleDescriptions.push(`Allowed scopes: ${allowedScopes.join(', ')}`);
|
|
14830
|
+
}
|
|
14831
|
+
}
|
|
14832
|
+
// Subject rules
|
|
14833
|
+
if (rules['subject-full-stop']) {
|
|
14834
|
+
const [level, condition] = rules['subject-full-stop'];
|
|
14835
|
+
if (level > 0) {
|
|
14836
|
+
const verb = condition === 'always' ? 'must' : 'must not';
|
|
14837
|
+
ruleDescriptions.push(`Subject ${verb} end with a period`);
|
|
14838
|
+
}
|
|
14839
|
+
}
|
|
14840
|
+
if (rules['subject-empty']) {
|
|
14841
|
+
const [level, condition] = rules['subject-empty'];
|
|
14842
|
+
if (level > 0) {
|
|
14843
|
+
const requirement = condition === 'never' ? 'must not be empty' : 'must be empty';
|
|
14844
|
+
ruleDescriptions.push(`Subject ${requirement}`);
|
|
14845
|
+
}
|
|
14846
|
+
}
|
|
14847
|
+
return ruleDescriptions.length > 0
|
|
14848
|
+
? `## Commitlint Rules\nYour commit message must follow these project-specific rules:\n${ruleDescriptions.map(rule => `- ${rule}`).join('\n')}\n`
|
|
14849
|
+
: '';
|
|
14850
|
+
}
|
|
14851
|
+
/**
|
|
14852
|
+
* Get commitlint rules context for prompt if config exists
|
|
14853
|
+
*/
|
|
14854
|
+
async function getCommitlintRulesContext() {
|
|
14855
|
+
try {
|
|
14856
|
+
const config = await loadCommitlintConfig();
|
|
14857
|
+
return formatCommitlintRulesForPrompt(config);
|
|
14858
|
+
}
|
|
14859
|
+
catch (error) {
|
|
14860
|
+
return '';
|
|
14532
14861
|
}
|
|
14533
14862
|
}
|
|
14534
14863
|
/**
|
|
@@ -14537,10 +14866,7 @@ async function loadCommitlintConfig() {
|
|
|
14537
14866
|
async function validateCommitMessage(message, options = {}) {
|
|
14538
14867
|
try {
|
|
14539
14868
|
const config = await loadCommitlintConfig();
|
|
14540
|
-
|
|
14541
|
-
const commitlint = await import('@commitlint/core');
|
|
14542
|
-
const { lint } = commitlint;
|
|
14543
|
-
const result = await lint(message, config.rules, options);
|
|
14869
|
+
const result = await core$1.lint(message, config.rules, options);
|
|
14544
14870
|
return {
|
|
14545
14871
|
valid: result.valid,
|
|
14546
14872
|
errors: result.errors.map((error) => error.message),
|
|
@@ -14558,6 +14884,9 @@ async function validateCommitMessage(message, options = {}) {
|
|
|
14558
14884
|
|
|
14559
14885
|
var commitlintValidator = /*#__PURE__*/Object.freeze({
|
|
14560
14886
|
__proto__: null,
|
|
14887
|
+
CommitlintValidationError: CommitlintValidationError,
|
|
14888
|
+
formatCommitlintRulesForPrompt: formatCommitlintRulesForPrompt,
|
|
14889
|
+
getCommitlintRulesContext: getCommitlintRulesContext,
|
|
14561
14890
|
loadCommitlintConfig: loadCommitlintConfig,
|
|
14562
14891
|
validateCommitMessage: validateCommitMessage
|
|
14563
14892
|
});
|
|
@@ -14594,29 +14923,45 @@ async function handleValidationErrors(message, validationResult, options) {
|
|
|
14594
14923
|
message: 'How would you like to proceed?:',
|
|
14595
14924
|
choices: [
|
|
14596
14925
|
{
|
|
14597
|
-
name: '
|
|
14598
|
-
value: '
|
|
14599
|
-
description: '
|
|
14926
|
+
name: 'Try 2 more attempts',
|
|
14927
|
+
value: 'retry',
|
|
14928
|
+
description: 'Let the AI try generating 2 more commit messages with error feedback',
|
|
14600
14929
|
},
|
|
14601
14930
|
{
|
|
14602
|
-
name: '
|
|
14603
|
-
value: '
|
|
14604
|
-
description: '
|
|
14931
|
+
name: 'Edit manually',
|
|
14932
|
+
value: 'edit',
|
|
14933
|
+
description: 'Edit the commit message manually to fix the issues',
|
|
14605
14934
|
},
|
|
14606
14935
|
{
|
|
14607
14936
|
name: 'Abort',
|
|
14608
14937
|
value: 'abort',
|
|
14609
|
-
description: 'Abort the commit',
|
|
14938
|
+
description: 'Abort the commit process',
|
|
14610
14939
|
},
|
|
14611
14940
|
],
|
|
14612
14941
|
});
|
|
14613
14942
|
switch (choice) {
|
|
14614
|
-
case '
|
|
14943
|
+
case 'edit': {
|
|
14615
14944
|
// Edit message manually
|
|
14616
14945
|
const editedMessage = await editResult(message, options);
|
|
14946
|
+
// Validate the manually edited message if commitlint config exists
|
|
14947
|
+
const { hasCommitlintConfig } = await Promise.resolve().then(function () { return hasCommitlintConfig$1; });
|
|
14948
|
+
const hasConfig = await hasCommitlintConfig();
|
|
14949
|
+
if (hasConfig) {
|
|
14950
|
+
const { validateCommitMessage } = await Promise.resolve().then(function () { return commitlintValidator; });
|
|
14951
|
+
const editedValidationResult = await validateCommitMessage(editedMessage);
|
|
14952
|
+
if (!editedValidationResult.valid) {
|
|
14953
|
+
// Show validation errors for the edited message
|
|
14954
|
+
options.logger.log('\nEdited commit message also has validation issues:', { color: 'yellow' });
|
|
14955
|
+
editedValidationResult.errors.forEach((error) => {
|
|
14956
|
+
options.logger.log(` • ${error}`, { color: 'red' });
|
|
14957
|
+
});
|
|
14958
|
+
// Recursively handle validation errors for the edited message
|
|
14959
|
+
return await handleValidationErrors(editedMessage, editedValidationResult, options);
|
|
14960
|
+
}
|
|
14961
|
+
}
|
|
14617
14962
|
return { message: editedMessage, action: 'edit' };
|
|
14618
14963
|
}
|
|
14619
|
-
case '
|
|
14964
|
+
case 'retry':
|
|
14620
14965
|
// Regenerate message
|
|
14621
14966
|
return { message, action: 'regenerate' };
|
|
14622
14967
|
default:
|