@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
@@ -4,10 +4,12 @@
4
4
  * Provides CLI error handling, validation, and user interface utilities
5
5
  */
6
6
 
7
- import fs from 'fs';
8
- import path from 'path';
9
- import { GitError, ConfigError, NetworkError, ErrorContext } from './error-classes.js';
10
- import colors from '../constants/colors.js';
7
+ import fs from 'node:fs'
8
+ import path from 'node:path'
9
+ import process from 'node:process'
10
+
11
+ import colors from '../constants/colors.js'
12
+ import { ConfigError, ErrorContext, GitError, NetworkError } from './error-classes.js'
11
13
 
12
14
  /**
13
15
  * Enhanced error handling for CLI applications with context-aware tips
@@ -16,23 +18,23 @@ import colors from '../constants/colors.js';
16
18
  * @param {Object} options - Error handling options
17
19
  */
18
20
  export function handleCLIError(error, context = 'application', options = {}) {
19
- const { exitOnError = true, showTips = true, showStack = false } = options;
20
-
21
- console.error(colors.errorMessage(`❌ Failed to ${context}: ${error.message}`));
22
-
21
+ const { exitOnError = true, showTips = true, showStack = false } = options
22
+
23
+ console.error(colors.errorMessage(`❌ Failed to ${context}: ${error.message}`))
24
+
23
25
  if (showStack && error.stack) {
24
- console.error(colors.gray(error.stack));
26
+ console.error(colors.gray(error.stack))
25
27
  }
26
-
28
+
27
29
  if (showTips) {
28
- const tips = getContextualTips(error, context);
30
+ const tips = getContextualTips(error, context)
29
31
  if (tips.length > 0) {
30
- tips.forEach(tip => console.error(colors.infoMessage(`💡 Tip: ${tip}`)));
32
+ tips.forEach((tip) => console.error(colors.infoMessage(`💡 Tip: ${tip}`)))
31
33
  }
32
34
  }
33
-
35
+
34
36
  if (exitOnError) {
35
- process.exit(1);
37
+ process.exit(1)
36
38
  }
37
39
  }
38
40
 
