@entro314labs/ai-changelog-generator 3.1.1 → 3.2.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +412 -875
  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 +91 -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 +328 -192
  15. package/src/domains/changelog/changelog.service.js +1174 -783
  16. package/src/domains/changelog/workspace-changelog.service.js +487 -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,19 +1,17 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { ProviderError } from '../../shared/utils/utils.js';
4
- import colors from '../../shared/constants/colors.js';
1
+ import process from 'node:process'
5
2
 
3
+ import colors from '../../shared/constants/colors.js'
6
4
  // Import all providers from new location
7
- import { AnthropicProvider } from './implementations/anthropic.js';
8
- import AzureOpenAIProvider from './implementations/azure.js';
9
- import DummyProvider from './implementations/dummy.js';
10
- import GoogleProvider from './implementations/google.js';
11
- import HuggingFaceProvider from './implementations/huggingface.js';
12
- import LMStudioProvider from './implementations/lmstudio.js';
13
- import MockProvider from './implementations/mock.js';
14
- import OllamaProvider from './implementations/ollama.js';
15
- import { OpenAIProvider } from './implementations/openai.js';
16
- import VertexAIProvider from './implementations/vertex.js';
5
+ import { AnthropicProvider } from './implementations/anthropic.js'
6
+ import AzureOpenAIProvider from './implementations/azure.js'
7
+ import DummyProvider from './implementations/dummy.js'
8
+ import GoogleProvider from './implementations/google.js'
9
+ import HuggingFaceProvider from './implementations/huggingface.js'
10
+ import LMStudioProvider from './implementations/lmstudio.js'
11
+ import MockProvider from './implementations/mock.js'
12
+ import OllamaProvider from './implementations/ollama.js'
13
+ import { OpenAIProvider } from './implementations/openai.js'
14
+ import VertexAIProvider from './implementations/vertex.js'
17
15
 
18
16
  /**
19
17
  * ProviderManager Service
@@ -22,31 +20,31 @@ import VertexAIProvider from './implementations/vertex.js';
22
20
  */
23
21
  export class ProviderManagerService {
24
22
  constructor(config = {}, options = {}) {
25
- this.config = config;
26
- this.providers = [];
27
- this.activeProvider = null;
23
+ this.config = config
24
+ this.providers = []
25
+ this.activeProvider = null
28
26
  this.options = {
29
27
  fallbackToDefault: true,
30
28
  defaultProviderName: 'openai',
31
- ...options
32
- };
29
+ ...options,
30
+ }
33
31
 
34
32
  // Static provider mapping with new classes
35
33
  this.providerClasses = {
36
- 'anthropic': AnthropicProvider,
37
- 'azure': AzureOpenAIProvider,
38
- 'dummy': DummyProvider,
39
- 'google': GoogleProvider,
40
- 'huggingface': HuggingFaceProvider,
41
- 'lmstudio': LMStudioProvider,
42
- 'mock': MockProvider,
43
- 'ollama': OllamaProvider,
44
- 'openai': OpenAIProvider,
45
- 'vertex': VertexAIProvider
46
- };
47
-
48
- this.loadProviders();
49
- this.determineActiveProvider();
34
+ anthropic: AnthropicProvider,
35
+ azure: AzureOpenAIProvider,
36
+ dummy: DummyProvider,
37
+ google: GoogleProvider,
38
+ huggingface: HuggingFaceProvider,
39
+ lmstudio: LMStudioProvider,
40
+ mock: MockProvider,
41
+ ollama: OllamaProvider,
42
+ openai: OpenAIProvider,
43
+ vertex: VertexAIProvider,
44
+ }
45
+
46
+ this.loadProviders()
47
+ this.determineActiveProvider()
50
48
  }
51
49
 
