@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
@@ -1,4 +1,3 @@
1
- import simpleGit from 'simple-git';
2
1
  import { MCPError } from '../utils/errors.js';
3
2
  export class GitSyncTool {
4
3
  constructor() {
@@ -39,12 +38,14 @@ export class GitSyncTool {
39
38
  if (!action || !projectPath) {
40
39
  throw new MCPError('VALIDATION_ERROR', 'action and projectPath are required');
41
40
  }
42
- const git = simpleGit({ baseDir: projectPath });
41
+ if (!ctx.gitAdapter) {
42
+ throw new MCPError('INTERNAL_ERROR', 'Git adapter not available');
43
+ }
43
44
  switch (action) {
44
45
  case 'fetch': {
45
46
  const remote = params.remote || 'origin';
46
47
  try {
47
- await git.fetch(remote);
48
+ await ctx.gitAdapter.fetch(projectPath, remote);
48
49
  return { success: true, projectPath, remote, action: 'fetch' };
49
50
  }
50
51
  catch (err) {
@@ -53,15 +54,13 @@ export class GitSyncTool {
53
54
  }
54
55
  case 'pull': {
55
56
  const remote = params.remote || 'origin';
56
- const branch = params.branch;
57
+ let branch = params.branch;
57
58
  try {
58
- if (branch) {
59
- await git.pull(remote, branch);
60
- }
61
- else {
62
- await git.pull(remote);
59
+ if (!branch) {
60
+ branch = await ctx.gitAdapter.getCurrentBranch(projectPath);
63
61
  }
64
- return { success: true, projectPath, remote, branch: branch || 'current', action: 'pull' };
62
+ await ctx.gitAdapter.pull(projectPath, remote, branch);
63
+ return { success: true, projectPath, remote, branch, action: 'pull' };
65
64
  }
66
65
  catch (err) {
67
66
  throw new MCPError('PULL_ERROR', `Failed to pull from ${remote}: ${err.message}`);
@@ -69,16 +68,20 @@ export class GitSyncTool {
69
68
  }
70
69
  case 'push': {
71
70
  const remote = params.remote || 'origin';
72
- const branch = params.branch;
71
+ let branch = params.branch;
72
+ const force = params.force || false;
73
73
  try {
74
- const pushResult = await git.push(remote, branch);
74
+ if (!branch) {
75
+ branch = await ctx.gitAdapter.getCurrentBranch(projectPath);
76
+ }
77
+ await ctx.gitAdapter.push(projectPath, remote, branch, force);
75
78
  return {
76
79
  success: true,
77
80
  projectPath,
78
81
  remote,
79
- branch: branch || 'current',
82
+ branch,
80
83
  action: 'push',
81
- result: pushResult
84
+ message: 'Push successful'
82
85
  };
83
86
  }
84
87
  catch (err) {
@@ -87,14 +90,20 @@ export class GitSyncTool {
87
90
  }
88
91
  case 'sync': {
89
92
  const remote = params.remote || 'origin';
90
- const branch = params.branch;
93
+ let branch = params.branch;
91
94
  try {
92
- if (branch) {
93
- await git.checkout(branch);
95
+ if (!branch) {
96
+ branch = await ctx.gitAdapter.getCurrentBranch(projectPath);
97
+ }
98
+ // Checkout if needed (not strictly necessary if we are already on the branch, but good for safety)
99
+ const currentBranch = await ctx.gitAdapter.getCurrentBranch(projectPath);
100
+ if (branch !== currentBranch) {
101
+ await ctx.gitAdapter.checkout(projectPath, branch);
94
102
  }
95
- await git.fetch(remote);
96
- await git.pull(remote, branch);
97
- return { success: true, projectPath, remote, branch: branch || 'current', message: 'Repository synced' };
103
+ await ctx.gitAdapter.fetch(projectPath, remote);
104
+ await ctx.gitAdapter.pull(projectPath, remote, branch);
105
+ await ctx.gitAdapter.push(projectPath, remote, branch);
106
+ return { success: true, projectPath, remote, branch, message: 'Repository synced' };
98
107
  }
99
108
  catch (err) {
100
109
  throw new MCPError('SYNC_ERROR', `Failed to sync with ${remote}: ${err.message}`);
@@ -102,8 +111,8 @@ export class GitSyncTool {
102
111
  }
103
112
  case 'status': {
104
113
  try {
105
- const status = await git.status();
106
- const remotes = await git.getRemotes(true);
114
+ const status = await ctx.gitAdapter.status(projectPath);
115
+ const remotes = await ctx.gitAdapter.listRemotes(projectPath);
107
116
  return {
108
117
  success: true,
109
118
  projectPath,
@@ -114,7 +123,7 @@ export class GitSyncTool {
114
123
  modified: status.modified,
115
124
  created: status.created,
116
125
  deleted: status.deleted,
117
- clean: status.isClean(),
126
+ clean: status.isClean,
118
127
  },
119
128
  remotes,
120
129
  };
@@ -126,7 +135,7 @@ export class GitSyncTool {
126
135
  case 'one-shot': {
127
136
  // One-shot sync: fetch, pull, and push in a single operation
128
137
  const remote = params.remote || 'origin';
129
- const branch = params.branch;
138
+ let branch = params.branch;
130
139
  const results = {
131
140
  success: true,
132
141
  projectPath,
@@ -135,31 +144,30 @@ export class GitSyncTool {
135
144
  steps: [],
136
145
  };
137
146
  try {
147
+ if (!branch) {
148
+ branch = await ctx.gitAdapter.getCurrentBranch(projectPath);
149
+ results.branch = branch;
150
+ }
138
151
  // Step 1: Fetch
139
152
  results.steps.push({ action: 'fetch', timestamp: new Date().toISOString() });
140
- await git.fetch(remote);
153
+ await ctx.gitAdapter.fetch(projectPath, remote);
141
154
  // Step 2: Pull
142
155
  results.steps.push({ action: 'pull', timestamp: new Date().toISOString() });
143
- if (branch) {
144
- await git.pull(remote, branch);
145
- }
146
- else {
147
- await git.pull(remote);
148
- }
156
+ await ctx.gitAdapter.pull(projectPath, remote, branch);
149
157
  // Step 3: Push
150
158
  results.steps.push({ action: 'push', timestamp: new Date().toISOString() });
151
- const pushResult = await git.push(remote, branch, ['--set-upstream']);
159
+ await ctx.gitAdapter.push(projectPath, remote, branch, false, true);
152
160
  results.pushResult = {
153
- pushed: pushResult.pushed || [],
154
- remoteMessages: pushResult.remoteMessages?.all || [],
161
+ pushed: true, // Isomorphic git doesn't return detailed push stats easily
162
+ message: 'Push successful'
155
163
  };
156
164
  // Step 4: Final status
157
- const finalStatus = await git.status();
165
+ const finalStatus = await ctx.gitAdapter.status(projectPath);
158
166
  results.finalStatus = {
159
167
  branch: finalStatus.current,
160
168
  ahead: finalStatus.ahead,
161
169
  behind: finalStatus.behind,
162
- clean: finalStatus.isClean(),
170
+ clean: finalStatus.isClean,
163
171
  };
164
172
  return results;
165
173
  }
@@ -26,10 +26,18 @@ export declare class GitTagsTool implements Tool {
26
26
  type: string;
27
27
  description: string;
28
28
  };
29
+ annotated: {
30
+ type: string;
31
+ description: string;
32
+ };
29
33
  force: {
30
34
  type: string;
31
35
  description: string;
32
36
  };
37
+ remote: {
38
+ type: string;
39
+ description: string;
40
+ };
33
41
  };
34
42
  required: string[];
35
43
  };
@@ -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 { getRepoInfo } from '../utils/repoHelpers.js';
@@ -30,9 +29,17 @@ export class GitTagsTool {
30
29
  type: "string",
31
30
  description: "Tag annotation message (optional for create)"
32
31
  },
32
+ annotated: {
33
+ type: "boolean",
34
+ description: "Create annotated tag (optional for create, default: false)"
35
+ },
33
36
  force: {
34
37
  type: "boolean",
35
38
  description: "Force create/delete/push tag (default: true)"
39
+ },
40
+ remote: {
41
+ type: "string",
42
+ description: "Remote name (optional for push, default: origin)"
36
43
  }
37
44
  },
38
45
  required: ["projectPath", "action"]
@@ -44,37 +51,37 @@ export class GitTagsTool {
44
51
  if (!action || !projectPath) {
45
52
  throw new MCPError('VALIDATION_ERROR', 'action and projectPath are required');
46
53
  }
47
- const git = simpleGit({ baseDir: projectPath });
54
+ if (!ctx.gitAdapter) {
55
+ throw new MCPError('INTERNAL_ERROR', 'Git adapter not available');
56
+ }
48
57
  switch (action) {
49
58
  case 'create': {
50
59
  const tagName = params.tagName;
51
60
  if (!tagName)
52
61
  throw new MCPError('VALIDATION_ERROR', 'tagName is required');
53
- if (params.annotated) {
54
- await git.addAnnotatedTag(tagName, params.message || tagName);
55
- }
56
- else {
57
- await git.addTag(tagName);
58
- }
62
+ const ref = params.ref || 'HEAD';
63
+ const message = params.annotated ? (params.message || tagName) : undefined;
64
+ await ctx.gitAdapter.createTag(projectPath, tagName, ref, message);
59
65
  return { success: true, tag: tagName, local: true };
60
66
  }
61
67
  case 'list': {
62
- const localTags = await git.tags();
68
+ const localTags = await ctx.gitAdapter.listTags(projectPath);
63
69
  const results = {
64
70
  success: true,
65
- local: { tags: localTags.all },
71
+ local: { tags: localTags },
66
72
  providers: {}
67
73
  };
68
74
  // Also query remote APIs
69
75
  const repoInfo = getRepoInfo(projectPath);
70
- const githubOwner = process.env.GITHUB_USERNAME;
71
- const giteaOwner = process.env.GITEA_USERNAME;
76
+ const repo = params.repo || repoInfo.repoName;
77
+ const githubOwner = params.owner || process.env.GITHUB_USERNAME;
78
+ const giteaOwner = params.owner || process.env.GITEA_USERNAME;
72
79
  // GitHub
73
80
  if (ctx.providerManager.github && githubOwner) {
74
81
  try {
75
82
  const tags = await ctx.providerManager.github.rest.repos.listTags({
76
83
  owner: githubOwner,
77
- repo: repoInfo.repoName,
84
+ repo: repo,
78
85
  });
79
86
  results.providers.github = {
80
87
  success: true,
@@ -93,7 +100,7 @@ export class GitTagsTool {
93
100
  // Gitea
94
101
  if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
95
102
  try {
96
- const tags = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repoInfo.repoName}/tags`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
103
+ const tags = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/tags`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
97
104
  results.providers.gitea = {
98
105
  success: true,
99
106
  tags: tags.data.map((t) => ({
@@ -114,30 +121,24 @@ export class GitTagsTool {
114
121
  const tagName = params.tagName;
115
122
  if (!tagName)
116
123
  throw new MCPError('VALIDATION_ERROR', 'tagName is required');
117
- const localTags = await git.tags();
118
- const localTag = localTags.all.find(t => t === tagName);
124
+ const localTags = await ctx.gitAdapter.listTags(projectPath);
125
+ const localTag = localTags.find(t => t === tagName);
119
126
  const results = {
120
127
  success: true,
121
128
  local: localTag ? { found: true, tag: tagName } : { found: false },
122
129
  providers: {}
123
130
  };
124
- if (localTag) {
125
- try {
126
- const show = await git.show([tagName]);
127
- results.local.details = show;
128
- }
129
- catch { }
130
- }
131
131
  // Also query remote APIs
132
132
  const repoInfo = getRepoInfo(projectPath);
133
- const githubOwner = process.env.GITHUB_USERNAME;
134
- const giteaOwner = process.env.GITEA_USERNAME;
133
+ const repo = params.repo || repoInfo.repoName;
134
+ const githubOwner = params.owner || process.env.GITHUB_USERNAME;
135
+ const giteaOwner = params.owner || process.env.GITEA_USERNAME;
135
136
  // GitHub
136
137
  if (ctx.providerManager.github && githubOwner) {
137
138
  try {
138
139
  const tag = await ctx.providerManager.github.rest.git.getRef({
139
140
  owner: githubOwner,
140
- repo: repoInfo.repoName,
141
+ repo: repo,
141
142
  ref: `tags/${tagName}`,
142
143
  });
143
144
  results.providers.github = {
@@ -157,7 +158,7 @@ export class GitTagsTool {
157
158
  // Gitea
158
159
  if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
159
160
  try {
160
- const tags = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repoInfo.repoName}/tags`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
161
+ const tags = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/tags`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
161
162
  const tag = tags.data.find((t) => t.name === tagName);
162
163
  if (tag) {
163
164
  results.providers.gitea = {
@@ -183,15 +184,23 @@ export class GitTagsTool {
183
184
  const tagName = params.tagName;
184
185
  if (!tagName)
185
186
  throw new MCPError('VALIDATION_ERROR', 'tagName is required');
186
- await git.tag(['-d', tagName]);
187
+ await ctx.gitAdapter.deleteTag(projectPath, tagName);
187
188
  return { success: true, deleted: tagName, local: true };
188
189
  }
190
+ case 'push': {
191
+ const tagName = params.tagName;
192
+ if (!tagName)
193
+ throw new MCPError('VALIDATION_ERROR', 'tagName is required');
194
+ const remote = params.remote || 'origin';
195
+ await ctx.gitAdapter.pushTag(projectPath, remote, tagName);
196
+ return { success: true, pushed: tagName, remote };
197
+ }
189
198
  case 'search': {
190
199
  const pattern = params.pattern;
191
200
  if (!pattern)
192
201
  throw new MCPError('VALIDATION_ERROR', 'pattern is required');
193
- const localTags = await git.tags();
194
- const filtered = localTags.all.filter(t => t.includes(pattern));
202
+ const localTags = await ctx.gitAdapter.listTags(projectPath);
203
+ const filtered = localTags.filter(t => t.includes(pattern));
195
204
  const results = {
196
205
  success: true,
197
206
  local: { tags: filtered },
@@ -199,14 +208,15 @@ export class GitTagsTool {
199
208
  };
200
209
  // Also search remote APIs
201
210
  const repoInfo = getRepoInfo(projectPath);
202
- const githubOwner = process.env.GITHUB_USERNAME;
203
- const giteaOwner = process.env.GITEA_USERNAME;
211
+ const repo = params.repo || repoInfo.repoName;
212
+ const githubOwner = params.owner || process.env.GITHUB_USERNAME;
213
+ const giteaOwner = params.owner || process.env.GITEA_USERNAME;
204
214
  // GitHub
205
215
  if (ctx.providerManager.github && githubOwner) {
206
216
  try {
207
217
  const tags = await ctx.providerManager.github.rest.repos.listTags({
208
218
  owner: githubOwner,
209
- repo: repoInfo.repoName,
219
+ repo: repo,
210
220
  });
211
221
  const matchedTags = tags.data.filter((t) => t.name.includes(pattern));
212
222
  results.providers.github = {
@@ -221,7 +231,7 @@ export class GitTagsTool {
221
231
  // Gitea
222
232
  if (ctx.providerManager.giteaBaseUrl && giteaOwner) {
223
233
  try {
224
- const tags = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repoInfo.repoName}/tags`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
234
+ const tags = await axios.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repo}/tags`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
225
235
  const matchedTags = tags.data.filter((t) => t.name.includes(pattern));
226
236
  results.providers.gitea = {
227
237
  success: true,
@@ -22,6 +22,33 @@ export declare class GitUpdateTool implements Tool {
22
22
  type: string;
23
23
  description: string;
24
24
  };
25
+ message: {
26
+ type: string;
27
+ description: string;
28
+ };
29
+ commitMessage: {
30
+ type: string;
31
+ description: string;
32
+ };
33
+ branch: {
34
+ type: string;
35
+ description: string;
36
+ };
37
+ files: {
38
+ type: string;
39
+ items: {
40
+ type: string;
41
+ };
42
+ description: string;
43
+ };
44
+ repoName: {
45
+ type: string;
46
+ description: string;
47
+ };
48
+ description: {
49
+ type: string;
50
+ description: string;
51
+ };
25
52
  };
26
53
  required: string[];
27
54
  additionalProperties: boolean;
@@ -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';
@@ -27,6 +26,31 @@ export class GitUpdateTool {
27
26
  owner: {
28
27
  type: "string",
29
28
  description: "Repository owner for providers (optional, uses env vars)"
29
+ },
30
+ message: {
31
+ type: "string",
32
+ description: "Commit message"
33
+ },
34
+ commitMessage: {
35
+ type: "string",
36
+ description: "Commit message (alias)"
37
+ },
38
+ branch: {
39
+ type: "string",
40
+ description: "Branch name"
41
+ },
42
+ files: {
43
+ type: "array",
44
+ items: { type: "string" },
45
+ description: "Files to add"
46
+ },
47
+ repoName: {
48
+ type: "string",
49
+ description: "Repository name"
50
+ },
51
+ description: {
52
+ type: "string",
53
+ description: "Repository description"
30
54
  }
31
55
  },
32
56
  required: ["projectPath"],
@@ -38,10 +62,12 @@ export class GitUpdateTool {
38
62
  if (!projectPath) {
39
63
  throw new MCPError('VALIDATION_ERROR', 'projectPath is required');
40
64
  }
65
+ if (!ctx.gitAdapter) {
66
+ throw new MCPError('INTERNAL_ERROR', 'Git adapter not available');
67
+ }
41
68
  const commitMessage = params.message || params.commitMessage || `[git-update] Update at ${new Date().toISOString()}`;
42
69
  const branch = params.branch || 'master';
43
70
  const files = params.files || ['.'];
44
- const git = simpleGit({ baseDir: projectPath });
45
71
  // Resultado com rastreabilidade completa
46
72
  const results = {
47
73
  success: true,
@@ -65,7 +91,7 @@ export class GitUpdateTool {
65
91
  action: 'check_pre_update_status',
66
92
  timestamp: new Date().toISOString(),
67
93
  });
68
- const preStatus = await git.status();
94
+ const preStatus = await ctx.gitAdapter.status(projectPath);
69
95
  results.traceability.preUpdateStatus = {
70
96
  branch: preStatus.current,
71
97
  ahead: preStatus.ahead,
@@ -76,7 +102,7 @@ export class GitUpdateTool {
76
102
  renamed: preStatus.renamed,
77
103
  staged: preStatus.staged,
78
104
  conflicted: preStatus.conflicted,
79
- isClean: preStatus.isClean(),
105
+ isClean: preStatus.isClean,
80
106
  };
81
107
  // 2. Detectar arquivos alterados com diff
82
108
  results.traceability.updateSteps.push({
@@ -84,15 +110,11 @@ export class GitUpdateTool {
84
110
  action: 'detect_changes',
85
111
  timestamp: new Date().toISOString(),
86
112
  });
87
- const diff = await git.diff(['--name-status']);
88
- const diffLines = diff.split('\n').filter(l => l.trim());
89
- results.traceability.changedFiles = diffLines.map(line => {
90
- const [status, ...fileParts] = line.split('\t');
91
- return {
92
- status: status.trim(),
93
- file: fileParts.join('\t').trim(),
94
- };
95
- });
113
+ // Isomorphic git diff is basic, so we rely on status for changed files list
114
+ results.traceability.changedFiles = preStatus.files.map(f => ({
115
+ status: f.working_dir,
116
+ file: f.path
117
+ }));
96
118
  // 3. Add files
97
119
  results.traceability.updateSteps.push({
98
120
  step: 3,
@@ -100,7 +122,7 @@ export class GitUpdateTool {
100
122
  timestamp: new Date().toISOString(),
101
123
  files,
102
124
  });
103
- await git.add(files);
125
+ await ctx.gitAdapter.add(projectPath, files);
104
126
  // 4. Commit
105
127
  results.traceability.updateSteps.push({
106
128
  step: 4,
@@ -110,7 +132,13 @@ export class GitUpdateTool {
110
132
  });
111
133
  let commitResult;
112
134
  try {
113
- commitResult = await git.commit(commitMessage);
135
+ const sha = await ctx.gitAdapter.commit(projectPath, commitMessage);
136
+ commitResult = {
137
+ commit: sha,
138
+ summary: { changes: 0, insertions: 0, deletions: 0 }, // Basic adapter doesn't return stats yet
139
+ branch: await ctx.gitAdapter.getCurrentBranch(projectPath),
140
+ author: { name: 'unknown', email: 'unknown' } // We could fetch this if needed
141
+ };
114
142
  results.traceability.commit = {
115
143
  hash: commitResult.commit,
116
144
  summary: commitResult.summary,
@@ -119,7 +147,7 @@ export class GitUpdateTool {
119
147
  };
120
148
  }
121
149
  catch (err) {
122
- if (err.message.includes('nothing to commit')) {
150
+ if (err.message.includes('nothing to commit') || (preStatus.isClean && files[0] === '.')) {
123
151
  results.traceability.commit = { message: 'No changes to commit' };
124
152
  }
125
153
  else {
@@ -127,7 +155,7 @@ export class GitUpdateTool {
127
155
  }
128
156
  }
129
157
  // 5. Get remotes and ensure repos exist
130
- const remotes = await git.getRemotes(true);
158
+ const remotes = await ctx.gitAdapter.listRemotes(projectPath);
131
159
  let githubRemote = remotes.find(r => r.name === 'github' || r.name === 'origin');
132
160
  let giteaRemote = remotes.find(r => r.name === 'gitea');
133
161
  // Auto-create repos if they don't exist
@@ -169,8 +197,9 @@ export class GitUpdateTool {
169
197
  });
170
198
  }
171
199
  // Add remote
172
- await git.addRemote('github', `https://github.com/${githubOwner}/${repoName}.git`);
173
- githubRemote = { name: 'github', refs: { push: `https://github.com/${githubOwner}/${repoName}.git`, fetch: '' } };
200
+ const url = `https://github.com/${githubOwner}/${repoName}.git`;
201
+ await ctx.gitAdapter.addRemote(projectPath, 'github', url);
202
+ githubRemote = { name: 'github', remote: 'github', url, fetch: url };
174
203
  }
175
204
  catch (err) {
176
205
  results.traceability.errors.push({
@@ -211,8 +240,9 @@ export class GitUpdateTool {
211
240
  });
212
241
  }
213
242
  // Add remote
214
- await git.addRemote('gitea', `${ctx.providerManager.giteaBaseUrl}/${giteaOwner}/${repoName}.git`);
215
- giteaRemote = { name: 'gitea', refs: { push: `${ctx.providerManager.giteaBaseUrl}/${giteaOwner}/${repoName}.git`, fetch: '' } };
243
+ const url = `${ctx.providerManager.giteaBaseUrl}/${giteaOwner}/${repoName}.git`;
244
+ await ctx.gitAdapter.addRemote(projectPath, 'gitea', url);
245
+ giteaRemote = { name: 'gitea', remote: 'gitea', url, fetch: url };
216
246
  }
217
247
  catch (err) {
218
248
  results.traceability.errors.push({
@@ -233,15 +263,14 @@ export class GitUpdateTool {
233
263
  branch,
234
264
  });
235
265
  try {
236
- const pushResult = await git.push(githubRemote.name, branch);
266
+ await ctx.gitAdapter.push(projectPath, githubRemote.name, branch);
237
267
  results.providers.github = {
238
268
  success: true,
239
269
  remote: githubRemote.name,
240
- url: githubRemote.refs.push,
270
+ url: githubRemote.url,
241
271
  pushed: true,
242
272
  pushResult: {
243
- remoteMessages: pushResult.remoteMessages?.all || [],
244
- update: pushResult.update,
273
+ message: 'Push successful'
245
274
  }
246
275
  };
247
276
  // Verificar commit no GitHub via API
@@ -285,15 +314,14 @@ export class GitUpdateTool {
285
314
  branch,
286
315
  });
287
316
  try {
288
- const pushResult = await git.push(giteaRemote.name, branch);
317
+ await ctx.gitAdapter.push(projectPath, giteaRemote.name, branch);
289
318
  results.providers.gitea = {
290
319
  success: true,
291
320
  remote: giteaRemote.name,
292
- url: giteaRemote.refs.push,
321
+ url: giteaRemote.url,
293
322
  pushed: true,
294
323
  pushResult: {
295
- remoteMessages: pushResult.remoteMessages?.all || [],
296
- update: pushResult.update,
324
+ message: 'Push successful'
297
325
  }
298
326
  };
299
327
  // Verificar commit no Gitea via API
@@ -328,12 +356,12 @@ export class GitUpdateTool {
328
356
  action: 'check_post_update_status',
329
357
  timestamp: new Date().toISOString(),
330
358
  });
331
- const postStatus = await git.status();
359
+ const postStatus = await ctx.gitAdapter.status(projectPath);
332
360
  results.traceability.postUpdateStatus = {
333
361
  branch: postStatus.current,
334
362
  ahead: postStatus.ahead,
335
363
  behind: postStatus.behind,
336
- isClean: postStatus.isClean(),
364
+ isClean: postStatus.isClean,
337
365
  tracking: postStatus.tracking,
338
366
  };
339
367
  // 9. Salvar histórico local
@@ -374,12 +402,11 @@ export class GitUpdateTool {
374
402
  const githubOwner = params.owner || process.env.GITHUB_USERNAME;
375
403
  const giteaOwner = params.owner || process.env.GITEA_USERNAME;
376
404
  // Pegar nome do repo dos remotes do Git ao invés do nome da pasta
377
- const git = simpleGit(projectPath);
378
- const remotes = await git.getRemotes(true);
405
+ const remotes = await ctx.gitAdapter.listRemotes(projectPath);
379
406
  let repoName = getRepoNameFromPath(projectPath); // fallback
380
407
  if (remotes.length > 0) {
381
408
  const githubRemote = remotes.find(r => r.name === 'github') || remotes.find(r => r.name === 'origin') || remotes[0];
382
- const match = githubRemote.refs.push?.match(/\/([^\/]+?)(\.git)?$/);
409
+ const match = githubRemote.fetch?.match(/\/([^\/]+?)(\.git)?$/);
383
410
  if (match)
384
411
  repoName = match[1];
385
412
  }