@@ -43,67 +45,72 @@ export function handleCLIError(error, context = 'application', options = {}) {
43
45
  * @returns {Array} Array of helpful tip strings
44
46
  */
45
47
  function getContextualTips(error, context) {
46
- const tips = [];
47
- const message = error.message.toLowerCase();
48
-
48
+ const tips = []
49
+ const message = error.message.toLowerCase()
50
+
49
51
  // Git-related tips
50
52
  if (error instanceof GitError || message.includes('git')) {
51
53
  if (message.includes('not a git repository')) {
52
- tips.push('Make sure you\'re in a git repository directory');
53
- tips.push('Run `git init` to initialize a new repository');
54
+ tips.push("Make sure you're in a git repository directory")
55
+ tips.push('Run `git init` to initialize a new repository')
54
56
  } else if (message.includes('no commits')) {
55
- tips.push('Make at least one commit before generating a changelog');
57
+ tips.push('Make at least one commit before generating a changelog')
56
58
  } else if (message.includes('remote')) {
57
- tips.push('Check your git remote configuration with `git remote -v`');
59
+ tips.push('Check your git remote configuration with `git remote -v`')
58
60
  } else {
59
- tips.push('Ensure git is properly installed and configured');
61
+ tips.push('Ensure git is properly installed and configured')
60
62
  }
61
63
  }
62
-
64
+
63
65
  // Provider/API related tips
64
66
  if (error instanceof ConfigError || message.includes('provider') || message.includes('api key')) {
65
- tips.push('Configure at least one AI provider in your .env.local file');
66
- tips.push('Check the documentation for provider-specific setup instructions');
67
- tips.push('Verify your API keys are valid and have sufficient quota');
67
+ tips.push('Configure at least one AI provider in your .env.local file')
68
+ tips.push('Check the documentation for provider-specific setup instructions')
69
+ tips.push('Verify your API keys are valid and have sufficient quota')
68
70
  }
69
-
71
+
70
72
  // Network related tips
71
- if (error instanceof NetworkError || message.includes('enotfound') || message.includes('network') || message.includes('timeout')) {
72
- tips.push('Check your internet connection');
73
- tips.push('If using a proxy, verify your proxy settings');
74
- tips.push('Some providers may be temporarily unavailable - try again later');
73
+ if (
74
+ error instanceof NetworkError ||
75
+ message.includes('enotfound') ||
76
+ message.includes('network') ||
77
+ message.includes('timeout')
78
+ ) {
79
+ tips.push('Check your internet connection')
80
+ tips.push('If using a proxy, verify your proxy settings')
81
+ tips.push('Some providers may be temporarily unavailable - try again later')
75
82
  }
76
-
83
+
77
84
  // Permission related tips
78
85
  if (message.includes('permission') || message.includes('eacces')) {
79
- tips.push('Check file and directory permissions');
80
- tips.push('Try running with appropriate privileges (but avoid sudo if possible)');
81
- tips.push('Ensure the current user has write access to the working directory');
86
+ tips.push('Check file and directory permissions')
87
+ tips.push('Try running with appropriate privileges (but avoid sudo if possible)')
88
+ tips.push('Ensure the current user has write access to the working directory')
82
89
  }
83
-
90
+
84
91
  // File system tips
85
92
  if (message.includes('enoent') || message.includes('file not found')) {
86
- tips.push('Verify the file or directory exists');
87
- tips.push('Check the file path spelling and case sensitivity');
93
+ tips.push('Verify the file or directory exists')
94
+ tips.push('Check the file path spelling and case sensitivity')
88
95
  }
89
-
96
+
90
97
  // Memory/resource tips
91
98
  if (message.includes('out of memory') || message.includes('heap')) {
92
- tips.push('Try processing fewer commits at once');
93
- tips.push('Increase Node.js memory limit with --max-old-space-size');
99
+ tips.push('Try processing fewer commits at once')
100
+ tips.push('Increase Node.js memory limit with --max-old-space-size')
94
101
  }
95
-
102
+
96
103
  // Context-specific tips
97
104
  if (context.includes('changelog')) {
98
- tips.push('Try using a smaller commit range or batch processing');
105
+ tips.push('Try using a smaller commit range or batch processing')
99
106
  }
100
-
107
+
101
108
  if (context.includes('mcp')) {
102
- tips.push('Ensure MCP server dependencies are installed');
103
- tips.push('Check MCP server configuration and permissions');
109
+ tips.push('Ensure MCP server dependencies are installed')
110
+ tips.push('Check MCP server configuration and permissions')
104
111
  }
105
-
106
- return tips;
112
+
113
+ return tips
107
114
  }
108
115
 
109
116
  /**
@@ -112,54 +119,56 @@ function getContextualTips(error, context) {
112
119
  * @param {Object} options - Handler options
113
120
  */
114
121
  export function setupProcessErrorHandlers(appName = 'Application', options = {}) {
115
- const { gracefulShutdown = true, logErrors = true, showStack = false } = options;
116
-
122
+ const { gracefulShutdown = true, logErrors = true, showStack = false } = options
123
+
117
124
  process.on('uncaughtException', (error) => {
118
125
  if (logErrors) {
119
- console.error(colors.errorMessage(`💥 Uncaught Exception in ${appName}:`), error.message);
126
+ console.error(colors.errorMessage(`💥 Uncaught Exception in ${appName}:`), error.message)
120
127
  if (showStack) {
121
- console.error(colors.gray('Stack trace:'), error.stack);
128
+ console.error(colors.gray('Stack trace:'), error.stack)
122
129
  }
123
130
  }
124
-
125
- const errorContext = new ErrorContext()
131
+
132
+ const _errorContext = new ErrorContext()
126
133
  .add('type', 'uncaughtException')
127
134
  .add('appName', appName)
128
- .build();
129
-
130
- handleCLIError(error, 'handle uncaught exception', {
131
- exitOnError: true,
135
+ .build()
136
+
137
+ handleCLIError(error, 'handle uncaught exception', {
138
+ exitOnError: true,
132
139
  showTips: true,
133
- showStack
134
- });
135
- });
140
+ showStack,
141
+ })
142
+ })
136
143
 
137
144
  process.on('unhandledRejection', (reason, promise) => {
138
145
  if (logErrors) {
139
- console.error(colors.errorMessage(`💥 Unhandled Rejection in ${appName}:`));
140
- console.error('Promise:', promise);
141
- console.error('Reason:', reason);
146
+ console.error(colors.errorMessage(`💥 Unhandled Rejection in ${appName}:`))
147
+ console.error('Promise:', promise)
148
+ console.error('Reason:', reason)
142
149
  }
143
-
144
- const error = reason instanceof Error ? reason : new Error(String(reason));
145
- handleCLIError(error, 'handle unhandled promise rejection', {
146
- exitOnError: true,
150
+
151
+ const error = reason instanceof Error ? reason : new Error(String(reason))
152
+ handleCLIError(error, 'handle unhandled promise rejection', {
153
+ exitOnError: true,
147
154
  showTips: true,
148
- showStack
149
- });
150
- });
151
-
155
+ showStack,
156
+ })
157
+ })
158
+
152
159
  if (gracefulShutdown) {
153
160
  // Graceful shutdown handling
154
161
  process.on('SIGINT', () => {
155
- console.log(colors.infoMessage(`\n👋 ${appName} interrupted by user. Shutting down gracefully...`));
156
- process.exit(0);
157
- });
158
-
162
+ console.log(
163
+ colors.infoMessage(`\n👋 ${appName} interrupted by user. Shutting down gracefully...`)
164
+ )
165
+ process.exit(0)
166
+ })
167
+
159
168
  process.on('SIGTERM', () => {
160
- console.log(colors.infoMessage(`\n👋 ${appName} terminated. Shutting down gracefully...`));
161
- process.exit(0);
162
- });
169
+ console.log(colors.infoMessage(`\n👋 ${appName} terminated. Shutting down gracefully...`))
170
+ process.exit(0)
171
+ })
163
172
  }