52
50
  /**
@@ -56,33 +54,37 @@ export class ProviderManagerService {
56
54
  try {
57
55
  for (const [name, ProviderClass] of Object.entries(this.providerClasses)) {
58
56
  try {
59
- const provider = new ProviderClass(this.config);
57
+ const provider = new ProviderClass(this.config)
60
58
  this.providers.push({
61
59
  name: provider.getName(),
62
60
  instance: provider,
63
61
  available: provider.isAvailable(),
64
- capabilities: provider.getCapabilities ? provider.getCapabilities() : {}
65
- });
62
+ capabilities: provider.getCapabilities ? provider.getCapabilities() : {},
63
+ })
66
64
 
67
65
  // Collect provider status for summary instead of individual messages
68
66
  } catch (error) {
69
- console.error(colors.errorMessage(`Failed to load provider ${name}: ${error.message}`));
67
+ console.error(colors.errorMessage(`Failed to load provider ${name}: ${error.message}`))
70
68
  }
71
69
  }
72
70
 
73
71
  if (!process.env.MCP_SERVER_MODE) {
74
- const available = this.providers.filter(p => p.available).length;
75
- const isDevelopmentProvider = name => ['dummy', 'mock'].includes(name);
76
- const productionProviders = this.providers.filter(p => !isDevelopmentProvider(p.name));
77
- const availableProduction = productionProviders.filter(p => p.available);
72
+ const _available = this.providers.filter((p) => p.available).length
73
+ const isDevelopmentProvider = (name) => ['dummy', 'mock'].includes(name)
74
+ const productionProviders = this.providers.filter((p) => !isDevelopmentProvider(p.name))
75
+ const availableProduction = productionProviders.filter((p) => p.available)
78
76
 
79
77
  if (availableProduction.length > 0) {
80
- console.log(colors.infoMessage(`✅ ${availableProduction.length} provider${availableProduction.length > 1 ? 's' : ''} ready: ${availableProduction.map(p => p.name).join(', ')}`));
78
+ console.log(
79
+ colors.infoMessage(
80
+ `✅ ${availableProduction.length} provider${availableProduction.length > 1 ? 's' : ''} ready: ${availableProduction.map((p) => p.name).join(', ')}`
81
+ )
82
+ )
81
83
  }
82
84
  }
83
85
  } catch (error) {
84
- console.error(colors.errorMessage(`Failed to load providers: ${error.message}`));
85
- this.providers = [];
86
+ console.error(colors.errorMessage(`Failed to load providers: ${error.message}`))
87
+ this.providers = []
86
88
  }
87
89
  }
88
90
 
@@ -90,55 +92,75 @@ export class ProviderManagerService {
90
92
  * Determine the active provider based on configuration and availability
91
93
  */
