format-commit 0.4.0 → 1.0.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 +98 -22
- package/lib/ai-service.js +289 -0
- package/lib/commit.js +275 -76
- package/lib/create-branch.js +6 -25
- package/lib/default-config.json +7 -2
- package/lib/env-utils.js +109 -0
- package/lib/index.js +21 -14
- package/lib/options.json +27 -0
- package/lib/setup.js +169 -18
- package/lib/utils.js +115 -14
- package/package.json +11 -6
package/lib/options.json
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
{ "value": 2, "title": "type/scope/description" }
|
|
16
16
|
],
|
|
17
17
|
"versionChangeMode": [
|
|
18
|
+
{ "value": "ignore", "title": "never (ignore)" },
|
|
18
19
|
{ "value": "never", "title": "never (always ask)" },
|
|
19
20
|
{ "value": "releaseBranch", "title": "only on release branch" },
|
|
20
21
|
{ "value": "always" }
|
|
@@ -31,5 +32,31 @@
|
|
|
31
32
|
{ "value": "premajor" },
|
|
32
33
|
{ "value": "prerelease", "title": "prerelease <tag>" },
|
|
33
34
|
{ "value": "from-git" }
|
|
35
|
+
],
|
|
36
|
+
"aiProviders": [
|
|
37
|
+
{
|
|
38
|
+
"value": "anthropic",
|
|
39
|
+
"title": "Anthropic (Claude)",
|
|
40
|
+
"models": [
|
|
41
|
+
{ "value": "claude-haiku-4-5", "title": "Claude Haiku 4.5 (Fast & Cheap)" },
|
|
42
|
+
{ "value": "claude-sonnet-4-5", "title": "Claude Sonnet 4.5 (Balanced)" }
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"value": "openai",
|
|
47
|
+
"title": "OpenAI (GPT)",
|
|
48
|
+
"models": [
|
|
49
|
+
{ "value": "gpt-4o-mini", "title": "GPT-4o Mini (Fast & Cheap)" },
|
|
50
|
+
{ "value": "gpt-4o", "title": "GPT-4o (More Capable)" }
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"value": "google",
|
|
55
|
+
"title": "Google (Gemini)",
|
|
56
|
+
"models": [
|
|
57
|
+
{ "value": "gemini-2.0-flash", "title": "Gemini 2.0 Flash (Fast & Free)" },
|
|
58
|
+
{ "value": "gemini-1.5-pro", "title": "Gemini 1.5 Pro (More Capable)" }
|
|
59
|
+
]
|
|
60
|
+
}
|
|
34
61
|
]
|
|
35
62
|
}
|
package/lib/setup.js
CHANGED
|
@@ -1,18 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
import prompts from 'prompts';
|
|
2
|
+
import fs, { readFileSync } from 'fs';
|
|
3
|
+
import * as utils from './utils.js';
|
|
4
|
+
import * as envUtils from './env-utils.js';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { dirname, join } from 'path';
|
|
2
7
|
|
|
3
|
-
const prompts = require('prompts');
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const utils = require('./utils');
|
|
6
|
-
const defaultConfig = require('./default-config.json');
|
|
7
|
-
const options = require('./options.json');
|
|
8
8
|
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = dirname(__filename);
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
const defaultConfig = JSON.parse(
|
|
13
|
+
readFileSync(join(__dirname, 'default-config.json'), 'utf-8')
|
|
14
|
+
);
|
|
15
|
+
const options = JSON.parse(
|
|
16
|
+
readFileSync(join(__dirname, 'options.json'), 'utf-8')
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
export default async function setup(askForCommitAfter) {
|
|
11
21
|
utils.log('create config file');
|
|
12
22
|
|
|
13
|
-
|
|
14
|
-
* Get current git branch to pre-fill release branch option
|
|
15
|
-
*/
|
|
23
|
+
// Get current git branch to pre-fill release branch option
|
|
16
24
|
const currentBranch = utils.getCurrentBranch();
|
|
17
25
|
|
|
18
26
|
let cancelled = false;
|
|
@@ -61,6 +69,114 @@ module.exports = async (askForCommitAfter) => {
|
|
|
61
69
|
message: 'Display all npm version types?',
|
|
62
70
|
initial: defaultConfig.showAllVersionTypes,
|
|
63
71
|
},
|
|
72
|
+
{
|
|
73
|
+
type: 'confirm',
|
|
74
|
+
name: 'enableAI',
|
|
75
|
+
message: 'Enable AI commit title suggestions? (API key required)',
|
|
76
|
+
initial: false,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
type: prev => prev ? 'select' : null,
|
|
80
|
+
name: 'aiProvider',
|
|
81
|
+
message: 'AI provider',
|
|
82
|
+
choices: options.aiProviders,
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
type: prev => prev ? 'select' : null,
|
|
86
|
+
name: 'aiModel',
|
|
87
|
+
message: 'AI model',
|
|
88
|
+
choices: (prev, values) => {
|
|
89
|
+
const provider = options.aiProviders.find(p => p.value === values.aiProvider);
|
|
90
|
+
return provider ? provider.models : [];
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
type: (prev, values) => values.enableAI ? 'text' : null,
|
|
95
|
+
name: 'envPath',
|
|
96
|
+
message: 'Path to .env file for API key (will be created if doesn\'t exist)',
|
|
97
|
+
initial: '.env',
|
|
98
|
+
validate: (val) => {
|
|
99
|
+
if (!val || val.trim().length === 0) {
|
|
100
|
+
return 'Please provide a .env file path';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const envPath = val.trim();
|
|
104
|
+
const envExists = fs.existsSync(envPath);
|
|
105
|
+
|
|
106
|
+
// If .env exists, check that it is in .gitignore
|
|
107
|
+
if (envExists && !envUtils.isInGitignore(envPath)) {
|
|
108
|
+
return `${envPath} must be in .gitignore for security. Please add it first.`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return true;
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
type: (prev, values) => {
|
|
116
|
+
if (!values.enableAI) { return null; }
|
|
117
|
+
return fs.existsSync(values.envPath) ? 'text' : null;
|
|
118
|
+
},
|
|
119
|
+
name: 'envKeyName',
|
|
120
|
+
message: 'Name of the API key variable in .env (leave empty to create new)',
|
|
121
|
+
initial: (prev, values) => `${values.aiProvider.toUpperCase()}_API_KEY`,
|
|
122
|
+
validate: (val) => {
|
|
123
|
+
if (!val || val.trim().length === 0) {
|
|
124
|
+
return 'Please provide a variable name';
|
|
125
|
+
}
|
|
126
|
+
return true;
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
type: (prev, values) => {
|
|
131
|
+
if (!values.enableAI) { return null; }
|
|
132
|
+
|
|
133
|
+
const keyName = values.envKeyName || `${values.aiProvider.toUpperCase()}_API_KEY`;
|
|
134
|
+
const keyExists = envUtils.keyExistsInEnv(values.envPath, keyName);
|
|
135
|
+
|
|
136
|
+
if (keyExists) {
|
|
137
|
+
return 'confirm';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return null;
|
|
141
|
+
},
|
|
142
|
+
name: 'updateApiKey',
|
|
143
|
+
message: (prev, values) => {
|
|
144
|
+
const keyName = values.envKeyName || `${values.aiProvider.toUpperCase()}_API_KEY`;
|
|
145
|
+
const keyExists = envUtils.keyExistsInEnv(values.envPath, keyName);
|
|
146
|
+
|
|
147
|
+
if (keyExists) {
|
|
148
|
+
return `${keyName} already exists in ${values.envPath}. Update it?`;
|
|
149
|
+
}
|
|
150
|
+
const providerNames = { anthropic: 'Anthropic', openai: 'OpenAI', google: 'Google' };
|
|
151
|
+
return `Enter your ${providerNames[values.aiProvider] || values.aiProvider} API key`;
|
|
152
|
+
},
|
|
153
|
+
initial: false,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
type: (prev, values) => {
|
|
157
|
+
if (!values.enableAI) { return null; }
|
|
158
|
+
|
|
159
|
+
const keyName = values.envKeyName || `${values.aiProvider.toUpperCase()}_API_KEY`;
|
|
160
|
+
const keyExists = envUtils.keyExistsInEnv(values.envPath, keyName);
|
|
161
|
+
|
|
162
|
+
if (!keyExists || prev === true) {
|
|
163
|
+
return 'password';
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return null;
|
|
167
|
+
},
|
|
168
|
+
name: 'apiKey',
|
|
169
|
+
message: (prev, values) => {
|
|
170
|
+
const providerNames = { anthropic: 'Anthropic', openai: 'OpenAI', google: 'Google' };
|
|
171
|
+
return `Enter your ${providerNames[values.aiProvider] || values.aiProvider} API key`;
|
|
172
|
+
},
|
|
173
|
+
validate: (val) => {
|
|
174
|
+
if (!val || val.trim().length < 20) {
|
|
175
|
+
return 'Please provide a valid API key';
|
|
176
|
+
}
|
|
177
|
+
return true;
|
|
178
|
+
},
|
|
179
|
+
},
|
|
64
180
|
{
|
|
65
181
|
type: askForCommitAfter ? 'confirm' : null,
|
|
66
182
|
name: 'commitAfter',
|
|
@@ -74,17 +190,12 @@ module.exports = async (askForCommitAfter) => {
|
|
|
74
190
|
},
|
|
75
191
|
});
|
|
76
192
|
|
|
77
|
-
/**
|
|
78
|
-
* Handle prompt cancellation and stop setup execution
|
|
79
|
-
*/
|
|
80
193
|
if (cancelled) {
|
|
81
194
|
utils.log('setup cancelled', 'error');
|
|
82
195
|
return;
|
|
83
196
|
}
|
|
84
197
|
|
|
85
|
-
|
|
86
|
-
* Parse prompt data and write config file
|
|
87
|
-
*/
|
|
198
|
+
// Parse prompt data and write config file
|
|
88
199
|
const config = {
|
|
89
200
|
format: configChoices.format,
|
|
90
201
|
branchFormat: configChoices.branchFormat,
|
|
@@ -98,6 +209,45 @@ module.exports = async (askForCommitAfter) => {
|
|
|
98
209
|
releaseBranch: configChoices.releaseBranch,
|
|
99
210
|
showAllVersionTypes: configChoices.showAllVersionTypes,
|
|
100
211
|
};
|
|
212
|
+
|
|
213
|
+
// Handle AI configuration
|
|
214
|
+
if (configChoices.enableAI) {
|
|
215
|
+
const envPath = configChoices.envPath;
|
|
216
|
+
const keyName = configChoices.envKeyName || `${configChoices.aiProvider.toUpperCase()}_API_KEY`;
|
|
217
|
+
|
|
218
|
+
if (configChoices.apiKey) {
|
|
219
|
+
const saved = envUtils.setEnvKey(envPath, keyName, configChoices.apiKey);
|
|
220
|
+
if (!saved) {
|
|
221
|
+
utils.log(`Failed to save API key to ${envPath}`, 'error');
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
utils.log(`API key saved to ${envPath}`, 'success');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (!envUtils.isInGitignore(envPath)) {
|
|
228
|
+
const added = envUtils.addToGitignore(envPath);
|
|
229
|
+
if (added) {
|
|
230
|
+
utils.log(`${envPath} added to .gitignore`, 'success');
|
|
231
|
+
} else {
|
|
232
|
+
utils.log(`Failed to add ${envPath} to .gitignore`, 'warning');
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
config.ai = {
|
|
237
|
+
enabled: true,
|
|
238
|
+
provider: configChoices.aiProvider,
|
|
239
|
+
model: configChoices.aiModel,
|
|
240
|
+
envPath: envPath,
|
|
241
|
+
envKeyName: keyName,
|
|
242
|
+
largeDiffTokenThreshold: defaultConfig.ai.largeDiffTokenThreshold,
|
|
243
|
+
};
|
|
244
|
+
} else {
|
|
245
|
+
config.ai = {
|
|
246
|
+
enabled: false,
|
|
247
|
+
largeDiffTokenThreshold: defaultConfig.ai.largeDiffTokenThreshold,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
101
251
|
const parsedConfig = JSON.stringify(config, null, 2);
|
|
102
252
|
|
|
103
253
|
utils.log(`save ${options.configFile}.json file...`);
|
|
@@ -105,8 +255,9 @@ module.exports = async (askForCommitAfter) => {
|
|
|
105
255
|
try {
|
|
106
256
|
fs.writeFileSync(`./${options.configFile}.json`, parsedConfig);
|
|
107
257
|
utils.log('config file successfully created', 'success');
|
|
258
|
+
utils.log(`Customize default types and scopes in ${options.configFile}.json`);
|
|
108
259
|
} catch (err) {
|
|
109
|
-
utils.log(`
|
|
260
|
+
utils.log(`Unable to save config file: ${err.message}`, 'error');
|
|
110
261
|
return;
|
|
111
262
|
}
|
|
112
263
|
|
|
@@ -114,4 +265,4 @@ module.exports = async (askForCommitAfter) => {
|
|
|
114
265
|
config,
|
|
115
266
|
commitAfter: configChoices.commitAfter
|
|
116
267
|
};
|
|
117
|
-
}
|
|
268
|
+
}
|
package/lib/utils.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import kleur from 'kleur';
|
|
2
3
|
|
|
3
|
-
const { execSync } = require('child_process');
|
|
4
|
-
const { gray, bold, red, green, yellow } = require('kleur');
|
|
5
4
|
|
|
5
|
+
const { gray, bold, red, green, yellow } = kleur;
|
|
6
6
|
|
|
7
7
|
const askForVersion = (config, branch) => {
|
|
8
8
|
if (
|
|
@@ -18,11 +18,20 @@ const getCurrentBranch = () => {
|
|
|
18
18
|
return execSync('git rev-parse --abbrev-ref HEAD').toString().trim();
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
+
const hasStagedChanges = () => {
|
|
22
|
+
try {
|
|
23
|
+
const staged = execSync('git diff --cached --name-only', { encoding: 'utf-8' });
|
|
24
|
+
return staged.trim().length > 0;
|
|
25
|
+
} catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
21
30
|
const validCommitTitle = (title, lenMin, lenMax) => {
|
|
22
31
|
if (title.length < lenMin) {
|
|
23
|
-
return
|
|
32
|
+
return `Commit title too short (current ${title.length} - minimum ${lenMin})`;
|
|
24
33
|
} else if (title.length > lenMax) {
|
|
25
|
-
return
|
|
34
|
+
return `Commit title too long (current ${title.length} - maximum ${lenMax})`;
|
|
26
35
|
}
|
|
27
36
|
return true;
|
|
28
37
|
};
|
|
@@ -45,24 +54,31 @@ const validVersion = (version) => {
|
|
|
45
54
|
};
|
|
46
55
|
|
|
47
56
|
const formatCommitTitle = (type, title, format, scope = '*') => {
|
|
57
|
+
// Handle empty title
|
|
58
|
+
if (!title || title.trim().length === 0) {
|
|
59
|
+
return '';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const trimmedTitle = title.trim();
|
|
63
|
+
|
|
48
64
|
switch (format) {
|
|
49
65
|
case 1:
|
|
50
66
|
default:
|
|
51
|
-
return `(${type}) ${
|
|
67
|
+
return `(${type}) ${trimmedTitle[0].toUpperCase()}${trimmedTitle.slice(1).toLowerCase()}`;
|
|
52
68
|
case 2:
|
|
53
|
-
return `(${type}) ${
|
|
69
|
+
return `(${type}) ${trimmedTitle.toLowerCase()}`;
|
|
54
70
|
case 3:
|
|
55
|
-
return `${type}: ${
|
|
71
|
+
return `${type}: ${trimmedTitle[0].toUpperCase()}${trimmedTitle.slice(1).toLowerCase()}`;
|
|
56
72
|
case 4:
|
|
57
|
-
return `${type}: ${
|
|
73
|
+
return `${type}: ${trimmedTitle.toLowerCase()}`;
|
|
58
74
|
case 5:
|
|
59
|
-
return `${type}(${scope}) ${
|
|
75
|
+
return `${type}(${scope}) ${trimmedTitle[0].toUpperCase()}${trimmedTitle.slice(1).toLowerCase()}`;
|
|
60
76
|
case 6:
|
|
61
|
-
return `${type}(${scope}) ${
|
|
77
|
+
return `${type}(${scope}) ${trimmedTitle.toLowerCase()}`;
|
|
62
78
|
case 7:
|
|
63
|
-
return `${type}(${scope}): ${
|
|
79
|
+
return `${type}(${scope}): ${trimmedTitle[0].toUpperCase()}${trimmedTitle.slice(1).toLowerCase()}`;
|
|
64
80
|
case 8:
|
|
65
|
-
return `${type}(${scope}): ${
|
|
81
|
+
return `${type}(${scope}): ${trimmedTitle.toLowerCase()}`;
|
|
66
82
|
}
|
|
67
83
|
};
|
|
68
84
|
|
|
@@ -143,10 +159,94 @@ const checkBranchExists = (branchName) => {
|
|
|
143
159
|
}
|
|
144
160
|
};
|
|
145
161
|
|
|
162
|
+
/** Parse and validate commit title format, auto-correct case */
|
|
163
|
+
const parseAndNormalizeCommitTitle = (title, config) => {
|
|
164
|
+
let type, scope, message, detectedFormatGroup;
|
|
165
|
+
|
|
166
|
+
// Try different format patterns
|
|
167
|
+
const format7_8 = /^([^(]+)\(([^)]+)\):\s*(.+)$/; // type(scope): message
|
|
168
|
+
const format5_6 = /^([^(]+)\(([^)]+)\)\s+(.+)$/; // type(scope) message
|
|
169
|
+
const format3_4 = /^([^:]+):\s*(.+)$/; // type: message
|
|
170
|
+
const format1_2 = /^\(([^)]+)\)\s+(.+)$/; // (type) message
|
|
171
|
+
|
|
172
|
+
let match;
|
|
173
|
+
|
|
174
|
+
if ((match = title.match(format7_8))) {
|
|
175
|
+
[, type, scope, message] = match;
|
|
176
|
+
detectedFormatGroup = 'type(scope):';
|
|
177
|
+
} else if ((match = title.match(format5_6))) {
|
|
178
|
+
[, type, scope, message] = match;
|
|
179
|
+
detectedFormatGroup = 'type(scope)';
|
|
180
|
+
} else if ((match = title.match(format3_4))) {
|
|
181
|
+
[, type, message] = match;
|
|
182
|
+
detectedFormatGroup = 'type:';
|
|
183
|
+
} else if ((match = title.match(format1_2))) {
|
|
184
|
+
[, type, message] = match;
|
|
185
|
+
detectedFormatGroup = '(type)';
|
|
186
|
+
} else {
|
|
187
|
+
return { error: 'Invalid commit format. Expected format with type prefix.' };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Verify format matches config
|
|
191
|
+
let expectedFormatGroup;
|
|
192
|
+
if (config.format >= 7) {
|
|
193
|
+
expectedFormatGroup = 'type(scope):';
|
|
194
|
+
} else if (config.format >= 5) {
|
|
195
|
+
expectedFormatGroup = 'type(scope)';
|
|
196
|
+
} else if (config.format >= 3) {
|
|
197
|
+
expectedFormatGroup = 'type:';
|
|
198
|
+
} else {
|
|
199
|
+
expectedFormatGroup = '(type)';
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (detectedFormatGroup !== expectedFormatGroup) {
|
|
203
|
+
const exampleTitle = formatCommitTitle(
|
|
204
|
+
config.types[0].value,
|
|
205
|
+
'name',
|
|
206
|
+
config.format,
|
|
207
|
+
config.scopes?.[0]?.value
|
|
208
|
+
);
|
|
209
|
+
return { error: `Wrong format. Expected: "${exampleTitle}"` };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
type = type.trim();
|
|
213
|
+
message = message.trim();
|
|
214
|
+
if (scope) {
|
|
215
|
+
scope = scope.trim();
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Validate type exists (case-insensitive)
|
|
219
|
+
const validType = config.types.find(t => t.value.toLowerCase() === type.toLowerCase());
|
|
220
|
+
if (!validType) {
|
|
221
|
+
const validTypes = config.types.map(t => t.value).join(', ');
|
|
222
|
+
return { error: `Invalid type "${type}". Valid types: ${validTypes}` };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Validate scope if present (case-insensitive)
|
|
226
|
+
let validScope = scope;
|
|
227
|
+
if (scope) {
|
|
228
|
+
if (!config.scopes || config.scopes.length === 0) {
|
|
229
|
+
return { error: 'Scope not allowed in current format configuration' };
|
|
230
|
+
}
|
|
231
|
+
const foundScope = config.scopes.find(s => s.value.toLowerCase() === scope.toLowerCase());
|
|
232
|
+
if (!foundScope) {
|
|
233
|
+
const validScopes = config.scopes.map(s => s.value).join(', ');
|
|
234
|
+
return { error: `Invalid scope "${scope}". Valid scopes: ${validScopes}` };
|
|
235
|
+
}
|
|
236
|
+
validScope = foundScope.value;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Re-format with correct case
|
|
240
|
+
const normalized = formatCommitTitle(validType.value, message, config.format, validScope);
|
|
241
|
+
|
|
242
|
+
return { normalized };
|
|
243
|
+
};
|
|
244
|
+
|
|
146
245
|
|
|
147
|
-
|
|
246
|
+
export {
|
|
148
247
|
askForVersion,
|
|
149
248
|
getCurrentBranch,
|
|
249
|
+
hasStagedChanges,
|
|
150
250
|
validCommitTitle,
|
|
151
251
|
validCommitTitleSetupLength,
|
|
152
252
|
validBranchDescription,
|
|
@@ -154,6 +254,7 @@ module.exports = {
|
|
|
154
254
|
formatCommitTitle,
|
|
155
255
|
formatBranchName,
|
|
156
256
|
checkBranchExists,
|
|
257
|
+
parseAndNormalizeCommitTitle,
|
|
157
258
|
handleCmdExec,
|
|
158
259
|
log,
|
|
159
260
|
};
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "format-commit",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Lightweight CLI to standardize commit messages",
|
|
5
|
-
"license": "
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Lightweight CLI to standardize commit messages with AI-powered suggestions",
|
|
5
|
+
"license": "MIT",
|
|
6
6
|
"author": "Thomas BARKATS",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"git",
|
|
@@ -16,7 +16,12 @@
|
|
|
16
16
|
"workflow",
|
|
17
17
|
"tool",
|
|
18
18
|
"javascript",
|
|
19
|
-
"nodejs"
|
|
19
|
+
"nodejs",
|
|
20
|
+
"ai",
|
|
21
|
+
"gpt",
|
|
22
|
+
"claude",
|
|
23
|
+
"openai",
|
|
24
|
+
"anthropic"
|
|
20
25
|
],
|
|
21
26
|
"repository": {
|
|
22
27
|
"type": "git",
|
|
@@ -27,7 +32,7 @@
|
|
|
27
32
|
"url": "https://github.com/thomasbarkats/format-commit/issues"
|
|
28
33
|
},
|
|
29
34
|
"main": "./lib/index.js",
|
|
30
|
-
"type": "
|
|
35
|
+
"type": "module",
|
|
31
36
|
"engines": {
|
|
32
37
|
"node": ">=16.0.0"
|
|
33
38
|
},
|
|
@@ -45,7 +50,7 @@
|
|
|
45
50
|
"format-commit": "./lib/index.js"
|
|
46
51
|
},
|
|
47
52
|
"dependencies": {
|
|
48
|
-
"commander": "^14.0.
|
|
53
|
+
"commander": "^14.0.3",
|
|
49
54
|
"kleur": "^4.1.5",
|
|
50
55
|
"prompts": "^2.4.2"
|
|
51
56
|
},
|