164
173
  }
165
174
 
@@ -171,99 +180,98 @@ export function setupProcessErrorHandlers(appName = 'Application', options = {})
171
180
  * @param {Object} options - Application options
172
181
  */
173
182
  export async function runCLIApplication(appName, version, initFn, options = {}) {
174
- const {
175
- showStartupMessage = true,
183
+ const {
184
+ showStartupMessage = true,
176
185
  setupErrorHandlers = true,
177
186
  debugMode = process.env.DEBUG === 'true',
178
187
  showTips = true,
179
188
  validateGit = false,
180
189
  requiredEnvVars = [],
181
- startupTips = []
182
- } = options;
183
-
190
+ startupTips = [],
191
+ } = options
192
+
184
193
  if (setupErrorHandlers) {
185
- setupProcessErrorHandlers(appName, {
186
- gracefulShutdown: true,
187
- logErrors: true,
188
- showStack: debugMode
189
- });
194
+ setupProcessErrorHandlers(appName, {
195
+ gracefulShutdown: true,
196
+ logErrors: true,
197
+ showStack: debugMode,
198
+ })
190
199
  }
191
-
200
+
192
201
  if (showStartupMessage) {
193
- displayStartupMessage(appName, version, { debugMode });
202
+ displayStartupMessage(appName, version, { debugMode })
194
203
  }
195
-
204
+
196
205
  // Pre-flight checks
197
206
  if (validateGit && !validateGitRepository()) {
198
- console.error(colors.errorMessage('❌ Not in a git repository'));
199
- console.error(colors.infoMessage('💡 Tip: Navigate to a git repository or run `git init`'));
200
- process.exit(1);
207
+ console.error(colors.errorMessage('❌ Not in a git repository'))
208
+ console.error(colors.infoMessage('💡 Tip: Navigate to a git repository or run `git init`'))
209
+ process.exit(1)
201
210
  }
202
-
211
+
203
212
  if (requiredEnvVars.length > 0 && !validateEnvironment(requiredEnvVars)) {
204
- process.exit(1);
213
+ process.exit(1)
205
214
  }
206
-
215
+
207
216
  if (showTips && startupTips.length > 0) {
208
- showStartupTips(appName, startupTips);
217
+ showStartupTips(appName, startupTips)
209
218
  }
210
-
219
+
211
220
  try {
212
- await initFn();
221
+ await initFn()
213
222
  } catch (error) {
214
- handleCLIError(error, `start ${appName}`, {
215
- exitOnError: true,
223
+ handleCLIError(error, `start ${appName}`, {
224
+ exitOnError: true,
216
225
  showTips: true,
217
- showStack: debugMode
218
- });
226
+ showStack: debugMode,
227
+ })
219
228
  }
220
229
  }
