commitect 1.0.3 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/help.js +1 -1
- package/dist/index.js +1 -1
- package/dist/services/llm.d.ts +6 -1
- package/dist/services/llm.d.ts.map +1 -1
- package/dist/services/llm.js +176 -30
- package/dist/services/llm.js.map +1 -1
- package/package.json +1 -1
- package/public/banner.png +0 -0
- package/src/commands/help.ts +1 -1
- package/src/index.ts +1 -1
- package/src/services/llm.ts +263 -32
package/dist/commands/help.js
CHANGED
package/dist/index.js
CHANGED
package/dist/services/llm.d.ts
CHANGED
|
@@ -2,5 +2,10 @@ export interface CommitSuggestion {
|
|
|
2
2
|
intent: string;
|
|
3
3
|
message: string;
|
|
4
4
|
}
|
|
5
|
-
|
|
5
|
+
interface ChangeSummary {
|
|
6
|
+
total?: number;
|
|
7
|
+
renamed?: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function generateCommitMessage(diff: string, summary?: ChangeSummary): Promise<CommitSuggestion>;
|
|
10
|
+
export {};
|
|
6
11
|
//# sourceMappingURL=llm.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../../src/services/llm.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAID,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,
|
|
1
|
+
{"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../../src/services/llm.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAyBD,UAAU,aAAa;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAsF5G"}
|
package/dist/services/llm.js
CHANGED
|
@@ -2,7 +2,7 @@ import axios from 'axios';
|
|
|
2
2
|
import crypto from 'crypto';
|
|
3
3
|
import { commitCache } from '../utils/cache.js';
|
|
4
4
|
const API_ENDPOINT = 'http://commitintentdetector.runasp.net/api/Commit/analyze';
|
|
5
|
-
export async function generateCommitMessage(diff) {
|
|
5
|
+
export async function generateCommitMessage(diff, summary) {
|
|
6
6
|
const diffHash = crypto.createHash('sha1').update(diff).digest('hex');
|
|
7
7
|
// Check cache first
|
|
8
8
|
const cached = commitCache.get(diffHash);
|
|
@@ -68,7 +68,7 @@ export async function generateCommitMessage(diff) {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
console.warn('⚠ AI service unavailable, using fallback commit message.');
|
|
71
|
-
return generateFallbackCommit(diff);
|
|
71
|
+
return generateFallbackCommit(diff, summary);
|
|
72
72
|
}
|
|
73
73
|
function parseResponse(response) {
|
|
74
74
|
const lines = response.trim().split('\n');
|
|
@@ -90,37 +90,183 @@ function parseResponse(response) {
|
|
|
90
90
|
}
|
|
91
91
|
return { intent, message };
|
|
92
92
|
}
|
|
93
|
-
function
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
let
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
intent = 'Bug Fix';
|
|
100
|
-
message = 'fix reported issue';
|
|
101
|
-
}
|
|
102
|
-
else if (/(add|create|implement|introduce|new)/i.test(lowerDiff)) {
|
|
103
|
-
intent = 'Feature';
|
|
104
|
-
message = 'add new functionality';
|
|
105
|
-
}
|
|
106
|
-
else if (/(refactor|cleanup|restructure|optimize)/i.test(lowerDiff)) {
|
|
107
|
-
intent = 'Refactor';
|
|
108
|
-
message = 'refactor code structure';
|
|
93
|
+
function extractFilesFromDiff(diff) {
|
|
94
|
+
const files = new Set();
|
|
95
|
+
const regex = /^diff --git a\/(.+?) b\/(.+)$/gm;
|
|
96
|
+
let match;
|
|
97
|
+
while ((match = regex.exec(diff))) {
|
|
98
|
+
files.add(match[2]);
|
|
109
99
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
100
|
+
return [...files];
|
|
101
|
+
}
|
|
102
|
+
function isTrivialWhitespace(lines) {
|
|
103
|
+
return lines.every(l => /^[+-]\s*$/.test(l) ||
|
|
104
|
+
/^[+-]\s*[{}();,]*\s*$/.test(l));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Analyze the diff content for patterns
|
|
108
|
+
* @param diff - The git diff string
|
|
109
|
+
* @param summary - Optional summary with file change counts
|
|
110
|
+
* @returns Analysis results with detected patterns
|
|
111
|
+
*/
|
|
112
|
+
function analyzeDiff(diff, summary = {}) {
|
|
113
|
+
const lines = diff.split('\n');
|
|
114
|
+
const lowerDiff = diff.toLowerCase();
|
|
115
|
+
const files = extractFilesFromDiff(diff);
|
|
116
|
+
const addedLines = lines.filter(l => l.startsWith('+') && !l.startsWith('+++'));
|
|
117
|
+
const removedLines = lines.filter(l => l.startsWith('-') && !l.startsWith('---'));
|
|
118
|
+
const additions = addedLines.length;
|
|
119
|
+
const deletions = removedLines.length;
|
|
120
|
+
const matchesAny = (patterns, text) => patterns.some(p => p.test(text));
|
|
121
|
+
const patterns = {
|
|
122
|
+
bugFix: [/\b(fix(e[ds])?|bug|error|issue|crash|incorrect|fault)\b/i],
|
|
123
|
+
testFix: [/\b(fix|repair|correct).*(test|spec)\b/i],
|
|
124
|
+
refactor: [/\b(refactor|cleanup|simplify|restructure|reorganize)\b/i],
|
|
125
|
+
style: [/\b(format|lint|prettier|indent)\b/i]
|
|
126
|
+
};
|
|
127
|
+
const hasDocsChange = files.some(f => /\.(md|rst|txt)$/i.test(f) || /readme/i.test(f));
|
|
128
|
+
const hasTestChange = files.some(f => /(__tests__|\.test\.|\.spec\.)/i.test(f));
|
|
129
|
+
const hasConfigChange = files.some(f => /\.(json|ya?ml|env|toml)$/i.test(f));
|
|
130
|
+
const hasDependencyChange = files.some(f => /(package(-lock)?\.json|requirements\.txt|go\.mod|pom\.xml)/i.test(f));
|
|
131
|
+
const hasNewFunction = addedLines.some(l => /^\+\s*(export\s+)?(async\s+)?function\s+\w+/.test(l) ||
|
|
132
|
+
/^\+\s*(export\s+)?const\s+\w+\s*=\s*(async\s*)?\(/.test(l));
|
|
133
|
+
const hasNewClass = addedLines.some(l => /^\+\s*(export\s+)?class\s+\w+/.test(l));
|
|
134
|
+
const hasNewEndpoint = addedLines.some(l => /\b(app|router)\.(get|post|put|delete|patch)\b/i.test(l)) ||
|
|
135
|
+
/^\+\s*@(Get|Post|Put|Delete|Patch)\b/m.test(diff);
|
|
136
|
+
const hasNewComponent = files.some(f => /\.(jsx|tsx)$/i.test(f)) &&
|
|
137
|
+
addedLines.some(l => /^\+\s*(export\s+)?(function|const)\s+[A-Z]\w*/.test(l));
|
|
138
|
+
const hasWhitespaceOnly = additions + deletions > 0 &&
|
|
139
|
+
isTrivialWhitespace([...addedLines, ...removedLines]);
|
|
140
|
+
return {
|
|
141
|
+
hasBugFix: matchesAny(patterns.bugFix, diff),
|
|
142
|
+
hasTestFix: hasTestChange && matchesAny(patterns.testFix, diff),
|
|
143
|
+
hasNewFunction,
|
|
144
|
+
hasNewClass,
|
|
145
|
+
hasNewEndpoint,
|
|
146
|
+
hasNewComponent,
|
|
147
|
+
hasRefactor: matchesAny(patterns.refactor, diff),
|
|
148
|
+
hasRename: (summary.renamed ?? 0) > 0,
|
|
149
|
+
hasMovedCode: (summary.renamed ?? 0) > 0 && additions > 0 && deletions > 0,
|
|
150
|
+
hasDocsChange,
|
|
151
|
+
hasCommentChange: addedLines.some(l => /^\+\s*(\/\/|\/\*|\*)/.test(l)),
|
|
152
|
+
hasTestChange,
|
|
153
|
+
hasDeletions: deletions > 0,
|
|
154
|
+
hasStyleChange: matchesAny(patterns.style, diff),
|
|
155
|
+
hasWhitespaceOnly,
|
|
156
|
+
hasConfigChange,
|
|
157
|
+
hasDependencyChange,
|
|
158
|
+
additions,
|
|
159
|
+
deletions,
|
|
160
|
+
hasChanges: additions + deletions > 0
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Determine the commit intent type based on analysis
|
|
165
|
+
* @param analysis - Analysis results from analyzeDiff
|
|
166
|
+
* @param summary - Changes summary
|
|
167
|
+
* @returns The intent type string
|
|
168
|
+
*/
|
|
169
|
+
function determineIntent(analysis, summary = {}) {
|
|
170
|
+
if (!analysis.hasChanges)
|
|
171
|
+
return 'Chore';
|
|
172
|
+
if (analysis.hasBugFix || analysis.hasTestFix)
|
|
173
|
+
return 'Bug Fix';
|
|
174
|
+
if (analysis.hasTestChange && !analysis.hasBugFix)
|
|
175
|
+
return 'Test';
|
|
176
|
+
if (analysis.hasDocsChange)
|
|
177
|
+
return 'Documentation';
|
|
178
|
+
if (analysis.hasRefactor ||
|
|
179
|
+
analysis.hasMovedCode ||
|
|
180
|
+
(analysis.deletions > analysis.additions * 2))
|
|
181
|
+
return 'Refactor';
|
|
182
|
+
if (analysis.hasNewFunction ||
|
|
183
|
+
analysis.hasNewClass ||
|
|
184
|
+
analysis.hasNewComponent ||
|
|
185
|
+
analysis.hasNewEndpoint)
|
|
186
|
+
return 'Feature';
|
|
187
|
+
if (analysis.hasDependencyChange || analysis.hasConfigChange)
|
|
188
|
+
return 'Chore';
|
|
189
|
+
if (analysis.hasStyleChange || analysis.hasWhitespaceOnly)
|
|
190
|
+
return 'Style';
|
|
191
|
+
return 'Update';
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Generate a descriptive commit message based on analysis
|
|
195
|
+
* @param analysis - Analysis results from analyzeDiff
|
|
196
|
+
* @param intent - The determined intent type
|
|
197
|
+
* @returns A commit message string
|
|
198
|
+
*/
|
|
199
|
+
function generateMessage(analysis, intent, summary = {}) {
|
|
200
|
+
const fileCount = summary.total;
|
|
201
|
+
const hasFileCount = typeof fileCount === 'number' && fileCount > 0;
|
|
202
|
+
const fileWord = fileCount === 1 ? 'file' : 'files';
|
|
203
|
+
switch (intent) {
|
|
204
|
+
case 'Bug Fix':
|
|
205
|
+
return analysis.hasTestFix
|
|
206
|
+
? hasFileCount
|
|
207
|
+
? `fix failing tests in ${fileCount} ${fileWord}`
|
|
208
|
+
: 'fix failing tests'
|
|
209
|
+
: hasFileCount
|
|
210
|
+
? `fix issues in ${fileCount} ${fileWord}`
|
|
211
|
+
: 'fix issues';
|
|
212
|
+
case 'Feature':
|
|
213
|
+
if (analysis.hasNewEndpoint)
|
|
214
|
+
return 'add new API endpoints';
|
|
215
|
+
if (analysis.hasNewComponent)
|
|
216
|
+
return 'add new components';
|
|
217
|
+
if (analysis.hasNewClass || analysis.hasNewFunction)
|
|
218
|
+
return hasFileCount
|
|
219
|
+
? `add new functionality to ${fileCount} ${fileWord}`
|
|
220
|
+
: 'add new functionality';
|
|
221
|
+
return hasFileCount
|
|
222
|
+
? `implement new features in ${fileCount} ${fileWord}`
|
|
223
|
+
: 'implement new features';
|
|
224
|
+
case 'Refactor':
|
|
225
|
+
if (analysis.hasMovedCode)
|
|
226
|
+
return hasFileCount
|
|
227
|
+
? `restructure code in ${fileCount} ${fileWord}`
|
|
228
|
+
: 'restructure code';
|
|
229
|
+
if (analysis.deletions > analysis.additions * 2)
|
|
230
|
+
return hasFileCount
|
|
231
|
+
? `remove unused code from ${fileCount} ${fileWord}`
|
|
232
|
+
: 'remove unused code';
|
|
233
|
+
return hasFileCount
|
|
234
|
+
? `refactor code in ${fileCount} ${fileWord}`
|
|
235
|
+
: 'refactor code';
|
|
236
|
+
case 'Test':
|
|
237
|
+
return hasFileCount
|
|
238
|
+
? `add/update tests in ${fileCount} ${fileWord}`
|
|
239
|
+
: 'add/update tests';
|
|
240
|
+
case 'Chore':
|
|
241
|
+
if (analysis.hasDependencyChange)
|
|
242
|
+
return 'update dependencies';
|
|
243
|
+
if (analysis.hasConfigChange)
|
|
244
|
+
return 'update configuration files';
|
|
245
|
+
return 'update project configuration';
|
|
246
|
+
case 'Style':
|
|
247
|
+
return hasFileCount
|
|
248
|
+
? `format and style ${fileCount} ${fileWord}`
|
|
249
|
+
: 'format and style code';
|
|
250
|
+
case 'Documentation':
|
|
251
|
+
return hasFileCount
|
|
252
|
+
? fileCount === 1
|
|
253
|
+
? 'update documentation'
|
|
254
|
+
: `update documentation in ${fileCount} ${fileWord}`
|
|
255
|
+
: 'update documentation';
|
|
256
|
+
default:
|
|
257
|
+
return hasFileCount
|
|
258
|
+
? `update ${fileCount} ${fileWord}`
|
|
259
|
+
: 'update code';
|
|
121
260
|
}
|
|
261
|
+
}
|
|
262
|
+
function generateFallbackCommit(diff, summary) {
|
|
263
|
+
const diffHash = crypto.createHash('sha1').update(diff).digest('hex');
|
|
264
|
+
const analysis = analyzeDiff(diff, summary);
|
|
265
|
+
const intent = determineIntent(analysis, summary);
|
|
266
|
+
const message = generateMessage(analysis, intent, summary);
|
|
267
|
+
const result = { intent, message };
|
|
122
268
|
commitCache.set(diffHash, intent, message);
|
|
123
|
-
return
|
|
269
|
+
return result;
|
|
124
270
|
}
|
|
125
271
|
function sleep(ms) {
|
|
126
272
|
return new Promise(resolve => setTimeout(resolve, ms));
|
package/dist/services/llm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm.js","sourceRoot":"","sources":["../../src/services/llm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAqB,MAAM,OAAO,CAAC;AAC1C,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"llm.js","sourceRoot":"","sources":["../../src/services/llm.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
Binary file
|
package/src/commands/help.ts
CHANGED
package/src/index.ts
CHANGED
package/src/services/llm.ts
CHANGED
|
@@ -7,9 +7,37 @@ export interface CommitSuggestion {
|
|
|
7
7
|
message: string;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
interface DiffAnalysis {
|
|
11
|
+
hasBugFix: boolean;
|
|
12
|
+
hasTestFix: boolean;
|
|
13
|
+
hasNewFunction: boolean;
|
|
14
|
+
hasNewClass: boolean;
|
|
15
|
+
hasNewEndpoint: boolean;
|
|
16
|
+
hasNewComponent: boolean;
|
|
17
|
+
hasRefactor: boolean;
|
|
18
|
+
hasRename: boolean;
|
|
19
|
+
hasMovedCode: boolean;
|
|
20
|
+
hasDocsChange: boolean;
|
|
21
|
+
hasCommentChange: boolean;
|
|
22
|
+
hasTestChange: boolean;
|
|
23
|
+
hasDeletions: boolean;
|
|
24
|
+
hasStyleChange: boolean;
|
|
25
|
+
hasWhitespaceOnly: boolean;
|
|
26
|
+
hasConfigChange: boolean;
|
|
27
|
+
hasDependencyChange: boolean;
|
|
28
|
+
additions: number;
|
|
29
|
+
deletions: number;
|
|
30
|
+
hasChanges: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface ChangeSummary {
|
|
34
|
+
total?: number;
|
|
35
|
+
renamed?: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
10
38
|
const API_ENDPOINT = 'http://commitintentdetector.runasp.net/api/Commit/analyze';
|
|
11
39
|
|
|
12
|
-
export async function generateCommitMessage(diff: string): Promise<CommitSuggestion> {
|
|
40
|
+
export async function generateCommitMessage(diff: string, summary?: ChangeSummary): Promise<CommitSuggestion> {
|
|
13
41
|
const diffHash = crypto.createHash('sha1').update(diff).digest('hex');
|
|
14
42
|
|
|
15
43
|
// Check cache first
|
|
@@ -94,7 +122,7 @@ export async function generateCommitMessage(diff: string): Promise<CommitSuggest
|
|
|
94
122
|
}
|
|
95
123
|
|
|
96
124
|
console.warn('⚠ AI service unavailable, using fallback commit message.');
|
|
97
|
-
return generateFallbackCommit(diff);
|
|
125
|
+
return generateFallbackCommit(diff, summary);
|
|
98
126
|
}
|
|
99
127
|
|
|
100
128
|
function parseResponse(response: string): CommitSuggestion {
|
|
@@ -122,42 +150,245 @@ function parseResponse(response: string): CommitSuggestion {
|
|
|
122
150
|
return { intent, message };
|
|
123
151
|
}
|
|
124
152
|
|
|
125
|
-
function
|
|
126
|
-
const
|
|
153
|
+
function extractFilesFromDiff(diff: string): string[] {
|
|
154
|
+
const files = new Set<string>();
|
|
155
|
+
const regex = /^diff --git a\/(.+?) b\/(.+)$/gm;
|
|
156
|
+
let match;
|
|
157
|
+
while ((match = regex.exec(diff))) {
|
|
158
|
+
files.add(match[2]);
|
|
159
|
+
}
|
|
160
|
+
return [...files];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function isTrivialWhitespace(lines: string[]): boolean {
|
|
164
|
+
return lines.every(l =>
|
|
165
|
+
/^[+-]\s*$/.test(l) ||
|
|
166
|
+
/^[+-]\s*[{}();,]*\s*$/.test(l)
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Analyze the diff content for patterns
|
|
172
|
+
* @param diff - The git diff string
|
|
173
|
+
* @param summary - Optional summary with file change counts
|
|
174
|
+
* @returns Analysis results with detected patterns
|
|
175
|
+
*/
|
|
176
|
+
function analyzeDiff(diff: string, summary: ChangeSummary = {}): DiffAnalysis {
|
|
177
|
+
const lines = diff.split('\n');
|
|
127
178
|
const lowerDiff = diff.toLowerCase();
|
|
179
|
+
const files = extractFilesFromDiff(diff);
|
|
180
|
+
|
|
181
|
+
const addedLines = lines.filter(l => l.startsWith('+') && !l.startsWith('+++'));
|
|
182
|
+
const removedLines = lines.filter(l => l.startsWith('-') && !l.startsWith('---'));
|
|
183
|
+
|
|
184
|
+
const additions = addedLines.length;
|
|
185
|
+
const deletions = removedLines.length;
|
|
186
|
+
|
|
187
|
+
const matchesAny = (patterns: RegExp[], text: string): boolean =>
|
|
188
|
+
patterns.some(p => p.test(text));
|
|
189
|
+
|
|
190
|
+
const patterns = {
|
|
191
|
+
bugFix: [/\b(fix(e[ds])?|bug|error|issue|crash|incorrect|fault)\b/i],
|
|
192
|
+
testFix: [/\b(fix|repair|correct).*(test|spec)\b/i],
|
|
193
|
+
refactor: [/\b(refactor|cleanup|simplify|restructure|reorganize)\b/i],
|
|
194
|
+
style: [/\b(format|lint|prettier|indent)\b/i]
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const hasDocsChange = files.some(f =>
|
|
198
|
+
/\.(md|rst|txt)$/i.test(f) || /readme/i.test(f)
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
const hasTestChange = files.some(f =>
|
|
202
|
+
/(__tests__|\.test\.|\.spec\.)/i.test(f)
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
const hasConfigChange = files.some(f =>
|
|
206
|
+
/\.(json|ya?ml|env|toml)$/i.test(f)
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
const hasDependencyChange = files.some(f =>
|
|
210
|
+
/(package(-lock)?\.json|requirements\.txt|go\.mod|pom\.xml)/i.test(f)
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const hasNewFunction =
|
|
214
|
+
addedLines.some(l =>
|
|
215
|
+
/^\+\s*(export\s+)?(async\s+)?function\s+\w+/.test(l) ||
|
|
216
|
+
/^\+\s*(export\s+)?const\s+\w+\s*=\s*(async\s*)?\(/.test(l)
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
const hasNewClass =
|
|
220
|
+
addedLines.some(l =>
|
|
221
|
+
/^\+\s*(export\s+)?class\s+\w+/.test(l)
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const hasNewEndpoint =
|
|
225
|
+
addedLines.some(l =>
|
|
226
|
+
/\b(app|router)\.(get|post|put|delete|patch)\b/i.test(l)
|
|
227
|
+
) ||
|
|
228
|
+
/^\+\s*@(Get|Post|Put|Delete|Patch)\b/m.test(diff);
|
|
229
|
+
|
|
230
|
+
const hasNewComponent =
|
|
231
|
+
files.some(f => /\.(jsx|tsx)$/i.test(f)) &&
|
|
232
|
+
addedLines.some(l =>
|
|
233
|
+
/^\+\s*(export\s+)?(function|const)\s+[A-Z]\w*/.test(l)
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
const hasWhitespaceOnly =
|
|
237
|
+
additions + deletions > 0 &&
|
|
238
|
+
isTrivialWhitespace([...addedLines, ...removedLines]);
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
hasBugFix: matchesAny(patterns.bugFix, diff),
|
|
242
|
+
hasTestFix: hasTestChange && matchesAny(patterns.testFix, diff),
|
|
243
|
+
|
|
244
|
+
hasNewFunction,
|
|
245
|
+
hasNewClass,
|
|
246
|
+
hasNewEndpoint,
|
|
247
|
+
hasNewComponent,
|
|
248
|
+
|
|
249
|
+
hasRefactor: matchesAny(patterns.refactor, diff),
|
|
250
|
+
|
|
251
|
+
hasRename: (summary.renamed ?? 0) > 0,
|
|
252
|
+
hasMovedCode: (summary.renamed ?? 0) > 0 && additions > 0 && deletions > 0,
|
|
128
253
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
254
|
+
hasDocsChange,
|
|
255
|
+
hasCommentChange: addedLines.some(l => /^\+\s*(\/\/|\/\*|\*)/.test(l)),
|
|
256
|
+
hasTestChange,
|
|
257
|
+
|
|
258
|
+
hasDeletions: deletions > 0,
|
|
259
|
+
hasStyleChange: matchesAny(patterns.style, diff),
|
|
260
|
+
|
|
261
|
+
hasWhitespaceOnly,
|
|
262
|
+
hasConfigChange,
|
|
263
|
+
hasDependencyChange,
|
|
264
|
+
|
|
265
|
+
additions,
|
|
266
|
+
deletions,
|
|
267
|
+
hasChanges: additions + deletions > 0
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Determine the commit intent type based on analysis
|
|
273
|
+
* @param analysis - Analysis results from analyzeDiff
|
|
274
|
+
* @param summary - Changes summary
|
|
275
|
+
* @returns The intent type string
|
|
276
|
+
*/
|
|
277
|
+
function determineIntent(analysis: DiffAnalysis, summary: ChangeSummary = {}): string {
|
|
278
|
+
if (!analysis.hasChanges) return 'Chore';
|
|
279
|
+
|
|
280
|
+
if (analysis.hasBugFix || analysis.hasTestFix) return 'Bug Fix';
|
|
281
|
+
if (analysis.hasTestChange && !analysis.hasBugFix) return 'Test';
|
|
282
|
+
if (analysis.hasDocsChange) return 'Documentation';
|
|
283
|
+
|
|
284
|
+
if (
|
|
285
|
+
analysis.hasRefactor ||
|
|
286
|
+
analysis.hasMovedCode ||
|
|
287
|
+
(analysis.deletions > analysis.additions * 2)
|
|
288
|
+
)
|
|
289
|
+
return 'Refactor';
|
|
290
|
+
|
|
291
|
+
if (
|
|
292
|
+
analysis.hasNewFunction ||
|
|
293
|
+
analysis.hasNewClass ||
|
|
294
|
+
analysis.hasNewComponent ||
|
|
295
|
+
analysis.hasNewEndpoint
|
|
296
|
+
)
|
|
297
|
+
return 'Feature';
|
|
298
|
+
|
|
299
|
+
if (analysis.hasDependencyChange || analysis.hasConfigChange) return 'Chore';
|
|
300
|
+
if (analysis.hasStyleChange || analysis.hasWhitespaceOnly) return 'Style';
|
|
301
|
+
|
|
302
|
+
return 'Update';
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Generate a descriptive commit message based on analysis
|
|
307
|
+
* @param analysis - Analysis results from analyzeDiff
|
|
308
|
+
* @param intent - The determined intent type
|
|
309
|
+
* @returns A commit message string
|
|
310
|
+
*/
|
|
311
|
+
function generateMessage(analysis: DiffAnalysis, intent: string, summary: ChangeSummary = {}): string {
|
|
312
|
+
const fileCount = summary.total;
|
|
313
|
+
const hasFileCount = typeof fileCount === 'number' && fileCount > 0;
|
|
314
|
+
const fileWord = fileCount === 1 ? 'file' : 'files';
|
|
315
|
+
|
|
316
|
+
switch (intent) {
|
|
317
|
+
case 'Bug Fix':
|
|
318
|
+
return analysis.hasTestFix
|
|
319
|
+
? hasFileCount
|
|
320
|
+
? `fix failing tests in ${fileCount} ${fileWord}`
|
|
321
|
+
: 'fix failing tests'
|
|
322
|
+
: hasFileCount
|
|
323
|
+
? `fix issues in ${fileCount} ${fileWord}`
|
|
324
|
+
: 'fix issues';
|
|
325
|
+
|
|
326
|
+
case 'Feature':
|
|
327
|
+
if (analysis.hasNewEndpoint) return 'add new API endpoints';
|
|
328
|
+
if (analysis.hasNewComponent) return 'add new components';
|
|
329
|
+
if (analysis.hasNewClass || analysis.hasNewFunction)
|
|
330
|
+
return hasFileCount
|
|
331
|
+
? `add new functionality to ${fileCount} ${fileWord}`
|
|
332
|
+
: 'add new functionality';
|
|
333
|
+
return hasFileCount
|
|
334
|
+
? `implement new features in ${fileCount} ${fileWord}`
|
|
335
|
+
: 'implement new features';
|
|
336
|
+
|
|
337
|
+
case 'Refactor':
|
|
338
|
+
if (analysis.hasMovedCode)
|
|
339
|
+
return hasFileCount
|
|
340
|
+
? `restructure code in ${fileCount} ${fileWord}`
|
|
341
|
+
: 'restructure code';
|
|
342
|
+
if (analysis.deletions > analysis.additions * 2)
|
|
343
|
+
return hasFileCount
|
|
344
|
+
? `remove unused code from ${fileCount} ${fileWord}`
|
|
345
|
+
: 'remove unused code';
|
|
346
|
+
return hasFileCount
|
|
347
|
+
? `refactor code in ${fileCount} ${fileWord}`
|
|
348
|
+
: 'refactor code';
|
|
349
|
+
|
|
350
|
+
case 'Test':
|
|
351
|
+
return hasFileCount
|
|
352
|
+
? `add/update tests in ${fileCount} ${fileWord}`
|
|
353
|
+
: 'add/update tests';
|
|
354
|
+
|
|
355
|
+
case 'Chore':
|
|
356
|
+
if (analysis.hasDependencyChange) return 'update dependencies';
|
|
357
|
+
if (analysis.hasConfigChange) return 'update configuration files';
|
|
358
|
+
return 'update project configuration';
|
|
359
|
+
|
|
360
|
+
case 'Style':
|
|
361
|
+
return hasFileCount
|
|
362
|
+
? `format and style ${fileCount} ${fileWord}`
|
|
363
|
+
: 'format and style code';
|
|
364
|
+
|
|
365
|
+
case 'Documentation':
|
|
366
|
+
return hasFileCount
|
|
367
|
+
? fileCount === 1
|
|
368
|
+
? 'update documentation'
|
|
369
|
+
: `update documentation in ${fileCount} ${fileWord}`
|
|
370
|
+
: 'update documentation';
|
|
371
|
+
|
|
372
|
+
default:
|
|
373
|
+
return hasFileCount
|
|
374
|
+
? `update ${fileCount} ${fileWord}`
|
|
375
|
+
: 'update code';
|
|
155
376
|
}
|
|
377
|
+
}
|
|
156
378
|
|
|
379
|
+
function generateFallbackCommit(diff: string, summary?: ChangeSummary): CommitSuggestion {
|
|
380
|
+
const diffHash = crypto.createHash('sha1').update(diff).digest('hex');
|
|
381
|
+
|
|
382
|
+
const analysis = analyzeDiff(diff, summary);
|
|
383
|
+
const intent = determineIntent(analysis, summary);
|
|
384
|
+
const message = generateMessage(analysis, intent, summary);
|
|
385
|
+
|
|
386
|
+
const result = { intent, message };
|
|
157
387
|
commitCache.set(diffHash, intent, message);
|
|
158
|
-
|
|
388
|
+
|
|
389
|
+
return result;
|
|
159
390
|
}
|
|
160
391
|
|
|
161
392
|
function sleep(ms: number): Promise<void> {
|
|
162
393
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
163
|
-
}
|
|
394
|
+
}
|