@entro314labs/ai-changelog-generator 3.1.1 → 3.2.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +383 -877
  2. package/README.md +8 -3
  3. package/ai-changelog-mcp.sh +0 -0
  4. package/ai-changelog.sh +0 -0
  5. package/bin/ai-changelog-dxt.js +9 -9
  6. package/bin/ai-changelog-mcp.js +19 -17
  7. package/bin/ai-changelog.js +6 -6
  8. package/package.json +80 -48
  9. package/src/ai-changelog-generator.js +83 -81
  10. package/src/application/orchestrators/changelog.orchestrator.js +791 -516
  11. package/src/application/services/application.service.js +137 -128
  12. package/src/cli.js +76 -57
  13. package/src/domains/ai/ai-analysis.service.js +289 -209
  14. package/src/domains/analysis/analysis.engine.js +253 -193
  15. package/src/domains/changelog/changelog.service.js +1062 -784
  16. package/src/domains/changelog/workspace-changelog.service.js +420 -249
  17. package/src/domains/git/git-repository.analyzer.js +348 -258
  18. package/src/domains/git/git.service.js +132 -112
  19. package/src/infrastructure/cli/cli.controller.js +390 -274
  20. package/src/infrastructure/config/configuration.manager.js +220 -190
  21. package/src/infrastructure/interactive/interactive-staging.service.js +154 -135
  22. package/src/infrastructure/interactive/interactive-workflow.service.js +200 -159
  23. package/src/infrastructure/mcp/mcp-server.service.js +208 -207
  24. package/src/infrastructure/metrics/metrics.collector.js +140 -123
  25. package/src/infrastructure/providers/core/base-provider.js +87 -40
  26. package/src/infrastructure/providers/implementations/anthropic.js +101 -99
  27. package/src/infrastructure/providers/implementations/azure.js +124 -101
  28. package/src/infrastructure/providers/implementations/bedrock.js +136 -126
  29. package/src/infrastructure/providers/implementations/dummy.js +23 -23
  30. package/src/infrastructure/providers/implementations/google.js +123 -114
  31. package/src/infrastructure/providers/implementations/huggingface.js +94 -87
  32. package/src/infrastructure/providers/implementations/lmstudio.js +75 -60
  33. package/src/infrastructure/providers/implementations/mock.js +69 -73
  34. package/src/infrastructure/providers/implementations/ollama.js +89 -66
  35. package/src/infrastructure/providers/implementations/openai.js +88 -89
  36. package/src/infrastructure/providers/implementations/vertex.js +227 -197
  37. package/src/infrastructure/providers/provider-management.service.js +245 -207
  38. package/src/infrastructure/providers/provider-manager.service.js +145 -125
  39. package/src/infrastructure/providers/utils/base-provider-helpers.js +308 -302
  40. package/src/infrastructure/providers/utils/model-config.js +220 -195
  41. package/src/infrastructure/providers/utils/provider-utils.js +105 -100
  42. package/src/infrastructure/validation/commit-message-validation.service.js +259 -161
  43. package/src/shared/constants/colors.js +453 -180
  44. package/src/shared/utils/cli-demo.js +285 -0
  45. package/src/shared/utils/cli-entry-utils.js +257 -249
  46. package/src/shared/utils/cli-ui.js +447 -0
  47. package/src/shared/utils/diff-processor.js +513 -0
  48. package/src/shared/utils/error-classes.js +125 -156
  49. package/src/shared/utils/json-utils.js +93 -89
  50. package/src/shared/utils/utils.js +1117 -945
  51. package/types/index.d.ts +353 -344
@@ -1,8 +1,9 @@
1
- import colors from '../../shared/constants/colors.js';
1
+ import colors from '../../shared/constants/colors.js'
2
+ import { EnhancedConsole } from '../../shared/utils/cli-ui.js'
2
3
 
3
4
  /**
4
5
  * Commit Message Validation Service
5
- *
6
+ *
6
7
  * Provides comprehensive commit message validation based on:
7
8
  * - Conventional Commits specification
8
9
  * - Configuration-based rules
@@ -11,19 +12,31 @@ import colors from '../../shared/constants/colors.js';
11
12
  */
