@andrebuzeli/git-mcp 15.8.3 → 15.8.5

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.
@@ -1,431 +1,431 @@
1
- // Sistema de Erros Melhorado para AI Agents
2
-
3
- export class MCPError extends Error {
4
- constructor(code, message, data) {
5
- super(message);
6
- this.code = code;
7
- this.data = data;
8
- }
9
- }
10
-
11
- // Códigos de erro padronizados com sugestões para AI
12
- export const ERROR_CODES = {
13
- // Auth
14
- AUTH_GITHUB_INVALID: {
15
- code: "AUTH_GITHUB_INVALID",
16
- suggestion: "Verifique se GITHUB_TOKEN está configurado e é válido. Gere um novo token em github.com/settings/tokens"
17
- },
18
- AUTH_GITEA_INVALID: {
19
- code: "AUTH_GITEA_INVALID",
20
- suggestion: "Verifique se GITEA_TOKEN e GITEA_URL estão configurados corretamente"
21
- },
22
- AUTH_NO_PERMISSION: {
23
- code: "AUTH_NO_PERMISSION",
24
- suggestion: "Token não tem permissão para esta operação. Verifique os scopes do token"
25
- },
26
-
27
- // Repo
28
- REPO_NOT_FOUND: {
29
- code: "REPO_NOT_FOUND",
30
- suggestion: "Repositório não existe. Use action='init' com createIfMissing=true para criar"
31
- },
32
- REPO_NO_WRITE: {
33
- code: "REPO_NO_WRITE",
34
- suggestion: "Sem permissão de escrita. Verifique se você é owner ou colaborador do repositório"
35
- },
36
- REPO_ALREADY_EXISTS: {
37
- code: "REPO_ALREADY_EXISTS",
38
- suggestion: "Repositório já existe. Use action='ensure' para verificar ou escolha outro nome"
39
- },
40
-
41
- // Git Local
42
- NOT_A_GIT_REPO: {
43
- code: "NOT_A_GIT_REPO",
44
- suggestion: "Diretório não é um repositório git. Use action='init' primeiro para inicializar"
45
- },
46
- NO_COMMITS: {
47
- code: "NO_COMMITS",
48
- suggestion: "Repositório não tem commits. Use action='add' e depois action='commit' primeiro"
49
- },
50
- NOTHING_TO_COMMIT: {
51
- code: "NOTHING_TO_COMMIT",
52
- suggestion: "Working tree limpa, nada para commitar. Modifique arquivos primeiro"
53
- },
54
- NOTHING_TO_PUSH: {
55
- code: "NOTHING_TO_PUSH",
56
- suggestion: "Nenhuma mudança para push. Faça commits primeiro"
57
- },
58
-
59
- // Branches
60
- BRANCH_NOT_FOUND: {
61
- code: "BRANCH_NOT_FOUND",
62
- suggestion: "Branch não existe. Use action='list' para ver branches disponíveis"
63
- },
64
- BRANCH_ALREADY_EXISTS: {
65
- code: "BRANCH_ALREADY_EXISTS",
66
- suggestion: "Branch já existe. Use outro nome ou delete a existente primeiro"
67
- },
68
- CANNOT_DELETE_CURRENT: {
69
- code: "CANNOT_DELETE_CURRENT",
70
- suggestion: "Não pode deletar branch atual. Faça checkout para outra branch primeiro"
71
- },
72
-
73
- // Tags
74
- TAG_NOT_FOUND: {
75
- code: "TAG_NOT_FOUND",
76
- suggestion: "Tag não existe. Use action='list' para ver tags disponíveis"
77
- },
78
- TAG_ALREADY_EXISTS: {
79
- code: "TAG_ALREADY_EXISTS",
80
- suggestion: "Tag já existe. Use outro nome ou delete a existente primeiro"
81
- },
82
-
83
- // Refs
84
- REF_NOT_FOUND: {
85
- code: "REF_NOT_FOUND",
86
- suggestion: "Referência não encontrada. Verifique se o commit/branch/tag existe. Use action='log' para ver histórico"
87
- },
88
- INSUFFICIENT_HISTORY: {
89
- code: "INSUFFICIENT_HISTORY",
90
- suggestion: "Histórico insuficiente para HEAD~N. Use action='log' para verificar quantos commits existem"
91
- },
92
-
93
- // Stash
94
- NOTHING_TO_STASH: {
95
- code: "NOTHING_TO_STASH",
96
- suggestion: "Working tree limpa, nada para stash. Modifique arquivos primeiro"
97
- },
98
- STASH_NOT_FOUND: {
99
- code: "STASH_NOT_FOUND",
100
- suggestion: "Stash não encontrado. Use action='list' para ver stashes disponíveis"
101
- },
102
-
103
- // Remote
104
- REMOTE_NOT_FOUND: {
105
- code: "REMOTE_NOT_FOUND",
106
- suggestion: "Remote não configurado. Use action='ensure-remotes' para configurar"
107
- },
108
- PUSH_REJECTED: {
109
- code: "PUSH_REJECTED",
110
- suggestion: "Push rejeitado (histórico divergente). Use force=true para forçar, ou faça pull primeiro"
111
- },
112
- MERGE_CONFLICT: {
113
- code: "MERGE_CONFLICT",
114
- suggestion: "Conflito de merge. Resolva conflitos manualmente e faça novo commit"
115
- },
116
-
117
- // Network
118
- NETWORK_TIMEOUT: {
119
- code: "NETWORK_TIMEOUT",
120
- suggestion: "Timeout de conexão. Verifique sua internet e tente novamente"
121
- },
122
- RATE_LIMIT: {
123
- code: "RATE_LIMIT",
124
- suggestion: "Rate limit excedido. Aguarde alguns minutos antes de tentar novamente"
125
- },
126
-
127
- // Validation
128
- VALIDATION_ERROR: {
129
- code: "VALIDATION_ERROR",
130
- suggestion: "Parâmetros inválidos. Verifique os parâmetros obrigatórios da action"
131
- },
132
- MISSING_PARAMETER: {
133
- code: "MISSING_PARAMETER",
134
- suggestion: "Parâmetro obrigatório faltando"
135
- },
136
-
137
- // Files
138
- FILE_NOT_FOUND: {
139
- code: "FILE_NOT_FOUND",
140
- suggestion: "Arquivo não encontrado no repositório. Use action='list' para ver arquivos disponíveis"
141
- },
142
-
143
- // Issues/PRs
144
- ISSUE_NOT_FOUND: {
145
- code: "ISSUE_NOT_FOUND",
146
- suggestion: "Issue não encontrada. Use action='list' para ver issues disponíveis"
147
- },
148
- PR_NOT_FOUND: {
149
- code: "PR_NOT_FOUND",
150
- suggestion: "Pull Request não encontrado. Use action='list' para ver PRs disponíveis"
151
- },
152
-
153
- // Filesystem
154
- DISK_FULL: {
155
- code: "DISK_FULL",
156
- suggestion: "Disco cheio. Libere espaço e tente novamente"
157
- },
158
- FILE_TOO_LARGE: {
159
- code: "FILE_TOO_LARGE",
160
- suggestion: "Arquivo muito grande para Git. Considere usar Git LFS para arquivos grandes"
161
- },
162
- PERMISSION_DENIED_FS: {
163
- code: "PERMISSION_DENIED_FS",
164
- suggestion: "Sem permissão para acessar arquivo/diretório. Verifique permissões do sistema de arquivos"
165
- },
166
-
167
- // Git Corruption
168
- GIT_CORRUPTED: {
169
- code: "GIT_CORRUPTED",
170
- suggestion: "Repositório Git corrompido. Tente: 1) git fsck --full, 2) git gc --prune=now, ou 3) re-clone do remote"
171
- },
172
- GIT_LOCK_FILE: {
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"
175
- },
176
-
177
- // Remote/URL
178
- INVALID_REMOTE_URL: {
179
- code: "INVALID_REMOTE_URL",
180
- suggestion: "URL do remote inválida. Verifique o formato: https://github.com/user/repo.git ou git@github.com:user/repo.git"
181
- },
182
- REMOTE_UNREACHABLE: {
183
- code: "REMOTE_UNREACHABLE",
184
- suggestion: "Remote inacessível. Verifique: 1) URL está correta, 2) conexão de internet, 3) firewall/proxy"
185
- },
186
- SSL_ERROR: {
187
- code: "SSL_ERROR",
188
- suggestion: "Erro de SSL/TLS. Verifique certificados ou use http.sslVerify=false (não recomendado)"
189
- },
190
-
191
- // Generic
192
- UNKNOWN_ERROR: {
193
- code: "UNKNOWN_ERROR",
194
- suggestion: "Erro desconhecido. Verifique os logs para mais detalhes"
195
- }
196
- };
197
-
198
- // Função para criar erro com sugestão
199
- export function createError(errorCode, details = {}) {
200
- const errorInfo = ERROR_CODES[errorCode] || ERROR_CODES.UNKNOWN_ERROR;
201
- return new MCPError(errorInfo.code, details.message || errorInfo.code, {
202
- suggestion: errorInfo.suggestion,
203
- ...details
204
- });
205
- }
206
-
207
- // Função para mapear erros externos para códigos internos
208
- export function mapExternalError(error, context = {}) {
209
- const msg = error?.message?.toLowerCase() || String(error).toLowerCase();
210
-
211
- // GitHub/Gitea API errors
212
- if (msg.includes("bad credentials") || msg.includes("401")) {
213
- return context.provider === "gitea"
214
- ? createError("AUTH_GITEA_INVALID", { originalError: msg })
215
- : createError("AUTH_GITHUB_INVALID", { originalError: msg });
216
- }
217
-
218
- if (msg.includes("not found") || msg.includes("404")) {
219
- if (context.type === "repo") return createError("REPO_NOT_FOUND", { repo: context.repo, originalError: msg });
220
- if (context.type === "branch") return createError("BRANCH_NOT_FOUND", { branch: context.branch, originalError: msg });
221
- if (context.type === "tag") return createError("TAG_NOT_FOUND", { tag: context.tag, originalError: msg });
222
- if (context.type === "file") return createError("FILE_NOT_FOUND", { file: context.file, originalError: msg });
223
- if (context.type === "issue") return createError("ISSUE_NOT_FOUND", { number: context.number, originalError: msg });
224
- if (context.type === "pr") return createError("PR_NOT_FOUND", { number: context.number, originalError: msg });
225
- return createError("REF_NOT_FOUND", { ref: context.ref, originalError: msg });
226
- }
227
-
228
- if (msg.includes("permission") || msg.includes("403") || msg.includes("forbidden")) {
229
- return createError("AUTH_NO_PERMISSION", { originalError: msg });
230
- }
231
-
232
- if (msg.includes("rate limit") || msg.includes("429")) {
233
- return createError("RATE_LIMIT", { originalError: msg });
234
- }
235
-
236
- if (msg.includes("timeout") || msg.includes("etimedout") || msg.includes("econnrefused")) {
237
- return createError("NETWORK_TIMEOUT", { originalError: msg });
238
- }
239
-
240
- if (msg.includes("push rejected") || msg.includes("non-fast-forward")) {
241
- return createError("PUSH_REJECTED", { originalError: msg });
242
- }
243
-
244
- if (msg.includes("conflict")) {
245
- return createError("MERGE_CONFLICT", { originalError: msg });
246
- }
247
-
248
- if (msg.includes("already exists")) {
249
- if (context.type === "repo") return createError("REPO_ALREADY_EXISTS", { repo: context.repo, originalError: msg });
250
- if (context.type === "branch") return createError("BRANCH_ALREADY_EXISTS", { branch: context.branch, originalError: msg });
251
- if (context.type === "tag") return createError("TAG_ALREADY_EXISTS", { tag: context.tag, originalError: msg });
252
- return createError("BRANCH_ALREADY_EXISTS", { originalError: msg });
253
- }
254
-
255
- // isomorphic-git specific errors
256
- if (msg.includes("could not find") && msg.includes("head~")) {
257
- return createError("INSUFFICIENT_HISTORY", { originalError: msg });
258
- }
259
-
260
- if (msg.includes("is not a git repository") || msg.includes("could not find .git")) {
261
- return createError("NOT_A_GIT_REPO", { path: context.path, originalError: msg });
262
- }
263
-
264
- if (msg.includes("nothing to commit") || msg.includes("working tree clean")) {
265
- return createError("NOTHING_TO_COMMIT", { originalError: msg });
266
- }
267
-
268
- if (msg.includes("could not resolve ref") || msg.includes("invalid reference")) {
269
- return createError("REF_NOT_FOUND", { originalError: msg });
270
- }
271
-
272
- if (msg.includes("branch") && (msg.includes("does not exist") || msg.includes("not found"))) {
273
- return createError("BRANCH_NOT_FOUND", { originalError: msg });
274
- }
275
-
276
- if (msg.includes("tag") && (msg.includes("does not exist") || msg.includes("not found"))) {
277
- return createError("TAG_NOT_FOUND", { originalError: msg });
278
- }
279
-
280
- if (msg.includes("no commits") || msg.includes("unknown revision")) {
281
- return createError("NO_COMMITS", { originalError: msg });
282
- }
283
-
284
- if (msg.includes("nada para stash") || msg.includes("nothing to stash") || msg.includes("no local changes")) {
285
- return createError("NOTHING_TO_STASH", { originalError: msg });
286
- }
287
-
288
- if (msg.includes("stash") && (msg.includes("not found") || msg.includes("não encontrado"))) {
289
- return createError("STASH_NOT_FOUND", { originalError: msg });
290
- }
291
-
292
- if (msg.includes("author") || msg.includes("committer")) {
293
- return createError("VALIDATION_ERROR", {
294
- message: "Configure user.name e user.email",
295
- suggestion: "Use git-config para configurar ou forneça tokens de provider",
296
- originalError: msg
297
- });
298
- }
299
-
300
- // Filesystem errors
301
- if (msg.includes("enospc") || msg.includes("no space left") || msg.includes("disk full")) {
302
- return createError("DISK_FULL", { originalError: msg });
303
- }
304
-
305
- if (msg.includes("file too large") || msg.includes("exceeds") || msg.includes("size limit")) {
306
- return createError("FILE_TOO_LARGE", { originalError: msg });
307
- }
308
-
309
- if (msg.includes("eacces") || msg.includes("eperm") || (msg.includes("permission") && msg.includes("denied"))) {
310
- return createError("PERMISSION_DENIED_FS", { originalError: msg });
311
- }
312
-
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")) {
316
- return createError("GIT_CORRUPTED", { originalError: msg });
317
- }
318
-
319
- if (msg.includes(".git/index.lock") || msg.includes("unable to create") && msg.includes("lock")) {
320
- return createError("GIT_LOCK_FILE", { originalError: msg });
321
- }
322
-
323
- // Remote/URL errors
324
- if (msg.includes("invalid url") || msg.includes("malformed") || msg.includes("not a valid")) {
325
- return createError("INVALID_REMOTE_URL", { originalError: msg });
326
- }
327
-
328
- if (msg.includes("could not resolve host") || msg.includes("name resolution") ||
329
- msg.includes("no address") || msg.includes("unreachable")) {
330
- return createError("REMOTE_UNREACHABLE", { originalError: msg });
331
- }
332
-
333
- if (msg.includes("ssl") || msg.includes("certificate") || msg.includes("tls")) {
334
- return createError("SSL_ERROR", { originalError: msg });
335
- }
336
-
337
- // Generic fallback
338
- return createError("UNKNOWN_ERROR", { originalError: msg, context });
339
- }
340
-
341
- // ============ NEXT ACTIONS PARA AI AGENTS ============
342
- // Mapeia ação atual para próxima ação sugerida
343
- export const NEXT_ACTIONS = {
344
- // git-workflow
345
- "workflow:init": { tool: "git-workflow", action: "add", hint: "Adicione arquivos ao staging com files=['.']" },
346
- "workflow:status": { tool: "git-workflow", action: "add", hint: "Se há arquivos modificados, adicione-os ao staging" },
347
- "workflow:add": { tool: "git-workflow", action: "commit", hint: "Crie um commit com uma mensagem descritiva" },
348
- "workflow:commit": { tool: "git-workflow", action: "push", hint: "Envie as mudanças para GitHub/Gitea" },
349
- "workflow:push": null, // Fluxo completo
350
-
351
- // git-branches
352
- "branches:create": { tool: "git-branches", action: "checkout", hint: "Mude para a nova branch criada" },
353
- "branches:checkout": { tool: "git-workflow", action: "status", hint: "Verifique o estado da branch" },
354
- "branches:delete": null,
355
-
356
- // git-stash
357
- "stash:save": { tool: "git-branches", action: "checkout", hint: "Agora pode trocar de branch com segurança" },
358
- "stash:pop": { tool: "git-workflow", action: "status", hint: "Verifique as mudanças restauradas" },
359
-
360
- // git-merge
361
- "merge:merge": { tool: "git-workflow", action: "push", hint: "Envie o merge para os remotes" },
362
-
363
- // git-tags
364
- "tags:create": { tool: "git-tags", action: "push", hint: "Envie a tag para os remotes" },
365
- "tags:push": null,
366
-
367
- // git-remote
368
- "remote:ensure": { tool: "git-workflow", action: "push", hint: "Agora pode fazer push" },
369
- "remote:release-create": null,
370
-
371
- // git-sync
372
- "sync:pull": { tool: "git-workflow", action: "status", hint: "Verifique mudanças após pull" },
373
- "sync:fetch": { tool: "git-sync", action: "pull", hint: "Aplique as mudanças baixadas" },
374
- };
375
-
376
- /**
377
- * Retorna a próxima ação sugerida para o AI Agent
378
- * @param {string} tool - Nome da tool (git-workflow, git-branches, etc)
379
- * @param {string} action - Ação executada
380
- * @returns {object|null} - Próxima ação sugerida ou null se fluxo completo
381
- */
382
- export function getNextAction(tool, action) {
383
- const key = `${tool.replace('git-', '')}:${action}`;
384
- return NEXT_ACTIONS[key] || null;
385
- }
386
-
387
- // Resultado de sucesso com nextAction para AI
388
- export function asToolResult(result, options = {}) {
389
- const { tool, action } = options;
390
- const response = { ...result };
391
-
392
- // Adicionar próxima ação sugerida se disponível
393
- if (tool && action) {
394
- const next = getNextAction(tool, action);
395
- if (next) {
396
- response._nextAction = next;
397
- } else {
398
- response._flowComplete = true;
399
- }
400
- }
401
-
402
- return {
403
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
404
- isError: false,
405
- };
406
- }
407
-
408
- // Resultado de erro (formato melhorado para AI agents)
409
- export function asToolError(code, message, data) {
410
- const errorInfo = ERROR_CODES[code] || {};
411
- const response = {
412
- error: true,
413
- code: code,
414
- message: message,
415
- suggestion: errorInfo.suggestion || "Verifique os parâmetros e tente novamente",
416
- ...data
417
- };
418
- return {
419
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
420
- isError: true,
421
- };
422
- }
423
-
424
- // Helper para converter MCPError em resposta
425
- export function errorToResponse(error) {
426
- if (error instanceof MCPError) {
427
- return asToolError(error.code, error.message, error.data);
428
- }
429
- const mapped = mapExternalError(error);
430
- return asToolError(mapped.code, mapped.message, mapped.data);
431
- }
1
+ // Sistema de Erros Melhorado para AI Agents
2
+
3
+ export class MCPError extends Error {
4
+ constructor(code, message, data) {
5
+ super(message);
6
+ this.code = code;
7
+ this.data = data;
8
+ }
9
+ }
10
+
11
+ // Códigos de erro padronizados com sugestões para AI
12
+ export const ERROR_CODES = {
13
+ // Auth
14
+ AUTH_GITHUB_INVALID: {
15
+ code: "AUTH_GITHUB_INVALID",
16
+ suggestion: "Verifique se GITHUB_TOKEN está configurado e é válido. Gere um novo token em github.com/settings/tokens"
17
+ },
18
+ AUTH_GITEA_INVALID: {
19
+ code: "AUTH_GITEA_INVALID",
20
+ suggestion: "Verifique se GITEA_TOKEN e GITEA_URL estão configurados corretamente"
21
+ },
22
+ AUTH_NO_PERMISSION: {
23
+ code: "AUTH_NO_PERMISSION",
24
+ suggestion: "Token não tem permissão para esta operação. Verifique os scopes do token"
25
+ },
26
+
27
+ // Repo
28
+ REPO_NOT_FOUND: {
29
+ code: "REPO_NOT_FOUND",
30
+ suggestion: "Repositório não existe. Use action='init' com createIfMissing=true para criar"
31
+ },
32
+ REPO_NO_WRITE: {
33
+ code: "REPO_NO_WRITE",
34
+ suggestion: "Sem permissão de escrita. Verifique se você é owner ou colaborador do repositório"
35
+ },
36
+ REPO_ALREADY_EXISTS: {
37
+ code: "REPO_ALREADY_EXISTS",
38
+ suggestion: "Repositório já existe. Use action='ensure' para verificar ou escolha outro nome"
39
+ },
40
+
41
+ // Git Local
42
+ NOT_A_GIT_REPO: {
43
+ code: "NOT_A_GIT_REPO",
44
+ suggestion: "Diretório não é um repositório git. Use action='init' primeiro para inicializar"
45
+ },
46
+ NO_COMMITS: {
47
+ code: "NO_COMMITS",
48
+ suggestion: "Repositório não tem commits. Use action='add' e depois action='commit' primeiro"
49
+ },
50
+ NOTHING_TO_COMMIT: {
51
+ code: "NOTHING_TO_COMMIT",
52
+ suggestion: "Working tree limpa, nada para commitar. Modifique arquivos primeiro"
53
+ },
54
+ NOTHING_TO_PUSH: {
55
+ code: "NOTHING_TO_PUSH",
56
+ suggestion: "Nenhuma mudança para push. Faça commits primeiro"
57
+ },
58
+
59
+ // Branches
60
+ BRANCH_NOT_FOUND: {
61
+ code: "BRANCH_NOT_FOUND",
62
+ suggestion: "Branch não existe. Use action='list' para ver branches disponíveis"
63
+ },
64
+ BRANCH_ALREADY_EXISTS: {
65
+ code: "BRANCH_ALREADY_EXISTS",
66
+ suggestion: "Branch já existe. Use outro nome ou delete a existente primeiro"
67
+ },
68
+ CANNOT_DELETE_CURRENT: {
69
+ code: "CANNOT_DELETE_CURRENT",
70
+ suggestion: "Não pode deletar branch atual. Faça checkout para outra branch primeiro"
71
+ },
72
+
73
+ // Tags
74
+ TAG_NOT_FOUND: {
75
+ code: "TAG_NOT_FOUND",
76
+ suggestion: "Tag não existe. Use action='list' para ver tags disponíveis"
77
+ },
78
+ TAG_ALREADY_EXISTS: {
79
+ code: "TAG_ALREADY_EXISTS",
80
+ suggestion: "Tag já existe. Use outro nome ou delete a existente primeiro"
81
+ },
82
+
83
+ // Refs
84
+ REF_NOT_FOUND: {
85
+ code: "REF_NOT_FOUND",
86
+ suggestion: "Referência não encontrada. Verifique se o commit/branch/tag existe. Use action='log' para ver histórico"
87
+ },
88
+ INSUFFICIENT_HISTORY: {
89
+ code: "INSUFFICIENT_HISTORY",
90
+ suggestion: "Histórico insuficiente para HEAD~N. Use action='log' para verificar quantos commits existem"
91
+ },
92
+
93
+ // Stash
94
+ NOTHING_TO_STASH: {
95
+ code: "NOTHING_TO_STASH",
96
+ suggestion: "Working tree limpa, nada para stash. Modifique arquivos primeiro"
97
+ },
98
+ STASH_NOT_FOUND: {
99
+ code: "STASH_NOT_FOUND",
100
+ suggestion: "Stash não encontrado. Use action='list' para ver stashes disponíveis"
101
+ },
102
+
103
+ // Remote
104
+ REMOTE_NOT_FOUND: {
105
+ code: "REMOTE_NOT_FOUND",
106
+ suggestion: "Remote não configurado. Use action='ensure-remotes' para configurar"
107
+ },
108
+ PUSH_REJECTED: {
109
+ code: "PUSH_REJECTED",
110
+ suggestion: "Push rejeitado (histórico divergente). Use force=true para forçar, ou faça pull primeiro"
111
+ },
112
+ MERGE_CONFLICT: {
113
+ code: "MERGE_CONFLICT",
114
+ suggestion: "Conflito de merge. Resolva conflitos manualmente e faça novo commit"
115
+ },
116
+
117
+ // Network
118
+ NETWORK_TIMEOUT: {
119
+ code: "NETWORK_TIMEOUT",
120
+ suggestion: "Timeout de conexão. Verifique sua internet e tente novamente"
121
+ },
122
+ RATE_LIMIT: {
123
+ code: "RATE_LIMIT",
124
+ suggestion: "Rate limit excedido. Aguarde alguns minutos antes de tentar novamente"
125
+ },
126
+
127
+ // Validation
128
+ VALIDATION_ERROR: {
129
+ code: "VALIDATION_ERROR",
130
+ suggestion: "Parâmetros inválidos. Verifique os parâmetros obrigatórios da action"
131
+ },
132
+ MISSING_PARAMETER: {
133
+ code: "MISSING_PARAMETER",
134
+ suggestion: "Parâmetro obrigatório faltando"
135
+ },
136
+
137
+ // Files
138
+ FILE_NOT_FOUND: {
139
+ code: "FILE_NOT_FOUND",
140
+ suggestion: "Arquivo não encontrado no repositório. Use action='list' para ver arquivos disponíveis"
141
+ },
142
+
143
+ // Issues/PRs
144
+ ISSUE_NOT_FOUND: {
145
+ code: "ISSUE_NOT_FOUND",
146
+ suggestion: "Issue não encontrada. Use action='list' para ver issues disponíveis"
147
+ },
148
+ PR_NOT_FOUND: {
149
+ code: "PR_NOT_FOUND",
150
+ suggestion: "Pull Request não encontrado. Use action='list' para ver PRs disponíveis"
151
+ },
152
+
153
+ // Filesystem
154
+ DISK_FULL: {
155
+ code: "DISK_FULL",
156
+ suggestion: "Disco cheio. Libere espaço e tente novamente"
157
+ },
158
+ FILE_TOO_LARGE: {
159
+ code: "FILE_TOO_LARGE",
160
+ suggestion: "Arquivo muito grande para Git. Considere usar Git LFS para arquivos grandes"
161
+ },
162
+ PERMISSION_DENIED_FS: {
163
+ code: "PERMISSION_DENIED_FS",
164
+ suggestion: "Sem permissão para acessar arquivo/diretório. Verifique permissões do sistema de arquivos"
165
+ },
166
+
167
+ // Git Corruption
168
+ GIT_CORRUPTED: {
169
+ code: "GIT_CORRUPTED",
170
+ suggestion: "Repositório Git corrompido. Tente: 1) git fsck --full, 2) git gc --prune=now, ou 3) re-clone do remote"
171
+ },
172
+ GIT_LOCK_FILE: {
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"
175
+ },
176
+
177
+ // Remote/URL
178
+ INVALID_REMOTE_URL: {
179
+ code: "INVALID_REMOTE_URL",
180
+ suggestion: "URL do remote inválida. Verifique o formato: https://github.com/user/repo.git ou git@github.com:user/repo.git"
181
+ },
182
+ REMOTE_UNREACHABLE: {
183
+ code: "REMOTE_UNREACHABLE",
184
+ suggestion: "Remote inacessível. Verifique: 1) URL está correta, 2) conexão de internet, 3) firewall/proxy"
185
+ },
186
+ SSL_ERROR: {
187
+ code: "SSL_ERROR",
188
+ suggestion: "Erro de SSL/TLS. Verifique certificados ou use http.sslVerify=false (não recomendado)"
189
+ },
190
+
191
+ // Generic
192
+ UNKNOWN_ERROR: {
193
+ code: "UNKNOWN_ERROR",
194
+ suggestion: "Erro desconhecido. Verifique os logs para mais detalhes"
195
+ }
196
+ };
197
+
198
+ // Função para criar erro com sugestão
199
+ export function createError(errorCode, details = {}) {
200
+ const errorInfo = ERROR_CODES[errorCode] || ERROR_CODES.UNKNOWN_ERROR;
201
+ return new MCPError(errorInfo.code, details.message || errorInfo.code, {
202
+ suggestion: errorInfo.suggestion,
203
+ ...details
204
+ });
205
+ }
206
+
207
+ // Função para mapear erros externos para códigos internos
208
+ export function mapExternalError(error, context = {}) {
209
+ const msg = error?.message?.toLowerCase() || String(error).toLowerCase();
210
+
211
+ // GitHub/Gitea API errors
212
+ if (msg.includes("bad credentials") || msg.includes("401")) {
213
+ return context.provider === "gitea"
214
+ ? createError("AUTH_GITEA_INVALID", { originalError: msg })
215
+ : createError("AUTH_GITHUB_INVALID", { originalError: msg });
216
+ }
217
+
218
+ if (msg.includes("not found") || msg.includes("404")) {
219
+ if (context.type === "repo") return createError("REPO_NOT_FOUND", { repo: context.repo, originalError: msg });
220
+ if (context.type === "branch") return createError("BRANCH_NOT_FOUND", { branch: context.branch, originalError: msg });
221
+ if (context.type === "tag") return createError("TAG_NOT_FOUND", { tag: context.tag, originalError: msg });
222
+ if (context.type === "file") return createError("FILE_NOT_FOUND", { file: context.file, originalError: msg });
223
+ if (context.type === "issue") return createError("ISSUE_NOT_FOUND", { number: context.number, originalError: msg });
224
+ if (context.type === "pr") return createError("PR_NOT_FOUND", { number: context.number, originalError: msg });
225
+ return createError("REF_NOT_FOUND", { ref: context.ref, originalError: msg });
226
+ }
227
+
228
+ if (msg.includes("permission") || msg.includes("403") || msg.includes("forbidden")) {
229
+ return createError("AUTH_NO_PERMISSION", { originalError: msg });
230
+ }
231
+
232
+ if (msg.includes("rate limit") || msg.includes("429")) {
233
+ return createError("RATE_LIMIT", { originalError: msg });
234
+ }
235
+
236
+ if (msg.includes("timeout") || msg.includes("etimedout") || msg.includes("econnrefused")) {
237
+ return createError("NETWORK_TIMEOUT", { originalError: msg });
238
+ }
239
+
240
+ if (msg.includes("push rejected") || msg.includes("non-fast-forward")) {
241
+ return createError("PUSH_REJECTED", { originalError: msg });
242
+ }
243
+
244
+ if (msg.includes("conflict")) {
245
+ return createError("MERGE_CONFLICT", { originalError: msg });
246
+ }
247
+
248
+ if (msg.includes("already exists")) {
249
+ if (context.type === "repo") return createError("REPO_ALREADY_EXISTS", { repo: context.repo, originalError: msg });
250
+ if (context.type === "branch") return createError("BRANCH_ALREADY_EXISTS", { branch: context.branch, originalError: msg });
251
+ if (context.type === "tag") return createError("TAG_ALREADY_EXISTS", { tag: context.tag, originalError: msg });
252
+ return createError("BRANCH_ALREADY_EXISTS", { originalError: msg });
253
+ }
254
+
255
+ // isomorphic-git specific errors
256
+ if (msg.includes("could not find") && msg.includes("head~")) {
257
+ return createError("INSUFFICIENT_HISTORY", { originalError: msg });
258
+ }
259
+
260
+ if (msg.includes("is not a git repository") || msg.includes("could not find .git")) {
261
+ return createError("NOT_A_GIT_REPO", { path: context.path, originalError: msg });
262
+ }
263
+
264
+ if (msg.includes("nothing to commit") || msg.includes("working tree clean")) {
265
+ return createError("NOTHING_TO_COMMIT", { originalError: msg });
266
+ }
267
+
268
+ if (msg.includes("could not resolve ref") || msg.includes("invalid reference")) {
269
+ return createError("REF_NOT_FOUND", { originalError: msg });
270
+ }
271
+
272
+ if (msg.includes("branch") && (msg.includes("does not exist") || msg.includes("not found"))) {
273
+ return createError("BRANCH_NOT_FOUND", { originalError: msg });
274
+ }
275
+
276
+ if (msg.includes("tag") && (msg.includes("does not exist") || msg.includes("not found"))) {
277
+ return createError("TAG_NOT_FOUND", { originalError: msg });
278
+ }
279
+
280
+ if (msg.includes("no commits") || msg.includes("unknown revision")) {
281
+ return createError("NO_COMMITS", { originalError: msg });
282
+ }
283
+
284
+ if (msg.includes("nada para stash") || msg.includes("nothing to stash") || msg.includes("no local changes")) {
285
+ return createError("NOTHING_TO_STASH", { originalError: msg });
286
+ }
287
+
288
+ if (msg.includes("stash") && (msg.includes("not found") || msg.includes("não encontrado"))) {
289
+ return createError("STASH_NOT_FOUND", { originalError: msg });
290
+ }
291
+
292
+ if (msg.includes("author") || msg.includes("committer")) {
293
+ return createError("VALIDATION_ERROR", {
294
+ message: "Configure user.name e user.email",
295
+ suggestion: "Use git-config para configurar ou forneça tokens de provider",
296
+ originalError: msg
297
+ });
298
+ }
299
+
300
+ // Filesystem errors
301
+ if (msg.includes("enospc") || msg.includes("no space left") || msg.includes("disk full")) {
302
+ return createError("DISK_FULL", { originalError: msg });
303
+ }
304
+
305
+ if (msg.includes("file too large") || msg.includes("exceeds") || msg.includes("size limit")) {
306
+ return createError("FILE_TOO_LARGE", { originalError: msg });
307
+ }
308
+
309
+ if (msg.includes("eacces") || msg.includes("eperm") || (msg.includes("permission") && msg.includes("denied"))) {
310
+ return createError("PERMISSION_DENIED_FS", { originalError: msg });
311
+ }
312
+
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")) {
316
+ return createError("GIT_CORRUPTED", { originalError: msg });
317
+ }
318
+
319
+ if (msg.includes(".git/index.lock") || msg.includes("unable to create") && msg.includes("lock")) {
320
+ return createError("GIT_LOCK_FILE", { originalError: msg });
321
+ }
322
+
323
+ // Remote/URL errors
324
+ if (msg.includes("invalid url") || msg.includes("malformed") || msg.includes("not a valid")) {
325
+ return createError("INVALID_REMOTE_URL", { originalError: msg });
326
+ }
327
+
328
+ if (msg.includes("could not resolve host") || msg.includes("name resolution") ||
329
+ msg.includes("no address") || msg.includes("unreachable")) {
330
+ return createError("REMOTE_UNREACHABLE", { originalError: msg });
331
+ }
332
+
333
+ if (msg.includes("ssl") || msg.includes("certificate") || msg.includes("tls")) {
334
+ return createError("SSL_ERROR", { originalError: msg });
335
+ }
336
+
337
+ // Generic fallback
338
+ return createError("UNKNOWN_ERROR", { originalError: msg, context });
339
+ }
340
+
341
+ // ============ NEXT ACTIONS PARA AI AGENTS ============
342
+ // Mapeia ação atual para próxima ação sugerida
343
+ export const NEXT_ACTIONS = {
344
+ // git-workflow
345
+ "workflow:init": { tool: "git-workflow", action: "add", hint: "Adicione arquivos ao staging com files=['.']" },
346
+ "workflow:status": { tool: "git-workflow", action: "add", hint: "Se há arquivos modificados, adicione-os ao staging" },
347
+ "workflow:add": { tool: "git-workflow", action: "commit", hint: "Crie um commit com uma mensagem descritiva" },
348
+ "workflow:commit": { tool: "git-workflow", action: "push", hint: "Envie as mudanças para GitHub/Gitea" },
349
+ "workflow:push": null, // Fluxo completo
350
+
351
+ // git-branches
352
+ "branches:create": { tool: "git-branches", action: "checkout", hint: "Mude para a nova branch criada" },
353
+ "branches:checkout": { tool: "git-workflow", action: "status", hint: "Verifique o estado da branch" },
354
+ "branches:delete": null,
355
+
356
+ // git-stash
357
+ "stash:save": { tool: "git-branches", action: "checkout", hint: "Agora pode trocar de branch com segurança" },
358
+ "stash:pop": { tool: "git-workflow", action: "status", hint: "Verifique as mudanças restauradas" },
359
+
360
+ // git-merge
361
+ "merge:merge": { tool: "git-workflow", action: "push", hint: "Envie o merge para os remotes" },
362
+
363
+ // git-tags
364
+ "tags:create": { tool: "git-tags", action: "push", hint: "Envie a tag para os remotes" },
365
+ "tags:push": null,
366
+
367
+ // git-remote
368
+ "remote:ensure": { tool: "git-workflow", action: "push", hint: "Agora pode fazer push" },
369
+ "remote:release-create": null,
370
+
371
+ // git-sync
372
+ "sync:pull": { tool: "git-workflow", action: "status", hint: "Verifique mudanças após pull" },
373
+ "sync:fetch": { tool: "git-sync", action: "pull", hint: "Aplique as mudanças baixadas" },
374
+ };
375
+
376
+ /**
377
+ * Retorna a próxima ação sugerida para o AI Agent
378
+ * @param {string} tool - Nome da tool (git-workflow, git-branches, etc)
379
+ * @param {string} action - Ação executada
380
+ * @returns {object|null} - Próxima ação sugerida ou null se fluxo completo
381
+ */
382
+ export function getNextAction(tool, action) {
383
+ const key = `${tool.replace('git-', '')}:${action}`;
384
+ return NEXT_ACTIONS[key] || null;
385
+ }
386
+
387
+ // Resultado de sucesso com nextAction para AI
388
+ export function asToolResult(result, options = {}) {
389
+ const { tool, action } = options;
390
+ const response = { ...result };
391
+
392
+ // Adicionar próxima ação sugerida se disponível
393
+ if (tool && action) {
394
+ const next = getNextAction(tool, action);
395
+ if (next) {
396
+ response._nextAction = next;
397
+ } else {
398
+ response._flowComplete = true;
399
+ }
400
+ }
401
+
402
+ return {
403
+ content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
404
+ isError: false,
405
+ };
406
+ }
407
+
408
+ // Resultado de erro (formato melhorado para AI agents)
409
+ export function asToolError(code, message, data) {
410
+ const errorInfo = ERROR_CODES[code] || {};
411
+ const response = {
412
+ error: true,
413
+ code: code,
414
+ message: message,
415
+ suggestion: errorInfo.suggestion || "Verifique os parâmetros e tente novamente",
416
+ ...data
417
+ };
418
+ return {
419
+ content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
420
+ isError: true,
421
+ };
422
+ }
423
+
424
+ // Helper para converter MCPError em resposta
425
+ export function errorToResponse(error) {
426
+ if (error instanceof MCPError) {
427
+ return asToolError(error.code, error.message, error.data);
428
+ }
429
+ const mapped = mapExternalError(error);
430
+ return asToolError(mapped.code, mapped.message, mapped.data);
431
+ }