@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 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 21 Git tools
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: '6.3.2',
82
+ version: '7.2.0',
81
83
  });
82
84
  // Register tool list handler
83
85
  server.setRequestHandler(types_1.ListToolsRequestSchema, async () => {
@@ -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 || path.basename(projectPath);
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 || path.basename(projectPath);
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;
@@ -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
- const githubRemote = remotes.find(r => r.name === 'github' || r.name === 'origin');
151
- const giteaRemote = remotes.find(r => r.name === 'gitea');
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: 5,
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 = path.basename(projectPath);
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: 6,
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 = path.basename(projectPath);
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 = path.basename(projectPath).replace(/\s+/g, '-'); // fallback
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.0",
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
- "ajv": "^8.12.0"
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
- "typescript": "^5.0.0",
69
+ "@types/node": "^20.0.0",
73
70
  "jest": "^29.0.0",
74
- "ts-node": "^10.0.0",
71
+ "rimraf": "^5.0.0",
72
+ "semantic-release": "^20.1.0",
75
73
  "ts-jest": "^29.0.0",
76
- "rimraf": "^5.0.0"
74
+ "ts-node": "^10.0.0",
75
+ "typescript": "^5.0.0"
77
76
  },
78
77
  "engines": {
79
78
  "node": ">=18.0.0"