@andrebuzeli/git-mcp 13.6.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andrebuzeli/git-mcp",
3
- "version": "13.6.0",
3
+ "version": "14.0.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
@@ -19,6 +19,10 @@ import { createGitSyncTool } from "./tools/git-sync.js";
19
19
  import { createGitIssuesTool } from "./tools/git-issues.js";
20
20
  import { createGitPullsTool } from "./tools/git-pulls.js";
21
21
 
22
+ // #region agent log
23
+ const debugLog = (loc, msg, data) => { fetch('http://127.0.0.1:8242/ingest/e5799a4a-1a0d-4201-a6ce-42835e6f6fc7',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:loc,message:msg,data,timestamp:Date.now(),sessionId:'debug-session'})}).catch(()=>{}); };
24
+ // #endregion
25
+
22
26
  const transport = new StdioServerTransport();
23
27
  const server = new Server({ name: "git-mcpv2", version: "0.0.0" });
24
28
  server.connect(transport);
@@ -66,6 +70,10 @@ server.setRequestHandler(
66
70
  const tool = tools.find(t => t.name === name);
67
71
  if (!tool) return { content: [{ type: "text", text: `Tool não encontrada: ${name}` }], isError: true };
68
72
  try {
73
+ // #region agent log
74
+ const startTime = Date.now();
75
+ debugLog('index.js:call', `START ${name}`, { action: args.action, projectPath: args.projectPath, hypothesisId: 'A' });
76
+ // #endregion
69
77
  if (progressToken) {
70
78
  await server.notification({ method: "notifications/progress", params: { progressToken, progress: 0 } });
71
79
  }
@@ -73,6 +81,9 @@ server.setRequestHandler(
73
81
  if (progressToken) {
74
82
  await server.notification({ method: "notifications/progress", params: { progressToken, progress: 100 } });
75
83
  }
84
+ // #region agent log
85
+ debugLog('index.js:call', `END ${name}`, { duration: Date.now() - startTime, hypothesisId: 'A' });
86
+ // #endregion
76
87
  return result;
77
88
  } catch (e) {
78
89
  return asToolError(e.code || "ERROR", e.message || String(e));
@@ -2,6 +2,10 @@ import { Octokit } from "@octokit/rest";
2
2
  import axios from "axios";
3
3
  import { getProvidersEnv } from "../utils/repoHelpers.js";
4
4
 
5
+ // #region agent log
6
+ const debugLog = (loc, msg, data) => { fetch('http://127.0.0.1:8242/ingest/e5799a4a-1a0d-4201-a6ce-42835e6f6fc7',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:loc,message:msg,data,timestamp:Date.now(),sessionId:'debug-session'})}).catch(()=>{}); };
7
+ // #endregion
8
+
5
9
  export class ProviderManager {
6
10
  constructor() {
7
11
  const { githubToken, giteaUrl, giteaToken } = getProvidersEnv();
@@ -18,12 +22,22 @@ export class ProviderManager {
18
22
  if (!this.github) return "";
19
23
  const now = Date.now();
20
24
  if (this._githubOwner && now - this._ownerFetchedAt < 5 * 60 * 1000) return this._githubOwner;
25
+ // #region agent log
26
+ const t0 = Date.now();
27
+ debugLog('providerManager.js:getGitHubOwner', 'START API call', { hypothesisId: 'B' });
28
+ // #endregion
21
29
  try {
22
30
  const me = await this.github.rest.users.getAuthenticated();
23
31
  this._githubOwner = me.data.login || "";
24
32
  this._ownerFetchedAt = now;
33
+ // #region agent log
34
+ debugLog('providerManager.js:getGitHubOwner', 'END API call', { duration: Date.now() - t0, owner: this._githubOwner, hypothesisId: 'B' });
35
+ // #endregion
25
36
  return this._githubOwner;
26
37
  } catch {
38
+ // #region agent log
39
+ debugLog('providerManager.js:getGitHubOwner', 'FAILED API call', { duration: Date.now() - t0, hypothesisId: 'B' });
40
+ // #endregion
27
41
  return "";
28
42
  }
29
43
  }
@@ -32,6 +46,10 @@ export class ProviderManager {
32
46
  if (!this.giteaUrl || !this.giteaToken) return "";
33
47
  const now = Date.now();
34
48
  if (this._giteaOwner && now - this._ownerFetchedAt < 5 * 60 * 1000) return this._giteaOwner;
49
+ // #region agent log
50
+ const t0 = Date.now();
51
+ debugLog('providerManager.js:getGiteaOwner', 'START API call', { hypothesisId: 'B' });
52
+ // #endregion
35
53
  try {
36
54
  const r = await axios.get(`${this.giteaUrl}/api/v1/user`, {
37
55
  headers: { Authorization: `token ${this.giteaToken}` },
@@ -40,8 +58,14 @@ export class ProviderManager {
40
58
  const d = r.data || {};
41
59
  this._giteaOwner = d.login || d.username || "";
42
60
  this._ownerFetchedAt = now;
61
+ // #region agent log
62
+ debugLog('providerManager.js:getGiteaOwner', 'END API call', { duration: Date.now() - t0, owner: this._giteaOwner, hypothesisId: 'B' });
63
+ // #endregion
43
64
  return this._giteaOwner;
44
65
  } catch {
66
+ // #region agent log
67
+ debugLog('providerManager.js:getGiteaOwner', 'FAILED API call', { duration: Date.now() - t0, hypothesisId: 'B' });
68
+ // #endregion
45
69
  return "";
46
70
  }
47
71
  }
@@ -7,16 +7,52 @@ export function createGitBranchesTool(git) {
7
7
  const inputSchema = {
8
8
  type: "object",
9
9
  properties: {
10
- projectPath: { type: "string" },
11
- action: { type: "string", enum: ["list", "create", "delete", "rename", "checkout"] },
12
- branch: { type: "string" },
13
- newBranch: { type: "string" },
14
- force: { type: "boolean" }
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: true
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
- return asToolResult({ local, remote });
64
+ const current = await git.getCurrentBranch(projectPath);
65
+ return asToolResult({ current, local, remote });
29
66
  }
30
67
  if (action === "create") {
31
- const ref = args.branch;
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(ref)) {
36
- return asToolError("BRANCH_ALREADY_EXISTS", `Branch '${ref}' já existe`, { branch: ref, existingBranches: existing });
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, ref);
39
- return asToolResult({ success: true, branch: ref });
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
- const ref = args.branch;
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(ref)) {
47
- return asToolError("BRANCH_NOT_FOUND", `Branch '${ref}' não encontrada`, { branch: ref, availableBranches: existing });
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 (ref === current) {
51
- return asToolError("CANNOT_DELETE_CURRENT", `Não pode deletar branch atual '${ref}'`, { branch: ref, suggestion: "Faça checkout para outra branch primeiro" });
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, ref, !!args.force);
54
- return asToolResult({ success: true, branch: ref });
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
- const oldName = args.branch;
58
- const newName = args.newBranch;
59
- if (!oldName || !newName) return asToolError("MISSING_PARAMETER", "branch e newBranch são obrigatórios", { parameters: ["branch", "newBranch"] });
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
- const ref = args.branch;
65
- if (!ref) return asToolError("MISSING_PARAMETER", "branch é obrigatório", { parameter: "branch" });
66
- await git.checkout(projectPath, ref);
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 não suportada: ${action}`, { availableActions: ["list", "create", "delete", "rename", "checkout"] });
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: "Gerencia branches", inputSchema, handle };
105
+ return { name: "git-branches", description, inputSchema, handle };
76
106
  }
77
-
@@ -7,16 +7,55 @@ export function createGitConfigTool(git) {
7
7
  const inputSchema = {
8
8
  type: "object",
9
9
  properties: {
10
- projectPath: { type: "string" },
11
- action: { type: "string", enum: ["get", "set", "unset", "list"] },
12
- key: { type: "string" },
13
- value: { type: "string" },
14
- scope: { type: "string", enum: ["local", "global", "system"], default: "local" }
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: true
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 não suportada: ${action}`, { availableActions: ["get", "set", "unset", "list"] });
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: "Gerencia configurações Git (local/global/system)", inputSchema, handle };
90
+ return { name: "git-config", description, inputSchema, handle };
52
91
  }
@@ -7,15 +7,45 @@ export function createGitFilesTool(git) {
7
7
  const inputSchema = {
8
8
  type: "object",
9
9
  properties: {
10
- projectPath: { type: "string" },
11
- action: { type: "string", enum: ["list", "read"] },
12
- filepath: { type: "string" },
13
- ref: { type: "string" }
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: true
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
- availableFiles: files.slice(0, 20),
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 não suportada: ${action}`, { availableActions: ["list", "read"] });
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: "Lista e lê arquivos do repo", inputSchema, handle };
79
+ return { name: "git-files", description, inputSchema, handle };
51
80
  }
@@ -7,15 +7,49 @@ export function createGitHistoryTool(git) {
7
7
  const inputSchema = {
8
8
  type: "object",
9
9
  properties: {
10
- projectPath: { type: "string" },
11
- action: { type: "string", enum: ["log"] },
12
- ref: { type: "string" },
13
- maxCount: { type: "number" }
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: true
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 não suportada: ${action}`, { availableActions: ["log"] });
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: "Histórico de commits", inputSchema, handle };
87
+ return { name: "git-history", description, inputSchema, handle };
53
88
  }
@@ -7,14 +7,51 @@ export function createGitIgnoreTool(git) {
7
7
  const inputSchema = {
8
8
  type: "object",
9
9
  properties: {
10
- projectPath: { type: "string" },
11
- action: { type: "string", enum: ["list", "create", "add", "remove"] },
12
- patterns: { type: "array", items: { type: "string" } }
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: true
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, ex: ['node_modules/', '*.log', '.env']"
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 para adicionar ao .gitignore", { parameter: "patterns" });
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 para remover do .gitignore", { parameter: "patterns" });
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 não suportada: ${action}`, { availableActions: ["list", "create", "add", "remove"] });
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: "Gerencia .gitignore", inputSchema, handle };
95
+ return { name: "git-ignore", description, inputSchema, handle };
59
96
  }