@andrebuzeli/git-mcp 15.9.1 → 15.9.2
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/index.js +1 -1
- package/src/tools/git-workflow.js +57 -157
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -47,7 +47,7 @@ if (!hasGitHub && !hasGitea) {
|
|
|
47
47
|
|
|
48
48
|
const transport = new StdioServerTransport();
|
|
49
49
|
const server = new Server(
|
|
50
|
-
{ name: "git-mcpv2", version: "15.
|
|
50
|
+
{ name: "git-mcpv2", version: "15.9.2" },
|
|
51
51
|
{ capabilities: { tools: {}, resources: {}, prompts: {} } }
|
|
52
52
|
);
|
|
53
53
|
server.connect(transport);
|
|
@@ -8,13 +8,13 @@ export function createGitWorkflowTool(pm, git) {
|
|
|
8
8
|
const inputSchema = {
|
|
9
9
|
type: "object",
|
|
10
10
|
properties: {
|
|
11
|
-
projectPath: {
|
|
12
|
-
type: "string",
|
|
13
|
-
description: "Caminho absoluto do diretório do projeto (ex: 'C:/Users/user/projeto' ou '/home/user/projeto')"
|
|
11
|
+
projectPath: {
|
|
12
|
+
type: "string",
|
|
13
|
+
description: "Caminho absoluto do diretório do projeto (ex: 'C:/Users/user/projeto' ou '/home/user/projeto')"
|
|
14
14
|
},
|
|
15
|
-
action: {
|
|
16
|
-
type: "string",
|
|
17
|
-
enum: ["init", "status", "add", "remove", "commit", "push", "ensure-remotes", "clean"
|
|
15
|
+
action: {
|
|
16
|
+
type: "string",
|
|
17
|
+
enum: ["init", "status", "add", "remove", "commit", "push", "ensure-remotes", "clean"],
|
|
18
18
|
description: `Ação a executar:
|
|
19
19
|
- init: Inicializa repositório git local E cria repos no GitHub/Gitea automaticamente
|
|
20
20
|
- status: Retorna arquivos modificados, staged, untracked (use ANTES de commit para ver o que mudou)
|
|
@@ -23,20 +23,19 @@ export function createGitWorkflowTool(pm, git) {
|
|
|
23
23
|
- commit: Cria commit com os arquivos staged (use DEPOIS de add)
|
|
24
24
|
- push: Envia commits para GitHub E Gitea em paralelo (use DEPOIS de commit)
|
|
25
25
|
- ensure-remotes: Configura remotes GitHub e Gitea (use se push falhar por falta de remote)
|
|
26
|
-
- clean: Remove arquivos não rastreados do working directory
|
|
27
|
-
- update: Atualiza projeto completo: init (se necessário), add, commit, ensure-remotes e push`
|
|
26
|
+
- clean: Remove arquivos não rastreados do working directory`
|
|
28
27
|
},
|
|
29
|
-
files: {
|
|
30
|
-
type: "array",
|
|
28
|
+
files: {
|
|
29
|
+
type: "array",
|
|
31
30
|
items: { type: "string" },
|
|
32
31
|
description: "Lista de arquivos para add/remove. Use ['.'] para todos os arquivos. Ex: ['src/index.js', 'package.json']"
|
|
33
32
|
},
|
|
34
|
-
message: {
|
|
33
|
+
message: {
|
|
35
34
|
type: "string",
|
|
36
|
-
description: "Mensagem do commit. Obrigatório para action='commit'.
|
|
35
|
+
description: "Mensagem do commit. Obrigatório para action='commit'. Ex: 'feat: adiciona nova funcionalidade'"
|
|
37
36
|
},
|
|
38
|
-
force: {
|
|
39
|
-
type: "boolean",
|
|
37
|
+
force: {
|
|
38
|
+
type: "boolean",
|
|
40
39
|
description: "Force push (use apenas se push normal falhar com erro de histórico divergente). Default: false"
|
|
41
40
|
},
|
|
42
41
|
createGitignore: {
|
|
@@ -48,7 +47,7 @@ export function createGitWorkflowTool(pm, git) {
|
|
|
48
47
|
description: "Se true, repositório será PÚBLICO. Default: false (privado). Aplica-se a action='init' e 'ensure-remotes'"
|
|
49
48
|
},
|
|
50
49
|
dryRun: {
|
|
51
|
-
type: "boolean",
|
|
50
|
+
type: "boolean",
|
|
52
51
|
description: "Se true, simula a operação sem executar (útil para testes). Default: false"
|
|
53
52
|
}
|
|
54
53
|
},
|
|
@@ -72,7 +71,6 @@ QUANDO USAR CADA ACTION:
|
|
|
72
71
|
- init: Apenas uma vez, para novos projetos (cria .gitignore automaticamente)
|
|
73
72
|
- ensure-remotes: Se push falhar por falta de configuração
|
|
74
73
|
- clean: Limpar arquivos não rastreados
|
|
75
|
-
- update: Para atualizar projeto completo (init + add + commit + push) - mais rápido que fazer cada ação separadamente
|
|
76
74
|
|
|
77
75
|
EXEMPLOS DE USO:
|
|
78
76
|
• Iniciar projeto: { "projectPath": "/path/to/project", "action": "init" }
|
|
@@ -90,29 +88,29 @@ EXEMPLOS DE USO:
|
|
|
90
88
|
try {
|
|
91
89
|
// #region agent log
|
|
92
90
|
if (process.env.DEBUG_AGENT_LOG) {
|
|
93
|
-
fetch('http://127.0.0.1:8243/ingest/a7114eec-653b-43b0-9f09-7073baee17bf',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'run1',hypothesisId:'H1',location:'git-workflow.js:handle-entry',message:'handle start',data:{action,projectPath},timestamp:Date.now()})}).catch(()=>{});
|
|
91
|
+
fetch('http://127.0.0.1:8243/ingest/a7114eec-653b-43b0-9f09-7073baee17bf', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sessionId: 'debug-session', runId: 'run1', hypothesisId: 'H1', location: 'git-workflow.js:handle-entry', message: 'handle start', data: { action, projectPath }, timestamp: Date.now() }) }).catch(() => { });
|
|
94
92
|
}
|
|
95
93
|
// #endregion
|
|
96
94
|
|
|
97
95
|
validateProjectPath(projectPath);
|
|
98
96
|
// #region agent log
|
|
99
97
|
if (process.env.DEBUG_AGENT_LOG) {
|
|
100
|
-
fetch('http://127.0.0.1:8243/ingest/a7114eec-653b-43b0-9f09-7073baee17bf',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'run1',hypothesisId:'H1',location:'git-workflow.js:handle-validated',message:'projectPath validated',data:{action},timestamp:Date.now()})}).catch(()=>{});
|
|
98
|
+
fetch('http://127.0.0.1:8243/ingest/a7114eec-653b-43b0-9f09-7073baee17bf', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sessionId: 'debug-session', runId: 'run1', hypothesisId: 'H1', location: 'git-workflow.js:handle-validated', message: 'projectPath validated', data: { action }, timestamp: Date.now() }) }).catch(() => { });
|
|
101
99
|
}
|
|
102
100
|
// #endregion
|
|
103
101
|
if (action === "init") {
|
|
104
102
|
if (args.dryRun) {
|
|
105
|
-
return asToolResult({
|
|
106
|
-
success: true,
|
|
103
|
+
return asToolResult({
|
|
104
|
+
success: true,
|
|
107
105
|
dryRun: true,
|
|
108
106
|
message: "DRY RUN: Repositório seria inicializado localmente e nos providers",
|
|
109
107
|
repoName: getRepoNameFromPath(projectPath),
|
|
110
108
|
gitignoreCreated: shouldCreateGitignore
|
|
111
109
|
});
|
|
112
110
|
}
|
|
113
|
-
|
|
111
|
+
|
|
114
112
|
await git.init(projectPath);
|
|
115
|
-
|
|
113
|
+
|
|
116
114
|
// Criar .gitignore baseado no tipo de projeto
|
|
117
115
|
const shouldCreateGitignore = args.createGitignore !== false;
|
|
118
116
|
let gitignoreCreated = false;
|
|
@@ -122,12 +120,12 @@ EXEMPLOS DE USO:
|
|
|
122
120
|
await git.createGitignore(projectPath, patterns);
|
|
123
121
|
gitignoreCreated = true;
|
|
124
122
|
}
|
|
125
|
-
|
|
123
|
+
|
|
126
124
|
const repo = getRepoNameFromPath(projectPath);
|
|
127
125
|
const isPublic = args.isPublic === true; // Default: privado
|
|
128
126
|
const ensured = await pm.ensureRepos({ repoName: repo, createIfMissing: true, isPublic });
|
|
129
|
-
return asToolResult({
|
|
130
|
-
success: true,
|
|
127
|
+
return asToolResult({
|
|
128
|
+
success: true,
|
|
131
129
|
ensured,
|
|
132
130
|
isPrivate: !isPublic,
|
|
133
131
|
gitignoreCreated,
|
|
@@ -138,24 +136,24 @@ EXEMPLOS DE USO:
|
|
|
138
136
|
const st = await git.status(projectPath);
|
|
139
137
|
// #region agent log
|
|
140
138
|
if (process.env.DEBUG_AGENT_LOG) {
|
|
141
|
-
fetch('http://127.0.0.1:8243/ingest/a7114eec-653b-43b0-9f09-7073baee17bf',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'run1',hypothesisId:'H2',location:'git-workflow.js:status',message:'status result',data:{modified:st.modified.length,created:st.created.length,deleted:st.deleted.length,notAdded:st.not_added.length,isClean:st.isClean},timestamp:Date.now()})}).catch(()=>{});
|
|
139
|
+
fetch('http://127.0.0.1:8243/ingest/a7114eec-653b-43b0-9f09-7073baee17bf', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sessionId: 'debug-session', runId: 'run1', hypothesisId: 'H2', location: 'git-workflow.js:status', message: 'status result', data: { modified: st.modified.length, created: st.created.length, deleted: st.deleted.length, notAdded: st.not_added.length, isClean: st.isClean }, timestamp: Date.now() }) }).catch(() => { });
|
|
142
140
|
}
|
|
143
141
|
// #endregion
|
|
144
|
-
|
|
142
|
+
|
|
145
143
|
if (args.dryRun) {
|
|
146
|
-
return asToolResult({
|
|
147
|
-
success: true,
|
|
144
|
+
return asToolResult({
|
|
145
|
+
success: true,
|
|
148
146
|
dryRun: true,
|
|
149
147
|
message: "DRY RUN: Status seria verificado",
|
|
150
148
|
...st
|
|
151
149
|
});
|
|
152
150
|
}
|
|
153
|
-
|
|
151
|
+
|
|
154
152
|
// Adicionar contexto para AI Agent decidir próximo passo
|
|
155
153
|
const hasUnstaged = st.not_added?.length > 0 || st.modified?.length > 0 || st.created?.length > 0;
|
|
156
154
|
const hasStaged = st.staged?.length > 0;
|
|
157
155
|
const hasConflicts = st.conflicted?.length > 0;
|
|
158
|
-
|
|
156
|
+
|
|
159
157
|
let _aiContext = {
|
|
160
158
|
needsAdd: hasUnstaged && !hasStaged,
|
|
161
159
|
readyToCommit: hasStaged && !hasConflicts,
|
|
@@ -163,7 +161,7 @@ EXEMPLOS DE USO:
|
|
|
163
161
|
isClean: st.isClean,
|
|
164
162
|
suggestedAction: null
|
|
165
163
|
};
|
|
166
|
-
|
|
164
|
+
|
|
167
165
|
if (hasConflicts) {
|
|
168
166
|
_aiContext.suggestedAction = "Resolva os conflitos manualmente antes de continuar";
|
|
169
167
|
} else if (hasStaged) {
|
|
@@ -173,21 +171,21 @@ EXEMPLOS DE USO:
|
|
|
173
171
|
} else {
|
|
174
172
|
_aiContext.suggestedAction = "Working tree limpa. Modifique arquivos ou use action='push' se há commits pendentes";
|
|
175
173
|
}
|
|
176
|
-
|
|
174
|
+
|
|
177
175
|
return asToolResult({ ...st, _aiContext }, { tool: 'workflow', action: 'status' });
|
|
178
176
|
}
|
|
179
177
|
if (action === "add") {
|
|
180
178
|
const files = Array.isArray(args.files) && args.files.length ? args.files : ["."];
|
|
181
|
-
|
|
179
|
+
|
|
182
180
|
if (args.dryRun) {
|
|
183
|
-
return asToolResult({
|
|
184
|
-
success: true,
|
|
181
|
+
return asToolResult({
|
|
182
|
+
success: true,
|
|
185
183
|
dryRun: true,
|
|
186
184
|
message: `DRY RUN: Arquivos seriam adicionados: ${files.join(", ")}`,
|
|
187
185
|
files
|
|
188
186
|
});
|
|
189
187
|
}
|
|
190
|
-
|
|
188
|
+
|
|
191
189
|
await git.add(projectPath, files);
|
|
192
190
|
return asToolResult({ success: true, files }, { tool: 'workflow', action: 'add' });
|
|
193
191
|
}
|
|
@@ -200,19 +198,19 @@ EXEMPLOS DE USO:
|
|
|
200
198
|
if (!args.message) {
|
|
201
199
|
return asToolError("MISSING_PARAMETER", "message é obrigatório para commit", { parameter: "message" });
|
|
202
200
|
}
|
|
203
|
-
|
|
201
|
+
|
|
204
202
|
if (args.dryRun) {
|
|
205
|
-
return asToolResult({
|
|
206
|
-
success: true,
|
|
203
|
+
return asToolResult({
|
|
204
|
+
success: true,
|
|
207
205
|
dryRun: true,
|
|
208
206
|
message: `DRY RUN: Commit seria criado com mensagem: "${args.message}"`
|
|
209
207
|
});
|
|
210
208
|
}
|
|
211
|
-
|
|
209
|
+
|
|
212
210
|
const sha = await git.commit(projectPath, args.message);
|
|
213
211
|
// #region agent log
|
|
214
212
|
if (process.env.DEBUG_AGENT_LOG) {
|
|
215
|
-
fetch('http://127.0.0.1:8243/ingest/a7114eec-653b-43b0-9f09-7073baee17bf',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({sessionId:'debug-session',runId:'run1',hypothesisId:'H3',location:'git-workflow.js:commit',message:'commit created',data:{sha,message:args.message},timestamp:Date.now()})}).catch(()=>{});
|
|
213
|
+
fetch('http://127.0.0.1:8243/ingest/a7114eec-653b-43b0-9f09-7073baee17bf', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sessionId: 'debug-session', runId: 'run1', hypothesisId: 'H3', location: 'git-workflow.js:commit', message: 'commit created', data: { sha, message: args.message }, timestamp: Date.now() }) }).catch(() => { });
|
|
216
214
|
}
|
|
217
215
|
// #endregion
|
|
218
216
|
return asToolResult({ success: true, sha, message: args.message }, { tool: 'workflow', action: 'commit' });
|
|
@@ -220,19 +218,19 @@ EXEMPLOS DE USO:
|
|
|
220
218
|
if (action === "clean") {
|
|
221
219
|
if (args.dryRun) {
|
|
222
220
|
const result = await git.cleanUntracked(projectPath);
|
|
223
|
-
return asToolResult({
|
|
224
|
-
success: true,
|
|
221
|
+
return asToolResult({
|
|
222
|
+
success: true,
|
|
225
223
|
dryRun: true,
|
|
226
224
|
message: `DRY RUN: ${result.cleaned.length} arquivo(s) seriam removidos`,
|
|
227
225
|
wouldClean: result.cleaned
|
|
228
226
|
});
|
|
229
227
|
}
|
|
230
|
-
|
|
228
|
+
|
|
231
229
|
const result = await git.cleanUntracked(projectPath);
|
|
232
|
-
return asToolResult({
|
|
233
|
-
success: true,
|
|
230
|
+
return asToolResult({
|
|
231
|
+
success: true,
|
|
234
232
|
...result,
|
|
235
|
-
message: result.cleaned.length > 0
|
|
233
|
+
message: result.cleaned.length > 0
|
|
236
234
|
? `${result.cleaned.length} arquivo(s) não rastreados removidos`
|
|
237
235
|
: "Nenhum arquivo para limpar"
|
|
238
236
|
});
|
|
@@ -240,7 +238,7 @@ EXEMPLOS DE USO:
|
|
|
240
238
|
if (action === "ensure-remotes") {
|
|
241
239
|
const repo = getRepoNameFromPath(projectPath);
|
|
242
240
|
const isPublic = args.isPublic === true; // Default: privado
|
|
243
|
-
|
|
241
|
+
|
|
244
242
|
if (args.dryRun) {
|
|
245
243
|
const ensured = await pm.ensureRepos({ repoName: repo, createIfMissing: false, isPublic }); // Don't create for dry run
|
|
246
244
|
const ghOwner = await pm.getGitHubOwner();
|
|
@@ -248,9 +246,9 @@ EXEMPLOS DE USO:
|
|
|
248
246
|
const githubUrl = ghOwner ? `https://github.com/${ghOwner}/${repo}.git` : "";
|
|
249
247
|
const base = pm.giteaUrl?.replace(/\/$/, "") || "";
|
|
250
248
|
const giteaUrl = geOwner && base ? `${base}/${geOwner}/${repo}.git` : "";
|
|
251
|
-
|
|
252
|
-
return asToolResult({
|
|
253
|
-
success: true,
|
|
249
|
+
|
|
250
|
+
return asToolResult({
|
|
251
|
+
success: true,
|
|
254
252
|
dryRun: true,
|
|
255
253
|
message: "DRY RUN: Remotes seriam configurados",
|
|
256
254
|
repo,
|
|
@@ -259,7 +257,7 @@ EXEMPLOS DE USO:
|
|
|
259
257
|
ensured
|
|
260
258
|
});
|
|
261
259
|
}
|
|
262
|
-
|
|
260
|
+
|
|
263
261
|
const ensured = await pm.ensureRepos({ repoName: repo, createIfMissing: true, isPublic });
|
|
264
262
|
const ghOwner = await pm.getGitHubOwner();
|
|
265
263
|
const geOwner = await pm.getGiteaOwner();
|
|
@@ -273,120 +271,22 @@ EXEMPLOS DE USO:
|
|
|
273
271
|
if (action === "push") {
|
|
274
272
|
const branch = await git.getCurrentBranch(projectPath);
|
|
275
273
|
const force = !!args.force;
|
|
276
|
-
|
|
274
|
+
|
|
277
275
|
if (args.dryRun) {
|
|
278
|
-
return asToolResult({
|
|
279
|
-
success: true,
|
|
276
|
+
return asToolResult({
|
|
277
|
+
success: true,
|
|
280
278
|
dryRun: true,
|
|
281
279
|
message: `DRY RUN: Push seria executado na branch '${branch}'${force ? ' (force)' : ''}`,
|
|
282
280
|
branch,
|
|
283
281
|
force
|
|
284
282
|
});
|
|
285
283
|
}
|
|
286
|
-
|
|
284
|
+
|
|
287
285
|
const result = await git.pushParallel(projectPath, branch, force);
|
|
288
286
|
return asToolResult({ success: true, branch, ...result });
|
|
289
287
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
return asToolResult({
|
|
293
|
-
success: true,
|
|
294
|
-
dryRun: true,
|
|
295
|
-
message: "DRY RUN: Update completo seria executado (init se necessário, add, commit, push)"
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const results = {
|
|
300
|
-
init: null,
|
|
301
|
-
ensureRemotes: null,
|
|
302
|
-
add: null,
|
|
303
|
-
commit: null,
|
|
304
|
-
push: null
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
const repo = getRepoNameFromPath(projectPath);
|
|
308
|
-
const isPublic = args.isPublic === true;
|
|
309
|
-
|
|
310
|
-
// 1. Verificar se é repo Git, se não for, fazer init
|
|
311
|
-
const isRepo = await git.isRepo(projectPath).catch(() => false);
|
|
312
|
-
if (!isRepo) {
|
|
313
|
-
await git.init(projectPath);
|
|
314
|
-
|
|
315
|
-
// Criar .gitignore baseado no tipo de projeto
|
|
316
|
-
const shouldCreateGitignore = args.createGitignore !== false;
|
|
317
|
-
if (shouldCreateGitignore) {
|
|
318
|
-
const projectType = detectProjectType(projectPath);
|
|
319
|
-
const patterns = GITIGNORE_TEMPLATES[projectType] || GITIGNORE_TEMPLATES.general;
|
|
320
|
-
await git.createGitignore(projectPath, patterns);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
const ensured = await pm.ensureRepos({ repoName: repo, createIfMissing: true, isPublic });
|
|
324
|
-
results.init = { success: true, ensured, isPrivate: !isPublic, gitignoreCreated: shouldCreateGitignore };
|
|
325
|
-
} else {
|
|
326
|
-
results.init = { success: true, skipped: true, message: "Repositório já existe" };
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// 2. Garantir remotes configurados (sempre verifica/configura)
|
|
330
|
-
const ensured = await pm.ensureRepos({ repoName: repo, createIfMissing: true, isPublic });
|
|
331
|
-
const ghOwner = await pm.getGitHubOwner();
|
|
332
|
-
const geOwner = await pm.getGiteaOwner();
|
|
333
|
-
const githubUrl = ghOwner ? `https://github.com/${ghOwner}/${repo}.git` : "";
|
|
334
|
-
const base = pm.giteaUrl?.replace(/\/$/, "") || "";
|
|
335
|
-
const giteaUrl = geOwner && base ? `${base}/${geOwner}/${repo}.git` : "";
|
|
336
|
-
await git.ensureRemotes(projectPath, { githubUrl, giteaUrl });
|
|
337
|
-
const remotes = await git.listRemotes(projectPath);
|
|
338
|
-
results.ensureRemotes = { success: true, ensured, remotes };
|
|
339
|
-
|
|
340
|
-
// 3. Verificar status e fazer add se necessário
|
|
341
|
-
const status = await git.status(projectPath);
|
|
342
|
-
if (!status.isClean || status.modified?.length > 0 || status.created?.length > 0 || status.notAdded?.length > 0) {
|
|
343
|
-
await git.add(projectPath, ["."]);
|
|
344
|
-
results.add = { success: true, files: ["."] };
|
|
345
|
-
} else {
|
|
346
|
-
results.add = { success: true, skipped: true, message: "Nenhum arquivo para adicionar" };
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// 4. Verificar se há algo staged e fazer commit
|
|
350
|
-
const statusAfterAdd = await git.status(projectPath);
|
|
351
|
-
if (statusAfterAdd.staged?.length > 0) {
|
|
352
|
-
// Gerar mensagem padrão se não fornecida
|
|
353
|
-
const commitMessage = args.message || `Update: ${new Date().toISOString().split('T')[0]} - ${statusAfterAdd.staged.length} arquivo(s) modificado(s)`;
|
|
354
|
-
const sha = await git.commit(projectPath, commitMessage);
|
|
355
|
-
results.commit = { success: true, sha, message: commitMessage };
|
|
356
|
-
} else {
|
|
357
|
-
results.commit = { success: true, skipped: true, message: "Nenhum arquivo staged para commit" };
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// 5. Fazer push (só se houver commits para enviar)
|
|
361
|
-
const branch = await git.getCurrentBranch(projectPath);
|
|
362
|
-
const force = !!args.force;
|
|
363
|
-
try {
|
|
364
|
-
const pushResult = await git.pushParallel(projectPath, branch, force);
|
|
365
|
-
results.push = { success: true, branch, ...pushResult };
|
|
366
|
-
} catch (pushError) {
|
|
367
|
-
// Se push falhar mas não houver commits, não é erro crítico
|
|
368
|
-
if (results.commit?.skipped) {
|
|
369
|
-
results.push = { success: true, skipped: true, message: "Nenhum commit para enviar" };
|
|
370
|
-
} else {
|
|
371
|
-
throw pushError;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Resumo final
|
|
376
|
-
const allSuccess = Object.values(results).every(r => r?.success !== false);
|
|
377
|
-
const stepsExecuted = Object.entries(results)
|
|
378
|
-
.filter(([_, r]) => r && !r.skipped)
|
|
379
|
-
.map(([step, _]) => step);
|
|
380
|
-
|
|
381
|
-
return asToolResult({
|
|
382
|
-
success: allSuccess,
|
|
383
|
-
message: `Update completo executado: ${stepsExecuted.join(" → ")}`,
|
|
384
|
-
results,
|
|
385
|
-
stepsExecuted
|
|
386
|
-
}, { tool: 'workflow', action: 'update' });
|
|
387
|
-
}
|
|
388
|
-
return asToolError("VALIDATION_ERROR", `Ação '${action}' não suportada`, {
|
|
389
|
-
availableActions: ["init", "status", "add", "remove", "commit", "push", "ensure-remotes", "clean", "update"],
|
|
288
|
+
return asToolError("VALIDATION_ERROR", `Ação '${action}' não suportada`, {
|
|
289
|
+
availableActions: ["init", "status", "add", "remove", "commit", "push", "ensure-remotes", "clean"],
|
|
390
290
|
suggestion: "Use uma das actions disponíveis"
|
|
391
291
|
});
|
|
392
292
|
} catch (e) {
|