@entro314labs/ai-changelog-generator 3.0.5 → 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 -785
  2. package/README.md +30 -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 +84 -52
  9. package/src/ai-changelog-generator.js +83 -81
  10. package/src/application/orchestrators/changelog.orchestrator.js +1040 -296
  11. package/src/application/services/application.service.js +145 -123
  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 +415 -247
  20. package/src/infrastructure/config/configuration.manager.js +220 -190
  21. package/src/infrastructure/interactive/interactive-staging.service.js +332 -0
  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 +556 -0
  43. package/src/shared/constants/colors.js +467 -172
  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 +1299 -775
  51. package/types/index.d.ts +353 -344
@@ -3,22 +3,18 @@
3
3
  * Used for testing without real API credentials
4
4
  */
5
5
 
6
- import { BaseProvider } from '../core/base-provider.js';
7
- import { ProviderError } from '../../../shared/utils/utils.js';
6
+ import { ProviderError } from '../../../shared/utils/utils.js'
7
+ import { BaseProvider } from '../core/base-provider.js'
8
8
 
9
9
  class MockProvider extends BaseProvider {
10
10
  constructor(config = {}) {
11
- super(config);
12
- this.name = 'mock';
13
- this.mockResponses = config.MOCK_RESPONSES || {};
14
- this.shouldFail = config.MOCK_SHOULD_FAIL === 'true';
15
- this.failureRate = parseFloat(config.MOCK_FAILURE_RATE || '0.1');
16
- this.latency = parseInt(config.MOCK_LATENCY || '500', 10);
17
- this.models = [
18
- 'mock-basic',
19
- 'mock-standard',
20
- 'mock-advanced'
21
- ];
11
+ super(config)
12
+ this.name = 'mock'
13
+ this.mockResponses = config.MOCK_RESPONSES || {}
14
+ this.shouldFail = config.MOCK_SHOULD_FAIL === 'true'
15
+ this.failureRate = Number.parseFloat(config.MOCK_FAILURE_RATE || '0.1')
16
+ this.latency = Number.parseInt(config.MOCK_LATENCY || '500', 10)
17
+ this.models = ['mock-basic', 'mock-standard', 'mock-advanced']
22
18
  }
23
19
 
24
20
  /**
@@ -26,7 +22,7 @@ class MockProvider extends BaseProvider {
26
22
  * @returns {string} Provider name
27
23
  */
28
24
  getName() {
29
- return this.name;
25
+ return this.name
30
26
  }
31
27
 
32
28
  /**
@@ -34,7 +30,7 @@ class MockProvider extends BaseProvider {
34
30
  * @returns {boolean} Always true for mock provider
35
31
  */
36
32
  isAvailable() {
37
- return true;
33
+ return true
38
34
  }
39
35
 
40
36
  /**
@@ -43,7 +39,7 @@ class MockProvider extends BaseProvider {
43
39
  * @returns {Promise} - Promise that resolves after delay
44
40
  */
45
41
  async _simulateLatency() {
46
- return new Promise(resolve => setTimeout(resolve, this.latency));
42
+ return new Promise((resolve) => setTimeout(resolve, this.latency))
47
43
  }
48
44
 
49
45
  /**
@@ -52,9 +48,9 @@ class MockProvider extends BaseProvider {
52
48
  */
53
49
  _shouldFailOperation() {
54
50
  if (this.shouldFail) {
55
- return true;
51
+ return true
56
52
  }
57
- return Math.random() < this.failureRate;
53
+ return Math.random() < this.failureRate
58
54
  }
59
55
 
60
56
  /**
@@ -64,7 +60,7 @@ class MockProvider extends BaseProvider {
64
60
  * @returns {Promise} - Promise that resolves with mock completion
65
61
  */
66
62
  async generateCompletion(messages, options = {}) {
67
- await this._simulateLatency();
63
+ await this._simulateLatency()
68
64
 
69
65
  if (this._shouldFailOperation()) {
70
66
  throw new ProviderError(
@@ -72,22 +68,20 @@ class MockProvider extends BaseProvider {
72
68
  'mock_error',
73
69
  this.getName(),
74
70
  options.model || 'mock-standard'
75
- );
71
+ )
76
72
  }
77
73
 
78
- const model = options.model || 'mock-standard';
79
- const lastMessage = messages[messages.length - 1];
80
- const prompt = typeof lastMessage === 'string'
81
- ? lastMessage
82
- : (lastMessage.content || '');
74
+ const model = options.model || 'mock-standard'
75
+ const lastMessage = messages.at(-1)
76
+ const prompt = typeof lastMessage === 'string' ? lastMessage : lastMessage.content || ''
83
77
 
84
78
  // Check for predefined responses
85
79
  if (this.mockResponses[prompt]) {
86
80
  return {
87
81
  content: this.mockResponses[prompt],
88
82
  tokens: this.mockResponses[prompt].length / 4, // Rough estimate
89
- model: model
90
- };
83
+ model,
84
+ }
91
85
  }
92
86
 
93
87
  // Generate mock response based on commit convention if it looks like a commit message
@@ -95,16 +89,16 @@ class MockProvider extends BaseProvider {
95
89
  return {
96
90
  content: this._generateMockChangelog(prompt),
97
91
  tokens: 150,
98
- model: model
99
- };
92
+ model,
93
+ }
100
94
  }
