@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 +1 -1
- package/src/utils/errors.js +60 -60
- package/src/utils/gitAdapter.js +20 -7
package/package.json
CHANGED
package/src/utils/errors.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
package/src/utils/gitAdapter.js
CHANGED
|
@@ -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 = {
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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 (
|
|
154
|
-
console.error("[GitAdapter]
|
|
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
|
}
|