221
230
 
222
231
  /**
223
232
  * Enhanced MCP Server startup wrapper
224
233
  * @param {string} serverName - Name of the MCP server
225
- * @param {string} version - Version string
234
+ * @param {string} version - Version string
226
235
  * @param {Function} serverClass - MCP Server class constructor
227
236
  * @param {Object} options - Server options
228
237
  */
229
238
  export async function runMCPServer(serverName, version, serverClass, options = {}) {
230
- const {
239
+ const {
231
240
  debugMode = process.env.DEBUG === 'true',
232
241
  showTools = true,
233
242
  tools = [],
234
243
  showCapabilities = true,
235
- capabilities = []
236
- } = options;
237
-
238
- displayStartupMessage(serverName, version, {
244
+ capabilities = [],
245
+ } = options
246
+
247
+ displayStartupMessage(serverName, version, {
239
248
  type: 'MCP Server',
240
- debugMode
241
- });
242
-
243
- console.log(colors.processingMessage('🔌 Starting MCP server...'));
244
-
249
+ debugMode,
250
+ })
251
+
252
+ console.log(colors.processingMessage('🔌 Starting MCP server...'))
253
+
245
254
  if (showTools && tools.length > 0) {
246
- console.log(colors.infoMessage(`🎯 Available tools: ${tools.join(', ')}`));
255
+ console.log(colors.infoMessage(`🎯 Available tools: ${tools.join(', ')}`))
247
256
  }
248
-
257
+
249
258
  if (showCapabilities && capabilities.length > 0) {
250
- console.log(colors.infoMessage(`⚡ Capabilities: ${capabilities.join(', ')}`));
259
+ console.log(colors.infoMessage(`⚡ Capabilities: ${capabilities.join(', ')}`))
251
260
  }
252
-
261
+
253
262
  try {
254
- console.log(colors.processingMessage('✅ MCP server initializing...'));
255
-
256
- const server = new serverClass();
257
- await server.run();
258
-
259
- console.log(colors.successMessage('🚀 MCP server started successfully'));
260
-
263
+ console.log(colors.processingMessage('✅ MCP server initializing...'))
264
+
265
+ const server = new serverClass()
266
+ await server.run()
267
+
268
+ console.log(colors.successMessage('🚀 MCP server started successfully'))
261
269
  } catch (error) {
262
270
  handleCLIError(error, 'initialize MCP server', {
263
271
  exitOnError: true,
264
272
  showTips: true,
265
- showStack: debugMode
266
- });
273
+ showStack: debugMode,
274
+ })
267
275
  }
268
276
  }
269
277
 