101
95
 
102
96
  // Default mock response
103
97
  return {
104
98
  content: `This is a mock response from the ${model} model.`,
105
99
  tokens: 10,
106
- model: model
107
- };
100
+ model,
101
+ }
108
102
  }
109
103
 
110
104
  /**
@@ -113,15 +107,17 @@ class MockProvider extends BaseProvider {
113
107
  * @returns {string} - Mock changelog entry
114
108
  */
115
109
  _generateMockChangelog(commitMessage) {
116
- const typeMatch = commitMessage.match(/^(feat|fix|docs|style|refactor|perf|test|build|ci|chore)(\(.+\))?:/);
110
+ const typeMatch = commitMessage.match(
111
+ /^(feat|fix|docs|style|refactor|perf|test|build|ci|chore)(\(.+\))?:/
112
+ )
117
113
 
118
114
  if (!typeMatch) {
119
- return 'Mock changelog entry for conventional commit';
115
+ return 'Mock changelog entry for conventional commit'
120
116
  }
121
117
 
122
- const type = typeMatch[1];
123
- const scope = typeMatch[2] ? typeMatch[2].replace(/[()]/g, '') : '';
124
- const description = commitMessage.split(':')[1]?.trim() || '';
118
+ const type = typeMatch[1]
119
+ const scope = typeMatch[2] ? typeMatch[2].replace(/[()]/g, '') : ''
120
+ const description = commitMessage.split(':')[1]?.trim() || ''
125
121
 
126
122
  const typeMap = {
127
123
  feat: 'Feature',
@@ -133,11 +129,11 @@ class MockProvider extends BaseProvider {
133
129
  test: 'Tests',
134
130
  build: 'Build',
135
131
  ci: 'CI',
136
- chore: 'Chore'
137
- };
132
+ chore: 'Chore',
133
+ }
138
134
 
139
- const title = `${typeMap[type]}${scope ? ` (${scope})` : ''}`;
140
- return `### ${title}\n\n- ${description}\n`;
135
+ const title = `${typeMap[type]}${scope ? ` (${scope})` : ''}`
136
+ return `### ${title}\n\n- ${description}\n`
141
137
  }
142
138
 
143
139
  /**
@@ -149,29 +145,29 @@ class MockProvider extends BaseProvider {
149
145
  if (!commitInfo.message) {
150
146
  return {
151
147
  model: 'mock-standard',
152
- reason: 'Default model selected due to insufficient commit information'
153
- };
148
+ reason: 'Default model selected due to insufficient commit information',
149
+ }
154
150
  }
155
151
 
156
152
  // Simple logic based on commit complexity
157
- const filesChanged = commitInfo.files?.length || 0;
158
- const linesChanged = (commitInfo.additions || 0) + (commitInfo.deletions || 0);
153
+ const filesChanged = commitInfo.files?.length || 0
154
+ const linesChanged = (commitInfo.additions || 0) + (commitInfo.deletions || 0)
159
155
 
160
156
  if (filesChanged > 10 || linesChanged > 500 || commitInfo.breaking) {
161
157
  return {
162
158
  model: 'mock-advanced',
163
- reason: 'Complex commit detected, using advanced model'
164
- };
165
- } else if (filesChanged > 3 || linesChanged > 100) {
159
+ reason: 'Complex commit detected, using advanced model',
160
+ }
161
+ }
162
+ if (filesChanged > 3 || linesChanged > 100) {
166
163
  return {
167
164
  model: 'mock-standard',
168
- reason: 'Moderate commit detected, using standard model'
169
- };
170
- } else {
171
- return {
172
- model: 'mock-basic',
173
- reason: 'Simple commit detected, using basic model'
174
- };
165
+ reason: 'Moderate commit detected, using standard model',
166
+ }
167
+ }
168
+ return {
169
+ model: 'mock-basic',
170
+ reason: 'Simple commit detected, using basic model',
175
171
  }
176
172
  }
177
173
 
@@ -181,23 +177,23 @@ class MockProvider extends BaseProvider {
181
177
  * @returns {Promise} - Promise that resolves with validation result
182
178
  */
