@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andrebuzeli/git-mcp",
3
- "version": "15.6.0",
3
+ "version": "15.8.0",
4
4
  "private": false,
5
5
  "description": "MCP server para Git com operações locais e sincronização paralela GitHub/Gitea",
6
6
  "license": "MIT",
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.6.0" },
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) {