@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
package/dist/tools/gitTags.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 { getRepoInfo } from '../utils/repoHelpers.js';
|
|
@@ -30,9 +29,17 @@ export class GitTagsTool {
|
|
|
30
29
|
type: "string",
|
|
31
30
|
description: "Tag annotation message (optional for create)"
|
|
32
31
|
},
|
|
32
|
+
annotated: {
|
|
33
|
+
type: "boolean",
|
|
34
|
+
description: "Create annotated tag (optional for create, default: false)"
|
|
35
|
+
},
|
|
33
36
|
force: {
|
|
34
37
|
type: "boolean",
|
|
35
38
|
description: "Force create/delete/push tag (default: true)"
|
|
39
|
+
},
|
|
40
|
+
remote: {
|
|
41
|
+
type: "string",
|
|
42
|
+
description: "Remote name (optional for push, default: origin)"
|
|
36
43
|
}
|
|
37
44
|
},
|
|
38
45
|
required: ["projectPath", "action"]
|
|
@@ -44,37 +51,37 @@ export class GitTagsTool {
|
|
|
44
51
|
if (!action || !projectPath) {
|
|
45
52
|
throw new MCPError('VALIDATION_ERROR', 'action and projectPath are required');
|
|
46
53
|
}
|
|
47
|
-
|
|
54
|
+
if (!ctx.gitAdapter) {
|
|
55
|
+
throw new MCPError('INTERNAL_ERROR', 'Git adapter not available');
|
|
56
|
+
}
|
|
48
57
|
switch (action) {
|
|
49
58
|
case 'create': {
|
|
50
59
|
const tagName = params.tagName;
|
|
51
60
|
if (!tagName)
|
|
52
61
|
throw new MCPError('VALIDATION_ERROR', 'tagName is required');
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
else {
|
|
57
|
-
await git.addTag(tagName);
|
|
58
|
-
}
|
|
62
|
+
const ref = params.ref || 'HEAD';
|
|
63
|
+
const message = params.annotated ? (params.message || tagName) : undefined;
|
|
64
|
+
await ctx.gitAdapter.createTag(projectPath, tagName, ref, message);
|
|
59
65
|
return { success: true, tag: tagName, local: true };
|
|
60
66
|
}
|
|
61
67
|
case 'list': {
|
|
62
|
-
const localTags = await
|
|
68
|
+
const localTags = await ctx.gitAdapter.listTags(projectPath);
|
|
63
69
|
const results = {
|
|
64
70
|
success: true,
|
|
65
|
-
local: { tags: localTags
|
|
71
|
+
local: { tags: localTags },
|
|
66
72
|
providers: {}
|
|
67
73
|
};
|
|
68
74
|
// Also query remote APIs
|
|
69
75
|
const repoInfo = getRepoInfo(projectPath);
|
|
70
|
-
const
|
|
71
|
-
const
|
|
76
|
+
const repo = params.repo || repoInfo.repoName;
|
|
77
|
+
const githubOwner = params.owner || process.env.GITHUB_USERNAME;
|
|
78
|
+
const giteaOwner = params.owner || process.env.GITEA_USERNAME;
|
|
72
79
|
// GitHub
|
|
73
80
|
if (ctx.providerManager.github && githubOwner) {
|
|
74
81
|
try {
|
|
75
82
|
const tags = await ctx.providerManager.github.rest.repos.listTags({
|
|
76
83
|
owner: githubOwner,
|
|
77
|
-
repo:
|
|
84
|
+
repo: repo,
|
|
78
85
|
});
|
|
79
86
|
results.providers.github = {
|
|
80
87
|
success: true,
|
|
@@ -93,7 +100,7 @@ export class GitTagsTool {
|
|
|
93
100
|
// Gitea
|
|
94
101
|
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
95
102
|
try {
|
|
96
|
-
const tags = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${
|
|
103
|
+
const tags = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/tags`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
97
104
|
results.providers.gitea = {
|
|
98
105
|
success: true,
|
|
99
106
|
tags: tags.data.map((t) => ({
|
|
@@ -114,30 +121,24 @@ export class GitTagsTool {
|
|
|
114
121
|
const tagName = params.tagName;
|
|
115
122
|
if (!tagName)
|
|
116
123
|
throw new MCPError('VALIDATION_ERROR', 'tagName is required');
|
|
117
|
-
const localTags = await
|
|
118
|
-
const localTag = localTags.
|
|
124
|
+
const localTags = await ctx.gitAdapter.listTags(projectPath);
|
|
125
|
+
const localTag = localTags.find(t => t === tagName);
|
|
119
126
|
const results = {
|
|
120
127
|
success: true,
|
|
121
128
|
local: localTag ? { found: true, tag: tagName } : { found: false },
|
|
122
129
|
providers: {}
|
|
123
130
|
};
|
|
124
|
-
if (localTag) {
|
|
125
|
-
try {
|
|
126
|
-
const show = await git.show([tagName]);
|
|
127
|
-
results.local.details = show;
|
|
128
|
-
}
|
|
129
|
-
catch { }
|
|
130
|
-
}
|
|
131
131
|
// Also query remote APIs
|
|
132
132
|
const repoInfo = getRepoInfo(projectPath);
|
|
133
|
-
const
|
|
134
|
-
const
|
|
133
|
+
const repo = params.repo || repoInfo.repoName;
|
|
134
|
+
const githubOwner = params.owner || process.env.GITHUB_USERNAME;
|
|
135
|
+
const giteaOwner = params.owner || process.env.GITEA_USERNAME;
|
|
135
136
|
// GitHub
|
|
136
137
|
if (ctx.providerManager.github && githubOwner) {
|
|
137
138
|
try {
|
|
138
139
|
const tag = await ctx.providerManager.github.rest.git.getRef({
|
|
139
140
|
owner: githubOwner,
|
|
140
|
-
repo:
|
|
141
|
+
repo: repo,
|
|
141
142
|
ref: `tags/${tagName}`,
|
|
142
143
|
});
|
|
143
144
|
results.providers.github = {
|
|
@@ -157,7 +158,7 @@ export class GitTagsTool {
|
|
|
157
158
|
// Gitea
|
|
158
159
|
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
159
160
|
try {
|
|
160
|
-
const tags = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${
|
|
161
|
+
const tags = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/tags`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
161
162
|
const tag = tags.data.find((t) => t.name === tagName);
|
|
162
163
|
if (tag) {
|
|
163
164
|
results.providers.gitea = {
|
|
@@ -183,15 +184,23 @@ export class GitTagsTool {
|
|
|
183
184
|
const tagName = params.tagName;
|
|
184
185
|
if (!tagName)
|
|
185
186
|
throw new MCPError('VALIDATION_ERROR', 'tagName is required');
|
|
186
|
-
await
|
|
187
|
+
await ctx.gitAdapter.deleteTag(projectPath, tagName);
|
|
187
188
|
return { success: true, deleted: tagName, local: true };
|
|
188
189
|
}
|
|
190
|
+
case 'push': {
|
|
191
|
+
const tagName = params.tagName;
|
|
192
|
+
if (!tagName)
|
|
193
|
+
throw new MCPError('VALIDATION_ERROR', 'tagName is required');
|
|
194
|
+
const remote = params.remote || 'origin';
|
|
195
|
+
await ctx.gitAdapter.pushTag(projectPath, remote, tagName);
|
|
196
|
+
return { success: true, pushed: tagName, remote };
|
|
197
|
+
}
|
|
189
198
|
case 'search': {
|
|
190
199
|
const pattern = params.pattern;
|
|
191
200
|
if (!pattern)
|
|
192
201
|
throw new MCPError('VALIDATION_ERROR', 'pattern is required');
|
|
193
|
-
const localTags = await
|
|
194
|
-
const filtered = localTags.
|
|
202
|
+
const localTags = await ctx.gitAdapter.listTags(projectPath);
|
|
203
|
+
const filtered = localTags.filter(t => t.includes(pattern));
|
|
195
204
|
const results = {
|
|
196
205
|
success: true,
|
|
197
206
|
local: { tags: filtered },
|
|
@@ -199,14 +208,15 @@ export class GitTagsTool {
|
|
|
199
208
|
};
|
|
200
209
|
// Also search remote APIs
|
|
201
210
|
const repoInfo = getRepoInfo(projectPath);
|
|
202
|
-
const
|
|
203
|
-
const
|
|
211
|
+
const repo = params.repo || repoInfo.repoName;
|
|
212
|
+
const githubOwner = params.owner || process.env.GITHUB_USERNAME;
|
|
213
|
+
const giteaOwner = params.owner || process.env.GITEA_USERNAME;
|
|
204
214
|
// GitHub
|
|
205
215
|
if (ctx.providerManager.github && githubOwner) {
|
|
206
216
|
try {
|
|
207
217
|
const tags = await ctx.providerManager.github.rest.repos.listTags({
|
|
208
218
|
owner: githubOwner,
|
|
209
|
-
repo:
|
|
219
|
+
repo: repo,
|
|
210
220
|
});
|
|
211
221
|
const matchedTags = tags.data.filter((t) => t.name.includes(pattern));
|
|
212
222
|
results.providers.github = {
|
|
@@ -221,7 +231,7 @@ export class GitTagsTool {
|
|
|
221
231
|
// Gitea
|
|
222
232
|
if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
|
|
223
233
|
try {
|
|
224
|
-
const tags = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${
|
|
234
|
+
const tags = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/tags`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
225
235
|
const matchedTags = tags.data.filter((t) => t.name.includes(pattern));
|
|
226
236
|
results.providers.gitea = {
|
|
227
237
|
success: true,
|
|
@@ -22,6 +22,33 @@ export declare class GitUpdateTool implements Tool {
|
|
|
22
22
|
type: string;
|
|
23
23
|
description: string;
|
|
24
24
|
};
|
|
25
|
+
message: {
|
|
26
|
+
type: string;
|
|
27
|
+
description: string;
|
|
28
|
+
};
|
|
29
|
+
commitMessage: {
|
|
30
|
+
type: string;
|
|
31
|
+
description: string;
|
|
32
|
+
};
|
|
33
|
+
branch: {
|
|
34
|
+
type: string;
|
|
35
|
+
description: string;
|
|
36
|
+
};
|
|
37
|
+
files: {
|
|
38
|
+
type: string;
|
|
39
|
+
items: {
|
|
40
|
+
type: string;
|
|
41
|
+
};
|
|
42
|
+
description: string;
|
|
43
|
+
};
|
|
44
|
+
repoName: {
|
|
45
|
+
type: string;
|
|
46
|
+
description: string;
|
|
47
|
+
};
|
|
48
|
+
description: {
|
|
49
|
+
type: string;
|
|
50
|
+
description: string;
|
|
51
|
+
};
|
|
25
52
|
};
|
|
26
53
|
required: string[];
|
|
27
54
|
additionalProperties: boolean;
|
package/dist/tools/gitUpdate.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';
|
|
@@ -27,6 +26,31 @@ export class GitUpdateTool {
|
|
|
27
26
|
owner: {
|
|
28
27
|
type: "string",
|
|
29
28
|
description: "Repository owner for providers (optional, uses env vars)"
|
|
29
|
+
},
|
|
30
|
+
message: {
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "Commit message"
|
|
33
|
+
},
|
|
34
|
+
commitMessage: {
|
|
35
|
+
type: "string",
|
|
36
|
+
description: "Commit message (alias)"
|
|
37
|
+
},
|
|
38
|
+
branch: {
|
|
39
|
+
type: "string",
|
|
40
|
+
description: "Branch name"
|
|
41
|
+
},
|
|
42
|
+
files: {
|
|
43
|
+
type: "array",
|
|
44
|
+
items: { type: "string" },
|
|
45
|
+
description: "Files to add"
|
|
46
|
+
},
|
|
47
|
+
repoName: {
|
|
48
|
+
type: "string",
|
|
49
|
+
description: "Repository name"
|
|
50
|
+
},
|
|
51
|
+
description: {
|
|
52
|
+
type: "string",
|
|
53
|
+
description: "Repository description"
|
|
30
54
|
}
|
|
31
55
|
},
|
|
32
56
|
required: ["projectPath"],
|
|
@@ -38,10 +62,12 @@ export class GitUpdateTool {
|
|
|
38
62
|
if (!projectPath) {
|
|
39
63
|
throw new MCPError('VALIDATION_ERROR', 'projectPath is required');
|
|
40
64
|
}
|
|
65
|
+
if (!ctx.gitAdapter) {
|
|
66
|
+
throw new MCPError('INTERNAL_ERROR', 'Git adapter not available');
|
|
67
|
+
}
|
|
41
68
|
const commitMessage = params.message || params.commitMessage || `[git-update] Update at ${new Date().toISOString()}`;
|
|
42
69
|
const branch = params.branch || 'master';
|
|
43
70
|
const files = params.files || ['.'];
|
|
44
|
-
const git = simpleGit({ baseDir: projectPath });
|
|
45
71
|
// Resultado com rastreabilidade completa
|
|
46
72
|
const results = {
|
|
47
73
|
success: true,
|
|
@@ -65,7 +91,7 @@ export class GitUpdateTool {
|
|
|
65
91
|
action: 'check_pre_update_status',
|
|
66
92
|
timestamp: new Date().toISOString(),
|
|
67
93
|
});
|
|
68
|
-
const preStatus = await
|
|
94
|
+
const preStatus = await ctx.gitAdapter.status(projectPath);
|
|
69
95
|
results.traceability.preUpdateStatus = {
|
|
70
96
|
branch: preStatus.current,
|
|
71
97
|
ahead: preStatus.ahead,
|
|
@@ -76,7 +102,7 @@ export class GitUpdateTool {
|
|
|
76
102
|
renamed: preStatus.renamed,
|
|
77
103
|
staged: preStatus.staged,
|
|
78
104
|
conflicted: preStatus.conflicted,
|
|
79
|
-
isClean: preStatus.isClean
|
|
105
|
+
isClean: preStatus.isClean,
|
|
80
106
|
};
|
|
81
107
|
// 2. Detectar arquivos alterados com diff
|
|
82
108
|
results.traceability.updateSteps.push({
|
|
@@ -84,15 +110,11 @@ export class GitUpdateTool {
|
|
|
84
110
|
action: 'detect_changes',
|
|
85
111
|
timestamp: new Date().toISOString(),
|
|
86
112
|
});
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
status: status.trim(),
|
|
93
|
-
file: fileParts.join('\t').trim(),
|
|
94
|
-
};
|
|
95
|
-
});
|
|
113
|
+
// Isomorphic git diff is basic, so we rely on status for changed files list
|
|
114
|
+
results.traceability.changedFiles = preStatus.files.map(f => ({
|
|
115
|
+
status: f.working_dir,
|
|
116
|
+
file: f.path
|
|
117
|
+
}));
|
|
96
118
|
// 3. Add files
|
|
97
119
|
results.traceability.updateSteps.push({
|
|
98
120
|
step: 3,
|
|
@@ -100,7 +122,7 @@ export class GitUpdateTool {
|
|
|
100
122
|
timestamp: new Date().toISOString(),
|
|
101
123
|
files,
|
|
102
124
|
});
|
|
103
|
-
await
|
|
125
|
+
await ctx.gitAdapter.add(projectPath, files);
|
|
104
126
|
// 4. Commit
|
|
105
127
|
results.traceability.updateSteps.push({
|
|
106
128
|
step: 4,
|
|
@@ -110,7 +132,13 @@ export class GitUpdateTool {
|
|
|
110
132
|
});
|
|
111
133
|
let commitResult;
|
|
112
134
|
try {
|
|
113
|
-
|
|
135
|
+
const sha = await ctx.gitAdapter.commit(projectPath, commitMessage);
|
|
136
|
+
commitResult = {
|
|
137
|
+
commit: sha,
|
|
138
|
+
summary: { changes: 0, insertions: 0, deletions: 0 }, // Basic adapter doesn't return stats yet
|
|
139
|
+
branch: await ctx.gitAdapter.getCurrentBranch(projectPath),
|
|
140
|
+
author: { name: 'unknown', email: 'unknown' } // We could fetch this if needed
|
|
141
|
+
};
|
|
114
142
|
results.traceability.commit = {
|
|
115
143
|
hash: commitResult.commit,
|
|
116
144
|
summary: commitResult.summary,
|
|
@@ -119,7 +147,7 @@ export class GitUpdateTool {
|
|
|
119
147
|
};
|
|
120
148
|
}
|
|
121
149
|
catch (err) {
|
|
122
|
-
if (err.message.includes('nothing to commit')) {
|
|
150
|
+
if (err.message.includes('nothing to commit') || (preStatus.isClean && files[0] === '.')) {
|
|
123
151
|
results.traceability.commit = { message: 'No changes to commit' };
|
|
124
152
|
}
|
|
125
153
|
else {
|
|
@@ -127,7 +155,7 @@ export class GitUpdateTool {
|
|
|
127
155
|
}
|
|
128
156
|
}
|
|
129
157
|
// 5. Get remotes and ensure repos exist
|
|
130
|
-
const remotes = await
|
|
158
|
+
const remotes = await ctx.gitAdapter.listRemotes(projectPath);
|
|
131
159
|
let githubRemote = remotes.find(r => r.name === 'github' || r.name === 'origin');
|
|
132
160
|
let giteaRemote = remotes.find(r => r.name === 'gitea');
|
|
133
161
|
// Auto-create repos if they don't exist
|
|
@@ -169,8 +197,9 @@ export class GitUpdateTool {
|
|
|
169
197
|
});
|
|
170
198
|
}
|
|
171
199
|
// Add remote
|
|
172
|
-
|
|
173
|
-
|
|
200
|
+
const url = `https://github.com/${githubOwner}/${repoName}.git`;
|
|
201
|
+
await ctx.gitAdapter.addRemote(projectPath, 'github', url);
|
|
202
|
+
githubRemote = { name: 'github', remote: 'github', url, fetch: url };
|
|
174
203
|
}
|
|
175
204
|
catch (err) {
|
|
176
205
|
results.traceability.errors.push({
|
|
@@ -211,8 +240,9 @@ export class GitUpdateTool {
|
|
|
211
240
|
});
|
|
212
241
|
}
|
|
213
242
|
// Add remote
|
|
214
|
-
|
|
215
|
-
|
|
243
|
+
const url = `${ctx.providerManager.giteaBaseUrl}/${giteaOwner}/${repoName}.git`;
|
|
244
|
+
await ctx.gitAdapter.addRemote(projectPath, 'gitea', url);
|
|
245
|
+
giteaRemote = { name: 'gitea', remote: 'gitea', url, fetch: url };
|
|
216
246
|
}
|
|
217
247
|
catch (err) {
|
|
218
248
|
results.traceability.errors.push({
|
|
@@ -233,15 +263,14 @@ export class GitUpdateTool {
|
|
|
233
263
|
branch,
|
|
234
264
|
});
|
|
235
265
|
try {
|
|
236
|
-
|
|
266
|
+
await ctx.gitAdapter.push(projectPath, githubRemote.name, branch);
|
|
237
267
|
results.providers.github = {
|
|
238
268
|
success: true,
|
|
239
269
|
remote: githubRemote.name,
|
|
240
|
-
url: githubRemote.
|
|
270
|
+
url: githubRemote.url,
|
|
241
271
|
pushed: true,
|
|
242
272
|
pushResult: {
|
|
243
|
-
|
|
244
|
-
update: pushResult.update,
|
|
273
|
+
message: 'Push successful'
|
|
245
274
|
}
|
|
246
275
|
};
|
|
247
276
|
// Verificar commit no GitHub via API
|
|
@@ -285,15 +314,14 @@ export class GitUpdateTool {
|
|
|
285
314
|
branch,
|
|
286
315
|
});
|
|
287
316
|
try {
|
|
288
|
-
|
|
317
|
+
await ctx.gitAdapter.push(projectPath, giteaRemote.name, branch);
|
|
289
318
|
results.providers.gitea = {
|
|
290
319
|
success: true,
|
|
291
320
|
remote: giteaRemote.name,
|
|
292
|
-
url: giteaRemote.
|
|
321
|
+
url: giteaRemote.url,
|
|
293
322
|
pushed: true,
|
|
294
323
|
pushResult: {
|
|
295
|
-
|
|
296
|
-
update: pushResult.update,
|
|
324
|
+
message: 'Push successful'
|
|
297
325
|
}
|
|
298
326
|
};
|
|
299
327
|
// Verificar commit no Gitea via API
|
|
@@ -328,12 +356,12 @@ export class GitUpdateTool {
|
|
|
328
356
|
action: 'check_post_update_status',
|
|
329
357
|
timestamp: new Date().toISOString(),
|
|
330
358
|
});
|
|
331
|
-
const postStatus = await
|
|
359
|
+
const postStatus = await ctx.gitAdapter.status(projectPath);
|
|
332
360
|
results.traceability.postUpdateStatus = {
|
|
333
361
|
branch: postStatus.current,
|
|
334
362
|
ahead: postStatus.ahead,
|
|
335
363
|
behind: postStatus.behind,
|
|
336
|
-
isClean: postStatus.isClean
|
|
364
|
+
isClean: postStatus.isClean,
|
|
337
365
|
tracking: postStatus.tracking,
|
|
338
366
|
};
|
|
339
367
|
// 9. Salvar histórico local
|
|
@@ -374,12 +402,11 @@ export class GitUpdateTool {
|
|
|
374
402
|
const githubOwner = params.owner || process.env.GITHUB_USERNAME;
|
|
375
403
|
const giteaOwner = params.owner || process.env.GITEA_USERNAME;
|
|
376
404
|
// Pegar nome do repo dos remotes do Git ao invés do nome da pasta
|
|
377
|
-
const
|
|
378
|
-
const remotes = await git.getRemotes(true);
|
|
405
|
+
const remotes = await ctx.gitAdapter.listRemotes(projectPath);
|
|
379
406
|
let repoName = getRepoNameFromPath(projectPath); // fallback
|
|
380
407
|
if (remotes.length > 0) {
|
|
381
408
|
const githubRemote = remotes.find(r => r.name === 'github') || remotes.find(r => r.name === 'origin') || remotes[0];
|
|
382
|
-
const match = githubRemote.
|
|
409
|
+
const match = githubRemote.fetch?.match(/\/([^\/]+?)(\.git)?$/);
|
|
383
410
|
if (match)
|
|
384
411
|
repoName = match[1];
|
|
385
412
|
}
|
package/dist/tools/gitUpload.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';
|
|
@@ -42,11 +41,13 @@ export class GitUploadTool {
|
|
|
42
41
|
if (!projectPath) {
|
|
43
42
|
throw new MCPError('VALIDATION_ERROR', 'projectPath is required');
|
|
44
43
|
}
|
|
44
|
+
if (!ctx.gitAdapter) {
|
|
45
|
+
throw new MCPError('INTERNAL_ERROR', 'Git adapter not available');
|
|
46
|
+
}
|
|
45
47
|
const repoName = params.repoName || getRepoNameFromPath(projectPath);
|
|
46
48
|
const description = params.description || `Project uploaded via git-upload at ${new Date().toISOString()}`;
|
|
47
49
|
const isPrivate = params.private !== undefined ? params.private : true;
|
|
48
50
|
const branch = params.branch || 'master';
|
|
49
|
-
const git = simpleGit({ baseDir: projectPath });
|
|
50
51
|
// Resultado com rastreabilidade completa
|
|
51
52
|
const results = {
|
|
52
53
|
success: true,
|
|
@@ -64,20 +65,16 @@ export class GitUploadTool {
|
|
|
64
65
|
try {
|
|
65
66
|
// 0. Verificar se é um repositório Git, se não for, inicializar
|
|
66
67
|
try {
|
|
67
|
-
await
|
|
68
|
+
await ctx.gitAdapter.status(projectPath);
|
|
68
69
|
}
|
|
69
70
|
catch (err) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
throw err;
|
|
80
|
-
}
|
|
71
|
+
// If status fails, assume not a git repo or other error
|
|
72
|
+
results.traceability.uploadSteps.push({
|
|
73
|
+
step: 0,
|
|
74
|
+
action: 'init_git_repository',
|
|
75
|
+
timestamp: new Date().toISOString(),
|
|
76
|
+
});
|
|
77
|
+
await ctx.gitAdapter.init(projectPath, branch);
|
|
81
78
|
}
|
|
82
79
|
// 1. Verificar status local
|
|
83
80
|
results.traceability.uploadSteps.push({
|
|
@@ -85,7 +82,7 @@ export class GitUploadTool {
|
|
|
85
82
|
action: 'check_local_status',
|
|
86
83
|
timestamp: new Date().toISOString(),
|
|
87
84
|
});
|
|
88
|
-
const status = await
|
|
85
|
+
const status = await ctx.gitAdapter.status(projectPath);
|
|
89
86
|
results.traceability.localChanges = {
|
|
90
87
|
modified: status.modified,
|
|
91
88
|
created: status.created,
|
|
@@ -93,17 +90,17 @@ export class GitUploadTool {
|
|
|
93
90
|
renamed: status.renamed,
|
|
94
91
|
staged: status.staged,
|
|
95
92
|
conflicted: status.conflicted,
|
|
96
|
-
isClean: status.isClean
|
|
93
|
+
isClean: status.isClean,
|
|
97
94
|
};
|
|
98
95
|
// 2. Verificar se tem mudanças
|
|
99
|
-
if (!status.isClean
|
|
96
|
+
if (!status.isClean) {
|
|
100
97
|
results.traceability.uploadSteps.push({
|
|
101
98
|
step: 2,
|
|
102
99
|
action: 'stage_all_changes',
|
|
103
100
|
timestamp: new Date().toISOString(),
|
|
104
101
|
files: [...status.modified, ...status.created, ...status.deleted],
|
|
105
102
|
});
|
|
106
|
-
await
|
|
103
|
+
await ctx.gitAdapter.add(projectPath, ['.']);
|
|
107
104
|
}
|
|
108
105
|
// 3. Commit com rastreabilidade
|
|
109
106
|
const commitMessage = params.commitMessage || `[git-upload] Upload project at ${new Date().toISOString()}`;
|
|
@@ -115,7 +112,13 @@ export class GitUploadTool {
|
|
|
115
112
|
});
|
|
116
113
|
let commitResult;
|
|
117
114
|
try {
|
|
118
|
-
|
|
115
|
+
const sha = await ctx.gitAdapter.commit(projectPath, commitMessage);
|
|
116
|
+
commitResult = {
|
|
117
|
+
commit: sha,
|
|
118
|
+
summary: { changes: 0, insertions: 0, deletions: 0 },
|
|
119
|
+
branch: await ctx.gitAdapter.getCurrentBranch(projectPath),
|
|
120
|
+
author: { name: 'unknown', email: 'unknown' }
|
|
121
|
+
};
|
|
119
122
|
results.traceability.commit = {
|
|
120
123
|
hash: commitResult.commit,
|
|
121
124
|
summary: commitResult.summary,
|
|
@@ -124,7 +127,7 @@ export class GitUploadTool {
|
|
|
124
127
|
};
|
|
125
128
|
}
|
|
126
129
|
catch (err) {
|
|
127
|
-
if (err.message.includes('nothing to commit')) {
|
|
130
|
+
if (err.message.includes('nothing to commit') || (status.isClean && status.staged.length === 0)) {
|
|
128
131
|
results.traceability.commit = { message: 'No changes to commit' };
|
|
129
132
|
}
|
|
130
133
|
else {
|
|
@@ -174,12 +177,12 @@ export class GitUploadTool {
|
|
|
174
177
|
};
|
|
175
178
|
}
|
|
176
179
|
// Adicionar remote se necessário (URL LIMPA sem token)
|
|
177
|
-
const remotes = await
|
|
180
|
+
const remotes = await ctx.gitAdapter.listRemotes(projectPath);
|
|
178
181
|
const githubRemote = remotes.find(r => r.name === 'github');
|
|
179
182
|
if (!githubRemote) {
|
|
180
183
|
// Usar URL limpa sem token para segurança
|
|
181
184
|
const remoteUrl = `https://github.com/${githubOwner}/${repoName}.git`;
|
|
182
|
-
await
|
|
185
|
+
await ctx.gitAdapter.addRemote(projectPath, 'github', remoteUrl);
|
|
183
186
|
results.traceability.uploadSteps.push({
|
|
184
187
|
step: 5,
|
|
185
188
|
action: 'add_github_remote',
|
|
@@ -195,19 +198,12 @@ export class GitUploadTool {
|
|
|
195
198
|
branch,
|
|
196
199
|
});
|
|
197
200
|
try {
|
|
198
|
-
// Configurar credential helper temporário para este push
|
|
199
|
-
await git.addConfig('credential.https://github.com.helper', 'store');
|
|
200
|
-
await git.addConfig(`credential.https://github.com.username`, githubOwner);
|
|
201
201
|
// Push com --set-upstream mas SEM --force (segurança)
|
|
202
|
-
await
|
|
203
|
-
// Limpar credential helper após push
|
|
204
|
-
await git.addConfig('credential.https://github.com.helper', '');
|
|
202
|
+
await ctx.gitAdapter.push(projectPath, 'github', branch, false, true);
|
|
205
203
|
results.providers.github.pushed = true;
|
|
206
204
|
results.providers.github.pushStatus = 'success';
|
|
207
205
|
}
|
|
208
206
|
catch (pushErr) {
|
|
209
|
-
// Limpar credential helper em caso de erro
|
|
210
|
-
await git.addConfig('credential.https://github.com.helper', '');
|
|
211
207
|
results.providers.github.pushed = false;
|
|
212
208
|
results.providers.github.pushError = pushErr.message;
|
|
213
209
|
results.traceability.errors.push({
|
|
@@ -271,14 +267,14 @@ export class GitUploadTool {
|
|
|
271
267
|
};
|
|
272
268
|
}
|
|
273
269
|
// Adicionar remote se necessário (URL LIMPA sem token)
|
|
274
|
-
const remotes = await
|
|
270
|
+
const remotes = await ctx.gitAdapter.listRemotes(projectPath);
|
|
275
271
|
const giteaRemote = remotes.find(r => r.name === 'gitea');
|
|
276
272
|
if (!giteaRemote) {
|
|
277
273
|
// Usar URL limpa sem token para segurança
|
|
278
274
|
const baseUrl = ctx.providerManager.giteaBaseUrl;
|
|
279
275
|
const cleanUrl = baseUrl.replace(/\/$/, ''); // Remover barra final se houver
|
|
280
276
|
const remoteUrl = `${cleanUrl}/${giteaOwner}/${repoName}.git`;
|
|
281
|
-
await
|
|
277
|
+
await ctx.gitAdapter.addRemote(projectPath, 'gitea', remoteUrl);
|
|
282
278
|
results.traceability.uploadSteps.push({
|
|
283
279
|
step: 8,
|
|
284
280
|
action: 'add_gitea_remote',
|
|
@@ -294,23 +290,12 @@ export class GitUploadTool {
|
|
|
294
290
|
branch,
|
|
295
291
|
});
|
|
296
292
|
try {
|
|
297
|
-
const baseUrl = ctx.providerManager.giteaBaseUrl;
|
|
298
|
-
const cleanUrl = baseUrl.replace(/\/$/, '');
|
|
299
|
-
// Configurar credential helper temporário
|
|
300
|
-
await git.addConfig(`credential.${cleanUrl}.helper`, 'store');
|
|
301
|
-
await git.addConfig(`credential.${cleanUrl}.username`, giteaOwner);
|
|
302
293
|
// Push com --set-upstream mas SEM --force (segurança)
|
|
303
|
-
await
|
|
304
|
-
// Limpar credential helper após push
|
|
305
|
-
await git.addConfig(`credential.${cleanUrl}.helper`, '');
|
|
294
|
+
await ctx.gitAdapter.push(projectPath, 'gitea', branch, false, true);
|
|
306
295
|
results.providers.gitea.pushed = true;
|
|
307
296
|
results.providers.gitea.pushStatus = 'success';
|
|
308
297
|
}
|
|
309
298
|
catch (pushErr) {
|
|
310
|
-
// Limpar credential helper em caso de erro
|
|
311
|
-
const baseUrl = ctx.providerManager.giteaBaseUrl;
|
|
312
|
-
const cleanUrl = baseUrl.replace(/\/$/, '');
|
|
313
|
-
await git.addConfig(`credential.${cleanUrl}.helper`, '');
|
|
314
299
|
results.providers.gitea.pushed = false;
|
|
315
300
|
results.providers.gitea.pushError = pushErr.message;
|
|
316
301
|
results.traceability.errors.push({
|