@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.
- package/dist/index.js +1 -1
- package/dist/server.d.ts +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +8 -2
- package/dist/server.js.map +1 -1
- package/dist/tools/git-release.js +1 -1
- package/dist/tools/git-release.js.map +1 -1
- package/dist/tools/git-update.d.ts +224 -0
- package/dist/tools/git-update.d.ts.map +1 -0
- package/dist/tools/git-update.js +948 -0
- package/dist/tools/git-update.js.map +1 -0
- package/dist/utils/parameter-validator.d.ts +1 -0
- package/dist/utils/parameter-validator.d.ts.map +1 -1
- package/dist/utils/parameter-validator.js +79 -2
- package/dist/utils/parameter-validator.js.map +1 -1
- package/package.json +1 -1
|
@@ -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
|