@entro314labs/ai-changelog-generator 3.0.5

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 (47) hide show
  1. package/CHANGELOG.md +801 -0
  2. package/LICENSE +21 -0
  3. package/README.md +393 -0
  4. package/ai-changelog-mcp.sh +93 -0
  5. package/ai-changelog.sh +103 -0
  6. package/bin/ai-changelog-dxt.js +35 -0
  7. package/bin/ai-changelog-mcp.js +34 -0
  8. package/bin/ai-changelog.js +18 -0
  9. package/package.json +135 -0
  10. package/src/ai-changelog-generator.js +258 -0
  11. package/src/application/orchestrators/changelog.orchestrator.js +730 -0
  12. package/src/application/services/application.service.js +301 -0
  13. package/src/cli.js +157 -0
  14. package/src/domains/ai/ai-analysis.service.js +486 -0
  15. package/src/domains/analysis/analysis.engine.js +445 -0
  16. package/src/domains/changelog/changelog.service.js +1761 -0
  17. package/src/domains/changelog/workspace-changelog.service.js +505 -0
  18. package/src/domains/git/git-repository.analyzer.js +588 -0
  19. package/src/domains/git/git.service.js +302 -0
  20. package/src/infrastructure/cli/cli.controller.js +517 -0
  21. package/src/infrastructure/config/configuration.manager.js +538 -0
  22. package/src/infrastructure/interactive/interactive-workflow.service.js +444 -0
  23. package/src/infrastructure/mcp/mcp-server.service.js +540 -0
  24. package/src/infrastructure/metrics/metrics.collector.js +362 -0
  25. package/src/infrastructure/providers/core/base-provider.js +184 -0
  26. package/src/infrastructure/providers/implementations/anthropic.js +329 -0
  27. package/src/infrastructure/providers/implementations/azure.js +296 -0
  28. package/src/infrastructure/providers/implementations/bedrock.js +393 -0
  29. package/src/infrastructure/providers/implementations/dummy.js +112 -0
  30. package/src/infrastructure/providers/implementations/google.js +320 -0
  31. package/src/infrastructure/providers/implementations/huggingface.js +301 -0
  32. package/src/infrastructure/providers/implementations/lmstudio.js +189 -0
  33. package/src/infrastructure/providers/implementations/mock.js +275 -0
  34. package/src/infrastructure/providers/implementations/ollama.js +151 -0
  35. package/src/infrastructure/providers/implementations/openai.js +273 -0
  36. package/src/infrastructure/providers/implementations/vertex.js +438 -0
  37. package/src/infrastructure/providers/provider-management.service.js +415 -0
  38. package/src/infrastructure/providers/provider-manager.service.js +363 -0
  39. package/src/infrastructure/providers/utils/base-provider-helpers.js +660 -0
  40. package/src/infrastructure/providers/utils/model-config.js +610 -0
  41. package/src/infrastructure/providers/utils/provider-utils.js +286 -0
  42. package/src/shared/constants/colors.js +370 -0
  43. package/src/shared/utils/cli-entry-utils.js +525 -0
  44. package/src/shared/utils/error-classes.js +423 -0
  45. package/src/shared/utils/json-utils.js +318 -0
  46. package/src/shared/utils/utils.js +1997 -0
  47. package/types/index.d.ts +464 -0