92
94
  determineActiveProvider() {
93
- const { AI_PROVIDER: requestedProvider } = this.config;
95
+ const { AI_PROVIDER: requestedProvider } = this.config
94
96
 
95
97
  // Handle explicit provider request
96
98
  if (requestedProvider && requestedProvider.toLowerCase() !== 'auto') {
97
- const provider = this.findProviderByName(requestedProvider);
99
+ const provider = this.findProviderByName(requestedProvider)
98
100
 
99
- if (provider && provider.instance.isAvailable()) {
100
- this.activeProvider = provider.instance;
101
+ if (provider?.instance.isAvailable()) {
102
+ this.activeProvider = provider.instance
101
103
  if (!process.env.MCP_SERVER_MODE) {
102
- console.log(colors.successMessage(`🎯 Using provider: ${provider.instance.getName()}`));
104
+ console.log(colors.successMessage(`🎯 Using provider: ${provider.instance.getName()}`))
103
105
  }
104
- return;
105
- } else if (provider) {
106
- console.log(colors.warningMessage(`Requested provider ${requestedProvider} is not available, auto-selecting...`));
106
+ return
107
+ }
108
+ if (provider) {
109
+ console.log(
110
+ colors.warningMessage(
111
+ `Requested provider ${requestedProvider} is not available, auto-selecting...`
112
+ )
113
+ )
107
114
  } else {
108
- console.log(colors.warningMessage(`Requested provider ${requestedProvider} not found, auto-selecting...`));
115
+ console.log(
116
+ colors.warningMessage(
117
+ `Requested provider ${requestedProvider} not found, auto-selecting...`
118
+ )
119
+ )
109
120
  }
110
121
  }
111
122
 
112
123
  // Auto-select the first available provider
113
- const availableProviders = this.providers.filter(p => p.available);
124
+ const availableProviders = this.providers.filter((p) => p.available)
114
125
 
115
126
  if (availableProviders.length === 0) {
116
- console.log(colors.warningMessage('⚠️ No AI providers configured'));
117
- console.log(colors.infoMessage('💡 To enable AI-powered analysis:'));
118
- console.log(colors.infoMessage(' 1. Run: ai-changelog init'));
119
- console.log(colors.infoMessage(' 2. Or set API keys in .env.local'));
120
- console.log(colors.infoMessage(' 3. Supported providers: OpenAI, Anthropic, Azure, Google'));
121
- console.log(colors.dim(' Using pattern-based analysis for now...'));
122
- this.activeProvider = null;
123
- return;
127
+ console.log(colors.warningMessage('⚠️ No AI providers configured'))
128
+ console.log(colors.infoMessage('💡 To enable AI-powered analysis:'))
129
+ console.log(colors.infoMessage(' 1. Run: ai-changelog init'))
130
+ console.log(colors.infoMessage(' 2. Or set API keys in .env.local'))
131
+ console.log(colors.infoMessage(' 3. Supported providers: OpenAI, Anthropic, Azure, Google'))
132
+ console.log(colors.dim(' Using pattern-based analysis for now...'))
133
+ this.activeProvider = null
134
+ return
124
135
  }
125
136
 
126
137
  // Priority order for auto-selection
127
- const priorityOrder = ['openai', 'anthropic', 'azure', 'google', 'vertex', 'huggingface', 'ollama', 'lmstudio'];
138
+ const priorityOrder = [
139
+ 'openai',
140
+ 'anthropic',
141
+ 'azure',
142
+ 'google',
143
+ 'vertex',
144
+ 'huggingface',
145
+ 'ollama',
146
+ 'lmstudio',
147
+ ]
128
148
 
129
149
  for (const providerName of priorityOrder) {
130
- const provider = availableProviders.find(p => p.name === providerName);
150
+ const provider = availableProviders.find((p) => p.name === providerName)
131
151
  if (provider) {
132
- this.activeProvider = provider.instance;
133
- console.log(colors.successMessage(`Auto-selected provider: ${provider.instance.getName()}`));
134
- return;
152
+ this.activeProvider = provider.instance
153
+ console.log(colors.successMessage(`Auto-selected provider: ${provider.instance.getName()}`))
154
+ return
135
155
  }
136
156
  }
137
157
 
138
158
  // Fallback to first available
139
- this.activeProvider = availableProviders[0].instance;
159
+ this.activeProvider = availableProviders[0].instance
140
160
  if (!process.env.MCP_SERVER_MODE) {
141
- console.log(colors.successMessage(`Using first available provider: ${this.activeProvider.getName()}`));
161
+ console.log(
162
+ colors.successMessage(`Using first available provider: ${this.activeProvider.getName()}`)
163
+ )
142
164
  }
143
165
  }
144
166
 
@@ -146,90 +168,90 @@ export class ProviderManagerService {
146
168
  * Get the active provider instance
147
169
  */
148
170
  getActiveProvider() {
149
- return this.activeProvider;
171
+ return this.activeProvider
150
172
  }
151
173
 
152
174
  /**
153
175
  * Get all loaded providers
154
176
  */
155
177
  getAllProviders() {
156
- return this.providers;
178
+ return this.providers
157
179
  }
158
180
 
159
181
  /**
160
182
  * Find provider by name
161
183
  */
162
184
  findProviderByName(name) {
163
- return this.providers.find(p => p.name.toLowerCase() === name.toLowerCase());
185
+ return this.providers.find((p) => p.name.toLowerCase() === name.toLowerCase())
164
186
  }
165
187
 
166
188
  /**
167
189
  * Switch to a different provider
168
190
  */
169
191
  switchProvider(providerName) {
170
- const provider = this.findProviderByName(providerName);
192
+ const provider = this.findProviderByName(providerName)
171
193
 
172
194
  if (!provider) {
173
195
  return {
174
196
  success: false,
175
- error: `Provider '${providerName}' not found`
176
- };
197
+ error: `Provider '${providerName}' not found`,
198
+ }
177
199
  }
178
200
 
179
201
  if (!provider.instance.isAvailable()) {
180
202
  return {
181
203
  success: false,
182
- error: `Provider '${providerName}' is not properly configured`
183
- };
204
+ error: `Provider '${providerName}' is not properly configured`,
205
+ }
184
206
  }
185
207
 
186
- this.activeProvider = provider.instance;
208
+ this.activeProvider = provider.instance
187
209
  return {
188
210
  success: true,
189
- provider: providerName
190
- };
211
+ provider: providerName,
212
+ }
191
213
  }