@@ -274,19 +282,19 @@ export async function runMCPServer(serverName, version, serverClass, options = {
274
282
  * @param {Object} options - Display options
275
283
  */
276
284
  function displayStartupMessage(name, version, options = {}) {
277
- const { type = 'CLI Tool', debugMode = false } = options;
278
-
279
- console.log(colors.header(`🤖 ${name}`));
280
- console.log(colors.label(`📦 Version: ${colors.highlight(version)}`));
281
- console.log(colors.label(`🏷️ Type: ${colors.value(type)}`));
282
-
285
+ const { type = 'CLI Tool', debugMode = false } = options
286
+
287
+ console.log(colors.header(`🤖 ${name}`))
288
+ console.log(colors.label(`📦 Version: ${colors.highlight(version)}`))
289
+ console.log(colors.label(`🏷️ Type: ${colors.value(type)}`))
290
+
283
291
  if (debugMode) {
284
- console.log(colors.warningMessage('🐛 Debug mode enabled'));
285
- console.log(colors.label(`📁 Working directory: ${colors.path(process.cwd())}`));
286
- console.log(colors.label(`⚙️ Node.js: ${colors.highlight(process.version)}`));
287
- console.log(colors.label(`🖥️ Platform: ${colors.value(process.platform)} ${process.arch}`));
292
+ console.log(colors.warningMessage('🐛 Debug mode enabled'))
293
+ console.log(colors.label(`📁 Working directory: ${colors.path(process.cwd())}`))
294
+ console.log(colors.label(`⚙️ Node.js: ${colors.highlight(process.version)}`))
295
+ console.log(colors.label(`🖥️ Platform: ${colors.value(process.platform)} ${process.arch}`))
288
296
  }
289
- console.log('');
297
+ console.log('')
290
298
  }
291
299
 
292
300
  /**
@@ -297,28 +305,28 @@ function displayStartupMessage(name, version, options = {}) {
297
305
  * @returns {boolean} Whether all required arguments are present
298
306
  */
299
307
  export function validateRequiredArgs(requiredArgs, argv, options = {}) {
300
- const { showUsage = true } = options;
301
- const missing = requiredArgs.filter(arg => !argv[arg]);
302
-
308
+ const { showUsage = true } = options
309
+ const missing = requiredArgs.filter((arg) => !argv[arg])
310
+
303
311
  if (missing.length > 0) {
304
- console.error(colors.errorMessage(`❌ Missing required arguments: ${missing.join(', ')}`));
305
-
312
+ console.error(colors.errorMessage(`❌ Missing required arguments: ${missing.join(', ')}`))
313
+
306
314
  if (showUsage) {
307
- console.error(colors.infoMessage('💡 Tip: Use --help to see usage information'));
308
-
315
+ console.error(colors.infoMessage('💡 Tip: Use --help to see usage information'))
316
+
309
317
  // Show specific argument descriptions if available
310
- missing.forEach(arg => {
311
- const description = getArgumentDescription(arg);
318
+ missing.forEach((arg) => {
319
+ const description = getArgumentDescription(arg)
312
320
  if (description) {
313
- console.error(colors.gray(` --${arg}: ${description}`));
321
+ console.error(colors.gray(` --${arg}: ${description}`))
314
322
  }
315
- });
323
+ })
316
324
  }
317
-
318
- return false;
325
+
326
+ return false
319
327
  }
320
-
321
- return true;
328
+
329
+ return true
322
330
  }
323
331
 
324
332
  /**
@@ -328,15 +336,15 @@ export function validateRequiredArgs(requiredArgs, argv, options = {}) {
328
336
  */
329
337
  function getArgumentDescription(arg) {
330
338
  const descriptions = {
331
- 'version': 'Version to use for changelog generation',
332
- 'since': 'Git reference to start changelog from',
333
- 'provider': 'AI provider to use (openai, anthropic, etc.)',
334
- 'model': 'AI model to use for analysis',
335
- 'output': 'Output file path for generated changelog',
336
- 'format': 'Output format (markdown, json)',
337
- 'config': 'Configuration file path'
338
- };
339
- return descriptions[arg] || '';
339
+ version: 'Version to use for changelog generation',
340
+ since: 'Git reference to start changelog from',
341
+ provider: 'AI provider to use (openai, anthropic, etc.)',
342
+ model: 'AI model to use for analysis',
343
+ output: 'Output file path for generated changelog',
344
+ format: 'Output format (markdown, json)',
345
+ config: 'Configuration file path',
346
+ }
347
+ return descriptions[arg] || ''
340
348
  }
341
349
 
342
350
  /**
@@ -346,27 +354,31 @@ function getArgumentDescription(arg) {
346
354
  * @returns {boolean} Whether all required environment variables are set
347
355
  */
348
356
  export function validateEnvironment(requiredEnvVars = [], options = {}) {
349
- const { showExamples = true } = options;
350
- const missing = requiredEnvVars.filter(envVar => !process.env[envVar]);
351
-
357
+ const { showExamples = true } = options
358
+ const missing = requiredEnvVars.filter((envVar) => !process.env[envVar])
359
+
352
360
  if (missing.length > 0) {
353
- console.error(colors.errorMessage(`❌ Missing required environment variables: ${missing.join(', ')}`));
354
- console.error(colors.infoMessage('💡 Tip: Check your .env.local file or environment configuration'));
355
-
361
+ console.error(
362
+ colors.errorMessage(`❌ Missing required environment variables: ${missing.join(', ')}`)
363
+ )
364
+ console.error(
365
+ colors.infoMessage('💡 Tip: Check your .env.local file or environment configuration')
366
+ )
367
+
356
368
  if (showExamples) {
357
- console.error(colors.gray('\nExample .env.local configuration:'));
358
- missing.forEach(envVar => {
359
- const example = getEnvVarExample(envVar);
369
+ console.error(colors.gray('\nExample .env.local configuration:'))
370
+ missing.forEach((envVar) => {
371
+ const example = getEnvVarExample(envVar)
360
372
  if (example) {
361
- console.error(colors.gray(`${envVar}=${example}`));
373
+ console.error(colors.gray(`${envVar}=${example}`))
362
374
  }
363
- });
375
+ })
364
376
  }
365
-
366
- return false;
377
+
378
+ return false
367
379
  }
368
-
369
- return true;
380
+
381
+ return true
370
382
  }
371
383
 
372
384
  /**
@@ -376,16 +388,16 @@ export function validateEnvironment(requiredEnvVars = [], options = {}) {
376
388
  */
377
389
  function getEnvVarExample(envVar) {
378
390
  const examples = {
379
- 'OPENAI_API_KEY': 'sk-...',
380
- 'ANTHROPIC_API_KEY': 'sk-ant-...',
381
- 'AZURE_OPENAI_API_KEY': 'your-azure-key',
382
- 'AZURE_OPENAI_ENDPOINT': 'https://your-resource.openai.azure.com/',
383
- 'GOOGLE_AI_API_KEY': 'your-google-ai-key',
384
- 'HUGGINGFACE_API_KEY': 'hf_...',
385
- 'DATABASE_URL': 'postgresql://user:pass@localhost:5432/db',
386
- 'DEBUG': 'true'
387
- };
388
- return examples[envVar] || 'your-value-here';
391
+ OPENAI_API_KEY: 'sk-...',
392
+ ANTHROPIC_API_KEY: 'sk-ant-...',
393
+ AZURE_OPENAI_API_KEY: 'your-azure-key',
394
+ AZURE_OPENAI_ENDPOINT: 'https://your-resource.openai.azure.com/',
395
+ GOOGLE_AI_API_KEY: 'your-google-ai-key',
396
+ HUGGINGFACE_API_KEY: 'hf_...',
397
+ DATABASE_URL: 'postgresql://user:pass@localhost:5432/db',
398
+ DEBUG: 'true',
399
+ }
400
+ return examples[envVar] || 'your-value-here'
389
401
  }
