@andre.buzeli/git-mcp 16.1.3 → 16.1.6

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/README.md CHANGED
@@ -17,10 +17,11 @@ Servidor MCP (Model Context Protocol) para operações Git locais sem git instal
17
17
  }
18
18
  }
19
19
  }
20
- }
20
+ }
21
21
  ```
22
22
 
23
- ## Tools
23
+ ## Tools
24
+
24
25
 
25
26
  - git-workflow: init, status, add, remove, commit, ensure-remotes, push
26
27
  - git-update: status -> add -> commit -> push (all-in-one)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andre.buzeli/git-mcp",
3
- "version": "16.1.3",
3
+ "version": "16.1.6",
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",
@@ -19,14 +19,25 @@
19
19
  "git-mcpv2": "src/index.js"
20
20
  },
21
21
  "scripts": {
22
- "start": "node src/index.js"
22
+ "start": "node src/index.js",
23
+ "test": "vitest run",
24
+ "test:unit": "vitest run tests/unit",
25
+ "test:integration": "vitest run --config tests/integration/vitest.config.js",
26
+ "test:e2e": "vitest run tests/e2e",
27
+ "test:property": "vitest run tests/property",
28
+ "test:watch": "vitest",
29
+ "test:coverage": "vitest run --coverage"
23
30
  },
24
31
  "dependencies": {
25
32
  "@modelcontextprotocol/sdk": "^1.11.0",
26
33
  "@octokit/rest": "^20.0.0",
27
- "ajv": "^8.12.0",
34
+ "ajv": "^8.18.0",
28
35
  "archiver": "^7.0.0",
29
36
  "axios": "^1.7.7",
30
37
  "zod": "^3.23.0"
38
+ },
39
+ "devDependencies": {
40
+ "fast-check": "^4.6.0",
41
+ "vitest": "^4.1.0"
31
42
  }
32
43
  }
package/src/index.js CHANGED
@@ -59,48 +59,44 @@ const server = new Server(
59
59
  },
60
60
  instructions: `# git-mcp — Instruções para AI Agents
61
61
 
