@andrebuzeli/git-mcp 5.1.0 → 5.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 (48) hide show
  1. package/dist/__tests__/setup.d.ts +10 -0
  2. package/dist/__tests__/setup.d.ts.map +1 -0
  3. package/dist/__tests__/setup.js +105 -0
  4. package/dist/__tests__/setup.js.map +1 -0
  5. package/dist/index.js +1 -1
  6. package/dist/server.d.ts +8 -1
  7. package/dist/server.d.ts.map +1 -1
  8. package/dist/server.js +100 -4
  9. package/dist/server.js.map +1 -1
  10. package/dist/tools/git-archive.d.ts.map +1 -1
  11. package/dist/tools/git-archive.js +1 -3
  12. package/dist/tools/git-archive.js.map +1 -1
  13. package/dist/tools/git-automations.d.ts +257 -0
  14. package/dist/tools/git-automations.d.ts.map +1 -0
  15. package/dist/tools/git-automations.js +878 -0
  16. package/dist/tools/git-automations.js.map +1 -0
  17. package/dist/tools/git-bisect.d.ts +158 -0
  18. package/dist/tools/git-bisect.d.ts.map +1 -0
  19. package/dist/tools/git-bisect.js +571 -0
  20. package/dist/tools/git-bisect.js.map +1 -0
  21. package/dist/tools/git-changelog.d.ts +253 -0
  22. package/dist/tools/git-changelog.d.ts.map +1 -0
  23. package/dist/tools/git-changelog.js +728 -0
  24. package/dist/tools/git-changelog.js.map +1 -0
  25. package/dist/tools/git-cherry-pick.d.ts +150 -0
  26. package/dist/tools/git-cherry-pick.d.ts.map +1 -0
  27. package/dist/tools/git-cherry-pick.js +455 -0
  28. package/dist/tools/git-cherry-pick.js.map +1 -0
  29. package/dist/tools/git-dependencies.d.ts +233 -0
  30. package/dist/tools/git-dependencies.d.ts.map +1 -0
  31. package/dist/tools/git-dependencies.js +761 -0
  32. package/dist/tools/git-dependencies.js.map +1 -0
  33. package/dist/tools/git-hooks.d.ts +146 -0
  34. package/dist/tools/git-hooks.d.ts.map +1 -0
  35. package/dist/tools/git-hooks.js +634 -0
  36. package/dist/tools/git-hooks.js.map +1 -0
  37. package/dist/tools/git-stats-personal.d.ts +210 -0
  38. package/dist/tools/git-stats-personal.d.ts.map +1 -0
  39. package/dist/tools/git-stats-personal.js +718 -0
  40. package/dist/tools/git-stats-personal.js.map +1 -0
  41. package/dist/tools/git-tags.d.ts.map +1 -1
  42. package/dist/tools/git-tags.js +11 -3
  43. package/dist/tools/git-tags.js.map +1 -1
  44. package/dist/utils/parameter-validator.d.ts +9 -0
  45. package/dist/utils/parameter-validator.d.ts.map +1 -1
  46. package/dist/utils/parameter-validator.js +390 -14
  47. package/dist/utils/parameter-validator.js.map +1 -1
  48. package/package.json +81 -78
