@andrebuzeli/git-mcp 10.0.7 → 10.0.9

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.
Files changed (45) hide show
  1. package/LICENSE +0 -0
  2. package/README.md +1 -1
  3. package/dist/providers/providerManager.js +2 -4
  4. package/dist/scripts/test_e2e.d.ts +1 -0
  5. package/dist/scripts/test_e2e.js +199 -0
  6. package/dist/scripts/test_exhaustive.d.ts +1 -0
  7. package/dist/scripts/test_exhaustive.js +275 -0
  8. package/dist/scripts/test_gitea_creation.d.ts +1 -0
  9. package/dist/scripts/test_gitea_creation.js +116 -0
  10. package/dist/scripts/verify_setup.d.ts +1 -0
  11. package/dist/scripts/verify_setup.js +61 -0
  12. package/dist/tools/gitAnalytics.js +3 -3
  13. package/dist/tools/gitBackup.d.ts +2 -2
  14. package/dist/tools/gitBackup.js +5 -5
  15. package/dist/tools/gitBranches.js +40 -29
  16. package/dist/tools/gitConfig.d.ts +2 -2
  17. package/dist/tools/gitConfig.js +12 -13
  18. package/dist/tools/gitFix.d.ts +2 -1
  19. package/dist/tools/gitFix.js +20 -24
  20. package/dist/tools/gitFix.tool.d.ts +2 -2
  21. package/dist/tools/gitFix.tool.js +2 -2
  22. package/dist/tools/gitHistory.js +4 -5
  23. package/dist/tools/gitIssues.js +8 -8
  24. package/dist/tools/gitMonitor.js +28 -33
  25. package/dist/tools/gitPulls.js +8 -8
  26. package/dist/tools/gitRelease.js +5 -6
  27. package/dist/tools/gitRemote.d.ts +1 -11
  28. package/dist/tools/gitRemote.js +14 -20
  29. package/dist/tools/gitReset.js +6 -7
  30. package/dist/tools/gitStash.d.ts +1 -12
  31. package/dist/tools/gitStash.js +11 -22
  32. package/dist/tools/gitSync.js +44 -36
  33. package/dist/tools/gitTags.d.ts +8 -0
  34. package/dist/tools/gitTags.js +44 -34
  35. package/dist/tools/gitUpdate.d.ts +27 -0
  36. package/dist/tools/gitUpdate.js +61 -34
  37. package/dist/tools/gitUpload.js +29 -44
  38. package/dist/tools/gitWorkflow.js +27 -46
  39. package/dist/utils/gitAdapter.js +10 -0
  40. package/dist/utils/repoHelpers.js +7 -12
  41. package/package.json +2 -3
  42. package/dist/server-new.d.ts +0 -2
  43. package/dist/server-new.js +0 -224
  44. package/dist/tools/gitFiles-new.d.ts +0 -89
  45. package/dist/tools/gitFiles-new.js +0 -335
