@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
@@ -4,21 +4,21 @@
4
4
  * Supports Gemini 2.5, 2.0 and 1.5 models
5
5
  */
6
6
 
7
- import { GoogleGenAI } from '@google/genai';
8
- import { BaseProvider } from '../core/base-provider.js';
9
- import { ProviderError } from '../../../shared/utils/utils.js';
10
- import { applyMixins } from '../utils/base-provider-helpers.js';
11
- import { buildClientOptions } from '../utils/provider-utils.js';
7
+ import { GoogleGenAI } from '@google/genai'
8
+
9
+ import { BaseProvider } from '../core/base-provider.js'
10
+ import { applyMixins } from '../utils/base-provider-helpers.js'
11
+ import { buildClientOptions } from '../utils/provider-utils.js'
12
12
 
13
13
  class GoogleProvider extends BaseProvider {
14
14
  constructor(config) {
15
- super(config);
16
- this.genAI = null;
17
- this.modelCache = new Map();
18
- this.maxCacheSize = 50; // Limit cache size to prevent memory leaks
15
+ super(config)
16
+ this.genAI = null
17
+ this.modelCache = new Map()
18
+ this.maxCacheSize = 50 // Limit cache size to prevent memory leaks
19
19
 
20
20
  if (this.isAvailable()) {
21
- this.initializeClient();
21
+ this.initializeClient()
22
22
  }
23
23
  }
24
24
 
@@ -26,29 +26,31 @@ class GoogleProvider extends BaseProvider {
26
26
  const clientOptions = buildClientOptions(this.getProviderConfig(), {
27
27
  apiVersion: 'v1',
28
28
  timeout: 60000,
29
- maxRetries: 2
30
- });
29
+ maxRetries: 2,
30
+ })
31
31
 
32
32
  this.genAI = new GoogleGenAI({
33
33
  apiKey: clientOptions.apiKey,
34
34
  apiVersion: clientOptions.apiVersion,
35
35
  apiEndpoint: clientOptions.apiEndpoint,
36
36
  timeout: clientOptions.timeout,
37
- retry: clientOptions.maxRetries ? {
38
- retries: clientOptions.maxRetries,
39
- factor: 2,
40
- minTimeout: 1000,
41
- maxTimeout: 60000
42
- } : undefined
43
- });
37
+ retry: clientOptions.maxRetries
38
+ ? {
39
+ retries: clientOptions.maxRetries,
40
+ factor: 2,
41
+ minTimeout: 1000,
42
+ maxTimeout: 60000,
43
+ }
44
+ : undefined,
45
+ })
44
46
  }
45
47
 
46
48
  getName() {
47
- return 'google';
49
+ return 'google'
48
50
  }
49
51
 
50
52
  isAvailable() {
51
- return !!this.config.GOOGLE_API_KEY;
53
+ return !!this.config.GOOGLE_API_KEY
52
54
  }
53
55
 
54
56
  async generateCompletion(messages, options = {}) {
@@ -56,43 +58,44 @@ class GoogleProvider extends BaseProvider {
56
58
  return this.handleProviderError(
57
59
  new Error('Google provider is not configured'),
58
60
  'generate_completion'
59
- );
61
+ )
60
62
  }
61
63
 
62
64
  try {
63
- const modelConfig = this.getProviderModelConfig();
64
- const modelName = options.model || modelConfig.standardModel;
65
+ const modelConfig = this.getProviderModelConfig()
66
+ const modelName = options.model || modelConfig.standardModel
65
67
 
66
- const systemInstruction = messages.find(m => m.role === 'system')?.content;
68
+ const systemInstruction = messages.find((m) => m.role === 'system')?.content
67
69
 
68
70
  // Separate system instruction from chat history
69
71
  const history = messages
70
- .filter(m => m.role !== 'system')
71
- .map(m => {
72
- const role = m.role === 'assistant' ? 'model' : 'user';
72
+ .filter((m) => m.role !== 'system')
73
+ .map((m) => {
74
+ const role = m.role === 'assistant' ? 'model' : 'user'
73
75
  if (typeof m.content === 'string') {
74
- return { role, parts: [{ text: m.content }] };
76
+ return { role, parts: [{ text: m.content }] }
75
77
  }
76
78
  if (Array.isArray(m.content)) {
77
- const parts = m.content.map(part => {
79
+ const parts = m.content.map((part) => {
78
80
  if (typeof part === 'string') {
79
- return { text: part };
80
- } else if (part.type === 'image_url') {
81
+ return { text: part }
82
+ }
83
+ if (part.type === 'image_url') {
81
84
  return {
82
85
  inlineData: {
83
86
  mimeType: 'image/jpeg',
84
87
  data: part.image_url.url.startsWith('data:image/')
85
88
  ? part.image_url.url.split(',')[1]
86
- : Buffer.from(part.image_url.url).toString('base64')
87
- }
88
- };
89
+ : Buffer.from(part.image_url.url).toString('base64'),
90
+ },
91
+ }
89
92
  }
90
- return { text: JSON.stringify(part) };
91
- });
92
- return { role, parts };
93
+ return { text: JSON.stringify(part) }
94
+ })
95
+ return { role, parts }
93
96
  }
94
- return { role, parts: [{ text: JSON.stringify(m.content) }] };
95
- });
97
+ return { role, parts: [{ text: JSON.stringify(m.content) }] }
98
+ })
96
99
 
