@untools/commitgen 0.2.6 ā 0.3.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/dist/commands/login.d.ts +1 -0
- package/dist/commands/login.js +91 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/index.js +21 -1
- package/dist/index.js.map +1 -1
- package/dist/providers/commitgen.d.ts +9 -0
- package/dist/providers/commitgen.js +77 -0
- package/dist/providers/commitgen.js.map +1 -0
- package/dist/providers/index.js +3 -11
- package/dist/providers/index.js.map +1 -1
- package/dist/types.d.ts +8 -1
- package/package.json +4 -4
- package/README.md +0 -734
- package/dist/package.json +0 -70
- package/dist/src/commands/configure.d.ts +0 -1
- package/dist/src/commands/configure.js +0 -96
- package/dist/src/commands/configure.js.map +0 -1
- package/dist/src/config.d.ts +0 -15
- package/dist/src/config.js +0 -56
- package/dist/src/config.js.map +0 -1
- package/dist/src/index.d.ts +0 -2
- package/dist/src/index.js +0 -649
- package/dist/src/index.js.map +0 -1
- package/dist/src/providers/base.d.ts +0 -9
- package/dist/src/providers/base.js +0 -107
- package/dist/src/providers/base.js.map +0 -1
- package/dist/src/providers/index.d.ts +0 -4
- package/dist/src/providers/index.js +0 -41
- package/dist/src/providers/index.js.map +0 -1
- package/dist/src/providers/vercel-google.d.ts +0 -10
- package/dist/src/providers/vercel-google.js +0 -113
- package/dist/src/providers/vercel-google.js.map +0 -1
- package/dist/src/types.d.ts +0 -46
- package/dist/src/types.js +0 -4
- package/dist/src/types.js.map +0 -1
- package/dist/src/utils/commit-history.d.ts +0 -35
- package/dist/src/utils/commit-history.js +0 -165
- package/dist/src/utils/commit-history.js.map +0 -1
- package/dist/src/utils/issue-tracker.d.ts +0 -43
- package/dist/src/utils/issue-tracker.js +0 -207
- package/dist/src/utils/issue-tracker.js.map +0 -1
- package/dist/src/utils/loading.d.ts +0 -14
- package/dist/src/utils/loading.js +0 -72
- package/dist/src/utils/loading.js.map +0 -1
- package/dist/src/utils/multi-commit.d.ts +0 -40
- package/dist/src/utils/multi-commit.js +0 -274
- package/dist/src/utils/multi-commit.js.map +0 -1
package/dist/src/index.js
DELETED
|
@@ -1,649 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
-
};
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
// ./src/index.ts
|
|
8
|
-
const child_process_1 = require("child_process");
|
|
9
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
-
const commander_1 = require("commander");
|
|
11
|
-
const inquirer_1 = __importDefault(require("inquirer"));
|
|
12
|
-
const config_1 = require("./config");
|
|
13
|
-
const configure_1 = require("./commands/configure");
|
|
14
|
-
const providers_1 = require("./providers");
|
|
15
|
-
const commit_history_1 = require("./utils/commit-history");
|
|
16
|
-
const multi_commit_1 = require("./utils/multi-commit");
|
|
17
|
-
const issue_tracker_1 = require("./utils/issue-tracker");
|
|
18
|
-
const loading_1 = require("./utils/loading");
|
|
19
|
-
const package_json_1 = __importDefault(require("../package.json"));
|
|
20
|
-
const getVersionSimple = () => {
|
|
21
|
-
return package_json_1.default.version;
|
|
22
|
-
};
|
|
23
|
-
// Graceful shutdown handler
|
|
24
|
-
process.on("SIGINT", () => {
|
|
25
|
-
console.log(chalk_1.default.yellow("\n\nš Cancelled by user"));
|
|
26
|
-
process.exit(0);
|
|
27
|
-
});
|
|
28
|
-
process.on("SIGTERM", () => {
|
|
29
|
-
console.log(chalk_1.default.yellow("\n\nš Cancelled by user"));
|
|
30
|
-
process.exit(0);
|
|
31
|
-
});
|
|
32
|
-
/**
|
|
33
|
-
* Wrapper for inquirer prompts that handles SIGINT gracefully
|
|
34
|
-
*/
|
|
35
|
-
async function safePrompt(promptConfig) {
|
|
36
|
-
try {
|
|
37
|
-
const result = await inquirer_1.default.prompt(promptConfig);
|
|
38
|
-
return result;
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
if (error instanceof Error && error.message.includes("SIGINT")) {
|
|
42
|
-
console.log(chalk_1.default.yellow("\n\nš Cancelled by user"));
|
|
43
|
-
process.exit(0);
|
|
44
|
-
}
|
|
45
|
-
throw error;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
class CommitGen {
|
|
49
|
-
constructor() {
|
|
50
|
-
this.historyAnalyzer = new commit_history_1.CommitHistoryAnalyzer();
|
|
51
|
-
this.multiCommitAnalyzer = new multi_commit_1.MultiCommitAnalyzer();
|
|
52
|
-
this.issueTracker = new issue_tracker_1.IssueTrackerIntegration();
|
|
53
|
-
}
|
|
54
|
-
exec(cmd) {
|
|
55
|
-
try {
|
|
56
|
-
return (0, child_process_1.execSync)(cmd, {
|
|
57
|
-
encoding: "utf8",
|
|
58
|
-
stdio: ["pipe", "pipe", "ignore"],
|
|
59
|
-
}).trim();
|
|
60
|
-
}
|
|
61
|
-
catch (error) {
|
|
62
|
-
return "";
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
isGitRepo() {
|
|
66
|
-
return this.exec("git rev-parse --git-dir") !== "";
|
|
67
|
-
}
|
|
68
|
-
getStagedChanges() {
|
|
69
|
-
return this.exec("git diff --cached --stat");
|
|
70
|
-
}
|
|
71
|
-
getStagedDiff() {
|
|
72
|
-
return this.exec("git diff --cached");
|
|
73
|
-
}
|
|
74
|
-
getTrackedChanges() {
|
|
75
|
-
return this.exec("git diff --stat");
|
|
76
|
-
}
|
|
77
|
-
analyzeChanges() {
|
|
78
|
-
const staged = this.getStagedChanges();
|
|
79
|
-
const unstaged = this.getTrackedChanges();
|
|
80
|
-
const diff = this.getStagedDiff();
|
|
81
|
-
const files = [];
|
|
82
|
-
if (staged) {
|
|
83
|
-
const lines = staged.split("\n");
|
|
84
|
-
lines.forEach((line) => {
|
|
85
|
-
const match = line.match(/^\s*(.+?)\s+\|/);
|
|
86
|
-
if (match)
|
|
87
|
-
files.push(match[1]);
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
const additions = (diff.match(/^\+(?!\+)/gm) || []).length;
|
|
91
|
-
const deletions = (diff.match(/^-(?!-)/gm) || []).length;
|
|
92
|
-
return {
|
|
93
|
-
filesChanged: files,
|
|
94
|
-
additions,
|
|
95
|
-
deletions,
|
|
96
|
-
hasStaged: staged !== "",
|
|
97
|
-
hasUnstaged: unstaged !== "",
|
|
98
|
-
diff,
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
formatCommitMessage(msg) {
|
|
102
|
-
let result = `${msg.type}`;
|
|
103
|
-
if (msg.scope)
|
|
104
|
-
result += `(${msg.scope})`;
|
|
105
|
-
if (msg.breaking)
|
|
106
|
-
result += "!";
|
|
107
|
-
result += `: ${msg.subject}`;
|
|
108
|
-
if (msg.body)
|
|
109
|
-
result += `\n\n${msg.body}`;
|
|
110
|
-
if (msg.breaking)
|
|
111
|
-
result += `\n\nBREAKING CHANGE: Major version update required`;
|
|
112
|
-
return result;
|
|
113
|
-
}
|
|
114
|
-
displayAnalysis(analysis) {
|
|
115
|
-
console.log(chalk_1.default.cyan.bold("\nš Analysis:"));
|
|
116
|
-
console.log(chalk_1.default.gray(` Files changed: ${chalk_1.default.white(analysis.filesChanged.length)}`));
|
|
117
|
-
console.log(chalk_1.default.gray(` Additions: ${chalk_1.default.green(`+${analysis.additions}`)}`));
|
|
118
|
-
console.log(chalk_1.default.gray(` Deletions: ${chalk_1.default.red(`-${analysis.deletions}`)}`));
|
|
119
|
-
console.log(chalk_1.default.cyan.bold("\nš Changed files:"));
|
|
120
|
-
analysis.filesChanged.slice(0, 10).forEach((f) => {
|
|
121
|
-
const ext = f.split(".").pop();
|
|
122
|
-
const icon = this.getFileIcon(ext || "");
|
|
123
|
-
console.log(chalk_1.default.gray(` ${icon} ${f}`));
|
|
124
|
-
});
|
|
125
|
-
if (analysis.filesChanged.length > 10) {
|
|
126
|
-
console.log(chalk_1.default.gray(` ... and ${analysis.filesChanged.length - 10} more files`));
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
getFileIcon(ext) {
|
|
130
|
-
const icons = {
|
|
131
|
-
ts: "š",
|
|
132
|
-
js: "š",
|
|
133
|
-
tsx: "āļø",
|
|
134
|
-
jsx: "āļø",
|
|
135
|
-
json: "š",
|
|
136
|
-
md: "š",
|
|
137
|
-
css: "šØ",
|
|
138
|
-
scss: "šØ",
|
|
139
|
-
html: "š",
|
|
140
|
-
test: "š§Ŗ",
|
|
141
|
-
spec: "š§Ŗ",
|
|
142
|
-
};
|
|
143
|
-
return icons[ext] || "š";
|
|
144
|
-
}
|
|
145
|
-
hasEnvironmentApiKey(provider) {
|
|
146
|
-
switch (provider) {
|
|
147
|
-
case "vercel-google":
|
|
148
|
-
return !!process.env.GOOGLE_GENERATIVE_AI_API_KEY;
|
|
149
|
-
case "vercel-openai":
|
|
150
|
-
return !!process.env.OPENAI_API_KEY;
|
|
151
|
-
case "groq":
|
|
152
|
-
return !!process.env.GROQ_API_KEY;
|
|
153
|
-
case "openai":
|
|
154
|
-
return !!process.env.OPENAI_API_KEY;
|
|
155
|
-
case "google":
|
|
156
|
-
return !!process.env.GOOGLE_GENERATIVE_AI_API_KEY;
|
|
157
|
-
default:
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
combineCommitMessages(messages) {
|
|
162
|
-
const types = messages.map((m) => m.type);
|
|
163
|
-
const typeCount = types.reduce((acc, t) => {
|
|
164
|
-
acc[t] = (acc[t] || 0) + 1;
|
|
165
|
-
return acc;
|
|
166
|
-
}, {});
|
|
167
|
-
const mostCommonType = Object.entries(typeCount).sort((a, b) => b[1] - a[1])[0][0];
|
|
168
|
-
const scopes = messages.map((m) => m.scope).filter(Boolean);
|
|
169
|
-
const uniqueScopes = [...new Set(scopes)];
|
|
170
|
-
const scope = uniqueScopes.length > 0 ? uniqueScopes.slice(0, 2).join(", ") : undefined;
|
|
171
|
-
const subjects = messages.map((m) => m.subject);
|
|
172
|
-
const combinedSubject = subjects.join("; ");
|
|
173
|
-
const bodies = messages.map((m) => m.body).filter(Boolean);
|
|
174
|
-
const combinedBody = bodies.length > 0 ? bodies.join("\n\n") : undefined;
|
|
175
|
-
const hasBreaking = messages.some((m) => m.breaking);
|
|
176
|
-
return {
|
|
177
|
-
type: mostCommonType,
|
|
178
|
-
scope,
|
|
179
|
-
subject: combinedSubject,
|
|
180
|
-
body: combinedBody,
|
|
181
|
-
breaking: hasBreaking,
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
async run(options) {
|
|
185
|
-
console.log(chalk_1.default.bold.cyan("\nš CommitGen") +
|
|
186
|
-
chalk_1.default.gray(" - AI-Powered Commit Message Generator\n"));
|
|
187
|
-
if (!this.isGitRepo()) {
|
|
188
|
-
console.error(chalk_1.default.red("ā Error: Not a git repository"));
|
|
189
|
-
process.exit(1);
|
|
190
|
-
}
|
|
191
|
-
const analysis = this.analyzeChanges();
|
|
192
|
-
if (!analysis.hasStaged) {
|
|
193
|
-
console.log(chalk_1.default.yellow("ā ļø No staged changes found."));
|
|
194
|
-
if (analysis.hasUnstaged) {
|
|
195
|
-
console.log(chalk_1.default.blue("š” You have unstaged changes. Stage them with:") +
|
|
196
|
-
chalk_1.default.gray(" git add <files>"));
|
|
197
|
-
}
|
|
198
|
-
process.exit(0);
|
|
199
|
-
}
|
|
200
|
-
// Check for issue tracking integration
|
|
201
|
-
let issueRef = null;
|
|
202
|
-
if (options.linkIssues !== false) {
|
|
203
|
-
issueRef = this.issueTracker.extractIssueFromBranch();
|
|
204
|
-
if (issueRef) {
|
|
205
|
-
console.log(chalk_1.default.cyan(`\n${this.issueTracker.getIssueDisplay(issueRef)} detected`));
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
// Check if multi-commit mode should be suggested
|
|
209
|
-
if (options.multiCommit !== false &&
|
|
210
|
-
this.multiCommitAnalyzer.shouldSplit(analysis)) {
|
|
211
|
-
const result = await safePrompt([
|
|
212
|
-
{
|
|
213
|
-
type: "confirm",
|
|
214
|
-
name: "useMultiCommit",
|
|
215
|
-
message: chalk_1.default.yellow("š Multiple concerns detected. Split into separate commits?"),
|
|
216
|
-
default: true,
|
|
217
|
-
},
|
|
218
|
-
]);
|
|
219
|
-
if (!result)
|
|
220
|
-
return;
|
|
221
|
-
if (result.useMultiCommit) {
|
|
222
|
-
return this.runMultiCommit(analysis, options);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
this.displayAnalysis(analysis);
|
|
226
|
-
// Load commit history pattern for personalization
|
|
227
|
-
let historyPattern = null;
|
|
228
|
-
if (options.learnFromHistory !== false) {
|
|
229
|
-
historyPattern = await this.historyAnalyzer.getCommitPattern();
|
|
230
|
-
if (historyPattern) {
|
|
231
|
-
console.log(chalk_1.default.cyan("\nš Personalizing based on your commit history"));
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
let suggestions = [];
|
|
235
|
-
let usingFallback = false;
|
|
236
|
-
if (options.useAi) {
|
|
237
|
-
try {
|
|
238
|
-
const configManager = new config_1.ConfigManager();
|
|
239
|
-
let providerConfig = configManager.getProviderConfig();
|
|
240
|
-
// Override model if specified in options
|
|
241
|
-
if (options.model) {
|
|
242
|
-
providerConfig = { ...providerConfig, model: options.model };
|
|
243
|
-
console.log(chalk_1.default.blue(`šÆ Using model: ${options.model}`));
|
|
244
|
-
}
|
|
245
|
-
if (!providerConfig.apiKey &&
|
|
246
|
-
!this.hasEnvironmentApiKey(providerConfig.provider)) {
|
|
247
|
-
console.log(chalk_1.default.yellow("\nā ļø API key not found for the selected provider."));
|
|
248
|
-
const result = await safePrompt([
|
|
249
|
-
{
|
|
250
|
-
type: "confirm",
|
|
251
|
-
name: "shouldConfigure",
|
|
252
|
-
message: "Would you like to configure your API key now?",
|
|
253
|
-
default: true,
|
|
254
|
-
},
|
|
255
|
-
]);
|
|
256
|
-
if (!result)
|
|
257
|
-
return;
|
|
258
|
-
if (result.shouldConfigure) {
|
|
259
|
-
await (0, configure_1.configureCommand)();
|
|
260
|
-
providerConfig = configManager.getProviderConfig();
|
|
261
|
-
}
|
|
262
|
-
else {
|
|
263
|
-
console.log(chalk_1.default.gray("Falling back to rule-based suggestions...\n"));
|
|
264
|
-
suggestions = this.getFallbackSuggestions(analysis);
|
|
265
|
-
usingFallback = true;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
if (!usingFallback) {
|
|
269
|
-
const modelDisplay = providerConfig.model || "default";
|
|
270
|
-
const provider = (0, providers_1.createProvider)(providerConfig);
|
|
271
|
-
suggestions = await (0, loading_1.withLoading)(`Generating commit messages using ${providerConfig.provider} (${modelDisplay})...`, async () => await provider.generateCommitMessage(analysis), "Commit messages generated");
|
|
272
|
-
if (!suggestions || suggestions.length === 0) {
|
|
273
|
-
throw new Error("No suggestions generated");
|
|
274
|
-
}
|
|
275
|
-
// Personalize suggestions based on history
|
|
276
|
-
if (historyPattern) {
|
|
277
|
-
suggestions = suggestions.map((msg) => this.historyAnalyzer.personalizeCommitMessage(msg, historyPattern));
|
|
278
|
-
}
|
|
279
|
-
// Adjust type based on issue if available
|
|
280
|
-
if (issueRef) {
|
|
281
|
-
suggestions = suggestions.map((msg) => ({
|
|
282
|
-
...msg,
|
|
283
|
-
type: this.issueTracker.suggestTypeFromIssue(issueRef, msg.type),
|
|
284
|
-
}));
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
catch (error) {
|
|
289
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
290
|
-
// Check if it's an API overload error
|
|
291
|
-
if (errorMessage.includes("overloaded") ||
|
|
292
|
-
errorMessage.includes("503")) {
|
|
293
|
-
console.warn(chalk_1.default.yellow(`\nā ļø ${errorMessage}`));
|
|
294
|
-
console.log(chalk_1.default.blue("š” Tip: You can use 'commitgen --no-use-ai' to skip AI generation"));
|
|
295
|
-
}
|
|
296
|
-
else if (errorMessage.includes("API key")) {
|
|
297
|
-
console.warn(chalk_1.default.yellow(`\nā ļø ${errorMessage}`));
|
|
298
|
-
const result = await safePrompt([
|
|
299
|
-
{
|
|
300
|
-
type: "confirm",
|
|
301
|
-
name: "shouldReconfigure",
|
|
302
|
-
message: "Would you like to reconfigure your API key?",
|
|
303
|
-
default: true,
|
|
304
|
-
},
|
|
305
|
-
]);
|
|
306
|
-
if (!result)
|
|
307
|
-
return;
|
|
308
|
-
if (result.shouldReconfigure) {
|
|
309
|
-
await (0, configure_1.configureCommand)();
|
|
310
|
-
console.log(chalk_1.default.blue("\nš Please run the command again with your new configuration."));
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
else {
|
|
315
|
-
console.warn(chalk_1.default.yellow(`\nā ļø AI generation failed: ${errorMessage}`));
|
|
316
|
-
}
|
|
317
|
-
console.log(chalk_1.default.gray("\nFalling back to rule-based suggestions...\n"));
|
|
318
|
-
suggestions = this.getFallbackSuggestions(analysis);
|
|
319
|
-
usingFallback = true;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
else {
|
|
323
|
-
console.log(chalk_1.default.gray("\nš Using rule-based suggestions (AI disabled)\n"));
|
|
324
|
-
suggestions = this.getFallbackSuggestions(analysis);
|
|
325
|
-
usingFallback = true;
|
|
326
|
-
}
|
|
327
|
-
await this.commitInteractive(suggestions, analysis, issueRef, options);
|
|
328
|
-
}
|
|
329
|
-
async runMultiCommit(analysis, options) {
|
|
330
|
-
const groups = this.multiCommitAnalyzer.groupFiles(analysis);
|
|
331
|
-
console.log(chalk_1.default.cyan.bold(`\nš Splitting into ${groups.length} commits:\n`));
|
|
332
|
-
groups.forEach((group, i) => {
|
|
333
|
-
console.log(chalk_1.default.gray(`${i + 1}. ${group.reason}`));
|
|
334
|
-
console.log(chalk_1.default.gray(` Files: ${group.files.slice(0, 3).join(", ")}${group.files.length > 3 ? "..." : ""}`));
|
|
335
|
-
});
|
|
336
|
-
const result = await safePrompt([
|
|
337
|
-
{
|
|
338
|
-
type: "confirm",
|
|
339
|
-
name: "proceed",
|
|
340
|
-
message: "Proceed with multi-commit?",
|
|
341
|
-
default: true,
|
|
342
|
-
},
|
|
343
|
-
]);
|
|
344
|
-
if (!result)
|
|
345
|
-
return;
|
|
346
|
-
if (!result.proceed) {
|
|
347
|
-
console.log(chalk_1.default.yellow("\nCancelled. Falling back to single commit mode."));
|
|
348
|
-
return this.run({ ...options, multiCommit: false });
|
|
349
|
-
}
|
|
350
|
-
for (let i = 0; i < groups.length; i++) {
|
|
351
|
-
const group = groups[i];
|
|
352
|
-
console.log(chalk_1.default.cyan.bold(`\nš Commit ${i + 1}/${groups.length}: ${group.reason}`));
|
|
353
|
-
// Generate suggestions for this group
|
|
354
|
-
let suggestions = [group.suggestedMessage];
|
|
355
|
-
if (options.useAi) {
|
|
356
|
-
try {
|
|
357
|
-
const configManager = new config_1.ConfigManager();
|
|
358
|
-
let providerConfig = configManager.getProviderConfig();
|
|
359
|
-
// Override model if specified
|
|
360
|
-
if (options.model) {
|
|
361
|
-
providerConfig = { ...providerConfig, model: options.model };
|
|
362
|
-
}
|
|
363
|
-
if (providerConfig.apiKey ||
|
|
364
|
-
this.hasEnvironmentApiKey(providerConfig.provider)) {
|
|
365
|
-
const provider = (0, providers_1.createProvider)(providerConfig);
|
|
366
|
-
const loader = new loading_1.LoadingIndicator("Generating commit message...");
|
|
367
|
-
loader.start();
|
|
368
|
-
try {
|
|
369
|
-
suggestions = await provider.generateCommitMessage(group.analysis);
|
|
370
|
-
loader.succeed("Generated");
|
|
371
|
-
}
|
|
372
|
-
catch (err) {
|
|
373
|
-
loader.stop();
|
|
374
|
-
throw err;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
catch (error) {
|
|
379
|
-
console.log(chalk_1.default.gray("Using suggested message for this commit"));
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
await this.commitInteractive(suggestions, group.analysis, null, options, group.files);
|
|
383
|
-
}
|
|
384
|
-
console.log(chalk_1.default.green.bold("\nā
All commits completed!"));
|
|
385
|
-
}
|
|
386
|
-
async commitInteractive(suggestions, analysis, issueRef, options, specificFiles) {
|
|
387
|
-
console.log(chalk_1.default.cyan.bold("š” Suggested commit messages:\n"));
|
|
388
|
-
const choices = suggestions.map((s, i) => {
|
|
389
|
-
const formatted = this.formatCommitMessage(s);
|
|
390
|
-
const preview = formatted.split("\n")[0];
|
|
391
|
-
return {
|
|
392
|
-
name: `${chalk_1.default.gray(`${i + 1}.`)} ${preview}`,
|
|
393
|
-
value: i,
|
|
394
|
-
short: preview,
|
|
395
|
-
};
|
|
396
|
-
});
|
|
397
|
-
choices.push({
|
|
398
|
-
name: chalk_1.default.magenta("š Combine all suggestions"),
|
|
399
|
-
value: -1,
|
|
400
|
-
short: "Combined",
|
|
401
|
-
});
|
|
402
|
-
choices.push({
|
|
403
|
-
name: chalk_1.default.gray("āļø Write custom message"),
|
|
404
|
-
value: -2,
|
|
405
|
-
short: "Custom message",
|
|
406
|
-
});
|
|
407
|
-
const result = await safePrompt([
|
|
408
|
-
{
|
|
409
|
-
type: "list",
|
|
410
|
-
name: "selectedIndex",
|
|
411
|
-
message: "Choose a commit message:",
|
|
412
|
-
choices,
|
|
413
|
-
pageSize: 10,
|
|
414
|
-
},
|
|
415
|
-
]);
|
|
416
|
-
if (!result)
|
|
417
|
-
return;
|
|
418
|
-
const { selectedIndex } = result;
|
|
419
|
-
let commitMessage;
|
|
420
|
-
if (selectedIndex === -1) {
|
|
421
|
-
const combined = this.combineCommitMessages(suggestions);
|
|
422
|
-
const combinedFormatted = this.formatCommitMessage(combined);
|
|
423
|
-
console.log(chalk_1.default.cyan("\nš¦ Combined message:"));
|
|
424
|
-
console.log(chalk_1.default.white(combinedFormatted));
|
|
425
|
-
const actionResult = await safePrompt([
|
|
426
|
-
{
|
|
427
|
-
type: "list",
|
|
428
|
-
name: "action",
|
|
429
|
-
message: "What would you like to do?",
|
|
430
|
-
choices: [
|
|
431
|
-
{ name: "ā
Use this combined message", value: "use" },
|
|
432
|
-
{ name: "āļø Edit this message", value: "edit" },
|
|
433
|
-
{ name: "š Go back to suggestions", value: "back" },
|
|
434
|
-
],
|
|
435
|
-
},
|
|
436
|
-
]);
|
|
437
|
-
if (!actionResult)
|
|
438
|
-
return;
|
|
439
|
-
if (actionResult.action === "back") {
|
|
440
|
-
return this.commitInteractive(suggestions, analysis, issueRef, options, specificFiles);
|
|
441
|
-
}
|
|
442
|
-
else if (actionResult.action === "edit") {
|
|
443
|
-
const editResult = await safePrompt([
|
|
444
|
-
{
|
|
445
|
-
type: "input",
|
|
446
|
-
name: "edited",
|
|
447
|
-
message: "Edit commit message:",
|
|
448
|
-
default: combinedFormatted,
|
|
449
|
-
validate: (input) => {
|
|
450
|
-
if (!input.trim())
|
|
451
|
-
return "Commit message cannot be empty";
|
|
452
|
-
return true;
|
|
453
|
-
},
|
|
454
|
-
},
|
|
455
|
-
]);
|
|
456
|
-
if (!editResult)
|
|
457
|
-
return;
|
|
458
|
-
commitMessage = editResult.edited;
|
|
459
|
-
}
|
|
460
|
-
else {
|
|
461
|
-
commitMessage = combinedFormatted;
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
else if (selectedIndex === -2) {
|
|
465
|
-
const customResult = await safePrompt([
|
|
466
|
-
{
|
|
467
|
-
type: "input",
|
|
468
|
-
name: "customMessage",
|
|
469
|
-
message: "Enter your commit message:",
|
|
470
|
-
validate: (input) => {
|
|
471
|
-
if (!input.trim())
|
|
472
|
-
return "Commit message cannot be empty";
|
|
473
|
-
return true;
|
|
474
|
-
},
|
|
475
|
-
},
|
|
476
|
-
]);
|
|
477
|
-
if (!customResult)
|
|
478
|
-
return;
|
|
479
|
-
commitMessage = customResult.customMessage;
|
|
480
|
-
}
|
|
481
|
-
else {
|
|
482
|
-
let selected = suggestions[selectedIndex];
|
|
483
|
-
// Add issue reference if available
|
|
484
|
-
if (issueRef && options.linkIssues !== false) {
|
|
485
|
-
selected = this.issueTracker.appendIssueToCommit(selected, issueRef);
|
|
486
|
-
}
|
|
487
|
-
const formatted = this.formatCommitMessage(selected);
|
|
488
|
-
const actionResult = await safePrompt([
|
|
489
|
-
{
|
|
490
|
-
type: "list",
|
|
491
|
-
name: "action",
|
|
492
|
-
message: "What would you like to do?",
|
|
493
|
-
choices: [
|
|
494
|
-
{ name: "ā
Use this message", value: "use" },
|
|
495
|
-
{ name: "āļø Edit this message", value: "edit" },
|
|
496
|
-
{ name: "š Choose a different message", value: "back" },
|
|
497
|
-
],
|
|
498
|
-
},
|
|
499
|
-
]);
|
|
500
|
-
if (!actionResult)
|
|
501
|
-
return;
|
|
502
|
-
if (actionResult.action === "back") {
|
|
503
|
-
return this.commitInteractive(suggestions, analysis, issueRef, options, specificFiles);
|
|
504
|
-
}
|
|
505
|
-
else if (actionResult.action === "edit") {
|
|
506
|
-
const editResult = await safePrompt([
|
|
507
|
-
{
|
|
508
|
-
type: "input",
|
|
509
|
-
name: "edited",
|
|
510
|
-
message: "Edit commit message:",
|
|
511
|
-
default: formatted,
|
|
512
|
-
validate: (input) => {
|
|
513
|
-
if (!input.trim())
|
|
514
|
-
return "Commit message cannot be empty";
|
|
515
|
-
return true;
|
|
516
|
-
},
|
|
517
|
-
},
|
|
518
|
-
]);
|
|
519
|
-
if (!editResult)
|
|
520
|
-
return;
|
|
521
|
-
commitMessage = editResult.edited;
|
|
522
|
-
}
|
|
523
|
-
else {
|
|
524
|
-
commitMessage = formatted;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
if (!commitMessage.trim()) {
|
|
528
|
-
console.log(chalk_1.default.red("\nā Commit cancelled - empty message"));
|
|
529
|
-
return;
|
|
530
|
-
}
|
|
531
|
-
try {
|
|
532
|
-
let commitCmd = specificFiles
|
|
533
|
-
? `git commit ${specificFiles
|
|
534
|
-
.map((f) => `"${f}"`)
|
|
535
|
-
.join(" ")} -m "${commitMessage.replace(/"/g, '\\"')}"`
|
|
536
|
-
: `git commit -m "${commitMessage.replace(/"/g, '\\"')}"`;
|
|
537
|
-
if (options.noverify) {
|
|
538
|
-
commitCmd += " --no-verify";
|
|
539
|
-
}
|
|
540
|
-
this.exec(commitCmd);
|
|
541
|
-
console.log(chalk_1.default.green("\nā
Commit successful!"));
|
|
542
|
-
if (options.push && !specificFiles) {
|
|
543
|
-
console.log(chalk_1.default.blue("\nš¤ Pushing to remote..."));
|
|
544
|
-
const currentBranch = this.exec("git branch --show-current");
|
|
545
|
-
this.exec(`git push origin ${currentBranch}`);
|
|
546
|
-
console.log(chalk_1.default.green("ā
Pushed successfully!"));
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
catch (error) {
|
|
550
|
-
console.error(chalk_1.default.red("ā Commit failed:"), error);
|
|
551
|
-
process.exit(1);
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
getFallbackSuggestions(analysis) {
|
|
555
|
-
const { filesChanged, additions, deletions } = analysis;
|
|
556
|
-
const suggestions = [];
|
|
557
|
-
const hasTests = filesChanged.some((f) => f.includes("test") || f.includes("spec") || f.includes("__tests__"));
|
|
558
|
-
const hasDocs = filesChanged.some((f) => f.includes("README") || f.includes(".md"));
|
|
559
|
-
const hasConfig = filesChanged.some((f) => f.includes("config") ||
|
|
560
|
-
f.includes(".json") ||
|
|
561
|
-
f.includes("package.json"));
|
|
562
|
-
if (additions > deletions * 2 && additions > 20) {
|
|
563
|
-
suggestions.push({
|
|
564
|
-
type: "feat",
|
|
565
|
-
subject: `add new feature`,
|
|
566
|
-
});
|
|
567
|
-
}
|
|
568
|
-
if (deletions > additions * 2 && deletions > 20) {
|
|
569
|
-
suggestions.push({
|
|
570
|
-
type: "refactor",
|
|
571
|
-
subject: `remove unused code`,
|
|
572
|
-
});
|
|
573
|
-
}
|
|
574
|
-
if (hasTests) {
|
|
575
|
-
suggestions.push({
|
|
576
|
-
type: "test",
|
|
577
|
-
subject: `add tests`,
|
|
578
|
-
});
|
|
579
|
-
}
|
|
580
|
-
if (hasDocs) {
|
|
581
|
-
suggestions.push({
|
|
582
|
-
type: "docs",
|
|
583
|
-
subject: "update documentation",
|
|
584
|
-
});
|
|
585
|
-
}
|
|
586
|
-
if (hasConfig) {
|
|
587
|
-
suggestions.push({
|
|
588
|
-
type: "chore",
|
|
589
|
-
subject: "update configuration",
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
if (suggestions.length === 0) {
|
|
593
|
-
suggestions.push({
|
|
594
|
-
type: "feat",
|
|
595
|
-
subject: `add feature`,
|
|
596
|
-
}, {
|
|
597
|
-
type: "fix",
|
|
598
|
-
subject: `fix issue`,
|
|
599
|
-
}, {
|
|
600
|
-
type: "refactor",
|
|
601
|
-
subject: `refactor code`,
|
|
602
|
-
});
|
|
603
|
-
}
|
|
604
|
-
return suggestions.slice(0, 5);
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
// CLI setup
|
|
608
|
-
const program = new commander_1.Command();
|
|
609
|
-
program
|
|
610
|
-
.name("commitgen")
|
|
611
|
-
.description("AI-powered commit message generator for Git")
|
|
612
|
-
.version(getVersionSimple())
|
|
613
|
-
.option("-p, --push", "Push changes after committing")
|
|
614
|
-
.option("-n, --noverify", "Skip git hooks (--no-verify)")
|
|
615
|
-
.option("--use-ai", "Use AI generation (default: enabled)")
|
|
616
|
-
.option("--no-use-ai", "Disable AI generation, use rule-based suggestions only")
|
|
617
|
-
.option("-m, --multi-commit", "Enable multi-commit mode for atomic commits")
|
|
618
|
-
.option("--no-multi-commit", "Disable multi-commit mode")
|
|
619
|
-
.option("--no-history", "Disable commit history learning")
|
|
620
|
-
.option("--no-issues", "Disable issue tracker integration")
|
|
621
|
-
.option("--model <model>", "Specify AI model to use (e.g., gemini-1.5-flash, gemini-2.5-pro)")
|
|
622
|
-
.action(async (options) => {
|
|
623
|
-
const commitGen = new CommitGen();
|
|
624
|
-
// Default useAi to true if not explicitly set
|
|
625
|
-
if (options.useAi === undefined) {
|
|
626
|
-
options.useAi = true;
|
|
627
|
-
}
|
|
628
|
-
await commitGen.run(options);
|
|
629
|
-
});
|
|
630
|
-
program
|
|
631
|
-
.command("config")
|
|
632
|
-
.description("Configure AI provider and settings")
|
|
633
|
-
.action(configure_1.configureCommand);
|
|
634
|
-
program
|
|
635
|
-
.command("show-config")
|
|
636
|
-
.description("Show current configuration")
|
|
637
|
-
.action(() => {
|
|
638
|
-
const configManager = new config_1.ConfigManager();
|
|
639
|
-
const config = configManager.getProviderConfig();
|
|
640
|
-
console.log(chalk_1.default.cyan.bold("\nāļø Current Configuration\n"));
|
|
641
|
-
console.log(chalk_1.default.gray(`Provider: ${chalk_1.default.white(config.provider)}`));
|
|
642
|
-
console.log(chalk_1.default.gray(`Model: ${chalk_1.default.white(config.model || "default")}`));
|
|
643
|
-
console.log(chalk_1.default.gray(`API Key: ${config.apiKey ? chalk_1.default.green("configured") : chalk_1.default.red("not set")}`));
|
|
644
|
-
if (!config.apiKey) {
|
|
645
|
-
console.log(chalk_1.default.yellow("\nš” Run 'commitgen config' to set up your API key"));
|
|
646
|
-
}
|
|
647
|
-
});
|
|
648
|
-
program.parse();
|
|
649
|
-
//# sourceMappingURL=index.js.map
|