git-coco 0.20.1 → 0.21.1
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 +484 -129
- package/dist/index.js +483 -128
- package/package.json +5 -5
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.1";
|
|
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(),
|
|
7577
|
-
});
|
|
7631
|
+
title: stringType().describe("Title of the commit message"),
|
|
7632
|
+
body: stringType().describe("Body of the commit message"),
|
|
7633
|
+
}).describe("Object with commit message 'title' and 'body'");
|
|
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("Object with Conventional Commit message 'title' and 'body' adhering to Conventional Commits specification");
|
|
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
|
}
|
|
@@ -11112,6 +11244,13 @@ const handler$3 = async (argv, logger) => {
|
|
|
11112
11244
|
}
|
|
11113
11245
|
}
|
|
11114
11246
|
async function parser(changes) {
|
|
11247
|
+
if (config.noDiff) {
|
|
11248
|
+
// When noDiff is enabled, just return a simple summary without parsing file contents
|
|
11249
|
+
const filesSummary = changes
|
|
11250
|
+
.map((change) => `${change.status}: ${change.filePath}`)
|
|
11251
|
+
.join('\n');
|
|
11252
|
+
return `Staged files:\n${filesSummary}`;
|
|
11253
|
+
}
|
|
11115
11254
|
return await fileChangeParser({
|
|
11116
11255
|
changes,
|
|
11117
11256
|
commit: '--staged',
|
|
@@ -11123,7 +11262,7 @@ const handler$3 = async (argv, logger) => {
|
|
|
11123
11262
|
options: {
|
|
11124
11263
|
...config,
|
|
11125
11264
|
prompt: config.prompt ||
|
|
11126
|
-
(
|
|
11265
|
+
(USE_CONVENTIONAL_COMMITS
|
|
11127
11266
|
? CONVENTIONAL_COMMIT_PROMPT.template
|
|
11128
11267
|
: COMMIT_PROMPT.template),
|
|
11129
11268
|
logger,
|
|
@@ -11136,21 +11275,27 @@ const handler$3 = async (argv, logger) => {
|
|
|
11136
11275
|
retryMessageOnly: 'Restart the function execution from generating the commit message',
|
|
11137
11276
|
retryFull: 'Restart the function execution from the beginning, regenerating both the diff summary and commit message',
|
|
11138
11277
|
},
|
|
11278
|
+
customEditFunction: async (message, options) => {
|
|
11279
|
+
const { editCommitMessage } = await Promise.resolve().then(function () { return editCommitMessage$1; });
|
|
11280
|
+
return editCommitMessage(message, options);
|
|
11281
|
+
},
|
|
11139
11282
|
},
|
|
11140
11283
|
},
|
|
11141
11284
|
factory,
|
|
11142
11285
|
parser,
|
|
11143
11286
|
agent: async (context, options) => {
|
|
11144
|
-
// Check if conventional commits are enabled via config or CLI flag
|
|
11145
|
-
const useConventional = config.conventionalCommits || argv.conventional;
|
|
11146
11287
|
// Select the appropriate schema based on whether conventional commits are enabled
|
|
11147
|
-
const schema =
|
|
11288
|
+
const schema = USE_CONVENTIONAL_COMMITS
|
|
11148
11289
|
? ConventionalCommitMessageResponseSchema
|
|
11149
11290
|
: CommitMessageResponseSchema;
|
|
11150
11291
|
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
|
-
{
|
|
11292
|
+
${schema.description}
|
|
11293
|
+
{
|
|
11294
|
+
"title": "The commit title",
|
|
11295
|
+
"body": "The commit body"
|
|
11296
|
+
}`;
|
|
11152
11297
|
// Use conventional commit prompt if enabled
|
|
11153
|
-
const promptTemplate =
|
|
11298
|
+
const promptTemplate = USE_CONVENTIONAL_COMMITS ? CONVENTIONAL_COMMIT_PROMPT : COMMIT_PROMPT;
|
|
11154
11299
|
const prompt = getPrompt({
|
|
11155
11300
|
template: options.prompt,
|
|
11156
11301
|
variables: promptTemplate.inputVariables,
|
|
@@ -11180,6 +11325,13 @@ const handler$3 = async (argv, logger) => {
|
|
|
11180
11325
|
: config.includeBranchName !== false; // Default to true if not explicitly set to false
|
|
11181
11326
|
// Create branch name context string based on the configuration
|
|
11182
11327
|
const branchNameContext = includeBranchName ? `Current git branch name: ${branchName}` : '';
|
|
11328
|
+
// Load commitlint rules context if available
|
|
11329
|
+
const hasCommitLintConfig = await hasCommitlintConfig();
|
|
11330
|
+
let commitlint_rules_context = '';
|
|
11331
|
+
if (USE_CONVENTIONAL_COMMITS || hasCommitLintConfig) {
|
|
11332
|
+
const { getCommitlintRulesContext } = await Promise.resolve().then(function () { return commitlintValidator; });
|
|
11333
|
+
commitlint_rules_context = await getCommitlintRulesContext();
|
|
11334
|
+
}
|
|
11183
11335
|
// Get variables for the prompt
|
|
11184
11336
|
const variables = {
|
|
11185
11337
|
summary: context,
|
|
@@ -11187,59 +11339,106 @@ const handler$3 = async (argv, logger) => {
|
|
|
11187
11339
|
additional_context: additional_context,
|
|
11188
11340
|
commit_history: commit_history,
|
|
11189
11341
|
branch_name_context: branchNameContext,
|
|
11342
|
+
commitlint_rules_context: commitlint_rules_context,
|
|
11190
11343
|
};
|
|
11191
11344
|
const maxAttempts = config.service.provider === 'ollama' && 'maxParsingAttempts' in config.service
|
|
11192
11345
|
? config.service.maxParsingAttempts || 3
|
|
11193
11346
|
: 3;
|
|
11194
|
-
|
|
11195
|
-
|
|
11196
|
-
|
|
11197
|
-
|
|
11198
|
-
|
|
11347
|
+
// Custom retry logic for commitlint validation
|
|
11348
|
+
let retryCount = 0;
|
|
11349
|
+
let validationErrors = '';
|
|
11350
|
+
const generateCommitMessage = async () => {
|
|
11351
|
+
// Update variables with validation errors for retry attempts
|
|
11352
|
+
const currentVariables = {
|
|
11353
|
+
...variables,
|
|
11354
|
+
additional_context: validationErrors
|
|
11355
|
+
? `${variables.additional_context}\n\n## Validation Errors from Previous Attempt\nPlease fix the following issues:\n${validationErrors}`
|
|
11356
|
+
: variables.additional_context,
|
|
11357
|
+
};
|
|
11358
|
+
const commitMsg = await executeChainWithSchema(schema, llm, prompt, currentVariables, {
|
|
11359
|
+
retryOptions: {
|
|
11360
|
+
maxAttempts,
|
|
11361
|
+
onRetry: (attempt, error) => {
|
|
11362
|
+
logger.verbose(`Failed to parse commit message (attempt ${attempt}/${maxAttempts}): ${error.message}`, { color: 'yellow' });
|
|
11363
|
+
},
|
|
11199
11364
|
},
|
|
11200
|
-
|
|
11201
|
-
|
|
11202
|
-
|
|
11203
|
-
|
|
11204
|
-
|
|
11205
|
-
|
|
11206
|
-
|
|
11207
|
-
|
|
11365
|
+
fallbackParser: (text) => ({
|
|
11366
|
+
title: text.split('\n')[0] || 'Auto-generated commit',
|
|
11367
|
+
body: text.split('\n').slice(1).join('\n') || 'Generated commit message',
|
|
11368
|
+
}),
|
|
11369
|
+
onFallback: () => {
|
|
11370
|
+
logger.verbose('Max retry attempts reached. Falling back to simple text output.', {
|
|
11371
|
+
color: 'red',
|
|
11372
|
+
});
|
|
11373
|
+
},
|
|
11374
|
+
});
|
|
11375
|
+
// Construct the full commit message
|
|
11376
|
+
const appendedText = argv.append ? `\n\n${argv.append}` : '';
|
|
11377
|
+
const ticketId = extractTicketIdFromBranchName(branchName);
|
|
11378
|
+
const ticketFooter = argv.appendTicket && ticketId ? `\n\nPart of **${ticketId}**` : '';
|
|
11379
|
+
const fullMessage = `${commitMsg.title}\n\n${commitMsg.body}${appendedText}${ticketFooter}`;
|
|
11380
|
+
// If commitlint validation is needed, validate the message
|
|
11381
|
+
if (USE_CONVENTIONAL_COMMITS || hasCommitLintConfig) {
|
|
11382
|
+
const { validateCommitMessage, CommitlintValidationError } = await Promise.resolve().then(function () { return commitlintValidator; });
|
|
11383
|
+
const validationResult = await validateCommitMessage(fullMessage);
|
|
11384
|
+
logger.verbose(`Validation result: ${JSON.stringify(validationResult)}`, {
|
|
11385
|
+
color: 'yellow',
|
|
11208
11386
|
});
|
|
11387
|
+
if (!validationResult.valid) {
|
|
11388
|
+
retryCount++;
|
|
11389
|
+
// Format validation errors for next attempt
|
|
11390
|
+
validationErrors = validationResult.errors.map((error) => `- ${error}`).join('\n');
|
|
11391
|
+
// Auto-retry up to 2 times
|
|
11392
|
+
if (retryCount <= 2) {
|
|
11393
|
+
logger.verbose(`Commit message validation failed (attempt ${retryCount}/2). Retrying with error feedback...`, { color: 'yellow' });
|
|
11394
|
+
throw new CommitlintValidationError(`Validation failed: ${validationResult.errors.join('; ')}`, validationResult, fullMessage);
|
|
11395
|
+
}
|
|
11396
|
+
// After 2 failed attempts, let the user decide
|
|
11397
|
+
const { handleValidationErrors } = await Promise.resolve().then(function () { return commitValidationHandler; });
|
|
11398
|
+
const validationHandlerResult = await handleValidationErrors(fullMessage, validationResult, {
|
|
11399
|
+
logger,
|
|
11400
|
+
interactive: INTERACTIVE,
|
|
11401
|
+
openInEditor: config.openInEditor,
|
|
11402
|
+
});
|
|
11403
|
+
logger.verbose(`Validation handler result: ${JSON.stringify(validationHandlerResult)}`, {
|
|
11404
|
+
color: 'blue',
|
|
11405
|
+
});
|
|
11406
|
+
switch (validationHandlerResult.action) {
|
|
11407
|
+
case 'proceed':
|
|
11408
|
+
return validationHandlerResult.message;
|
|
11409
|
+
case 'edit':
|
|
11410
|
+
return validationHandlerResult.message;
|
|
11411
|
+
case 'regenerate':
|
|
11412
|
+
// Reset retry count and validation errors for fresh attempts
|
|
11413
|
+
retryCount = 0;
|
|
11414
|
+
validationErrors = '';
|
|
11415
|
+
throw new CommitlintValidationError('User requested regeneration', validationResult, fullMessage);
|
|
11416
|
+
case 'abort':
|
|
11417
|
+
logger.log('\nAborting commit due to validation errors.', { color: 'red' });
|
|
11418
|
+
process.exit(1);
|
|
11419
|
+
}
|
|
11420
|
+
}
|
|
11421
|
+
}
|
|
11422
|
+
return fullMessage;
|
|
11423
|
+
};
|
|
11424
|
+
// Custom shouldRetry function for commitlint errors
|
|
11425
|
+
const shouldRetryCommitlint = (error) => {
|
|
11426
|
+
return error.name === 'CommitlintValidationError';
|
|
11427
|
+
};
|
|
11428
|
+
// Use retry wrapper for commitlint validation with up to 4 total attempts
|
|
11429
|
+
// (2 automatic retries + 2 more if user chooses "Try again")
|
|
11430
|
+
return await withRetry(generateCommitMessage, {
|
|
11431
|
+
maxAttempts: 6, // Allow for multiple user retry requests
|
|
11432
|
+
shouldRetry: shouldRetryCommitlint,
|
|
11433
|
+
backoffMs: 0, // No delay needed for commitlint retries
|
|
11434
|
+
onRetry: (attempt, error) => {
|
|
11435
|
+
if (error.name === 'CommitlintValidationError' && attempt <= 2) {
|
|
11436
|
+
// Don't log for auto-retries, we already log in the function
|
|
11437
|
+
return;
|
|
11438
|
+
}
|
|
11439
|
+
logger.verbose(`Retrying commit message generation (attempt ${attempt}): ${error.message}`, { color: 'yellow' });
|
|
11209
11440
|
},
|
|
11210
11441
|
});
|
|
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
11442
|
},
|
|
11244
11443
|
noResult: async () => {
|
|
11245
11444
|
await noResult$2({ git, logger });
|
|
@@ -11280,28 +11479,6 @@ const builder$2 = (yargs) => {
|
|
|
11280
11479
|
return yargs.options(options$2).usage(getCommandUsageHeader(command$2));
|
|
11281
11480
|
};
|
|
11282
11481
|
|
|
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
11482
|
/**
|
|
11306
11483
|
* Executes a command as a Promise and returns the result.
|
|
11307
11484
|
*
|
|
@@ -13221,11 +13398,13 @@ function isObservable(obj) {
|
|
|
13221
13398
|
return !!obj && (obj instanceof Observable || (isFunction(obj.lift) && isFunction(obj.subscribe)));
|
|
13222
13399
|
}
|
|
13223
13400
|
|
|
13224
|
-
var EmptyError = createErrorClass(function (_super) {
|
|
13225
|
-
|
|
13226
|
-
|
|
13227
|
-
|
|
13228
|
-
|
|
13401
|
+
var EmptyError = createErrorClass(function (_super) {
|
|
13402
|
+
return function EmptyErrorImpl() {
|
|
13403
|
+
_super(this);
|
|
13404
|
+
this.name = 'EmptyError';
|
|
13405
|
+
this.message = 'no elements in sequence';
|
|
13406
|
+
};
|
|
13407
|
+
});
|
|
13229
13408
|
|
|
13230
13409
|
function lastValueFrom(source, config) {
|
|
13231
13410
|
var hasConfig = typeof config === 'object';
|
|
@@ -14512,23 +14691,183 @@ y.command(review.command, review.desc, review.builder, review.handler);
|
|
|
14512
14691
|
y.command(init.command, init.desc, init.builder, init.handler);
|
|
14513
14692
|
y.help().parse(process.argv.slice(2));
|
|
14514
14693
|
|
|
14694
|
+
/**
|
|
14695
|
+
* Edit a commit message with commitlint validation if config exists
|
|
14696
|
+
*/
|
|
14697
|
+
async function editCommitMessage(message, options) {
|
|
14698
|
+
// First, let the user edit the message
|
|
14699
|
+
const editedMessage = await editResult(message, options);
|
|
14700
|
+
// Then validate it against commitlint if config exists
|
|
14701
|
+
const { hasCommitlintConfig } = await Promise.resolve().then(function () { return hasCommitlintConfig$1; });
|
|
14702
|
+
const hasConfig = await hasCommitlintConfig();
|
|
14703
|
+
if (hasConfig) {
|
|
14704
|
+
const { validateCommitMessage } = await Promise.resolve().then(function () { return commitlintValidator; });
|
|
14705
|
+
const { handleValidationErrors } = await Promise.resolve().then(function () { return commitValidationHandler; });
|
|
14706
|
+
const validationResult = await validateCommitMessage(editedMessage);
|
|
14707
|
+
if (!validationResult.valid) {
|
|
14708
|
+
// Show validation errors and get user action
|
|
14709
|
+
const validationHandlerResult = await handleValidationErrors(editedMessage, validationResult, {
|
|
14710
|
+
logger: options.logger,
|
|
14711
|
+
interactive: options.interactive,
|
|
14712
|
+
openInEditor: options.openInEditor,
|
|
14713
|
+
});
|
|
14714
|
+
// Return the result from the validation handler
|
|
14715
|
+
return validationHandlerResult.message;
|
|
14716
|
+
}
|
|
14717
|
+
}
|
|
14718
|
+
return editedMessage;
|
|
14719
|
+
}
|
|
14720
|
+
|
|
14721
|
+
var editCommitMessage$1 = /*#__PURE__*/Object.freeze({
|
|
14722
|
+
__proto__: null,
|
|
14723
|
+
editCommitMessage: editCommitMessage
|
|
14724
|
+
});
|
|
14725
|
+
|
|
14726
|
+
/**
|
|
14727
|
+
* Custom error for commitlint validation failures
|
|
14728
|
+
* This allows the retry system to identify these errors specifically
|
|
14729
|
+
*/
|
|
14730
|
+
class CommitlintValidationError extends Error {
|
|
14731
|
+
constructor(message, validationResult, commitMessage) {
|
|
14732
|
+
super(message);
|
|
14733
|
+
this.name = 'CommitlintValidationError';
|
|
14734
|
+
this.validationResult = validationResult;
|
|
14735
|
+
this.commitMessage = commitMessage;
|
|
14736
|
+
}
|
|
14737
|
+
}
|
|
14515
14738
|
/**
|
|
14516
14739
|
* Load commitlint configuration
|
|
14517
14740
|
*/
|
|
14518
14741
|
async function loadCommitlintConfig() {
|
|
14519
|
-
|
|
14520
|
-
const
|
|
14521
|
-
|
|
14742
|
+
const projectRoot = findProjectRoot(process.cwd());
|
|
14743
|
+
const cwd = projectRoot || process.cwd();
|
|
14744
|
+
// @commitlint/load has issues with ESM configs (e.g. commitlint.config.js with `export default`).
|
|
14745
|
+
// Let's try to load them manually first.
|
|
14746
|
+
const esmConfigCandidates = COMMITLINT_CONFIG_FILES.filter((file) => file.endsWith('.js'));
|
|
14747
|
+
for (const configFile of esmConfigCandidates) {
|
|
14748
|
+
const configPath = path.join(cwd, configFile);
|
|
14749
|
+
if (fs.existsSync(configPath)) {
|
|
14750
|
+
try {
|
|
14751
|
+
const module = await import(url.pathToFileURL(configPath).href);
|
|
14752
|
+
if (module.default &&
|
|
14753
|
+
(Object.keys(module.default.rules || {}).length > 0 ||
|
|
14754
|
+
(module.default.extends && module.default.extends.length > 0))) {
|
|
14755
|
+
// We found a config, now let commitlint process it (for extends etc)
|
|
14756
|
+
return await core$1.load(module.default, { cwd });
|
|
14757
|
+
}
|
|
14758
|
+
}
|
|
14759
|
+
catch (error) {
|
|
14760
|
+
// Failed to import, maybe not an ESM file after all or syntax error.
|
|
14761
|
+
// We will let the standard load take a chance.
|
|
14762
|
+
}
|
|
14763
|
+
}
|
|
14764
|
+
}
|
|
14522
14765
|
try {
|
|
14523
|
-
//
|
|
14524
|
-
const config = await load();
|
|
14525
|
-
|
|
14766
|
+
// Let @commitlint/load try to find the config. This works for CJS, JSON, and YAML.
|
|
14767
|
+
const config = await core$1.load({}, { cwd });
|
|
14768
|
+
// Check if a real config was loaded.
|
|
14769
|
+
if (config.extends.length > 0 || Object.keys(config.rules).length > 0) {
|
|
14770
|
+
return config;
|
|
14771
|
+
}
|
|
14526
14772
|
}
|
|
14527
|
-
catch {
|
|
14528
|
-
//
|
|
14529
|
-
|
|
14530
|
-
|
|
14531
|
-
|
|
14773
|
+
catch (error) {
|
|
14774
|
+
// Could be an error parsing, or just not found. Fall through to default.
|
|
14775
|
+
}
|
|
14776
|
+
// If nothing worked, fallback to conventional config
|
|
14777
|
+
return core$1.load({
|
|
14778
|
+
extends: ['@commitlint/config-conventional'],
|
|
14779
|
+
});
|
|
14780
|
+
}
|
|
14781
|
+
/**
|
|
14782
|
+
* Format commitlint rules into a human-readable string for AI prompts
|
|
14783
|
+
*/
|
|
14784
|
+
function formatCommitlintRulesForPrompt(config) {
|
|
14785
|
+
if (!config.rules || Object.keys(config.rules).length === 0) {
|
|
14786
|
+
return '';
|
|
14787
|
+
}
|
|
14788
|
+
const ruleDescriptions = [];
|
|
14789
|
+
// Add information about extends if present
|
|
14790
|
+
if (config.extends && config.extends.length > 0) {
|
|
14791
|
+
ruleDescriptions.push(`Following ${config.extends.join(', ')} configuration`);
|
|
14792
|
+
}
|
|
14793
|
+
// Process key rules that affect commit message format
|
|
14794
|
+
const rules = config.rules;
|
|
14795
|
+
// Header length rules
|
|
14796
|
+
if (rules['header-max-length']) {
|
|
14797
|
+
const [level, , maxLength] = rules['header-max-length'];
|
|
14798
|
+
if (level > 0) {
|
|
14799
|
+
ruleDescriptions.push(`Header (title) must be ${maxLength} characters or less (including spaces)`);
|
|
14800
|
+
}
|
|
14801
|
+
}
|
|
14802
|
+
if (rules['header-min-length']) {
|
|
14803
|
+
const [level, , minLength] = rules['header-min-length'];
|
|
14804
|
+
if (level > 0) {
|
|
14805
|
+
ruleDescriptions.push(`Header (title) must be at least ${minLength} characters (including spaces)`);
|
|
14806
|
+
}
|
|
14807
|
+
}
|
|
14808
|
+
// Body length rules
|
|
14809
|
+
if (rules['body-max-line-length']) {
|
|
14810
|
+
const [level, , maxLength] = rules['body-max-line-length'];
|
|
14811
|
+
if (level > 0) {
|
|
14812
|
+
ruleDescriptions.push(`Body lines must be ${maxLength} characters or less (including spaces)`);
|
|
14813
|
+
}
|
|
14814
|
+
}
|
|
14815
|
+
// Type rules
|
|
14816
|
+
if (rules['type-enum']) {
|
|
14817
|
+
const [level, , allowedTypes] = rules['type-enum'];
|
|
14818
|
+
if (level > 0 && Array.isArray(allowedTypes)) {
|
|
14819
|
+
ruleDescriptions.push(`Allowed types: ${allowedTypes.join(', ')}`);
|
|
14820
|
+
}
|
|
14821
|
+
}
|
|
14822
|
+
// Case rules
|
|
14823
|
+
if (rules['type-case']) {
|
|
14824
|
+
const [level, , caseType] = rules['type-case'];
|
|
14825
|
+
if (level > 0) {
|
|
14826
|
+
ruleDescriptions.push(`Type must be ${caseType} case`);
|
|
14827
|
+
}
|
|
14828
|
+
}
|
|
14829
|
+
if (rules['subject-case']) {
|
|
14830
|
+
const [level, , caseType] = rules['subject-case'];
|
|
14831
|
+
if (level > 0) {
|
|
14832
|
+
ruleDescriptions.push(`Subject must be ${caseType} case`);
|
|
14833
|
+
}
|
|
14834
|
+
}
|
|
14835
|
+
// Scope rules
|
|
14836
|
+
if (rules['scope-enum']) {
|
|
14837
|
+
const [level, , allowedScopes] = rules['scope-enum'];
|
|
14838
|
+
if (level > 0 && Array.isArray(allowedScopes)) {
|
|
14839
|
+
ruleDescriptions.push(`Allowed scopes: ${allowedScopes.join(', ')}`);
|
|
14840
|
+
}
|
|
14841
|
+
}
|
|
14842
|
+
// Subject rules
|
|
14843
|
+
if (rules['subject-full-stop']) {
|
|
14844
|
+
const [level, condition] = rules['subject-full-stop'];
|
|
14845
|
+
if (level > 0) {
|
|
14846
|
+
const verb = condition === 'always' ? 'must' : 'must not';
|
|
14847
|
+
ruleDescriptions.push(`Subject ${verb} end with a period`);
|
|
14848
|
+
}
|
|
14849
|
+
}
|
|
14850
|
+
if (rules['subject-empty']) {
|
|
14851
|
+
const [level, condition] = rules['subject-empty'];
|
|
14852
|
+
if (level > 0) {
|
|
14853
|
+
const requirement = condition === 'never' ? 'must not be empty' : 'must be empty';
|
|
14854
|
+
ruleDescriptions.push(`Subject ${requirement}`);
|
|
14855
|
+
}
|
|
14856
|
+
}
|
|
14857
|
+
return ruleDescriptions.length > 0
|
|
14858
|
+
? `## Commitlint Rules\nYour commit message must follow these project-specific rules:\n${ruleDescriptions.map(rule => `- ${rule}`).join('\n')}\n`
|
|
14859
|
+
: '';
|
|
14860
|
+
}
|
|
14861
|
+
/**
|
|
14862
|
+
* Get commitlint rules context for prompt if config exists
|
|
14863
|
+
*/
|
|
14864
|
+
async function getCommitlintRulesContext() {
|
|
14865
|
+
try {
|
|
14866
|
+
const config = await loadCommitlintConfig();
|
|
14867
|
+
return formatCommitlintRulesForPrompt(config);
|
|
14868
|
+
}
|
|
14869
|
+
catch (error) {
|
|
14870
|
+
return '';
|
|
14532
14871
|
}
|
|
14533
14872
|
}
|
|
14534
14873
|
/**
|
|
@@ -14537,10 +14876,7 @@ async function loadCommitlintConfig() {
|
|
|
14537
14876
|
async function validateCommitMessage(message, options = {}) {
|
|
14538
14877
|
try {
|
|
14539
14878
|
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);
|
|
14879
|
+
const result = await core$1.lint(message, config.rules, options);
|
|
14544
14880
|
return {
|
|
14545
14881
|
valid: result.valid,
|
|
14546
14882
|
errors: result.errors.map((error) => error.message),
|
|
@@ -14558,6 +14894,9 @@ async function validateCommitMessage(message, options = {}) {
|
|
|
14558
14894
|
|
|
14559
14895
|
var commitlintValidator = /*#__PURE__*/Object.freeze({
|
|
14560
14896
|
__proto__: null,
|
|
14897
|
+
CommitlintValidationError: CommitlintValidationError,
|
|
14898
|
+
formatCommitlintRulesForPrompt: formatCommitlintRulesForPrompt,
|
|
14899
|
+
getCommitlintRulesContext: getCommitlintRulesContext,
|
|
14561
14900
|
loadCommitlintConfig: loadCommitlintConfig,
|
|
14562
14901
|
validateCommitMessage: validateCommitMessage
|
|
14563
14902
|
});
|
|
@@ -14594,29 +14933,45 @@ async function handleValidationErrors(message, validationResult, options) {
|
|
|
14594
14933
|
message: 'How would you like to proceed?:',
|
|
14595
14934
|
choices: [
|
|
14596
14935
|
{
|
|
14597
|
-
name: '
|
|
14598
|
-
value: '
|
|
14599
|
-
description: '
|
|
14936
|
+
name: 'Try 2 more attempts',
|
|
14937
|
+
value: 'retry',
|
|
14938
|
+
description: 'Let the AI try generating 2 more commit messages with error feedback',
|
|
14600
14939
|
},
|
|
14601
14940
|
{
|
|
14602
|
-
name: '
|
|
14603
|
-
value: '
|
|
14604
|
-
description: '
|
|
14941
|
+
name: 'Edit manually',
|
|
14942
|
+
value: 'edit',
|
|
14943
|
+
description: 'Edit the commit message manually to fix the issues',
|
|
14605
14944
|
},
|
|
14606
14945
|
{
|
|
14607
14946
|
name: 'Abort',
|
|
14608
14947
|
value: 'abort',
|
|
14609
|
-
description: 'Abort the commit',
|
|
14948
|
+
description: 'Abort the commit process',
|
|
14610
14949
|
},
|
|
14611
14950
|
],
|
|
14612
14951
|
});
|
|
14613
14952
|
switch (choice) {
|
|
14614
|
-
case '
|
|
14953
|
+
case 'edit': {
|
|
14615
14954
|
// Edit message manually
|
|
14616
14955
|
const editedMessage = await editResult(message, options);
|
|
14956
|
+
// Validate the manually edited message if commitlint config exists
|
|
14957
|
+
const { hasCommitlintConfig } = await Promise.resolve().then(function () { return hasCommitlintConfig$1; });
|
|
14958
|
+
const hasConfig = await hasCommitlintConfig();
|
|
14959
|
+
if (hasConfig) {
|
|
14960
|
+
const { validateCommitMessage } = await Promise.resolve().then(function () { return commitlintValidator; });
|
|
14961
|
+
const editedValidationResult = await validateCommitMessage(editedMessage);
|
|
14962
|
+
if (!editedValidationResult.valid) {
|
|
14963
|
+
// Show validation errors for the edited message
|
|
14964
|
+
options.logger.log('\nEdited commit message also has validation issues:', { color: 'yellow' });
|
|
14965
|
+
editedValidationResult.errors.forEach((error) => {
|
|
14966
|
+
options.logger.log(` • ${error}`, { color: 'red' });
|
|
14967
|
+
});
|
|
14968
|
+
// Recursively handle validation errors for the edited message
|
|
14969
|
+
return await handleValidationErrors(editedMessage, editedValidationResult, options);
|
|
14970
|
+
}
|
|
14971
|
+
}
|
|
14617
14972
|
return { message: editedMessage, action: 'edit' };
|
|
14618
14973
|
}
|
|
14619
|
-
case '
|
|
14974
|
+
case 'retry':
|
|
14620
14975
|
// Regenerate message
|
|
14621
14976
|
return { message, action: 'regenerate' };
|
|
14622
14977
|
default:
|