@ksw8954/git-ai-commit 1.0.5 → 1.0.7
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/ai.d.ts.map +1 -1
- package/dist/commands/ai.js +11 -0
- package/dist/commands/ai.js.map +1 -1
- package/dist/commands/commit.d.ts +2 -1
- package/dist/commands/commit.d.ts.map +1 -1
- package/dist/commands/commit.js +140 -59
- package/dist/commands/commit.js.map +1 -1
- package/dist/commands/git.d.ts +5 -0
- package/dist/commands/git.d.ts.map +1 -1
- package/dist/commands/git.js +48 -0
- package/dist/commands/git.js.map +1 -1
- package/dist/commands/tag.d.ts +3 -0
- package/dist/commands/tag.d.ts.map +1 -1
- package/dist/commands/tag.js +138 -12
- package/dist/commands/tag.js.map +1 -1
- package/dist/prompts/commit.d.ts.map +1 -1
- package/dist/prompts/commit.js +3 -1
- package/dist/prompts/commit.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/ai.test.ts +38 -0
- package/src/__tests__/git.test.ts +101 -1
- package/src/__tests__/preCommit.test.ts +146 -0
- package/src/__tests__/tagCommand.test.ts +182 -1
- package/src/commands/ai.ts +13 -0
- package/src/commands/commit.ts +168 -69
- package/src/commands/git.ts +48 -0
- package/src/commands/tag.ts +159 -12
- package/src/prompts/commit.ts +3 -1
package/src/commands/tag.ts
CHANGED
|
@@ -67,6 +67,70 @@ export class TagCommand {
|
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
// Check if tag already exists locally
|
|
71
|
+
const localTagExists = await GitService.tagExists(trimmedName);
|
|
72
|
+
let remoteTagExists = false;
|
|
73
|
+
let wasTagReplaced = false;
|
|
74
|
+
|
|
75
|
+
if (localTagExists) {
|
|
76
|
+
console.log(`⚠️ Tag ${trimmedName} already exists locally.`);
|
|
77
|
+
const shouldDelete = await this.confirmTagDelete(trimmedName);
|
|
78
|
+
|
|
79
|
+
if (!shouldDelete) {
|
|
80
|
+
console.log('Tag creation cancelled by user.');
|
|
81
|
+
await LogService.append({
|
|
82
|
+
command: 'tag',
|
|
83
|
+
args: { name: trimmedName, ...options, apiKey: options.apiKey ? '***' : undefined },
|
|
84
|
+
status: 'cancelled',
|
|
85
|
+
details: 'user declined to replace existing tag'
|
|
86
|
+
});
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Check if tag exists on remote
|
|
91
|
+
remoteTagExists = await GitService.remoteTagExists(trimmedName);
|
|
92
|
+
|
|
93
|
+
if (remoteTagExists) {
|
|
94
|
+
console.log(`⚠️ Tag ${trimmedName} also exists on remote.`);
|
|
95
|
+
const shouldDeleteRemote = await this.confirmRemoteTagDelete(trimmedName);
|
|
96
|
+
|
|
97
|
+
if (shouldDeleteRemote) {
|
|
98
|
+
console.log(`Deleting remote tag ${trimmedName}...`);
|
|
99
|
+
const remoteDeleted = await GitService.deleteRemoteTag(trimmedName);
|
|
100
|
+
if (!remoteDeleted) {
|
|
101
|
+
console.error('❌ Failed to delete remote tag');
|
|
102
|
+
await LogService.append({
|
|
103
|
+
command: 'tag',
|
|
104
|
+
args: { name: trimmedName, ...options, apiKey: options.apiKey ? '***' : undefined },
|
|
105
|
+
status: 'failure',
|
|
106
|
+
details: 'remote tag deletion failed'
|
|
107
|
+
});
|
|
108
|
+
process.exit(1);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
console.log(`✅ Remote tag ${trimmedName} deleted`);
|
|
112
|
+
remoteTagExists = false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Delete local tag
|
|
117
|
+
console.log(`Deleting local tag ${trimmedName}...`);
|
|
118
|
+
const localDeleted = await GitService.deleteLocalTag(trimmedName);
|
|
119
|
+
if (!localDeleted) {
|
|
120
|
+
console.error('❌ Failed to delete local tag');
|
|
121
|
+
await LogService.append({
|
|
122
|
+
command: 'tag',
|
|
123
|
+
args: { name: trimmedName, ...options, apiKey: options.apiKey ? '***' : undefined },
|
|
124
|
+
status: 'failure',
|
|
125
|
+
details: 'local tag deletion failed'
|
|
126
|
+
});
|
|
127
|
+
process.exit(1);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
console.log(`✅ Local tag ${trimmedName} deleted`);
|
|
131
|
+
wasTagReplaced = true;
|
|
132
|
+
}
|
|
133
|
+
|
|
70
134
|
let tagMessage = options.message?.trim();
|
|
71
135
|
|
|
72
136
|
if (!tagMessage) {
|
|
@@ -168,20 +232,55 @@ export class TagCommand {
|
|
|
168
232
|
const shouldPush = await this.confirmTagPush(trimmedName);
|
|
169
233
|
|
|
170
234
|
if (shouldPush) {
|
|
171
|
-
|
|
172
|
-
const
|
|
235
|
+
// If tag was replaced or remote tag still exists, use force push
|
|
236
|
+
const needsForcePush = wasTagReplaced || remoteTagExists;
|
|
237
|
+
|
|
238
|
+
if (needsForcePush) {
|
|
239
|
+
console.log(`⚠️ Tag ${trimmedName} exists on remote. Force push is required.`);
|
|
240
|
+
const shouldForcePush = await this.confirmForcePush(trimmedName);
|
|
241
|
+
|
|
242
|
+
if (!shouldForcePush) {
|
|
243
|
+
console.log('Tag push cancelled by user.');
|
|
244
|
+
await LogService.append({
|
|
245
|
+
command: 'tag',
|
|
246
|
+
args: { name: trimmedName, ...options, apiKey: options.apiKey ? '***' : undefined },
|
|
247
|
+
status: 'cancelled',
|
|
248
|
+
details: 'user declined force push'
|
|
249
|
+
});
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
173
252
|
|
|
174
|
-
|
|
175
|
-
|
|
253
|
+
console.log(`Force pushing tag ${trimmedName} to remote...`);
|
|
254
|
+
const pushSuccess = await GitService.forcePushTag(trimmedName);
|
|
255
|
+
|
|
256
|
+
if (pushSuccess) {
|
|
257
|
+
console.log(`✅ Tag ${trimmedName} force pushed successfully!`);
|
|
258
|
+
} else {
|
|
259
|
+
console.error('❌ Failed to force push tag to remote');
|
|
260
|
+
await LogService.append({
|
|
261
|
+
command: 'tag',
|
|
262
|
+
args: { name: trimmedName, ...options, apiKey: options.apiKey ? '***' : undefined },
|
|
263
|
+
status: 'failure',
|
|
264
|
+
details: 'tag force push failed'
|
|
265
|
+
});
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
176
268
|
} else {
|
|
177
|
-
console.
|
|
178
|
-
await
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
269
|
+
console.log(`Pushing tag ${trimmedName} to remote...`);
|
|
270
|
+
const pushSuccess = await GitService.pushTag(trimmedName);
|
|
271
|
+
|
|
272
|
+
if (pushSuccess) {
|
|
273
|
+
console.log(`✅ Tag ${trimmedName} pushed successfully!`);
|
|
274
|
+
} else {
|
|
275
|
+
console.error('❌ Failed to push tag to remote');
|
|
276
|
+
await LogService.append({
|
|
277
|
+
command: 'tag',
|
|
278
|
+
args: { name: trimmedName, ...options, apiKey: options.apiKey ? '***' : undefined },
|
|
279
|
+
status: 'failure',
|
|
280
|
+
details: 'tag push failed'
|
|
281
|
+
});
|
|
282
|
+
process.exit(1);
|
|
283
|
+
}
|
|
185
284
|
}
|
|
186
285
|
}
|
|
187
286
|
|
|
@@ -224,6 +323,54 @@ export class TagCommand {
|
|
|
224
323
|
return normalized === 'y' || normalized === 'yes';
|
|
225
324
|
}
|
|
226
325
|
|
|
326
|
+
private async confirmTagDelete(tagName: string): Promise<boolean> {
|
|
327
|
+
const rl = readline.createInterface({
|
|
328
|
+
input: process.stdin,
|
|
329
|
+
output: process.stdout
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const answer: string = await new Promise(resolve => {
|
|
333
|
+
rl.question(`Delete existing tag ${tagName} and create a new one? (y/n): `, resolve);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
rl.close();
|
|
337
|
+
|
|
338
|
+
const normalized = answer.trim().toLowerCase();
|
|
339
|
+
return normalized === 'y' || normalized === 'yes';
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
private async confirmRemoteTagDelete(tagName: string): Promise<boolean> {
|
|
343
|
+
const rl = readline.createInterface({
|
|
344
|
+
input: process.stdin,
|
|
345
|
+
output: process.stdout
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const answer: string = await new Promise(resolve => {
|
|
349
|
+
rl.question(`Also delete remote tag ${tagName}? (y/n): `, resolve);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
rl.close();
|
|
353
|
+
|
|
354
|
+
const normalized = answer.trim().toLowerCase();
|
|
355
|
+
return normalized === 'y' || normalized === 'yes';
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
private async confirmForcePush(tagName: string): Promise<boolean> {
|
|
359
|
+
const rl = readline.createInterface({
|
|
360
|
+
input: process.stdin,
|
|
361
|
+
output: process.stdout
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
const answer: string = await new Promise(resolve => {
|
|
365
|
+
rl.question(`Force push tag ${tagName} to remote? (y/n): `, resolve);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
rl.close();
|
|
369
|
+
|
|
370
|
+
const normalized = answer.trim().toLowerCase();
|
|
371
|
+
return normalized === 'y' || normalized === 'yes';
|
|
372
|
+
}
|
|
373
|
+
|
|
227
374
|
getCommand(): Command {
|
|
228
375
|
return this.program;
|
|
229
376
|
}
|
package/src/prompts/commit.ts
CHANGED
|
@@ -46,7 +46,9 @@ ${customInstructions}
|
|
|
46
46
|
## CRITICAL: Commit Message Output Rules
|
|
47
47
|
- DO NOT include any memory bank status indicators like "[Memory Bank: Active]" or "[Memory Bank: Missing]"
|
|
48
48
|
- DO NOT include any task-specific formatting or artifacts from other rules
|
|
49
|
+
- DO NOT use any Markdown styling (no **bold**, __underline__, \`code\`, links, or emojis) in the commit header, body, or footer
|
|
49
50
|
- ONLY Generate a clean conventional commit message as specified below
|
|
51
|
+
- Output exactly ONE conventional commit message. Choose a single primary type; never return multiple headers or multiple type prefixes.
|
|
50
52
|
|
|
51
53
|
${gitContext}
|
|
52
54
|
|
|
@@ -92,7 +94,7 @@ ${footerGuidelines}
|
|
|
92
94
|
|
|
93
95
|
## Analysis Instructions
|
|
94
96
|
When analyzing staged changes:
|
|
95
|
-
1. Determine Primary Type based on the nature of changes
|
|
97
|
+
1. Determine a single Primary Type based on the dominant nature of the changes (if multiple types apply, pick the most impactful one and stick to it)
|
|
96
98
|
2. Identify Scope from modified directories or modules
|
|
97
99
|
3. Craft Description focusing on the most significant change
|
|
98
100
|
4. Determine if there are Breaking Changes
|