@andrebuzeli/git-mcp 13.7.0 → 14.0.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/tools/git-branches.js +64 -35
- package/src/tools/git-config.js +51 -12
- package/src/tools/git-files.js +39 -10
- package/src/tools/git-history.js +44 -9
- package/src/tools/git-ignore.js +48 -11
- package/src/tools/git-issues.js +56 -18
- package/src/tools/git-pulls.js +70 -21
- package/src/tools/git-remote.js +129 -60
- package/src/tools/git-reset.js +51 -11
- package/src/tools/git-stash.js +60 -20
- package/src/tools/git-sync.js +41 -9
- package/src/tools/git-tags.js +62 -30
- package/src/tools/git-workflow.js +61 -22
package/src/tools/git-sync.js
CHANGED
|
@@ -7,15 +7,47 @@ export function createGitSyncTool(git) {
|
|
|
7
7
|
const inputSchema = {
|
|
8
8
|
type: "object",
|
|
9
9
|
properties: {
|
|
10
|
-
projectPath: {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
projectPath: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "Caminho absoluto do diretório do projeto"
|
|
13
|
+
},
|
|
14
|
+
action: {
|
|
15
|
+
type: "string",
|
|
16
|
+
enum: ["pull", "fetch"],
|
|
17
|
+
description: `Ação a executar:
|
|
18
|
+
- fetch: Baixa commits do remote sem aplicar (seguro, apenas atualiza refs remotas)
|
|
19
|
+
- pull: Baixa E aplica commits do remote (pode gerar conflitos)`
|
|
20
|
+
},
|
|
21
|
+
remote: {
|
|
22
|
+
type: "string",
|
|
23
|
+
description: "Nome do remote específico (github, gitea, origin). Se não especificado, tenta todos"
|
|
24
|
+
},
|
|
25
|
+
branch: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "Branch para sincronizar. Default: branch atual"
|
|
28
|
+
}
|
|
14
29
|
},
|
|
15
30
|
required: ["projectPath", "action"],
|
|
16
|
-
additionalProperties:
|
|
31
|
+
additionalProperties: false
|
|
17
32
|
};
|
|
18
33
|
|
|
34
|
+
const description = `Sincronização com repositórios remotos (GitHub/Gitea).
|
|
35
|
+
|
|
36
|
+
DIFERENÇA FETCH vs PULL:
|
|
37
|
+
- fetch: Seguro - apenas baixa, não modifica working directory
|
|
38
|
+
- pull: Baixa E aplica - pode gerar conflitos se houver mudanças locais
|
|
39
|
+
|
|
40
|
+
QUANDO USAR:
|
|
41
|
+
- fetch: Para ver se há atualizações sem aplicar
|
|
42
|
+
- pull: Para atualizar seu código local com as mudanças do remote
|
|
43
|
+
|
|
44
|
+
FLUXO RECOMENDADO:
|
|
45
|
+
1. git-workflow status → verificar se tem mudanças locais
|
|
46
|
+
2. git-sync fetch → baixar atualizações
|
|
47
|
+
3. git-sync pull → aplicar atualizações (se não tiver conflitos)
|
|
48
|
+
|
|
49
|
+
NOTA: Se pull falhar com conflito, resolva manualmente e faça commit.`;
|
|
50
|
+
|
|
19
51
|
async function handle(args) {
|
|
20
52
|
const validate = ajv.compile(inputSchema);
|
|
21
53
|
if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
|
|
@@ -33,7 +65,7 @@ export function createGitSyncTool(git) {
|
|
|
33
65
|
branch,
|
|
34
66
|
fetched: successful.map(x => x.remote),
|
|
35
67
|
failed: failed.map(x => ({ remote: x.remote, error: x.error })),
|
|
36
|
-
message: successful.length === 0 ? "Nenhum remote alcançado.
|
|
68
|
+
message: successful.length === 0 ? "Nenhum remote alcançado. Use git-remote ensure para configurar." : `Fetch de ${successful.length} remote(s) concluído`
|
|
37
69
|
});
|
|
38
70
|
}
|
|
39
71
|
if (action === "pull") {
|
|
@@ -47,14 +79,14 @@ export function createGitSyncTool(git) {
|
|
|
47
79
|
branch,
|
|
48
80
|
pulled: successful.map(x => x.remote),
|
|
49
81
|
failed: failed.map(x => ({ remote: x.remote, error: x.error })),
|
|
50
|
-
message: successful.length === 0 ? "Nenhum remote alcançado.
|
|
82
|
+
message: successful.length === 0 ? "Nenhum remote alcançado. Use git-remote ensure para configurar." : `Pull de ${successful.length} remote(s) concluído`
|
|
51
83
|
});
|
|
52
84
|
}
|
|
53
|
-
return asToolError("VALIDATION_ERROR", `Ação
|
|
85
|
+
return asToolError("VALIDATION_ERROR", `Ação '${action}' não suportada`, { availableActions: ["pull", "fetch"] });
|
|
54
86
|
} catch (e) {
|
|
55
87
|
return errorToResponse(e);
|
|
56
88
|
}
|
|
57
89
|
}
|
|
58
90
|
|
|
59
|
-
return { name: "git-sync", description
|
|
91
|
+
return { name: "git-sync", description, inputSchema, handle };
|
|
60
92
|
}
|
package/src/tools/git-tags.js
CHANGED
|
@@ -7,16 +7,54 @@ export function createGitTagsTool(git) {
|
|
|
7
7
|
const inputSchema = {
|
|
8
8
|
type: "object",
|
|
9
9
|
properties: {
|
|
10
|
-
projectPath: {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
projectPath: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "Caminho absoluto do diretório do projeto"
|
|
13
|
+
},
|
|
14
|
+
action: {
|
|
15
|
+
type: "string",
|
|
16
|
+
enum: ["list", "create", "delete", "push"],
|
|
17
|
+
description: `Ação a executar:
|
|
18
|
+
- list: Lista todas as tags existentes
|
|
19
|
+
- create: Cria nova tag no commit atual ou especificado
|
|
20
|
+
- delete: Remove uma tag local
|
|
21
|
+
- push: Envia tag para GitHub e Gitea`
|
|
22
|
+
},
|
|
23
|
+
tag: {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: "Nome da tag. Use versionamento semântico: v1.0.0, v1.1.0, v2.0.0"
|
|
26
|
+
},
|
|
27
|
+
ref: {
|
|
28
|
+
type: "string",
|
|
29
|
+
description: "Commit/branch para criar tag (opcional, default: HEAD)"
|
|
30
|
+
},
|
|
31
|
+
message: {
|
|
32
|
+
type: "string",
|
|
33
|
+
description: "Mensagem da tag anotada (opcional). Se fornecido, cria tag anotada"
|
|
34
|
+
}
|
|
15
35
|
},
|
|
16
36
|
required: ["projectPath", "action"],
|
|
17
|
-
additionalProperties:
|
|
37
|
+
additionalProperties: false
|
|
18
38
|
};
|
|
19
39
|
|
|
40
|
+
const description = `Gerenciamento de tags Git para versionamento.
|
|
41
|
+
|
|
42
|
+
AÇÕES DISPONÍVEIS:
|
|
43
|
+
- list: Ver todas as tags existentes
|
|
44
|
+
- create: Criar nova tag de versão
|
|
45
|
+
- delete: Remover tag local
|
|
46
|
+
- push: Enviar tag para GitHub e Gitea
|
|
47
|
+
|
|
48
|
+
VERSIONAMENTO SEMÂNTICO:
|
|
49
|
+
- v1.0.0: Versão inicial
|
|
50
|
+
- v1.1.0: Nova funcionalidade (minor)
|
|
51
|
+
- v1.0.1: Correção de bug (patch)
|
|
52
|
+
- v2.0.0: Breaking change (major)
|
|
53
|
+
|
|
54
|
+
FLUXO TÍPICO:
|
|
55
|
+
1. git-tags create tag='v1.0.0' message='Release inicial'
|
|
56
|
+
2. git-tags push tag='v1.0.0'`;
|
|
57
|
+
|
|
20
58
|
async function handle(args) {
|
|
21
59
|
const validate = ajv.compile(inputSchema);
|
|
22
60
|
if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
|
|
@@ -27,38 +65,32 @@ export function createGitTagsTool(git) {
|
|
|
27
65
|
return asToolResult({ tags, count: tags.length });
|
|
28
66
|
}
|
|
29
67
|
if (action === "create") {
|
|
30
|
-
|
|
31
|
-
if (!tag) return asToolError("MISSING_PARAMETER", "tag é obrigatório", { parameter: "tag" });
|
|
32
|
-
// Auto-verificação: checar se já existe
|
|
68
|
+
if (!args.tag) return asToolError("MISSING_PARAMETER", "tag é obrigatório", { parameter: "tag" });
|
|
33
69
|
const existing = await git.listTags(projectPath);
|
|
34
|
-
if (existing.includes(tag)) {
|
|
35
|
-
return asToolError("TAG_ALREADY_EXISTS", `Tag '${tag}' já existe`, { tag, existingTags: existing });
|
|
70
|
+
if (existing.includes(args.tag)) {
|
|
71
|
+
return asToolError("TAG_ALREADY_EXISTS", `Tag '${args.tag}' já existe`, { tag: args.tag, existingTags: existing });
|
|
36
72
|
}
|
|
37
|
-
await git.createTag(projectPath, tag, args.ref || "HEAD", args.message);
|
|
38
|
-
return asToolResult({ success: true, tag, ref: args.ref || "HEAD" });
|
|
73
|
+
await git.createTag(projectPath, args.tag, args.ref || "HEAD", args.message);
|
|
74
|
+
return asToolResult({ success: true, tag: args.tag, ref: args.ref || "HEAD", message: `Tag '${args.tag}' criada. Use action='push' para enviar ao GitHub/Gitea.` });
|
|
39
75
|
}
|
|
40
76
|
if (action === "delete") {
|
|
41
|
-
|
|
42
|
-
if (!tag) return asToolError("MISSING_PARAMETER", "tag é obrigatório", { parameter: "tag" });
|
|
43
|
-
// Auto-verificação: checar se existe
|
|
77
|
+
if (!args.tag) return asToolError("MISSING_PARAMETER", "tag é obrigatório", { parameter: "tag" });
|
|
44
78
|
const existing = await git.listTags(projectPath);
|
|
45
|
-
if (!existing.includes(tag)) {
|
|
46
|
-
return asToolError("TAG_NOT_FOUND", `Tag '${tag}' não encontrada`, { tag, existingTags: existing });
|
|
79
|
+
if (!existing.includes(args.tag)) {
|
|
80
|
+
return asToolError("TAG_NOT_FOUND", `Tag '${args.tag}' não encontrada`, { tag: args.tag, existingTags: existing });
|
|
47
81
|
}
|
|
48
|
-
await git.deleteTag(projectPath, tag);
|
|
49
|
-
return asToolResult({ success: true, tag });
|
|
82
|
+
await git.deleteTag(projectPath, args.tag);
|
|
83
|
+
return asToolResult({ success: true, tag: args.tag, deleted: true });
|
|
50
84
|
}
|
|
51
85
|
if (action === "push") {
|
|
52
|
-
|
|
53
|
-
if (!tag) return asToolError("MISSING_PARAMETER", "tag é obrigatório", { parameter: "tag" });
|
|
54
|
-
// Auto-verificação: checar se existe
|
|
86
|
+
if (!args.tag) return asToolError("MISSING_PARAMETER", "tag é obrigatório", { parameter: "tag" });
|
|
55
87
|
const existing = await git.listTags(projectPath);
|
|
56
|
-
if (!existing.includes(tag)) {
|
|
57
|
-
return asToolError("TAG_NOT_FOUND", `Tag '${tag}' não existe localmente. Crie primeiro com action='create'`, { tag, existingTags: existing });
|
|
88
|
+
if (!existing.includes(args.tag)) {
|
|
89
|
+
return asToolError("TAG_NOT_FOUND", `Tag '${args.tag}' não existe localmente. Crie primeiro com action='create'`, { tag: args.tag, existingTags: existing });
|
|
58
90
|
}
|
|
59
91
|
const results = await Promise.allSettled([
|
|
60
|
-
git.pushTag(projectPath, "github", tag),
|
|
61
|
-
git.pushTag(projectPath, "gitea", tag)
|
|
92
|
+
git.pushTag(projectPath, "github", args.tag),
|
|
93
|
+
git.pushTag(projectPath, "gitea", args.tag)
|
|
62
94
|
]);
|
|
63
95
|
const pushed = [];
|
|
64
96
|
const failed = [];
|
|
@@ -67,13 +99,13 @@ export function createGitTagsTool(git) {
|
|
|
67
99
|
if (results[1].status === "fulfilled") pushed.push("gitea");
|
|
68
100
|
else failed.push({ remote: "gitea", error: results[1].reason?.message });
|
|
69
101
|
|
|
70
|
-
return asToolResult({ success: pushed.length > 0, tag, pushed, failed });
|
|
102
|
+
return asToolResult({ success: pushed.length > 0, tag: args.tag, pushed, failed });
|
|
71
103
|
}
|
|
72
|
-
return asToolError("VALIDATION_ERROR", `Ação
|
|
104
|
+
return asToolError("VALIDATION_ERROR", `Ação '${action}' não suportada`, { availableActions: ["list", "create", "delete", "push"] });
|
|
73
105
|
} catch (e) {
|
|
74
106
|
return errorToResponse(e);
|
|
75
107
|
}
|
|
76
108
|
}
|
|
77
109
|
|
|
78
|
-
return { name: "git-tags", description
|
|
110
|
+
return { name: "git-tags", description, inputSchema, handle };
|
|
79
111
|
}
|
|
@@ -8,16 +8,56 @@ export function createGitWorkflowTool(pm, git) {
|
|
|
8
8
|
const inputSchema = {
|
|
9
9
|
type: "object",
|
|
10
10
|
properties: {
|
|
11
|
-
projectPath: {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
projectPath: {
|
|
12
|
+
type: "string",
|
|
13
|
+
description: "Caminho absoluto do diretório do projeto (ex: 'C:/Users/user/projeto' ou '/home/user/projeto')"
|
|
14
|
+
},
|
|
15
|
+
action: {
|
|
16
|
+
type: "string",
|
|
17
|
+
enum: ["init", "status", "add", "remove", "commit", "push", "ensure-remotes"],
|
|
18
|
+
description: `Ação a executar:
|
|
19
|
+
- init: Inicializa repositório git local E cria repos no GitHub/Gitea automaticamente
|
|
20
|
+
- status: Retorna arquivos modificados, staged, untracked (use ANTES de commit para ver o que mudou)
|
|
21
|
+
- add: Adiciona arquivos ao staging (use ANTES de commit)
|
|
22
|
+
- remove: Remove arquivos do staging
|
|
23
|
+
- commit: Cria commit com os arquivos staged (use DEPOIS de add)
|
|
24
|
+
- push: Envia commits para GitHub E Gitea em paralelo (use DEPOIS de commit)
|
|
25
|
+
- ensure-remotes: Configura remotes GitHub e Gitea (use se push falhar por falta de remote)`
|
|
26
|
+
},
|
|
27
|
+
files: {
|
|
28
|
+
type: "array",
|
|
29
|
+
items: { type: "string" },
|
|
30
|
+
description: "Lista de arquivos para add/remove. Use ['.'] para todos os arquivos. Ex: ['src/index.js', 'package.json']"
|
|
31
|
+
},
|
|
32
|
+
message: {
|
|
33
|
+
type: "string",
|
|
34
|
+
description: "Mensagem do commit. Obrigatório para action='commit'. Ex: 'feat: adiciona nova funcionalidade'"
|
|
35
|
+
},
|
|
36
|
+
force: {
|
|
37
|
+
type: "boolean",
|
|
38
|
+
description: "Force push (use apenas se push normal falhar com erro de histórico divergente). Default: false"
|
|
39
|
+
}
|
|
16
40
|
},
|
|
17
41
|
required: ["projectPath", "action"],
|
|
18
|
-
additionalProperties:
|
|
42
|
+
additionalProperties: false
|
|
19
43
|
};
|
|
20
44
|
|
|
45
|
+
const description = `Operações Git essenciais com sincronização automática GitHub + Gitea.
|
|
46
|
+
|
|
47
|
+
FLUXO TÍPICO DE TRABALHO:
|
|
48
|
+
1. git-workflow status → ver arquivos modificados
|
|
49
|
+
2. git-workflow add → adicionar arquivos ao staging
|
|
50
|
+
3. git-workflow commit → criar commit
|
|
51
|
+
4. git-workflow push → enviar para GitHub e Gitea
|
|
52
|
+
|
|
53
|
+
QUANDO USAR CADA ACTION:
|
|
54
|
+
- status: Para verificar estado atual do repositório
|
|
55
|
+
- add: Quando há arquivos modificados para commitar
|
|
56
|
+
- commit: Após add, para salvar as mudanças
|
|
57
|
+
- push: Após commit, para sincronizar com remotes
|
|
58
|
+
- init: Apenas uma vez, para novos projetos
|
|
59
|
+
- ensure-remotes: Se push falhar por falta de configuração`;
|
|
60
|
+
|
|
21
61
|
async function handle(args) {
|
|
22
62
|
const validate = ajv.compile(inputSchema);
|
|
23
63
|
if (!validate(args || {})) {
|
|
@@ -29,7 +69,7 @@ export function createGitWorkflowTool(pm, git) {
|
|
|
29
69
|
await git.init(projectPath);
|
|
30
70
|
const repo = getRepoNameFromPath(projectPath);
|
|
31
71
|
const ensured = await pm.ensureRepos({ repoName: repo, createIfMissing: true });
|
|
32
|
-
return asToolResult({ success: true, ensured, message: "Repositório inicializado" });
|
|
72
|
+
return asToolResult({ success: true, ensured, message: "Repositório inicializado localmente e nos providers" });
|
|
33
73
|
}
|
|
34
74
|
if (action === "status") {
|
|
35
75
|
const st = await git.status(projectPath);
|
|
@@ -38,7 +78,7 @@ export function createGitWorkflowTool(pm, git) {
|
|
|
38
78
|
if (action === "add") {
|
|
39
79
|
const files = Array.isArray(args.files) && args.files.length ? args.files : ["."];
|
|
40
80
|
await git.add(projectPath, files);
|
|
41
|
-
return asToolResult({ success: true, files });
|
|
81
|
+
return asToolResult({ success: true, files, nextStep: "Use action='commit' com message para criar o commit" });
|
|
42
82
|
}
|
|
43
83
|
if (action === "remove") {
|
|
44
84
|
const files = Array.isArray(args.files) ? args.files : [];
|
|
@@ -46,9 +86,11 @@ export function createGitWorkflowTool(pm, git) {
|
|
|
46
86
|
return asToolResult({ success: true, files });
|
|
47
87
|
}
|
|
48
88
|
if (action === "commit") {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
89
|
+
if (!args.message) {
|
|
90
|
+
return asToolError("MISSING_PARAMETER", "message é obrigatório para commit", { parameter: "message" });
|
|
91
|
+
}
|
|
92
|
+
const sha = await git.commit(projectPath, args.message);
|
|
93
|
+
return asToolResult({ success: true, sha, message: args.message, nextStep: "Use action='push' para enviar ao GitHub/Gitea" });
|
|
52
94
|
}
|
|
53
95
|
if (action === "ensure-remotes") {
|
|
54
96
|
const repo = getRepoNameFromPath(projectPath);
|
|
@@ -59,20 +101,18 @@ export function createGitWorkflowTool(pm, git) {
|
|
|
59
101
|
const base = pm.giteaUrl?.replace(/\/$/, "") || "";
|
|
60
102
|
const giteaUrl = geOwner && base ? `${base}/${geOwner}/${repo}.git` : "";
|
|
61
103
|
await git.ensureRemotes(projectPath, { githubUrl, giteaUrl });
|
|
62
|
-
|
|
104
|
+
const remotes = await git.listRemotes(projectPath);
|
|
105
|
+
return asToolResult({ success: true, ensured, remotes, nextStep: "Agora pode usar action='push'" });
|
|
63
106
|
}
|
|
64
107
|
if (action === "push") {
|
|
65
108
|
const branch = await git.getCurrentBranch(projectPath);
|
|
66
109
|
const force = !!args.force;
|
|
67
|
-
await git.pushParallel(projectPath, branch, force);
|
|
68
|
-
return asToolResult({ success: true, branch,
|
|
69
|
-
}
|
|
70
|
-
if (action === "sync" || action === "pull") {
|
|
71
|
-
// Basic implementation: fetch + status; full merge/pull can be added
|
|
72
|
-
return asToolResult({ success: true, message: "Operação de pull/sync não-op realizada" });
|
|
110
|
+
const result = await git.pushParallel(projectPath, branch, force);
|
|
111
|
+
return asToolResult({ success: true, branch, ...result });
|
|
73
112
|
}
|
|
74
|
-
return asToolError("VALIDATION_ERROR", `Ação
|
|
75
|
-
availableActions: ["init", "status", "add", "remove", "commit", "push", "
|
|
113
|
+
return asToolError("VALIDATION_ERROR", `Ação '${action}' não suportada`, {
|
|
114
|
+
availableActions: ["init", "status", "add", "remove", "commit", "push", "ensure-remotes"],
|
|
115
|
+
suggestion: "Use uma das actions disponíveis"
|
|
76
116
|
});
|
|
77
117
|
} catch (e) {
|
|
78
118
|
return errorToResponse(e);
|
|
@@ -81,9 +121,8 @@ export function createGitWorkflowTool(pm, git) {
|
|
|
81
121
|
|
|
82
122
|
return {
|
|
83
123
|
name: "git-workflow",
|
|
84
|
-
description
|
|
124
|
+
description,
|
|
85
125
|
inputSchema,
|
|
86
126
|
handle
|
|
87
127
|
};
|
|
88
128
|
}
|
|
89
|
-
|