97
100
  const generationConfig = {
98
101
  temperature: options.temperature || 0.4,
@@ -101,8 +104,9 @@ class GoogleProvider extends BaseProvider {
101
104
  topK: options.top_k || 64,
102
105
  candidateCount: options.n || 1,
103
106
  stopSequences: options.stop || [],
104
- responseMimeType: options.response_format?.type === 'json_object' ? 'application/json' : undefined
105
- };
107
+ responseMimeType:
108
+ options.response_format?.type === 'json_object' ? 'application/json' : undefined,
109
+ }
106
110
 
107
111
  const safetySettings = [
108
112
  {
@@ -120,59 +124,64 @@ class GoogleProvider extends BaseProvider {
120
124
  {
121
125
  category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
122
126
  threshold: 'BLOCK_MEDIUM_AND_ABOVE',
123
- }
124
- ];
127
+ },
128
+ ]
125
129
 
126
130
  const modelOptions = {
127
131
  model: modelName,
128
132
  generationConfig,
129
- safetySettings
130
- };
133
+ safetySettings,
134
+ }
131
135
 
132
136
  if (systemInstruction) {
133
- modelOptions.systemInstruction = systemInstruction;
137
+ modelOptions.systemInstruction = systemInstruction
134
138
  }
135
139
 
136
140
  if (options.tools && options.tools.length > 0) {
137
141
  try {
138
- const tools = options.tools.map(tool => ({
139
- functionDeclarations: [{
140
- name: tool.function.name,
141
- description: tool.function.description,
142
- parameters: tool.function.parameters
143
- }]
144
- }));
145
-
146
- const model = this.genAI.models.getModel(modelName);
142
+ const tools = options.tools.map((tool) => ({
143
+ functionDeclarations: [
144
+ {
145
+ name: tool.function.name,
146
+ description: tool.function.description,
147
+ parameters: tool.function.parameters,
148
+ },
149
+ ],
150
+ }))
151
+
152
+ const model = this.genAI.models.getModel(modelName)
147
153
  const chat = model.startChat({
148
154
  tools,
149
155
  history: history.slice(0, -1),
150
- systemInstruction: systemInstruction ? { text: systemInstruction } : undefined
151
- });
156
+ systemInstruction: systemInstruction ? { text: systemInstruction } : undefined,
157
+ })
152
158
 
153
- const lastMessage = history[history.length - 1];
154
- const result = await chat.sendMessage(lastMessage.parts);
155
- const response = result.response;
159
+ const lastMessage = history.at(-1)
160
+ const result = await chat.sendMessage(lastMessage.parts)
161
+ const response = result.response
156
162
 
157
- const functionCalls = response.functionCalls();
163
+ const functionCalls = response.functionCalls()
158
164
 
