@andrebuzeli/git-mcp 5.2.9 → 5.3.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.
@@ -0,0 +1,948 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GitUpdateTool = void 0;
4
+ const git_command_executor_1 = require("../utils/git-command-executor");
5
+ const operation_error_handler_1 = require("../utils/operation-error-handler");
6
+ const parameter_validator_1 = require("../utils/parameter-validator");
7
+ const provider_operation_handler_1 = require("../providers/provider-operation-handler");
8
+ const logger_1 = require("../utils/logger");
9
+ class GitUpdateTool {
10
+ gitExecutor;
11
+ providerHandler;
12
+ logger;
13
+ updateHistory = [];
14
+ constructor(providerConfig) {
15
+ this.gitExecutor = new git_command_executor_1.GitCommandExecutor();
16
+ this.logger = new logger_1.Logger();
17
+ if (providerConfig) {
18
+ this.providerHandler = new provider_operation_handler_1.ProviderOperationHandler(providerConfig);
19
+ }
20
+ }
21
+ static getToolSchema() {
22
+ return {
23
+ name: 'git-update',
24
+ description: 'Advanced project update tool with history tracking, changelog generation, and multi-provider synchronization',
25
+ inputSchema: {
26
+ type: 'object',
27
+ properties: {
28
+ action: {
29
+ type: 'string',
30
+ enum: ['update', 'history', 'changelog', 'track', 'sync-providers', 'status', 'rollback', 'compare'],
31
+ description: 'Update operation to perform'
32
+ },
33
+ projectPath: {
34
+ type: 'string',
35
+ description: 'Path to the Git repository'
36
+ },
37
+ provider: {
38
+ type: 'string',
39
+ enum: ['github', 'gitea', 'both'],
40
+ description: 'Provider for remote operations'
41
+ },
42
+ updateType: {
43
+ type: 'string',
44
+ enum: ['all', 'dependencies', 'code', 'docs', 'config'],
45
+ description: 'Type of update to perform'
46
+ },
47
+ autoCommit: {
48
+ type: 'boolean',
49
+ description: 'Automatically commit changes'
50
+ },
51
+ commitMessage: {
52
+ type: 'string',
53
+ description: 'Custom commit message'
54
+ },
55
+ createTag: {
56
+ type: 'boolean',
57
+ description: 'Create a tag after update'
58
+ },
59
+ tagName: {
60
+ type: 'string',
61
+ description: 'Name for the tag'
62
+ },
63
+ since: {
64
+ type: 'string',
65
+ description: 'Start date for history (ISO format or relative)'
66
+ },
67
+ until: {
68
+ type: 'string',
69
+ description: 'End date for history (ISO format or relative)'
70
+ },
71
+ author: {
72
+ type: 'string',
73
+ description: 'Filter by author'
74
+ },
75
+ format: {
76
+ type: 'string',
77
+ enum: ['json', 'markdown', 'text'],
78
+ description: 'Output format for history'
79
+ },
80
+ changelogPath: {
81
+ type: 'string',
82
+ description: 'Path to changelog file'
83
+ },
84
+ version: {
85
+ type: 'string',
86
+ description: 'Version for changelog entry'
87
+ },
88
+ includeCommits: {
89
+ type: 'boolean',
90
+ description: 'Include commit details in changelog'
91
+ },
92
+ groupByType: {
93
+ type: 'boolean',
94
+ description: 'Group changelog entries by type'
95
+ },
96
+ trackFile: {
97
+ type: 'string',
98
+ description: 'File to track for changes'
99
+ },
100
+ trackPattern: {
101
+ type: 'string',
102
+ description: 'Pattern to track for changes'
103
+ },
104
+ watchMode: {
105
+ type: 'boolean',
106
+ description: 'Enable watch mode for tracking'
107
+ },
108
+ providers: {
109
+ type: 'array',
110
+ items: { type: 'string' },
111
+ description: 'Providers to sync with'
112
+ },
113
+ forceSync: {
114
+ type: 'boolean',
115
+ description: 'Force synchronization'
116
+ },
117
+ rollbackTo: {
118
+ type: 'string',
119
+ description: 'Target for rollback (commit, tag, or version)'
120
+ },
121
+ rollbackType: {
122
+ type: 'string',
123
+ enum: ['commit', 'tag', 'version'],
124
+ description: 'Type of rollback target'
125
+ },
126
+ compareWith: {
127
+ type: 'string',
128
+ description: 'Target to compare with'
129
+ },
130
+ compareType: {
131
+ type: 'string',
132
+ enum: ['commit', 'tag', 'branch', 'provider'],
133
+ description: 'Type of comparison target'
134
+ }
135
+ },
136
+ required: ['action', 'projectPath']
137
+ }
138
+ };
139
+ }
140
+ async execute(params) {
141
+ const startTime = Date.now();
142
+ this.logger.info(`Executing git-update ${params.action}`);
143
+ try {
144
+ // Validate parameters
145
+ const validation = parameter_validator_1.ParameterValidator.validateToolParams('git-update', params);
146
+ if (!validation.isValid) {
147
+ return operation_error_handler_1.OperationErrorHandler.createToolError('VALIDATION_ERROR', 'Parameter validation failed', params.action, validation, validation.suggestions);
148
+ }
149
+ // Route to appropriate handler
150
+ switch (params.action) {
151
+ case 'update':
152
+ return await this.handleUpdate(params, startTime);
153
+ case 'history':
154
+ return await this.handleHistory(params, startTime);
155
+ case 'changelog':
156
+ return await this.handleChangelog(params, startTime);
157
+ case 'track':
158
+ return await this.handleTrack(params, startTime);
159
+ case 'sync-providers':
160
+ return await this.handleSyncProviders(params, startTime);
161
+ case 'status':
162
+ return await this.handleStatus(params, startTime);
163
+ case 'rollback':
164
+ return await this.handleRollback(params, startTime);
165
+ case 'compare':
166
+ return await this.handleCompare(params, startTime);
167
+ default:
168
+ return operation_error_handler_1.OperationErrorHandler.createToolError('UNSUPPORTED_OPERATION', `Update operation '${params.action}' is not supported`, params.action, {}, ['Use one of: update, history, changelog, track, sync-providers, status, rollback, compare']);
169
+ }
170
+ }
171
+ catch (error) {
172
+ this.logger.error('Error executing git-update');
173
+ return operation_error_handler_1.OperationErrorHandler.createToolError('EXECUTION_ERROR', `Failed to execute git-update ${params.action}: ${error instanceof Error ? error.message : 'Unknown error'}`, params.action, { error: error instanceof Error ? error.message : String(error) }, ['Check project path and Git repository status']);
174
+ }
175
+ }
176
+ async handleUpdate(params, startTime) {
177
+ try {
178
+ const updateType = params.updateType || 'all';
179
+ const changes = [];
180
+ const files = [];
181
+ this.logger.info(`Performing ${updateType} update`);
182
+ // Perform update based on type
183
+ switch (updateType) {
184
+ case 'dependencies':
185
+ await this.updateDependencies(params.projectPath);
186
+ changes.push('Updated dependencies');
187
+ break;
188
+ case 'code':
189
+ await this.updateCode(params.projectPath);
190
+ changes.push('Updated code files');
191
+ break;
192
+ case 'docs':
193
+ await this.updateDocs(params.projectPath);
194
+ changes.push('Updated documentation');
195
+ break;
196
+ case 'config':
197
+ await this.updateConfig(params.projectPath);
198
+ changes.push('Updated configuration');
199
+ break;
200
+ case 'all':
201
+ default:
202
+ await this.updateAll(params.projectPath);
203
+ changes.push('Updated all project components');
204
+ break;
205
+ }
206
+ // Get changed files
207
+ const statusResult = await this.gitExecutor.executeGitCommand('status', ['--porcelain'], params.projectPath);
208
+ if (statusResult.success) {
209
+ const changedFiles = statusResult.stdout
210
+ .split('\n')
211
+ .filter(line => line.trim())
212
+ .map(line => line.substring(3).trim());
213
+ files.push(...changedFiles);
214
+ }
215
+ // Auto commit if requested
216
+ let commitHash;
217
+ if (params.autoCommit !== false && files.length > 0) {
218
+ const commitMessage = params.commitMessage || `Update: ${changes.join(', ')}`;
219
+ const addResult = await this.gitExecutor.executeGitCommand('add', ['.'], params.projectPath);
220
+ if (addResult.success) {
221
+ const commitResult = await this.gitExecutor.executeGitCommand('commit', ['-m', commitMessage], params.projectPath);
222
+ if (commitResult.success) {
223
+ commitHash = await this.getCurrentCommitHash(params.projectPath);
224
+ }
225
+ }
226
+ }
227
+ // Create tag if requested
228
+ let tagName;
229
+ if (params.createTag && commitHash) {
230
+ tagName = params.tagName || `update-${Date.now()}`;
231
+ await this.gitExecutor.executeGitCommand('tag', [tagName], params.projectPath);
232
+ }
233
+ // Record in history
234
+ const historyEntry = {
235
+ timestamp: new Date().toISOString(),
236
+ action: 'update',
237
+ details: changes.join(', '),
238
+ files,
239
+ commitHash,
240
+ tagName
241
+ };
242
+ this.updateHistory.push(historyEntry);
243
+ const executionTime = Date.now() - startTime;
244
+ return {
245
+ success: true,
246
+ data: {
247
+ updateType,
248
+ changes,
249
+ files,
250
+ commitHash,
251
+ tagName,
252
+ executionTime
253
+ },
254
+ metadata: {
255
+ operation: params.action,
256
+ timestamp: new Date().toISOString(),
257
+ executionTime
258
+ }
259
+ };
260
+ }
261
+ catch (error) {
262
+ return operation_error_handler_1.OperationErrorHandler.createToolError('UPDATE_ERROR', `Failed to perform update: ${error instanceof Error ? error.message : 'Unknown error'}`, params.action, { updateType: params.updateType }, ['Check repository status and permissions']);
263
+ }
264
+ }
265
+ async handleHistory(params, startTime) {
266
+ try {
267
+ const since = params.since || '1 week ago';
268
+ const until = params.until || 'now';
269
+ const author = params.author;
270
+ const format = params.format || 'json';
271
+ this.logger.info(`Getting update history since ${since} until ${until}`);
272
+ // Get Git log
273
+ const logArgs = ['--since', since, '--until', until];
274
+ if (author) {
275
+ logArgs.push('--author', author);
276
+ }
277
+ logArgs.push('--pretty=format:%H|%an|%ad|%s', '--date=iso');
278
+ const logResult = await this.gitExecutor.executeGitCommand('log', logArgs, params.projectPath);
279
+ if (!logResult.success) {
280
+ throw new Error('Failed to get Git history');
281
+ }
282
+ const commits = logResult.stdout
283
+ .split('\n')
284
+ .filter(line => line.trim())
285
+ .map(line => {
286
+ const [hash, author, date, message] = line.split('|');
287
+ return {
288
+ hash: hash.substring(0, 8),
289
+ fullHash: hash,
290
+ author,
291
+ date,
292
+ message
293
+ };
294
+ });
295
+ // Get update history
296
+ const filteredHistory = this.updateHistory.filter(entry => {
297
+ const entryDate = new Date(entry.timestamp);
298
+ const sinceDate = new Date(since);
299
+ const untilDate = new Date(until);
300
+ return entryDate >= sinceDate && entryDate <= untilDate;
301
+ });
302
+ let output;
303
+ switch (format) {
304
+ case 'markdown':
305
+ output = this.formatHistoryMarkdown(commits, filteredHistory);
306
+ break;
307
+ case 'text':
308
+ output = this.formatHistoryText(commits, filteredHistory);
309
+ break;
310
+ case 'json':
311
+ default:
312
+ output = {
313
+ commits,
314
+ updateHistory: filteredHistory,
315
+ summary: {
316
+ totalCommits: commits.length,
317
+ totalUpdates: filteredHistory.length,
318
+ period: { since, until }
319
+ }
320
+ };
321
+ break;
322
+ }
323
+ const executionTime = Date.now() - startTime;
324
+ return {
325
+ success: true,
326
+ data: output,
327
+ metadata: {
328
+ operation: params.action,
329
+ timestamp: new Date().toISOString(),
330
+ executionTime
331
+ }
332
+ };
333
+ }
334
+ catch (error) {
335
+ return operation_error_handler_1.OperationErrorHandler.createToolError('HISTORY_ERROR', `Failed to get history: ${error instanceof Error ? error.message : 'Unknown error'}`, params.action, { since: params.since, until: params.until }, ['Check date format and repository access']);
336
+ }
337
+ }
338
+ async handleChangelog(params, startTime) {
339
+ try {
340
+ const changelogPath = params.changelogPath || 'CHANGELOG.md';
341
+ const version = params.version || await this.getCurrentVersion(params.projectPath);
342
+ const includeCommits = params.includeCommits !== false;
343
+ const groupByType = params.groupByType !== false;
344
+ this.logger.info(`Generating changelog for version ${version}`);
345
+ // Get commits since last tag
346
+ const lastTag = await this.getLastTag(params.projectPath);
347
+ const sinceRef = lastTag || 'HEAD~10';
348
+ const logResult = await this.gitExecutor.executeGitCommand('log', [
349
+ `${sinceRef}..HEAD`,
350
+ '--pretty=format:%H|%an|%ad|%s',
351
+ '--date=iso'
352
+ ], params.projectPath);
353
+ const commits = logResult.stdout
354
+ .split('\n')
355
+ .filter(line => line.trim())
356
+ .map(line => {
357
+ const [hash, author, date, message] = line.split('|');
358
+ return {
359
+ hash: hash.substring(0, 8),
360
+ fullHash: hash,
361
+ author,
362
+ date,
363
+ message,
364
+ type: this.parseCommitType(message)
365
+ };
366
+ });
367
+ // Generate changelog entry
368
+ const changelogEntry = {
369
+ version,
370
+ date: new Date().toISOString().split('T')[0],
371
+ type: this.determineReleaseType(commits),
372
+ description: this.generateReleaseDescription(commits),
373
+ commits: includeCommits ? commits.map(c => c.message) : [],
374
+ author: commits.length > 0 ? commits[0].author : 'Unknown',
375
+ files: await this.getChangedFiles(sinceRef, params.projectPath)
376
+ };
377
+ // Generate changelog content
378
+ const changelogContent = this.generateChangelogContent(changelogEntry, groupByType);
379
+ // Write to file
380
+ const fs = require('fs');
381
+ const path = require('path');
382
+ const fullPath = path.join(params.projectPath, changelogPath);
383
+ let existingContent = '';
384
+ if (fs.existsSync(fullPath)) {
385
+ existingContent = fs.readFileSync(fullPath, 'utf8');
386
+ }
387
+ const newContent = this.mergeChangelogContent(changelogContent, existingContent);
388
+ fs.writeFileSync(fullPath, newContent);
389
+ const executionTime = Date.now() - startTime;
390
+ return {
391
+ success: true,
392
+ data: {
393
+ version,
394
+ changelogPath,
395
+ entry: changelogEntry,
396
+ content: changelogContent
397
+ },
398
+ metadata: {
399
+ operation: params.action,
400
+ timestamp: new Date().toISOString(),
401
+ executionTime
402
+ }
403
+ };
404
+ }
405
+ catch (error) {
406
+ return operation_error_handler_1.OperationErrorHandler.createToolError('CHANGELOG_ERROR', `Failed to generate changelog: ${error instanceof Error ? error.message : 'Unknown error'}`, params.action, { version: params.version, changelogPath: params.changelogPath }, ['Check repository status and file permissions']);
407
+ }
408
+ }
409
+ async handleTrack(params, startTime) {
410
+ try {
411
+ const trackFile = params.trackFile;
412
+ const trackPattern = params.trackPattern;
413
+ const watchMode = params.watchMode || false;
414
+ this.logger.info(`Tracking changes${trackFile ? ` for file: ${trackFile}` : ''}${trackPattern ? ` with pattern: ${trackPattern}` : ''}`);
415
+ if (trackFile) {
416
+ // Track specific file
417
+ const fileStatus = await this.trackFile(trackFile, params.projectPath);
418
+ const historyEntry = {
419
+ timestamp: new Date().toISOString(),
420
+ action: 'track',
421
+ details: `Tracked file: ${trackFile}`,
422
+ files: [trackFile],
423
+ provider: params.provider || 'both'
424
+ };
425
+ this.updateHistory.push(historyEntry);
426
+ return {
427
+ success: true,
428
+ data: {
429
+ trackedFile: trackFile,
430
+ status: fileStatus,
431
+ watchMode
432
+ },
433
+ metadata: {
434
+ operation: params.action,
435
+ timestamp: new Date().toISOString(),
436
+ executionTime: Date.now() - startTime
437
+ }
438
+ };
439
+ }
440
+ else if (trackPattern) {
441
+ // Track files matching pattern
442
+ const matchingFiles = await this.trackPattern(trackPattern, params.projectPath);
443
+ const historyEntry = {
444
+ timestamp: new Date().toISOString(),
445
+ action: 'track',
446
+ details: `Tracked pattern: ${trackPattern}`,
447
+ files: matchingFiles,
448
+ provider: params.provider || 'both'
449
+ };
450
+ this.updateHistory.push(historyEntry);
451
+ return {
452
+ success: true,
453
+ data: {
454
+ pattern: trackPattern,
455
+ matchingFiles,
456
+ count: matchingFiles.length,
457
+ watchMode
458
+ },
459
+ metadata: {
460
+ operation: params.action,
461
+ timestamp: new Date().toISOString(),
462
+ executionTime: Date.now() - startTime
463
+ }
464
+ };
465
+ }
466
+ else {
467
+ // Track all changes
468
+ const allChanges = await this.trackAllChanges(params.projectPath);
469
+ const historyEntry = {
470
+ timestamp: new Date().toISOString(),
471
+ action: 'track',
472
+ details: 'Tracked all changes',
473
+ files: allChanges,
474
+ provider: params.provider || 'both'
475
+ };
476
+ this.updateHistory.push(historyEntry);
477
+ return {
478
+ success: true,
479
+ data: {
480
+ allChanges,
481
+ count: allChanges.length,
482
+ watchMode
483
+ },
484
+ metadata: {
485
+ operation: params.action,
486
+ timestamp: new Date().toISOString(),
487
+ executionTime: Date.now() - startTime
488
+ }
489
+ };
490
+ }
491
+ }
492
+ catch (error) {
493
+ return operation_error_handler_1.OperationErrorHandler.createToolError('TRACK_ERROR', `Failed to track changes: ${error instanceof Error ? error.message : 'Unknown error'}`, params.action, { trackFile: params.trackFile, trackPattern: params.trackPattern }, ['Check file paths and repository status']);
494
+ }
495
+ }
496
+ async handleSyncProviders(params, startTime) {
497
+ try {
498
+ const providers = params.providers || ['github', 'gitea'];
499
+ const forceSync = params.forceSync || false;
500
+ this.logger.info(`Syncing with providers: ${providers.join(', ')}`);
501
+ const syncResults = {};
502
+ for (const provider of providers) {
503
+ try {
504
+ if (this.providerHandler) {
505
+ // Sync with provider
506
+ const syncResult = await this.providerHandler.executeOperation({
507
+ provider: provider,
508
+ operation: 'sync',
509
+ parameters: {
510
+ projectPath: params.projectPath,
511
+ force: forceSync
512
+ },
513
+ requiresAuth: true,
514
+ isRemoteOperation: true
515
+ });
516
+ syncResults[provider] = {
517
+ success: true,
518
+ result: syncResult
519
+ };
520
+ }
521
+ else {
522
+ syncResults[provider] = {
523
+ success: false,
524
+ error: 'Provider handler not configured'
525
+ };
526
+ }
527
+ }
528
+ catch (error) {
529
+ syncResults[provider] = {
530
+ success: false,
531
+ error: error instanceof Error ? error.message : 'Unknown error'
532
+ };
533
+ }
534
+ }
535
+ const historyEntry = {
536
+ timestamp: new Date().toISOString(),
537
+ action: 'sync-providers',
538
+ details: `Synced with providers: ${providers.join(', ')}`,
539
+ files: [],
540
+ provider: providers.join(',')
541
+ };
542
+ this.updateHistory.push(historyEntry);
543
+ const executionTime = Date.now() - startTime;
544
+ return {
545
+ success: true,
546
+ data: {
547
+ providers,
548
+ results: syncResults,
549
+ summary: {
550
+ total: providers.length,
551
+ successful: Object.values(syncResults).filter((r) => r.success).length,
552
+ failed: Object.values(syncResults).filter((r) => !r.success).length
553
+ }
554
+ },
555
+ metadata: {
556
+ operation: params.action,
557
+ timestamp: new Date().toISOString(),
558
+ executionTime
559
+ }
560
+ };
561
+ }
562
+ catch (error) {
563
+ return operation_error_handler_1.OperationErrorHandler.createToolError('SYNC_ERROR', `Failed to sync providers: ${error instanceof Error ? error.message : 'Unknown error'}`, params.action, { providers: params.providers }, ['Check provider configuration and network connectivity']);
564
+ }
565
+ }
566
+ async handleStatus(params, startTime) {
567
+ try {
568
+ this.logger.info('Getting project status');
569
+ // Get local status
570
+ const branchResult = await this.gitExecutor.executeGitCommand('branch', ['--show-current'], params.projectPath);
571
+ const lastCommitResult = await this.gitExecutor.executeGitCommand('log', ['-1', '--format=%H|%an|%ad|%s', '--date=iso'], params.projectPath);
572
+ const statusResult = await this.gitExecutor.executeGitCommand('status', ['--porcelain'], params.projectPath);
573
+ const localStatus = {
574
+ branch: branchResult.success ? branchResult.stdout.trim() : 'unknown',
575
+ lastCommit: lastCommitResult.success ? lastCommitResult.stdout.split('|')[0].substring(0, 8) : 'unknown',
576
+ uncommittedChanges: statusResult.success ? statusResult.stdout.split('\n').filter(line => line.trim()).length : 0,
577
+ untrackedFiles: statusResult.success ? statusResult.stdout.split('\n').filter(line => line.startsWith('??')).length : 0
578
+ };
579
+ // Get provider status
580
+ const providerStatus = {};
581
+ if (this.providerHandler) {
582
+ const providers = ['github', 'gitea'];
583
+ for (const provider of providers) {
584
+ try {
585
+ const statusResult = await this.providerHandler.executeOperation({
586
+ provider: provider,
587
+ operation: 'status',
588
+ parameters: {
589
+ projectPath: params.projectPath
590
+ },
591
+ requiresAuth: true,
592
+ isRemoteOperation: true
593
+ });
594
+ providerStatus[provider] = {
595
+ lastSync: new Date().toISOString(),
596
+ ahead: 0,
597
+ behind: 0,
598
+ conflicts: false,
599
+ ...statusResult
600
+ };
601
+ }
602
+ catch (error) {
603
+ providerStatus[provider] = {
604
+ lastSync: 'never',
605
+ ahead: 0,
606
+ behind: 0,
607
+ conflicts: false,
608
+ error: error instanceof Error ? error.message : 'Unknown error'
609
+ };
610
+ }
611
+ }
612
+ }
613
+ const projectStatus = {
614
+ localStatus,
615
+ providerStatus,
616
+ updateHistory: this.updateHistory.slice(-10) // Last 10 entries
617
+ };
618
+ const executionTime = Date.now() - startTime;
619
+ return {
620
+ success: true,
621
+ data: projectStatus,
622
+ metadata: {
623
+ operation: params.action,
624
+ timestamp: new Date().toISOString(),
625
+ executionTime
626
+ }
627
+ };
628
+ }
629
+ catch (error) {
630
+ return operation_error_handler_1.OperationErrorHandler.createToolError('STATUS_ERROR', `Failed to get status: ${error instanceof Error ? error.message : 'Unknown error'}`, params.action, {}, ['Check repository status and provider configuration']);
631
+ }
632
+ }
633
+ async handleRollback(params, startTime) {
634
+ try {
635
+ const rollbackTo = params.rollbackTo;
636
+ const rollbackType = params.rollbackType || 'commit';
637
+ if (!rollbackTo) {
638
+ throw new Error('rollbackTo parameter is required');
639
+ }
640
+ this.logger.info(`Rolling back to ${rollbackType}: ${rollbackTo}`);
641
+ let targetRef;
642
+ switch (rollbackType) {
643
+ case 'tag':
644
+ targetRef = rollbackTo;
645
+ break;
646
+ case 'version':
647
+ targetRef = `v${rollbackTo}`;
648
+ break;
649
+ case 'commit':
650
+ default:
651
+ targetRef = rollbackTo;
652
+ break;
653
+ }
654
+ // Perform rollback
655
+ const resetResult = await this.gitExecutor.executeGitCommand('reset', ['--hard', targetRef], params.projectPath);
656
+ if (!resetResult.success) {
657
+ throw new Error(`Failed to reset to ${targetRef}`);
658
+ }
659
+ const historyEntry = {
660
+ timestamp: new Date().toISOString(),
661
+ action: 'rollback',
662
+ details: `Rolled back to ${rollbackType}: ${rollbackTo}`,
663
+ files: [],
664
+ commitHash: targetRef
665
+ };
666
+ this.updateHistory.push(historyEntry);
667
+ const executionTime = Date.now() - startTime;
668
+ return {
669
+ success: true,
670
+ data: {
671
+ rollbackTo,
672
+ rollbackType,
673
+ targetRef,
674
+ resetResult: resetResult.stdout
675
+ },
676
+ metadata: {
677
+ operation: params.action,
678
+ timestamp: new Date().toISOString(),
679
+ executionTime
680
+ }
681
+ };
682
+ }
683
+ catch (error) {
684
+ return operation_error_handler_1.OperationErrorHandler.createToolError('ROLLBACK_ERROR', `Failed to rollback: ${error instanceof Error ? error.message : 'Unknown error'}`, params.action, { rollbackTo: params.rollbackTo, rollbackType: params.rollbackType }, ['Check target reference and repository status']);
685
+ }
686
+ }
687
+ async handleCompare(params, startTime) {
688
+ try {
689
+ const compareWith = params.compareWith;
690
+ const compareType = params.compareType || 'commit';
691
+ if (!compareWith) {
692
+ throw new Error('compareWith parameter is required');
693
+ }
694
+ this.logger.info(`Comparing with ${compareType}: ${compareWith}`);
695
+ // Get diff
696
+ const diffResult = await this.gitExecutor.executeGitCommand('diff', [compareWith, 'HEAD'], params.projectPath);
697
+ if (!diffResult.success) {
698
+ throw new Error('Failed to get diff');
699
+ }
700
+ // Get commit comparison
701
+ const logResult = await this.gitExecutor.executeGitCommand('log', [
702
+ `${compareWith}..HEAD`,
703
+ '--pretty=format:%H|%an|%ad|%s',
704
+ '--date=iso'
705
+ ], params.projectPath);
706
+ const commits = logResult.stdout
707
+ .split('\n')
708
+ .filter(line => line.trim())
709
+ .map(line => {
710
+ const [hash, author, date, message] = line.split('|');
711
+ return {
712
+ hash: hash.substring(0, 8),
713
+ fullHash: hash,
714
+ author,
715
+ date,
716
+ message
717
+ };
718
+ });
719
+ // Get file changes
720
+ const filesResult = await this.gitExecutor.executeGitCommand('diff', [
721
+ '--name-only',
722
+ compareWith,
723
+ 'HEAD'
724
+ ], params.projectPath);
725
+ const changedFiles = filesResult.stdout
726
+ .split('\n')
727
+ .filter(line => line.trim());
728
+ const executionTime = Date.now() - startTime;
729
+ return {
730
+ success: true,
731
+ data: {
732
+ compareWith,
733
+ compareType,
734
+ commits,
735
+ changedFiles,
736
+ diffStats: this.parseDiffStats(diffResult.stdout),
737
+ summary: {
738
+ commitsAhead: commits.length,
739
+ filesChanged: changedFiles.length
740
+ }
741
+ },
742
+ metadata: {
743
+ operation: params.action,
744
+ timestamp: new Date().toISOString(),
745
+ executionTime
746
+ }
747
+ };
748
+ }
749
+ catch (error) {
750
+ return operation_error_handler_1.OperationErrorHandler.createToolError('COMPARE_ERROR', `Failed to compare: ${error instanceof Error ? error.message : 'Unknown error'}`, params.action, { compareWith: params.compareWith, compareType: params.compareType }, ['Check target reference and repository status']);
751
+ }
752
+ }
753
+ // Helper methods
754
+ async updateDependencies(projectPath) {
755
+ // Update package.json dependencies
756
+ const packageJsonPath = `${projectPath}/package.json`;
757
+ const fs = require('fs');
758
+ if (fs.existsSync(packageJsonPath)) {
759
+ // This is a placeholder - in a real implementation, you would:
760
+ // 1. Parse package.json
761
+ // 2. Check for outdated dependencies
762
+ // 3. Update to latest versions
763
+ // 4. Run npm update or similar
764
+ this.logger.info('Updating dependencies');
765
+ }
766
+ }
767
+ async updateCode(projectPath) {
768
+ // Update code files based on patterns or rules
769
+ this.logger.info('Updating code files');
770
+ }
771
+ async updateDocs(projectPath) {
772
+ // Update documentation files
773
+ this.logger.info('Updating documentation');
774
+ }
775
+ async updateConfig(projectPath) {
776
+ // Update configuration files
777
+ this.logger.info('Updating configuration');
778
+ }
779
+ async updateAll(projectPath) {
780
+ await this.updateDependencies(projectPath);
781
+ await this.updateCode(projectPath);
782
+ await this.updateDocs(projectPath);
783
+ await this.updateConfig(projectPath);
784
+ }
785
+ async getCurrentCommitHash(projectPath) {
786
+ const result = await this.gitExecutor.executeGitCommand('rev-parse', ['HEAD'], projectPath);
787
+ return result.success ? result.stdout.trim() : '';
788
+ }
789
+ async getCurrentVersion(projectPath) {
790
+ const packageJsonPath = `${projectPath}/package.json`;
791
+ const fs = require('fs');
792
+ if (fs.existsSync(packageJsonPath)) {
793
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
794
+ return packageJson.version || '1.0.0';
795
+ }
796
+ return '1.0.0';
797
+ }
798
+ async getLastTag(projectPath) {
799
+ const result = await this.gitExecutor.executeGitCommand('describe', ['--tags', '--abbrev=0'], projectPath);
800
+ return result.success ? result.stdout.trim() : null;
801
+ }
802
+ async getChangedFiles(sinceRef, projectPath) {
803
+ const result = await this.gitExecutor.executeGitCommand('diff', [
804
+ '--name-only',
805
+ sinceRef,
806
+ 'HEAD'
807
+ ], projectPath);
808
+ return result.success
809
+ ? result.stdout.split('\n').filter(line => line.trim())
810
+ : [];
811
+ }
812
+ parseCommitType(message) {
813
+ const match = message.match(/^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\(.+\))?:/);
814
+ return match ? match[1] : 'other';
815
+ }
816
+ determineReleaseType(commits) {
817
+ if (commits.some(c => c.type === 'breaking' || c.message.includes('BREAKING CHANGE'))) {
818
+ return 'breaking';
819
+ }
820
+ if (commits.some(c => c.type === 'feat')) {
821
+ return 'feature';
822
+ }
823
+ if (commits.some(c => c.type === 'fix')) {
824
+ return 'fix';
825
+ }
826
+ return 'chore';
827
+ }
828
+ generateReleaseDescription(commits) {
829
+ const types = commits.reduce((acc, commit) => {
830
+ acc[commit.type] = (acc[commit.type] || 0) + 1;
831
+ return acc;
832
+ }, {});
833
+ const descriptions = [];
834
+ if (types.feat)
835
+ descriptions.push(`${types.feat} new feature${types.feat > 1 ? 's' : ''}`);
836
+ if (types.fix)
837
+ descriptions.push(`${types.fix} bug fix${types.fix > 1 ? 'es' : ''}`);
838
+ if (types.docs)
839
+ descriptions.push(`${types.docs} documentation update${types.docs > 1 ? 's' : ''}`);
840
+ if (types.refactor)
841
+ descriptions.push(`${types.refactor} refactoring${types.refactor > 1 ? 's' : ''}`);
842
+ return descriptions.length > 0 ? descriptions.join(', ') : 'Various improvements and fixes';
843
+ }
844
+ generateChangelogContent(entry, groupByType) {
845
+ const date = entry.date;
846
+ const version = entry.version;
847
+ const description = entry.description;
848
+ let content = `## [${version}] - ${date}\n\n`;
849
+ content += `### ${this.capitalizeFirst(entry.type)}\n`;
850
+ content += `${description}\n\n`;
851
+ if (entry.commits.length > 0) {
852
+ content += `#### Commits\n`;
853
+ entry.commits.forEach(commit => {
854
+ content += `- ${commit}\n`;
855
+ });
856
+ content += '\n';
857
+ }
858
+ if (entry.files.length > 0) {
859
+ content += `#### Files Changed\n`;
860
+ entry.files.forEach(file => {
861
+ content += `- ${file}\n`;
862
+ });
863
+ content += '\n';
864
+ }
865
+ return content;
866
+ }
867
+ mergeChangelogContent(newContent, existingContent) {
868
+ if (!existingContent) {
869
+ return `# Changelog\n\nAll notable changes to this project will be documented in this file.\n\n${newContent}`;
870
+ }
871
+ const lines = existingContent.split('\n');
872
+ const insertIndex = lines.findIndex(line => line.startsWith('## ['));
873
+ if (insertIndex === -1) {
874
+ return `${existingContent}\n\n${newContent}`;
875
+ }
876
+ lines.splice(insertIndex, 0, newContent);
877
+ return lines.join('\n');
878
+ }
879
+ async trackFile(filePath, projectPath) {
880
+ const result = await this.gitExecutor.executeGitCommand('status', ['--porcelain', filePath], projectPath);
881
+ return {
882
+ file: filePath,
883
+ status: result.success ? result.stdout.trim() : 'unknown',
884
+ exists: require('fs').existsSync(`${projectPath}/${filePath}`)
885
+ };
886
+ }
887
+ async trackPattern(pattern, projectPath) {
888
+ const result = await this.gitExecutor.executeGitCommand('ls-files', [pattern], projectPath);
889
+ return result.success
890
+ ? result.stdout.split('\n').filter(line => line.trim())
891
+ : [];
892
+ }
893
+ async trackAllChanges(projectPath) {
894
+ const result = await this.gitExecutor.executeGitCommand('status', ['--porcelain'], projectPath);
895
+ return result.success
896
+ ? result.stdout.split('\n').filter(line => line.trim()).map(line => line.substring(3).trim())
897
+ : [];
898
+ }
899
+ formatHistoryMarkdown(commits, history) {
900
+ let content = '# Update History\n\n';
901
+ content += '## Recent Commits\n\n';
902
+ commits.slice(0, 10).forEach(commit => {
903
+ content += `- **${commit.hash}** - ${commit.message} (${commit.author}, ${commit.date})\n`;
904
+ });
905
+ content += '\n## Update Operations\n\n';
906
+ history.slice(-10).forEach(entry => {
907
+ content += `- **${entry.timestamp}** - ${entry.action}: ${entry.details}\n`;
908
+ });
909
+ return content;
910
+ }
911
+ formatHistoryText(commits, history) {
912
+ let content = 'UPDATE HISTORY\n';
913
+ content += '==============\n\n';
914
+ content += 'RECENT COMMITS:\n';
915
+ commits.slice(0, 10).forEach(commit => {
916
+ content += `${commit.hash} - ${commit.message} (${commit.author}, ${commit.date})\n`;
917
+ });
918
+ content += '\nUPDATE OPERATIONS:\n';
919
+ history.slice(-10).forEach(entry => {
920
+ content += `${entry.timestamp} - ${entry.action}: ${entry.details}\n`;
921
+ });
922
+ return content;
923
+ }
924
+ parseDiffStats(diffOutput) {
925
+ const lines = diffOutput.split('\n');
926
+ let insertions = 0;
927
+ let deletions = 0;
928
+ let files = 0;
929
+ lines.forEach(line => {
930
+ if (line.startsWith('+++') || line.startsWith('---')) {
931
+ if (line.includes('b/'))
932
+ files++;
933
+ }
934
+ else if (line.startsWith('+') && !line.startsWith('+++')) {
935
+ insertions++;
936
+ }
937
+ else if (line.startsWith('-') && !line.startsWith('---')) {
938
+ deletions++;
939
+ }
940
+ });
941
+ return { insertions, deletions, files };
942
+ }
943
+ capitalizeFirst(str) {
944
+ return str.charAt(0).toUpperCase() + str.slice(1);
945
+ }
946
+ }
947
+ exports.GitUpdateTool = GitUpdateTool;
948
+ //# sourceMappingURL=git-update.js.map