390
402
 
391
403
  /**
@@ -395,35 +407,34 @@ function getEnvVarExample(envVar) {
395
407
  * @returns {Object} Validation result with details
396
408
  */
397
409
  export function validateGitRepository(pathToCheck = process.cwd(), options = {}) {
398
- const { checkCommits = false, showDetails = false } = options;
399
-
410
+ const { checkCommits = false, showDetails = false } = options
411
+
400
412
  try {
401
- const gitPath = path.join(pathToCheck, '.git');
402
- const isGitRepo = fs.existsSync(gitPath);
403
-
413
+ const gitPath = path.join(pathToCheck, '.git')
414
+ const isGitRepo = fs.existsSync(gitPath)
415
+
404
416
  if (!isGitRepo) {
405
- return { valid: false, reason: 'not_git_repository' };
417
+ return { valid: false, reason: 'not_git_repository' }
406
418
  }
407
-
408
- const result = { valid: true, path: pathToCheck };
409
-
419
+
420
+ const result = { valid: true, path: pathToCheck }
421
+
410
422
  if (checkCommits) {
411
423
  // Git validation requires command execution
412
- result.hasCommits = true; // Assume true for basic validation
424
+ result.hasCommits = true // Assume true for basic validation
413
425
  }
414
-
426
+
415
427
  if (showDetails) {
416
- console.log(colors.successMessage(`✅ Valid git repository: ${pathToCheck}`));
428
+ console.log(colors.successMessage(`✅ Valid git repository: ${pathToCheck}`))
417
429
  }
418
-
419
- return result;
420
-
430
+
431
+ return result
421
432
  } catch (error) {
422
- return {
423
- valid: false,
424
- reason: 'access_error',
425
- error: error.message
426
- };
433
+ return {
434
+ valid: false,
435
+ reason: 'access_error',
436
+ error: error.message,
437
+ }
427
438
  }
428
439
  }
429
440
 
@@ -433,13 +444,15 @@ export function validateGitRepository(pathToCheck = process.cwd(), options = {})
433
444
  * @param {Array} tips - Array of tip strings to display
434
445
  */