183
179
  async validateModelAvailability(model) {
184
- await this._simulateLatency();
180
+ await this._simulateLatency()
185
181
 
186
182
  if (this._shouldFailOperation()) {
187
183
  return {
188
184
  available: false,
189
185
  error: 'Mock validation failure',
190
- alternatives: this.models
191
- };
186
+ alternatives: this.models,
187
+ }
192
188
  }
193
189
 
194
- const isAvailable = this.models.includes(model);
190
+ const isAvailable = this.models.includes(model)
195
191
 
196
192
  return {
197
193
  available: isAvailable,
198
194
  model: isAvailable ? model : null,
199
- alternatives: isAvailable ? [] : this.models
200
- };
195
+ alternatives: isAvailable ? [] : this.models,
196
+ }
201
197
  }
202
198
 
203
199
  /**
@@ -205,22 +201,22 @@ class MockProvider extends BaseProvider {
205
201
  * @returns {Promise} - Promise that resolves with connection test result
206
202
  */
207
203
  async testConnection() {
208
- await this._simulateLatency();
204
+ await this._simulateLatency()
209
205
 
210
206
  if (this._shouldFailOperation()) {
211
207
  return {
212
208
  success: false,
213
209
  error: 'Mock connection failure',
214
- provider: this.getName()
215
- };
210
+ provider: this.getName(),
211
+ }
216
212
  }
217
213
 
218
214
  return {
219
215
  success: true,
220
216
  provider: this.getName(),
221
217
  model: 'mock-standard',
222
- response: 'Mock connection successful'
223
- };
218
+ response: 'Mock connection successful',
219
+ }
224
220
  }
225
221
 