62
- ## REGRA #1: projectPath é OBRIGATÓRIO
63
- Toda tool (exceto git-help) exige "projectPath" com o caminho absoluto do projeto aberto no IDE.
64
- O servidor NÃO consegue detectar o projeto automaticamente.
65
- Exemplo: "projectPath": "C:/Users/user/meu-projeto" ou "/home/user/meu-projeto"
66
-
67
- ## REGRA #2: action é OBRIGATÓRIO
68
- Toda tool usa o padrão { "projectPath": "...", "action": "nome-da-action", ...params }.
69
- Consulte o enum de cada tool para saber as actions disponíveis.
70
-
71
- ## Guia Rápido de Tools
72
-
73
- | Necessidade | Tool | Action | Params obrigatórios |
74
- |-------------|------|--------|---------------------|
75
- | Atualizar projeto completo | git-workflow | update | message |
76
- | Ver status do repo | git-workflow | status | - |
77
- | Inicializar repo novo | git-workflow | init | - |
78
- | Ver diferenças locais | git-diff | show | - |
79
- | Comparar branches/commits | git-diff | compare | target |
80
- | Criar branch | git-branches | create | branch |
81
- | Trocar branch | git-branches | checkout | branch |
82
- | Listar branches | git-branches | list | - |
83
- | Merge de branch | git-merge | merge | sourceBranch |
84
- | Criar tag | git-tags | create | tag |
85
- | Clonar repo | git-clone | clone | url |
86
- | Criar issue | git-issues | create | title |
87
- | Criar PR | git-pulls | create | head, title |
88
- | Salvar stash | git-stash | save | - |
89
- | Reset commits | git-reset | soft/mixed/hard | - |
90
- | Configurar git | git-config | get/set | key |
91
- | Gerenciar .gitignore | git-ignore | add/list | patterns |
92
- | Ler arquivo do git | git-files | read | filepath |
93
- | Ver histórico | git-history | log | - |
94
- | Fetch/Pull remoto | git-sync | fetch/pull | - |
95
- | Gerenciar remotes | git-remote | list/ensure | - |
96
-
97
- ## Exemplo Completo (ação mais comum)
98
- \`\`\`json
99
- { "projectPath": "/caminho/do/projeto", "action": "update", "message": "feat: descrição das mudanças" }
100
- \`\`\`
101
-
102
- ## Dica
103
- Use git-help action="listTools" para ver todas as tools e actions disponíveis em tempo real.`
62
+ ATENÇÃO: NUNCA chame uma tool sem os parâmetros obrigatórios. Toda call incompleta retorna erro.
63
+
64
+ ## Parâmetros obrigatórios em TODAS as tools (exceto git-help)
65
+ - "projectPath": caminho absoluto do projeto no sistema de arquivos do usuário. O servidor NÃO detecta automaticamente.
66
+ - "action": string com o nome da action a executar (ver enum no schema de cada tool)
67
+
68
+ ## Exemplos prontos para uso copie e substitua os valores
69
+
70
+ git-workflow (update): {"projectPath":"<PATH>","action":"update","message":"feat: descrição"}
71
+ git-workflow (status): {"projectPath":"<PATH>","action":"status"}
72
+ git-workflow (init): {"projectPath":"<PATH>","action":"init"}
73
+ git-workflow (push): {"projectPath":"<PATH>","action":"push"}
74
+ git-diff (show): {"projectPath":"<PATH>","action":"show"}
75
+ git-diff (compare): {"projectPath":"<PATH>","action":"compare","target":"main"}
76
+ git-branches (list): {"projectPath":"<PATH>","action":"list"}
77
+ git-branches (create): {"projectPath":"<PATH>","action":"create","branch":"feature/x"}
78
+ git-branches (checkout):{"projectPath":"<PATH>","action":"checkout","branch":"main"}
79
+ git-merge (merge): {"projectPath":"<PATH>","action":"merge","branch":"feature/x"}
80
+ git-tags (create): {"projectPath":"<PATH>","action":"create","tag":"v1.0.0"}
81
+ git-tags (push): {"projectPath":"<PATH>","action":"push","tag":"v1.0.0"}
82
+ git-stash (save): {"projectPath":"<PATH>","action":"save"}
83
+ git-stash (pop): {"projectPath":"<PATH>","action":"pop"}
84
+ git-reset (soft): {"projectPath":"<PATH>","action":"soft","ref":"HEAD~1"}
85
+ git-reset (hard): {"projectPath":"<PATH>","action":"hard","ref":"HEAD~1"}
86
+ git-history (log): {"projectPath":"<PATH>","action":"log"}
87
+ git-remote (ensure): {"projectPath":"<PATH>","action":"ensure"}
88
+ git-remote (list): {"projectPath":"<PATH>","action":"list"}
89
+ git-remote (release-create): {"projectPath":"<PATH>","action":"release-create","tag":"v1.0.0","name":"Release v1.0.0"}
90
+ git-worktree (list): {"projectPath":"<PATH>","action":"list"}
91
+ git-worktree (add): {"projectPath":"<PATH>","action":"add","branch":"feature/x"}
92
+ git-sync (pull): {"projectPath":"<PATH>","action":"pull"}
93
+ git-config (get): {"projectPath":"<PATH>","action":"get","key":"user.name"}
94
+ git-ignore (add): {"projectPath":"<PATH>","action":"add","patterns":["node_modules/"]}
95
+ git-files (read): {"projectPath":"<PATH>","action":"read","filepath":"package.json"}
96
+ git-issues (create): {"projectPath":"<PATH>","action":"create","title":"Bug: descrição"}
97
+ git-pulls (create): {"projectPath":"<PATH>","action":"create","head":"feature/x","base":"main","title":"feat: x"}
98
+ git-clone (clone): {"projectPath":"<PARENT_DIR>","action":"clone","url":"https://github.com/user/repo.git"}
99
+ git-help: {"listTools":true} ← única tool sem projectPath/action obrigatórios`
104
100
  }
105
101
  );
106
102
  server.connect(transport);
@@ -22,17 +22,21 @@ Operações Git essenciais.
22
22
 
23
23
  | Action | Descrição | Parâmetros |
24
24
  |--------|-----------|------------|
25
- | **update** | **⭐ RECOMENDADO - Fluxo completo automatizado** | **message, files=["."], gitignore=["pattern"], force, skipIfClean, organization** |
26
- | status | Ver arquivos modificados/staged | - |
25
+ | **update** | **⭐ RECOMENDADO - Fluxo completo automatizado** | **message (obrigatório), files=["."], gitignore=["pattern"], force, skipIfClean, channel, syncBranches, organization, isPublic, dryRun** |
26
+ | status | Ver arquivos modificados/staged | dryRun |
27
27
  | add | Adicionar ao staging | files=["."] ou ["arquivo.js"] |
28
- | commit | Criar commit | message="descrição" (obrigatório) |
29
- | push | Enviar para GitHub+Gitea | force=true se histórico divergir |
30
- | init | Inicializar repo + criar remotes | createGitignore=true, organization |
31
- | ensure-remotes | Configurar remotes | organization |
32
- | clean | Remover arquivos não rastreados | - |
28
+ | remove | Remover do staging | files=["arquivo.js"] |
29
+ | commit | Criar commit | message="descrição" (obrigatório), dryRun |
30
+ | push | Enviar para GitHub+Gitea | force=true se histórico divergir, organization |
31
+ | init | Inicializar repo + criar remotes | createGitignore=true, isPublic, organization, dryRun |
32
+ | ensure-remotes | Configurar remotes | organization, isPublic, dryRun |
33
+ | clean | Remover arquivos não rastreados | dryRun |
34
+ | init-branch | Criar worktree/branch atalho | branch (obrigatório), channel |
33
35
 
34
36
  **Exemplo update:** \`{ "projectPath": "/path", "action": "update", "message": "feat: nova func", "gitignore": ["*.log"] }\`
35
37
  **Com organização:** \`{ "projectPath": "/path", "action": "init", "organization": "automacao-casa" }\`
38
+ **Com channel:** \`{ "projectPath": "/path", "action": "update", "message": "fix: patch", "channel": "beta" }\`
39
+ **Sync worktrees:** \`{ "projectPath": "/path", "action": "update", "message": "sync", "syncBranches": true }\`
36
40
 
37
41
  ---
38
42
 
@@ -51,20 +55,20 @@ Gerenciar branches.
51
55
 
52
56
  ---
53
57
 
54
- ## git-merge (NOVO)
58
+ ## git-merge
55
59
  Mesclar branches.
56
60
 
57
61
  | Action | Descrição | Parâmetros |
58
62
  |--------|-----------|------------|
59
- | merge | Mesclar branch | sourceBranch="feature/x", targetBranch="main" |
60
- | status | Verificar conflitos | - |
61
- | abort | Abortar merge | - |
63
+ | merge | Mesclar branch na branch atual | branch="feature/x" (obrigatório), message="opcional", squash=true |
64
+ | status | Verificar se há merge em andamento | - |
65
+ | abort | Abortar merge com conflitos | - |
62
66
 
63
- **Opções:** fastForwardOnly=true (só FF), squash=true (um commit)
67
+ **Nota:** Faça checkout para a branch destino antes de executar merge. squash=true combina todos os commits em um.
64
68
 
65
69
  ---
66
70
 
67
- ## git-diff (NOVO)
71
+ ## git-diff
68
72
  Visualizar diferenças.
69
73
 
70
74
  | Action | Descrição | Parâmetros |
@@ -75,14 +79,14 @@ Visualizar diferenças.
75
79
 
76
80
  ---
77
81
 
78
- ## git-clone (NOVO)
82
+ ## git-clone
79
83
  Clonar repositórios.
80
84
 
81
85
  | Action | Descrição | Parâmetros |
82
86
  |--------|-----------|------------|
83
- | clone | Clonar repo | url="https://...", targetPath="./pasta" |
87
+ | clone | Clonar repo | url="https://..." (obrigatório), name="pasta-destino" (opcional) |
84
88
 
85
- **Opções:** branch="main", depth=1 (shallow clone)
89
+ **Opções:** branch="main", depth=1 (shallow clone). projectPath é o diretório PAI onde o repo será clonado.
86
90
 
87
91
  ---
88
92
 
@@ -92,9 +96,9 @@ Versionamento semântico.
92
96
  | Action | Descrição | Parâmetros |
93
97
  |--------|-----------|------------|
94
98
  | list | Listar tags | - |
95
- | create | Criar tag | tag="v1.0.0", message="opcional" |
96
- | delete | Deletar tag | tag="v1.0.0" |
97
- | push | Enviar para remotes | tag="v1.0.0" |
99
+ | create | Criar tag | tag="v1.0.0" (obrigatório), ref="HEAD" (opcional), message="opcional" (cria tag anotada) |
100
+ | delete | Deletar tag local | tag="v1.0.0" (obrigatório) |
101
+ | push | Enviar para remotes | tag="v1.0.0" (obrigatório) |
98
102
 
99
103
  **Versões:** v1.0.0 (inicial), v1.1.0 (feature), v1.0.1 (bugfix), v2.0.0 (breaking)
100
104
 
@@ -106,10 +110,10 @@ Salvar mudanças temporariamente.
106
110
  | Action | Descrição | Parâmetros |
107
111
  |--------|-----------|------------|
108
112
  | list | Ver stashes salvos | - |
109
- | save | Salvar mudanças | message="WIP", includeUntracked=true |
110
- | pop | Restaurar e remover | ref="stash@{0}" |
111
- | apply | Restaurar sem remover | ref="stash@{0}" |
112
- | drop | Remover sem restaurar | ref="stash@{0}" |
113
+ | save | Salvar mudanças | message="WIP" (opcional), includeUntracked=true (opcional) |
114
+ | pop | Restaurar e remover | ref="stash@{0}" (opcional, default: mais recente) |
115
+ | apply | Restaurar sem remover | ref="stash@{0}" (opcional, default: mais recente) |
116
+ | drop | Remover sem restaurar | ref="stash@{0}" (opcional, default: mais recente) |
113
117
  | clear | Remover todos | - |
114
118
 
115
119
  ---
@@ -119,12 +123,13 @@ Desfazer commits. ⚠️ CUIDADO
119
123
 
120
124
  | Action | Descrição | Parâmetros |
121
125
  |--------|-----------|------------|
122
- | soft | Mantém mudanças staged | ref="HEAD~1" |
123
- | mixed | Mantém arquivos, remove staging | ref="HEAD~1" |
124
- | hard | ⚠️ DESCARTA tudo | ref="HEAD~1" |
125
- | hard-clean | ⚠️⚠️ DESCARTA + remove untracked | ref="HEAD" |
126
+ | soft | Mantém mudanças staged | ref="HEAD~1" (obrigatório) |
127
+ | mixed | Mantém arquivos, remove staging | ref="HEAD~1" (obrigatório) |
128
+ | hard | ⚠️ DESCARTA tudo | ref="HEAD~1" (obrigatório) |
129
+ | hard-clean | ⚠️⚠️ DESCARTA + remove untracked | ref="HEAD" (obrigatório) |
126
130
 
127
- **Refs:** HEAD~1 (1 commit), HEAD~2 (2 commits), SHA específico
131
+ **Refs:** HEAD~1 (1 commit atrás), HEAD~2 (2 commits), SHA específico do commit
132
+ **Nota:** hard e hard-clean pedem confirmação antes de executar.
128
133
 
129
134
  ---
130
135
 
@@ -146,6 +151,8 @@ Operações em repos remotos GitHub/Gitea.
146
151
  | fork-list | Listar forks | organization |
147
152
  | star-set | Dar estrela | - |
148
153
  | star-unset | Remover estrela | - |
154
+ | subscription-set | Ativar notificações | - |
155
+ | subscription-unset | Desativar notificações | - |
149
156
  | contents-create | Criar arquivo via API | path="", content="", branch="", organization |
150
157
 
151
158
  **Exemplo com organização:** \`{ "projectPath": "/path", "action": "ensure", "organization": "minha-org" }\`
@@ -171,9 +178,9 @@ Gerenciar issues.
171
178
 
172
179
  | Action | Descrição | Parâmetros |
173
180
  |--------|-----------|------------|
174
- | create | Criar issue | title="Bug: ...", body="Descrição" |
175
- | list | Listar issues | - |
176
- | comment | Comentar | number=1, body="Comentário" |
181
+ | create | Criar issue | title="Bug: ..." (obrigatório), body="Descrição", organization |
182
+ | list | Listar issues | organization |
183
+ | comment | Comentar | number=1 (obrigatório), body="Comentário" (obrigatório), organization |
177
184
 
178
185
  ---
179
186
 
@@ -182,9 +189,9 @@ Gerenciar Pull Requests.
182
189
 
183
190
  | Action | Descrição | Parâmetros |
184
191
  |--------|-----------|------------|
185
- | create | Criar PR | head="feature/x", base="main", title="" |
186
- | list | Listar PRs | - |
187
- | files | Ver arquivos do PR | number=1 |
192
+ | create | Criar PR | head="feature/x" (obrigatório), base="main" (obrigatório), title="", body="", organization |
193
+ | list | Listar PRs | organization |
194
+ | files | Ver arquivos do PR | number=1 (obrigatório), organization |
188
195
 
189
196
  ---
190
197
 
@@ -203,12 +210,13 @@ Configurações Git.
203
210
 
204
211
  | Action | Descrição | Parâmetros |
205
212
  |--------|-----------|------------|
206
- | get | Obter valor | key="user.name" |
207
- | set | Definir valor | key="user.name", value="Nome" |
208
- | unset | Remover | key="user.name" |
213
+ | get | Obter valor | key="user.name" (obrigatório), scope="local" |
214
+ | set | Definir valor | key="user.name" (obrigatório), value="Nome", scope="local" |
215
+ | unset | Remover | key="user.name" (obrigatório), scope="local" |
209
216
  | list | Listar todas | scope="local" |
210
217
 
211
- **Keys:** user.name, user.email, core.autocrlf
218
+ **Keys comuns:** user.name, user.email, core.autocrlf, core.editor
219
+ **Scopes:** local (só este repo), global (todos os repos), system (todo o sistema). Default: local
212
220
 
213
221
  ---
214
222
 
@@ -237,9 +245,39 @@ Gerenciar .gitignore.
237
245
  | Action | Descrição | Parâmetros |
238
246
  |--------|-----------|------------|
239
247
  | list | Ver padrões | - |
240
- | create | Criar .gitignore | patterns=["node_modules/","*.log"] |
241
- | add | Adicionar padrões | patterns=["dist/"] |
242
- | remove | Remover padrões | patterns=["*.log"] |
248
+ | create | Criar .gitignore | patterns=["node_modules/","*.log"] (obrigatório) |
249
+ | add | Adicionar padrões | patterns=["dist/"] (obrigatório) |
250
+ | remove | Remover padrões | patterns=["*.log"] (obrigatório) |
251
+
252
+ ---
253
+
254
+ ## git-worktree
255
+ Gerenciar múltiplos diretórios de trabalho (worktrees).
256
+
257
+ | Action | Descrição | Parâmetros |
258
+ |--------|-----------|------------|
259
+ | list | Listar worktrees ativos | - |
260
+ | add | Criar novo worktree | branch="nome" (obrigatório), path="caminho" (opcional), channel="production/beta/alpha", force |
261
+ | remove | Remover worktree | branch="nome" ou path="caminho" (um obrigatório), force |
262
+ | prune | Limpar worktrees deletados manualmente | - |
263
+ | setup | Configurar estrutura recomendada | - |
264
+ | set-channel | Definir canal de deploy da branch | branch="nome" (obrigatório), channel="production/beta/alpha" (obrigatório) |
265
+
266
+ **Channels:** production (branch normal), beta (push para branch-beta), alpha (push para branch-alpha)
267
+ **Nota:** path em add é relativo ao projectPath. Default: ../branch-name
268
+
269
+ ---
270
+
271
+ ## git-help
272
+ Orientação sobre qual tool usar.
273
+
274
+ | Parâmetro | Descrição |
275
+ |-----------|-----------|
276
+ | query="o que fazer" | Recomenda a tool certa para a intenção |
277
+ | listTools=true | Lista todas as tools com descrição e actions |
278
+ | showFlows=true | Mostra fluxos de trabalho comuns passo a passo |
279
+
280
+ **Nota:** Única tool que não exige projectPath.
243
281
 
244
282
  ---
245
283
 
@@ -1,5 +1,5 @@
1
1
  import Ajv from "ajv";
2
- import { asToolError, asToolResult, errorToResponse } from "../utils/errors.js";
2
+ import { asToolError, asToolResult, errorToResponse, handleEmptyCall } from "../utils/errors.js";
3
3
  import { validateProjectPath } from "../utils/repoHelpers.js";
4
4
 
5
5
  const ajv = new Ajv({ allErrors: true });
@@ -59,8 +59,11 @@ CONVENÇÕES DE NOMES:
59
59
  - release/versao: Para releases`;