435
446
  export function showStartupTips(appName, tips = []) {
436
- if (tips.length === 0) return;
437
-
438
- console.log(colors.header(`💡 ${appName} Tips:`));
447
+ if (tips.length === 0) {
448
+ return
449
+ }
450
+
451
+ console.log(colors.header(`💡 ${appName} Tips:`))
439
452
  tips.forEach((tip, index) => {
440
- console.log(colors.infoMessage(` ${index + 1}. ${tip}`));
441
- });
442
- console.log('');
453
+ console.log(colors.infoMessage(` ${index + 1}. ${tip}`))
454
+ })
455
+ console.log('')
443
456
  }
444
457
 
445
458
  /**
@@ -449,25 +462,25 @@ export function showStartupTips(appName, tips = []) {
449
462
  * @param {Object} additional - Additional version info
450
463
  */
451
464
  export function displayVersionInfo(name, version, additional = {}) {
452
- console.log(colors.header(`${name} v${version}`));
453
-
465
+ console.log(colors.header(`${name} v${version}`))
466
+
454
467
  if (additional.nodeVersion !== false) {
455
- console.log(colors.label(`Node.js: ${colors.highlight(process.version)}`));
468
+ console.log(colors.label(`Node.js: ${colors.highlight(process.version)}`))
456
469
  }
457
-
470
+
458
471
  if (additional.platform !== false) {
459
- console.log(colors.label(`Platform: ${colors.value(process.platform)} ${process.arch}`));
472
+ console.log(colors.label(`Platform: ${colors.value(process.platform)} ${process.arch}`))
460
473
  }
461
-
474
+
462
475
  if (additional.gitVersion && additional.gitVersion !== false) {
463
- console.log(colors.label(`Git: ${colors.highlight(additional.gitVersion || 'Available')}`));
476
+ console.log(colors.label(`Git: ${colors.highlight(additional.gitVersion || 'Available')}`))
464
477
  }
465
-
478
+
466
479
  Object.entries(additional).forEach(([key, value]) => {
467
480
  if (typeof value === 'string' && !['nodeVersion', 'platform', 'gitVersion'].includes(key)) {
468
- console.log(colors.label(`${key}: ${colors.value(value)}`));
481
+ console.log(colors.label(`${key}: ${colors.value(value)}`))
469
482
  }
470
- });
483
+ })
471
484
  }
472
485
 
473
486
  /**
@@ -480,8 +493,8 @@ export function getDefaultStartupTips() {
480
493
  'Use --help to see all available commands and options',
481
494
  'Run in a git repository with existing commits for best results',
482
495
  'Try --analysis-mode detailed for comprehensive changelog generation',
483
- 'Use --interactive for guided changelog creation'
484
- ];
496
+ 'Use --interactive for guided changelog creation',
497
+ ]
485
498
  }
486
499
 
487
500
  /**
@@ -490,36 +503,31 @@ export function getDefaultStartupTips() {
490
503
  * @returns {Object} Validation result
491
504
  */
492
505
  export function validateCLIPreconditions(requirements = {}) {
493
- const {
494
- gitRepository = false,
495
- envVars = [],
496
- args = [],
497
- argv = {}
498
- } = requirements;
499
-
506
+ const { gitRepository = false, envVars = [], args = [], argv = {} } = requirements
507
+
500
508
  const results = {
501
509
  valid: true,
502
510
  errors: [],
503
- warnings: []
504
- };
505
-
511
+ warnings: [],
512
+ }
513
+
506
514
  if (gitRepository) {
507
- const gitResult = validateGitRepository();
515
+ const gitResult = validateGitRepository()
508
516
  if (!gitResult.valid) {
509
- results.valid = false;
510
- results.errors.push('Not in a git repository');
517
+ results.valid = false
518
+ results.errors.push('Not in a git repository')
511
519
  }
512
520
  }
513
-
521
+
514
522
  if (envVars.length > 0 && !validateEnvironment(envVars, { showExamples: false })) {
515
- results.valid = false;
516
- results.errors.push('Missing required environment variables');
523
+ results.valid = false
524
+ results.errors.push('Missing required environment variables')
517
525
  }
518
-
526
+
519
527
  if (args.length > 0 && !validateRequiredArgs(args, argv, { showUsage: false })) {
520
- results.valid = false;
521
- results.errors.push('Missing required arguments');
528
+ results.valid = false
529
+ results.errors.push('Missing required arguments')
522
530
  }
523
-
524
- return results;
525
- }
531
+
532
+ return results
533
+ }