159
165
  return {
160
166
  content: response.text(),
161
167
  model: modelName,
162
- tool_calls: functionCalls.length > 0 ? functionCalls.map(call => ({
163
- id: `call_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
164
- type: 'function',
165
- function: {
166
- name: call.name,
167
- arguments: call.args ? JSON.stringify(call.args) : '{}'
168
- }
169
- })) : undefined,
168
+ tool_calls:
169
+ functionCalls.length > 0
170
+ ? functionCalls.map((call) => ({
171
+ id: `call_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`,
172
+ type: 'function',
173
+ function: {
174
+ name: call.name,
175
+ arguments: call.args ? JSON.stringify(call.args) : '{}',
176
+ },
177
+ }))
178
+ : undefined,
170
179
  tokens: response.usageMetadata?.totalTokenCount || 0,
171
- finish_reason: functionCalls.length > 0 ? 'tool_calls' : 'stop'
172
- };
180
+ finish_reason: functionCalls.length > 0 ? 'tool_calls' : 'stop',
181
+ }
173
182
  } catch (error) {
174
- console.error('Error in tool calling:', error);
175
- return this.handleProviderError(error, 'generate_completion', { model: options.model });
183
+ console.error('Error in tool calling:', error)
184
+ return this.handleProviderError(error, 'generate_completion', { model: options.model })
176
185
  }
177
186
  }
178
187
 
@@ -181,40 +190,40 @@ class GoogleProvider extends BaseProvider {
181
190
  model: modelName,
182
191
  generationConfig,
183
192
  safetySettings,
184
- systemInstruction: systemInstruction ? { text: systemInstruction } : undefined
185
- });
193
+ systemInstruction: systemInstruction ? { text: systemInstruction } : undefined,
194
+ })
186
195
 
187
196
  const result = await model.generateContent({
188
- contents: history
189
- });
197
+ contents: history,
198
+ })
190
199
 
191
- const response = await result.response;
200
+ const response = await result.response
192
201
 
193
202
  return {
194
203
  content: response.text(),
195
204
  model: modelName,
196
205
  tokens: response.usageMetadata?.totalTokenCount || 0,
197
- finish_reason: response.candidates?.[0]?.finishReason || 'stop'
198
- };
206
+ finish_reason: response.candidates?.[0]?.finishReason || 'stop',
207
+ }
199
208
  } catch (error) {
200
209
  // Handle rate limits and retries
201
210
  if (error.message.includes('rate limit') || error.message.includes('quota')) {
202
- const retryDelay = this.getRetryDelay();
203
- console.warn(`Rate limit hit, retrying in ${retryDelay}ms...`);
204
- await this.sleep(retryDelay);
205
- return this.generateCompletion(messages, options);
211
+ const retryDelay = this.getRetryDelay()
212
+ console.warn(`Rate limit hit, retrying in ${retryDelay}ms...`)
213
+ await this.sleep(retryDelay)
214
+ return this.generateCompletion(messages, options)
206
215
  }
207
- return this.handleProviderError(error, 'generate_completion', { model: options.model });
216
+ return this.handleProviderError(error, 'generate_completion', { model: options.model })
208
217
  }
209
218
  }
210
219
 
211
220
  // Google-specific helper methods
212
221
  getRetryDelay() {
213
- return Math.random() * 5000 + 1000; // Random delay between 1-6 seconds
222
+ return Math.random() * 5000 + 1000 // Random delay between 1-6 seconds
214
223
  }
215
224
 
216
225
  sleep(ms) {
217
- return new Promise(resolve => setTimeout(resolve, ms));
226
+ return new Promise((resolve) => setTimeout(resolve, ms))
218
227
  }
219
228
 
220
229
  getAvailableModels() {
@@ -224,50 +233,50 @@ class GoogleProvider extends BaseProvider {
224
233
  name: 'Gemini 2.5 Flash',
225
234
  contextWindow: 1048576, // 1M tokens
226
235
  maxOutput: 8192,
227
- inputCost: 0.00000075, // $0.75 per 1M tokens
228
- outputCost: 0.00000300, // $3.00 per 1M tokens
236
+ inputCost: 0.00000075, // $0.75 per 1M tokens
237
+ outputCost: 0.000003, // $3.00 per 1M tokens
229
238
  features: ['text', 'vision', 'tools', 'multimodal', 'speed'],
230
- description: 'Fastest Gemini 2.5 model for high-throughput tasks'
239
+ description: 'Fastest Gemini 2.5 model for high-throughput tasks',
231
240
  },
232
241
  {
233
242
  id: 'gemini-2.5-pro',
234
243
  name: 'Gemini 2.5 Pro',
235
244
  contextWindow: 2097152, // 2M tokens
236
245
  maxOutput: 8192,
237
- inputCost: 0.00000125, // $1.25 per 1M tokens
238
- outputCost: 0.00000500, // $5.00 per 1M tokens
246
+ inputCost: 0.00000125, // $1.25 per 1M tokens
247
+ outputCost: 0.000005, // $5.00 per 1M tokens
239
248
  features: ['text', 'vision', 'tools', 'reasoning', 'thinking'],
240
- description: 'Most capable Gemini model with thinking mode support'
249
+ description: 'Most capable Gemini model with thinking mode support',
241
250
  },
242
251
  {
243
252
  id: 'gemini-2.0-flash-001',
244
253
  name: 'Gemini 2.0 Flash',
245
254
  contextWindow: 1048576, // 1M tokens
246
255
  maxOutput: 8192,
247
- inputCost: 0.00000075, // $0.75 per 1M tokens
248
- outputCost: 0.00000300, // $3.00 per 1M tokens
256
+ inputCost: 0.00000075, // $0.75 per 1M tokens
257
+ outputCost: 0.000003, // $3.00 per 1M tokens
249
258
  features: ['text', 'vision', 'tools', 'multimodal', 'functions'],
250
- description: 'Multimodal capabilities with function calling'
259
+ description: 'Multimodal capabilities with function calling',
251
260
  },
252
261
  {
253
262
  id: 'gemini-1.5-pro',
254
263
  name: 'Gemini 1.5 Pro',
255
264
  contextWindow: 2097152, // 2M tokens
256
265
  maxOutput: 8192,
257
- inputCost: 0.00000125, // $1.25 per 1M tokens
258
- outputCost: 0.00000500, // $5.00 per 1M tokens
266
+ inputCost: 0.00000125, // $1.25 per 1M tokens
267
+ outputCost: 0.000005, // $5.00 per 1M tokens
259
268
  features: ['text', 'vision', 'tools'],
260
- description: 'High-intelligence model for complex reasoning'
269
+ description: 'High-intelligence model for complex reasoning',
261
270
  },
262
271
  {
263
272
  id: 'gemini-1.5-flash',
264
273
  name: 'Gemini 1.5 Flash',
265
274
  contextWindow: 1048576, // 1M tokens
266
275
  maxOutput: 8192,
267
- inputCost: 0.00000075, // $0.75 per 1M tokens
268
- outputCost: 0.00000300, // $3.00 per 1M tokens
276
+ inputCost: 0.00000075, // $0.75 per 1M tokens
277
+ outputCost: 0.000003, // $3.00 per 1M tokens
269
278
  features: ['text', 'vision', 'tools'],
270
- description: 'Fast and versatile performance across diverse tasks'
279
+ description: 'Fast and versatile performance across diverse tasks',
271
280
  },
272
281
  {
273
282
  id: 'gemini-1.5-flash-8b',
@@ -275,19 +284,19 @@ class GoogleProvider extends BaseProvider {
275
284
  contextWindow: 1048576, // 1M tokens
276
285
  maxOutput: 8192,
277
286
  inputCost: 0.000000375, // $0.375 per 1M tokens
278
- outputCost: 0.000000150, // $1.50 per 1M tokens
287
+ outputCost: 0.00000015, // $1.50 per 1M tokens
279
288
  features: ['text', 'vision'],
280
- description: 'High volume and lower intelligence tasks'
281
- }
282
- ];
289
+ description: 'High volume and lower intelligence tasks',
290
+ },
291
+ ]
283
292
  }
284
293
 
285
294
  getDefaultModel() {
286
- return 'gemini-2.5-flash';
295
+ return 'gemini-2.5-flash'
287
296
  }
288
297
 
289
298
  getRequiredEnvVars() {
290
- return ['GOOGLE_API_KEY'];
299
+ return ['GOOGLE_API_KEY']
291
300
  }
292
301
 
293
302
  getProviderModelConfig() {
@@ -299,8 +308,8 @@ class GoogleProvider extends BaseProvider {
299
308
  reasoningModel: 'gemini-2.5-pro',
300
309
  default: 'gemini-2.5-flash',
301
310
  temperature: 0.4,
302
- maxTokens: 8192
303
- };
311
+ maxTokens: 8192,
312
+ }
304
313
  }
305
314
 
306
315
  getModelCapabilities(modelName) {
@@ -311,10 +320,10 @@ class GoogleProvider extends BaseProvider {
311
320
  multimodal: true,
312
321
  largeContext: true,
313
322
  thinking: modelName.includes('2.5-pro'),
314
- speed: modelName.includes('flash')
315
- };
323
+ speed: modelName.includes('flash'),
324
+ }
316
325
  }
317
326
  }
318
327
 
319
328
  // Apply mixins to add standard provider functionality
320
- export default applyMixins(GoogleProvider, 'google');
329
+ export default applyMixins(GoogleProvider, 'google')