@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.
- package/README.md +39 -125
- package/package.json +27 -44
- package/src/index.js +146 -139
- package/src/providers/providerManager.js +203 -203
- package/src/tools/git-diff.js +137 -126
- package/src/tools/git-help.js +285 -285
- package/src/tools/git-remote.js +472 -472
- package/src/tools/git-workflow.js +403 -403
- package/src/utils/env.js +104 -104
- package/src/utils/errors.js +431 -431
- package/src/utils/gitAdapter.js +932 -932
- package/src/utils/hooks.js +255 -255
- package/src/utils/metrics.js +198 -198
- package/src/utils/providerExec.js +58 -58
- package/src/utils/repoHelpers.js +160 -160
- package/src/utils/retry.js +123 -123
- package/install.sh +0 -68
package/src/utils/errors.js
CHANGED
|
@@ -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
|
+
}
|