192
214
 
193
215
  /**
194
216
  * List all providers with their status
195
217
  */
196
218
  listProviders() {
197
- return this.providers.map(p => ({
219
+ return this.providers.map((p) => ({
198
220
  name: p.name,
199
221
  available: p.available,
200
222
  active: this.activeProvider?.getName() === p.name,
201
223
  capabilities: p.capabilities,
202
- configuration: p.instance.getConfiguration ? p.instance.getConfiguration() : {}
203
- }));
224
+ configuration: p.instance.getConfiguration ? p.instance.getConfiguration() : {},
225
+ }))
204
226
  }
205
227
 
206
228
  /**
207
229
  * Test connection to a specific provider
208
230
  */
209
231
  async testProvider(providerName) {
210
- const provider = this.findProviderByName(providerName);
232
+ const provider = this.findProviderByName(providerName)
211
233
 
212
234
  if (!provider) {
213
235
  return {
214
236
  success: false,
215
- error: `Provider '${providerName}' not found`
216
- };
237
+ error: `Provider '${providerName}' not found`,
238
+ }
217
239
  }
218
240
 
219
241
  if (!provider.instance.isAvailable()) {
220
242
  return {
221
243
  success: false,
222
- error: `Provider '${providerName}' is not properly configured`
223
- };
244
+ error: `Provider '${providerName}' is not properly configured`,
245
+ }
224
246
  }
225
247
 
226
248
  try {
227
- return await provider.instance.testConnection();
249
+ return await provider.instance.testConnection()
228
250
  } catch (error) {
229
251
  return {
230
252
  success: false,
231
- error: error.message
232
- };
253
+ error: error.message,
254
+ }
233
255
  }
234
256
  }
235
257
 
@@ -237,61 +259,61 @@ export class ProviderManagerService {
237
259
  * Get provider capabilities
238
260
  */
239
261
  getProviderCapabilities(providerName) {
240
- const provider = this.findProviderByName(providerName);
262
+ const provider = this.findProviderByName(providerName)
241
263
 
242
264
  if (!provider) {
243
- return null;
265
+ return null
244
266
  }
245
267
 
246
- return provider.instance.getCapabilities ? provider.instance.getCapabilities() : {};
268
+ return provider.instance.getCapabilities ? provider.instance.getCapabilities() : {}
247
269
  }
248
270
 
249
271
  /**
250
272
  * Validate all providers
251
273
  */
252
274
  async validateAll() {
253
- const results = {};
275
+ const results = {}
254
276
 
255
277
  for (const provider of this.providers) {
256
278
  if (provider.available) {
257
279
  try {
258
- results[provider.name] = await provider.instance.testConnection();
280
+ results[provider.name] = await provider.instance.testConnection()
259
281
  } catch (error) {
260
282
  results[provider.name] = {
261
283
  success: false,
262
- error: error.message
263
- };
284
+ error: error.message,
285
+ }
264
286
  }
265
287
  } else {
266
288
  results[provider.name] = {
267
289
  success: false,
268
- error: 'Provider not configured'
269
- };
290
+ error: 'Provider not configured',
291
+ }
270
292
  }
271
293
  }
