@andrebuzeli/git-mcp 2.42.0 → 2.43.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.
@@ -6,6 +6,18 @@ const index_js_1 = require("../providers/index.js");
6
6
  const user_detection_js_1 = require("../utils/user-detection.js");
7
7
  const git_operations_js_1 = require("../utils/git-operations.js");
8
8
  const GitUpdateProjectInputSchema = zod_1.z.discriminatedUnion('action', [
9
+ // Action: init - upload inicial completo (substitui upload-project)
10
+ zod_1.z.object({
11
+ action: zod_1.z.literal('init'),
12
+ repo: zod_1.z.string(),
13
+ projectPath: zod_1.z.string(),
14
+ provider: zod_1.z.enum(['gitea', 'github']),
15
+ message: zod_1.z.string().min(1, 'Commit message is required'),
16
+ branch: zod_1.z.string().optional(),
17
+ createRepo: zod_1.z.boolean().optional(),
18
+ forcePush: zod_1.z.boolean().optional(),
19
+ granular: zod_1.z.boolean().optional() // commits granulares por tipo de arquivo
20
+ }),
9
21
  // Action: update - commit incremental
10
22
  zod_1.z.object({
11
23
  action: zod_1.z.literal('update'),
@@ -38,6 +50,13 @@ const GitUpdateProjectInputSchema = zod_1.z.discriminatedUnion('action', [
38
50
  oneline: zod_1.z.boolean().optional(),
39
51
  branch: zod_1.z.string().optional()
40
52
  }),
53
+ // Action: reset - desfazer mudanças
54
+ zod_1.z.object({
55
+ action: zod_1.z.literal('reset'),
56
+ projectPath: zod_1.z.string(),
57
+ mode: zod_1.z.enum(['soft', 'mixed', 'hard']).optional(),
58
+ commit: zod_1.z.string().optional()
59
+ }),
41
60
  // Action: stash - gerenciar mudanças temporárias
42
61
  zod_1.z.object({
43
62
  action: zod_1.z.literal('stash'),
@@ -60,18 +79,6 @@ const GitUpdateProjectInputSchema = zod_1.z.discriminatedUnion('action', [
60
79
  message: zod_1.z.string().min(1, 'Commit message is required'),
61
80
  branch: zod_1.z.string().optional(),
62
81
  forcePush: zod_1.z.boolean().optional()
63
- }),
64
- // Action: init - upload inicial completo (substitui upload-project)
65
- zod_1.z.object({
66
- action: zod_1.z.literal('init'),
67
- repo: zod_1.z.string(),
68
- projectPath: zod_1.z.string(),
69
- provider: zod_1.z.enum(['gitea', 'github']),
70
- message: zod_1.z.string().min(1, 'Commit message is required'),
71
- branch: zod_1.z.string().optional(),
72
- createRepo: zod_1.z.boolean().optional(),
73
- forcePush: zod_1.z.boolean().optional(),
74
- granular: zod_1.z.boolean().optional() // commits granulares por tipo de arquivo
75
82
  })
76
83
  ]);
77
84
  const GitUpdateProjectResultSchema = zod_1.z.object({
@@ -83,13 +90,13 @@ const GitUpdateProjectResultSchema = zod_1.z.object({
83
90
  });
84
91
  exports.gitUpdateProjectTool = {
85
92
  name: 'git-update-project',
86
- description: 'tool: Gerenciamento COMPLETO de projetos Git (local + remoto)\\n──────────────\\n8 ACTIONS DISPONÍVEIS:\\n• init: UPLOAD INICIAL completo (substitui upload-project)\\n• update: Commit incremental automático\\n• status: Verificar status do repositório\\n• diff: Mostrar diferenças entre versões\\n• log: Histórico de commits\\n• stash: Gerenciar mudanças temporárias\\n• pull: Atualizar do repositório remoto\\n• sync: Sincronização completa (pull+commit+push)\\n───────────────\\nCOMMITS REAIS + RASTREABILIDADE TOTAL\\nFunciona com GitHub, Gitea e qualquer repositório Git',
93
+ description: 'tool: Gerenciamento COMPLETO de projetos Git (local + remoto)\\n──────────────\\n9 ACTIONS DISPONÍVEIS:\\n• init: UPLOAD INICIAL completo (substitui upload-project)\\n• update: Commit incremental automático\\n• status: Verificar status do repositório\\n• diff: Mostrar diferenças entre versões\\n• log: Histórico de commits\\n• reset: Desfazer mudanças\\n• stash: Gerenciar mudanças temporárias\\n• pull: Atualizar do repositório remoto\\n• sync: Sincronização completa (pull+commit+push)\\n───────────────\\nCOMMITS REAIS + RASTREABILIDADE TOTAL\\nFunciona com GitHub, Gitea e qualquer repositório Git',
87
94
  inputSchema: {
88
95
  type: 'object',
89
96
  properties: {
90
97
  action: {
91
98
  type: 'string',
92
- enum: ['init', 'update', 'status', 'diff', 'log', 'stash', 'pull', 'sync'],
99
+ enum: ['init', 'update', 'status', 'diff', 'log', 'reset', 'stash', 'pull', 'sync'],
93
100
  description: 'Action to perform'
94
101
  },
95
102
  // Parâmetros comuns
@@ -108,6 +115,7 @@ exports.gitUpdateProjectTool = {
108
115
  commit: { type: 'string', description: 'Target commit for reset/diff' },
109
116
  maxCount: { type: 'number', description: 'Max commits for log', default: 10 },
110
117
  oneline: { type: 'boolean', description: 'One line format for log', default: true },
118
+ mode: { type: 'string', enum: ['soft', 'mixed', 'hard'], description: 'Reset mode', default: 'mixed' },
111
119
  operation: { type: 'string', enum: ['save', 'pop', 'apply', 'list', 'drop', 'clear'], description: 'Stash operation', default: 'save' }
112
120
  },
113
121
  required: ['action', 'projectPath']
@@ -127,6 +135,8 @@ exports.gitUpdateProjectTool = {
127
135
  return await this.handleDiff(validatedInput);
128
136
  case 'log':
129
137
  return await this.handleLog(validatedInput);
138
+ case 'reset':
139
+ return await this.handleReset(validatedInput);
130
140
  case 'stash':
131
141
  return await this.handleStash(validatedInput);
132
142
  case 'pull':
@@ -276,26 +286,194 @@ exports.gitUpdateProjectTool = {
276
286
  };
277
287
  }
278
288
  },
289
+ // Action: init - upload inicial completo (substitui upload-project)
290
+ async handleInit(params) {
291
+ try {
292
+ const { repo, projectPath, message, branch = 'main', forcePush = false, createRepo = false, granular = false, provider: providerName } = params;
293
+ const gitOps = new git_operations_js_1.GitOperations(projectPath);
294
+ // Aplicar auto-detecção de usuário
295
+ const processedInput = await (0, user_detection_js_1.applyAutoUserDetection)(params, providerName);
296
+ const provider = index_js_1.globalProviderFactory.getProvider(processedInput.provider);
297
+ if (!provider) {
298
+ throw new Error(`Provider '${providerName}' não encontrado`);
299
+ }
300
+ const currentUser = await provider.getCurrentUser();
301
+ const owner = currentUser.login;
302
+ // 1. Inicializar repositório Git local se necessário
303
+ const isGitRepo = await gitOps.isGitRepository();
304
+ if (!isGitRepo) {
305
+ const initResult = await gitOps.initRepository();
306
+ if (!initResult.success) {
307
+ throw new Error(`Falha ao inicializar repositório Git: ${initResult.error}`);
308
+ }
309
+ }
310
+ // 2. Verificar/criar repositório remoto
311
+ let repoExists = true;
312
+ try {
313
+ await provider.getRepository(owner, repo);
314
+ }
315
+ catch (error) {
316
+ repoExists = false;
317
+ if (createRepo) {
318
+ try {
319
+ await provider.createRepository(repo, `Projeto ${repo}`, false);
320
+ repoExists = true;
321
+ }
322
+ catch (createError) {
323
+ console.warn(`Aviso: Não foi possível criar repositório: ${createError instanceof Error ? createError.message : String(createError)}`);
324
+ }
325
+ }
326
+ else {
327
+ throw new Error(`Repositório '${repo}' não existe. Use createRepo: true para criar automaticamente.`);
328
+ }
329
+ }
330
+ // 3. Preparar arquivos para commit
331
+ let filesCommitted = 0;
332
+ let commitsCreated = 0;
333
+ if (granular) {
334
+ // Commits granulares por tipo de arquivo (melhor rastreabilidade)
335
+ const fileGroups = await this.groupFilesByType(projectPath);
336
+ for (const [fileType, files] of Object.entries(fileGroups)) {
337
+ if (files.length > 0) {
338
+ // Adicionar arquivos do tipo específico
339
+ const addResult = await gitOps.addFiles(files);
340
+ if (addResult.success) {
341
+ // Verificar se há mudanças para commitar
342
+ const statusResult = await gitOps.status({ porcelain: true });
343
+ if (statusResult.success && statusResult.output.trim()) {
344
+ const commitResult = await gitOps.commit(`${message} - ${fileType} files`);
345
+ if (commitResult.success) {
346
+ commitsCreated++;
347
+ filesCommitted += files.length;
348
+ }
349
+ }
350
+ }
351
+ }
352
+ }
353
+ }
354
+ else {
355
+ // Commit único de todos os arquivos (como upload-project)
356
+ const addResult = await gitOps.addFiles(['.']);
357
+ if (addResult.success) {
358
+ const statusResult = await gitOps.status({ porcelain: true });
359
+ if (statusResult.success && statusResult.output.trim()) {
360
+ const commitResult = await gitOps.commit(message);
361
+ if (commitResult.success) {
362
+ commitsCreated++;
363
+ filesCommitted = statusResult.output.trim().split('\n').length;
364
+ }
365
+ }
366
+ }
367
+ }
368
+ // 4. Configurar e fazer push para remote
369
+ let pushSuccessful = false;
370
+ const remoteUrl = provider.getRepositoryUrl(owner, repo);
371
+ // Configurar remote origin
372
+ const remoteResult = await gitOps.remote('show', 'origin');
373
+ if (!remoteResult.success) {
374
+ const addRemoteResult = await gitOps.remote('add', 'origin', remoteUrl);
375
+ if (!addRemoteResult.success && !addRemoteResult.error?.includes('already exists')) {
376
+ console.warn(`Aviso: Falha ao adicionar remote: ${addRemoteResult.error}`);
377
+ }
378
+ }
379
+ else {
380
+ // Atualizar URL se necessário
381
+ const setUrlResult = await gitOps.remote('set-url', 'origin', remoteUrl);
382
+ if (!setUrlResult.success) {
383
+ console.warn(`Aviso: Falha ao atualizar remote URL: ${setUrlResult.error}`);
384
+ }
385
+ }
386
+ // Fazer push
387
+ if (commitsCreated > 0) {
388
+ try {
389
+ const pushOptions = { setUpstream: true };
390
+ if (forcePush)
391
+ pushOptions.force = true;
392
+ const pushResult = await gitOps.push('origin', branch, pushOptions);
393
+ pushSuccessful = pushResult.success;
394
+ if (!pushSuccessful) {
395
+ console.warn(`Aviso: Push falhou: ${pushResult.error}`);
396
+ }
397
+ }
398
+ catch (pushErr) {
399
+ console.warn(`Aviso: Erro no push: ${pushErr instanceof Error ? pushErr.message : String(pushErr)}`);
400
+ }
401
+ }
402
+ return {
403
+ success: true,
404
+ action: 'init',
405
+ message: `Upload inicial concluído com ${commitsCreated} commit(s) e ${filesCommitted} arquivo(s)`,
406
+ data: {
407
+ repo,
408
+ branch,
409
+ provider: providerName,
410
+ owner,
411
+ remoteUrl,
412
+ repositoryCreated: !repoExists && createRepo,
413
+ gitInit: !isGitRepo,
414
+ commitsCreated,
415
+ filesCommitted,
416
+ pushSuccessful,
417
+ granular,
418
+ implementation: 'REAL_GIT_OPERATIONS_v2.40.0',
419
+ note: granular ?
420
+ 'Commits granulares criados por tipo de arquivo para melhor rastreabilidade' :
421
+ 'Commit único criado (como upload-project original)'
422
+ }
423
+ };
424
+ }
425
+ catch (error) {
426
+ return {
427
+ success: false,
428
+ action: 'init',
429
+ message: 'Erro no upload inicial do projeto',
430
+ error: error instanceof Error ? error.message : String(error)
431
+ };
432
+ }
433
+ },
279
434
  // Action: status - verificar status do repositório
280
435
  async handleStatus(params) {
281
436
  try {
282
437
  const { projectPath, detailed = false } = params;
283
438
  const gitOps = new git_operations_js_1.GitOperations(projectPath);
439
+ // Verificar se é um repositório Git
284
440
  const isGitRepo = await gitOps.isGitRepository();
285
- if (!isGitRepo)
286
- throw new Error(`Diretório não é um repositório Git válido`);
441
+ if (!isGitRepo) {
442
+ throw new Error(`Diretório '${projectPath}' não é um repositório Git válido`);
443
+ }
444
+ // Obter status detalhado
287
445
  const statusResult = await gitOps.status({ porcelain: false });
288
- if (!statusResult.success)
446
+ if (!statusResult.success) {
289
447
  throw new Error(`Falha ao obter status: ${statusResult.error}`);
448
+ }
449
+ // Obter branch atual
290
450
  const branchResult = await gitOps.getCurrentBranch();
291
451
  const currentBranch = branchResult.success ? branchResult.output.trim() : 'unknown';
452
+ // Contar mudanças
292
453
  const porcelainResult = await gitOps.status({ porcelain: true });
293
454
  const changes = porcelainResult.success ? porcelainResult.output.trim() : '';
294
455
  const filesChanged = changes ? changes.split('\n').filter(line => line.trim().length > 0).length : 0;
295
- return { success: true, action: 'status', message: 'Status obtido', data: { currentBranch, filesChanged, hasChanges: filesChanged > 0, statusOutput: detailed ? statusResult.output : statusResult.output.split('\n').slice(0, 10).join('\n'), implementation: 'REAL_GIT_OPERATIONS_v2.38.0', detailed } };
456
+ return {
457
+ success: true,
458
+ action: 'status',
459
+ message: `Status do repositório obtido com sucesso`,
460
+ data: {
461
+ currentBranch,
462
+ filesChanged,
463
+ hasChanges: filesChanged > 0,
464
+ statusOutput: detailed ? statusResult.output : statusResult.output.split('\n').slice(0, 10).join('\n'),
465
+ implementation: 'REAL_GIT_OPERATIONS_v2.38.0',
466
+ detailed
467
+ }
468
+ };
296
469
  }
297
470
  catch (error) {
298
- return { success: false, action: 'status', message: 'Erro status', error: error instanceof Error ? error.message : String(error) };
471
+ return {
472
+ success: false,
473
+ action: 'status',
474
+ message: 'Erro ao verificar status do repositório',
475
+ error: error instanceof Error ? error.message : String(error)
476
+ };
299
477
  }
300
478
  },
301
479
  // Action: diff - mostrar diferenças
@@ -303,9 +481,11 @@ exports.gitUpdateProjectTool = {
303
481
  try {
304
482
  const { projectPath, staged = false, nameOnly = false, commit } = params;
305
483
  const gitOps = new git_operations_js_1.GitOperations(projectPath);
484
+ // Verificar se é um repositório Git
306
485
  const isGitRepo = await gitOps.isGitRepository();
307
- if (!isGitRepo)
308
- throw new Error(`Diretório não é um repositório Git válido`);
486
+ if (!isGitRepo) {
487
+ throw new Error(`Diretório '${projectPath}' não é um repositório Git válido`);
488
+ }
309
489
  const diffOptions = {};
310
490
  if (staged)
311
491
  diffOptions.cached = true;
@@ -314,13 +494,31 @@ exports.gitUpdateProjectTool = {
314
494
  if (commit)
315
495
  diffOptions.commit = commit;
316
496
  const diffResult = await gitOps.diff(diffOptions);
317
- if (!diffResult.success && diffResult.error)
318
- throw new Error(`Falha diff: ${diffResult.error}`);
497
+ if (!diffResult.success && diffResult.error) {
498
+ throw new Error(`Falha ao obter diferenças: ${diffResult.error}`);
499
+ }
319
500
  const hasDifferences = diffResult.output.trim().length > 0;
320
- return { success: true, action: 'diff', message: hasDifferences ? 'Diferenças encontradas' : 'Sem diferenças', data: { hasDifferences, diffOutput: diffResult.output, staged, nameOnly, commit: commit || 'HEAD', implementation: 'REAL_GIT_OPERATIONS_v2.38.0' } };
501
+ return {
502
+ success: true,
503
+ action: 'diff',
504
+ message: hasDifferences ? 'Diferenças encontradas' : 'Nenhuma diferença encontrada',
505
+ data: {
506
+ hasDifferences,
507
+ diffOutput: diffResult.output,
508
+ staged,
509
+ nameOnly,
510
+ commit: commit || 'HEAD',
511
+ implementation: 'REAL_GIT_OPERATIONS_v2.38.0'
512
+ }
513
+ };
321
514
  }
322
515
  catch (error) {
323
- return { success: false, action: 'diff', message: 'Erro diff', error: error instanceof Error ? error.message : String(error) };
516
+ return {
517
+ success: false,
518
+ action: 'diff',
519
+ message: 'Erro ao obter diferenças',
520
+ error: error instanceof Error ? error.message : String(error)
521
+ };
324
522
  }
325
523
  },
326
524
  // Action: log - histórico de commits
@@ -328,9 +526,12 @@ exports.gitUpdateProjectTool = {
328
526
  try {
329
527
  const { projectPath, maxCount = 10, oneline = true, branch } = params;
330
528
  const gitOps = new git_operations_js_1.GitOperations(projectPath);
529
+ // Verificar se é um repositório Git
331
530
  const isGitRepo = await gitOps.isGitRepository();
332
- if (!isGitRepo)
333
- throw new Error(`Diretório não é um repositório Git válido`);
531
+ if (!isGitRepo) {
532
+ throw new Error(`Diretório '${projectPath}' não é um repositório Git válido`);
533
+ }
534
+ // Obter histórico de commits
334
535
  const logOptions = {};
335
536
  if (maxCount)
336
537
  logOptions.maxCount = maxCount;
@@ -339,14 +540,32 @@ exports.gitUpdateProjectTool = {
339
540
  if (branch)
340
541
  logOptions.branches = [branch];
341
542
  const logResult = await gitOps.log(logOptions);
342
- if (!logResult.success)
343
- throw new Error(`Falha log: ${logResult.error}`);
543
+ if (!logResult.success) {
544
+ throw new Error(`Falha ao obter histórico: ${logResult.error}`);
545
+ }
344
546
  const commits = logResult.output.trim();
345
547
  const commitCount = commits ? commits.split('\n').filter(line => line.trim().length > 0).length : 0;
346
- return { success: true, action: 'log', message: `${commitCount} commits`, data: { commits: commits || 'Sem commits', commitCount, maxCount, oneline, branch: branch || 'all', implementation: 'REAL_GIT_OPERATIONS_v2.38.0' } };
548
+ return {
549
+ success: true,
550
+ action: 'log',
551
+ message: `${commitCount} commits encontrados no histórico`,
552
+ data: {
553
+ commits: commits || 'Nenhum commit encontrado',
554
+ commitCount,
555
+ maxCount,
556
+ oneline,
557
+ branch: branch || 'all',
558
+ implementation: 'REAL_GIT_OPERATIONS_v2.38.0'
559
+ }
560
+ };
347
561
  }
348
562
  catch (error) {
349
- return { success: false, action: 'log', message: 'Erro log', error: error instanceof Error ? error.message : String(error) };
563
+ return {
564
+ success: false,
565
+ action: 'log',
566
+ message: 'Erro ao obter histórico de commits',
567
+ error: error instanceof Error ? error.message : String(error)
568
+ };
350
569
  }
351
570
  },
352
571
  // Action: reset - desfazer mudanças
@@ -354,18 +573,37 @@ exports.gitUpdateProjectTool = {
354
573
  try {
355
574
  const { projectPath, mode = 'mixed', commit } = params;
356
575
  const gitOps = new git_operations_js_1.GitOperations(projectPath);
576
+ // Verificar se é um repositório Git
357
577
  const isGitRepo = await gitOps.isGitRepository();
358
- if (!isGitRepo)
359
- throw new Error(`Diretório não é um repositório Git válido`);
578
+ if (!isGitRepo) {
579
+ throw new Error(`Diretório '${projectPath}' não é um repositório Git válido`);
580
+ }
581
+ // Executar reset
360
582
  const target = commit || 'HEAD';
361
583
  const resetOptions = { mode };
362
584
  const resetResult = await gitOps.reset(target, resetOptions);
363
- if (!resetResult.success)
364
- throw new Error(`Falha reset: ${resetResult.error}`);
365
- return { success: true, action: 'reset', message: `Reset ${mode} executado`, data: { mode, targetCommit: target, resetOutput: resetResult.output, implementation: 'REAL_GIT_OPERATIONS_v2.38.0' } };
585
+ if (!resetResult.success) {
586
+ throw new Error(`Falha no reset: ${resetResult.error}`);
587
+ }
588
+ return {
589
+ success: true,
590
+ action: 'reset',
591
+ message: `Reset ${mode} executado com sucesso`,
592
+ data: {
593
+ mode,
594
+ targetCommit: target,
595
+ resetOutput: resetResult.output,
596
+ implementation: 'REAL_GIT_OPERATIONS_v2.38.0'
597
+ }
598
+ };
366
599
  }
367
600
  catch (error) {
368
- return { success: false, action: 'reset', message: 'Erro reset', error: error instanceof Error ? error.message : String(error) };
601
+ return {
602
+ success: false,
603
+ action: 'reset',
604
+ message: 'Erro ao executar reset',
605
+ error: error instanceof Error ? error.message : String(error)
606
+ };
369
607
  }
370
608
  },
371
609
  // Action: stash - gerenciar mudanças temporárias
@@ -373,9 +611,11 @@ exports.gitUpdateProjectTool = {
373
611
  try {
374
612
  const { projectPath, operation = 'save', message } = params;
375
613
  const gitOps = new git_operations_js_1.GitOperations(projectPath);
614
+ // Verificar se é um repositório Git
376
615
  const isGitRepo = await gitOps.isGitRepository();
377
- if (!isGitRepo)
378
- throw new Error(`Diretório não é um repositório Git válido`);
616
+ if (!isGitRepo) {
617
+ throw new Error(`Diretório '${projectPath}' não é um repositório Git válido`);
618
+ }
379
619
  let stashResult;
380
620
  switch (operation) {
381
621
  case 'save':
@@ -396,14 +636,31 @@ exports.gitUpdateProjectTool = {
396
636
  case 'clear':
397
637
  stashResult = await gitOps.stash('clear');
398
638
  break;
399
- default: throw new Error(`Operação stash '${operation}' não suportada`);
639
+ default:
640
+ throw new Error(`Operação de stash '${operation}' não suportada`);
641
+ }
642
+ if (!stashResult.success) {
643
+ throw new Error(`Falha na operação stash ${operation}: ${stashResult.error}`);
400
644
  }
401
- if (!stashResult.success)
402
- throw new Error(`Falha stash ${operation}: ${stashResult.error}`);
403
- return { success: true, action: 'stash', message: `Stash ${operation} executado`, data: { operation, message: message || undefined, stashOutput: stashResult.output, implementation: 'REAL_GIT_OPERATIONS_v2.38.0' } };
645
+ return {
646
+ success: true,
647
+ action: 'stash',
648
+ message: `Operação stash '${operation}' executada com sucesso`,
649
+ data: {
650
+ operation,
651
+ message: message || undefined,
652
+ stashOutput: stashResult.output,
653
+ implementation: 'REAL_GIT_OPERATIONS_v2.38.0'
654
+ }
655
+ };
404
656
  }
405
657
  catch (error) {
406
- return { success: false, action: 'stash', message: 'Erro stash', error: error instanceof Error ? error.message : String(error) };
658
+ return {
659
+ success: false,
660
+ action: 'stash',
661
+ message: 'Erro na operação de stash',
662
+ error: error instanceof Error ? error.message : String(error)
663
+ };
407
664
  }
408
665
  },
409
666
  // Action: pull - atualizar do remoto
@@ -411,173 +668,161 @@ exports.gitUpdateProjectTool = {
411
668
  try {
412
669
  const { projectPath, branch } = params;
413
670
  const gitOps = new git_operations_js_1.GitOperations(projectPath);
671
+ // Verificar se é um repositório Git
414
672
  const isGitRepo = await gitOps.isGitRepository();
415
- if (!isGitRepo)
416
- throw new Error(`Diretório não é um repositório Git válido`);
673
+ if (!isGitRepo) {
674
+ throw new Error(`Diretório '${projectPath}' não é um repositório Git válido`);
675
+ }
676
+ // Executar pull
417
677
  const pullResult = await gitOps.pull('origin', branch);
418
- if (!pullResult.success)
419
- throw new Error(`Falha pull: ${pullResult.error}`);
420
- return { success: true, action: 'pull', message: 'Pull executado', data: { remote: 'origin', branch: branch || 'current', pullOutput: pullResult.output, implementation: 'REAL_GIT_OPERATIONS_v2.38.0' } };
678
+ if (!pullResult.success) {
679
+ throw new Error(`Falha no pull: ${pullResult.error}`);
680
+ }
681
+ return {
682
+ success: true,
683
+ action: 'pull',
684
+ message: `Pull do remoto executado com sucesso`,
685
+ data: {
686
+ remote: 'origin',
687
+ branch: branch || 'current',
688
+ pullOutput: pullResult.output,
689
+ implementation: 'REAL_GIT_OPERATIONS_v2.38.0'
690
+ }
691
+ };
421
692
  }
422
693
  catch (error) {
423
- return { success: false, action: 'pull', message: 'Erro pull', error: error instanceof Error ? error.message : String(error) };
694
+ return {
695
+ success: false,
696
+ action: 'pull',
697
+ message: 'Erro ao executar pull',
698
+ error: error instanceof Error ? error.message : String(error)
699
+ };
424
700
  }
425
701
  },
426
- // Action: sync - sincronização completa
702
+ // Action: sync - sincronização completa (pull + commit + push)
427
703
  async handleSync(params) {
428
704
  try {
429
705
  const { repo, projectPath, message, branch = 'main', forcePush = false, provider: providerName } = params;
430
706
  const gitOps = new git_operations_js_1.GitOperations(projectPath);
707
+ // Verificar se é um repositório Git
431
708
  const isGitRepo = await gitOps.isGitRepository();
432
- if (!isGitRepo)
433
- throw new Error(`Diretório não é um repositório Git válido`);
434
- const syncResults = { pull: { success: false, output: '', error: '' }, commit: { success: false, output: '', error: '', created: false }, push: { success: false, output: '', error: '' } };
709
+ if (!isGitRepo) {
710
+ throw new Error(`Diretório '${projectPath}' não é um repositório Git válido`);
711
+ }
712
+ const syncResults = {
713
+ pull: { success: false, output: '', error: '' },
714
+ commit: { success: false, output: '', error: '', created: false },
715
+ push: { success: false, output: '', error: '' }
716
+ };
717
+ // 1. Pull do remoto
435
718
  try {
436
719
  const pullResult = await gitOps.pull('origin', branch);
437
- syncResults.pull = { success: pullResult.success, output: pullResult.output, error: pullResult.error || '' };
720
+ syncResults.pull = {
721
+ success: pullResult.success,
722
+ output: pullResult.output,
723
+ error: pullResult.error || ''
724
+ };
438
725
  }
439
726
  catch (pullErr) {
440
727
  syncResults.pull.error = pullErr instanceof Error ? pullErr.message : String(pullErr);
441
728
  }
729
+ // 2. Verificar se há mudanças após pull
442
730
  const statusResult = await gitOps.status({ porcelain: true });
443
731
  const hasChanges = statusResult.success && statusResult.output.trim().length > 0;
444
732
  if (hasChanges) {
733
+ // Fazer commit das mudanças
445
734
  const addResult = await gitOps.addFiles(['.']);
446
735
  if (addResult.success) {
447
736
  const commitResult = await gitOps.commit(message);
448
- syncResults.commit = { success: commitResult.success, output: commitResult.output, error: commitResult.error || '', created: commitResult.success };
737
+ syncResults.commit = {
738
+ success: commitResult.success,
739
+ output: commitResult.output,
740
+ error: commitResult.error || '',
741
+ created: commitResult.success
742
+ };
449
743
  }
450
744
  else {
451
- syncResults.commit.error = `Falha add: ${addResult.error}`;
745
+ syncResults.commit.error = `Falha ao adicionar arquivos: ${addResult.error}`;
452
746
  }
453
747
  }
748
+ // 3. Push se commit foi criado
454
749
  if (syncResults.commit.created) {
455
750
  try {
456
751
  const pushOptions = {};
457
752
  if (forcePush)
458
753
  pushOptions.force = true;
459
754
  const pushResult = await gitOps.push('origin', branch, pushOptions);
460
- syncResults.push = { success: pushResult.success, output: pushResult.output, error: pushResult.error || '' };
755
+ syncResults.push = {
756
+ success: pushResult.success,
757
+ output: pushResult.output,
758
+ error: pushResult.error || ''
759
+ };
461
760
  }
462
761
  catch (pushErr) {
463
762
  syncResults.push.error = pushErr instanceof Error ? pushErr.message : String(pushErr);
464
763
  }
465
764
  }
466
765
  const overallSuccess = syncResults.pull.success && (!hasChanges || syncResults.commit.success);
467
- return { success: overallSuccess, action: 'sync', message: `Sincronização ${overallSuccess ? 'concluída' : 'problemas'}`, data: { repo, branch, provider: providerName, syncResults, hasChanges, implementation: 'REAL_GIT_OPERATIONS_v2.38.0', summary: { pullSuccessful: syncResults.pull.success, commitCreated: syncResults.commit.created, pushSuccessful: syncResults.push.success } } };
468
- }
469
- catch (error) {
470
- return { success: false, action: 'sync', message: 'Erro sync', error: error instanceof Error ? error.message : String(error) };
471
- }
472
- },
473
- // Action: init - upload inicial completo
474
- async handleInit(params) {
475
- try {
476
- const { repo, projectPath, message, branch = 'main', forcePush = false, createRepo = false, granular = false, provider: providerName } = params;
477
- const gitOps = new git_operations_js_1.GitOperations(projectPath);
478
- const processedInput = await (0, user_detection_js_1.applyAutoUserDetection)(params, providerName);
479
- const provider = index_js_1.globalProviderFactory.getProvider(processedInput.provider);
480
- if (!provider)
481
- throw new Error(`Provider '${providerName}' não encontrado`);
482
- const currentUser = await provider.getCurrentUser();
483
- const owner = currentUser.login;
484
- const isGitRepo = await gitOps.isGitRepository();
485
- if (!isGitRepo) {
486
- const initResult = await gitOps.initRepository();
487
- if (!initResult.success)
488
- throw new Error(`Falha init: ${initResult.error}`);
489
- }
490
- let repoExists = true;
491
- try {
492
- await provider.getRepository(owner, repo);
493
- }
494
- catch (error) {
495
- repoExists = false;
496
- if (createRepo) {
497
- try {
498
- await provider.createRepository(repo, `Projeto ${repo}`, false);
499
- repoExists = true;
500
- }
501
- catch (createError) {
502
- console.warn(`Aviso: Falha criar repo: ${createError instanceof Error ? createError.message : String(createError)}`);
503
- }
504
- }
505
- else {
506
- throw new Error(`Repositório '${repo}' não existe. Use createRepo: true.`);
507
- }
508
- }
509
- let filesCommitted = 0;
510
- let commitsCreated = 0;
511
- const remoteUrl = provider.getRepositoryUrl(owner, repo);
512
- if (granular) {
513
- const fileGroups = await this.groupFilesByType(projectPath);
514
- for (const [fileType, files] of Object.entries(fileGroups)) {
515
- if (files.length > 0) {
516
- const addResult = await gitOps.addFiles(files);
517
- if (addResult.success) {
518
- const statusResult = await gitOps.status({ porcelain: true });
519
- if (statusResult.success && statusResult.output.trim()) {
520
- const commitResult = await gitOps.commit(`${message} - ${fileType} files`);
521
- if (commitResult.success) {
522
- commitsCreated++;
523
- filesCommitted += files.length;
524
- }
525
- }
526
- }
527
- }
528
- }
529
- }
530
- else {
531
- const addResult = await gitOps.addFiles(['.']);
532
- if (addResult.success) {
533
- const statusResult = await gitOps.status({ porcelain: true });
534
- if (statusResult.success && statusResult.output.trim()) {
535
- const commitResult = await gitOps.commit(message);
536
- if (commitResult.success) {
537
- commitsCreated++;
538
- filesCommitted = statusResult.output.trim().split('\n').length;
539
- }
766
+ return {
767
+ success: overallSuccess,
768
+ action: 'sync',
769
+ message: `Sincronização ${overallSuccess ? 'concluída' : 'com problemas'}`,
770
+ data: {
771
+ repo,
772
+ branch,
773
+ provider: providerName,
774
+ syncResults,
775
+ hasChanges,
776
+ implementation: 'REAL_GIT_OPERATIONS_v2.38.0',
777
+ summary: {
778
+ pullSuccessful: syncResults.pull.success,
779
+ commitCreated: syncResults.commit.created,
780
+ pushSuccessful: syncResults.push.success
540
781
  }
541
782
  }
542
- }
543
- let pushSuccessful = false;
544
- if (commitsCreated > 0) {
545
- try {
546
- const pushOptions = { setUpstream: true };
547
- if (forcePush)
548
- pushOptions.force = true;
549
- const pushResult = await gitOps.push('origin', branch, pushOptions);
550
- pushSuccessful = pushResult.success;
551
- }
552
- catch (pushErr) {
553
- console.warn(`Aviso push: ${pushErr instanceof Error ? pushErr.message : String(pushErr)}`);
554
- }
555
- }
556
- return { success: true, action: 'init', message: `Upload concluído: ${commitsCreated} commits, ${filesCommitted} arquivos`, data: { repo, branch, provider: providerName, owner, remoteUrl, repositoryCreated: !repoExists && createRepo, gitInit: !isGitRepo, commitsCreated, filesCommitted, pushSuccessful, granular, implementation: 'REAL_GIT_OPERATIONS_v2.40.0', note: granular ? 'Commits granulares por tipo' : 'Commit único' } };
783
+ };
557
784
  }
558
785
  catch (error) {
559
- return { success: false, action: 'init', message: 'Erro init', error: error instanceof Error ? error.message : String(error) };
786
+ return {
787
+ success: false,
788
+ action: 'sync',
789
+ message: 'Erro na sincronização completa',
790
+ error: error instanceof Error ? error.message : String(error)
791
+ };
560
792
  }
561
793
  },
794
+ // Helper method para agrupar arquivos por tipo
562
795
  async groupFilesByType(projectPath) {
563
796
  const gitOps = new git_operations_js_1.GitOperations(projectPath);
564
797
  const statusResult = await gitOps.status({ porcelain: true });
565
- if (!statusResult.success)
798
+ if (!statusResult.success) {
566
799
  return {};
800
+ }
567
801
  const lines = statusResult.output.trim().split('\n').filter(line => line.trim());
568
- const fileGroups = { 'config': [], 'source': [], 'documentation': [], 'assets': [], 'other': [] };
802
+ const fileGroups = {
803
+ 'config': [],
804
+ 'source': [],
805
+ 'documentation': [],
806
+ 'assets': [],
807
+ 'other': []
808
+ };
569
809
  for (const line of lines) {
810
+ // Extrair nome do arquivo (remover status indicators)
570
811
  const fileName = line.substring(3).trim();
571
- if (fileName.includes('package.json') || fileName.includes('.config.') || fileName.includes('tsconfig') || fileName.includes('.env')) {
812
+ if (fileName.includes('package.json') || fileName.includes('.config.') ||
813
+ fileName.includes('tsconfig') || fileName.includes('.env')) {
572
814
  fileGroups.config.push(fileName);
573
815
  }
574
- else if (fileName.endsWith('.ts') || fileName.endsWith('.js') || fileName.endsWith('.py') || fileName.endsWith('.java')) {
816
+ else if (fileName.endsWith('.ts') || fileName.endsWith('.js') ||
817
+ fileName.endsWith('.py') || fileName.endsWith('.java')) {
575
818
  fileGroups.source.push(fileName);
576
819
  }
577
- else if (fileName.endsWith('.md') || fileName.endsWith('.txt') || fileName.includes('README') || fileName.includes('LICENSE')) {
820
+ else if (fileName.endsWith('.md') || fileName.endsWith('.txt') ||
821
+ fileName.includes('README') || fileName.includes('LICENSE')) {
578
822
  fileGroups.documentation.push(fileName);
579
823
  }
580
- else if (fileName.endsWith('.png') || fileName.endsWith('.jpg') || fileName.endsWith('.svg') || fileName.endsWith('.css')) {
824
+ else if (fileName.endsWith('.png') || fileName.endsWith('.jpg') ||
825
+ fileName.endsWith('.svg') || fileName.endsWith('.css')) {
581
826
  fileGroups.assets.push(fileName);
582
827
  }
583
828
  else {