@@ -0,0 +1,61 @@
1
+ import { ProviderManager } from '../providers/providerManager.js';
2
+ import { IsomorphicGitAdapter } from '../utils/gitAdapter.js';
3
+ import * as path from 'path';
4
+ import * as fs from 'fs';
5
+ async function verify() {
6
+ console.log('🔍 Verifying setup and credentials...');
7
+ // 1. Check Environment Variables
8
+ console.log('\n1. Environment Variables:');
9
+ const vars = ['GITHUB_TOKEN', 'GITHUB_USERNAME', 'GITEA_TOKEN', 'GITEA_URL', 'GITEA_USERNAME'];
10
+ const missing = [];
11
+ for (const v of vars) {
12
+ if (process.env[v]) {
13
+ console.log(` ✅ ${v} is set`);
14
+ }
15
+ else {
16
+ console.log(` ❌ ${v} is MISSING`);
17
+ missing.push(v);
18
+ }
19
+ }
20
+ // 2. Validate Providers
21
+ console.log('\n2. Provider Connection:');
22
+ const providerManager = new ProviderManager();
23
+ const results = await providerManager.validateConfiguredProviders();
24
+ if (results.github) {
25
+ console.log(` GitHub: ${results.github.ok ? '✅ Connected' : '❌ Failed: ' + results.github.error}`);
26
+ }
27
+ else {
28
+ console.log(' GitHub: ⚠️ Not configured');
29
+ }
30
+ if (results.gitea) {
31
+ console.log(` Gitea: ${results.gitea.ok ? '✅ Connected' : '❌ Failed: ' + results.gitea.error}`);
32
+ }
33
+ else {
34
+ console.log(' Gitea: ⚠️ Not configured');
35
+ }
36
+ // 3. Validate GitAdapter
37
+ console.log('\n3. GitAdapter Initialization:');
38
+ try {
39
+ const gitAdapter = new IsomorphicGitAdapter(providerManager);
40
+ console.log(' ✅ GitAdapter initialized successfully');
41
+ // Try a simple local operation if we are in a git repo
42
+ const currentDir = process.cwd();
43
+ if (fs.existsSync(path.join(currentDir, '.git'))) {
44
+ try {
45
+ const branch = await gitAdapter.getCurrentBranch(currentDir);
46
+ console.log(` ✅ Local Git check passed (Current branch: ${branch})`);
47
+ }
48
+ catch (e) {
49
+ console.log(` ❌ Local Git check failed: ${e.message}`);
50
+ }
51
+ }
52
+ else {
53
+ console.log(' ℹ️ Current directory is not a git repo, skipping local check');
54
+ }
55
+ }
56
+ catch (err) {
57
+ console.log(` ❌ GitAdapter initialization failed: ${err.message}`);
58
+ }
59
+ console.log('\n🏁 Verification Complete');
60
+ }
61
+ verify().catch(console.error);
@@ -45,10 +45,10 @@ export class GitAnalyticsTool {
45
45
  throw new MCPError('VALIDATION_ERROR', 'projectPath is required');
46
46
  // Auto-extract repo info from projectPath
47
47
  const repoInfo = getRepoInfo(projectPath);
48
- const repo = repoInfo.repoName;
48
+ const repo = params.repo || repoInfo.repoName;
49
49
  // Each provider uses its own username from env
50
- const githubOwner = process.env.GITHUB_USERNAME;
51
- const giteaOwner = process.env.GITEA_USERNAME;
50
+ const githubOwner = params.owner || process.env.GITHUB_USERNAME;
51
+ const giteaOwner = params.owner || process.env.GITEA_USERNAME;
52
52
  switch (action) {
53
53
  case 'stats': {
54
54
  const results = { success: true, providers: {} };
@@ -40,8 +40,8 @@ export declare class GitBackupTool implements Tool {
40
40
  backupData: {
41
41
  projectPath: any;
42
42
  timestamp: string;
43
- branch: string | null;
44
- lastCommit: string | undefined;
43
+ branch: string;
44
+ lastCommit: string | null;
45
45
  files: number;
46
46
  };
47
47
  message: string;
@@ -1,6 +1,5 @@
1
1
  import fs from 'fs/promises';
2
2
  import path from 'path';
3
- import simpleGit from 'simple-git';
4
3
  import { MCPError } from '../utils/errors.js';
5
4
  export class GitBackupTool {
6
5
  constructor() {
@@ -46,6 +45,7 @@ export class GitBackupTool {
46
45
  throw new MCPError('VALIDATION_ERROR', 'action and projectPath are required');
47
46
  }
48
47
  switch (action) {
48
+ case 'create':
49
49
  case 'backup': {
50
50
  const backupDir = params.backupDir || path.join(projectPath, '.git-backups');
51
51
  const backupName = params.backupName || `backup-${Date.now()}.json`;
@@ -53,14 +53,14 @@ export class GitBackupTool {
53
53
  // Criar diretório de backups
54
54
  await fs.mkdir(backupDir, { recursive: true });
55
55
  const backupPath = path.join(backupDir, backupName);
56
- const git = simpleGit({ baseDir: projectPath });
57
- const status = await git.status();
58
- const log = await git.log({ maxCount: 1 });
56
+ const git = ctx.gitAdapter;
57
+ const status = await git.status(projectPath);
58
+ const log = await git.log(projectPath, { maxCount: 1 });
59
59
  const backupData = {
60
60
  projectPath,
61
61
  timestamp: new Date().toISOString(),
62
62
  branch: status.current,
63
- lastCommit: log.latest?.hash,
63
+ lastCommit: log.length > 0 ? log[0].hash : null,
64
64
  files: status.files.length,
65
65
  };
66
66
  await fs.writeFile(backupPath, JSON.stringify(backupData, null, 2));
@@ -1,4 +1,3 @@
1
- import simpleGit from 'simple-git';
2
1
  import { MCPError } from '../utils/errors.js';
3
2
  import { requireConfirmationIfDestructive } from '../utils/safetyController.js';
4
3
  import { getRepoInfo } from '../utils/repoHelpers.js';
@@ -62,7 +61,9 @@ export class GitBranchesTool {
62
61
  if (!action || !projectPath) {
63
62
  throw new MCPError('VALIDATION_ERROR', 'action and projectPath are required');
64
63
  }
65
- const git = simpleGit({ baseDir: projectPath });
64
+ if (!ctx.gitAdapter) {
65
+ throw new MCPError('INTERNAL_ERROR', 'Git adapter not available');
66
+ }
66
67
  switch (action) {
67
68
  case 'create': {
68
69
  const branchName = params.branchName;
@@ -70,34 +71,36 @@ export class GitBranchesTool {
70
71
  throw new MCPError('VALIDATION_ERROR', 'branchName is required');
71
72
  const sourceBranch = params.sourceBranch;
72
73
  if (sourceBranch) {
73
- await git.checkout(sourceBranch);
74
+ await ctx.gitAdapter.checkout(projectPath, sourceBranch);
74
75
  }
75
- await git.checkoutLocalBranch(branchName);
76
+ await ctx.gitAdapter.createBranch(projectPath, branchName);
76
77
  if (params.checkout !== false) {
77
- await git.checkout(branchName);
78
+ await ctx.gitAdapter.checkout(projectPath, branchName);
78
79
  }
79
80
  return { success: true, branch: branchName, local: true };
80
81
  }
81
82
  case 'list': {
82
- const localBranches = await git.branch();
83
+ const branches = await ctx.gitAdapter.listBranches(projectPath);
84
+ const current = await ctx.gitAdapter.getCurrentBranch(projectPath);
83
85
  const results = {
84
86
  success: true,
85
87
  local: {
86
- branches: localBranches.all,
87
- current: localBranches.current,
88
+ branches,
89
+ current,
88
90
  },
89
91
  providers: {}
90
92
  };
91
93
  // Also query remote APIs
92
94
  const repoInfo = getRepoInfo(projectPath);
93
- const githubOwner = process.env.GITHUB_USERNAME;
94
- const giteaOwner = process.env.GITEA_USERNAME;
95
+ const repo = params.repo || repoInfo.repoName;
96
+ const githubOwner = params.owner || process.env.GITHUB_USERNAME;
97
+ const giteaOwner = params.owner || process.env.GITEA_USERNAME;
95
98
  // GitHub
96
99
  if (ctx.providerManager.github && githubOwner) {
97
100
  try {
98
101
  const branches = await ctx.providerManager.github.rest.repos.listBranches({
99
102
  owner: githubOwner,
100
- repo: repoInfo.repoName,
103
+ repo: repo,
101
104
  });
102
105
  results.providers.github = {
103
106
  success: true,
@@ -115,7 +118,7 @@ export class GitBranchesTool {
115
118
  // Gitea
116
119
  if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
117
120
  try {
118
- const branches = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repoInfo.repoName}/branches`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
121
+ const branches = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/branches`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
119
122
  results.providers.gitea = {
120
123
  success: true,
121
124
  branches: branches.data.map((b) => ({
@@ -135,23 +138,25 @@ export class GitBranchesTool {
135
138
  const branchName = params.branchName;
136
139
  if (!branchName)
137
140
  throw new MCPError('VALIDATION_ERROR', 'branchName is required');
138
- const branches = await git.branch();
139
- const branch = branches.all.find(b => b === branchName);
141
+ const branches = await ctx.gitAdapter.listBranches(projectPath);
142
+ const current = await ctx.gitAdapter.getCurrentBranch(projectPath);
143
+ const found = branches.includes(branchName);
140
144
  const results = {
141
145
  success: true,
142
- local: branch ? { branch, current: branches.current === branchName } : { found: false },
146
+ local: found ? { branch: branchName, current: current === branchName } : { found: false },
143
147
  providers: {}
144
148
  };
145
149
  // Also query remote APIs
146
150
  const repoInfo = getRepoInfo(projectPath);
147
- const githubOwner = process.env.GITHUB_USERNAME;
148
- const giteaOwner = process.env.GITEA_USERNAME;
151
+ const repo = params.repo || repoInfo.repoName;
152
+ const githubOwner = params.owner || process.env.GITHUB_USERNAME;
153
+ const giteaOwner = params.owner || process.env.GITEA_USERNAME;
149
154
  // GitHub
150
155
  if (ctx.providerManager.github && githubOwner) {
151
156
  try {
152
157
  const branchData = await ctx.providerManager.github.rest.repos.getBranch({
153
158
  owner: githubOwner,
154
- repo: repoInfo.repoName,
159
+ repo: repo,
155
160
  branch: branchName,
156
161
  });
157
162
  results.providers.github = {
@@ -171,7 +176,7 @@ export class GitBranchesTool {
171
176
  // Gitea
172
177
  if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
173
178
  try {
174
- const branchData = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repoInfo.repoName}/branches/${branchName}`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
179
+ const branchData = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/branches/${branchName}`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
175
180
  results.providers.gitea = {
176
181
  success: true,
177
182
  branch: {
@@ -193,7 +198,7 @@ export class GitBranchesTool {
193
198
  const branchName = params.branchName;
194
199
  if (!branchName)
195
200
  throw new MCPError('VALIDATION_ERROR', 'branchName is required');
196
- await git.deleteLocalBranch(branchName, params.force);
201
+ await ctx.gitAdapter.deleteBranch(projectPath, branchName, params.force);
197
202
  return { success: true, deleted: branchName, local: true };
198
203
  }
199
204
  case 'merge': {
@@ -202,9 +207,9 @@ export class GitBranchesTool {
202
207
  if (!branchName)
203
208
  throw new MCPError('VALIDATION_ERROR', 'branchName is required');
204
209
  if (targetBranch) {
205
- await git.checkout(targetBranch);
210
+ await ctx.gitAdapter.checkout(projectPath, targetBranch);
206
211
  }
207
- const result = await git.merge([branchName]);
212
+ const result = await ctx.gitAdapter.merge(projectPath, branchName);
208
213
  return { success: true, result, local: true };
209
214
  }
210
215
  case 'compare': {
@@ -213,28 +218,34 @@ export class GitBranchesTool {
213
218
  if (!baseBranch || !compareBranch) {
214
219
  throw new MCPError('VALIDATION_ERROR', 'baseBranch and compareBranch are required');
215
220
  }
216
- const diff = await git.diff([`${baseBranch}...${compareBranch}`]);
217
- const log = await git.log({ from: baseBranch, to: compareBranch });
221
+ // Basic comparison using log
222
+ // Note: This is a simplified comparison compared to git log base..compare
223
+ const baseLog = await ctx.gitAdapter.log(projectPath, { ref: baseBranch, maxCount: 20 });
224
+ const compareLog = await ctx.gitAdapter.log(projectPath, { ref: compareBranch, maxCount: 20 });
225
+ // Find commits in compare that are not in base
226
+ const baseHashes = new Set(baseLog.map(c => c.hash));
227
+ const uniqueCommits = compareLog.filter(c => !baseHashes.has(c.hash));
218
228
  const results = {
219
229
  success: true,
220
230
  local: {
221
231
  baseBranch,
222
232
  compareBranch,
223
- commits: log.all,
224
- diff,
233
+ commits: uniqueCommits,
234
+ diff: "Diff not supported in adapter yet", // Placeholder
225
235
  },
226
236
  providers: {}
227
237
  };
228
238
  // Also compare on remote APIs
229
239
  const repoInfo = getRepoInfo(projectPath);
230
- const githubOwner = process.env.GITHUB_USERNAME;
231
- const giteaOwner = process.env.GITEA_USERNAME;
240
+ const repo = params.repo || repoInfo.repoName;
241
+ const githubOwner = params.owner || process.env.GITHUB_USERNAME;
242
+ const giteaOwner = params.owner || process.env.GITEA_USERNAME;
232
243
  // GitHub
233
244
  if (ctx.providerManager.github && githubOwner) {
234
245
  try {
235
246
  const comparison = await ctx.providerManager.github.rest.repos.compareCommits({
236
247
  owner: githubOwner,
237
- repo: repoInfo.repoName,
248
+ repo: repo,
238
249
  base: baseBranch,
239
250
  head: compareBranch,
240
251
  });
@@ -63,7 +63,7 @@ export declare class GitConfigTool implements Tool {
63
63
  } | {
64
64
  success: boolean;
65
65
  scope: string;
66
- configs: import("simple-git").ConfigValues;
66
+ configs: Record<string, string>;
67
67
  key?: undefined;
68
68
  value?: undefined;
69
69
  message?: undefined;
@@ -74,7 +74,7 @@ export declare class GitConfigTool implements Tool {
74
74
  } | {
75
75
  success: boolean;
76
76
  key: any;
77
- value: string | null;
77
+ value: string | undefined;
78
78
  scope: string | undefined;
79
79
  message?: undefined;
80
80
  configs?: undefined;
@@ -1,4 +1,3 @@
1
- import simpleGit from 'simple-git';
2
1
  import { MCPError } from '../utils/errors.js';
3
2
  import * as fs from 'fs/promises';
4
3
  import * as path from 'path';
@@ -49,49 +48,49 @@ export class GitConfigTool {
49
48
  if (!action || !projectPath) {
50
49
  throw new MCPError('VALIDATION_ERROR', 'action and projectPath are required');
51
50
  }
52
- const git = simpleGit({ baseDir: projectPath });
51
+ if (!ctx.gitAdapter) {
52
+ throw new MCPError('INTERNAL_ERROR', 'Git adapter not available');
53
+ }
53
54
  const scope = params.global ? 'global' : params.system ? 'system' : 'local';
54
55
  switch (action) {
55
56
  case 'get': {
56
57
  const key = params.key;
57
58
  if (!key)
58
59
  throw new MCPError('VALIDATION_ERROR', 'key is required');
59
- try {
60
- const value = await git.getConfig(key, scope);
61
- return { success: true, key, value: value.value };
62
- }
63
- catch (err) {
60
+ const value = await ctx.gitAdapter.getConfig(projectPath, key, scope);
61
+ if (value === undefined) {
64
62
  throw new MCPError('CONFIG_NOT_FOUND', `Configuration key '${key}' not found`);
65
63
  }
64
+ return { success: true, key, value };
66
65
  }
67
66
  case 'set': {
68
67
  const key = params.key;
69
68
  const value = params.value;
70
69
  if (!key || value === undefined)
71
70
  throw new MCPError('VALIDATION_ERROR', 'key and value are required');
72
- await git.addConfig(key, value, false, scope);
71
+ await ctx.gitAdapter.setConfig(projectPath, key, value, scope);
73
72
  return { success: true, key, value };
74
73
  }
75
74
  case 'unset': {
76
75
  const key = params.key;
77
76
  if (!key)
78
77
  throw new MCPError('VALIDATION_ERROR', 'key is required');
79
- await git.raw(['config', `--${scope}`, '--unset', key]);
78
+ await ctx.gitAdapter.unsetConfig(projectPath, key, scope);
80
79
  return { success: true, key, message: 'Configuration removed' };
81
80
  }
82
81
  case 'list': {
83
- const configs = await git.listConfig(scope);
84
- return { success: true, scope, configs: configs.all };
82
+ const configs = await ctx.gitAdapter.listConfig(projectPath, scope);
83
+ return { success: true, scope, configs };
85
84
  }
86
85
  case 'show': {
87
86
  const key = params.key;
88
87
  if (!key)
89
88
  throw new MCPError('VALIDATION_ERROR', 'key is required');
90
- const value = await git.getConfig(key, scope);
89
+ const value = await ctx.gitAdapter.getConfig(projectPath, key, scope);
91
90
  return {
92
91
  success: true,
93
92
  key,
94
- value: value.value,
93
+ value,
95
94
  scope: params.showScope ? scope : undefined,
96
95
  };
97
96
  }
@@ -1,3 +1,4 @@
1
- export declare function handleGitFix(args: any): Promise<{
1
+ import { MCPContext } from '../types.js';
2
+ export declare function handleGitFix(args: any, ctx: MCPContext): Promise<{
2
3
  content: any[];
3
4
  }>;
@@ -1,9 +1,8 @@
1
- import simpleGit from 'simple-git';
2
1
  import path from 'path';
3
2
  import fs from 'fs/promises';
4
3
  import { existsSync } from 'fs';
5
4
  import { buildGitHubUrl, buildGiteaUrl } from '../utils/repoHelpers.js';
6
- export async function handleGitFix(args) {
5
+ export async function handleGitFix(args, ctx) {
7
6
  try {
8
7
  const { projectPath, githubRepo, giteaRepo, autoDetect = true } = args;
9
8
  if (!projectPath) {
@@ -13,7 +12,7 @@ export async function handleGitFix(args) {
13
12
  if (!existsSync(absolutePath)) {
14
13
  throw new Error(`Caminho não existe: ${absolutePath}`);
15
14
  }
16
- const git = simpleGit(absolutePath);
15
+ const git = ctx.gitAdapter;
17
16
  const result = {
18
17
  success: false,
19
18
  projectPath: absolutePath,
@@ -28,24 +27,21 @@ export async function handleGitFix(args) {
28
27
  // Verificar se é um repositório Git
29
28
  let isGitRepo = false;
30
29
  try {
31
- await git.status();
30
+ await git.status(absolutePath);
32
31
  isGitRepo = true;
33
32
  result.fixed.push('✅ Repositório Git válido encontrado');
34
33
  }
35
34
  catch (err) {
36
- if (err.message.includes('not a git repository')) {
37
- result.warnings.push('⚠️ Não é um repositório Git - inicializando...');
38
- await git.init();
39
- result.fixed.push('✅ Git inicializado');
40
- isGitRepo = true;
41
- }
42
- else {
43
- throw err;
44
- }
35
+ // isomorphic-git throws if not a repo, but we should check the error message or type if possible
36
+ // For now, assume any error means it's not a repo or has issues
37
+ result.warnings.push('⚠️ Não é um repositório Git - inicializando...');
38
+ await git.init(absolutePath);
39
+ result.fixed.push('✅ Git inicializado');
40
+ isGitRepo = true;
45
41
  }
46
42
  // Capturar remotes antes
47
- const remotesBefore = await git.getRemotes(true);
48
- result.remotes.before = remotesBefore.map(r => ({ name: r.name, url: r.refs.fetch || '' }));
43
+ const remotesBefore = await git.listRemotes(absolutePath);
44
+ result.remotes.before = remotesBefore.map(r => ({ name: r.name, url: r.url || '' }));
49
45
  // Obter usernames das env vars (OBRIGATÓRIO usar as credenciais fornecidas)
50
46
  const githubUsername = process.env.GITHUB_USERNAME;
51
47
  const giteaUsername = process.env.GITEA_USERNAME;
@@ -58,7 +54,7 @@ export async function handleGitFix(args) {
58
54
  let detectedRepoName = null;
59
55
  if (autoDetect && remotesBefore.length > 0) {
60
56
  for (const remote of remotesBefore) {
61
- const url = remote.refs.fetch || '';
57
+ const url = remote.url || '';
62
58
  // Detectar APENAS o nome do repo (sem username)
63
59
  if (url.includes('github.com') && !detectedRepoName) {
64
60
  const match = url.match(/github\.com[:/][^/]+\/([^/.]+)/);
@@ -102,27 +98,27 @@ export async function handleGitFix(args) {
102
98
  for (const remoteName of remotesToRemove) {
103
99
  const exists = remotesBefore.find(r => r.name === remoteName);
104
100
  if (exists) {
105
- await git.removeRemote(remoteName);
101
+ await git.removeRemote(absolutePath, remoteName);
106
102
  result.fixed.push(`🗑️ Removido remote antigo: ${remoteName}`);
107
103
  }
108
104
  }
109
105
  // Adicionar novos remotes no padrão dual
110
106
  const githubUrl = buildGitHubUrl(finalGithubRepo.split('/')[0], finalGithubRepo.split('/')[1]);
111
107
  const giteaUrlFull = buildGiteaUrl(finalGiteaRepo.split('/')[0], finalGiteaRepo.split('/')[1]);
112
- await git.addRemote('github', githubUrl);
108
+ await git.addRemote(absolutePath, 'github', githubUrl);
113
109
  result.fixed.push(`✅ Adicionado remote GitHub: ${githubUrl.replace(/oauth2:[^@]+@/, 'oauth2:***@')}`);
114
- await git.addRemote('gitea', giteaUrlFull);
110
+ await git.addRemote(absolutePath, 'gitea', giteaUrlFull);
115
111
  result.fixed.push(`✅ Adicionado remote Gitea: ${giteaUrlFull.replace(/:[^:]+@/, ':***@')}`);
116
112
  // Configurar origin como push múltiplo
117
- await git.addRemote('origin', githubUrl);
118
- await git.addConfig('remote.origin.pushurl', giteaUrlFull, false, 'local');
113
+ await git.addRemote(absolutePath, 'origin', githubUrl);
114
+ await git.setConfig(absolutePath, 'remote.origin.pushurl', giteaUrlFull, 'local');
119
115
  result.fixed.push(`✅ Configurado origin para push dual (GitHub + Gitea)`);
120
116
  // Capturar remotes depois
121
- const remotesAfter = await git.getRemotes(true);
122
- result.remotes.after = remotesAfter.map(r => ({ name: r.name, url: r.refs.fetch || '' }));
117
+ const remotesAfter = await git.listRemotes(absolutePath);
118
+ result.remotes.after = remotesAfter.map(r => ({ name: r.name, url: r.url || '' }));
123
119
  // Verificar se há commits
124
120
  try {
125
- await git.log();
121
+ await git.log(absolutePath);
126
122
  result.fixed.push('✅ Histórico de commits preservado');
127
123
  }
128
124
  catch (err) {
@@ -1,4 +1,4 @@
1
- import { Tool } from '../types.js';
1
+ import { Tool, MCPContext } from '../types.js';
2
2
  export declare class GitFixTool implements Tool {
3
3
  name: string;
4
4
  description: string;
@@ -25,7 +25,7 @@ export declare class GitFixTool implements Tool {
25
25
  required: string[];
26
26
  additionalProperties: boolean;
27
27
  };
28
- handle(args: any): Promise<{
28
+ handle(args: any, ctx: MCPContext): Promise<{
29
29
  content: any[];
30
30
  }>;
31
31
  }
@@ -86,7 +86,7 @@ export class GitFixTool {
86
86
  additionalProperties: true
87
87
  };
88
88
  }
89
- async handle(args) {
90
- return handleGitFix(args);
89
+ async handle(args, ctx) {
90
+ return handleGitFix(args, ctx);
91
91
  }
92
92
  }
@@ -1,4 +1,3 @@
1
- import simpleGit from 'simple-git';
2
1
  import { MCPError } from '../utils/errors.js';
3
2
  import axios from 'axios';
4
3
  import * as fs from 'fs/promises';
@@ -44,7 +43,7 @@ export class GitHistoryTool {
44
43
  throw new MCPError('VALIDATION_ERROR', 'projectPath is required');
45
44
  }
46
45
  const action = params.action || 'track';
47
- const git = simpleGit({ baseDir: projectPath });
46
+ const git = ctx.gitAdapter;
48
47
  switch (action) {
49
48
  case 'track': {
50
49
  // Rastrear mudança específica e criar histórico remoto
@@ -82,9 +81,9 @@ export class GitHistoryTool {
82
81
  };
83
82
  try {
84
83
  // 1. Coletar detalhes da mudança
85
- const status = await git.status();
86
- const log = await git.log({ maxCount: 1 });
87
- const lastCommit = log.latest;
84
+ const status = await git.status(projectPath);
85
+ const log = await git.log(projectPath, { maxCount: 1 });
86
+ const lastCommit = log.length > 0 ? log[0] : null;
88
87
  results.traceability.changeDetails = {
89
88
  branch: status.current,
90
89
  lastCommit: lastCommit ? {
@@ -84,9 +84,9 @@ export class GitIssuesTool {
84
84
  if (!projectPath)
85
85
  throw new MCPError('VALIDATION_ERROR', 'projectPath is required');
86
86
  const repoInfo = getRepoInfo(projectPath);
87
- const repo = repoInfo.repoName;
88
- const githubOwner = repoInfo.githubOwner || process.env.GITHUB_USERNAME;
89
- const giteaOwner = repoInfo.giteaOwner || process.env.GITEA_USERNAME;
87
+ const repo = params.repo || repoInfo.repoName;
88
+ const githubOwner = params.owner || repoInfo.githubOwner || process.env.GITHUB_USERNAME;
89
+ const giteaOwner = params.owner || repoInfo.giteaOwner || process.env.GITEA_USERNAME;
90
90
  switch (action) {
91
91
  case 'create': {
92
92
  if (!params.title)
@@ -174,7 +174,7 @@ export class GitIssuesTool {
174
174
  return results;
175
175
  }
176
176
  case 'get': {
177
- const issue_number = params.issue_number;
177
+ const issue_number = params.issue_number || params.issueNumber;
178
178
  if (!issue_number)
179
179
  throw new MCPError('VALIDATION_ERROR', 'issue_number is required');
180
180
  if (!repo)
@@ -210,7 +210,7 @@ export class GitIssuesTool {
210
210
  return results;
211
211
  }
212
212
  case 'update': {
213
- const issue_number = params.issue_number;
213
+ const issue_number = params.issue_number || params.issueNumber;
214
214
  if (!issue_number)
215
215
  throw new MCPError('VALIDATION_ERROR', 'issue_number is required');
216
216
  if (!repo)
@@ -253,7 +253,7 @@ export class GitIssuesTool {
253
253
  return results;
254
254
  }
255
255
  case 'close': {
256
- const issue_number = params.issue_number;
256
+ const issue_number = params.issue_number || params.issueNumber;
257
257
  if (!issue_number)
258
258
  throw new MCPError('VALIDATION_ERROR', 'issue_number is required');
259
259
  if (!repo)
@@ -290,8 +290,8 @@ export class GitIssuesTool {
290
290
  return results;
291
291
  }
292
292
  case 'comment': {
293
- const issue_number = params.issue_number;
294
- const comment_body = params.comment_body;
293
+ const issue_number = params.issue_number || params.issueNumber;
294
+ const comment_body = params.comment_body || params.body;
295
295
  if (!issue_number)
296
296
  throw new MCPError('VALIDATION_ERROR', 'issue_number is required');
297
297
  if (!comment_body)