272
294
 
273
- return results;
295
+ return results
274
296
  }
275
297
 
276
298
  /**
277
299
  * Get provider statistics
278
300
  */
279
301
  getStats() {
280
- const total = this.providers.length;
281
- const available = this.providers.filter(p => p.available).length;
282
- const configured = available;
302
+ const total = this.providers.length
303
+ const available = this.providers.filter((p) => p.available).length
304
+ const configured = available
283
305
 
284
306
  return {
285
307
  total,
286
308
  available,
287
309
  configured,
288
310
  active: this.activeProvider?.getName() || null,
289
- providers: this.providers.map(p => ({
311
+ providers: this.providers.map((p) => ({
290
312
  name: p.name,
291
313
  available: p.available,
292
- active: this.activeProvider?.getName() === p.name
293
- }))
294
- };
314
+ active: this.activeProvider?.getName() === p.name,
315
+ })),
316
+ }
295
317
  }
296
318
 
297
319
  /**
@@ -299,20 +321,20 @@ export class ProviderManagerService {
299
321
  */
300
322
  reload(newConfig = null) {
301
323
  if (newConfig) {
302
- this.config = { ...this.config, ...newConfig };
324
+ this.config = { ...this.config, ...newConfig }
303
325
  }
304
326
 
305
- this.providers = [];
306
- this.activeProvider = null;
307
- this.loadProviders();
308
- this.determineActiveProvider();
327
+ this.providers = []
328
+ this.activeProvider = null
329
+ this.loadProviders()
330
+ this.determineActiveProvider()
309
331
  }
310
332
 
311
333
  /**
312
334
  * Check if any provider is available
313
335
  */
314
336
  hasAvailableProvider() {
315
- return this.activeProvider !== null;
337
+ return this.activeProvider !== null
316
338
  }
317
339
 
318
340
  /**
@@ -320,44 +342,42 @@ export class ProviderManagerService {
320
342
  */
321
343
  getAvailableProviders() {
322
344
  return this.providers
323
- .filter(p => p.available)
324
- .map(p => ({
345
+ .filter((p) => p.available)
346
+ .map((p) => ({
325
347
  name: p.name,
326
348
  instance: p.instance,
327
- capabilities: p.capabilities
328
- }));
349
+ capabilities: p.capabilities,
350
+ }))
329
351
  }
330
352
 
331
353
  /**
332
354
  * Get simple list of available provider names
333
355
  */
334
356
  getAvailableProviderNames() {
335
- return this.providers
336
- .filter(p => p.available)
337
- .map(p => p.name);
357
+ return this.providers.filter((p) => p.available).map((p) => p.name)
338
358
  }
339
359
 
340
360
  /**
341
361
  * Get configured provider priority order
342
362
  */
343
363
  getProviderPriority() {
344
- return ['openai', 'anthropic', 'azure', 'google', 'vertex', 'huggingface', 'ollama', 'lmstudio'];
364
+ return ['openai', 'anthropic', 'azure', 'google', 'vertex', 'huggingface', 'ollama', 'lmstudio']
345
365
  }
346
366
 
347
367
  /**
348
368
  * Validate if provider name exists in available providers
349
369
  */
350
370
  validateProviderName(name) {
351
- return Object.keys(this.providerClasses).includes(name.toLowerCase());
371
+ return Object.keys(this.providerClasses).includes(name.toLowerCase())
352
372
  }
353
373
 
354
374
  /**
355
375
  * Get the default fallback provider
356
376
  */
357
377
  getDefaultProvider() {
358
- return this.findProviderByName(this.options.defaultProviderName);
378
+ return this.findProviderByName(this.options.defaultProviderName)
359
379
  }
360
380
  }
361
381
 
362
382
  // Backward compatibility export
363
- export default ProviderManagerService;
383
+ export default ProviderManagerService