12
13
  export class CommitMessageValidationService {
13
14
  constructor(configManager) {
14
- this.configManager = configManager;
15
- this.config = this.loadValidationConfig();
15
+ this.configManager = configManager
16
+ this.config = this.loadValidationConfig()
16
17
  }
17
18
 
18
19
  /**
19
20
  * Load validation configuration from config files
20
21
  */
21
22
  loadValidationConfig() {
22
- const config = this.configManager.getAll();
23
-
23
+ const config = this.configManager.getAll() || {}
24
+
24
25
  // Default validation rules
25
26
  const defaults = {
26
- commitTypes: ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'ci', 'chore', 'revert'],
27
+ commitTypes: [
28
+ 'feat',
29
+ 'fix',
30
+ 'docs',
31
+ 'style',
32
+ 'refactor',
33
+ 'perf',
34
+ 'test',
35
+ 'build',
36
+ 'ci',
37
+ 'chore',
38
+ 'revert',
39
+ ],
27
40
  commitScopes: [], // Empty means any scope is allowed
28
41
  maxSubjectLength: 72,
29
42
  minSubjectLength: 10,
@@ -34,19 +47,19 @@ export class CommitMessageValidationService {
34
47
  subjectCase: 'lower', // 'lower', 'sentence', 'any'
35
48
  subjectEndPunctuation: false, // Don't allow period at end
36
49
  bodyLineLength: 100,
37
- footerFormat: 'conventional' // 'conventional', 'any'
38
- };
50
+ footerFormat: 'conventional', // 'conventional', 'any'
51
+ }
39
52
 
40
53
  // Merge with config from ai-changelog.config.yaml
41
- const yamlConfig = config.convention || {};
42
-
54
+ const yamlConfig = config.convention || {}
55
+
43
56
  return {
44
57
  ...defaults,
45
58
  ...yamlConfig,
46
59
  // Override with specific validation settings if they exist
47
60
  commitTypes: yamlConfig.commitTypes || defaults.commitTypes,
48
- commitScopes: yamlConfig.commitScopes || defaults.commitScopes
49
- };
61
+ commitScopes: yamlConfig.commitScopes || defaults.commitScopes,
62
+ }
50
63
  }
51
64
 
52
65
  /**
@@ -54,52 +67,64 @@ export class CommitMessageValidationService {
54
67
  */
55
68
  async validateCommitMessage(message, context = {}) {
56
69
  if (!message || typeof message !== 'string') {
57
- return this.createValidationResult(false, ['Commit message is required'], []);
70
+ return this.createValidationResult(false, ['Commit message is required'], [])
58
71
  }
59
72
 
60
- const trimmedMessage = message.trim();
73
+ const trimmedMessage = message.trim()
61
74
  if (trimmedMessage.length === 0) {
62
- return this.createValidationResult(false, ['Commit message cannot be empty'], []);
75
+ return this.createValidationResult(false, ['Commit message cannot be empty'], [])
63
76
  }
64
77
 
65
- const lines = trimmedMessage.split('\n');
66
- const subject = lines[0];
67
- const body = lines.slice(2).join('\n').trim(); // Skip blank line after subject
68
- const hasBlankLineAfterSubject = lines.length > 1 && lines[1].trim() === '';
78
+ const lines = trimmedMessage.split('\n')
79
+ const subject = lines[0]
80
+ const body = lines.slice(2).join('\n').trim() // Skip blank line after subject
81
+ const hasBlankLineAfterSubject = lines.length > 1 && lines[1].trim() === ''
69
82
 
70
- const errors = [];
71
- const warnings = [];
72
- const suggestions = [];
83
+ const errors = []
84
+ const warnings = []
85
+ const suggestions = []
73
86
 
74
87
  // Parse conventional commit format
75
- const conventionalCommit = this.parseConventionalCommit(subject);
88
+ const conventionalCommit = this.parseConventionalCommit(subject)
76
89
 
77
90
  // Subject validation
78
- this.validateSubject(subject, conventionalCommit, errors, warnings, suggestions, context);
91
+ this.validateSubject(subject, conventionalCommit, errors, warnings, suggestions, context)
79
92
 
80
93
  // Body validation
81
94
  if (lines.length > 1) {
82
- this.validateBody(body, hasBlankLineAfterSubject, lines, errors, warnings, suggestions);
95
+ this.validateBody(body, hasBlankLineAfterSubject, lines, errors, warnings, suggestions)
83
96
  }
84
97
 
85
98
  // Footer validation
86
- const footerLines = this.extractFooterLines(lines);
99
+ const footerLines = this.extractFooterLines(lines)
87
100
  if (footerLines.length > 0) {
88
- this.validateFooter(footerLines, errors, warnings, suggestions);
101
+ this.validateFooter(footerLines, errors, warnings, suggestions)
89
102
  }
90
103
 
91
104
  // Context-based validation (branch intelligence)
92
105
  if (context.branchAnalysis) {
93
- this.validateAgainstBranchContext(conventionalCommit, context.branchAnalysis, warnings, suggestions);
106
+ this.validateAgainstBranchContext(
107
+ conventionalCommit,
108
+ context.branchAnalysis,
109
+ warnings,
110
+ suggestions
111
+ )
94
112
  }
95
113
 
96
114
  // Configuration-based validation
97
- this.validateAgainstConfig(conventionalCommit, errors, warnings, suggestions);
115
+ this.validateAgainstConfig(conventionalCommit, errors, warnings, suggestions)
98
116
 
99
- const isValid = errors.length === 0;
100
- const score = this.calculateValidationScore(errors, warnings, suggestions);
117
+ const isValid = errors.length === 0
118
+ const score = this.calculateValidationScore(errors, warnings, suggestions)
101
119
 
102
- return this.createValidationResult(isValid, errors, warnings, suggestions, score, conventionalCommit);
120
+ return this.createValidationResult(
121
+ isValid,
122
+ errors,
123
+ warnings,
124
+ suggestions,
125
+ score,
126
+ conventionalCommit
127
+ )
103
128
  }