@@ -0,0 +1,525 @@
1
+ /**
2
+ * CLI Entry Point Utilities
3
+ * Provides process error handlers, startup tips, and common CLI patterns
4
+ * Provides CLI error handling, validation, and user interface utilities
5
+ */
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';
11
+
12
+ /**
13
+ * Enhanced error handling for CLI applications with context-aware tips
14
+ * @param {Error} error - The error that occurred
15
+ * @param {string} context - Context where the error occurred
16
+ * @param {Object} options - Error handling options
17
+ */
18
+ 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
+
23
+ if (showStack && error.stack) {
24
+ console.error(colors.gray(error.stack));
25
+ }
26
+
27
+ if (showTips) {
28
+ const tips = getContextualTips(error, context);
29
+ if (tips.length > 0) {
30
+ tips.forEach(tip => console.error(colors.infoMessage(`💡 Tip: ${tip}`)));
31
+ }
32
+ }
33
+
34
+ if (exitOnError) {
35
+ process.exit(1);
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Get contextual tips based on error type and message
41
+ * @param {Error} error - The error that occurred
42
+ * @param {string} context - Context where the error occurred
43
+ * @returns {Array} Array of helpful tip strings
44
+ */
45
+ function getContextualTips(error, context) {
46
+ const tips = [];
47
+ const message = error.message.toLowerCase();
48
+
49
+ // Git-related tips
50
+ if (error instanceof GitError || message.includes('git')) {
51
+ 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
+ } else if (message.includes('no commits')) {
55
+ tips.push('Make at least one commit before generating a changelog');
56
+ } else if (message.includes('remote')) {
57
+ tips.push('Check your git remote configuration with `git remote -v`');
58
+ } else {
59
+ tips.push('Ensure git is properly installed and configured');
60
+ }
61
+ }
62
+
63
+ // Provider/API related tips
64
+ 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');
68
+ }
69
+
70
+ // 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');
75
+ }
76
+
77
+ // Permission related tips
78
+ 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');
82
+ }
83
+
84
+ // File system tips
85
+ 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');
88
+ }
89
+
90
+ // Memory/resource tips
91
+ 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');
94
+ }
95
+
96
+ // Context-specific tips
97
+ if (context.includes('changelog')) {
98
+ tips.push('Try using a smaller commit range or batch processing');
99
+ }
100
+
101
+ if (context.includes('mcp')) {
102
+ tips.push('Ensure MCP server dependencies are installed');
103
+ tips.push('Check MCP server configuration and permissions');
104
+ }
105
+
106
+ return tips;
107
+ }
108
+
109
+ /**
110
+ * Comprehensive process error handlers for CLI applications
111
+ * @param {string} appName - Name of the application for error messages
112
+ * @param {Object} options - Handler options
113
+ */
114
+ export function setupProcessErrorHandlers(appName = 'Application', options = {}) {
115
+ const { gracefulShutdown = true, logErrors = true, showStack = false } = options;
116
+
117
+ process.on('uncaughtException', (error) => {
118
+ if (logErrors) {
119
+ console.error(colors.errorMessage(`💥 Uncaught Exception in ${appName}:`), error.message);
120
+ if (showStack) {
121
+ console.error(colors.gray('Stack trace:'), error.stack);
122
+ }
123
+ }
124
+
125
+ const errorContext = new ErrorContext()
126
+ .add('type', 'uncaughtException')
127
+ .add('appName', appName)
128
+ .build();
129
+
130
+ handleCLIError(error, 'handle uncaught exception', {
131
+ exitOnError: true,
132
+ showTips: true,
133
+ showStack
134
+ });
135
+ });
136
+
137
+ process.on('unhandledRejection', (reason, promise) => {
138
+ if (logErrors) {
139
+ console.error(colors.errorMessage(`💥 Unhandled Rejection in ${appName}:`));
140
+ console.error('Promise:', promise);
141
+ console.error('Reason:', reason);
142
+ }
143
+
144
+ const error = reason instanceof Error ? reason : new Error(String(reason));
145
+ handleCLIError(error, 'handle unhandled promise rejection', {
146
+ exitOnError: true,
147
+ showTips: true,
148
+ showStack
149
+ });
150
+ });
151
+
152
+ if (gracefulShutdown) {
153
+ // Graceful shutdown handling
154
+ process.on('SIGINT', () => {
155
+ console.log(colors.infoMessage(`\n👋 ${appName} interrupted by user. Shutting down gracefully...`));
156
+ process.exit(0);
157
+ });
158
+
159
+ process.on('SIGTERM', () => {
160
+ console.log(colors.infoMessage(`\n👋 ${appName} terminated. Shutting down gracefully...`));
161
+ process.exit(0);
162
+ });
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Enhanced CLI application wrapper with comprehensive startup handling
168
+ * @param {string} appName - Name of the application
169
+ * @param {string} version - Version string
170
+ * @param {Function} initFn - Initialization function to run
171
+ * @param {Object} options - Application options
172
+ */
173
+ export async function runCLIApplication(appName, version, initFn, options = {}) {
174
+ const {
175
+ showStartupMessage = true,
176
+ setupErrorHandlers = true,
177
+ debugMode = process.env.DEBUG === 'true',
178
+ showTips = true,
179
+ validateGit = false,
180
+ requiredEnvVars = [],
181
+ startupTips = []
182
+ } = options;
183
+
184
+ if (setupErrorHandlers) {
185
+ setupProcessErrorHandlers(appName, {
186
+ gracefulShutdown: true,
187
+ logErrors: true,
188
+ showStack: debugMode
189
+ });
190
+ }
191
+
192
+ if (showStartupMessage) {
193
+ displayStartupMessage(appName, version, { debugMode });
194
+ }
195
+
196
+ // Pre-flight checks
197
+ 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);
201
+ }
202
+
203
+ if (requiredEnvVars.length > 0 && !validateEnvironment(requiredEnvVars)) {
204
+ process.exit(1);
205
+ }
206
+
207
+ if (showTips && startupTips.length > 0) {
208
+ showStartupTips(appName, startupTips);
209
+ }
210
+
211
+ try {
212
+ await initFn();
213
+ } catch (error) {
214
+ handleCLIError(error, `start ${appName}`, {
215
+ exitOnError: true,
216
+ showTips: true,
217
+ showStack: debugMode
218
+ });
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Enhanced MCP Server startup wrapper
224
+ * @param {string} serverName - Name of the MCP server
225
+ * @param {string} version - Version string
226
+ * @param {Function} serverClass - MCP Server class constructor
227
+ * @param {Object} options - Server options
228
+ */
229
+ export async function runMCPServer(serverName, version, serverClass, options = {}) {
230
+ const {
231
+ debugMode = process.env.DEBUG === 'true',
232
+ showTools = true,
233
+ tools = [],
234
+ showCapabilities = true,
235
+ capabilities = []
236
+ } = options;
237
+
238
+ displayStartupMessage(serverName, version, {
239
+ type: 'MCP Server',
240
+ debugMode
241
+ });
242
+
243
+ console.log(colors.processingMessage('🔌 Starting MCP server...'));
244
+
245
+ if (showTools && tools.length > 0) {
246
+ console.log(colors.infoMessage(`🎯 Available tools: ${tools.join(', ')}`));
247
+ }
248
+
249
+ if (showCapabilities && capabilities.length > 0) {
250
+ console.log(colors.infoMessage(`⚡ Capabilities: ${capabilities.join(', ')}`));
251
+ }
252
+
253
+ 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
+
261
+ } catch (error) {
262
+ handleCLIError(error, 'initialize MCP server', {
263
+ exitOnError: true,
264
+ showTips: true,
265
+ showStack: debugMode
266
+ });
267
+ }
268
+ }
269
+
270
+ /**
271
+ * Display formatted startup message
272
+ * @param {string} name - Application name
273
+ * @param {string} version - Version string
274
+ * @param {Object} options - Display options
275
+ */
276
+ 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
+
283
+ 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}`));
288
+ }
289
+ console.log('');
290
+ }
291
+
292
+ /**
293
+ * Enhanced CLI argument validation with detailed feedback
294
+ * @param {Array} requiredArgs - Required argument names
295
+ * @param {Object} argv - Parsed arguments object
296
+ * @param {Object} options - Validation options
297
+ * @returns {boolean} Whether all required arguments are present
298
+ */
299
+ export function validateRequiredArgs(requiredArgs, argv, options = {}) {
300
+ const { showUsage = true } = options;
301
+ const missing = requiredArgs.filter(arg => !argv[arg]);
302
+
303
+ if (missing.length > 0) {
304
+ console.error(colors.errorMessage(`❌ Missing required arguments: ${missing.join(', ')}`));
305
+
306
+ if (showUsage) {
307
+ console.error(colors.infoMessage('💡 Tip: Use --help to see usage information'));
308
+
309
+ // Show specific argument descriptions if available
310
+ missing.forEach(arg => {
311
+ const description = getArgumentDescription(arg);
312
+ if (description) {
313
+ console.error(colors.gray(` --${arg}: ${description}`));
314
+ }
315
+ });
316
+ }
317
+
318
+ return false;
319
+ }
320
+
321
+ return true;
322
+ }
323
+
324
+ /**
325
+ * Get description for common CLI arguments
326
+ * @param {string} arg - Argument name
327
+ * @returns {string} Argument description
328
+ */
329
+ function getArgumentDescription(arg) {
330
+ 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] || '';
340
+ }
341
+
342
+ /**
343
+ * Enhanced environment validation with helpful guidance
344
+ * @param {Array} requiredEnvVars - Required environment variable names
345
+ * @param {Object} options - Validation options
346
+ * @returns {boolean} Whether all required environment variables are set
347
+ */
348
+ export function validateEnvironment(requiredEnvVars = [], options = {}) {
349
+ const { showExamples = true } = options;
350
+ const missing = requiredEnvVars.filter(envVar => !process.env[envVar]);
351
+
352
+ 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
+
356
+ if (showExamples) {
357
+ console.error(colors.gray('\nExample .env.local configuration:'));
358
+ missing.forEach(envVar => {
359
+ const example = getEnvVarExample(envVar);
360
+ if (example) {
361
+ console.error(colors.gray(`${envVar}=${example}`));
362
+ }
363
+ });
364
+ }
365
+
366
+ return false;
367
+ }
368
+
369
+ return true;
370
+ }
371
+
372
+ /**
373
+ * Get example values for common environment variables
374
+ * @param {string} envVar - Environment variable name
375
+ * @returns {string} Example value
376
+ */
377
+ function getEnvVarExample(envVar) {
378
+ 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';
389
+ }
390
+
391
+ /**
392
+ * Enhanced git repository validation
393
+ * @param {string} pathToCheck - Path to check (defaults to current directory)
394
+ * @param {Object} options - Validation options
395
+ * @returns {Object} Validation result with details
396
+ */
397
+ export function validateGitRepository(pathToCheck = process.cwd(), options = {}) {
398
+ const { checkCommits = false, showDetails = false } = options;
399
+
400
+ try {
401
+ const gitPath = path.join(pathToCheck, '.git');
402
+ const isGitRepo = fs.existsSync(gitPath);
403
+
404
+ if (!isGitRepo) {
405
+ return { valid: false, reason: 'not_git_repository' };
406
+ }
407
+
408
+ const result = { valid: true, path: pathToCheck };
409
+
410
+ if (checkCommits) {
411
+ // Git validation requires command execution
412
+ result.hasCommits = true; // Assume true for basic validation
413
+ }
414
+
415
+ if (showDetails) {
416
+ console.log(colors.successMessage(`✅ Valid git repository: ${pathToCheck}`));
417
+ }
418
+
419
+ return result;
420
+
421
+ } catch (error) {
422
+ return {
423
+ valid: false,
424
+ reason: 'access_error',
425
+ error: error.message
426
+ };
427
+ }
428
+ }
429
+
430
+ /**
431
+ * Display helpful startup tips
432
+ * @param {string} appName - Name of the application
433
+ * @param {Array} tips - Array of tip strings to display
434
+ */
435
+ export function showStartupTips(appName, tips = []) {
436
+ if (tips.length === 0) return;
437
+
438
+ console.log(colors.header(`💡 ${appName} Tips:`));
439
+ tips.forEach((tip, index) => {
440
+ console.log(colors.infoMessage(` ${index + 1}. ${tip}`));
441
+ });
442
+ console.log('');
443
+ }
444
+
445
+ /**
446
+ * Enhanced version information display
447
+ * @param {string} name - Application name
448
+ * @param {string} version - Version string
449
+ * @param {Object} additional - Additional version info
450
+ */
451
+ export function displayVersionInfo(name, version, additional = {}) {
452
+ console.log(colors.header(`${name} v${version}`));
453
+
454
+ if (additional.nodeVersion !== false) {
455
+ console.log(colors.label(`Node.js: ${colors.highlight(process.version)}`));
456
+ }
457
+
458
+ if (additional.platform !== false) {
459
+ console.log(colors.label(`Platform: ${colors.value(process.platform)} ${process.arch}`));
460
+ }
461
+
462
+ if (additional.gitVersion && additional.gitVersion !== false) {
463
+ console.log(colors.label(`Git: ${colors.highlight(additional.gitVersion || 'Available')}`));
464
+ }
465
+
466
+ Object.entries(additional).forEach(([key, value]) => {
467
+ if (typeof value === 'string' && !['nodeVersion', 'platform', 'gitVersion'].includes(key)) {
468
+ console.log(colors.label(`${key}: ${colors.value(value)}`));
469
+ }
470
+ });
471
+ }
472
+
473
+ /**
474
+ * Create startup tips for AI changelog generator
475
+ * @returns {Array} Array of helpful tips
476
+ */
477
+ export function getDefaultStartupTips() {
478
+ return [
479
+ 'Ensure you have at least one AI provider configured in .env.local',
480
+ 'Use --help to see all available commands and options',
481
+ 'Run in a git repository with existing commits for best results',
482
+ 'Try --analysis-mode detailed for comprehensive changelog generation',
483
+ 'Use --interactive for guided changelog creation'
484
+ ];
485
+ }
486
+
487
+ /**
488
+ * Validate common CLI preconditions
489
+ * @param {Object} requirements - Requirements to validate
490
+ * @returns {Object} Validation result
491
+ */
492
+ export function validateCLIPreconditions(requirements = {}) {
493
+ const {
494
+ gitRepository = false,
495
+ envVars = [],
496
+ args = [],
497
+ argv = {}
498
+ } = requirements;
499
+
500
+ const results = {
501
+ valid: true,
502
+ errors: [],
503
+ warnings: []
504
+ };
505
+
506
+ if (gitRepository) {
507
+ const gitResult = validateGitRepository();
508
+ if (!gitResult.valid) {
509
+ results.valid = false;
510
+ results.errors.push('Not in a git repository');
511
+ }
512
+ }
513
+
514
+ if (envVars.length > 0 && !validateEnvironment(envVars, { showExamples: false })) {
515
+ results.valid = false;
516
+ results.errors.push('Missing required environment variables');
517
+ }
518
+
519
+ if (args.length > 0 && !validateRequiredArgs(args, argv, { showUsage: false })) {
520
+ results.valid = false;
521
+ results.errors.push('Missing required arguments');
522
+ }
523
+
524
+ return results;
525
+ }