@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/package.json
CHANGED
|
@@ -7,16 +7,52 @@ export function createGitBranchesTool(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", "rename", "checkout"],
|
|
17
|
+
description: `Ação a executar:
|
|
18
|
+
- list: Lista todas as branches locais e remotas
|
|
19
|
+
- create: Cria nova branch a partir do HEAD atual
|
|
20
|
+
- delete: Remove uma branch (não pode ser a atual)
|
|
21
|
+
- rename: Renomeia uma branch existente
|
|
22
|
+
- checkout: Muda para outra branch`
|
|
23
|
+
},
|
|
24
|
+
branch: {
|
|
25
|
+
type: "string",
|
|
26
|
+
description: "Nome da branch para create/delete/checkout. Ex: 'feature/nova-funcionalidade', 'bugfix/correcao'"
|
|
27
|
+
},
|
|
28
|
+
newBranch: {
|
|
29
|
+
type: "string",
|
|
30
|
+
description: "Novo nome da branch (apenas para action='rename')"
|
|
31
|
+
},
|
|
32
|
+
force: {
|
|
33
|
+
type: "boolean",
|
|
34
|
+
description: "Forçar delete mesmo se branch não está merged. Default: false"
|
|
35
|
+
}
|
|
15
36
|
},
|
|
16
37
|
required: ["projectPath", "action"],
|
|
17
|
-
additionalProperties:
|
|
38
|
+
additionalProperties: false
|
|
18
39
|
};
|
|
19
40
|
|
|
41
|
+
const description = `Gerenciamento de branches Git.
|
|
42
|
+
|
|
43
|
+
AÇÕES DISPONÍVEIS:
|
|
44
|
+
- list: Ver branches existentes (locais e remotas)
|
|
45
|
+
- create: Criar nova branch para desenvolvimento
|
|
46
|
+
- checkout: Trocar de branch
|
|
47
|
+
- delete: Remover branch após merge
|
|
48
|
+
- rename: Renomear branch
|
|
49
|
+
|
|
50
|
+
CONVENÇÕES DE NOMES:
|
|
51
|
+
- feature/nome: Para novas funcionalidades
|
|
52
|
+
- bugfix/nome: Para correções de bugs
|
|
53
|
+
- hotfix/nome: Para correções urgentes
|
|
54
|
+
- release/versao: Para releases`;
|
|
55
|
+
|
|
20
56
|
async function handle(args) {
|
|
21
57
|
const validate = ajv.compile(inputSchema);
|
|
22
58
|
if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
|
|
@@ -25,53 +61,46 @@ export function createGitBranchesTool(git) {
|
|
|
25
61
|
if (action === "list") {
|
|
26
62
|
const local = await git.listBranches(projectPath, false);
|
|
27
63
|
const remote = await git.listBranches(projectPath, true).catch(() => []);
|
|
28
|
-
|
|
64
|
+
const current = await git.getCurrentBranch(projectPath);
|
|
65
|
+
return asToolResult({ current, local, remote });
|
|
29
66
|
}
|
|
30
67
|
if (action === "create") {
|
|
31
|
-
|
|
32
|
-
if (!ref) return asToolError("MISSING_PARAMETER", "branch é obrigatório", { parameter: "branch" });
|
|
33
|
-
// Auto-correção: verificar se já existe
|
|
68
|
+
if (!args.branch) return asToolError("MISSING_PARAMETER", "branch é obrigatório para criar", { parameter: "branch" });
|
|
34
69
|
const existing = await git.listBranches(projectPath, false);
|
|
35
|
-
if (existing.includes(
|
|
36
|
-
return asToolError("BRANCH_ALREADY_EXISTS", `Branch '${
|
|
70
|
+
if (existing.includes(args.branch)) {
|
|
71
|
+
return asToolError("BRANCH_ALREADY_EXISTS", `Branch '${args.branch}' já existe`, { branch: args.branch, existingBranches: existing });
|
|
37
72
|
}
|
|
38
|
-
await git.createBranch(projectPath,
|
|
39
|
-
return asToolResult({ success: true, branch:
|
|
73
|
+
await git.createBranch(projectPath, args.branch);
|
|
74
|
+
return asToolResult({ success: true, branch: args.branch, message: `Branch '${args.branch}' criada. Use action='checkout' para mudar para ela.` });
|
|
40
75
|
}
|
|
41
76
|
if (action === "delete") {
|
|
42
|
-
|
|
43
|
-
if (!ref) return asToolError("MISSING_PARAMETER", "branch é obrigatório", { parameter: "branch" });
|
|
44
|
-
// Auto-correção: verificar se existe e não é a atual
|
|
77
|
+
if (!args.branch) return asToolError("MISSING_PARAMETER", "branch é obrigatório para deletar", { parameter: "branch" });
|
|
45
78
|
const existing = await git.listBranches(projectPath, false);
|
|
46
|
-
if (!existing.includes(
|
|
47
|
-
return asToolError("BRANCH_NOT_FOUND", `Branch '${
|
|
79
|
+
if (!existing.includes(args.branch)) {
|
|
80
|
+
return asToolError("BRANCH_NOT_FOUND", `Branch '${args.branch}' não encontrada`, { branch: args.branch, availableBranches: existing });
|
|
48
81
|
}
|
|
49
82
|
const current = await git.getCurrentBranch(projectPath);
|
|
50
|
-
if (
|
|
51
|
-
return asToolError("CANNOT_DELETE_CURRENT", `Não pode deletar branch atual '${
|
|
83
|
+
if (args.branch === current) {
|
|
84
|
+
return asToolError("CANNOT_DELETE_CURRENT", `Não pode deletar branch atual '${args.branch}'`, { branch: args.branch, suggestion: "Faça checkout para outra branch primeiro" });
|
|
52
85
|
}
|
|
53
|
-
await git.deleteBranch(projectPath,
|
|
54
|
-
return asToolResult({ success: true, branch:
|
|
86
|
+
await git.deleteBranch(projectPath, args.branch, !!args.force);
|
|
87
|
+
return asToolResult({ success: true, branch: args.branch, deleted: true });
|
|
55
88
|
}
|
|
56
89
|
if (action === "rename") {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
await git.renameBranch(projectPath, oldName, newName);
|
|
61
|
-
return asToolResult({ success: true, from: oldName, to: newName });
|
|
90
|
+
if (!args.branch || !args.newBranch) return asToolError("MISSING_PARAMETER", "branch e newBranch são obrigatórios", { parameters: ["branch", "newBranch"] });
|
|
91
|
+
await git.renameBranch(projectPath, args.branch, args.newBranch);
|
|
92
|
+
return asToolResult({ success: true, from: args.branch, to: args.newBranch });
|
|
62
93
|
}
|
|
63
94
|
if (action === "checkout") {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return asToolResult({ success: true, branch: ref });
|
|
95
|
+
if (!args.branch) return asToolError("MISSING_PARAMETER", "branch é obrigatório para checkout", { parameter: "branch" });
|
|
96
|
+
await git.checkout(projectPath, args.branch);
|
|
97
|
+
return asToolResult({ success: true, branch: args.branch, message: `Mudou para branch '${args.branch}'` });
|
|
68
98
|
}
|
|
69
|
-
return asToolError("VALIDATION_ERROR", `Ação
|
|
99
|
+
return asToolError("VALIDATION_ERROR", `Ação '${action}' não suportada`, { availableActions: ["list", "create", "delete", "rename", "checkout"] });
|
|
70
100
|
} catch (e) {
|
|
71
101
|
return errorToResponse(e);
|
|
72
102
|
}
|
|
73
103
|
}
|
|
74
104
|
|
|
75
|
-
return { name: "git-branches", description
|
|
105
|
+
return { name: "git-branches", description, inputSchema, handle };
|
|
76
106
|
}
|
|
77
|
-
|
package/src/tools/git-config.js
CHANGED
|
@@ -7,16 +7,55 @@ export function createGitConfigTool(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: ["get", "set", "unset", "list"],
|
|
17
|
+
description: `Ação a executar:
|
|
18
|
+
- get: Obter valor de uma configuração específica
|
|
19
|
+
- set: Definir valor de uma configuração
|
|
20
|
+
- unset: Remover uma configuração
|
|
21
|
+
- list: Listar todas as configurações do escopo`
|
|
22
|
+
},
|
|
23
|
+
key: {
|
|
24
|
+
type: "string",
|
|
25
|
+
description: "Chave da configuração. Ex: 'user.name', 'user.email', 'core.autocrlf'"
|
|
26
|
+
},
|
|
27
|
+
value: {
|
|
28
|
+
type: "string",
|
|
29
|
+
description: "Valor a definir (apenas para action='set')"
|
|
30
|
+
},
|
|
31
|
+
scope: {
|
|
32
|
+
type: "string",
|
|
33
|
+
enum: ["local", "global", "system"],
|
|
34
|
+
description: "Escopo da configuração. local: apenas este repo, global: todos os repos do usuário, system: todo o sistema"
|
|
35
|
+
}
|
|
15
36
|
},
|
|
16
37
|
required: ["projectPath", "action"],
|
|
17
|
-
additionalProperties:
|
|
38
|
+
additionalProperties: false
|
|
18
39
|
};
|
|
19
40
|
|
|
41
|
+
const description = `Gerenciamento de configurações Git.
|
|
42
|
+
|
|
43
|
+
CONFIGURAÇÕES COMUNS:
|
|
44
|
+
- user.name: Nome do autor dos commits
|
|
45
|
+
- user.email: Email do autor dos commits
|
|
46
|
+
- core.autocrlf: Conversão de fim de linha (true/false/input)
|
|
47
|
+
- core.editor: Editor padrão para commits
|
|
48
|
+
|
|
49
|
+
ESCOPOS:
|
|
50
|
+
- local: Apenas neste repositório (.git/config)
|
|
51
|
+
- global: Todos os repos do usuário (~/.gitconfig)
|
|
52
|
+
- system: Todo o sistema (/etc/gitconfig)
|
|
53
|
+
|
|
54
|
+
EXEMPLOS:
|
|
55
|
+
- Definir nome: action='set' key='user.name' value='Meu Nome'
|
|
56
|
+
- Definir email: action='set' key='user.email' value='email@example.com'
|
|
57
|
+
- Ver nome: action='get' key='user.name'`;
|
|
58
|
+
|
|
20
59
|
async function handle(args) {
|
|
21
60
|
const validate = ajv.compile(inputSchema);
|
|
22
61
|
if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
|
|
@@ -26,27 +65,27 @@ export function createGitConfigTool(git) {
|
|
|
26
65
|
if (action === "get") {
|
|
27
66
|
if (!args.key) return asToolError("MISSING_PARAMETER", "key é obrigatório", { parameter: "key" });
|
|
28
67
|
const val = await git.getConfig(projectPath, args.key, scope);
|
|
29
|
-
return asToolResult({ key: args.key, value: val, found: val !== undefined });
|
|
68
|
+
return asToolResult({ key: args.key, value: val, found: val !== undefined, scope });
|
|
30
69
|
}
|
|
31
70
|
if (action === "set") {
|
|
32
71
|
if (!args.key) return asToolError("MISSING_PARAMETER", "key é obrigatório", { parameter: "key" });
|
|
33
72
|
await git.setConfig(projectPath, args.key, args.value ?? "", scope);
|
|
34
|
-
return asToolResult({ success: true, key: args.key, value: args.value ?? "" });
|
|
73
|
+
return asToolResult({ success: true, key: args.key, value: args.value ?? "", scope });
|
|
35
74
|
}
|
|
36
75
|
if (action === "unset") {
|
|
37
76
|
if (!args.key) return asToolError("MISSING_PARAMETER", "key é obrigatório", { parameter: "key" });
|
|
38
77
|
await git.unsetConfig(projectPath, args.key, scope);
|
|
39
|
-
return asToolResult({ success: true, key: args.key });
|
|
78
|
+
return asToolResult({ success: true, key: args.key, removed: true, scope });
|
|
40
79
|
}
|
|
41
80
|
if (action === "list") {
|
|
42
81
|
const items = await git.listConfig(projectPath, scope);
|
|
43
|
-
return asToolResult({ scope, items, count: Object.keys(items).length });
|
|
82
|
+
return asToolResult({ scope, configs: items, count: Object.keys(items).length });
|
|
44
83
|
}
|
|
45
|
-
return asToolError("VALIDATION_ERROR", `Ação
|
|
84
|
+
return asToolError("VALIDATION_ERROR", `Ação '${action}' não suportada`, { availableActions: ["get", "set", "unset", "list"] });
|
|
46
85
|
} catch (e) {
|
|
47
86
|
return errorToResponse(e);
|
|
48
87
|
}
|
|
49
88
|
}
|
|
50
89
|
|
|
51
|
-
return { name: "git-config", description
|
|
90
|
+
return { name: "git-config", description, inputSchema, handle };
|
|
52
91
|
}
|
package/src/tools/git-files.js
CHANGED
|
@@ -7,15 +7,45 @@ export function createGitFilesTool(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: ["list", "read"],
|
|
17
|
+
description: `Ação a executar:
|
|
18
|
+
- list: Lista todos os arquivos rastreados pelo Git em um commit/branch
|
|
19
|
+
- read: Lê o conteúdo de um arquivo em um commit/branch específico`
|
|
20
|
+
},
|
|
21
|
+
filepath: {
|
|
22
|
+
type: "string",
|
|
23
|
+
description: "Caminho do arquivo relativo à raiz do repo (action='read'). Ex: 'src/index.js', 'package.json'"
|
|
24
|
+
},
|
|
25
|
+
ref: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "Referência Git para ler (commit SHA, branch, tag). Default: HEAD (commit atual)"
|
|
28
|
+
}
|
|
14
29
|
},
|
|
15
30
|
required: ["projectPath", "action"],
|
|
16
|
-
additionalProperties:
|
|
31
|
+
additionalProperties: false
|
|
17
32
|
};
|
|
18
33
|
|
|
34
|
+
const description = `Leitura de arquivos do repositório Git.
|
|
35
|
+
|
|
36
|
+
QUANDO USAR:
|
|
37
|
+
- Ver arquivos de um commit antigo
|
|
38
|
+
- Comparar versões de arquivo entre commits
|
|
39
|
+
- Listar arquivos rastreados pelo Git
|
|
40
|
+
|
|
41
|
+
EXEMPLOS:
|
|
42
|
+
- Listar arquivos: action='list'
|
|
43
|
+
- Listar arquivos de tag: action='list' ref='v1.0.0'
|
|
44
|
+
- Ler arquivo atual: action='read' filepath='package.json'
|
|
45
|
+
- Ler arquivo de commit antigo: action='read' filepath='src/index.js' ref='abc1234'
|
|
46
|
+
|
|
47
|
+
NOTA: Esta tool lê arquivos do histórico Git, não do sistema de arquivos.`;
|
|
48
|
+
|
|
19
49
|
async function handle(args) {
|
|
20
50
|
const validate = ajv.compile(inputSchema);
|
|
21
51
|
if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
|
|
@@ -23,7 +53,7 @@ export function createGitFilesTool(git) {
|
|
|
23
53
|
try {
|
|
24
54
|
if (action === "list") {
|
|
25
55
|
const files = await git.listFiles(projectPath, args.ref || "HEAD");
|
|
26
|
-
return asToolResult({ files, count: files.length });
|
|
56
|
+
return asToolResult({ files, count: files.length, ref: args.ref || "HEAD" });
|
|
27
57
|
}
|
|
28
58
|
if (action === "read") {
|
|
29
59
|
if (!args.filepath) return asToolError("MISSING_PARAMETER", "filepath é obrigatório", { parameter: "filepath" });
|
|
@@ -31,21 +61,20 @@ export function createGitFilesTool(git) {
|
|
|
31
61
|
const text = await git.readFile(projectPath, args.filepath, args.ref || "HEAD");
|
|
32
62
|
return { content: [{ type: "text", text }], isError: false };
|
|
33
63
|
} catch (e) {
|
|
34
|
-
// Auto-diagnóstico: listar arquivos disponíveis se não encontrar
|
|
35
64
|
const files = await git.listFiles(projectPath, args.ref || "HEAD").catch(() => []);
|
|
36
65
|
return asToolError("FILE_NOT_FOUND", `Arquivo '${args.filepath}' não encontrado`, {
|
|
37
66
|
filepath: args.filepath,
|
|
38
67
|
ref: args.ref || "HEAD",
|
|
39
|
-
|
|
68
|
+
similarFiles: files.filter(f => f.includes(args.filepath.split("/").pop())).slice(0, 10),
|
|
40
69
|
totalFiles: files.length
|
|
41
70
|
});
|
|
42
71
|
}
|
|
43
72
|
}
|
|
44
|
-
return asToolError("VALIDATION_ERROR", `Ação
|
|
73
|
+
return asToolError("VALIDATION_ERROR", `Ação '${action}' não suportada`, { availableActions: ["list", "read"] });
|
|
45
74
|
} catch (e) {
|
|
46
75
|
return errorToResponse(e);
|
|
47
76
|
}
|
|
48
77
|
}
|
|
49
78
|
|
|
50
|
-
return { name: "git-files", description
|
|
79
|
+
return { name: "git-files", description, inputSchema, handle };
|
|
51
80
|
}
|
package/src/tools/git-history.js
CHANGED
|
@@ -7,15 +7,49 @@ export function createGitHistoryTool(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: ["log"],
|
|
17
|
+
description: "Ação a executar: log - exibe histórico de commits"
|
|
18
|
+
},
|
|
19
|
+
ref: {
|
|
20
|
+
type: "string",
|
|
21
|
+
description: "Referência inicial para o log (branch, tag, SHA). Default: HEAD"
|
|
22
|
+
},
|
|
23
|
+
maxCount: {
|
|
24
|
+
type: "number",
|
|
25
|
+
description: "Número máximo de commits a retornar. Default: 50"
|
|
26
|
+
}
|
|
14
27
|
},
|
|
15
28
|
required: ["projectPath", "action"],
|
|
16
|
-
additionalProperties:
|
|
29
|
+
additionalProperties: false
|
|
17
30
|
};
|
|
18
31
|
|
|
32
|
+
const description = `Histórico de commits do repositório Git.
|
|
33
|
+
|
|
34
|
+
QUANDO USAR:
|
|
35
|
+
- Ver commits recentes
|
|
36
|
+
- Encontrar SHA de commit específico para reset/checkout
|
|
37
|
+
- Verificar quem fez qual alteração
|
|
38
|
+
- Ver histórico de uma branch
|
|
39
|
+
|
|
40
|
+
INFORMAÇÕES RETORNADAS:
|
|
41
|
+
- sha: Hash completo do commit
|
|
42
|
+
- shortSha: Hash curto (7 caracteres)
|
|
43
|
+
- message: Mensagem do commit
|
|
44
|
+
- author: Nome do autor
|
|
45
|
+
- email: Email do autor
|
|
46
|
+
- date: Data do commit
|
|
47
|
+
|
|
48
|
+
EXEMPLOS:
|
|
49
|
+
- Ver últimos 10 commits: action='log' maxCount=10
|
|
50
|
+
- Ver histórico de branch: action='log' ref='develop'
|
|
51
|
+
- Ver histórico de tag: action='log' ref='v1.0.0'`;
|
|
52
|
+
|
|
19
53
|
async function handle(args) {
|
|
20
54
|
const validate = ajv.compile(inputSchema);
|
|
21
55
|
if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
|
|
@@ -27,7 +61,7 @@ export function createGitHistoryTool(git) {
|
|
|
27
61
|
return asToolResult({
|
|
28
62
|
commits: [],
|
|
29
63
|
count: 0,
|
|
30
|
-
message: "Nenhum commit encontrado. Use action='commit' para criar o primeiro commit."
|
|
64
|
+
message: "Nenhum commit encontrado. Use git-workflow action='commit' para criar o primeiro commit."
|
|
31
65
|
});
|
|
32
66
|
}
|
|
33
67
|
return asToolResult({
|
|
@@ -40,14 +74,15 @@ export function createGitHistoryTool(git) {
|
|
|
40
74
|
email: c.author.email,
|
|
41
75
|
date: c.date
|
|
42
76
|
})),
|
|
43
|
-
count: items.length
|
|
77
|
+
count: items.length,
|
|
78
|
+
ref: args.ref || "HEAD"
|
|
44
79
|
});
|
|
45
80
|
}
|
|
46
|
-
return asToolError("VALIDATION_ERROR", `Ação
|
|
81
|
+
return asToolError("VALIDATION_ERROR", `Ação '${action}' não suportada`, { availableActions: ["log"] });
|
|
47
82
|
} catch (e) {
|
|
48
83
|
return errorToResponse(e);
|
|
49
84
|
}
|
|
50
85
|
}
|
|
51
86
|
|
|
52
|
-
return { name: "git-history", description
|
|
87
|
+
return { name: "git-history", description, inputSchema, handle };
|
|
53
88
|
}
|
package/src/tools/git-ignore.js
CHANGED
|
@@ -7,14 +7,51 @@ export function createGitIgnoreTool(git) {
|
|
|
7
7
|
const inputSchema = {
|
|
8
8
|
type: "object",
|
|
9
9
|
properties: {
|
|
10
|
-
projectPath: {
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
projectPath: {
|
|
11
|
+
type: "string",
|
|
12
|
+
description: "Caminho absoluto do diretório do projeto"
|
|
13
|
+
},
|
|
14
|
+
action: {
|
|
15
|
+
type: "string",
|
|
16
|
+
enum: ["list", "create", "add", "remove"],
|
|
17
|
+
description: `Ação a executar:
|
|
18
|
+
- list: Lista padrões atuais do .gitignore
|
|
19
|
+
- create: Cria novo .gitignore (sobrescreve existente)
|
|
20
|
+
- add: Adiciona padrões ao .gitignore existente
|
|
21
|
+
- remove: Remove padrões do .gitignore`
|
|
22
|
+
},
|
|
23
|
+
patterns: {
|
|
24
|
+
type: "array",
|
|
25
|
+
items: { type: "string" },
|
|
26
|
+
description: "Padrões para ignorar. Ex: ['node_modules/', '*.log', '.env', 'dist/', '*.tmp']"
|
|
27
|
+
}
|
|
13
28
|
},
|
|
14
29
|
required: ["projectPath", "action"],
|
|
15
|
-
additionalProperties:
|
|
30
|
+
additionalProperties: false
|
|
16
31
|
};
|
|
17
32
|
|
|
33
|
+
const description = `Gerenciamento do arquivo .gitignore.
|
|
34
|
+
|
|
35
|
+
PADRÕES COMUNS:
|
|
36
|
+
- node_modules/: Dependências Node.js
|
|
37
|
+
- *.log: Arquivos de log
|
|
38
|
+
- .env: Variáveis de ambiente (segredos)
|
|
39
|
+
- dist/, build/: Arquivos compilados
|
|
40
|
+
- .DS_Store: Arquivos do macOS
|
|
41
|
+
- Thumbs.db: Arquivos do Windows
|
|
42
|
+
- *.tmp, *.bak: Arquivos temporários
|
|
43
|
+
- .idea/, .vscode/: Configurações de IDE
|
|
44
|
+
|
|
45
|
+
SINTAXE:
|
|
46
|
+
- pasta/: Ignora diretório
|
|
47
|
+
- *.ext: Ignora por extensão
|
|
48
|
+
- !arquivo: Exceção (não ignorar)
|
|
49
|
+
- **/pasta: Ignora em qualquer nível
|
|
50
|
+
|
|
51
|
+
EXEMPLOS:
|
|
52
|
+
- Criar para Node.js: action='create' patterns=['node_modules/', '*.log', '.env', 'dist/']
|
|
53
|
+
- Adicionar padrão: action='add' patterns=['*.tmp']`;
|
|
54
|
+
|
|
18
55
|
async function handle(args) {
|
|
19
56
|
const validate = ajv.compile(inputSchema);
|
|
20
57
|
if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
|
|
@@ -23,37 +60,37 @@ export function createGitIgnoreTool(git) {
|
|
|
23
60
|
try {
|
|
24
61
|
if (action === "list") {
|
|
25
62
|
const items = await git.listGitignore(projectPath);
|
|
26
|
-
return asToolResult({ items, count: items.length });
|
|
63
|
+
return asToolResult({ patterns: items, count: items.length, hasGitignore: items.length > 0 || true });
|
|
27
64
|
}
|
|
28
65
|
if (action === "create") {
|
|
29
66
|
if (patterns.length === 0) {
|
|
30
67
|
return asToolError("MISSING_PARAMETER", "patterns é obrigatório para criar .gitignore", {
|
|
31
68
|
parameter: "patterns",
|
|
32
|
-
suggestion: "Forneça um array de padrões
|
|
69
|
+
suggestion: "Forneça um array de padrões. Ex: ['node_modules/', '*.log', '.env']"
|
|
33
70
|
});
|
|
34
71
|
}
|
|
35
72
|
await git.createGitignore(projectPath, patterns);
|
|
36
|
-
return asToolResult({ success: true, patterns });
|
|
73
|
+
return asToolResult({ success: true, patterns, message: ".gitignore criado" });
|
|
37
74
|
}
|
|
38
75
|
if (action === "add") {
|
|
39
76
|
if (patterns.length === 0) {
|
|
40
|
-
return asToolError("MISSING_PARAMETER", "patterns é obrigatório
|
|
77
|
+
return asToolError("MISSING_PARAMETER", "patterns é obrigatório", { parameter: "patterns" });
|
|
41
78
|
}
|
|
42
79
|
await git.addToGitignore(projectPath, patterns);
|
|
43
80
|
return asToolResult({ success: true, added: patterns });
|
|
44
81
|
}
|
|
45
82
|
if (action === "remove") {
|
|
46
83
|
if (patterns.length === 0) {
|
|
47
|
-
return asToolError("MISSING_PARAMETER", "patterns é obrigatório
|
|
84
|
+
return asToolError("MISSING_PARAMETER", "patterns é obrigatório", { parameter: "patterns" });
|
|
48
85
|
}
|
|
49
86
|
await git.removeFromGitignore(projectPath, patterns);
|
|
50
87
|
return asToolResult({ success: true, removed: patterns });
|
|
51
88
|
}
|
|
52
|
-
return asToolError("VALIDATION_ERROR", `Ação
|
|
89
|
+
return asToolError("VALIDATION_ERROR", `Ação '${action}' não suportada`, { availableActions: ["list", "create", "add", "remove"] });
|
|
53
90
|
} catch (e) {
|
|
54
91
|
return errorToResponse(e);
|
|
55
92
|
}
|
|
56
93
|
}
|
|
57
94
|
|
|
58
|
-
return { name: "git-ignore", description
|
|
95
|
+
return { name: "git-ignore", description, inputSchema, handle };
|
|
59
96
|
}
|
package/src/tools/git-issues.js
CHANGED
|
@@ -10,49 +10,87 @@ export function createGitIssuesTool(pm) {
|
|
|
10
10
|
const inputSchema = {
|
|
11
11
|
type: "object",
|
|
12
12
|
properties: {
|
|
13
|
-
projectPath: {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
projectPath: {
|
|
14
|
+
type: "string",
|
|
15
|
+
description: "Caminho absoluto do diretório do projeto"
|
|
16
|
+
},
|
|
17
|
+
action: {
|
|
18
|
+
type: "string",
|
|
19
|
+
enum: ["create", "list", "comment"],
|
|
20
|
+
description: `Ação a executar:
|
|
21
|
+
- create: Cria nova issue no GitHub E Gitea
|
|
22
|
+
- list: Lista issues existentes
|
|
23
|
+
- comment: Adiciona comentário a uma issue existente`
|
|
24
|
+
},
|
|
25
|
+
title: {
|
|
26
|
+
type: "string",
|
|
27
|
+
description: "Título da issue (obrigatório para action='create'). Ex: 'Bug: aplicação crasha ao clicar em salvar'"
|
|
28
|
+
},
|
|
29
|
+
body: {
|
|
30
|
+
type: "string",
|
|
31
|
+
description: "Descrição detalhada da issue ou texto do comentário"
|
|
32
|
+
},
|
|
33
|
+
number: {
|
|
34
|
+
type: "number",
|
|
35
|
+
description: "Número da issue para comentar (obrigatório para action='comment')"
|
|
36
|
+
}
|
|
18
37
|
},
|
|
19
38
|
required: ["projectPath", "action"],
|
|
20
|
-
additionalProperties:
|
|
39
|
+
additionalProperties: false
|
|
21
40
|
};
|
|
22
41
|
|
|
42
|
+
const description = `Gerenciamento de Issues no GitHub e Gitea.
|
|
43
|
+
|
|
44
|
+
QUANDO USAR:
|
|
45
|
+
- Reportar bugs encontrados
|
|
46
|
+
- Sugerir novas funcionalidades
|
|
47
|
+
- Documentar tarefas a fazer
|
|
48
|
+
- Acompanhar progresso do projeto
|
|
49
|
+
|
|
50
|
+
AÇÕES:
|
|
51
|
+
- create: Criar nova issue (executa em AMBOS os providers)
|
|
52
|
+
- list: Ver issues existentes
|
|
53
|
+
- comment: Comentar em issue existente
|
|
54
|
+
|
|
55
|
+
BOAS PRÁTICAS:
|
|
56
|
+
- Use títulos descritivos
|
|
57
|
+
- Inclua passos para reproduzir bugs
|
|
58
|
+
- Use labels para categorizar (via git-remote label-create)`;
|
|
59
|
+
|
|
23
60
|
async function handle(args) {
|
|
24
61
|
const validate = ajv.compile(inputSchema);
|
|
25
62
|
if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
|
|
26
63
|
const repo = getRepoNameFromPath(args.projectPath);
|
|
27
64
|
try {
|
|
28
65
|
if (args.action === "create") {
|
|
66
|
+
if (!args.title) return asToolError("MISSING_PARAMETER", "title é obrigatório para criar issue", { parameter: "title" });
|
|
29
67
|
const out = await runBoth(pm, {
|
|
30
|
-
github: async (owner) => { const r = await pm.github.rest.issues.create({ owner, repo, title: args.title
|
|
31
|
-
gitea: async (owner) => { const base = pm.giteaUrl.replace(/\/$/, ""); const r = await axios.post(`${base}/api/v1/repos/${owner}/${repo}/issues`, { title: args.title
|
|
68
|
+
github: async (owner) => { const r = await pm.github.rest.issues.create({ owner, repo, title: args.title, body: args.body || "" }); return { ok: true, number: r.data.number, url: r.data.html_url }; },
|
|
69
|
+
gitea: async (owner) => { const base = pm.giteaUrl.replace(/\/$/, ""); const r = await axios.post(`${base}/api/v1/repos/${owner}/${repo}/issues`, { title: args.title, body: args.body || "" }, { headers: { Authorization: `token ${pm.giteaToken}` } }); return { ok: true, number: r.data?.number }; }
|
|
32
70
|
});
|
|
33
|
-
return asToolResult({ success: !!(out.github?.ok || out.gitea?.ok), providers: out });
|
|
71
|
+
return asToolResult({ success: !!(out.github?.ok || out.gitea?.ok), title: args.title, providers: out });
|
|
34
72
|
}
|
|
35
73
|
if (args.action === "list") {
|
|
36
74
|
const out = await runBoth(pm, {
|
|
37
|
-
github: async (owner) => { const r = await pm.github.rest.issues.listForRepo({ owner, repo, state: "all" }); return { ok: true, count: r.data.length }; },
|
|
38
|
-
gitea: async (owner) => { const base = pm.giteaUrl.replace(/\/$/, ""); const r = await axios.get(`${base}/api/v1/repos/${owner}/${repo}/issues`, { headers: { Authorization: `token ${pm.giteaToken}` } }); return { ok: true, count: (r.data||[]).length }; }
|
|
75
|
+
github: async (owner) => { const r = await pm.github.rest.issues.listForRepo({ owner, repo, state: "all", per_page: 30 }); return { ok: true, count: r.data.length, issues: r.data.slice(0, 10).map(i => ({ number: i.number, title: i.title, state: i.state })) }; },
|
|
76
|
+
gitea: async (owner) => { const base = pm.giteaUrl.replace(/\/$/, ""); const r = await axios.get(`${base}/api/v1/repos/${owner}/${repo}/issues?state=all`, { headers: { Authorization: `token ${pm.giteaToken}` } }); return { ok: true, count: (r.data||[]).length, issues: (r.data||[]).slice(0, 10).map(i => ({ number: i.number, title: i.title, state: i.state })) }; }
|
|
39
77
|
});
|
|
40
78
|
return asToolResult({ providers: out });
|
|
41
79
|
}
|
|
42
80
|
if (args.action === "comment") {
|
|
43
|
-
|
|
44
|
-
if (!
|
|
81
|
+
if (!args.number) return asToolError("MISSING_PARAMETER", "number é obrigatório para comentar", { parameter: "number" });
|
|
82
|
+
if (!args.body) return asToolError("MISSING_PARAMETER", "body é obrigatório para comentar", { parameter: "body" });
|
|
45
83
|
const out = await runBoth(pm, {
|
|
46
|
-
github: async (owner) => { await pm.github.rest.issues.createComment({ owner, repo, issue_number:
|
|
47
|
-
gitea: async (owner) => { const base = pm.giteaUrl.replace(/\/$/, ""); await axios.post(`${base}/api/v1/repos/${owner}/${repo}/issues/${
|
|
84
|
+
github: async (owner) => { await pm.github.rest.issues.createComment({ owner, repo, issue_number: args.number, body: args.body }); return { ok: true }; },
|
|
85
|
+
gitea: async (owner) => { const base = pm.giteaUrl.replace(/\/$/, ""); await axios.post(`${base}/api/v1/repos/${owner}/${repo}/issues/${args.number}/comments`, { body: args.body }, { headers: { Authorization: `token ${pm.giteaToken}` } }); return { ok: true }; }
|
|
48
86
|
});
|
|
49
|
-
return asToolResult({ success: !!(out.github?.ok || out.gitea?.ok), providers: out });
|
|
87
|
+
return asToolResult({ success: !!(out.github?.ok || out.gitea?.ok), issueNumber: args.number, providers: out });
|
|
50
88
|
}
|
|
51
|
-
return asToolError("VALIDATION_ERROR", `Ação
|
|
89
|
+
return asToolError("VALIDATION_ERROR", `Ação '${args.action}' não suportada`, { availableActions: ["create", "list", "comment"] });
|
|
52
90
|
} catch (e) {
|
|
53
91
|
return errorToResponse(e);
|
|
54
92
|
}
|
|
55
93
|
}
|
|
56
94
|
|
|
57
|
-
return { name: "git-issues", description
|
|
95
|
+
return { name: "git-issues", description, inputSchema, handle };
|
|
58
96
|
}
|