@@ -0,0 +1,728 @@
1
+ "use strict";
2
+ /**
3
+ * Git Changelog Tool
4
+ *
5
+ * Changelog generation tool providing comprehensive changelog operations.
6
+ * Supports conventional commits parsing, multiple formats, and semantic versioning.
7
+ *
8
+ * Operations: generate, update, preview, formats
9
+ */
10
+ var __importDefault = (this && this.__importDefault) || function (mod) {
11
+ return (mod && mod.__esModule) ? mod : { "default": mod };
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.GitChangelogTool = void 0;
15
+ const git_command_executor_js_1 = require("../utils/git-command-executor.js");
16
+ const parameter_validator_js_1 = require("../utils/parameter-validator.js");
17
+ const operation_error_handler_js_1 = require("../utils/operation-error-handler.js");
18
+ const fs_1 = __importDefault(require("fs"));
19
+ const path_1 = __importDefault(require("path"));
20
+ class GitChangelogTool {
21
+ gitExecutor;
22
+ // Conventional commit types and their display names
23
+ commitTypes = {
24
+ feat: { emoji: '✨', name: 'Features', description: 'New features' },
25
+ fix: { emoji: '🐛', name: 'Bug Fixes', description: 'Bug fixes' },
26
+ docs: { emoji: '📚', name: 'Documentation', description: 'Documentation changes' },
27
+ style: { emoji: '💎', name: 'Styles', description: 'Code style changes' },
28
+ refactor: { emoji: '📦', name: 'Code Refactoring', description: 'Code refactoring' },
29
+ perf: { emoji: '⚡️', name: 'Performance Improvements', description: 'Performance improvements' },
30
+ test: { emoji: '✅', name: 'Tests', description: 'Test changes' },
31
+ build: { emoji: '🏗️', name: 'Build System', description: 'Build system changes' },
32
+ ci: { emoji: '👷', name: 'Continuous Integration', description: 'CI changes' },
33
+ chore: { emoji: '🔧', name: 'Chores', description: 'Chore changes' },
34
+ revert: { emoji: '⏪️', name: 'Reverts', description: 'Reverted changes' }
35
+ };
36
+ constructor() {
37
+ this.gitExecutor = new git_command_executor_js_1.GitCommandExecutor();
38
+ }
39
+ /**
40
+ * Execute git-changelog operation
41
+ */
42
+ async execute(params) {
43
+ const startTime = Date.now();
44
+ try {
45
+ // Validate basic parameters
46
+ const validation = parameter_validator_js_1.ParameterValidator.validateToolParams('git-changelog', params);
47
+ if (!validation.isValid) {
48
+ return operation_error_handler_js_1.OperationErrorHandler.createToolError('VALIDATION_ERROR', `Parameter validation failed: ${validation.errors.join(', ')}`, params.action, { validationErrors: validation.errors }, validation.suggestions);
49
+ }
50
+ // Validate operation-specific parameters
51
+ const operationValidation = parameter_validator_js_1.ParameterValidator.validateOperationParams('git-changelog', params.action, params);
52
+ if (!operationValidation.isValid) {
53
+ return operation_error_handler_js_1.OperationErrorHandler.createToolError('VALIDATION_ERROR', `Operation validation failed: ${operationValidation.errors.join(', ')}`, params.action, { validationErrors: operationValidation.errors }, operationValidation.suggestions);
54
+ }
55
+ // Route to appropriate handler
56
+ switch (params.action) {
57
+ case 'generate':
58
+ return await this.handleGenerate(params, startTime);
59
+ case 'update':
60
+ return await this.handleUpdate(params, startTime);
61
+ case 'preview':
62
+ return await this.handlePreview(params, startTime);
63
+ case 'formats':
64
+ return await this.handleFormats(params, startTime);
65
+ default:
66
+ return operation_error_handler_js_1.OperationErrorHandler.createToolError('INVALID_ACTION', `Unknown action: ${params.action}`, params.action, { supportedActions: ['generate', 'update', 'preview', 'formats'] }, ['Use one of the supported actions']);
67
+ }
68
+ }
69
+ catch (error) {
70
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
71
+ return operation_error_handler_js_1.OperationErrorHandler.createToolError('EXECUTION_ERROR', `Failed to execute ${params.action}: ${errorMessage}`, params.action, { error: errorMessage }, ['Check the error details and try again']);
72
+ }
73
+ }
74
+ /**
75
+ * Generate changelog
76
+ */
77
+ async handleGenerate(params, startTime) {
78
+ try {
79
+ const commits = await this.getCommits(params);
80
+ const changelog = await this.generateChangelog(commits, params);
81
+ // Write to file if output path is specified
82
+ if (params.stdout) {
83
+ const outputPath = path_1.default.resolve(params.projectPath, params.stdout);
84
+ fs_1.default.writeFileSync(outputPath, changelog, 'utf8');
85
+ }
86
+ const executionTime = Date.now() - startTime;
87
+ return {
88
+ success: true,
89
+ data: {
90
+ changelog: {
91
+ content: changelog,
92
+ format: params.format || 'markdown',
93
+ outputPath: params.stdout,
94
+ version: params.version,
95
+ commits: commits.length,
96
+ stats: this.getChangelogStats(commits)
97
+ },
98
+ message: `Changelog generated successfully${params.stdout ? ` and saved to ${params.stdout}` : ''}`,
99
+ executionTime
100
+ }
101
+ };
102
+ }
103
+ catch (error) {
104
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
105
+ return operation_error_handler_js_1.OperationErrorHandler.createToolError('GENERATE_ERROR', `Failed to generate changelog: ${errorMessage}`, params.action, { error: errorMessage }, ['Check repository state', 'Verify date/tag parameters']);
106
+ }
107
+ }
108
+ /**
109
+ * Update existing changelog
110
+ */
111
+ async handleUpdate(params, startTime) {
112
+ try {
113
+ if (!params.stdout) {
114
+ return operation_error_handler_js_1.OperationErrorHandler.createToolError('MISSING_OUTPUT', 'Output path is required for update operation', params.action, { output: params.stdout }, ['Provide output parameter with changelog file path']);
115
+ }
116
+ const outputPath = path_1.default.resolve(params.projectPath, params.stdout);
117
+ // Read existing changelog
118
+ let existingContent = '';
119
+ if (fs_1.default.existsSync(outputPath)) {
120
+ existingContent = fs_1.default.readFileSync(outputPath, 'utf8');
121
+ }
122
+ // Generate new changelog
123
+ const commits = await this.getCommits(params);
124
+ const newChangelog = await this.generateChangelog(commits, params);
125
+ // Merge with existing content
126
+ const updatedContent = this.mergeChangelog(existingContent, newChangelog, params);
127
+ // Write updated content
128
+ fs_1.default.writeFileSync(outputPath, updatedContent, 'utf8');
129
+ const executionTime = Date.now() - startTime;
130
+ return {
131
+ success: true,
132
+ data: {
133
+ changelog: {
134
+ content: updatedContent,
135
+ format: params.format || 'markdown',
136
+ outputPath: params.stdout,
137
+ version: params.version,
138
+ commits: commits.length,
139
+ stats: this.getChangelogStats(commits),
140
+ updated: true
141
+ },
142
+ message: `Changelog updated successfully in ${params.stdout}`,
143
+ executionTime
144
+ }
145
+ };
146
+ }
147
+ catch (error) {
148
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
149
+ return operation_error_handler_js_1.OperationErrorHandler.createToolError('UPDATE_ERROR', `Failed to update changelog: ${errorMessage}`, params.action, { error: errorMessage }, ['Check output file permissions', 'Verify changelog format']);
150
+ }
151
+ }
152
+ /**
153
+ * Preview changelog
154
+ */
155
+ async handlePreview(params, startTime) {
156
+ try {
157
+ const commits = await this.getCommits(params);
158
+ const preview = await this.generateChangelog(commits, params);
159
+ const executionTime = Date.now() - startTime;
160
+ return {
161
+ success: true,
162
+ data: {
163
+ changelog: {
164
+ content: preview,
165
+ format: params.format || 'markdown',
166
+ version: params.version,
167
+ commits: commits.length,
168
+ stats: this.getChangelogStats(commits),
169
+ preview: true
170
+ },
171
+ message: 'Changelog preview generated successfully',
172
+ executionTime
173
+ }
174
+ };
175
+ }
176
+ catch (error) {
177
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
178
+ return operation_error_handler_js_1.OperationErrorHandler.createToolError('PREVIEW_ERROR', `Failed to preview changelog: ${errorMessage}`, params.action, { error: errorMessage }, ['Check repository state', 'Verify date/tag parameters']);
179
+ }
180
+ }
181
+ /**
182
+ * Get available formats
183
+ */
184
+ async handleFormats(params, startTime) {
185
+ try {
186
+ const formats = [
187
+ {
188
+ name: 'markdown',
189
+ description: 'Markdown format with headers and lists',
190
+ extension: '.md',
191
+ template: 'markdown'
192
+ },
193
+ {
194
+ name: 'json',
195
+ description: 'JSON format with structured data',
196
+ extension: '.json',
197
+ template: 'json'
198
+ },
199
+ {
200
+ name: 'html',
201
+ description: 'HTML format with styling',
202
+ extension: '.html',
203
+ template: 'html'
204
+ },
205
+ {
206
+ name: 'plain',
207
+ description: 'Plain text format',
208
+ extension: '.txt',
209
+ template: 'plain'
210
+ }
211
+ ];
212
+ const executionTime = Date.now() - startTime;
213
+ return {
214
+ success: true,
215
+ data: {
216
+ formats,
217
+ message: 'Available changelog formats retrieved successfully',
218
+ executionTime
219
+ }
220
+ };
221
+ }
222
+ catch (error) {
223
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
224
+ return operation_error_handler_js_1.OperationErrorHandler.createToolError('FORMATS_ERROR', `Failed to get formats: ${errorMessage}`, params.action, { error: errorMessage }, ['Check tool configuration']);
225
+ }
226
+ }
227
+ /**
228
+ * Get commits for changelog
229
+ */
230
+ async getCommits(params) {
231
+ let command = 'git log --pretty=format:"%H|%an|%ae|%ad|%s|%b" --date=iso';
232
+ // Add date/tag filters
233
+ if (params.fromTag && params.toTag) {
234
+ command += ` ${params.toTag}..${params.fromTag}`;
235
+ }
236
+ else if (params.fromTag) {
237
+ command += ` ${params.fromTag}..HEAD`;
238
+ }
239
+ else if (params.since && params.until) {
240
+ command += ` --since="${params.since}" --until="${params.until}"`;
241
+ }
242
+ else if (params.since) {
243
+ command += ` --since="${params.since}"`;
244
+ }
245
+ else if (params.until) {
246
+ command += ` --until="${params.until}"`;
247
+ }
248
+ // Add merge commit filter
249
+ if (!params.includeMergeCommits) {
250
+ command += ' --no-merges';
251
+ }
252
+ // Add commit limit
253
+ if (params.maxCommits) {
254
+ command += ` --max-count=${params.maxCommits}`;
255
+ }
256
+ const result = await this.gitExecutor.executeGitCommand(command, [], params.projectPath);
257
+ if (!result.success) {
258
+ return [];
259
+ }
260
+ const commits = [];
261
+ const lines = result.stdout.trim().split('\n').filter(line => line.trim());
262
+ for (const line of lines) {
263
+ const [hash, author, email, date, subject, body] = line.split('|', 6);
264
+ const commit = {
265
+ hash,
266
+ author,
267
+ email,
268
+ date: new Date(date),
269
+ subject,
270
+ body: body || '',
271
+ type: this.parseCommitType(subject),
272
+ scope: this.parseCommitScope(subject),
273
+ breaking: this.isBreakingChange(subject, body),
274
+ issues: this.extractIssues(subject, body)
275
+ };
276
+ // Apply filters
277
+ if (this.shouldIncludeCommit(commit, params)) {
278
+ commits.push(commit);
279
+ }
280
+ }
281
+ return commits;
282
+ }
283
+ /**
284
+ * Generate changelog content
285
+ */
286
+ async generateChangelog(commits, params) {
287
+ const format = params.format || 'markdown';
288
+ switch (format) {
289
+ case 'markdown':
290
+ return this.generateMarkdownChangelog(commits, params);
291
+ case 'json':
292
+ return this.generateJsonChangelog(commits, params);
293
+ case 'html':
294
+ return this.generateHtmlChangelog(commits, params);
295
+ case 'plain':
296
+ return this.generatePlainChangelog(commits, params);
297
+ default:
298
+ return this.generateMarkdownChangelog(commits, params);
299
+ }
300
+ }
301
+ /**
302
+ * Generate Markdown changelog
303
+ */
304
+ generateMarkdownChangelog(commits, params) {
305
+ let changelog = '';
306
+ // Header
307
+ const version = params.version || 'Unreleased';
308
+ const date = new Date().toISOString().split('T')[0];
309
+ changelog += `# Changelog\n\n`;
310
+ changelog += `## [${version}] - ${date}\n\n`;
311
+ // Breaking changes
312
+ const breakingChanges = commits.filter(c => c.breaking);
313
+ if (breakingChanges.length > 0) {
314
+ changelog += `### ⚠️ Breaking Changes\n\n`;
315
+ for (const commit of breakingChanges) {
316
+ changelog += `- ${commit.subject.replace(/^BREAKING CHANGE: /, '')}\n`;
317
+ }
318
+ changelog += '\n';
319
+ }
320
+ // Group by type
321
+ const grouped = this.groupCommitsByType(commits);
322
+ for (const [type, typeCommits] of Object.entries(grouped)) {
323
+ if (typeCommits.length === 0)
324
+ continue;
325
+ const typeInfo = this.commitTypes[type];
326
+ if (typeInfo) {
327
+ changelog += `### ${typeInfo.emoji} ${typeInfo.name}\n\n`;
328
+ for (const commit of typeCommits) {
329
+ const scope = commit.scope ? `**${commit.scope}**: ` : '';
330
+ const description = this.formatCommitDescription(commit.subject, type);
331
+ changelog += `- ${scope}${description}\n`;
332
+ }
333
+ changelog += '\n';
334
+ }
335
+ }
336
+ // Footer
337
+ if (params.includeIssues) {
338
+ const allIssues = commits.flatMap(c => c.issues);
339
+ if (allIssues.length > 0) {
340
+ changelog += `### 🔗 Issues & Pull Requests\n\n`;
341
+ const uniqueIssues = [...new Set(allIssues)];
342
+ for (const issue of uniqueIssues) {
343
+ changelog += `- ${issue}\n`;
344
+ }
345
+ changelog += '\n';
346
+ }
347
+ }
348
+ return changelog;
349
+ }
350
+ /**
351
+ * Generate JSON changelog
352
+ */
353
+ generateJsonChangelog(commits, params) {
354
+ const changelog = {
355
+ version: params.version || 'Unreleased',
356
+ date: new Date().toISOString(),
357
+ changes: {
358
+ breaking: commits.filter(c => c.breaking),
359
+ features: commits.filter(c => c.type === 'feat'),
360
+ fixes: commits.filter(c => c.type === 'fix'),
361
+ other: commits.filter(c => !['feat', 'fix'].includes(c.type) && !c.breaking)
362
+ },
363
+ stats: this.getChangelogStats(commits),
364
+ commits: commits.map(c => ({
365
+ hash: c.hash,
366
+ type: c.type,
367
+ scope: c.scope,
368
+ subject: c.subject,
369
+ author: c.author,
370
+ date: c.date.toISOString(),
371
+ breaking: c.breaking,
372
+ issues: c.issues
373
+ }))
374
+ };
375
+ return JSON.stringify(changelog, null, 2);
376
+ }
377
+ /**
378
+ * Generate HTML changelog
379
+ */
380
+ generateHtmlChangelog(commits, params) {
381
+ let html = `
382
+ <!DOCTYPE html>
383
+ <html>
384
+ <head>
385
+ <title>Changelog</title>
386
+ <style>
387
+ body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
388
+ .version { color: #0366d6; border-bottom: 2px solid #e1e4e8; padding-bottom: 10px; }
389
+ .breaking { background: #fff5f5; border-left: 4px solid #d73a49; padding: 10px; margin: 10px 0; }
390
+ .feature { color: #28a745; }
391
+ .fix { color: #d73a49; }
392
+ .other { color: #6a737d; }
393
+ .commit { margin: 5px 0; }
394
+ .scope { font-weight: bold; }
395
+ ul { list-style-type: none; padding-left: 0; }
396
+ </style>
397
+ </head>
398
+ <body>
399
+ <h1>Changelog</h1>
400
+ <div class="version">
401
+ <h2>${params.version || 'Unreleased'} - ${new Date().toISOString().split('T')[0]}</h2>
402
+ </div>
403
+ `;
404
+ // Breaking changes
405
+ const breakingChanges = commits.filter(c => c.breaking);
406
+ if (breakingChanges.length > 0) {
407
+ html += `<div class="breaking">
408
+ <h3>⚠️ Breaking Changes</h3>
409
+ <ul>`;
410
+ for (const commit of breakingChanges) {
411
+ html += `<li class="commit">${commit.subject.replace(/^BREAKING CHANGE: /, '')}</li>`;
412
+ }
413
+ html += `</ul></div>`;
414
+ }
415
+ // Group by type
416
+ const grouped = this.groupCommitsByType(commits);
417
+ for (const [type, typeCommits] of Object.entries(grouped)) {
418
+ if (typeCommits.length === 0)
419
+ continue;
420
+ const typeInfo = this.commitTypes[type];
421
+ if (typeInfo) {
422
+ html += `<h3>${typeInfo.emoji} ${typeInfo.name}</h3>
423
+ <ul>`;
424
+ for (const commit of typeCommits) {
425
+ const scope = commit.scope ? `<span class="scope">${commit.scope}:</span> ` : '';
426
+ const description = this.formatCommitDescription(commit.subject, type);
427
+ html += `<li class="commit ${type}">${scope}${description}</li>`;
428
+ }
429
+ html += `</ul>`;
430
+ }
431
+ }
432
+ html += `</body></html>`;
433
+ return html;
434
+ }
435
+ /**
436
+ * Generate plain text changelog
437
+ */
438
+ generatePlainChangelog(commits, params) {
439
+ let changelog = `CHANGELOG\n`;
440
+ changelog += `${'='.repeat(50)}\n\n`;
441
+ changelog += `${params.version || 'Unreleased'} - ${new Date().toISOString().split('T')[0]}\n\n`;
442
+ // Group by type
443
+ const grouped = this.groupCommitsByType(commits);
444
+ for (const [type, typeCommits] of Object.entries(grouped)) {
445
+ if (typeCommits.length === 0)
446
+ continue;
447
+ const typeInfo = this.commitTypes[type];
448
+ if (typeInfo) {
449
+ changelog += `${typeInfo.name.toUpperCase()}\n`;
450
+ changelog += `${'-'.repeat(typeInfo.name.length)}\n`;
451
+ for (const commit of typeCommits) {
452
+ const scope = commit.scope ? `${commit.scope}: ` : '';
453
+ const description = this.formatCommitDescription(commit.subject, type);
454
+ changelog += ` - ${scope}${description}\n`;
455
+ }
456
+ changelog += '\n';
457
+ }
458
+ }
459
+ return changelog;
460
+ }
461
+ /**
462
+ * Parse commit type from subject
463
+ */
464
+ parseCommitType(subject) {
465
+ const match = subject.match(/^(\w+)(?:\(.+\))?:/);
466
+ return match ? match[1].toLowerCase() : 'other';
467
+ }
468
+ /**
469
+ * Parse commit scope from subject
470
+ */
471
+ parseCommitScope(subject) {
472
+ const match = subject.match(/^\w+\((.+)\):/);
473
+ return match ? match[1] : null;
474
+ }
475
+ /**
476
+ * Check if commit is a breaking change
477
+ */
478
+ isBreakingChange(subject, body) {
479
+ return subject.includes('BREAKING CHANGE:') ||
480
+ subject.includes('!') ||
481
+ body.includes('BREAKING CHANGE:');
482
+ }
483
+ /**
484
+ * Extract issues and PRs from commit
485
+ */
486
+ extractIssues(subject, body) {
487
+ const issues = [];
488
+ const text = `${subject} ${body}`;
489
+ // GitHub issues/PRs
490
+ const githubMatches = text.match(/#\d+/g);
491
+ if (githubMatches) {
492
+ issues.push(...githubMatches);
493
+ }
494
+ // Jira issues
495
+ const jiraMatches = text.match(/[A-Z]+-\d+/g);
496
+ if (jiraMatches) {
497
+ issues.push(...jiraMatches);
498
+ }
499
+ return issues;
500
+ }
501
+ /**
502
+ * Check if commit should be included
503
+ */
504
+ shouldIncludeCommit(commit, params) {
505
+ // Filter by type
506
+ if (params.includeTypes && !params.includeTypes.includes(commit.type)) {
507
+ return false;
508
+ }
509
+ if (params.excludeTypes && params.excludeTypes.includes(commit.type)) {
510
+ return false;
511
+ }
512
+ // Filter by scope
513
+ if (params.includeScopes && commit.scope && !params.includeScopes.includes(commit.scope)) {
514
+ return false;
515
+ }
516
+ if (params.excludeScopes && commit.scope && params.excludeScopes.includes(commit.scope)) {
517
+ return false;
518
+ }
519
+ // Filter reverts
520
+ if (!params.includeReverts && commit.type === 'revert') {
521
+ return false;
522
+ }
523
+ return true;
524
+ }
525
+ /**
526
+ * Group commits by type
527
+ */
528
+ groupCommitsByType(commits) {
529
+ const grouped = {};
530
+ for (const commit of commits) {
531
+ if (!grouped[commit.type]) {
532
+ grouped[commit.type] = [];
533
+ }
534
+ grouped[commit.type].push(commit);
535
+ }
536
+ return grouped;
537
+ }
538
+ /**
539
+ * Format commit description
540
+ */
541
+ formatCommitDescription(subject, type) {
542
+ // Remove type and scope from subject
543
+ let description = subject.replace(/^\w+(?:\(.+\))?: ?/, '');
544
+ // Remove breaking change prefix
545
+ description = description.replace(/^BREAKING CHANGE: ?/, '');
546
+ return description;
547
+ }
548
+ /**
549
+ * Get changelog statistics
550
+ */
551
+ getChangelogStats(commits) {
552
+ const stats = {
553
+ totalCommits: commits.length,
554
+ breakingChanges: commits.filter(c => c.breaking).length,
555
+ features: commits.filter(c => c.type === 'feat').length,
556
+ fixes: commits.filter(c => c.type === 'fix').length,
557
+ types: {},
558
+ scopes: {},
559
+ authors: {},
560
+ issues: []
561
+ };
562
+ for (const commit of commits) {
563
+ // Count types
564
+ stats.types[commit.type] = (stats.types[commit.type] || 0) + 1;
565
+ // Count scopes
566
+ if (commit.scope) {
567
+ stats.scopes[commit.scope] = (stats.scopes[commit.scope] || 0) + 1;
568
+ }
569
+ // Count authors
570
+ stats.authors[commit.author] = (stats.authors[commit.author] || 0) + 1;
571
+ // Collect issues
572
+ stats.issues.push(...commit.issues);
573
+ }
574
+ // Remove duplicates from issues
575
+ stats.issues = [...new Set(stats.issues)];
576
+ return stats;
577
+ }
578
+ /**
579
+ * Merge new changelog with existing content
580
+ */
581
+ mergeChangelog(existing, newContent, params) {
582
+ if (!existing.trim()) {
583
+ return newContent;
584
+ }
585
+ // Simple merge: prepend new content
586
+ return `${newContent}\n\n---\n\n${existing}`;
587
+ }
588
+ /**
589
+ * Get tool schema for MCP registration
590
+ */
591
+ static getToolSchema() {
592
+ return {
593
+ name: 'git-changelog',
594
+ description: 'Automatic changelog generation tool. Creates changelogs from conventional commits with multiple output formats and filtering options.',
595
+ inputSchema: {
596
+ type: 'object',
597
+ properties: {
598
+ "action": {
599
+ "type": "string",
600
+ "enum": [
601
+ "generate",
602
+ "update",
603
+ "preview",
604
+ "formats"
605
+ ],
606
+ "description": "The operation to perform"
607
+ },
608
+ "projectPath": {
609
+ "type": "string",
610
+ "description": "Path to the Git repository"
611
+ },
612
+ "format": {
613
+ "type": "string",
614
+ "description": "format parameter"
615
+ },
616
+ "output": {
617
+ "type": "string",
618
+ "description": "output parameter"
619
+ },
620
+ "fromTag": {
621
+ "type": "string",
622
+ "description": "fromTag parameter"
623
+ },
624
+ "toTag": {
625
+ "type": "string",
626
+ "description": "toTag parameter"
627
+ },
628
+ "since": {
629
+ "type": "string",
630
+ "description": "since parameter"
631
+ },
632
+ "until": {
633
+ "type": "string",
634
+ "description": "until parameter"
635
+ },
636
+ "groupBy": {
637
+ "type": "string",
638
+ "description": "groupBy parameter"
639
+ },
640
+ "includeTypes": {
641
+ "type": "array",
642
+ "items": {
643
+ "type": "string"
644
+ },
645
+ "description": "includeTypes parameter"
646
+ },
647
+ "excludeTypes": {
648
+ "type": "array",
649
+ "items": {
650
+ "type": "string"
651
+ },
652
+ "description": "excludeTypes parameter"
653
+ },
654
+ "includeScopes": {
655
+ "type": "array",
656
+ "items": {
657
+ "type": "string"
658
+ },
659
+ "description": "includeScopes parameter"
660
+ },
661
+ "excludeScopes": {
662
+ "type": "array",
663
+ "items": {
664
+ "type": "string"
665
+ },
666
+ "description": "excludeScopes parameter"
667
+ },
668
+ "includeBreakingChanges": {
669
+ "type": "string",
670
+ "description": "includeBreakingChanges parameter"
671
+ },
672
+ "includeMergeCommits": {
673
+ "type": "string",
674
+ "description": "includeMergeCommits parameter"
675
+ },
676
+ "includeReverts": {
677
+ "type": "string",
678
+ "description": "includeReverts parameter"
679
+ },
680
+ "includeIssues": {
681
+ "type": "string",
682
+ "description": "includeIssues parameter"
683
+ },
684
+ "template": {
685
+ "type": "string",
686
+ "description": "template parameter"
687
+ },
688
+ "headerTemplate": {
689
+ "type": "string",
690
+ "description": "headerTemplate parameter"
691
+ },
692
+ "sectionTemplate": {
693
+ "type": "string",
694
+ "description": "sectionTemplate parameter"
695
+ },
696
+ "commitTemplate": {
697
+ "type": "string",
698
+ "description": "commitTemplate parameter"
699
+ },
700
+ "version": {
701
+ "type": "string",
702
+ "description": "version parameter"
703
+ },
704
+ "unreleased": {
705
+ "type": "string",
706
+ "description": "unreleased parameter"
707
+ },
708
+ "showStats": {
709
+ "type": "string",
710
+ "description": "showStats parameter"
711
+ },
712
+ "maxCommits": {
713
+ "type": "string",
714
+ "description": "maxCommits parameter"
715
+ }
716
+ },
717
+ required: ['action', 'projectPath'],
718
+ additionalProperties: false
719
+ },
720
+ errorCodes: {
721
+ 'VALIDATION_ERROR': 'Parameter validation failed',
722
+ 'EXECUTION_ERROR': 'Tool execution failed'
723
+ }
724
+ };
725
+ }
726
+ }
727
+ exports.GitChangelogTool = GitChangelogTool;
728
+ //# sourceMappingURL=git-changelog.js.map