@andrebuzeli/git-mcp 15.9.0 → 15.9.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andrebuzeli/git-mcp",
3
- "version": "15.9.0",
3
+ "version": "15.9.1",
4
4
  "private": false,
5
5
  "description": "MCP server para Git com operações locais e sincronização paralela GitHub/Gitea",
6
6
  "license": "MIT",
@@ -16,14 +16,14 @@ export const ERROR_CODES = {
16
16
  suggestion: "Verifique se GITHUB_TOKEN está configurado e é válido. Gere um novo token em github.com/settings/tokens"
17
17
  },
18
18
  AUTH_GITEA_INVALID: {
19
- code: "AUTH_GITEA_INVALID",
19
+ code: "AUTH_GITEA_INVALID",
20
20
  suggestion: "Verifique se GITEA_TOKEN e GITEA_URL estão configurados corretamente"
21
21
  },
22
22
  AUTH_NO_PERMISSION: {
23
23
  code: "AUTH_NO_PERMISSION",
24
24
  suggestion: "Token não tem permissão para esta operação. Verifique os scopes do token"
25
25
  },
26
-
26
+
27
27
  // Repo
28
28
  REPO_NOT_FOUND: {
29
29
  code: "REPO_NOT_FOUND",
@@ -37,7 +37,7 @@ export const ERROR_CODES = {
37
37
  code: "REPO_ALREADY_EXISTS",
38
38
  suggestion: "Repositório já existe. Use action='ensure' para verificar ou escolha outro nome"
39
39
  },
40
-
40
+
41
41
  // Git Local
42
42
  NOT_A_GIT_REPO: {
43
43
  code: "NOT_A_GIT_REPO",
@@ -55,7 +55,7 @@ export const ERROR_CODES = {
55
55
  code: "NOTHING_TO_PUSH",
56
56
  suggestion: "Nenhuma mudança para push. Faça commits primeiro"
57
57
  },
58
-
58
+
59
59
  // Branches
60
60
  BRANCH_NOT_FOUND: {
61
61
  code: "BRANCH_NOT_FOUND",
@@ -69,7 +69,7 @@ export const ERROR_CODES = {
69
69
  code: "CANNOT_DELETE_CURRENT",
70
70
  suggestion: "Não pode deletar branch atual. Faça checkout para outra branch primeiro"
71
71
  },
72
-
72
+
73
73
  // Tags
74
74
  TAG_NOT_FOUND: {
75
75
  code: "TAG_NOT_FOUND",
@@ -79,7 +79,7 @@ export const ERROR_CODES = {
79
79
  code: "TAG_ALREADY_EXISTS",
80
80
  suggestion: "Tag já existe. Use outro nome ou delete a existente primeiro"
81
81
  },
82
-
82
+
83
83
  // Refs
84
84
  REF_NOT_FOUND: {
85
85
  code: "REF_NOT_FOUND",
@@ -89,7 +89,7 @@ export const ERROR_CODES = {
89
89
  code: "INSUFFICIENT_HISTORY",
90
90
  suggestion: "Histórico insuficiente para HEAD~N. Use action='log' para verificar quantos commits existem"
91
91
  },
92
-
92
+
93
93
  // Stash
94
94
  NOTHING_TO_STASH: {
95
95
  code: "NOTHING_TO_STASH",
@@ -99,7 +99,7 @@ export const ERROR_CODES = {
99
99
  code: "STASH_NOT_FOUND",
100
100
  suggestion: "Stash não encontrado. Use action='list' para ver stashes disponíveis"
101
101
  },
102
-
102
+
103
103
  // Remote
104
104
  REMOTE_NOT_FOUND: {
105
105
  code: "REMOTE_NOT_FOUND",
@@ -113,7 +113,7 @@ export const ERROR_CODES = {
113
113
  code: "MERGE_CONFLICT",
114
114
  suggestion: "Conflito de merge. Resolva conflitos manualmente e faça novo commit"
115
115
  },
116
-
116
+
117
117
  // Network
118
118
  NETWORK_TIMEOUT: {
119
119
  code: "NETWORK_TIMEOUT",
@@ -123,7 +123,7 @@ export const ERROR_CODES = {
123
123
  code: "RATE_LIMIT",
124
124
  suggestion: "Rate limit excedido. Aguarde alguns minutos antes de tentar novamente"
125
125
  },
126
-
126
+
127
127
  // Validation
128
128
  VALIDATION_ERROR: {
129
129
  code: "VALIDATION_ERROR",
@@ -133,13 +133,13 @@ export const ERROR_CODES = {
133
133
  code: "MISSING_PARAMETER",
134
134
  suggestion: "Parâmetro obrigatório faltando"
135
135
  },
136
-
136
+
137
137
  // Files
138
138
  FILE_NOT_FOUND: {
139
139
  code: "FILE_NOT_FOUND",
140
140
  suggestion: "Arquivo não encontrado no repositório. Use action='list' para ver arquivos disponíveis"
141
141
  },
142
-
142
+
143
143
  // Issues/PRs
144
144
  ISSUE_NOT_FOUND: {
145
145
  code: "ISSUE_NOT_FOUND",
@@ -149,7 +149,7 @@ export const ERROR_CODES = {
149
149
  code: "PR_NOT_FOUND",
150
150
  suggestion: "Pull Request não encontrado. Use action='list' para ver PRs disponíveis"
151
151
  },
152
-
152
+
153
153
  // Filesystem
154
154
  DISK_FULL: {
155
155
  code: "DISK_FULL",
@@ -163,7 +163,7 @@ export const ERROR_CODES = {
163
163
  code: "PERMISSION_DENIED_FS",
164
164
  suggestion: "Sem permissão para acessar arquivo/diretório. Verifique permissões do sistema de arquivos"
165
165
  },
166
-
166
+
167
167
  // Git Corruption
168
168
  GIT_CORRUPTED: {
169
169
  code: "GIT_CORRUPTED",
@@ -171,9 +171,9 @@ export const ERROR_CODES = {
171
171
  },
172
172
  GIT_LOCK_FILE: {
173
173
  code: "GIT_LOCK_FILE",
174
- suggestion: "Arquivo de lock presente (.git/index.lock). Outro processo git pode estar rodando. Se não, delete o arquivo de lock manualmente"
174
+ suggestion: "Arquivo de lock presente (.git/index.lock). Outro processo git pode estar rodando (comum em drives de rede). O sistema tentou auto-corrigir, mas se persistir, delete o arquivo de lock manualmente."
175
175
  },
176
-
176
+
177
177
  // Remote/URL
178
178
  INVALID_REMOTE_URL: {
179
179
  code: "INVALID_REMOTE_URL",
@@ -187,7 +187,7 @@ export const ERROR_CODES = {
187
187
  code: "SSL_ERROR",
188
188
  suggestion: "Erro de SSL/TLS. Verifique certificados ou use http.sslVerify=false (não recomendado)"
189
189
  },
190
-
190
+
191
191
  // Generic
192
192
  UNKNOWN_ERROR: {
193
193
  code: "UNKNOWN_ERROR",
@@ -207,14 +207,14 @@ export function createError(errorCode, details = {}) {
207
207
  // Função para mapear erros externos para códigos internos
208
208
  export function mapExternalError(error, context = {}) {
209
209
  const msg = error?.message?.toLowerCase() || String(error).toLowerCase();
210
-
210
+
211
211
  // GitHub/Gitea API errors
212
212
  if (msg.includes("bad credentials") || msg.includes("401")) {
213
- return context.provider === "gitea"
213
+ return context.provider === "gitea"
214
214
  ? createError("AUTH_GITEA_INVALID", { originalError: msg })
215
215
  : createError("AUTH_GITHUB_INVALID", { originalError: msg });
216
216
  }
217
-
217
+
218
218
  if (msg.includes("not found") || msg.includes("404")) {
219
219
  if (context.type === "repo") return createError("REPO_NOT_FOUND", { repo: context.repo, originalError: msg });
220
220
  if (context.type === "branch") return createError("BRANCH_NOT_FOUND", { branch: context.branch, originalError: msg });
@@ -224,116 +224,116 @@ export function mapExternalError(error, context = {}) {
224
224
  if (context.type === "pr") return createError("PR_NOT_FOUND", { number: context.number, originalError: msg });
225
225
  return createError("REF_NOT_FOUND", { ref: context.ref, originalError: msg });
226
226
  }
227
-
227
+
228
228
  if (msg.includes("permission") || msg.includes("403") || msg.includes("forbidden")) {
229
229
  return createError("AUTH_NO_PERMISSION", { originalError: msg });
230
230
  }
231
-
231
+
232
232
  if (msg.includes("rate limit") || msg.includes("429")) {
233
233
  return createError("RATE_LIMIT", { originalError: msg });
234
234
  }
235
-
235
+
236
236
  if (msg.includes("timeout") || msg.includes("etimedout") || msg.includes("econnrefused")) {
237
237
  return createError("NETWORK_TIMEOUT", { originalError: msg });
238
238
  }
239
-
239
+
240
240
  if (msg.includes("push rejected") || msg.includes("non-fast-forward")) {
241
241
  return createError("PUSH_REJECTED", { originalError: msg });
242
242
  }
243
-
243
+
244
244
  if (msg.includes("conflict")) {
245
245
  return createError("MERGE_CONFLICT", { originalError: msg });
246
246
  }
247
-
247
+
248
248
  if (msg.includes("already exists")) {
249
249
  if (context.type === "repo") return createError("REPO_ALREADY_EXISTS", { repo: context.repo, originalError: msg });
250
250
  if (context.type === "branch") return createError("BRANCH_ALREADY_EXISTS", { branch: context.branch, originalError: msg });
251
251
  if (context.type === "tag") return createError("TAG_ALREADY_EXISTS", { tag: context.tag, originalError: msg });
252
252
  return createError("BRANCH_ALREADY_EXISTS", { originalError: msg });
253
253
  }
254
-
254
+
255
255
  // isomorphic-git specific errors
256
256
  if (msg.includes("could not find") && msg.includes("head~")) {
257
257
  return createError("INSUFFICIENT_HISTORY", { originalError: msg });
258
258
  }
259
-
259
+
260
260
  if (msg.includes("is not a git repository") || msg.includes("could not find .git")) {
261
261
  return createError("NOT_A_GIT_REPO", { path: context.path, originalError: msg });
262
262
  }
263
-
263
+
264
264
  if (msg.includes("nothing to commit") || msg.includes("working tree clean")) {
265
265
  return createError("NOTHING_TO_COMMIT", { originalError: msg });
266
266
  }
267
-
267
+
268
268
  if (msg.includes("could not resolve ref") || msg.includes("invalid reference")) {
269
269
  return createError("REF_NOT_FOUND", { originalError: msg });
270
270
  }
271
-
271
+
272
272
  if (msg.includes("branch") && (msg.includes("does not exist") || msg.includes("not found"))) {
273
273
  return createError("BRANCH_NOT_FOUND", { originalError: msg });
274
274
  }
275
-
275
+
276
276
  if (msg.includes("tag") && (msg.includes("does not exist") || msg.includes("not found"))) {
277
277
  return createError("TAG_NOT_FOUND", { originalError: msg });
278
278
  }
279
-
279
+
280
280
  if (msg.includes("no commits") || msg.includes("unknown revision")) {
281
281
  return createError("NO_COMMITS", { originalError: msg });
282
282
  }
283
-
283
+
284
284
  if (msg.includes("nada para stash") || msg.includes("nothing to stash") || msg.includes("no local changes")) {
285
285
  return createError("NOTHING_TO_STASH", { originalError: msg });
286
286
  }
287
-
287
+
288
288
  if (msg.includes("stash") && (msg.includes("not found") || msg.includes("não encontrado"))) {
289
289
  return createError("STASH_NOT_FOUND", { originalError: msg });
290
290
  }
291
-
291
+
292
292
  if (msg.includes("author") || msg.includes("committer")) {
293
- return createError("VALIDATION_ERROR", {
294
- message: "Configure user.name e user.email",
293
+ return createError("VALIDATION_ERROR", {
294
+ message: "Configure user.name e user.email",
295
295
  suggestion: "Use git-config para configurar ou forneça tokens de provider",
296
- originalError: msg
296
+ originalError: msg
297
297
  });
298
298
  }
299
-
299
+
300
300
  // Filesystem errors
301
301
  if (msg.includes("enospc") || msg.includes("no space left") || msg.includes("disk full")) {
302
302
  return createError("DISK_FULL", { originalError: msg });
303
303
  }
304
-
304
+
305
305
  if (msg.includes("file too large") || msg.includes("exceeds") || msg.includes("size limit")) {
306
306
  return createError("FILE_TOO_LARGE", { originalError: msg });
307
307
  }
308
-
308
+
309
309
  if (msg.includes("eacces") || msg.includes("eperm") || (msg.includes("permission") && msg.includes("denied"))) {
310
310
  return createError("PERMISSION_DENIED_FS", { originalError: msg });
311
311
  }
312
-
312
+
313
313
  // Git corruption
314
- if (msg.includes("corrupt") || msg.includes("bad object") || msg.includes("broken") ||
315
- msg.includes("missing object") || msg.includes("invalid sha") || msg.includes("fatal: packed")) {
314
+ if (msg.includes("corrupt") || msg.includes("bad object") || msg.includes("broken") ||
315
+ msg.includes("missing object") || msg.includes("invalid sha") || msg.includes("fatal: packed")) {
316
316
  return createError("GIT_CORRUPTED", { originalError: msg });
317
317
  }
318
-
318
+
319
319
  if (msg.includes(".git/index.lock") || msg.includes("unable to create") && msg.includes("lock")) {
320
320
  return createError("GIT_LOCK_FILE", { originalError: msg });
321
321
  }
322
-
322
+
323
323
  // Remote/URL errors
324
324
  if (msg.includes("invalid url") || msg.includes("malformed") || msg.includes("not a valid")) {
325
325
  return createError("INVALID_REMOTE_URL", { originalError: msg });
326
326
  }
327
-
328
- if (msg.includes("could not resolve host") || msg.includes("name resolution") ||
329
- msg.includes("no address") || msg.includes("unreachable")) {
327
+
328
+ if (msg.includes("could not resolve host") || msg.includes("name resolution") ||
329
+ msg.includes("no address") || msg.includes("unreachable")) {
330
330
  return createError("REMOTE_UNREACHABLE", { originalError: msg });
331
331
  }
332
-
332
+
333
333
  if (msg.includes("ssl") || msg.includes("certificate") || msg.includes("tls")) {
334
334
  return createError("SSL_ERROR", { originalError: msg });
335
335
  }
336
-
336
+
337
337
  // Generic fallback
338
338
  return createError("UNKNOWN_ERROR", { originalError: msg, context });
339
339
  }
@@ -347,27 +347,27 @@ export const NEXT_ACTIONS = {
347
347
  "workflow:add": { tool: "git-workflow", action: "commit", hint: "Crie um commit com uma mensagem descritiva" },
348
348
  "workflow:commit": { tool: "git-workflow", action: "push", hint: "Envie as mudanças para GitHub/Gitea" },
349
349
  "workflow:push": null, // Fluxo completo
350
-
350
+
351
351
  // git-branches
352
352
  "branches:create": { tool: "git-branches", action: "checkout", hint: "Mude para a nova branch criada" },
353
353
  "branches:checkout": { tool: "git-workflow", action: "status", hint: "Verifique o estado da branch" },
354
354
  "branches:delete": null,
355
-
355
+
356
356
  // git-stash
357
357
  "stash:save": { tool: "git-branches", action: "checkout", hint: "Agora pode trocar de branch com segurança" },
358
358
  "stash:pop": { tool: "git-workflow", action: "status", hint: "Verifique as mudanças restauradas" },
359
-
359
+
360
360
  // git-merge
361
361
  "merge:merge": { tool: "git-workflow", action: "push", hint: "Envie o merge para os remotes" },
362
-
362
+
363
363
  // git-tags
364
364
  "tags:create": { tool: "git-tags", action: "push", hint: "Envie a tag para os remotes" },
365
365
  "tags:push": null,
366
-
366
+
367
367
  // git-remote
368
368
  "remote:ensure": { tool: "git-workflow", action: "push", hint: "Agora pode fazer push" },
369
369
  "remote:release-create": null,
370
-
370
+
371
371
  // git-sync
372
372
  "sync:pull": { tool: "git-workflow", action: "status", hint: "Verifique mudanças após pull" },
373
373
  "sync:fetch": { tool: "git-sync", action: "pull", hint: "Aplique as mudanças baixadas" },
@@ -388,7 +388,7 @@ export function getNextAction(tool, action) {
388
388
  export function asToolResult(result, options = {}) {
389
389
  const { tool, action } = options;
390
390
  const response = { ...result };
391
-
391
+
392
392
  // Adicionar próxima ação sugerida se disponível
393
393
  if (tool && action) {
394
394
  const next = getNextAction(tool, action);
@@ -398,7 +398,7 @@ export function asToolResult(result, options = {}) {
398
398
  response._flowComplete = true;
399
399
  }
400
400
  }
401
-
401
+
402
402
  return {
403
403
  content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
404
404
  isError: false,
@@ -28,7 +28,11 @@ export class GitAdapter {
28
28
  constructor(providerManager) {
29
29
  this.pm = providerManager;
30
30
  this.gitPath = "git"; // Initial default
31
- this.gitEnv = { ...process.env, LANG: "en_US.UTF-8" }; // Base env
31
+ this.gitEnv = {
32
+ ...process.env,
33
+ LANG: "en_US.UTF-8",
34
+ GIT_OPTIONAL_LOCKS: "0", // Evita operações de background que seguram lock (importante para drives de rede)
35
+ };
32
36
  this.timeout = GIT_TIMEOUT;
33
37
  this.resolvePromise = this._resolveGit();
34
38
  }
@@ -145,13 +149,22 @@ export class GitAdapter {
145
149
 
146
150
  if (shouldDelete) {
147
151
  console.error("[GitAdapter] Auto-fix: removing stale .git/index.lock...");
148
- fs.unlinkSync(lockPath);
149
- // Espera um pouco para o sistema de arquivos (especialmente SMB) registrar
150
- await new Promise(r => setTimeout(r, 1000));
151
- return await this._exec(dir, args, { ...options, _lockRetried: true });
152
+ try {
153
+ fs.unlinkSync(lockPath);
154
+ } catch (unlinkErr) {
155
+ console.error("[GitAdapter] Failed to delete lock file:", unlinkErr.message);
156
+ // Set shouldDelete to false so we don't try to retry if deletion failed
157
+ shouldDelete = false;
158
+ }
159
+
160
+ if (shouldDelete) {
161
+ // Espera um pouco para o sistema de arquivos (especialmente SMB) registrar
162
+ await new Promise(r => setTimeout(r, 2000));
163
+ return await this._exec(dir, args, { ...options, _lockRetried: true });
164
+ }
152
165
  }
153
- } catch (unlinkError) {
154
- console.error("[GitAdapter] Failed to check/remove lock file:", unlinkError.message);
166
+ } catch (fsError) {
167
+ console.error("[GitAdapter] Error handling lock file:", fsError.message);
155
168
  // Continua para lançar o erro original
156
169
  }
157
170
  }