@andrebuzeli/git-mcp 15.6.0 → 15.8.0
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/index.js +1 -1
- package/src/tools/git-workflow.js +104 -4
- package/src/utils/gitAdapter.js +932 -873
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -47,7 +47,7 @@ if (!hasGitHub && !hasGitea) {
|
|
|
47
47
|
|
|
48
48
|
const transport = new StdioServerTransport();
|
|
49
49
|
const server = new Server(
|
|
50
|
-
{ name: "git-mcpv2", version: "15.
|
|
50
|
+
{ name: "git-mcpv2", version: "15.8.0" },
|
|
51
51
|
{ capabilities: { tools: {}, resources: {}, prompts: {} } }
|
|
52
52
|
);
|
|
53
53
|
server.connect(transport);
|
|
@@ -14,7 +14,7 @@ export function createGitWorkflowTool(pm, git) {
|
|
|
14
14
|
},
|
|
15
15
|
action: {
|
|
16
16
|
type: "string",
|
|
17
|
-
enum: ["init", "status", "add", "remove", "commit", "push", "ensure-remotes", "clean"],
|
|
17
|
+
enum: ["init", "status", "add", "remove", "commit", "push", "ensure-remotes", "clean", "update"],
|
|
18
18
|
description: `Ação a executar:
|
|
19
19
|
- init: Inicializa repositório git local E cria repos no GitHub/Gitea automaticamente
|
|
20
20
|
- status: Retorna arquivos modificados, staged, untracked (use ANTES de commit para ver o que mudou)
|
|
@@ -23,7 +23,8 @@ export function createGitWorkflowTool(pm, git) {
|
|
|
23
23
|
- commit: Cria commit com os arquivos staged (use DEPOIS de add)
|
|
24
24
|
- push: Envia commits para GitHub E Gitea em paralelo (use DEPOIS de commit)
|
|
25
25
|
- ensure-remotes: Configura remotes GitHub e Gitea (use se push falhar por falta de remote)
|
|
26
|
-
- clean: Remove arquivos não rastreados do working directory
|
|
26
|
+
- clean: Remove arquivos não rastreados do working directory
|
|
27
|
+
- update: Atualiza projeto completo: init (se necessário), add, commit, ensure-remotes e push`
|
|
27
28
|
},
|
|
28
29
|
files: {
|
|
29
30
|
type: "array",
|
|
@@ -32,7 +33,7 @@ export function createGitWorkflowTool(pm, git) {
|
|
|
32
33
|
},
|
|
33
34
|
message: {
|
|
34
35
|
type: "string",
|
|
35
|
-
description: "Mensagem do commit. Obrigatório para action='commit'. Ex: 'feat: adiciona nova funcionalidade'"
|
|
36
|
+
description: "Mensagem do commit. Obrigatório para action='commit'. Opcional para action='update' (se não fornecido, usa mensagem padrão). Ex: 'feat: adiciona nova funcionalidade'"
|
|
36
37
|
},
|
|
37
38
|
force: {
|
|
38
39
|
type: "boolean",
|
|
@@ -71,6 +72,7 @@ QUANDO USAR CADA ACTION:
|
|
|
71
72
|
- init: Apenas uma vez, para novos projetos (cria .gitignore automaticamente)
|
|
72
73
|
- ensure-remotes: Se push falhar por falta de configuração
|
|
73
74
|
- clean: Limpar arquivos não rastreados
|
|
75
|
+
- update: Para atualizar projeto completo (init + add + commit + push) - mais rápido que fazer cada ação separadamente
|
|
74
76
|
|
|
75
77
|
EXEMPLOS DE USO:
|
|
76
78
|
• Iniciar projeto: { "projectPath": "/path/to/project", "action": "init" }
|
|
@@ -285,8 +287,106 @@ EXEMPLOS DE USO:
|
|
|
285
287
|
const result = await git.pushParallel(projectPath, branch, force);
|
|
286
288
|
return asToolResult({ success: true, branch, ...result });
|
|
287
289
|
}
|
|
290
|
+
if (action === "update") {
|
|
291
|
+
if (args.dryRun) {
|
|
292
|
+
return asToolResult({
|
|
293
|
+
success: true,
|
|
294
|
+
dryRun: true,
|
|
295
|
+
message: "DRY RUN: Update completo seria executado (init se necessário, add, commit, push)"
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const results = {
|
|
300
|
+
init: null,
|
|
301
|
+
ensureRemotes: null,
|
|
302
|
+
add: null,
|
|
303
|
+
commit: null,
|
|
304
|
+
push: null
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const repo = getRepoNameFromPath(projectPath);
|
|
308
|
+
const isPublic = args.isPublic === true;
|
|
309
|
+
|
|
310
|
+
// 1. Verificar se é repo Git, se não for, fazer init
|
|
311
|
+
const isRepo = await git.isRepo(projectPath).catch(() => false);
|
|
312
|
+
if (!isRepo) {
|
|
313
|
+
await git.init(projectPath);
|
|
314
|
+
|
|
315
|
+
// Criar .gitignore baseado no tipo de projeto
|
|
316
|
+
const shouldCreateGitignore = args.createGitignore !== false;
|
|
317
|
+
if (shouldCreateGitignore) {
|
|
318
|
+
const projectType = detectProjectType(projectPath);
|
|
319
|
+
const patterns = GITIGNORE_TEMPLATES[projectType] || GITIGNORE_TEMPLATES.general;
|
|
320
|
+
await git.createGitignore(projectPath, patterns);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const ensured = await pm.ensureRepos({ repoName: repo, createIfMissing: true, isPublic });
|
|
324
|
+
results.init = { success: true, ensured, isPrivate: !isPublic, gitignoreCreated: shouldCreateGitignore };
|
|
325
|
+
} else {
|
|
326
|
+
results.init = { success: true, skipped: true, message: "Repositório já existe" };
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// 2. Garantir remotes configurados (sempre verifica/configura)
|
|
330
|
+
const ensured = await pm.ensureRepos({ repoName: repo, createIfMissing: true, isPublic });
|
|
331
|
+
const ghOwner = await pm.getGitHubOwner();
|
|
332
|
+
const geOwner = await pm.getGiteaOwner();
|
|
333
|
+
const githubUrl = ghOwner ? `https://github.com/${ghOwner}/${repo}.git` : "";
|
|
334
|
+
const base = pm.giteaUrl?.replace(/\/$/, "") || "";
|
|
335
|
+
const giteaUrl = geOwner && base ? `${base}/${geOwner}/${repo}.git` : "";
|
|
336
|
+
await git.ensureRemotes(projectPath, { githubUrl, giteaUrl });
|
|
337
|
+
const remotes = await git.listRemotes(projectPath);
|
|
338
|
+
results.ensureRemotes = { success: true, ensured, remotes };
|
|
339
|
+
|
|
340
|
+
// 3. Verificar status e fazer add se necessário
|
|
341
|
+
const status = await git.status(projectPath);
|
|
342
|
+
if (!status.isClean || status.modified?.length > 0 || status.created?.length > 0 || status.notAdded?.length > 0) {
|
|
343
|
+
await git.add(projectPath, ["."]);
|
|
344
|
+
results.add = { success: true, files: ["."] };
|
|
345
|
+
} else {
|
|
346
|
+
results.add = { success: true, skipped: true, message: "Nenhum arquivo para adicionar" };
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// 4. Verificar se há algo staged e fazer commit
|
|
350
|
+
const statusAfterAdd = await git.status(projectPath);
|
|
351
|
+
if (statusAfterAdd.staged?.length > 0) {
|
|
352
|
+
// Gerar mensagem padrão se não fornecida
|
|
353
|
+
const commitMessage = args.message || `Update: ${new Date().toISOString().split('T')[0]} - ${statusAfterAdd.staged.length} arquivo(s) modificado(s)`;
|
|
354
|
+
const sha = await git.commit(projectPath, commitMessage);
|
|
355
|
+
results.commit = { success: true, sha, message: commitMessage };
|
|
356
|
+
} else {
|
|
357
|
+
results.commit = { success: true, skipped: true, message: "Nenhum arquivo staged para commit" };
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// 5. Fazer push (só se houver commits para enviar)
|
|
361
|
+
const branch = await git.getCurrentBranch(projectPath);
|
|
362
|
+
const force = !!args.force;
|
|
363
|
+
try {
|
|
364
|
+
const pushResult = await git.pushParallel(projectPath, branch, force);
|
|
365
|
+
results.push = { success: true, branch, ...pushResult };
|
|
366
|
+
} catch (pushError) {
|
|
367
|
+
// Se push falhar mas não houver commits, não é erro crítico
|
|
368
|
+
if (results.commit?.skipped) {
|
|
369
|
+
results.push = { success: true, skipped: true, message: "Nenhum commit para enviar" };
|
|
370
|
+
} else {
|
|
371
|
+
throw pushError;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Resumo final
|
|
376
|
+
const allSuccess = Object.values(results).every(r => r?.success !== false);
|
|
377
|
+
const stepsExecuted = Object.entries(results)
|
|
378
|
+
.filter(([_, r]) => r && !r.skipped)
|
|
379
|
+
.map(([step, _]) => step);
|
|
380
|
+
|
|
381
|
+
return asToolResult({
|
|
382
|
+
success: allSuccess,
|
|
383
|
+
message: `Update completo executado: ${stepsExecuted.join(" → ")}`,
|
|
384
|
+
results,
|
|
385
|
+
stepsExecuted
|
|
386
|
+
}, { tool: 'workflow', action: 'update' });
|
|
387
|
+
}
|
|
288
388
|
return asToolError("VALIDATION_ERROR", `Ação '${action}' não suportada`, {
|
|
289
|
-
availableActions: ["init", "status", "add", "remove", "commit", "push", "ensure-remotes"],
|
|
389
|
+
availableActions: ["init", "status", "add", "remove", "commit", "push", "ensure-remotes", "clean", "update"],
|
|
290
390
|
suggestion: "Use uma das actions disponíveis"
|
|
291
391
|
});
|
|
292
392
|
} catch (e) {
|