60
60
 
61
61
  async function handle(args) {
62
+ const emptyHelp = handleEmptyCall(args, inputSchema, "git-branches", { projectPath: "/path/to/project", action: "list" });
63
+ if (emptyHelp) return emptyHelp;
64
+
62
65
  const validate = ajv.compile(inputSchema);
63
- if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
66
+ if (!validate(args)) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
64
67
  const { projectPath, action } = args;
65
68
  try {
66
69
  validateProjectPath(projectPath);
@@ -1,5 +1,5 @@
1
1
  import Ajv from "ajv";
2
- import { asToolError, asToolResult, errorToResponse } from "../utils/errors.js";
2
+ import { asToolError, asToolResult, errorToResponse, handleEmptyCall } from "../utils/errors.js";
3
3
  import { validateProjectPath } from "../utils/repoHelpers.js";
4
4
  import { withRetry } from "../utils/retry.js";
5
5
 
@@ -55,8 +55,11 @@ EXEMPLO:
55
55
  - Shallow clone (rápido): action='clone' url='...' depth=1`;
56
56
 
57
57
  async function handle(args) {
58
+ const emptyHelp = handleEmptyCall(args, inputSchema, "git-clone", { projectPath: "/path/to/parent", action: "clone", url: "https://github.com/user/repo.git" });
59
+ if (emptyHelp) return emptyHelp;
60
+
58
61
  const validate = ajv.compile(inputSchema);
59
- if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
62
+ if (!validate(args)) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
60
63
 
61
64
  // projectPath aqui é o diretório PAI
62
65
  const { projectPath, action, url } = args;
@@ -67,6 +70,14 @@ EXEMPLO:
67
70
  validateProjectPath(projectPath);
68
71
 
69
72
  if (action === "clone") {
73
+ const validation = git.validateRemoteUrl(url);
74
+ if (!validation.valid) {
75
+ return asToolError("INVALID_URL", validation.error, {
76
+ url,
77
+ suggestion: validation.suggestion
78
+ });
79
+ }
80
+
70
81
  await withRetry(() => git.clone(projectPath, url, {
71
82
  name: args.name,
72
83
  branch: args.branch,
@@ -1,5 +1,5 @@
1
1
  import Ajv from "ajv";
2
- import { asToolError, asToolResult, errorToResponse } from "../utils/errors.js";
2
+ import { asToolError, asToolResult, errorToResponse, handleEmptyCall } from "../utils/errors.js";
3
3
  import { validateProjectPath } from "../utils/repoHelpers.js";
4
4
  import { withRetry } from "../utils/retry.js";
5
5
 
@@ -63,8 +63,11 @@ EXEMPLOS:
63
63
  - Ver nome: action='get' key='user.name'`;
64
64
 
65
65
  async function handle(args) {
66
+ const emptyHelp = handleEmptyCall(args, inputSchema, "git-config", { projectPath: "/path/to/project", action: "get", key: "user.name" });
67
+ if (emptyHelp) return emptyHelp;
68
+
66
69
  const validate = ajv.compile(inputSchema);
67
- if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
70
+ if (!validate(args)) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
68
71
  const { projectPath, action } = args;
69
72
  const scope = args.scope || "local";
70
73
  try {
@@ -1,9 +1,23 @@
1
1
  import Ajv from "ajv";
2
- import { asToolError, asToolResult, errorToResponse } from "../utils/errors.js";
2
+ import { asToolError, asToolResult, errorToResponse, handleEmptyCall } from "../utils/errors.js";
3
3
  import { validateProjectPath } from "../utils/repoHelpers.js";
4
4
 
5
5
  const ajv = new Ajv({ allErrors: true });
6
6
 
7
+ const DIFF_MAX_CHARS = 100_000;
8
+
9
+ function applyDiffLimit(diff) {
10
+ if (!diff || diff.length <= DIFF_MAX_CHARS) {
11
+ return { diff: diff || "", truncated: false };
12
+ }
13
+ return {
14
+ diff: diff.substring(0, DIFF_MAX_CHARS),
15
+ truncated: true,
16
+ truncatedAt: DIFF_MAX_CHARS,
17
+ originalSize: diff.length
18
+ };
19
+ }
20
+
7
21
  export function createGitDiffTool(git) {
8
22
  const inputSchema = {
9
23
  type: "object",
@@ -56,28 +70,35 @@ EXEMPLOS:
56
70
  - Estatísticas de mudança: action='stat' target='HEAD~1'`;
57
71
 
58
72
  async function handle(args) {
73
+ const emptyHelp = handleEmptyCall(args, inputSchema, "git-diff", { projectPath: "/path/to/project", action: "show" });
74
+ if (emptyHelp) return emptyHelp;
75
+
59
76
  const validate = ajv.compile(inputSchema);
60
- if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
77
+ if (!validate(args)) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
61
78
  const { projectPath, action } = args;
62
79
 
63
80
  try {
64
81
  validateProjectPath(projectPath);
65
82
 
66
83
  if (action === "show") {
67
- const diff = await git.diff(projectPath, { staged: args.staged });
84
+ const raw = await git.diff(projectPath, { staged: args.staged });
85
+ const { diff, ...truncInfo } = applyDiffLimit(raw);
68
86
  return asToolResult({
69
87
  diff: diff || "Nenhuma diferença encontrada",
70
- staged: !!args.staged
88
+ staged: !!args.staged,
89
+ ...truncInfo
71
90
  });
72
91
  }
73
92
 
74
93
  if (action === "compare") {
75
94
  if (!args.target) return asToolError("MISSING_PARAMETER", "target é obrigatório para compare", { parameter: "target", example: "main" });
76
- const diff = await git.diff(projectPath, { target: args.target, source: args.source });
95
+ const raw = await git.diff(projectPath, { target: args.target, source: args.source });
96
+ const { diff, ...truncInfo } = applyDiffLimit(raw);
77
97
  return asToolResult({
78
98
  diff: diff || "Nenhuma diferença encontrada",
79
99
  target: args.target,
80
- source: args.source || "HEAD"
100
+ source: args.source || "HEAD",
101
+ ...truncInfo
81
102
  });
82
103
  }
83
104
 
@@ -1,5 +1,5 @@
1
1
  import Ajv from "ajv";
2
- import { asToolError, asToolResult, errorToResponse } from "../utils/errors.js";
2
+ import { asToolError, asToolResult, errorToResponse, handleEmptyCall } from "../utils/errors.js";
3
3
  import { validateProjectPath, withRetry } from "../utils/repoHelpers.js";
4
4
 
5
5
  const ajv = new Ajv({ allErrors: true });
@@ -52,8 +52,11 @@ EXEMPLOS:
52
52
  NOTA: Esta tool lê arquivos do histórico Git, não do sistema de arquivos.`;
53
53
 
54
54
  async function handle(args) {
55
+ const emptyHelp = handleEmptyCall(args, inputSchema, "git-files", { projectPath: "/path/to/project", action: "list" });
56
+ if (emptyHelp) return emptyHelp;
57
+
55
58
  const validate = ajv.compile(inputSchema);
56
- if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
59
+ if (!validate(args)) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
57
60
  const { projectPath, action } = args;
58
61
  try {
59
62
  validateProjectPath(projectPath);
@@ -31,7 +31,7 @@ export function createGitHelpTool() {
31
31
  },
32
32
  "git-worktree": {
33
33
  purpose: "Gerenciar worktrees (multibranch)",
34
- actions: ["add", "list", "remove", "setup", "set-channel"],
34
+ actions: ["add", "list", "remove", "prune", "setup", "set-channel"],
35
35
  useWhen: "Trabalhar em múltiplas branches/plataformas simultaneamente no mesmo repo"
36
36
  },
37
37
  "git-branches": {
@@ -76,8 +76,8 @@ export function createGitHelpTool() {
76
76
  },
77
77
  "git-remote": {
78
78
  purpose: "Operações em GitHub/Gitea",
79
- actions: ["list", "ensure", "release-create", "topics-set", "label-create"],
80
- useWhen: "Criar release, configurar repositório remoto"
79
+ actions: ["list", "list-all", "list-orgs", "ensure", "release-create", "topics-set", "label-create", "milestone-create", "fork-create", "fork-list", "star-set", "star-unset", "subscription-set", "subscription-unset", "contents-create", "repo-delete"],
80
+ useWhen: "Criar release, configurar repositório remoto, gerenciar repos remotos"
81
81
  },
82
82
  "git-sync": {
83
83
  purpose: "Sincronizar com remotes",
@@ -108,6 +108,11 @@ export function createGitHelpTool() {
108
108
  purpose: "Gerenciar Pull Requests",
109
109
  actions: ["create", "list", "files"],
110
110
  useWhen: "Propor mudanças para review"
111
+ },
112
+ "git-help": {
113
+ purpose: "Orientação sobre qual tool usar e fluxos de trabalho",
114
+ actions: ["query", "listTools", "showFlows"],
115
+ useWhen: "Não sabe qual tool usar, quer ver lista de tools ou fluxos comuns. Única tool sem projectPath obrigatório."
111
116
  }
112
117
  };
113
118
 
@@ -1,5 +1,5 @@
1
1
  import Ajv from "ajv";
2
- import { asToolError, asToolResult, errorToResponse } from "../utils/errors.js";
2
+ import { asToolError, asToolResult, errorToResponse, handleEmptyCall } from "../utils/errors.js";
3
3
  import { validateProjectPath, withRetry } from "../utils/repoHelpers.js";
4
4
 
5
5
  const ajv = new Ajv({ allErrors: true });
@@ -58,13 +58,17 @@ EXEMPLOS:
58
58
  - Ver histórico de tag: action='log' ref='v1.0.0'`;
59
59
 
60
60
  async function handle(args) {
61
+ const emptyHelp = handleEmptyCall(args, inputSchema, "git-history", { projectPath: "/path/to/project", action: "log" });
62
+ if (emptyHelp) return emptyHelp;
63
+
61
64
  const validate = ajv.compile(inputSchema);
62
- if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
65
+ if (!validate(args)) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
63
66
  const { projectPath, action } = args;
64
67
  try {
65
68
  validateProjectPath(projectPath);
66
69
  if (action === "log") {
67
- const items = await withRetry(() => git.log(projectPath, { ref: args.ref || "HEAD", maxCount: args.maxCount || 50 }), 3, "history-log");
70
+ const maxCount = args.maxCount || 50;
71
+ const items = await withRetry(() => git.log(projectPath, { ref: args.ref || "HEAD", maxCount }), 3, "history-log");
68
72
  if (items.length === 0) {
69
73
  return asToolResult({
70
74
  commits: [],
@@ -83,7 +87,8 @@ EXEMPLOS:
83
87
  date: c.date
84
88
  })),
85
89
  count: items.length,
86
- ref: args.ref || "HEAD"
90
+ ref: args.ref || "HEAD",
91
+ hasMore: items.length === maxCount
87
92
  });
88
93
  }
89
94
  return asToolError("VALIDATION_ERROR", `Ação '${action}' não suportada`, { availableActions: ["log"] });
@@ -1,5 +1,5 @@
1
1
  import Ajv from "ajv";
2
- import { asToolError, asToolResult, errorToResponse } from "../utils/errors.js";
2
+ import { asToolError, asToolResult, errorToResponse, handleEmptyCall } from "../utils/errors.js";
3
3
  import { validateProjectPath } from "../utils/repoHelpers.js";
4
4
  import { withRetry } from "../utils/retry.js";
5
5
 
@@ -59,8 +59,11 @@ EXEMPLOS:
59
59
  - Adicionar padrão: action='add' patterns=['*.tmp']`;
60
60
 
61
61
  async function handle(args) {
62
+ const emptyHelp = handleEmptyCall(args, inputSchema, "git-ignore", { projectPath: "/path/to/project", action: "list" });
63
+ if (emptyHelp) return emptyHelp;
64
+
62
65
  const validate = ajv.compile(inputSchema);
63
- if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
66
+ if (!validate(args)) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
64
67
  const { projectPath, action } = args;
65
68
  const patterns = Array.isArray(args.patterns) ? args.patterns : [];
66
69
  try {
@@ -1,6 +1,6 @@
1
1
  import Ajv from "ajv";
2
2
  import axios from "axios";
3
- import { asToolError, asToolResult, errorToResponse } from "../utils/errors.js";
3
+ import { asToolError, asToolResult, errorToResponse, handleEmptyCall } from "../utils/errors.js";
4
4
  import { getRepoNameFromPath, validateProjectPath, withRetry } from "../utils/repoHelpers.js";
5
5
  import { runBoth } from "../utils/providerExec.js";
6
6
 
@@ -66,8 +66,11 @@ BOAS PRÁTICAS:
66
66
  - Use labels para categorizar (via git-remote label-create)`;
67
67
 
68
68
  async function handle(args) {
69
+ const emptyHelp = handleEmptyCall(args, inputSchema, "git-issues", { projectPath: "/path/to/project", action: "list" });
70
+ if (emptyHelp) return emptyHelp;
71
+
69
72
  const validate = ajv.compile(inputSchema);
70
- if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
73
+ if (!validate(args)) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
71
74
  validateProjectPath(args.projectPath);
72
75
  const repo = getRepoNameFromPath(args.projectPath);
73
76
  try {