226
222
  /**
@@ -232,8 +228,8 @@ class MockProvider extends BaseProvider {
232
228
  streaming: false,
233
229
  tool_use: true,
234
230
  vision: false,
235
- json_mode: true
236
- };
231
+ json_mode: true,
232
+ }
237
233
  }
238
234
 
239
235
  getAvailableModels() {
@@ -246,7 +242,7 @@ class MockProvider extends BaseProvider {
246
242
  inputCost: 0,
247
243
  outputCost: 0,
248
244
  features: ['text', 'testing'],
249
- description: 'Basic mock model for simple testing'
245
+ description: 'Basic mock model for simple testing',
250
246
  },
251
247
  {
252
248
  id: 'mock-standard',
@@ -256,7 +252,7 @@ class MockProvider extends BaseProvider {
256
252
  inputCost: 0,
257
253
  outputCost: 0,
258
254
  features: ['text', 'tools', 'testing'],
259
- description: 'Standard mock model for moderate testing'
255
+ description: 'Standard mock model for moderate testing',
260
256
  },
261
257
  {
262
258
  id: 'mock-advanced',
@@ -266,10 +262,10 @@ class MockProvider extends BaseProvider {
266
262
  inputCost: 0,
267
263
  outputCost: 0,
268
264
  features: ['text', 'tools', 'json', 'testing'],
269
- description: 'Advanced mock model for complex testing scenarios'
270
- }
271
- ];
265
+ description: 'Advanced mock model for complex testing scenarios',
266
+ },
267
+ ]
272
268
  }
273
269
  }
274
270
 
275
- export default MockProvider;
271
+ export default MockProvider
@@ -1,34 +1,37 @@
1
- import { Ollama } from 'ollama';
2
- import { BaseProvider } from '../core/base-provider.js';
3
- import { ProviderError } from '../../../shared/utils/utils.js';
4
- import { applyMixins } from '../utils/base-provider-helpers.js';
5
- import { buildClientOptions } from '../utils/provider-utils.js';
1
+ import process from 'node:process'
2
+
3
+ import { Ollama } from 'ollama'
4
+
5
+ import { ProviderError } from '../../../shared/utils/utils.js'
6
+ import { BaseProvider } from '../core/base-provider.js'
7
+ import { applyMixins } from '../utils/base-provider-helpers.js'
8
+ import { buildClientOptions } from '../utils/provider-utils.js'
6
9
 
7
10
  class OllamaProvider extends BaseProvider {
8
11
  constructor(config) {
9
- super(config);
10
- this.client = null;
12
+ super(config)
13
+ this.client = null
11
14
  if (this.isAvailable()) {
12
- this.initializeClient();
15
+ this.initializeClient()
13
16
  }
14
17
  }
15
18
 
16
19
  initializeClient() {
17
20
  const clientOptions = buildClientOptions(this.getProviderConfig(), {
18
- host: 'http://localhost:11434'
19
- });
21
+ host: 'http://localhost:11434',
22
+ })
20
23
 
21
24
  this.client = new Ollama({
22
- host: clientOptions.host
23
- });
25
+ host: clientOptions.host,
26
+ })
24
27
  }
25
28
 
26
29
  getName() {
27
- return 'ollama';
30
+ return 'ollama'
28
31
  }
29
32
 
30
33
  isAvailable() {
31
- return !!this.config.OLLAMA_HOST;
34
+ return !!this.config.OLLAMA_HOST
32
35
  }
33
36
 
34
37
  async generateCompletion(messages, options = {}) {
@@ -36,116 +39,136 @@ class OllamaProvider extends BaseProvider {
36
39
  return this.handleProviderError(
37
40
  new Error('Ollama provider is not configured'),
38
41
  'generate_completion'
39
- );
42
+ )
40
43
  }
41
44
 
42
45
  try {
43
46
  // Test connection first time if not already done
44
47
  if (!this._connectionTested) {
45
48
  try {
46
- await this.client.list();
47
- this._connectionTested = true;
49
+ await this.client.list()
50
+ this._connectionTested = true
48
51
  } catch (connectionError) {
49
52
  return this.handleProviderError(
50
- new Error(`Ollama server unreachable: ${connectionError.message}. Please run 'ollama serve' first.`),
53
+ new Error(
54
+ `Ollama server unreachable: ${connectionError.message}. Please run 'ollama serve' first.`
55
+ ),
51
56
  'generate_completion'
52
- );
57
+ )
53
58
  }
54
59
  }
55
60
 
56
- const modelConfig = this.getProviderModelConfig();
57
- const modelName = options.model || modelConfig.standardModel;
58
-
59
- const params = {
60
- model: modelName,
61
- messages: messages,
62
- stream: !!options.stream,
63
- options: {
64
- temperature: options.temperature || 0.7,
65
- top_p: options.top_p || 0.9,
66
- num_predict: options.max_tokens || 1024,
67
- stop: options.stop || [],
68
- },
69
- };
61
+ const modelConfig = this.getProviderModelConfig()
62
+ const modelName = options.model || modelConfig.standardModel
63
+
64
+ const params = {
65
+ model: modelName,
66
+ messages,
67
+ stream: !!options.stream,
68
+ options: {
69
+ temperature: options.temperature || 0.7,
70
+ top_p: options.top_p || 0.9,
71
+ num_predict: options.max_tokens || 1024,
72
+ stop: options.stop || [],
73
+ },
74
+ }
70
75
 
71
- if (options.tools && this.getCapabilities(modelName).tool_use) {
72
- params.tools = options.tools;
73
- }
76
+ if (options.tools && this.getCapabilities(modelName).tool_use) {
77
+ params.tools = options.tools
78
+ }
74
79
 
75
- if (options.response_format?.type === 'json_object' && this.getCapabilities(modelName).json_mode) {
76
- params.format = 'json';
77
- }
80
+ if (
81
+ options.response_format?.type === 'json_object' &&
82
+ this.getCapabilities(modelName).json_mode
83
+ ) {
84
+ params.format = 'json'
85
+ }
78
86
 
79
- if (params.stream) {
80
- const stream = await this.client.chat(params);
81
- return { stream, model: modelName };
82
- }
87
+ if (params.stream) {
88
+ const stream = await this.client.chat(params)
89
+ return { stream, model: modelName }
90
+ }
83
91
 
84
- const response = await this.client.chat(params);
92
+ const response = await this.client.chat(params)
85
93
  return {
86
94
  content: response.message.content,
87
95
  model: response.model,
88
96
  tokens: response.eval_count,
89
97
  finish_reason: response.done ? 'stop' : 'incomplete',
90
98
  tool_calls: response.message.tool_calls,
91
- };
99
+ }
92
100
  } catch (error) {
93
- return this.handleProviderError(error, 'generate_completion', { model: options.model });
101
+ return this.handleProviderError(error, 'generate_completion', { model: options.model })
94
102
  }
95
103
  }
96
104
 
97
105
  async generateEmbedding(text, options = {}) {
98
106
  if (!this.isAvailable()) {
99
- throw new ProviderError('Ollama provider is not configured', 'ollama', 'isAvailable');
107
+ throw new ProviderError('Ollama provider is not configured', 'ollama', 'isAvailable')
100
108
  }
101
109
 
102
- const modelName = options.model || this.config.OLLAMA_EMBEDDING_MODEL || this.config.AI_MODEL_EMBEDDING || 'nomic-embed-text';
110
+ const modelName =
111
+ options.model ||
112
+ this.config.OLLAMA_EMBEDDING_MODEL ||
113
+ this.config.AI_MODEL_EMBEDDING ||
114
+ 'nomic-embed-text'
103
115
 
104
116
  const response = await this.client.embeddings({
105
117
  model: modelName,
106
118
  prompt: text,
107
119
  options: {
108
- temperature: options.temperature || 0.0
109
- }
110
- });
120
+ temperature: options.temperature || 0.0,
121
+ },
122
+ })
111
123
 
112
124
  return {
113
125
  embedding: response.embedding,
114
126
  model: modelName,
115
- tokens: response.token_count || 0
116
- };
127
+ tokens: response.token_count || 0,
128
+ }
117
129
  }
118
130
 
119
131
  // Ollama-specific helper methods
120
132
  async getAvailableModels() {
121
- if (!this.isAvailable()) return [];
133
+ if (!this.isAvailable()) {
134
+ return []
135
+ }
122
136
  try {
123
- const response = await this.client.list();
124
- return response.models.map(m => m.name);
137
+ const response = await this.client.list()
138
+ return response.models.map((m) => m.name)
125
139
  } catch (error) {
126
140
  // Only log connection errors in development mode or when explicitly used
127
- if (!this._connectionErrorLogged && (process.env.NODE_ENV === 'development' || process.env.DEBUG)) {
128
- console.warn(`⚠️ Ollama connection failed: ${error.message}`);
129
- console.warn(`💡 Make sure Ollama is running: ollama serve`);
130
- this._connectionErrorLogged = true;
141
+ if (
142
+ !this._connectionErrorLogged &&
143
+ (process.env.NODE_ENV === 'development' || process.env.DEBUG)
144
+ ) {
145
+ console.warn(`⚠️ Ollama connection failed: ${error.message}`)
146
+ console.warn('💡 Make sure Ollama is running: ollama serve')
147
+ this._connectionErrorLogged = true
131
148
  }
132
- return [];
149
+ return []
133
150
  }
134
151
  }
135
152
 
136
153
  async pullModel(modelName) {
137
154
  if (!this.isAvailable()) {
138
- throw new ProviderError('Ollama provider is not configured', 'ollama', 'isAvailable');
155
+ throw new ProviderError('Ollama provider is not configured', 'ollama', 'isAvailable')
139
156
  }
140
157
 
141
158
  try {
142
- const pullStream = await this.client.pull({ model: modelName, stream: true });
143
- return { stream: pullStream, model: modelName };
159
+ const pullStream = await this.client.pull({ model: modelName, stream: true })
160
+ return { stream: pullStream, model: modelName }
144
161
  } catch (error) {
145
- throw new ProviderError(`Failed to pull model ${modelName}: ${error.message}`, 'ollama', 'pullModel', error, { modelName });
162
+ throw new ProviderError(
163
+ `Failed to pull model ${modelName}: ${error.message}`,
164
+ 'ollama',
165
+ 'pullModel',
166
+ error,
167
+ { modelName }
168
+ )
146
169
  }
147
170
  }
148
171
  }
149
172
 
150
173
  // Apply mixins to add standard provider functionality
151
- export default applyMixins(OllamaProvider, 'ollama');
174
+ export default applyMixins(OllamaProvider, 'ollama')