104
129
 
105
130
  /**
@@ -107,8 +132,8 @@ export class CommitMessageValidationService {
107
132
  */
108
133
  parseConventionalCommit(subject) {
109
134
  // Enhanced pattern to capture all parts
110
- const conventionalPattern = /^([a-z]+)(\(([^)]+)\))?(!)?: (.+)$/;
111
- const match = subject.match(conventionalPattern);
135
+ const conventionalPattern = /^([a-z]+)(\(([^)]+)\))?(!)?: (.+)$/
136
+ const match = subject.match(conventionalPattern)
112
137
 
113
138
  if (!match) {
114
139
  return {
@@ -116,8 +141,8 @@ export class CommitMessageValidationService {
116
141
  scope: null,
117
142
  breaking: false,
118
143
  description: subject,
119
- isConventional: false
120
- };
144
+ isConventional: false,
145
+ }
121
146
  }
122
147
 
123
148
  return {
@@ -125,73 +150,85 @@ export class CommitMessageValidationService {
125
150
  scope: match[3] || null,
126
151
  breaking: !!match[4], // Breaking change indicator (!)
127
152
  description: match[5],
128
- isConventional: true
129
- };
153
+ isConventional: true,
154
+ }
130
155
  }
131
156
 
132
157
  /**
133
158
  * Validate subject line
134
159
  */
