@andrebuzeli/git-mcp 5.5.2 → 5.8.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/README.md +69 -6
- package/dist/index.js +65 -9
- package/dist/resources/toolsGuide.d.ts +12 -0
- package/dist/resources/toolsGuide.js +1491 -0
- package/dist/server.d.ts +2 -1
- package/dist/server.js +33 -0
- package/dist/tools/gitBranches.d.ts +2 -2
- package/dist/tools/gitBranches.js +2 -1
- package/dist/tools/gitFiles.d.ts +6 -0
- package/dist/tools/gitFiles.js +10 -8
- package/dist/tools/gitHistory.d.ts +17 -0
- package/dist/tools/gitHistory.js +365 -0
- package/dist/tools/gitIssues.d.ts +1 -16
- package/dist/tools/gitIssues.js +208 -122
- package/dist/tools/gitPulls.d.ts +1 -69
- package/dist/tools/gitPulls.js +225 -127
- package/dist/tools/gitRelease.d.ts +1 -29
- package/dist/tools/gitRelease.js +277 -55
- package/dist/tools/gitUpdate.d.ts +14 -0
- package/dist/tools/gitUpdate.js +374 -0
- package/dist/tools/gitUpload.d.ts +11 -0
- package/dist/tools/gitUpload.js +342 -0
- package/dist/tools/gitWorkflow.d.ts +1 -64
- package/dist/tools/gitWorkflow.js +224 -50
- package/dist/types.d.ts +7 -0
- package/package.json +2 -2
package/dist/server.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ProviderManager } from './providers/providerManager';
|
|
2
|
-
import { Tool } from './types';
|
|
2
|
+
import { Tool, Resource } from './types';
|
|
3
3
|
export declare function createServer(opts: {
|
|
4
4
|
tools: Tool[];
|
|
5
5
|
providerManager: ProviderManager;
|
|
6
|
+
resources?: Resource[];
|
|
6
7
|
}): import("express-serve-static-core").Express;
|
package/dist/server.js
CHANGED
|
@@ -13,6 +13,39 @@ function createServer(opts) {
|
|
|
13
13
|
const toolRegistry = new Map();
|
|
14
14
|
for (const t of opts.tools)
|
|
15
15
|
toolRegistry.set(t.name, t);
|
|
16
|
+
const resourceRegistry = new Map();
|
|
17
|
+
if (opts.resources) {
|
|
18
|
+
for (const r of opts.resources)
|
|
19
|
+
resourceRegistry.set(r.uri, r);
|
|
20
|
+
}
|
|
21
|
+
// List all available resources
|
|
22
|
+
app.get('/resources', (req, res) => {
|
|
23
|
+
const resources = Array.from(resourceRegistry.values()).map(r => ({
|
|
24
|
+
uri: r.uri,
|
|
25
|
+
name: r.name,
|
|
26
|
+
description: r.description,
|
|
27
|
+
mimeType: r.mimeType
|
|
28
|
+
}));
|
|
29
|
+
res.json({ success: true, resources });
|
|
30
|
+
});
|
|
31
|
+
// Get specific resource
|
|
32
|
+
app.get('/resources/:uri(*)', (req, res) => {
|
|
33
|
+
const uri = req.params.uri;
|
|
34
|
+
const resource = resourceRegistry.get(uri);
|
|
35
|
+
if (!resource) {
|
|
36
|
+
return res.status(404).json((0, errors_1.createErrorResponse)('RESOURCE_NOT_FOUND', `Resource not found: ${uri}`));
|
|
37
|
+
}
|
|
38
|
+
res.json({
|
|
39
|
+
success: true,
|
|
40
|
+
resource: {
|
|
41
|
+
uri: resource.uri,
|
|
42
|
+
name: resource.name,
|
|
43
|
+
description: resource.description,
|
|
44
|
+
mimeType: resource.mimeType,
|
|
45
|
+
content: resource.content
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
});
|
|
16
49
|
app.post('/call', async (req, res) => {
|
|
17
50
|
const { tool, params } = req.body ?? {};
|
|
18
51
|
if (!tool)
|
|
@@ -25,9 +25,9 @@ export declare class GitBranchesTool implements Tool {
|
|
|
25
25
|
commits?: undefined;
|
|
26
26
|
diff?: undefined;
|
|
27
27
|
} | {
|
|
28
|
+
success: boolean;
|
|
28
29
|
branch: string;
|
|
29
30
|
current: boolean;
|
|
30
|
-
success?: undefined;
|
|
31
31
|
branches?: undefined;
|
|
32
32
|
deleted?: undefined;
|
|
33
33
|
result?: undefined;
|
|
@@ -58,11 +58,11 @@ export declare class GitBranchesTool implements Tool {
|
|
|
58
58
|
commits?: undefined;
|
|
59
59
|
diff?: undefined;
|
|
60
60
|
} | {
|
|
61
|
+
success: boolean;
|
|
61
62
|
baseBranch: any;
|
|
62
63
|
compareBranch: any;
|
|
63
64
|
commits: readonly (import("simple-git").DefaultLogFields & import("simple-git").ListLogLine)[];
|
|
64
65
|
diff: string;
|
|
65
|
-
success?: undefined;
|
|
66
66
|
branch?: undefined;
|
|
67
67
|
branches?: undefined;
|
|
68
68
|
current?: undefined;
|
|
@@ -53,7 +53,7 @@ class GitBranchesTool {
|
|
|
53
53
|
'Use git branch -a to see all available branches',
|
|
54
54
|
]);
|
|
55
55
|
}
|
|
56
|
-
return { branch, current: branches.current === branchName };
|
|
56
|
+
return { success: true, branch, current: branches.current === branchName };
|
|
57
57
|
}
|
|
58
58
|
case 'delete': {
|
|
59
59
|
(0, safetyController_1.requireConfirmationIfDestructive)('delete', params);
|
|
@@ -83,6 +83,7 @@ class GitBranchesTool {
|
|
|
83
83
|
const diff = await git.diff([`${baseBranch}...${compareBranch}`]);
|
|
84
84
|
const log = await git.log({ from: baseBranch, to: compareBranch });
|
|
85
85
|
return {
|
|
86
|
+
success: true,
|
|
86
87
|
baseBranch,
|
|
87
88
|
compareBranch,
|
|
88
89
|
commits: log.all,
|
package/dist/tools/gitFiles.d.ts
CHANGED
|
@@ -3,20 +3,26 @@ export declare class GitFilesTool implements Tool {
|
|
|
3
3
|
name: string;
|
|
4
4
|
description: string;
|
|
5
5
|
handle(params: Record<string, any>): Promise<{
|
|
6
|
+
success: boolean;
|
|
6
7
|
path: string;
|
|
7
8
|
content: string;
|
|
8
9
|
files?: undefined;
|
|
9
10
|
matches?: undefined;
|
|
11
|
+
count?: undefined;
|
|
10
12
|
} | {
|
|
13
|
+
success: boolean;
|
|
11
14
|
files: string[];
|
|
12
15
|
path?: undefined;
|
|
13
16
|
content?: undefined;
|
|
14
17
|
matches?: undefined;
|
|
18
|
+
count?: undefined;
|
|
15
19
|
} | {
|
|
20
|
+
success: boolean;
|
|
16
21
|
matches: {
|
|
17
22
|
file: string;
|
|
18
23
|
snippet: string;
|
|
19
24
|
}[];
|
|
25
|
+
count: number;
|
|
20
26
|
path?: undefined;
|
|
21
27
|
content?: undefined;
|
|
22
28
|
files?: undefined;
|
package/dist/tools/gitFiles.js
CHANGED
|
@@ -20,11 +20,12 @@ class GitFilesTool {
|
|
|
20
20
|
const fullPath = path_1.default.resolve(projectPath, params.filePath ?? '');
|
|
21
21
|
if (action === 'read') {
|
|
22
22
|
const content = await promises_1.default.readFile(fullPath, 'utf8');
|
|
23
|
-
return { path: fullPath, content };
|
|
23
|
+
return { success: true, path: fullPath, content };
|
|
24
24
|
}
|
|
25
25
|
if (action === 'list') {
|
|
26
|
-
const
|
|
27
|
-
|
|
26
|
+
const dirPath = params.directoryPath ? path_1.default.join(projectPath, params.directoryPath) : projectPath;
|
|
27
|
+
const files = await promises_1.default.readdir(dirPath);
|
|
28
|
+
return { success: true, files };
|
|
28
29
|
}
|
|
29
30
|
// Blocked operations
|
|
30
31
|
if (['create', 'update', 'delete'].includes(action)) {
|
|
@@ -32,20 +33,21 @@ class GitFilesTool {
|
|
|
32
33
|
}
|
|
33
34
|
if (action === 'search') {
|
|
34
35
|
// Simple string search within files in projectPath (non-recursive)
|
|
35
|
-
const
|
|
36
|
-
const
|
|
36
|
+
const searchPath = params.searchPath ? path_1.default.join(projectPath, params.searchPath) : projectPath;
|
|
37
|
+
const files = await promises_1.default.readdir(searchPath);
|
|
38
|
+
const q = params.searchText ?? params.query ?? '';
|
|
37
39
|
const matches = [];
|
|
38
40
|
for (const f of files) {
|
|
39
41
|
try {
|
|
40
|
-
const txt = await promises_1.default.readFile(path_1.default.join(
|
|
42
|
+
const txt = await promises_1.default.readFile(path_1.default.join(searchPath, f), 'utf8');
|
|
41
43
|
if (txt.includes(q))
|
|
42
|
-
matches.push({ file: f, snippet: txt.
|
|
44
|
+
matches.push({ file: f, snippet: txt.substring(txt.indexOf(q), txt.indexOf(q) + 200) });
|
|
43
45
|
}
|
|
44
46
|
catch (e) {
|
|
45
47
|
// ignore binary/unreadable
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
|
-
return { matches };
|
|
50
|
+
return { success: true, matches, count: matches.length };
|
|
49
51
|
}
|
|
50
52
|
throw new errors_1.MCPError('VALIDATION_ERROR', `Unsupported action: ${action}`);
|
|
51
53
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Tool, MCPContext } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Git History Tool - Mantém histórico detalhado de TODAS alterações
|
|
4
|
+
* Modo DUAL automático - cria issues de histórico em GitHub e Gitea
|
|
5
|
+
* Rastreabilidade completa de cada mudança local
|
|
6
|
+
*/
|
|
7
|
+
export declare class GitHistoryTool implements Tool {
|
|
8
|
+
name: string;
|
|
9
|
+
description: string;
|
|
10
|
+
handle(params: Record<string, any>, ctx: MCPContext): Promise<any>;
|
|
11
|
+
private trackChange;
|
|
12
|
+
private listLocalHistory;
|
|
13
|
+
private syncHistory;
|
|
14
|
+
private generateReport;
|
|
15
|
+
private saveLocalHistory;
|
|
16
|
+
private formatHistoryBody;
|
|
17
|
+
}
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.GitHistoryTool = void 0;
|
|
40
|
+
const simple_git_1 = __importDefault(require("simple-git"));
|
|
41
|
+
const errors_1 = require("../utils/errors");
|
|
42
|
+
const axios_1 = __importDefault(require("axios"));
|
|
43
|
+
const fs = __importStar(require("fs/promises"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
/**
|
|
46
|
+
* Git History Tool - Mantém histórico detalhado de TODAS alterações
|
|
47
|
+
* Modo DUAL automático - cria issues de histórico em GitHub e Gitea
|
|
48
|
+
* Rastreabilidade completa de cada mudança local
|
|
49
|
+
*/
|
|
50
|
+
class GitHistoryTool {
|
|
51
|
+
constructor() {
|
|
52
|
+
this.name = 'git-history';
|
|
53
|
+
this.description = 'Maintain detailed history of all project changes with remote tracking via issues - automatic dual-provider execution';
|
|
54
|
+
}
|
|
55
|
+
async handle(params, ctx) {
|
|
56
|
+
const projectPath = params.projectPath;
|
|
57
|
+
if (!projectPath) {
|
|
58
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'projectPath is required');
|
|
59
|
+
}
|
|
60
|
+
const action = params.action || 'track';
|
|
61
|
+
const git = (0, simple_git_1.default)({ baseDir: projectPath });
|
|
62
|
+
switch (action) {
|
|
63
|
+
case 'track': {
|
|
64
|
+
// Rastrear mudança específica e criar histórico remoto
|
|
65
|
+
return await this.trackChange(git, projectPath, params, ctx);
|
|
66
|
+
}
|
|
67
|
+
case 'list': {
|
|
68
|
+
// Listar histórico local
|
|
69
|
+
return await this.listLocalHistory(projectPath);
|
|
70
|
+
}
|
|
71
|
+
case 'sync': {
|
|
72
|
+
// Sincronizar histórico local com remoto
|
|
73
|
+
return await this.syncHistory(git, projectPath, params, ctx);
|
|
74
|
+
}
|
|
75
|
+
case 'report': {
|
|
76
|
+
// Gerar relatório consolidado
|
|
77
|
+
return await this.generateReport(projectPath, params, ctx);
|
|
78
|
+
}
|
|
79
|
+
default:
|
|
80
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', `Unsupported action: ${action}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async trackChange(git, projectPath, params, ctx) {
|
|
84
|
+
const results = {
|
|
85
|
+
success: true,
|
|
86
|
+
timestamp: new Date().toISOString(),
|
|
87
|
+
projectPath,
|
|
88
|
+
action: 'track',
|
|
89
|
+
providers: {},
|
|
90
|
+
traceability: {
|
|
91
|
+
changeDetails: {},
|
|
92
|
+
localHistory: null,
|
|
93
|
+
remoteHistory: {},
|
|
94
|
+
errors: [],
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
try {
|
|
98
|
+
// 1. Coletar detalhes da mudança
|
|
99
|
+
const status = await git.status();
|
|
100
|
+
const log = await git.log({ maxCount: 1 });
|
|
101
|
+
const lastCommit = log.latest;
|
|
102
|
+
results.traceability.changeDetails = {
|
|
103
|
+
branch: status.current,
|
|
104
|
+
lastCommit: lastCommit ? {
|
|
105
|
+
hash: lastCommit.hash,
|
|
106
|
+
date: lastCommit.date,
|
|
107
|
+
message: lastCommit.message,
|
|
108
|
+
author: lastCommit.author_name,
|
|
109
|
+
email: lastCommit.author_email,
|
|
110
|
+
} : null,
|
|
111
|
+
currentStatus: {
|
|
112
|
+
modified: status.modified,
|
|
113
|
+
created: status.created,
|
|
114
|
+
deleted: status.deleted,
|
|
115
|
+
staged: status.staged,
|
|
116
|
+
conflicted: status.conflicted,
|
|
117
|
+
},
|
|
118
|
+
description: params.description || params.message || 'Change tracked via git-history',
|
|
119
|
+
tags: params.tags || [],
|
|
120
|
+
category: params.category || 'general',
|
|
121
|
+
};
|
|
122
|
+
// 2. Salvar no histórico local
|
|
123
|
+
const localHistoryFile = await this.saveLocalHistory(projectPath, results.traceability.changeDetails);
|
|
124
|
+
results.traceability.localHistory = localHistoryFile;
|
|
125
|
+
// 3. Criar issue de histórico em ambos providers
|
|
126
|
+
const githubOwner = params.owner || process.env.GITHUB_USERNAME;
|
|
127
|
+
const giteaOwner = params.owner || process.env.GITEA_USERNAME;
|
|
128
|
+
const repo = params.repo || path.basename(projectPath);
|
|
129
|
+
const historyTitle = params.title || `[${results.traceability.changeDetails.category}] ${results.traceability.changeDetails.description}`;
|
|
130
|
+
const historyBody = this.formatHistoryBody(results.traceability.changeDetails, params);
|
|
131
|
+
// GITHUB
|
|
132
|
+
if (ctx.providerManager.github) {
|
|
133
|
+
try {
|
|
134
|
+
const issue = await ctx.providerManager.github.rest.issues.create({
|
|
135
|
+
owner: githubOwner,
|
|
136
|
+
repo,
|
|
137
|
+
title: historyTitle,
|
|
138
|
+
body: historyBody,
|
|
139
|
+
labels: ['git-history', results.traceability.changeDetails.category, ...results.traceability.changeDetails.tags],
|
|
140
|
+
});
|
|
141
|
+
results.providers.github = {
|
|
142
|
+
success: true,
|
|
143
|
+
issue: {
|
|
144
|
+
number: issue.data.number,
|
|
145
|
+
url: issue.data.html_url,
|
|
146
|
+
id: issue.data.id,
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
results.providers.github = {
|
|
152
|
+
success: false,
|
|
153
|
+
error: err.message,
|
|
154
|
+
};
|
|
155
|
+
results.traceability.errors.push({
|
|
156
|
+
provider: 'github',
|
|
157
|
+
error: err.message,
|
|
158
|
+
timestamp: new Date().toISOString(),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// GITEA
|
|
163
|
+
if (ctx.providerManager.giteaBaseUrl) {
|
|
164
|
+
try {
|
|
165
|
+
const issue = await axios_1.default.post(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/issues`, {
|
|
166
|
+
title: historyTitle,
|
|
167
|
+
body: historyBody,
|
|
168
|
+
}, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
169
|
+
results.providers.gitea = {
|
|
170
|
+
success: true,
|
|
171
|
+
issue: {
|
|
172
|
+
number: issue.data.number,
|
|
173
|
+
url: issue.data.html_url,
|
|
174
|
+
id: issue.data.id,
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
results.providers.gitea = {
|
|
180
|
+
success: false,
|
|
181
|
+
error: err.message,
|
|
182
|
+
};
|
|
183
|
+
results.traceability.errors.push({
|
|
184
|
+
provider: 'gitea',
|
|
185
|
+
error: err.message,
|
|
186
|
+
timestamp: new Date().toISOString(),
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return results;
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
throw new errors_1.MCPError('TRACK_ERROR', `Failed to track change: ${err.message}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async listLocalHistory(projectPath) {
|
|
197
|
+
try {
|
|
198
|
+
const historyDir = path.join(projectPath, '.git-mcp-history');
|
|
199
|
+
try {
|
|
200
|
+
await fs.access(historyDir);
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return {
|
|
204
|
+
success: true,
|
|
205
|
+
history: [],
|
|
206
|
+
message: 'No history found',
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
const files = await fs.readdir(historyDir);
|
|
210
|
+
const historyFiles = files.filter(f => f.startsWith('history-') && f.endsWith('.json'));
|
|
211
|
+
const history = await Promise.all(historyFiles.map(async (file) => {
|
|
212
|
+
const content = await fs.readFile(path.join(historyDir, file), 'utf-8');
|
|
213
|
+
return JSON.parse(content);
|
|
214
|
+
}));
|
|
215
|
+
return {
|
|
216
|
+
success: true,
|
|
217
|
+
count: history.length,
|
|
218
|
+
history: history.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
throw new errors_1.MCPError('LIST_ERROR', `Failed to list history: ${err.message}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
async syncHistory(git, projectPath, params, ctx) {
|
|
226
|
+
const results = {
|
|
227
|
+
success: true,
|
|
228
|
+
timestamp: new Date().toISOString(),
|
|
229
|
+
action: 'sync',
|
|
230
|
+
synced: 0,
|
|
231
|
+
providers: { github: {}, gitea: {} },
|
|
232
|
+
errors: [],
|
|
233
|
+
};
|
|
234
|
+
try {
|
|
235
|
+
const localHistory = await this.listLocalHistory(projectPath);
|
|
236
|
+
const githubOwner = params.owner || process.env.GITHUB_USERNAME;
|
|
237
|
+
const giteaOwner = params.owner || process.env.GITEA_USERNAME;
|
|
238
|
+
const repo = params.repo || path.basename(projectPath);
|
|
239
|
+
for (const historyEntry of localHistory.history) {
|
|
240
|
+
if (!historyEntry.synced) {
|
|
241
|
+
try {
|
|
242
|
+
const title = `[history-sync] ${historyEntry.description}`;
|
|
243
|
+
const body = this.formatHistoryBody(historyEntry, params);
|
|
244
|
+
// Sync to GitHub
|
|
245
|
+
if (ctx.providerManager.github) {
|
|
246
|
+
try {
|
|
247
|
+
await ctx.providerManager.github.rest.issues.create({
|
|
248
|
+
owner: githubOwner,
|
|
249
|
+
repo,
|
|
250
|
+
title,
|
|
251
|
+
body,
|
|
252
|
+
labels: ['git-history', 'synced'],
|
|
253
|
+
});
|
|
254
|
+
results.synced++;
|
|
255
|
+
}
|
|
256
|
+
catch (err) {
|
|
257
|
+
results.errors.push({ provider: 'github', error: err.message });
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// Sync to Gitea
|
|
261
|
+
if (ctx.providerManager.giteaBaseUrl) {
|
|
262
|
+
try {
|
|
263
|
+
await axios_1.default.post(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/issues`, { title, body }, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
264
|
+
results.synced++;
|
|
265
|
+
}
|
|
266
|
+
catch (err) {
|
|
267
|
+
results.errors.push({ provider: 'gitea', error: err.message });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
results.errors.push({ entry: historyEntry.timestamp, error: err.message });
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return results;
|
|
277
|
+
}
|
|
278
|
+
catch (err) {
|
|
279
|
+
throw new errors_1.MCPError('SYNC_ERROR', `Failed to sync history: ${err.message}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async generateReport(projectPath, params, ctx) {
|
|
283
|
+
try {
|
|
284
|
+
const localHistory = await this.listLocalHistory(projectPath);
|
|
285
|
+
const report = {
|
|
286
|
+
success: true,
|
|
287
|
+
timestamp: new Date().toISOString(),
|
|
288
|
+
projectPath,
|
|
289
|
+
summary: {
|
|
290
|
+
totalChanges: localHistory.count,
|
|
291
|
+
byCategory: {},
|
|
292
|
+
byMonth: {},
|
|
293
|
+
recentChanges: localHistory.history.slice(0, 10),
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
// Agrupar por categoria
|
|
297
|
+
localHistory.history.forEach((entry) => {
|
|
298
|
+
const cat = entry.category || 'general';
|
|
299
|
+
report.summary.byCategory[cat] = (report.summary.byCategory[cat] || 0) + 1;
|
|
300
|
+
const month = new Date(entry.timestamp).toISOString().slice(0, 7);
|
|
301
|
+
report.summary.byMonth[month] = (report.summary.byMonth[month] || 0) + 1;
|
|
302
|
+
});
|
|
303
|
+
return report;
|
|
304
|
+
}
|
|
305
|
+
catch (err) {
|
|
306
|
+
throw new errors_1.MCPError('REPORT_ERROR', `Failed to generate report: ${err.message}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
async saveLocalHistory(projectPath, changeDetails) {
|
|
310
|
+
const historyDir = path.join(projectPath, '.git-mcp-history');
|
|
311
|
+
await fs.mkdir(historyDir, { recursive: true });
|
|
312
|
+
const historyFile = path.join(historyDir, `history-${Date.now()}.json`);
|
|
313
|
+
const historyEntry = {
|
|
314
|
+
timestamp: new Date().toISOString(),
|
|
315
|
+
...changeDetails,
|
|
316
|
+
synced: false,
|
|
317
|
+
};
|
|
318
|
+
await fs.writeFile(historyFile, JSON.stringify(historyEntry, null, 2), 'utf-8');
|
|
319
|
+
return historyFile;
|
|
320
|
+
}
|
|
321
|
+
formatHistoryBody(changeDetails, params) {
|
|
322
|
+
return `
|
|
323
|
+
## 📜 Git History Entry
|
|
324
|
+
|
|
325
|
+
**Timestamp:** ${changeDetails.timestamp || new Date().toISOString()}
|
|
326
|
+
**Category:** ${changeDetails.category || 'general'}
|
|
327
|
+
**Branch:** ${changeDetails.branch || 'N/A'}
|
|
328
|
+
|
|
329
|
+
### 📝 Description
|
|
330
|
+
${changeDetails.description || 'No description provided'}
|
|
331
|
+
|
|
332
|
+
### 🔧 Last Commit
|
|
333
|
+
${changeDetails.lastCommit ? `
|
|
334
|
+
- **Hash:** \`${changeDetails.lastCommit.hash}\`
|
|
335
|
+
- **Author:** ${changeDetails.lastCommit.author} (${changeDetails.lastCommit.email})
|
|
336
|
+
- **Date:** ${changeDetails.lastCommit.date}
|
|
337
|
+
- **Message:** ${changeDetails.lastCommit.message}
|
|
338
|
+
` : 'No recent commit'}
|
|
339
|
+
|
|
340
|
+
### 📊 Current Status
|
|
341
|
+
${changeDetails.currentStatus ? `
|
|
342
|
+
- **Modified:** ${changeDetails.currentStatus.modified?.length || 0} files
|
|
343
|
+
- **Created:** ${changeDetails.currentStatus.created?.length || 0} files
|
|
344
|
+
- **Deleted:** ${changeDetails.currentStatus.deleted?.length || 0} files
|
|
345
|
+
- **Staged:** ${changeDetails.currentStatus.staged?.length || 0} files
|
|
346
|
+
- **Conflicted:** ${changeDetails.currentStatus.conflicted?.length || 0} files
|
|
347
|
+
` : 'No status available'}
|
|
348
|
+
|
|
349
|
+
${changeDetails.currentStatus?.modified?.length > 0 ? `
|
|
350
|
+
### 📁 Modified Files
|
|
351
|
+
${changeDetails.currentStatus.modified.map((f) => `- ${f}`).join('\n')}
|
|
352
|
+
` : ''}
|
|
353
|
+
|
|
354
|
+
${changeDetails.tags?.length > 0 ? `
|
|
355
|
+
### 🏷️ Tags
|
|
356
|
+
${changeDetails.tags.map((t) => `\`${t}\``).join(', ')}
|
|
357
|
+
` : ''}
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
*Generated by git-mcp git-history tool*
|
|
361
|
+
*Tracked at: ${changeDetails.timestamp || new Date().toISOString()}*
|
|
362
|
+
`;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
exports.GitHistoryTool = GitHistoryTool;
|
|
@@ -2,20 +2,5 @@ import { Tool, MCPContext } from '../types';
|
|
|
2
2
|
export declare class GitIssuesTool implements Tool {
|
|
3
3
|
name: string;
|
|
4
4
|
description: string;
|
|
5
|
-
handle(params: Record<string, any>, ctx: MCPContext): Promise<
|
|
6
|
-
success: boolean;
|
|
7
|
-
issue: any;
|
|
8
|
-
issues?: undefined;
|
|
9
|
-
comment?: undefined;
|
|
10
|
-
} | {
|
|
11
|
-
success: boolean;
|
|
12
|
-
issues: any;
|
|
13
|
-
issue?: undefined;
|
|
14
|
-
comment?: undefined;
|
|
15
|
-
} | {
|
|
16
|
-
success: boolean;
|
|
17
|
-
comment: any;
|
|
18
|
-
issue?: undefined;
|
|
19
|
-
issues?: undefined;
|
|
20
|
-
}>;
|
|
5
|
+
handle(params: Record<string, any>, ctx: MCPContext): Promise<any>;
|
|
21
6
|
}
|