@andrebuzeli/git-mcp 7.1.0 → 7.2.1
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 +4 -2
- package/dist/tools/gitHistory.js +3 -2
- package/dist/tools/gitIgnore.d.ts +191 -0
- package/dist/tools/gitIgnore.js +354 -0
- package/dist/tools/gitUpdate.js +102 -8
- package/package.json +12 -13
package/dist/index.js
CHANGED
|
@@ -30,6 +30,7 @@ const gitUpload_1 = require("./tools/gitUpload");
|
|
|
30
30
|
const gitUpdate_1 = require("./tools/gitUpdate");
|
|
31
31
|
const gitHistory_1 = require("./tools/gitHistory");
|
|
32
32
|
const gitFix_tool_1 = require("./tools/gitFix.tool");
|
|
33
|
+
const gitIgnore_1 = require("./tools/gitIgnore");
|
|
33
34
|
const toolsGuide_1 = __importDefault(require("./resources/toolsGuide"));
|
|
34
35
|
async function main() {
|
|
35
36
|
// Load optional mcp.json configuration (will populate process.env if values present)
|
|
@@ -41,7 +42,7 @@ async function main() {
|
|
|
41
42
|
if (process.env.DEBUG) {
|
|
42
43
|
console.error('Provider validation:', JSON.stringify(validation, null, 2));
|
|
43
44
|
}
|
|
44
|
-
// Register all
|
|
45
|
+
// Register all 22 Git tools
|
|
45
46
|
const tools = [
|
|
46
47
|
new gitWorkflow_1.GitWorkflowTool(),
|
|
47
48
|
new gitFiles_1.GitFilesTool(),
|
|
@@ -64,6 +65,7 @@ async function main() {
|
|
|
64
65
|
new gitUpdate_1.GitUpdateTool(),
|
|
65
66
|
new gitHistory_1.GitHistoryTool(),
|
|
66
67
|
new gitFix_tool_1.GitFixTool(),
|
|
68
|
+
new gitIgnore_1.GitIgnoreTool(),
|
|
67
69
|
];
|
|
68
70
|
// Register resources
|
|
69
71
|
const resources = [
|
|
@@ -77,7 +79,7 @@ async function main() {
|
|
|
77
79
|
// Create MCP Server with STDIO transport
|
|
78
80
|
const server = new index_1.Server({
|
|
79
81
|
name: '@andrebuzeli/git-mcp',
|
|
80
|
-
version: '
|
|
82
|
+
version: '7.2.0',
|
|
81
83
|
});
|
|
82
84
|
// Register tool list handler
|
|
83
85
|
server.setRequestHandler(types_1.ListToolsRequestSchema, async () => {
|
package/dist/tools/gitHistory.js
CHANGED
|
@@ -42,6 +42,7 @@ const errors_1 = require("../utils/errors");
|
|
|
42
42
|
const axios_1 = __importDefault(require("axios"));
|
|
43
43
|
const fs = __importStar(require("fs/promises"));
|
|
44
44
|
const path = __importStar(require("path"));
|
|
45
|
+
const repoHelpers_1 = require("../utils/repoHelpers");
|
|
45
46
|
/**
|
|
46
47
|
* Git History Tool - Mantém histórico detalhado de TODAS alterações
|
|
47
48
|
* Modo DUAL automático - cria issues de histórico em GitHub e Gitea
|
|
@@ -125,7 +126,7 @@ class GitHistoryTool {
|
|
|
125
126
|
// 3. Criar issue de histórico em ambos providers
|
|
126
127
|
const githubOwner = params.owner || process.env.GITHUB_USERNAME;
|
|
127
128
|
const giteaOwner = params.owner || process.env.GITEA_USERNAME;
|
|
128
|
-
const repo = params.repo ||
|
|
129
|
+
const repo = params.repo || (0, repoHelpers_1.getRepoNameFromPath)(projectPath);
|
|
129
130
|
const historyTitle = params.title || `[${results.traceability.changeDetails.category}] ${results.traceability.changeDetails.description}`;
|
|
130
131
|
const historyBody = this.formatHistoryBody(results.traceability.changeDetails, params);
|
|
131
132
|
// GITHUB
|
|
@@ -235,7 +236,7 @@ class GitHistoryTool {
|
|
|
235
236
|
const localHistory = await this.listLocalHistory(projectPath);
|
|
236
237
|
const githubOwner = params.owner || process.env.GITHUB_USERNAME;
|
|
237
238
|
const giteaOwner = params.owner || process.env.GITEA_USERNAME;
|
|
238
|
-
const repo = params.repo ||
|
|
239
|
+
const repo = params.repo || (0, repoHelpers_1.getRepoNameFromPath)(projectPath);
|
|
239
240
|
for (const historyEntry of localHistory.history) {
|
|
240
241
|
if (!historyEntry.synced) {
|
|
241
242
|
try {
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { Tool, MCPContext } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Git Ignore Tool - Manage .gitignore file
|
|
4
|
+
* Create, read, add, remove patterns, and ensure .gitignore exists
|
|
5
|
+
*/
|
|
6
|
+
export declare class GitIgnoreTool implements Tool {
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
handle(params: Record<string, any>, ctx: MCPContext): Promise<{
|
|
10
|
+
success: boolean;
|
|
11
|
+
path: string;
|
|
12
|
+
exists: boolean;
|
|
13
|
+
content: string;
|
|
14
|
+
patterns: string[];
|
|
15
|
+
lines: number;
|
|
16
|
+
message?: undefined;
|
|
17
|
+
created?: undefined;
|
|
18
|
+
template?: undefined;
|
|
19
|
+
action?: undefined;
|
|
20
|
+
added?: undefined;
|
|
21
|
+
skipped?: undefined;
|
|
22
|
+
total?: undefined;
|
|
23
|
+
removed?: undefined;
|
|
24
|
+
remaining?: undefined;
|
|
25
|
+
updated?: undefined;
|
|
26
|
+
cleared?: undefined;
|
|
27
|
+
} | {
|
|
28
|
+
success: boolean;
|
|
29
|
+
path: string;
|
|
30
|
+
exists: boolean;
|
|
31
|
+
message: string;
|
|
32
|
+
content?: undefined;
|
|
33
|
+
patterns?: undefined;
|
|
34
|
+
lines?: undefined;
|
|
35
|
+
created?: undefined;
|
|
36
|
+
template?: undefined;
|
|
37
|
+
action?: undefined;
|
|
38
|
+
added?: undefined;
|
|
39
|
+
skipped?: undefined;
|
|
40
|
+
total?: undefined;
|
|
41
|
+
removed?: undefined;
|
|
42
|
+
remaining?: undefined;
|
|
43
|
+
updated?: undefined;
|
|
44
|
+
cleared?: undefined;
|
|
45
|
+
} | {
|
|
46
|
+
success: boolean;
|
|
47
|
+
message: string;
|
|
48
|
+
path: string;
|
|
49
|
+
exists?: undefined;
|
|
50
|
+
content?: undefined;
|
|
51
|
+
patterns?: undefined;
|
|
52
|
+
lines?: undefined;
|
|
53
|
+
created?: undefined;
|
|
54
|
+
template?: undefined;
|
|
55
|
+
action?: undefined;
|
|
56
|
+
added?: undefined;
|
|
57
|
+
skipped?: undefined;
|
|
58
|
+
total?: undefined;
|
|
59
|
+
removed?: undefined;
|
|
60
|
+
remaining?: undefined;
|
|
61
|
+
updated?: undefined;
|
|
62
|
+
cleared?: undefined;
|
|
63
|
+
} | {
|
|
64
|
+
success: boolean;
|
|
65
|
+
path: string;
|
|
66
|
+
created: boolean;
|
|
67
|
+
template: any;
|
|
68
|
+
lines: number;
|
|
69
|
+
exists?: undefined;
|
|
70
|
+
content?: undefined;
|
|
71
|
+
patterns?: undefined;
|
|
72
|
+
message?: undefined;
|
|
73
|
+
action?: undefined;
|
|
74
|
+
added?: undefined;
|
|
75
|
+
skipped?: undefined;
|
|
76
|
+
total?: undefined;
|
|
77
|
+
removed?: undefined;
|
|
78
|
+
remaining?: undefined;
|
|
79
|
+
updated?: undefined;
|
|
80
|
+
cleared?: undefined;
|
|
81
|
+
} | {
|
|
82
|
+
success: boolean;
|
|
83
|
+
path: string;
|
|
84
|
+
exists: boolean;
|
|
85
|
+
action: string;
|
|
86
|
+
message: string;
|
|
87
|
+
content?: undefined;
|
|
88
|
+
patterns?: undefined;
|
|
89
|
+
lines?: undefined;
|
|
90
|
+
created?: undefined;
|
|
91
|
+
template?: undefined;
|
|
92
|
+
added?: undefined;
|
|
93
|
+
skipped?: undefined;
|
|
94
|
+
total?: undefined;
|
|
95
|
+
removed?: undefined;
|
|
96
|
+
remaining?: undefined;
|
|
97
|
+
updated?: undefined;
|
|
98
|
+
cleared?: undefined;
|
|
99
|
+
} | {
|
|
100
|
+
success: boolean;
|
|
101
|
+
path: string;
|
|
102
|
+
created: boolean;
|
|
103
|
+
template: any;
|
|
104
|
+
action: string;
|
|
105
|
+
exists?: undefined;
|
|
106
|
+
content?: undefined;
|
|
107
|
+
patterns?: undefined;
|
|
108
|
+
lines?: undefined;
|
|
109
|
+
message?: undefined;
|
|
110
|
+
added?: undefined;
|
|
111
|
+
skipped?: undefined;
|
|
112
|
+
total?: undefined;
|
|
113
|
+
removed?: undefined;
|
|
114
|
+
remaining?: undefined;
|
|
115
|
+
updated?: undefined;
|
|
116
|
+
cleared?: undefined;
|
|
117
|
+
} | {
|
|
118
|
+
success: boolean;
|
|
119
|
+
path: string;
|
|
120
|
+
added: string[];
|
|
121
|
+
skipped: number;
|
|
122
|
+
total: number;
|
|
123
|
+
exists?: undefined;
|
|
124
|
+
content?: undefined;
|
|
125
|
+
patterns?: undefined;
|
|
126
|
+
lines?: undefined;
|
|
127
|
+
message?: undefined;
|
|
128
|
+
created?: undefined;
|
|
129
|
+
template?: undefined;
|
|
130
|
+
action?: undefined;
|
|
131
|
+
removed?: undefined;
|
|
132
|
+
remaining?: undefined;
|
|
133
|
+
updated?: undefined;
|
|
134
|
+
cleared?: undefined;
|
|
135
|
+
} | {
|
|
136
|
+
success: boolean;
|
|
137
|
+
path: string;
|
|
138
|
+
removed: number;
|
|
139
|
+
remaining: number;
|
|
140
|
+
exists?: undefined;
|
|
141
|
+
content?: undefined;
|
|
142
|
+
patterns?: undefined;
|
|
143
|
+
lines?: undefined;
|
|
144
|
+
message?: undefined;
|
|
145
|
+
created?: undefined;
|
|
146
|
+
template?: undefined;
|
|
147
|
+
action?: undefined;
|
|
148
|
+
added?: undefined;
|
|
149
|
+
skipped?: undefined;
|
|
150
|
+
total?: undefined;
|
|
151
|
+
updated?: undefined;
|
|
152
|
+
cleared?: undefined;
|
|
153
|
+
} | {
|
|
154
|
+
success: boolean;
|
|
155
|
+
path: string;
|
|
156
|
+
updated: boolean;
|
|
157
|
+
lines: any;
|
|
158
|
+
exists?: undefined;
|
|
159
|
+
content?: undefined;
|
|
160
|
+
patterns?: undefined;
|
|
161
|
+
message?: undefined;
|
|
162
|
+
created?: undefined;
|
|
163
|
+
template?: undefined;
|
|
164
|
+
action?: undefined;
|
|
165
|
+
added?: undefined;
|
|
166
|
+
skipped?: undefined;
|
|
167
|
+
total?: undefined;
|
|
168
|
+
removed?: undefined;
|
|
169
|
+
remaining?: undefined;
|
|
170
|
+
cleared?: undefined;
|
|
171
|
+
} | {
|
|
172
|
+
success: boolean;
|
|
173
|
+
path: string;
|
|
174
|
+
cleared: boolean;
|
|
175
|
+
template: any;
|
|
176
|
+
exists?: undefined;
|
|
177
|
+
content?: undefined;
|
|
178
|
+
patterns?: undefined;
|
|
179
|
+
lines?: undefined;
|
|
180
|
+
message?: undefined;
|
|
181
|
+
created?: undefined;
|
|
182
|
+
action?: undefined;
|
|
183
|
+
added?: undefined;
|
|
184
|
+
skipped?: undefined;
|
|
185
|
+
total?: undefined;
|
|
186
|
+
removed?: undefined;
|
|
187
|
+
remaining?: undefined;
|
|
188
|
+
updated?: undefined;
|
|
189
|
+
}>;
|
|
190
|
+
private getTemplate;
|
|
191
|
+
}
|
|
@@ -0,0 +1,354 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.GitIgnoreTool = void 0;
|
|
37
|
+
const fs = __importStar(require("fs/promises"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const errors_1 = require("../utils/errors");
|
|
40
|
+
/**
|
|
41
|
+
* Git Ignore Tool - Manage .gitignore file
|
|
42
|
+
* Create, read, add, remove patterns, and ensure .gitignore exists
|
|
43
|
+
*/
|
|
44
|
+
class GitIgnoreTool {
|
|
45
|
+
constructor() {
|
|
46
|
+
this.name = 'git-ignore';
|
|
47
|
+
this.description = 'Manage .gitignore file - create, add, remove, and update ignore patterns';
|
|
48
|
+
}
|
|
49
|
+
async handle(params, ctx) {
|
|
50
|
+
const projectPath = params.projectPath;
|
|
51
|
+
if (!projectPath) {
|
|
52
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'projectPath is required');
|
|
53
|
+
}
|
|
54
|
+
const action = params.action || 'read';
|
|
55
|
+
const gitignorePath = path.join(projectPath, '.gitignore');
|
|
56
|
+
switch (action) {
|
|
57
|
+
case 'read': {
|
|
58
|
+
try {
|
|
59
|
+
const content = await fs.readFile(gitignorePath, 'utf-8');
|
|
60
|
+
const patterns = content.split('\n').filter(l => l.trim() && !l.startsWith('#'));
|
|
61
|
+
return {
|
|
62
|
+
success: true,
|
|
63
|
+
path: gitignorePath,
|
|
64
|
+
exists: true,
|
|
65
|
+
content,
|
|
66
|
+
patterns,
|
|
67
|
+
lines: content.split('\n').length,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
if (err.code === 'ENOENT') {
|
|
72
|
+
return {
|
|
73
|
+
success: true,
|
|
74
|
+
path: gitignorePath,
|
|
75
|
+
exists: false,
|
|
76
|
+
message: '.gitignore does not exist',
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
case 'create': {
|
|
83
|
+
const template = params.template || 'default';
|
|
84
|
+
const content = this.getTemplate(template);
|
|
85
|
+
try {
|
|
86
|
+
await fs.access(gitignorePath);
|
|
87
|
+
return {
|
|
88
|
+
success: false,
|
|
89
|
+
message: '.gitignore already exists. Use "add" to add patterns.',
|
|
90
|
+
path: gitignorePath,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
await fs.writeFile(gitignorePath, content, 'utf-8');
|
|
95
|
+
return {
|
|
96
|
+
success: true,
|
|
97
|
+
path: gitignorePath,
|
|
98
|
+
created: true,
|
|
99
|
+
template,
|
|
100
|
+
lines: content.split('\n').length,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
case 'ensure': {
|
|
105
|
+
// Create if doesn't exist, otherwise do nothing
|
|
106
|
+
try {
|
|
107
|
+
await fs.access(gitignorePath);
|
|
108
|
+
return {
|
|
109
|
+
success: true,
|
|
110
|
+
path: gitignorePath,
|
|
111
|
+
exists: true,
|
|
112
|
+
action: 'none',
|
|
113
|
+
message: '.gitignore already exists',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
const template = params.template || 'default';
|
|
118
|
+
const content = this.getTemplate(template);
|
|
119
|
+
await fs.writeFile(gitignorePath, content, 'utf-8');
|
|
120
|
+
return {
|
|
121
|
+
success: true,
|
|
122
|
+
path: gitignorePath,
|
|
123
|
+
created: true,
|
|
124
|
+
template,
|
|
125
|
+
action: 'created',
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
case 'add': {
|
|
130
|
+
const patterns = params.patterns;
|
|
131
|
+
if (!patterns || !Array.isArray(patterns) || patterns.length === 0) {
|
|
132
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'patterns array is required for add action');
|
|
133
|
+
}
|
|
134
|
+
let content = '';
|
|
135
|
+
try {
|
|
136
|
+
content = await fs.readFile(gitignorePath, 'utf-8');
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
if (err.code === 'ENOENT') {
|
|
140
|
+
// Create new file
|
|
141
|
+
content = this.getTemplate('minimal');
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
throw err;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const existingPatterns = new Set(content.split('\n').map(l => l.trim()).filter(l => l && !l.startsWith('#')));
|
|
148
|
+
const newPatterns = [];
|
|
149
|
+
for (const pattern of patterns) {
|
|
150
|
+
if (!existingPatterns.has(pattern)) {
|
|
151
|
+
newPatterns.push(pattern);
|
|
152
|
+
existingPatterns.add(pattern);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (newPatterns.length > 0) {
|
|
156
|
+
const comment = params.comment ? `# ${params.comment}\n` : '';
|
|
157
|
+
content += `\n${comment}${newPatterns.join('\n')}\n`;
|
|
158
|
+
await fs.writeFile(gitignorePath, content, 'utf-8');
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
success: true,
|
|
162
|
+
path: gitignorePath,
|
|
163
|
+
added: newPatterns,
|
|
164
|
+
skipped: patterns.length - newPatterns.length,
|
|
165
|
+
total: existingPatterns.size,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
case 'remove': {
|
|
169
|
+
const patterns = params.patterns;
|
|
170
|
+
if (!patterns || !Array.isArray(patterns) || patterns.length === 0) {
|
|
171
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'patterns array is required for remove action');
|
|
172
|
+
}
|
|
173
|
+
let content = '';
|
|
174
|
+
try {
|
|
175
|
+
content = await fs.readFile(gitignorePath, 'utf-8');
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
if (err.code === 'ENOENT') {
|
|
179
|
+
return {
|
|
180
|
+
success: false,
|
|
181
|
+
message: '.gitignore does not exist',
|
|
182
|
+
path: gitignorePath,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
throw err;
|
|
186
|
+
}
|
|
187
|
+
const patternsToRemove = new Set(patterns);
|
|
188
|
+
const lines = content.split('\n');
|
|
189
|
+
const filteredLines = lines.filter(line => {
|
|
190
|
+
const trimmed = line.trim();
|
|
191
|
+
return !patternsToRemove.has(trimmed);
|
|
192
|
+
});
|
|
193
|
+
const removed = lines.length - filteredLines.length;
|
|
194
|
+
const newContent = filteredLines.join('\n');
|
|
195
|
+
await fs.writeFile(gitignorePath, newContent, 'utf-8');
|
|
196
|
+
return {
|
|
197
|
+
success: true,
|
|
198
|
+
path: gitignorePath,
|
|
199
|
+
removed,
|
|
200
|
+
remaining: filteredLines.filter(l => l.trim() && !l.startsWith('#')).length,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
case 'update': {
|
|
204
|
+
const content = params.content;
|
|
205
|
+
if (!content) {
|
|
206
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'content is required for update action');
|
|
207
|
+
}
|
|
208
|
+
await fs.writeFile(gitignorePath, content, 'utf-8');
|
|
209
|
+
return {
|
|
210
|
+
success: true,
|
|
211
|
+
path: gitignorePath,
|
|
212
|
+
updated: true,
|
|
213
|
+
lines: content.split('\n').length,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
case 'clear': {
|
|
217
|
+
const template = params.template || 'empty';
|
|
218
|
+
const content = template === 'empty' ? '' : this.getTemplate('minimal');
|
|
219
|
+
await fs.writeFile(gitignorePath, content, 'utf-8');
|
|
220
|
+
return {
|
|
221
|
+
success: true,
|
|
222
|
+
path: gitignorePath,
|
|
223
|
+
cleared: true,
|
|
224
|
+
template,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
default:
|
|
228
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', `Unsupported action: ${action}`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
getTemplate(template) {
|
|
232
|
+
const templates = {
|
|
233
|
+
empty: '',
|
|
234
|
+
minimal: `# Dependencies
|
|
235
|
+
node_modules/
|
|
236
|
+
.pnp
|
|
237
|
+
.pnp.js
|
|
238
|
+
|
|
239
|
+
# Build output
|
|
240
|
+
dist/
|
|
241
|
+
build/
|
|
242
|
+
*.log
|
|
243
|
+
`,
|
|
244
|
+
default: `# Dependencies
|
|
245
|
+
node_modules/
|
|
246
|
+
.pnp
|
|
247
|
+
.pnp.js
|
|
248
|
+
bower_components/
|
|
249
|
+
|
|
250
|
+
# Build output
|
|
251
|
+
dist/
|
|
252
|
+
build/
|
|
253
|
+
out/
|
|
254
|
+
.next/
|
|
255
|
+
.nuxt/
|
|
256
|
+
.cache/
|
|
257
|
+
.parcel-cache/
|
|
258
|
+
|
|
259
|
+
# Environment
|
|
260
|
+
.env
|
|
261
|
+
.env.local
|
|
262
|
+
.env.*.local
|
|
263
|
+
*.env
|
|
264
|
+
|
|
265
|
+
# IDE
|
|
266
|
+
.vscode/
|
|
267
|
+
.idea/
|
|
268
|
+
*.swp
|
|
269
|
+
*.swo
|
|
270
|
+
*~
|
|
271
|
+
.DS_Store
|
|
272
|
+
|
|
273
|
+
# Logs
|
|
274
|
+
logs/
|
|
275
|
+
*.log
|
|
276
|
+
npm-debug.log*
|
|
277
|
+
yarn-debug.log*
|
|
278
|
+
yarn-error.log*
|
|
279
|
+
lerna-debug.log*
|
|
280
|
+
pnpm-debug.log*
|
|
281
|
+
|
|
282
|
+
# Testing
|
|
283
|
+
coverage/
|
|
284
|
+
.nyc_output/
|
|
285
|
+
*.lcov
|
|
286
|
+
|
|
287
|
+
# Temporary
|
|
288
|
+
tmp/
|
|
289
|
+
temp/
|
|
290
|
+
*.tmp
|
|
291
|
+
`,
|
|
292
|
+
node: `# Node
|
|
293
|
+
node_modules/
|
|
294
|
+
npm-debug.log*
|
|
295
|
+
yarn-debug.log*
|
|
296
|
+
yarn-error.log*
|
|
297
|
+
lerna-debug.log*
|
|
298
|
+
pnpm-debug.log*
|
|
299
|
+
.pnpm-store/
|
|
300
|
+
.npm
|
|
301
|
+
.eslintcache
|
|
302
|
+
.node_repl_history
|
|
303
|
+
*.tgz
|
|
304
|
+
.yarn-integrity
|
|
305
|
+
.env
|
|
306
|
+
.env.test
|
|
307
|
+
.env.production
|
|
308
|
+
.cache
|
|
309
|
+
.next/
|
|
310
|
+
out/
|
|
311
|
+
build/
|
|
312
|
+
dist/
|
|
313
|
+
`,
|
|
314
|
+
python: `# Python
|
|
315
|
+
__pycache__/
|
|
316
|
+
*.py[cod]
|
|
317
|
+
*$py.class
|
|
318
|
+
*.so
|
|
319
|
+
.Python
|
|
320
|
+
build/
|
|
321
|
+
develop-eggs/
|
|
322
|
+
dist/
|
|
323
|
+
downloads/
|
|
324
|
+
eggs/
|
|
325
|
+
.eggs/
|
|
326
|
+
lib/
|
|
327
|
+
lib64/
|
|
328
|
+
parts/
|
|
329
|
+
sdist/
|
|
330
|
+
var/
|
|
331
|
+
wheels/
|
|
332
|
+
*.egg-info/
|
|
333
|
+
.installed.cfg
|
|
334
|
+
*.egg
|
|
335
|
+
MANIFEST
|
|
336
|
+
pip-log.txt
|
|
337
|
+
pip-delete-this-directory.txt
|
|
338
|
+
.tox/
|
|
339
|
+
.coverage
|
|
340
|
+
.pytest_cache/
|
|
341
|
+
.mypy_cache/
|
|
342
|
+
.dmypy.json
|
|
343
|
+
dmypy.json
|
|
344
|
+
.env
|
|
345
|
+
.venv
|
|
346
|
+
env/
|
|
347
|
+
venv/
|
|
348
|
+
ENV/
|
|
349
|
+
`,
|
|
350
|
+
};
|
|
351
|
+
return templates[template] || templates.default;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
exports.GitIgnoreTool = GitIgnoreTool;
|
package/dist/tools/gitUpdate.js
CHANGED
|
@@ -42,6 +42,7 @@ const errors_1 = require("../utils/errors");
|
|
|
42
42
|
const axios_1 = __importDefault(require("axios"));
|
|
43
43
|
const fs = __importStar(require("fs/promises"));
|
|
44
44
|
const path = __importStar(require("path"));
|
|
45
|
+
const repoHelpers_1 = require("../utils/repoHelpers");
|
|
45
46
|
/**
|
|
46
47
|
* Git Update Tool - Atualiza projeto completo em GitHub e Gitea automaticamente
|
|
47
48
|
* Modo DUAL automático com rastreabilidade completa
|
|
@@ -145,14 +146,107 @@ class GitUpdateTool {
|
|
|
145
146
|
throw err;
|
|
146
147
|
}
|
|
147
148
|
}
|
|
148
|
-
// 5. Get remotes
|
|
149
|
+
// 5. Get remotes and ensure repos exist
|
|
149
150
|
const remotes = await git.getRemotes(true);
|
|
150
|
-
|
|
151
|
-
|
|
151
|
+
let githubRemote = remotes.find(r => r.name === 'github' || r.name === 'origin');
|
|
152
|
+
let giteaRemote = remotes.find(r => r.name === 'gitea');
|
|
153
|
+
// Auto-create repos if they don't exist
|
|
154
|
+
const repoName = params.repoName || (0, repoHelpers_1.getRepoNameFromPath)(projectPath);
|
|
155
|
+
const description = params.description || `Project updated via git-update at ${new Date().toISOString()}`;
|
|
156
|
+
const isPrivate = params.private !== undefined ? params.private : true;
|
|
157
|
+
const githubOwner = params.owner || process.env.GITHUB_USERNAME;
|
|
158
|
+
const giteaOwner = process.env.GITEA_USERNAME;
|
|
159
|
+
// 5a. Ensure GitHub repo exists
|
|
160
|
+
if (ctx.providerManager.github && !githubRemote) {
|
|
161
|
+
results.traceability.updateSteps.push({
|
|
162
|
+
step: 5,
|
|
163
|
+
action: 'ensure_github_repo',
|
|
164
|
+
timestamp: new Date().toISOString(),
|
|
165
|
+
});
|
|
166
|
+
try {
|
|
167
|
+
// Try to get repo, create if doesn't exist
|
|
168
|
+
let repoExists = false;
|
|
169
|
+
try {
|
|
170
|
+
await ctx.providerManager.github.rest.repos.get({
|
|
171
|
+
owner: githubOwner,
|
|
172
|
+
repo: repoName
|
|
173
|
+
});
|
|
174
|
+
repoExists = true;
|
|
175
|
+
}
|
|
176
|
+
catch { }
|
|
177
|
+
if (!repoExists) {
|
|
178
|
+
await ctx.providerManager.github.rest.repos.createForAuthenticatedUser({
|
|
179
|
+
name: repoName,
|
|
180
|
+
description,
|
|
181
|
+
private: isPrivate,
|
|
182
|
+
auto_init: false,
|
|
183
|
+
});
|
|
184
|
+
results.traceability.updateSteps.push({
|
|
185
|
+
step: 5.1,
|
|
186
|
+
action: 'created_github_repo',
|
|
187
|
+
timestamp: new Date().toISOString(),
|
|
188
|
+
repo: repoName,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
// Add remote
|
|
192
|
+
await git.addRemote('github', `https://github.com/${githubOwner}/${repoName}.git`);
|
|
193
|
+
githubRemote = { name: 'github', refs: { push: `https://github.com/${githubOwner}/${repoName}.git`, fetch: '' } };
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
results.traceability.errors.push({
|
|
197
|
+
provider: 'github',
|
|
198
|
+
action: 'ensure_repo',
|
|
199
|
+
error: err.message,
|
|
200
|
+
timestamp: new Date().toISOString(),
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// 5b. Ensure Gitea repo exists
|
|
205
|
+
if (ctx.providerManager.giteaBaseUrl && !giteaRemote) {
|
|
206
|
+
results.traceability.updateSteps.push({
|
|
207
|
+
step: 5.5,
|
|
208
|
+
action: 'ensure_gitea_repo',
|
|
209
|
+
timestamp: new Date().toISOString(),
|
|
210
|
+
});
|
|
211
|
+
try {
|
|
212
|
+
// Try to get repo, create if doesn't exist
|
|
213
|
+
let repoExists = false;
|
|
214
|
+
try {
|
|
215
|
+
await axios_1.default.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repoName}`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
216
|
+
repoExists = true;
|
|
217
|
+
}
|
|
218
|
+
catch { }
|
|
219
|
+
if (!repoExists) {
|
|
220
|
+
await axios_1.default.post(`${ctx.providerManager.giteaBaseUrl}/api/v1/user/repos`, {
|
|
221
|
+
name: repoName,
|
|
222
|
+
description,
|
|
223
|
+
private: isPrivate,
|
|
224
|
+
auto_init: false,
|
|
225
|
+
}, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
226
|
+
results.traceability.updateSteps.push({
|
|
227
|
+
step: 5.6,
|
|
228
|
+
action: 'created_gitea_repo',
|
|
229
|
+
timestamp: new Date().toISOString(),
|
|
230
|
+
repo: repoName,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
// Add remote
|
|
234
|
+
await git.addRemote('gitea', `${ctx.providerManager.giteaBaseUrl}/${giteaOwner}/${repoName}.git`);
|
|
235
|
+
giteaRemote = { name: 'gitea', refs: { push: `${ctx.providerManager.giteaBaseUrl}/${giteaOwner}/${repoName}.git`, fetch: '' } };
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
results.traceability.errors.push({
|
|
239
|
+
provider: 'gitea',
|
|
240
|
+
action: 'ensure_repo',
|
|
241
|
+
error: err.message,
|
|
242
|
+
timestamp: new Date().toISOString(),
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
152
246
|
// 6. Push to GITHUB
|
|
153
247
|
if (githubRemote && ctx.providerManager.github) {
|
|
154
248
|
results.traceability.updateSteps.push({
|
|
155
|
-
step:
|
|
249
|
+
step: 6,
|
|
156
250
|
action: 'push_to_github',
|
|
157
251
|
timestamp: new Date().toISOString(),
|
|
158
252
|
remote: githubRemote.name,
|
|
@@ -174,7 +268,7 @@ class GitUpdateTool {
|
|
|
174
268
|
try {
|
|
175
269
|
if (results.traceability.commit?.hash) {
|
|
176
270
|
const githubOwner = params.owner || process.env.GITHUB_USERNAME;
|
|
177
|
-
const repo =
|
|
271
|
+
const repo = (0, repoHelpers_1.getRepoNameFromPath)(projectPath);
|
|
178
272
|
const commitInfo = await ctx.providerManager.github.rest.repos.getCommit({
|
|
179
273
|
owner: githubOwner,
|
|
180
274
|
repo,
|
|
@@ -204,7 +298,7 @@ class GitUpdateTool {
|
|
|
204
298
|
// 7. Push to GITEA
|
|
205
299
|
if (giteaRemote && ctx.providerManager.giteaBaseUrl) {
|
|
206
300
|
results.traceability.updateSteps.push({
|
|
207
|
-
step:
|
|
301
|
+
step: 7,
|
|
208
302
|
action: 'push_to_gitea',
|
|
209
303
|
timestamp: new Date().toISOString(),
|
|
210
304
|
remote: giteaRemote.name,
|
|
@@ -226,7 +320,7 @@ class GitUpdateTool {
|
|
|
226
320
|
try {
|
|
227
321
|
if (results.traceability.commit?.hash) {
|
|
228
322
|
const giteaOwner = params.owner || process.env.GITEA_USERNAME;
|
|
229
|
-
const repo =
|
|
323
|
+
const repo = (0, repoHelpers_1.getRepoNameFromPath)(projectPath);
|
|
230
324
|
const commitInfo = await axios_1.default.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/git/commits/${results.traceability.commit.hash}`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
231
325
|
results.providers.gitea.commitVerified = true;
|
|
232
326
|
results.providers.gitea.commitUrl = commitInfo.data.html_url;
|
|
@@ -302,7 +396,7 @@ class GitUpdateTool {
|
|
|
302
396
|
// Pegar nome do repo dos remotes do Git ao invés do nome da pasta
|
|
303
397
|
const git = (0, simple_git_1.default)(projectPath);
|
|
304
398
|
const remotes = await git.getRemotes(true);
|
|
305
|
-
let repoName =
|
|
399
|
+
let repoName = (0, repoHelpers_1.getRepoNameFromPath)(projectPath); // fallback
|
|
306
400
|
if (remotes.length > 0) {
|
|
307
401
|
const githubRemote = remotes.find(r => r.name === 'github') || remotes.find(r => r.name === 'origin') || remotes[0];
|
|
308
402
|
const match = githubRemote.refs.push?.match(/\/([^\/]+?)(\.git)?$/);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andrebuzeli/git-mcp",
|
|
3
|
-
"version": "7.1
|
|
3
|
+
"version": "7.2.1",
|
|
4
4
|
"description": "Professional MCP server for Git operations - STDIO UNIVERSAL: works in ANY IDE (Cursor, VSCode, Claude Desktop). Fully autonomous DUAL execution (GitHub + Gitea APIs) with automatic username detection. All tools execute on BOTH providers simultaneously. No manual parameters needed.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -51,29 +51,28 @@
|
|
|
51
51
|
},
|
|
52
52
|
"homepage": "https://github.com/andrebuzeli/git-mcp#readme",
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@modelcontextprotocol/sdk": "^0.4.0",
|
|
55
54
|
"@octokit/rest": "^20.0.0",
|
|
55
|
+
"ajv": "^8.12.0",
|
|
56
56
|
"axios": "^1.6.0",
|
|
57
|
-
"simple-git": "^3.20.0",
|
|
58
|
-
"express": "^4.18.2",
|
|
59
57
|
"body-parser": "^1.20.2",
|
|
60
|
-
"
|
|
58
|
+
"express": "^4.18.2",
|
|
59
|
+
"simple-git": "^3.20.0"
|
|
61
60
|
},
|
|
62
61
|
"devDependencies": {
|
|
63
|
-
"semantic-release": "^20.1.0",
|
|
64
|
-
"@semantic-release/npm": "^10.0.0",
|
|
65
|
-
"@semantic-release/github": "^9.0.0",
|
|
66
62
|
"@semantic-release/commit-analyzer": "^10.0.0",
|
|
63
|
+
"@semantic-release/github": "^9.0.0",
|
|
64
|
+
"@semantic-release/npm": "^10.0.0",
|
|
67
65
|
"@semantic-release/release-notes-generator": "^10.0.0",
|
|
68
|
-
"@types/node": "^20.0.0",
|
|
69
|
-
"@types/express": "^4.17.17",
|
|
70
66
|
"@types/body-parser": "^1.19.2",
|
|
67
|
+
"@types/express": "^4.17.17",
|
|
71
68
|
"@types/jest": "^29.0.0",
|
|
72
|
-
"
|
|
69
|
+
"@types/node": "^20.0.0",
|
|
73
70
|
"jest": "^29.0.0",
|
|
74
|
-
"
|
|
71
|
+
"rimraf": "^5.0.0",
|
|
72
|
+
"semantic-release": "^20.1.0",
|
|
75
73
|
"ts-jest": "^29.0.0",
|
|
76
|
-
"
|
|
74
|
+
"ts-node": "^10.0.0",
|
|
75
|
+
"typescript": "^5.0.0"
|
|
77
76
|
},
|
|
78
77
|
"engines": {
|
|
79
78
|
"node": ">=18.0.0"
|