135
- validateSubject(subject, parsed, errors, warnings, suggestions, context) {
160
+ validateSubject(subject, parsed, errors, warnings, suggestions, _context) {
136
161
  // Length validation
137
162
  if (subject.length < this.config.minSubjectLength) {
138
- errors.push(`Subject too short (${subject.length} chars, minimum ${this.config.minSubjectLength})`);
139
- suggestions.push('Add more detail about what was changed');
163
+ errors.push(
164
+ `Subject too short (${subject.length} chars, minimum ${this.config.minSubjectLength})`
165
+ )
166
+ suggestions.push('Add more detail about what was changed')
140
167
  }
141
168
 
142
169
  if (subject.length > this.config.maxSubjectLength) {
143
- errors.push(`Subject too long (${subject.length} chars, maximum ${this.config.maxSubjectLength})`);
144
- suggestions.push('Move additional details to the commit body');
170
+ errors.push(
171
+ `Subject too long (${subject.length} chars, maximum ${this.config.maxSubjectLength})`
172
+ )
173
+ suggestions.push('Move additional details to the commit body')
145
174
  }
146
175
 
147
176
  // Conventional commit format validation
148
177
  if (!parsed.isConventional) {
149
- errors.push('Subject does not follow conventional commit format');
150
- suggestions.push('Use format: type(scope): description (e.g., "feat: add new feature")');
151
- return; // Skip further validation if not conventional
178
+ errors.push('Subject does not follow conventional commit format')
179
+ suggestions.push('Use format: type(scope): description (e.g., "feat: add new feature")')
180
+ return // Skip further validation if not conventional
152
181
  }
153
182
 
154
183
  // Type validation
155
184
  if (!this.config.commitTypes.includes(parsed.type)) {
156
- errors.push(`Invalid commit type: "${parsed.type}"`);
157
- suggestions.push(`Use one of: ${this.config.commitTypes.join(', ')}`);
185
+ errors.push(`Invalid commit type: "${parsed.type}"`)
186
+ suggestions.push(`Use one of: ${this.config.commitTypes.join(', ')}`)
158
187
  }
159
188
 
160
189
  // Scope validation
161
- if (this.config.commitScopes.length > 0 && parsed.scope && !this.config.commitScopes.includes(parsed.scope)) {
162
- warnings.push(`Unexpected scope: "${parsed.scope}"`);
163
- suggestions.push(`Suggested scopes: ${this.config.commitScopes.join(', ')}`);
190
+ if (
191
+ this.config.commitScopes.length > 0 &&
192
+ parsed.scope &&
193
+ !this.config.commitScopes.includes(parsed.scope)
194
+ ) {
195
+ warnings.push(`Unexpected scope: "${parsed.scope}"`)
196
+ suggestions.push(`Suggested scopes: ${this.config.commitScopes.join(', ')}`)
164
197
  }
165
198
 
166
199
  if (this.config.requireScope && !parsed.scope) {
167
- errors.push('Scope is required for this repository');
168
- suggestions.push('Add scope in parentheses: type(scope): description');
200
+ errors.push('Scope is required for this repository')
201
+ suggestions.push('Add scope in parentheses: type(scope): description')
169
202
  }
170
203
 
171
204
  // Description validation
172
205
  if (!parsed.description || parsed.description.trim().length === 0) {
173
- errors.push('Description is required');
174
- suggestions.push('Add a clear description of what was changed');
206
+ errors.push('Description is required')
207
+ suggestions.push('Add a clear description of what was changed')
175
208
  }
176
209
 
177
210
  // Case validation
178
211
  if (this.config.subjectCase === 'lower' && parsed.description) {
179
- const firstChar = parsed.description.charAt(0);
212
+ const firstChar = parsed.description.charAt(0)
180
213
  if (firstChar !== firstChar.toLowerCase()) {
181
- warnings.push('Description should start with lowercase letter');
182
- suggestions.push(`Change "${firstChar}" to "${firstChar.toLowerCase()}"`);
214
+ warnings.push('Description should start with lowercase letter')
215
+ suggestions.push(`Change "${firstChar}" to "${firstChar.toLowerCase()}"`)
183
216
  }
184
217
  }
185
218
 
186
219
  // End punctuation validation
187
- if (!this.config.subjectEndPunctuation && parsed.description && parsed.description.endsWith('.')) {
188
- warnings.push('Subject should not end with a period');
189
- suggestions.push('Remove the trailing period');
220
+ if (
221
+ !this.config.subjectEndPunctuation &&
222
+ parsed.description &&
223
+ parsed.description.endsWith('.')
224
+ ) {
225
+ warnings.push('Subject should not end with a period')
226
+ suggestions.push('Remove the trailing period')
190
227
  }
191
228
 
192
229
  // Imperative mood validation
193
230
  if (parsed.description) {
194
- this.validateImperativeMood(parsed.description, warnings, suggestions);
231
+ this.validateImperativeMood(parsed.description, warnings, suggestions)
195
232
  }
196
233
  }
197
234
 
@@ -200,29 +237,73 @@ export class CommitMessageValidationService {
200
237
  */
201
238
  validateImperativeMood(description, warnings, suggestions) {
202
239
  const imperativeVerbs = [
203
- 'add', 'remove', 'fix', 'update', 'create', 'delete', 'implement', 'refactor',
204
- 'improve', 'enhance', 'optimize', 'change', 'move', 'rename', 'replace',
205
- 'upgrade', 'downgrade', 'install', 'uninstall', 'configure', 'setup',
206
- 'initialize', 'clean', 'format', 'lint', 'test', 'document'
207
- ];
240
+ 'add',
241
+ 'remove',
242
+ 'fix',
243
+ 'update',
244
+ 'create',
245
+ 'delete',
246
+ 'implement',
247
+ 'refactor',
248
+ 'improve',
249
+ 'enhance',
250
+ 'optimize',
251
+ 'change',
252
+ 'move',
253
+ 'rename',
254
+ 'replace',
255
+ 'upgrade',
256
+ 'downgrade',
257
+ 'install',
258
+ 'uninstall',
259
+ 'configure',
260
+ 'setup',
261
+ 'initialize',
262
+ 'clean',
263
+ 'format',
264
+ 'lint',
265
+ 'test',
266
+ 'document',
267
+ ]
208
268
 
209
269
  const nonImperativeIndicators = [
210
- 'added', 'removed', 'fixed', 'updated', 'created', 'deleted', 'implemented',
211
- 'improved', 'enhanced', 'optimized', 'changed', 'moved', 'renamed', 'replaced',
212
- 'upgraded', 'downgraded', 'installed', 'uninstalled', 'configured',
213
- 'initialized', 'cleaned', 'formatted', 'linted', 'tested', 'documented'
214
- ];
215
-
216
- const firstWord = description.split(' ')[0].toLowerCase();
270
+ 'added',
271
+ 'removed',
272
+ 'fixed',
273
+ 'updated',
274
+ 'created',
275
+ 'deleted',
276
+ 'implemented',
277
+ 'improved',
278
+ 'enhanced',
279
+ 'optimized',
280
+ 'changed',
281
+ 'moved',
282
+ 'renamed',
283
+ 'replaced',
284
+ 'upgraded',
285
+ 'downgraded',
286
+ 'installed',
287
+ 'uninstalled',
288
+ 'configured',
289
+ 'initialized',
290
+ 'cleaned',
291
+ 'formatted',
292
+ 'linted',
293
+ 'tested',
294
+ 'documented',
295
+ ]
296
+
297
+ const firstWord = description.split(' ')[0].toLowerCase()
217
298
 
218
299
  if (nonImperativeIndicators.includes(firstWord)) {
219
- warnings.push('Use imperative mood in description');
300
+ warnings.push('Use imperative mood in description')
220
301
  // Try to suggest imperative form
221
- const imperative = firstWord.replace(/ed$/, '').replace(/d$/, '');
302
+ const imperative = firstWord.replace(/ed$/, '').replace(/d$/, '')
222
303
  if (imperativeVerbs.includes(imperative)) {
223
- suggestions.push(`Change "${firstWord}" to "${imperative}"`);
304
+ suggestions.push(`Change "${firstWord}" to "${imperative}"`)
224
305
  } else {
225
- suggestions.push('Use imperative mood (e.g., "fix bug" not "fixed bug")');
306
+ suggestions.push('Use imperative mood (e.g., "fix bug" not "fixed bug")')
226
307
  }
227
308
  }
228
309
  }
@@ -230,27 +311,29 @@ export class CommitMessageValidationService {
230
311
  /**
231
312
  * Validate body
232
313
  */
233
- validateBody(body, hasBlankLine, lines, errors, warnings, suggestions) {
314
+ validateBody(body, hasBlankLine, _lines, errors, warnings, suggestions) {
234
315
  // Blank line separation
235
316
  if (!hasBlankLine) {
236
- errors.push('Missing blank line between subject and body');
237
- suggestions.push('Add a blank line after the subject');
317
+ errors.push('Missing blank line between subject and body')
318
+ suggestions.push('Add a blank line after the subject')
238
319
  }
239
320
 
240
321
  // Body line length
241
322
  if (body) {
242
- const bodyLines = body.split('\n');
323
+ const bodyLines = body.split('\n')
243
324
  bodyLines.forEach((line, index) => {
244
325
  if (line.length > this.config.bodyLineLength) {
245
- warnings.push(`Body line ${index + 1} too long (${line.length} chars, recommended max ${this.config.bodyLineLength})`);
326
+ warnings.push(
327
+ `Body line ${index + 1} too long (${line.length} chars, recommended max ${this.config.bodyLineLength})`
328
+ )
246
329
  }
247
- });
330
+ })
248
331
  }
249
332
 
250
333
  // Required body
251
334
  if (this.config.requireBody && (!body || body.trim().length === 0)) {
252
- errors.push('Commit body is required');
253
- suggestions.push('Add details about the changes in the commit body');
335
+ errors.push('Commit body is required')
336
+ suggestions.push('Add details about the changes in the commit body')
254
337
  }
255
338
  }
256
339
 
@@ -258,61 +341,66 @@ export class CommitMessageValidationService {
258
341
  * Extract footer lines (last paragraph that contains key-value pairs)
259
342
  */
260
343
  extractFooterLines(lines) {
261
- if (lines.length < 3) return [];
344
+ if (lines.length < 3) {
345
+ return []
346
+ }
262
347
 
263
- const footerLines = [];
348
+ const footerLines = []
264
349
  for (let i = lines.length - 1; i >= 0; i--) {
265
- const line = lines[i].trim();
350
+ const line = lines[i].trim()
266
351
  if (line === '') {
267
- break; // Empty line indicates end of footer
352
+ break // Empty line indicates end of footer
268
353
  }
269
354
  if (line.includes(':') || line.match(/^(BREAKING CHANGE|Closes?|Fixes?|Refs?)/i)) {
270
- footerLines.unshift(line);
355
+ footerLines.unshift(line)
271
356
  } else {
272
- break; // Non-footer line
357
+ break // Non-footer line
273
358
  }
274
359
  }
275
360
 
276
- return footerLines;
361
+ return footerLines
277
362
  }
278
363
 
279
364
  /**
280
365
  * Validate footer
281
366
  */
282
367
  validateFooter(footerLines, errors, warnings, suggestions) {
283
- footerLines.forEach(line => {
368
+ footerLines.forEach((line) => {
284
369
  // Validate footer format
285
- if (this.config.footerFormat === 'conventional') {
286
- if (!line.match(/^[A-Za-z-]+: .+/) && !line.match(/^BREAKING CHANGE: .+/)) {
287
- warnings.push(`Footer line doesn't follow conventional format: "${line}"`);
288
- suggestions.push('Use format: "Key: value" or "BREAKING CHANGE: description"');
289
- }
370
+ if (
371
+ this.config.footerFormat === 'conventional' &&
372
+ !(line.match(/^[A-Za-z-]+: .+/) || line.match(/^BREAKING CHANGE: .+/))
373
+ ) {
374
+ warnings.push(`Footer line doesn't follow conventional format: "${line}"`)
375
+ suggestions.push('Use format: "Key: value" or "BREAKING CHANGE: description"')
290
376
  }
291
377
 
292
378
  // Validate breaking changes
293
- if (line.startsWith('BREAKING CHANGE:')) {
294
- if (!this.config.allowBreakingChanges) {
295
- errors.push('Breaking changes are not allowed in this repository');
296
- }
379
+ if (line.startsWith('BREAKING CHANGE:') && !this.config.allowBreakingChanges) {
380
+ errors.push('Breaking changes are not allowed in this repository')
297
381
  }
298
- });
382
+ })
299
383
  }
300
384
 
301
385
  /**
302
386
  * Validate against branch context
303
387
  */
304
388
  validateAgainstBranchContext(parsed, branchAnalysis, warnings, suggestions) {
305
- if (!branchAnalysis || branchAnalysis.confidence < 50) return;
389
+ if (!branchAnalysis || branchAnalysis.confidence < 50) {
390
+ return
391
+ }
306
392
 
307
393
  // Type mismatch
308
394
  if (branchAnalysis.type && parsed.type && branchAnalysis.type !== parsed.type) {
309
- warnings.push(`Commit type "${parsed.type}" doesn't match branch type "${branchAnalysis.type}"`);
310
- suggestions.push(`Consider using type "${branchAnalysis.type}" based on branch name`);
395
+ warnings.push(
396
+ `Commit type "${parsed.type}" doesn't match branch type "${branchAnalysis.type}"`
397
+ )
398
+ suggestions.push(`Consider using type "${branchAnalysis.type}" based on branch name`)
311
399
  }
312
400
 
313
401
  // Missing ticket reference
314
402
  if (branchAnalysis.ticket && !this.containsTicketReference(parsed, branchAnalysis.ticket)) {
315
- suggestions.push(`Consider adding ticket reference: ${branchAnalysis.ticket}`);
403
+ suggestions.push(`Consider adding ticket reference: ${branchAnalysis.ticket}`)
316
404
  }
317
405
  }
318
406
 
@@ -320,14 +408,14 @@ export class CommitMessageValidationService {
320
408
  * Check if commit message contains ticket reference
321
409
  */
322
410
  containsTicketReference(parsed, ticket) {
323
- const fullMessage = `${parsed.type}${parsed.scope ? `(${parsed.scope})` : ''}: ${parsed.description}`;
324
- return fullMessage.includes(ticket);
411
+ const fullMessage = `${parsed.type}${parsed.scope ? `(${parsed.scope})` : ''}: ${parsed.description}`
412
+ return fullMessage.includes(ticket)
325
413
  }
326
414
 
327
415
  /**
328
416
  * Validate against configuration
329
417
  */
330
- validateAgainstConfig(parsed, errors, warnings, suggestions) {
418
+ validateAgainstConfig(_parsed, _errors, _warnings, _suggestions) {
331
419
  // This is handled in other validation methods
332
420
  // Additional config-specific validations can be added here
333
421
  }
@@ -336,17 +424,24 @@ export class CommitMessageValidationService {
336
424
  * Calculate validation score
337
425
  */
338
426
  calculateValidationScore(errors, warnings, suggestions) {
339
- let score = 100;
340
- score -= errors.length * 25; // Major issues
341
- score -= warnings.length * 10; // Minor issues
342
- score -= suggestions.length * 5; // Improvements
343
- return Math.max(0, score);
427
+ let score = 100
428
+ score -= errors.length * 25 // Major issues
429
+ score -= warnings.length * 10 // Minor issues
430
+ score -= suggestions.length * 5 // Improvements
431
+ return Math.max(0, score)
344
432
  }
345
433
 
346
434
  /**
347
435
  * Create validation result object
348
436
  */
349
- createValidationResult(isValid, errors = [], warnings = [], suggestions = [], score = 0, parsed = null) {
437
+ createValidationResult(
438
+ isValid,
439
+ errors = [],
440
+ warnings = [],
441
+ suggestions = [],
442
+ score = 0,
443
+ parsed = null
444
+ ) {
350
445
  return {
351
446
  valid: isValid,
352
447
  errors,
@@ -354,8 +449,8 @@ export class CommitMessageValidationService {
354
449
  suggestions,
355
450
  score,
356
451
  parsed,
357
- summary: this.generateValidationSummary(isValid, errors, warnings, suggestions, score)
358
- };
452
+ summary: this.generateValidationSummary(isValid, errors, warnings, suggestions, score),
453
+ }
359
454
  }
360
455
 
361
456
  /**
@@ -363,96 +458,99 @@ export class CommitMessageValidationService {
363
458
  */
364
459
  generateValidationSummary(isValid, errors, warnings, suggestions, score) {
365
460
  if (isValid && warnings.length === 0 && suggestions.length === 0) {
366
- return '✅ Perfect commit message!';
461
+ return '✅ Perfect commit message!'
367
462
  }
368
463
 
369
- const parts = [];
370
-
464
+ const parts = []
465
+
371
466
  if (errors.length > 0) {
372
- parts.push(`${errors.length} error${errors.length === 1 ? '' : 's'}`);
467
+ parts.push(`${errors.length} error${errors.length === 1 ? '' : 's'}`)
373
468
  }
374
-
469
+
375
470
  if (warnings.length > 0) {
376
- parts.push(`${warnings.length} warning${warnings.length === 1 ? '' : 's'}`);
471
+ parts.push(`${warnings.length} warning${warnings.length === 1 ? '' : 's'}`)
377
472
  }
378
-
473
+
379
474
  if (suggestions.length > 0) {
380
- parts.push(`${suggestions.length} suggestion${suggestions.length === 1 ? '' : 's'}`);
475
+ parts.push(`${suggestions.length} suggestion${suggestions.length === 1 ? '' : 's'}`)
381
476
  }
382
477
 
383
- const status = isValid ? '✅' : '❌';
384
- return `${status} ${parts.join(', ')} (Score: ${score}/100)`;
478
+ const status = isValid ? '✅' : '❌'
479
+ return `${status} ${parts.join(', ')} (Score: ${score}/100)`
385
480
  }
386
481
 
387
482
  /**
388
483
  * Display validation results with colors
389
484
  */
390
485
  displayValidationResults(validationResult) {
391
- const { valid, errors, warnings, suggestions, score, summary } = validationResult;
486
+ const { valid, errors, warnings, suggestions, score, summary } = validationResult
392
487
 
393
- console.log(colors.infoMessage(`\n🔍 Commit Message Validation:`));
394
- console.log(colors.secondary(`${summary}`));
488
+ EnhancedConsole.section('🔍 Commit Message Validation')
489
+ console.log(colors.secondary(`${summary}`))
395
490
 
396
491
  if (errors.length > 0) {
397
- console.log(colors.errorMessage('\n❌ Errors (must fix):'));
398
- errors.forEach(error => {
399
- console.log(colors.error(` • ${error}`));
400
- });
492
+ EnhancedConsole.space()
493
+ console.log(colors.statusSymbol('error', 'Errors (must fix):'))
494
+ errors.forEach((error) => {
495
+ console.log(` ${colors.symbols.bullet} ${colors.error(error)}`)
496
+ })
401
497
  }
402
498
 
403
499
  if (warnings.length > 0) {
404
- console.log(colors.warningMessage('\n⚠️ Warnings (recommended to fix):'));
405
- warnings.forEach(warning => {
406
- console.log(colors.warning(` • ${warning}`));
407
- });
500
+ EnhancedConsole.space()
501
+ console.log(colors.statusSymbol('warning', 'Warnings (recommended to fix):'))
502
+ warnings.forEach((warning) => {
503
+ console.log(` ${colors.symbols.bullet} ${colors.warning(warning)}`)
504
+ })
408
505
  }
409
506
 
410
507
  if (suggestions.length > 0) {
411
- console.log(colors.infoMessage('\n💡 Suggestions (optional improvements):'));
412
- suggestions.forEach(suggestion => {
413
- console.log(colors.dim(` • ${suggestion}`));
414
- });
508
+ EnhancedConsole.space()
509
+ console.log(colors.statusSymbol('info', 'Suggestions (optional improvements):'))
510
+ suggestions.forEach((suggestion) => {
511
+ console.log(` ${colors.symbols.bullet} ${colors.dim(suggestion)}`)
512
+ })
415
513
  }
416
514
 
417
- return valid;
515
+ return valid
418
516
  }
419
517
 
420
518
  /**
421
519
  * Interactive commit message improvement
422
520
  */
423
521
  async improveCommitMessage(message, context = {}) {
424
- const validation = await this.validateCommitMessage(message, context);
425
-
522
+ const validation = await this.validateCommitMessage(message, context)
523
+
426
524
  if (validation.valid && validation.warnings.length === 0) {
427
- return { improved: false, message, validation };
525
+ return { improved: false, message, validation }
428
526
  }
429
527
 
430
528
  // Generate improved message based on validation results
431
- let improved = message;
529
+ let improved = message
432
530
 
433
531
  // Fix common issues automatically
434
532
  if (validation.parsed) {
435
- const { type, scope, description, breaking } = validation.parsed;
436
-
533
+ const { type, scope, description, breaking } = validation.parsed
534
+
437
535
  // Fix case issues
438
536
  if (description) {
439
- let fixedDescription = description;
440
-
537
+ let fixedDescription = description
538
+
441
539
  // Fix capitalization
442
540
  if (this.config.subjectCase === 'lower') {
443
- fixedDescription = fixedDescription.charAt(0).toLowerCase() + fixedDescription.slice(1);
541
+ fixedDescription = fixedDescription.charAt(0).toLowerCase() + fixedDescription.slice(1)
444
542
  }
445
-
543
+
446
544
  // Remove trailing period
447
545
  if (!this.config.subjectEndPunctuation && fixedDescription.endsWith('.')) {
448
- fixedDescription = fixedDescription.slice(0, -1);
546
+ fixedDescription = fixedDescription.slice(0, -1)
449
547
  }
450
-
548
+
451
549
  // Reconstruct subject
452
- improved = `${type}${scope ? `(${scope})` : ''}${breaking ? '!' : ''}: ${fixedDescription}`;
550
+ improved = `${type}${scope ? `(${scope})` : ''}${breaking ? '!' : ''}: ${fixedDescription}`
453
551
  }
454
552
  }
455
553
 
456
- return { improved: improved !== message, message: improved, validation };
554
+ return { improved: improved !== message, message: improved, validation }
457
555
  }
458
- }
556
+ }