@andrebuzeli/git-mcp 10.0.6 → 10.0.8
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/LICENSE +0 -0
- package/README.md +1 -1
- package/dist/scripts/test_e2e.d.ts +1 -0
- package/dist/scripts/test_e2e.js +199 -0
- package/dist/scripts/test_exhaustive.d.ts +1 -0
- package/dist/scripts/test_exhaustive.js +275 -0
- package/dist/scripts/verify_setup.d.ts +1 -0
- package/dist/scripts/verify_setup.js +61 -0
- package/dist/tools/gitAnalytics.js +3 -3
- package/dist/tools/gitBackup.d.ts +2 -2
- package/dist/tools/gitBackup.js +5 -5
- package/dist/tools/gitBranches.js +40 -29
- package/dist/tools/gitConfig.d.ts +2 -2
- package/dist/tools/gitConfig.js +12 -13
- package/dist/tools/gitFix.d.ts +2 -1
- package/dist/tools/gitFix.js +20 -24
- package/dist/tools/gitFix.tool.d.ts +2 -2
- package/dist/tools/gitFix.tool.js +2 -2
- package/dist/tools/gitHistory.js +4 -5
- package/dist/tools/gitIssues.js +8 -8
- package/dist/tools/gitMonitor.js +28 -33
- package/dist/tools/gitPulls.js +8 -8
- package/dist/tools/gitRelease.js +5 -6
- package/dist/tools/gitRemote.d.ts +1 -28
- package/dist/tools/gitRemote.js +19 -23
- package/dist/tools/gitReset.js +6 -7
- package/dist/tools/gitStash.d.ts +1 -12
- package/dist/tools/gitStash.js +11 -22
- package/dist/tools/gitSync.js +44 -36
- package/dist/tools/gitTags.d.ts +8 -0
- package/dist/tools/gitTags.js +44 -34
- package/dist/tools/gitUpdate.d.ts +27 -0
- package/dist/tools/gitUpdate.js +61 -34
- package/dist/tools/gitUpload.js +29 -44
- package/dist/tools/gitWorkflow.js +27 -46
- package/package.json +2 -3
- package/dist/server-new.d.ts +0 -2
- package/dist/server-new.js +0 -224
- package/dist/tools/gitFiles-new.d.ts +0 -89
- package/dist/tools/gitFiles-new.js +0 -335
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import simpleGit from 'simple-git';
|
|
2
1
|
import { MCPError } from '../utils/errors.js';
|
|
3
2
|
import { requireConfirmationIfDestructive } from '../utils/safetyController.js';
|
|
4
3
|
import { getRepoInfo } from '../utils/repoHelpers.js';
|
|
@@ -62,7 +61,9 @@ export class GitBranchesTool {
|
|
|
62
61
|
if (!action || !projectPath) {
|
|
63
62
|
throw new MCPError('VALIDATION_ERROR', 'action and projectPath are required');
|
|
64
63
|
}
|
|
65
|
-
|
|
64
|
+
if (!ctx.gitAdapter) {
|
|
65
|
+
throw new MCPError('INTERNAL_ERROR', 'Git adapter not available');
|
|
66
|
+
}
|
|
66
67
|
switch (action) {
|
|
67
68
|
case 'create': {
|
|
68
69
|
const branchName = params.branchName;
|
|
@@ -70,34 +71,36 @@ export class GitBranchesTool {
|
|
|
70
71
|
throw new MCPError('VALIDATION_ERROR', 'branchName is required');
|
|
71
72
|
const sourceBranch = params.sourceBranch;
|
|
72
73
|
if (sourceBranch) {
|
|
73
|
-
await
|
|
74
|
+
await ctx.gitAdapter.checkout(projectPath, sourceBranch);
|
|
74
75
|
}
|
|
75
|
-
await
|
|
76
|
+
await ctx.gitAdapter.createBranch(projectPath, branchName);
|
|
76
77
|
if (params.checkout !== false) {
|
|
77
|
-
await
|
|
78
|
+
await ctx.gitAdapter.checkout(projectPath, branchName);
|
|
78
79
|
}
|
|
79
80
|
return { success: true, branch: branchName, local: true };
|
|
80
81
|
}
|
|
81
82
|
case 'list': {
|
|
82
|
-
const
|
|
83
|
+
const branches = await ctx.gitAdapter.listBranches(projectPath);
|
|
84
|
+
const current = await ctx.gitAdapter.getCurrentBranch(projectPath);
|
|
83
85
|
const results = {
|
|
84
86
|
success: true,
|
|
85
87
|
local: {
|
|
86
|
-
branches
|
|
87
|
-
current
|
|
88
|
+
branches,
|
|
89
|
+
current,
|
|
88
90
|
},
|
|
89
91
|
providers: {}
|
|
90
92
|
};
|
|
91
93
|
// Also query remote APIs
|
|
92
94
|
const repoInfo = getRepoInfo(projectPath);
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
+
const repo = params.repo || repoInfo.repoName;
|
|
96
|
+
const githubOwner = params.owner || process.env.GITHUB_USERNAME;
|
|
97
|
+
const giteaOwner = params.owner || process.env.GITEA_USERNAME;
|
|
95
98
|
// GitHub
|
|
96
99
|
if (ctx.providerManager.github && githubOwner) {
|
|
97
100
|
try {
|
|
98
101
|
const branches = await ctx.providerManager.github.rest.repos.listBranches({
|
|
99
102
|
owner: githubOwner,
|
|
100
|
-
repo:
|
|
103
|
+
repo: repo,
|
|
101
104
|
});
|
|
102
105
|
results.providers.github = {
|
|
103
106
|
success: true,
|
|
@@ -115,7 +118,7 @@ export class GitBranchesTool {
|
|
|
115
118
|
// Gitea
|
|
116
119
|
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
117
120
|
try {
|
|
118
|
-
const branches = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${
|
|
121
|
+
const branches = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/branches`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
119
122
|
results.providers.gitea = {
|
|
120
123
|
success: true,
|
|
121
124
|
branches: branches.data.map((b) => ({
|
|
@@ -135,23 +138,25 @@ export class GitBranchesTool {
|
|
|
135
138
|
const branchName = params.branchName;
|
|
136
139
|
if (!branchName)
|
|
137
140
|
throw new MCPError('VALIDATION_ERROR', 'branchName is required');
|
|
138
|
-
const branches = await
|
|
139
|
-
const
|
|
141
|
+
const branches = await ctx.gitAdapter.listBranches(projectPath);
|
|
142
|
+
const current = await ctx.gitAdapter.getCurrentBranch(projectPath);
|
|
143
|
+
const found = branches.includes(branchName);
|
|
140
144
|
const results = {
|
|
141
145
|
success: true,
|
|
142
|
-
local:
|
|
146
|
+
local: found ? { branch: branchName, current: current === branchName } : { found: false },
|
|
143
147
|
providers: {}
|
|
144
148
|
};
|
|
145
149
|
// Also query remote APIs
|
|
146
150
|
const repoInfo = getRepoInfo(projectPath);
|
|
147
|
-
const
|
|
148
|
-
const
|
|
151
|
+
const repo = params.repo || repoInfo.repoName;
|
|
152
|
+
const githubOwner = params.owner || process.env.GITHUB_USERNAME;
|
|
153
|
+
const giteaOwner = params.owner || process.env.GITEA_USERNAME;
|
|
149
154
|
// GitHub
|
|
150
155
|
if (ctx.providerManager.github && githubOwner) {
|
|
151
156
|
try {
|
|
152
157
|
const branchData = await ctx.providerManager.github.rest.repos.getBranch({
|
|
153
158
|
owner: githubOwner,
|
|
154
|
-
repo:
|
|
159
|
+
repo: repo,
|
|
155
160
|
branch: branchName,
|
|
156
161
|
});
|
|
157
162
|
results.providers.github = {
|
|
@@ -171,7 +176,7 @@ export class GitBranchesTool {
|
|
|
171
176
|
// Gitea
|
|
172
177
|
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
173
178
|
try {
|
|
174
|
-
const branchData = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${
|
|
179
|
+
const branchData = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/branches/${branchName}`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
175
180
|
results.providers.gitea = {
|
|
176
181
|
success: true,
|
|
177
182
|
branch: {
|
|
@@ -193,7 +198,7 @@ export class GitBranchesTool {
|
|
|
193
198
|
const branchName = params.branchName;
|
|
194
199
|
if (!branchName)
|
|
195
200
|
throw new MCPError('VALIDATION_ERROR', 'branchName is required');
|
|
196
|
-
await
|
|
201
|
+
await ctx.gitAdapter.deleteBranch(projectPath, branchName, params.force);
|
|
197
202
|
return { success: true, deleted: branchName, local: true };
|
|
198
203
|
}
|
|
199
204
|
case 'merge': {
|
|
@@ -202,9 +207,9 @@ export class GitBranchesTool {
|
|
|
202
207
|
if (!branchName)
|
|
203
208
|
throw new MCPError('VALIDATION_ERROR', 'branchName is required');
|
|
204
209
|
if (targetBranch) {
|
|
205
|
-
await
|
|
210
|
+
await ctx.gitAdapter.checkout(projectPath, targetBranch);
|
|
206
211
|
}
|
|
207
|
-
const result = await
|
|
212
|
+
const result = await ctx.gitAdapter.merge(projectPath, branchName);
|
|
208
213
|
return { success: true, result, local: true };
|
|
209
214
|
}
|
|
210
215
|
case 'compare': {
|
|
@@ -213,28 +218,34 @@ export class GitBranchesTool {
|
|
|
213
218
|
if (!baseBranch || !compareBranch) {
|
|
214
219
|
throw new MCPError('VALIDATION_ERROR', 'baseBranch and compareBranch are required');
|
|
215
220
|
}
|
|
216
|
-
|
|
217
|
-
|
|
221
|
+
// Basic comparison using log
|
|
222
|
+
// Note: This is a simplified comparison compared to git log base..compare
|
|
223
|
+
const baseLog = await ctx.gitAdapter.log(projectPath, { ref: baseBranch, maxCount: 20 });
|
|
224
|
+
const compareLog = await ctx.gitAdapter.log(projectPath, { ref: compareBranch, maxCount: 20 });
|
|
225
|
+
// Find commits in compare that are not in base
|
|
226
|
+
const baseHashes = new Set(baseLog.map(c => c.hash));
|
|
227
|
+
const uniqueCommits = compareLog.filter(c => !baseHashes.has(c.hash));
|
|
218
228
|
const results = {
|
|
219
229
|
success: true,
|
|
220
230
|
local: {
|
|
221
231
|
baseBranch,
|
|
222
232
|
compareBranch,
|
|
223
|
-
commits:
|
|
224
|
-
diff,
|
|
233
|
+
commits: uniqueCommits,
|
|
234
|
+
diff: "Diff not supported in adapter yet", // Placeholder
|
|
225
235
|
},
|
|
226
236
|
providers: {}
|
|
227
237
|
};
|
|
228
238
|
// Also compare on remote APIs
|
|
229
239
|
const repoInfo = getRepoInfo(projectPath);
|
|
230
|
-
const
|
|
231
|
-
const
|
|
240
|
+
const repo = params.repo || repoInfo.repoName;
|
|
241
|
+
const githubOwner = params.owner || process.env.GITHUB_USERNAME;
|
|
242
|
+
const giteaOwner = params.owner || process.env.GITEA_USERNAME;
|
|
232
243
|
// GitHub
|
|
233
244
|
if (ctx.providerManager.github && githubOwner) {
|
|
234
245
|
try {
|
|
235
246
|
const comparison = await ctx.providerManager.github.rest.repos.compareCommits({
|
|
236
247
|
owner: githubOwner,
|
|
237
|
-
repo:
|
|
248
|
+
repo: repo,
|
|
238
249
|
base: baseBranch,
|
|
239
250
|
head: compareBranch,
|
|
240
251
|
});
|
|
@@ -63,7 +63,7 @@ export declare class GitConfigTool implements Tool {
|
|
|
63
63
|
} | {
|
|
64
64
|
success: boolean;
|
|
65
65
|
scope: string;
|
|
66
|
-
configs:
|
|
66
|
+
configs: Record<string, string>;
|
|
67
67
|
key?: undefined;
|
|
68
68
|
value?: undefined;
|
|
69
69
|
message?: undefined;
|
|
@@ -74,7 +74,7 @@ export declare class GitConfigTool implements Tool {
|
|
|
74
74
|
} | {
|
|
75
75
|
success: boolean;
|
|
76
76
|
key: any;
|
|
77
|
-
value: string |
|
|
77
|
+
value: string | undefined;
|
|
78
78
|
scope: string | undefined;
|
|
79
79
|
message?: undefined;
|
|
80
80
|
configs?: undefined;
|
package/dist/tools/gitConfig.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import simpleGit from 'simple-git';
|
|
2
1
|
import { MCPError } from '../utils/errors.js';
|
|
3
2
|
import * as fs from 'fs/promises';
|
|
4
3
|
import * as path from 'path';
|
|
@@ -49,49 +48,49 @@ export class GitConfigTool {
|
|
|
49
48
|
if (!action || !projectPath) {
|
|
50
49
|
throw new MCPError('VALIDATION_ERROR', 'action and projectPath are required');
|
|
51
50
|
}
|
|
52
|
-
|
|
51
|
+
if (!ctx.gitAdapter) {
|
|
52
|
+
throw new MCPError('INTERNAL_ERROR', 'Git adapter not available');
|
|
53
|
+
}
|
|
53
54
|
const scope = params.global ? 'global' : params.system ? 'system' : 'local';
|
|
54
55
|
switch (action) {
|
|
55
56
|
case 'get': {
|
|
56
57
|
const key = params.key;
|
|
57
58
|
if (!key)
|
|
58
59
|
throw new MCPError('VALIDATION_ERROR', 'key is required');
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return { success: true, key, value: value.value };
|
|
62
|
-
}
|
|
63
|
-
catch (err) {
|
|
60
|
+
const value = await ctx.gitAdapter.getConfig(projectPath, key, scope);
|
|
61
|
+
if (value === undefined) {
|
|
64
62
|
throw new MCPError('CONFIG_NOT_FOUND', `Configuration key '${key}' not found`);
|
|
65
63
|
}
|
|
64
|
+
return { success: true, key, value };
|
|
66
65
|
}
|
|
67
66
|
case 'set': {
|
|
68
67
|
const key = params.key;
|
|
69
68
|
const value = params.value;
|
|
70
69
|
if (!key || value === undefined)
|
|
71
70
|
throw new MCPError('VALIDATION_ERROR', 'key and value are required');
|
|
72
|
-
await
|
|
71
|
+
await ctx.gitAdapter.setConfig(projectPath, key, value, scope);
|
|
73
72
|
return { success: true, key, value };
|
|
74
73
|
}
|
|
75
74
|
case 'unset': {
|
|
76
75
|
const key = params.key;
|
|
77
76
|
if (!key)
|
|
78
77
|
throw new MCPError('VALIDATION_ERROR', 'key is required');
|
|
79
|
-
await
|
|
78
|
+
await ctx.gitAdapter.unsetConfig(projectPath, key, scope);
|
|
80
79
|
return { success: true, key, message: 'Configuration removed' };
|
|
81
80
|
}
|
|
82
81
|
case 'list': {
|
|
83
|
-
const configs = await
|
|
84
|
-
return { success: true, scope, configs
|
|
82
|
+
const configs = await ctx.gitAdapter.listConfig(projectPath, scope);
|
|
83
|
+
return { success: true, scope, configs };
|
|
85
84
|
}
|
|
86
85
|
case 'show': {
|
|
87
86
|
const key = params.key;
|
|
88
87
|
if (!key)
|
|
89
88
|
throw new MCPError('VALIDATION_ERROR', 'key is required');
|
|
90
|
-
const value = await
|
|
89
|
+
const value = await ctx.gitAdapter.getConfig(projectPath, key, scope);
|
|
91
90
|
return {
|
|
92
91
|
success: true,
|
|
93
92
|
key,
|
|
94
|
-
value
|
|
93
|
+
value,
|
|
95
94
|
scope: params.showScope ? scope : undefined,
|
|
96
95
|
};
|
|
97
96
|
}
|
package/dist/tools/gitFix.d.ts
CHANGED
package/dist/tools/gitFix.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import simpleGit from 'simple-git';
|
|
2
1
|
import path from 'path';
|
|
3
2
|
import fs from 'fs/promises';
|
|
4
3
|
import { existsSync } from 'fs';
|
|
5
4
|
import { buildGitHubUrl, buildGiteaUrl } from '../utils/repoHelpers.js';
|
|
6
|
-
export async function handleGitFix(args) {
|
|
5
|
+
export async function handleGitFix(args, ctx) {
|
|
7
6
|
try {
|
|
8
7
|
const { projectPath, githubRepo, giteaRepo, autoDetect = true } = args;
|
|
9
8
|
if (!projectPath) {
|
|
@@ -13,7 +12,7 @@ export async function handleGitFix(args) {
|
|
|
13
12
|
if (!existsSync(absolutePath)) {
|
|
14
13
|
throw new Error(`Caminho não existe: ${absolutePath}`);
|
|
15
14
|
}
|
|
16
|
-
const git =
|
|
15
|
+
const git = ctx.gitAdapter;
|
|
17
16
|
const result = {
|
|
18
17
|
success: false,
|
|
19
18
|
projectPath: absolutePath,
|
|
@@ -28,24 +27,21 @@ export async function handleGitFix(args) {
|
|
|
28
27
|
// Verificar se é um repositório Git
|
|
29
28
|
let isGitRepo = false;
|
|
30
29
|
try {
|
|
31
|
-
await git.status();
|
|
30
|
+
await git.status(absolutePath);
|
|
32
31
|
isGitRepo = true;
|
|
33
32
|
result.fixed.push('✅ Repositório Git válido encontrado');
|
|
34
33
|
}
|
|
35
34
|
catch (err) {
|
|
36
|
-
if
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
else {
|
|
43
|
-
throw err;
|
|
44
|
-
}
|
|
35
|
+
// isomorphic-git throws if not a repo, but we should check the error message or type if possible
|
|
36
|
+
// For now, assume any error means it's not a repo or has issues
|
|
37
|
+
result.warnings.push('⚠️ Não é um repositório Git - inicializando...');
|
|
38
|
+
await git.init(absolutePath);
|
|
39
|
+
result.fixed.push('✅ Git inicializado');
|
|
40
|
+
isGitRepo = true;
|
|
45
41
|
}
|
|
46
42
|
// Capturar remotes antes
|
|
47
|
-
const remotesBefore = await git.
|
|
48
|
-
result.remotes.before = remotesBefore.map(r => ({ name: r.name, url: r.
|
|
43
|
+
const remotesBefore = await git.listRemotes(absolutePath);
|
|
44
|
+
result.remotes.before = remotesBefore.map(r => ({ name: r.name, url: r.url || '' }));
|
|
49
45
|
// Obter usernames das env vars (OBRIGATÓRIO usar as credenciais fornecidas)
|
|
50
46
|
const githubUsername = process.env.GITHUB_USERNAME;
|
|
51
47
|
const giteaUsername = process.env.GITEA_USERNAME;
|
|
@@ -58,7 +54,7 @@ export async function handleGitFix(args) {
|
|
|
58
54
|
let detectedRepoName = null;
|
|
59
55
|
if (autoDetect && remotesBefore.length > 0) {
|
|
60
56
|
for (const remote of remotesBefore) {
|
|
61
|
-
const url = remote.
|
|
57
|
+
const url = remote.url || '';
|
|
62
58
|
// Detectar APENAS o nome do repo (sem username)
|
|
63
59
|
if (url.includes('github.com') && !detectedRepoName) {
|
|
64
60
|
const match = url.match(/github\.com[:/][^/]+\/([^/.]+)/);
|
|
@@ -102,27 +98,27 @@ export async function handleGitFix(args) {
|
|
|
102
98
|
for (const remoteName of remotesToRemove) {
|
|
103
99
|
const exists = remotesBefore.find(r => r.name === remoteName);
|
|
104
100
|
if (exists) {
|
|
105
|
-
await git.removeRemote(remoteName);
|
|
101
|
+
await git.removeRemote(absolutePath, remoteName);
|
|
106
102
|
result.fixed.push(`🗑️ Removido remote antigo: ${remoteName}`);
|
|
107
103
|
}
|
|
108
104
|
}
|
|
109
105
|
// Adicionar novos remotes no padrão dual
|
|
110
106
|
const githubUrl = buildGitHubUrl(finalGithubRepo.split('/')[0], finalGithubRepo.split('/')[1]);
|
|
111
107
|
const giteaUrlFull = buildGiteaUrl(finalGiteaRepo.split('/')[0], finalGiteaRepo.split('/')[1]);
|
|
112
|
-
await git.addRemote('github', githubUrl);
|
|
108
|
+
await git.addRemote(absolutePath, 'github', githubUrl);
|
|
113
109
|
result.fixed.push(`✅ Adicionado remote GitHub: ${githubUrl.replace(/oauth2:[^@]+@/, 'oauth2:***@')}`);
|
|
114
|
-
await git.addRemote('gitea', giteaUrlFull);
|
|
110
|
+
await git.addRemote(absolutePath, 'gitea', giteaUrlFull);
|
|
115
111
|
result.fixed.push(`✅ Adicionado remote Gitea: ${giteaUrlFull.replace(/:[^:]+@/, ':***@')}`);
|
|
116
112
|
// Configurar origin como push múltiplo
|
|
117
|
-
await git.addRemote('origin', githubUrl);
|
|
118
|
-
await git.
|
|
113
|
+
await git.addRemote(absolutePath, 'origin', githubUrl);
|
|
114
|
+
await git.setConfig(absolutePath, 'remote.origin.pushurl', giteaUrlFull, 'local');
|
|
119
115
|
result.fixed.push(`✅ Configurado origin para push dual (GitHub + Gitea)`);
|
|
120
116
|
// Capturar remotes depois
|
|
121
|
-
const remotesAfter = await git.
|
|
122
|
-
result.remotes.after = remotesAfter.map(r => ({ name: r.name, url: r.
|
|
117
|
+
const remotesAfter = await git.listRemotes(absolutePath);
|
|
118
|
+
result.remotes.after = remotesAfter.map(r => ({ name: r.name, url: r.url || '' }));
|
|
123
119
|
// Verificar se há commits
|
|
124
120
|
try {
|
|
125
|
-
await git.log();
|
|
121
|
+
await git.log(absolutePath);
|
|
126
122
|
result.fixed.push('✅ Histórico de commits preservado');
|
|
127
123
|
}
|
|
128
124
|
catch (err) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Tool } from '../types.js';
|
|
1
|
+
import { Tool, MCPContext } from '../types.js';
|
|
2
2
|
export declare class GitFixTool implements Tool {
|
|
3
3
|
name: string;
|
|
4
4
|
description: string;
|
|
@@ -25,7 +25,7 @@ export declare class GitFixTool implements Tool {
|
|
|
25
25
|
required: string[];
|
|
26
26
|
additionalProperties: boolean;
|
|
27
27
|
};
|
|
28
|
-
handle(args: any): Promise<{
|
|
28
|
+
handle(args: any, ctx: MCPContext): Promise<{
|
|
29
29
|
content: any[];
|
|
30
30
|
}>;
|
|
31
31
|
}
|
package/dist/tools/gitHistory.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import simpleGit from 'simple-git';
|
|
2
1
|
import { MCPError } from '../utils/errors.js';
|
|
3
2
|
import axios from 'axios';
|
|
4
3
|
import * as fs from 'fs/promises';
|
|
@@ -44,7 +43,7 @@ export class GitHistoryTool {
|
|
|
44
43
|
throw new MCPError('VALIDATION_ERROR', 'projectPath is required');
|
|
45
44
|
}
|
|
46
45
|
const action = params.action || 'track';
|
|
47
|
-
const git =
|
|
46
|
+
const git = ctx.gitAdapter;
|
|
48
47
|
switch (action) {
|
|
49
48
|
case 'track': {
|
|
50
49
|
// Rastrear mudança específica e criar histórico remoto
|
|
@@ -82,9 +81,9 @@ export class GitHistoryTool {
|
|
|
82
81
|
};
|
|
83
82
|
try {
|
|
84
83
|
// 1. Coletar detalhes da mudança
|
|
85
|
-
const status = await git.status();
|
|
86
|
-
const log = await git.log({ maxCount: 1 });
|
|
87
|
-
const lastCommit = log.
|
|
84
|
+
const status = await git.status(projectPath);
|
|
85
|
+
const log = await git.log(projectPath, { maxCount: 1 });
|
|
86
|
+
const lastCommit = log.length > 0 ? log[0] : null;
|
|
88
87
|
results.traceability.changeDetails = {
|
|
89
88
|
branch: status.current,
|
|
90
89
|
lastCommit: lastCommit ? {
|
package/dist/tools/gitIssues.js
CHANGED
|
@@ -84,9 +84,9 @@ export class GitIssuesTool {
|
|
|
84
84
|
if (!projectPath)
|
|
85
85
|
throw new MCPError('VALIDATION_ERROR', 'projectPath is required');
|
|
86
86
|
const repoInfo = getRepoInfo(projectPath);
|
|
87
|
-
const repo = repoInfo.repoName;
|
|
88
|
-
const githubOwner = repoInfo.githubOwner || process.env.GITHUB_USERNAME;
|
|
89
|
-
const giteaOwner = repoInfo.giteaOwner || process.env.GITEA_USERNAME;
|
|
87
|
+
const repo = params.repo || repoInfo.repoName;
|
|
88
|
+
const githubOwner = params.owner || repoInfo.githubOwner || process.env.GITHUB_USERNAME;
|
|
89
|
+
const giteaOwner = params.owner || repoInfo.giteaOwner || process.env.GITEA_USERNAME;
|
|
90
90
|
switch (action) {
|
|
91
91
|
case 'create': {
|
|
92
92
|
if (!params.title)
|
|
@@ -174,7 +174,7 @@ export class GitIssuesTool {
|
|
|
174
174
|
return results;
|
|
175
175
|
}
|
|
176
176
|
case 'get': {
|
|
177
|
-
const issue_number = params.issue_number;
|
|
177
|
+
const issue_number = params.issue_number || params.issueNumber;
|
|
178
178
|
if (!issue_number)
|
|
179
179
|
throw new MCPError('VALIDATION_ERROR', 'issue_number is required');
|
|
180
180
|
if (!repo)
|
|
@@ -210,7 +210,7 @@ export class GitIssuesTool {
|
|
|
210
210
|
return results;
|
|
211
211
|
}
|
|
212
212
|
case 'update': {
|
|
213
|
-
const issue_number = params.issue_number;
|
|
213
|
+
const issue_number = params.issue_number || params.issueNumber;
|
|
214
214
|
if (!issue_number)
|
|
215
215
|
throw new MCPError('VALIDATION_ERROR', 'issue_number is required');
|
|
216
216
|
if (!repo)
|
|
@@ -253,7 +253,7 @@ export class GitIssuesTool {
|
|
|
253
253
|
return results;
|
|
254
254
|
}
|
|
255
255
|
case 'close': {
|
|
256
|
-
const issue_number = params.issue_number;
|
|
256
|
+
const issue_number = params.issue_number || params.issueNumber;
|
|
257
257
|
if (!issue_number)
|
|
258
258
|
throw new MCPError('VALIDATION_ERROR', 'issue_number is required');
|
|
259
259
|
if (!repo)
|
|
@@ -290,8 +290,8 @@ export class GitIssuesTool {
|
|
|
290
290
|
return results;
|
|
291
291
|
}
|
|
292
292
|
case 'comment': {
|
|
293
|
-
const issue_number = params.issue_number;
|
|
294
|
-
const comment_body = params.comment_body;
|
|
293
|
+
const issue_number = params.issue_number || params.issueNumber;
|
|
294
|
+
const comment_body = params.comment_body || params.body;
|
|
295
295
|
if (!issue_number)
|
|
296
296
|
throw new MCPError('VALIDATION_ERROR', 'issue_number is required');
|
|
297
297
|
if (!comment_body)
|
package/dist/tools/gitMonitor.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import simpleGit from 'simple-git';
|
|
2
1
|
import { MCPError } from '../utils/errors.js';
|
|
3
2
|
import { getRepoInfo } from '../utils/repoHelpers.js';
|
|
4
3
|
import axios from 'axios';
|
|
@@ -37,37 +36,36 @@ export class GitMonitorTool {
|
|
|
37
36
|
if (!action || !projectPath) {
|
|
38
37
|
throw new MCPError('VALIDATION_ERROR', 'action and projectPath are required');
|
|
39
38
|
}
|
|
40
|
-
const git =
|
|
39
|
+
const git = ctx.gitAdapter;
|
|
41
40
|
switch (action) {
|
|
42
41
|
case 'log': {
|
|
43
42
|
const options = {
|
|
44
43
|
maxCount: params.limit || 10,
|
|
45
44
|
};
|
|
46
45
|
if (params.since)
|
|
47
|
-
options.from = params.since;
|
|
48
|
-
if
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
options.author = params.author;
|
|
52
|
-
const log = await git.log(options);
|
|
46
|
+
options.from = params.since; // Note: GitAdapter log doesn't support 'from' date filtering natively yet, but we pass it anyway or ignore
|
|
47
|
+
// GitAdapter log supports 'ref' and 'maxCount'. Date filtering would need to be done manually if not supported.
|
|
48
|
+
// For now, we'll just get the log and filter if needed, or rely on maxCount.
|
|
49
|
+
const logEntries = await git.log(projectPath, options);
|
|
53
50
|
const results = {
|
|
54
51
|
success: true,
|
|
55
52
|
local: {
|
|
56
|
-
commits:
|
|
57
|
-
total:
|
|
53
|
+
commits: logEntries,
|
|
54
|
+
total: logEntries.length,
|
|
58
55
|
},
|
|
59
56
|
providers: {}
|
|
60
57
|
};
|
|
61
58
|
// Also query remote APIs
|
|
62
59
|
const repoInfo = getRepoInfo(projectPath);
|
|
63
|
-
const
|
|
64
|
-
const
|
|
60
|
+
const repo = params.repo || repoInfo.repoName;
|
|
61
|
+
const githubOwner = params.owner || process.env.GITHUB_USERNAME;
|
|
62
|
+
const giteaOwner = params.owner || process.env.GITEA_USERNAME;
|
|
65
63
|
// GitHub
|
|
66
64
|
if (ctx.providerManager.github && githubOwner) {
|
|
67
65
|
try {
|
|
68
66
|
const commits = await ctx.providerManager.github.rest.repos.listCommits({
|
|
69
67
|
owner: githubOwner,
|
|
70
|
-
repo:
|
|
68
|
+
repo: repo,
|
|
71
69
|
since: params.since,
|
|
72
70
|
until: params.until,
|
|
73
71
|
per_page: params.limit || 10,
|
|
@@ -91,7 +89,7 @@ export class GitMonitorTool {
|
|
|
91
89
|
// Gitea
|
|
92
90
|
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
93
91
|
try {
|
|
94
|
-
const commits = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${
|
|
92
|
+
const commits = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/commits`, {
|
|
95
93
|
params: {
|
|
96
94
|
since: params.since,
|
|
97
95
|
until: params.until,
|
|
@@ -118,7 +116,7 @@ export class GitMonitorTool {
|
|
|
118
116
|
return results;
|
|
119
117
|
}
|
|
120
118
|
case 'status': {
|
|
121
|
-
const status = await git.status();
|
|
119
|
+
const status = await git.status(projectPath);
|
|
122
120
|
const detailed = params.detailed ? {
|
|
123
121
|
branch: status.current,
|
|
124
122
|
ahead: status.ahead,
|
|
@@ -137,18 +135,13 @@ export class GitMonitorTool {
|
|
|
137
135
|
const branch = params.branch;
|
|
138
136
|
const options = {
|
|
139
137
|
maxCount: params.limit || 50,
|
|
138
|
+
ref: branch
|
|
140
139
|
};
|
|
141
|
-
|
|
142
|
-
options.from = branch;
|
|
143
|
-
if (params.since)
|
|
144
|
-
options.from = params.since;
|
|
145
|
-
if (params.author)
|
|
146
|
-
options.author = params.author;
|
|
147
|
-
const log = await git.log(options);
|
|
140
|
+
const logEntries = await git.log(projectPath, options);
|
|
148
141
|
const results = {
|
|
149
142
|
success: true,
|
|
150
143
|
local: {
|
|
151
|
-
commits:
|
|
144
|
+
commits: logEntries.map(c => ({
|
|
152
145
|
hash: c.hash,
|
|
153
146
|
date: c.date,
|
|
154
147
|
message: c.message,
|
|
@@ -160,14 +153,15 @@ export class GitMonitorTool {
|
|
|
160
153
|
};
|
|
161
154
|
// Also query remote APIs
|
|
162
155
|
const repoInfo = getRepoInfo(projectPath);
|
|
163
|
-
const
|
|
164
|
-
const
|
|
156
|
+
const repo = params.repo || repoInfo.repoName;
|
|
157
|
+
const githubOwner = params.owner || process.env.GITHUB_USERNAME;
|
|
158
|
+
const giteaOwner = params.owner || process.env.GITEA_USERNAME;
|
|
165
159
|
// GitHub
|
|
166
160
|
if (ctx.providerManager.github && githubOwner) {
|
|
167
161
|
try {
|
|
168
162
|
const commits = await ctx.providerManager.github.rest.repos.listCommits({
|
|
169
163
|
owner: githubOwner,
|
|
170
|
-
repo:
|
|
164
|
+
repo: repo,
|
|
171
165
|
sha: branch,
|
|
172
166
|
since: params.since,
|
|
173
167
|
per_page: params.limit || 50,
|
|
@@ -190,7 +184,7 @@ export class GitMonitorTool {
|
|
|
190
184
|
// Gitea
|
|
191
185
|
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
192
186
|
try {
|
|
193
|
-
const commits = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${
|
|
187
|
+
const commits = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/commits`, {
|
|
194
188
|
params: {
|
|
195
189
|
sha: branch,
|
|
196
190
|
since: params.since,
|
|
@@ -216,9 +210,9 @@ export class GitMonitorTool {
|
|
|
216
210
|
return results;
|
|
217
211
|
}
|
|
218
212
|
case 'contributors': {
|
|
219
|
-
const
|
|
213
|
+
const logEntries = await git.log(projectPath);
|
|
220
214
|
const contributors = new Map();
|
|
221
|
-
|
|
215
|
+
logEntries.forEach(commit => {
|
|
222
216
|
const key = commit.author_email;
|
|
223
217
|
if (contributors.has(key)) {
|
|
224
218
|
contributors.get(key).commits++;
|
|
@@ -239,14 +233,15 @@ export class GitMonitorTool {
|
|
|
239
233
|
};
|
|
240
234
|
// Also query remote APIs
|
|
241
235
|
const repoInfo = getRepoInfo(projectPath);
|
|
242
|
-
const
|
|
243
|
-
const
|
|
236
|
+
const repo = params.repo || repoInfo.repoName;
|
|
237
|
+
const githubOwner = params.owner || process.env.GITHUB_USERNAME;
|
|
238
|
+
const giteaOwner = params.owner || process.env.GITEA_USERNAME;
|
|
244
239
|
// GitHub
|
|
245
240
|
if (ctx.providerManager.github && githubOwner) {
|
|
246
241
|
try {
|
|
247
242
|
const contributors = await ctx.providerManager.github.rest.repos.listContributors({
|
|
248
243
|
owner: githubOwner,
|
|
249
|
-
repo:
|
|
244
|
+
repo: repo,
|
|
250
245
|
});
|
|
251
246
|
results.providers.github = {
|
|
252
247
|
success: true,
|
|
@@ -265,7 +260,7 @@ export class GitMonitorTool {
|
|
|
265
260
|
// Gitea
|
|
266
261
|
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
267
262
|
try {
|
|
268
|
-
const contributors = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${
|
|
263
|
+
const contributors = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/contributors`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
269
264
|
results.providers.gitea = {
|
|
270
265
|
success: true,
|
|
271
266
|
contributors: contributors.data.map((c) => ({
|