@andrebuzeli/git-mcp 10.0.7 β 10.0.8
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/LICENSE +0 -0
- package/README.md +1 -1
- package/dist/providers/providerManager.js +2 -4
- package/dist/scripts/test_e2e.d.ts +1 -0
- package/dist/scripts/test_e2e.js +199 -0
- package/dist/scripts/test_exhaustive.d.ts +1 -0
- package/dist/scripts/test_exhaustive.js +275 -0
- package/dist/scripts/verify_setup.d.ts +1 -0
- package/dist/scripts/verify_setup.js +61 -0
- package/dist/tools/gitAnalytics.js +3 -3
- package/dist/tools/gitBackup.d.ts +2 -2
- package/dist/tools/gitBackup.js +5 -5
- package/dist/tools/gitBranches.js +40 -29
- package/dist/tools/gitConfig.d.ts +2 -2
- package/dist/tools/gitConfig.js +12 -13
- package/dist/tools/gitFix.d.ts +2 -1
- package/dist/tools/gitFix.js +20 -24
- package/dist/tools/gitFix.tool.d.ts +2 -2
- package/dist/tools/gitFix.tool.js +2 -2
- package/dist/tools/gitHistory.js +4 -5
- package/dist/tools/gitIssues.js +8 -8
- package/dist/tools/gitMonitor.js +28 -33
- package/dist/tools/gitPulls.js +8 -8
- package/dist/tools/gitRelease.js +5 -6
- package/dist/tools/gitRemote.d.ts +1 -11
- package/dist/tools/gitRemote.js +14 -20
- package/dist/tools/gitReset.js +6 -7
- package/dist/tools/gitStash.d.ts +1 -12
- package/dist/tools/gitStash.js +11 -22
- package/dist/tools/gitSync.js +44 -36
- package/dist/tools/gitTags.d.ts +8 -0
- package/dist/tools/gitTags.js +44 -34
- package/dist/tools/gitUpdate.d.ts +27 -0
- package/dist/tools/gitUpdate.js +61 -34
- package/dist/tools/gitUpload.js +29 -44
- package/dist/tools/gitWorkflow.js +27 -46
- package/dist/utils/repoHelpers.js +7 -12
- package/package.json +2 -3
- package/dist/server-new.d.ts +0 -2
- package/dist/server-new.js +0 -224
- package/dist/tools/gitFiles-new.d.ts +0 -89
- package/dist/tools/gitFiles-new.js +0 -335
package/LICENSE
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
@@ -83,7 +83,7 @@ This MCP server supports **2 providers** (for remote API operations only):
|
|
|
83
83
|
- **`github`** - GitHub remote operations (issues, PRs, releases) via @octokit/rest
|
|
84
84
|
- **`gitea`** - Gitea remote operations (issues, PRs, releases) via axios to nas-ubuntu:3000
|
|
85
85
|
|
|
86
|
-
**Important**: Local Git operations (commit, branch, tag, stash, etc) use `
|
|
86
|
+
**Important**: Local Git operations (commit, branch, tag, stash, etc) use `isomorphic-git` directly and do **NOT** require a `provider` parameter. The `provider` parameter is only needed for remote API operations (issues, PRs, releases, etc). Gitea as a provider is only for remote API calls, not for local Git operations.
|
|
87
87
|
|
|
88
88
|
## Quick Start
|
|
89
89
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Octokit } from '@octokit/rest';
|
|
2
2
|
import axios from 'axios';
|
|
3
|
-
import { getGiteaUrl } from '../utils/repoHelpers.js';
|
|
4
3
|
export class ProviderManager {
|
|
5
4
|
constructor() {
|
|
6
5
|
const ghToken = process.env.GITHUB_TOKEN;
|
|
@@ -8,7 +7,7 @@ export class ProviderManager {
|
|
|
8
7
|
this.github = new Octokit({ auth: ghToken });
|
|
9
8
|
}
|
|
10
9
|
if (process.env.GITEA_URL && process.env.GITEA_TOKEN) {
|
|
11
|
-
this.giteaBaseUrl =
|
|
10
|
+
this.giteaBaseUrl = process.env.GITEA_URL;
|
|
12
11
|
this.giteaToken = process.env.GITEA_TOKEN;
|
|
13
12
|
}
|
|
14
13
|
}
|
|
@@ -32,8 +31,7 @@ export class ProviderManager {
|
|
|
32
31
|
}
|
|
33
32
|
if (this.giteaBaseUrl && this.giteaToken) {
|
|
34
33
|
try {
|
|
35
|
-
const
|
|
36
|
-
const resp = await axios.get(`${base}/api/v1/user`, {
|
|
34
|
+
const resp = await axios.get(`${this.giteaBaseUrl}/api/v1/user`, {
|
|
37
35
|
headers: { Authorization: `token ${this.giteaToken}` },
|
|
38
36
|
timeout: timeout,
|
|
39
37
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import { ProviderManager } from '../providers/providerManager.js';
|
|
5
|
+
import { IsomorphicGitAdapter } from '../utils/gitAdapter.js';
|
|
6
|
+
import { GitWorkflowTool } from '../tools/gitWorkflow.js';
|
|
7
|
+
import { GitRemoteTool } from '../tools/gitRemote.js';
|
|
8
|
+
import { GitBranchesTool } from '../tools/gitBranches.js';
|
|
9
|
+
import { GitIssuesTool } from '../tools/gitIssues.js';
|
|
10
|
+
import { GitConfigTool } from '../tools/gitConfig.js';
|
|
11
|
+
import { GitFilesTool } from '../tools/gitFiles.js';
|
|
12
|
+
// Set Credentials
|
|
13
|
+
process.env.GITEA_URL = "https://git.hubuzeli.com";
|
|
14
|
+
process.env.GITEA_TOKEN = "09b19262127241b2c608f1e58ac90af0cce21422";
|
|
15
|
+
process.env.GITEA_USERNAME = "andrebuzeli";
|
|
16
|
+
process.env.GITHUB_TOKEN = "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";
|
|
17
|
+
process.env.GITHUB_USERNAME = "Andre-Buzeli";
|
|
18
|
+
async function runE2ETest() {
|
|
19
|
+
const timestamp = Date.now();
|
|
20
|
+
const repoName = `git-mcp-e2e-test-${timestamp}`;
|
|
21
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'git-mcp-e2e-'));
|
|
22
|
+
console.log(`π Starting E2E Test Suite`);
|
|
23
|
+
console.log(`π Local Temp Dir: ${tempDir}`);
|
|
24
|
+
console.log(`π¦ Target Repo Name: ${repoName}`);
|
|
25
|
+
const providerManager = new ProviderManager();
|
|
26
|
+
const gitAdapter = new IsomorphicGitAdapter(providerManager);
|
|
27
|
+
const ctx = { providerManager, gitAdapter };
|
|
28
|
+
const tools = {
|
|
29
|
+
workflow: new GitWorkflowTool(),
|
|
30
|
+
remote: new GitRemoteTool(),
|
|
31
|
+
branches: new GitBranchesTool(),
|
|
32
|
+
issues: new GitIssuesTool(),
|
|
33
|
+
config: new GitConfigTool(),
|
|
34
|
+
files: new GitFilesTool(),
|
|
35
|
+
};
|
|
36
|
+
try {
|
|
37
|
+
// 1. Create Remote Repositories
|
|
38
|
+
console.log('\n1οΈβ£ Creating Remote Repositories...');
|
|
39
|
+
const createResult = await tools.workflow.handle({
|
|
40
|
+
action: 'create',
|
|
41
|
+
name: repoName,
|
|
42
|
+
description: 'Automated E2E Test Repository for git-mcp',
|
|
43
|
+
private: true,
|
|
44
|
+
projectPath: tempDir // Required by schema even if not used for remote create
|
|
45
|
+
}, ctx);
|
|
46
|
+
const githubRepo = createResult.providers.github?.repo;
|
|
47
|
+
const giteaRepo = createResult.providers.gitea?.repo;
|
|
48
|
+
if (githubRepo)
|
|
49
|
+
console.log(` β
GitHub Repo Created: ${githubRepo.html_url}`);
|
|
50
|
+
else
|
|
51
|
+
console.log(` β GitHub Repo Creation Failed: ${createResult.providers.github?.error}`);
|
|
52
|
+
if (giteaRepo)
|
|
53
|
+
console.log(` β
Gitea Repo Created: ${giteaRepo.html_url}`);
|
|
54
|
+
else
|
|
55
|
+
console.log(` β Gitea Repo Creation Failed: ${createResult.providers.gitea?.error}`);
|
|
56
|
+
// 2. Initialize Local Repository
|
|
57
|
+
console.log('\n2οΈβ£ Initializing Local Repository...');
|
|
58
|
+
await tools.workflow.handle({ action: 'init', projectPath: tempDir }, ctx);
|
|
59
|
+
await tools.config.handle({ action: 'set', projectPath: tempDir, key: 'user.name', value: 'E2E Test Bot' }, ctx);
|
|
60
|
+
await tools.config.handle({ action: 'set', projectPath: tempDir, key: 'user.email', value: 'bot@git-mcp.test' }, ctx);
|
|
61
|
+
console.log(' β
Local repo initialized and configured');
|
|
62
|
+
// 3. Create Content & Commit
|
|
63
|
+
console.log('\n3οΈβ£ Creating Content & Committing...');
|
|
64
|
+
fs.writeFileSync(path.join(tempDir, 'README.md'), `# ${repoName}\n\nAutomated test repository.`);
|
|
65
|
+
await tools.workflow.handle({ action: 'commit', projectPath: tempDir, message: 'Initial commit', files: ['.'] }, ctx);
|
|
66
|
+
console.log(' β
Initial commit created');
|
|
67
|
+
// 4. Add Remotes
|
|
68
|
+
console.log('\n4οΈβ£ Adding Remotes...');
|
|
69
|
+
if (githubRepo) {
|
|
70
|
+
await tools.remote.handle({ action: 'add', projectPath: tempDir, remoteName: 'github', url: githubRepo.clone_url }, ctx);
|
|
71
|
+
console.log(' β
GitHub remote added');
|
|
72
|
+
}
|
|
73
|
+
if (giteaRepo) {
|
|
74
|
+
await tools.remote.handle({ action: 'add', projectPath: tempDir, remoteName: 'gitea', url: giteaRepo.clone_url }, ctx);
|
|
75
|
+
console.log(' β
Gitea remote added');
|
|
76
|
+
}
|
|
77
|
+
// 5. Push to Remotes
|
|
78
|
+
console.log('\n5οΈβ£ Pushing to Remotes...');
|
|
79
|
+
if (githubRepo) {
|
|
80
|
+
try {
|
|
81
|
+
await tools.workflow.handle({ action: 'push', projectPath: tempDir, remote: 'github', branch: 'master' }, ctx);
|
|
82
|
+
console.log(' β
Pushed to GitHub');
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
console.log(` β Push to GitHub Failed: ${e.message}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (giteaRepo) {
|
|
89
|
+
try {
|
|
90
|
+
await tools.workflow.handle({ action: 'push', projectPath: tempDir, remote: 'gitea', branch: 'master' }, ctx);
|
|
91
|
+
console.log(' β
Pushed to Gitea');
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
console.log(` β Push to Gitea Failed: ${e.message}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// 6. Feature Branch Workflow
|
|
98
|
+
console.log('\n6οΈβ£ Feature Branch Workflow...');
|
|
99
|
+
await tools.branches.handle({ action: 'create', projectPath: tempDir, branchName: 'feature/test-1' }, ctx);
|
|
100
|
+
fs.writeFileSync(path.join(tempDir, 'feature.txt'), 'This is a feature file.');
|
|
101
|
+
await tools.workflow.handle({ action: 'commit', projectPath: tempDir, message: 'Add feature file', files: ['.'] }, ctx);
|
|
102
|
+
console.log(' β
Feature branch created and committed');
|
|
103
|
+
if (githubRepo) {
|
|
104
|
+
try {
|
|
105
|
+
await tools.workflow.handle({ action: 'push', projectPath: tempDir, remote: 'github', branch: 'feature/test-1' }, ctx);
|
|
106
|
+
console.log(' β
Feature branch pushed to GitHub');
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
console.log(` β Push feature to GitHub Failed: ${e.message}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// 7. Issue Management
|
|
113
|
+
console.log('\n7οΈβ£ Issue Management...');
|
|
114
|
+
let issueNumber = 0;
|
|
115
|
+
if (githubRepo) {
|
|
116
|
+
try {
|
|
117
|
+
const issue = await tools.issues.handle({
|
|
118
|
+
action: 'create',
|
|
119
|
+
projectPath: tempDir,
|
|
120
|
+
provider: 'github',
|
|
121
|
+
repo: repoName,
|
|
122
|
+
owner: process.env.GITHUB_USERNAME,
|
|
123
|
+
title: 'Test Issue',
|
|
124
|
+
body: 'This is an automated test issue.'
|
|
125
|
+
}, ctx);
|
|
126
|
+
issueNumber = issue.providers.github.issue.number;
|
|
127
|
+
console.log(` β
Created GitHub Issue #${issueNumber}`);
|
|
128
|
+
await tools.issues.handle({
|
|
129
|
+
action: 'comment',
|
|
130
|
+
projectPath: tempDir,
|
|
131
|
+
provider: 'github',
|
|
132
|
+
repo: repoName,
|
|
133
|
+
owner: process.env.GITHUB_USERNAME,
|
|
134
|
+
issueNumber: issueNumber,
|
|
135
|
+
body: 'Automated comment.'
|
|
136
|
+
}, ctx);
|
|
137
|
+
console.log(' β
Commented on GitHub Issue');
|
|
138
|
+
await tools.issues.handle({
|
|
139
|
+
action: 'close',
|
|
140
|
+
projectPath: tempDir,
|
|
141
|
+
provider: 'github',
|
|
142
|
+
repo: repoName,
|
|
143
|
+
owner: process.env.GITHUB_USERNAME,
|
|
144
|
+
issueNumber: issueNumber
|
|
145
|
+
}, ctx);
|
|
146
|
+
console.log(' β
Closed GitHub Issue');
|
|
147
|
+
}
|
|
148
|
+
catch (e) {
|
|
149
|
+
console.log(` β GitHub Issue Operations Failed: ${e.message}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// 8. Cleanup (Delete Remote Repos)
|
|
153
|
+
console.log('\n8οΈβ£ Cleanup (Deleting Remote Repos)...');
|
|
154
|
+
if (githubRepo) {
|
|
155
|
+
try {
|
|
156
|
+
await tools.workflow.handle({
|
|
157
|
+
action: 'delete',
|
|
158
|
+
projectPath: tempDir, // Required by schema
|
|
159
|
+
repo: repoName,
|
|
160
|
+
owner: process.env.GITHUB_USERNAME,
|
|
161
|
+
confirmDestructive: true
|
|
162
|
+
}, ctx);
|
|
163
|
+
console.log(' β
Deleted GitHub Repo');
|
|
164
|
+
}
|
|
165
|
+
catch (e) {
|
|
166
|
+
console.log(` β Delete GitHub Repo Failed: ${e.message}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (giteaRepo) {
|
|
170
|
+
try {
|
|
171
|
+
await tools.workflow.handle({
|
|
172
|
+
action: 'delete',
|
|
173
|
+
projectPath: tempDir, // Required by schema
|
|
174
|
+
repo: repoName,
|
|
175
|
+
owner: process.env.GITEA_USERNAME,
|
|
176
|
+
confirmDestructive: true
|
|
177
|
+
}, ctx);
|
|
178
|
+
console.log(' β
Deleted Gitea Repo');
|
|
179
|
+
}
|
|
180
|
+
catch (e) {
|
|
181
|
+
console.log(` β Delete Gitea Repo Failed: ${e.message}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Local Cleanup
|
|
185
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
186
|
+
console.log(' β
Deleted Local Temp Dir');
|
|
187
|
+
console.log('\nπ E2E Test Suite Completed!');
|
|
188
|
+
}
|
|
189
|
+
catch (error) {
|
|
190
|
+
console.error('\nβ E2E TEST FAILED FATALLY:', error);
|
|
191
|
+
// Try to cleanup even on failure
|
|
192
|
+
try {
|
|
193
|
+
if (fs.existsSync(tempDir))
|
|
194
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
195
|
+
}
|
|
196
|
+
catch { }
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
runE2ETest().catch(console.error);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import { ProviderManager } from '../providers/providerManager.js';
|
|
5
|
+
import { IsomorphicGitAdapter } from '../utils/gitAdapter.js';
|
|
6
|
+
// Import all tools
|
|
7
|
+
import { GitAnalyticsTool } from '../tools/gitAnalytics.js';
|
|
8
|
+
import { GitArchiveTool } from '../tools/gitArchive.js';
|
|
9
|
+
import { GitBackupTool } from '../tools/gitBackup.js';
|
|
10
|
+
import { GitBranchesTool } from '../tools/gitBranches.js';
|
|
11
|
+
import { GitChangelogTool } from '../tools/gitChangelog.js';
|
|
12
|
+
import { GitConfigTool } from '../tools/gitConfig.js';
|
|
13
|
+
import { GitFilesTool } from '../tools/gitFiles.js';
|
|
14
|
+
import { GitFixTool } from '../tools/gitFix.tool.js';
|
|
15
|
+
import { GitHistoryTool } from '../tools/gitHistory.js';
|
|
16
|
+
import { GitIgnoreTool } from '../tools/gitIgnore.js';
|
|
17
|
+
import { GitIssuesTool } from '../tools/gitIssues.js';
|
|
18
|
+
import { GitLogTool } from '../tools/gitLog.js';
|
|
19
|
+
import { GitMonitorTool } from '../tools/gitMonitor.js';
|
|
20
|
+
import { GitPackagesTool } from '../tools/gitPackages.js';
|
|
21
|
+
import { GitPullsTool } from '../tools/gitPulls.js';
|
|
22
|
+
import { GitPushTool } from '../tools/gitPush.js';
|
|
23
|
+
import { GitReleaseTool } from '../tools/gitRelease.js';
|
|
24
|
+
import { GitRemoteTool } from '../tools/gitRemote.js';
|
|
25
|
+
import { GitResetTool } from '../tools/gitReset.js';
|
|
26
|
+
import { GitStashTool } from '../tools/gitStash.js';
|
|
27
|
+
import { GitSyncTool } from '../tools/gitSync.js';
|
|
28
|
+
import { GitTagsTool } from '../tools/gitTags.js';
|
|
29
|
+
import { GitUpdateTool } from '../tools/gitUpdate.js';
|
|
30
|
+
import { GitUploadTool } from '../tools/gitUpload.js';
|
|
31
|
+
import { GitWorkflowTool } from '../tools/gitWorkflow.js';
|
|
32
|
+
// Set Credentials
|
|
33
|
+
process.env.GITEA_URL = "https://git.hubuzeli.com";
|
|
34
|
+
process.env.GITEA_TOKEN = "09b19262127241b2c608f1e58ac90af0cce21422";
|
|
35
|
+
process.env.GITEA_USERNAME = "andrebuzeli";
|
|
36
|
+
process.env.GITHUB_TOKEN = "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";
|
|
37
|
+
process.env.GITHUB_USERNAME = "Andre-Buzeli";
|
|
38
|
+
async function runExhaustiveTest() {
|
|
39
|
+
const timestamp = Date.now();
|
|
40
|
+
const repoName = `git-mcp-exhaustive-${timestamp}`;
|
|
41
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'git-mcp-exhaustive-'));
|
|
42
|
+
console.log(`π Starting EXHAUSTIVE Tools Test Suite`);
|
|
43
|
+
console.log(`π Local Temp Dir: ${tempDir}`);
|
|
44
|
+
console.log(`π¦ Target Repo Name: ${repoName}`);
|
|
45
|
+
const providerManager = new ProviderManager();
|
|
46
|
+
const gitAdapter = new IsomorphicGitAdapter(providerManager);
|
|
47
|
+
const ctx = { providerManager, gitAdapter };
|
|
48
|
+
// Instantiate all tools
|
|
49
|
+
const tools = {
|
|
50
|
+
analytics: new GitAnalyticsTool(),
|
|
51
|
+
archive: new GitArchiveTool(),
|
|
52
|
+
backup: new GitBackupTool(),
|
|
53
|
+
branches: new GitBranchesTool(),
|
|
54
|
+
changelog: new GitChangelogTool(),
|
|
55
|
+
config: new GitConfigTool(),
|
|
56
|
+
files: new GitFilesTool(),
|
|
57
|
+
fix: new GitFixTool(),
|
|
58
|
+
history: new GitHistoryTool(),
|
|
59
|
+
ignore: new GitIgnoreTool(),
|
|
60
|
+
issues: new GitIssuesTool(),
|
|
61
|
+
log: new GitLogTool(),
|
|
62
|
+
monitor: new GitMonitorTool(),
|
|
63
|
+
packages: new GitPackagesTool(),
|
|
64
|
+
pulls: new GitPullsTool(),
|
|
65
|
+
push: new GitPushTool(),
|
|
66
|
+
release: new GitReleaseTool(),
|
|
67
|
+
remote: new GitRemoteTool(),
|
|
68
|
+
reset: new GitResetTool(),
|
|
69
|
+
stash: new GitStashTool(),
|
|
70
|
+
sync: new GitSyncTool(),
|
|
71
|
+
tags: new GitTagsTool(),
|
|
72
|
+
update: new GitUpdateTool(),
|
|
73
|
+
upload: new GitUploadTool(),
|
|
74
|
+
workflow: new GitWorkflowTool(),
|
|
75
|
+
};
|
|
76
|
+
const results = {};
|
|
77
|
+
const detailedResults = [];
|
|
78
|
+
async function testAction(toolName, actionName, params, expectedSuccess = true) {
|
|
79
|
+
const testName = `${toolName}:${actionName}`;
|
|
80
|
+
console.log(`Testing ${testName}...`);
|
|
81
|
+
try {
|
|
82
|
+
// @ts-ignore
|
|
83
|
+
const result = await tools[toolName].handle(params, ctx);
|
|
84
|
+
if (expectedSuccess) {
|
|
85
|
+
console.log(` β
PASS`);
|
|
86
|
+
results[testName] = 'β
PASS';
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
console.log(` β FAIL (Expected Failure but Succeeded)`);
|
|
90
|
+
results[testName] = 'β FAIL (Unexpected Success)';
|
|
91
|
+
}
|
|
92
|
+
detailedResults.push({ test: testName, params, result, status: 'PASS' });
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
if (!expectedSuccess) {
|
|
97
|
+
console.log(` β
PASS (Expected Failure)`);
|
|
98
|
+
results[testName] = 'β
PASS (Expected Failure)';
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
console.error(` β FAIL: ${error.message}`);
|
|
102
|
+
results[testName] = 'β FAIL';
|
|
103
|
+
}
|
|
104
|
+
detailedResults.push({ test: testName, params, error: error.message, status: 'FAIL' });
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
// --- 1. GitWorkflow (Init & Create) ---
|
|
110
|
+
await testAction('workflow', 'init', { action: 'init', projectPath: tempDir });
|
|
111
|
+
await testAction('workflow', 'create', {
|
|
112
|
+
action: 'create',
|
|
113
|
+
projectPath: tempDir,
|
|
114
|
+
name: repoName,
|
|
115
|
+
description: 'Exhaustive Test Repo',
|
|
116
|
+
private: true
|
|
117
|
+
});
|
|
118
|
+
// --- 2. GitConfig ---
|
|
119
|
+
await testAction('config', 'set', { action: 'set', projectPath: tempDir, key: 'user.name', value: 'Test Bot' });
|
|
120
|
+
await testAction('config', 'set', { action: 'set', projectPath: tempDir, key: 'user.email', value: 'bot@test.com' });
|
|
121
|
+
await testAction('config', 'get', { action: 'get', projectPath: tempDir, key: 'user.name' });
|
|
122
|
+
await testAction('config', 'list', { action: 'list', projectPath: tempDir });
|
|
123
|
+
// unset is tricky with isomorphic-git adapter implementation details, skipping to avoid breaking config
|
|
124
|
+
// --- 3. GitFiles ---
|
|
125
|
+
await testAction('files', 'create', { action: 'create', projectPath: tempDir, filePath: 'README.md', content: '# Test Repo' });
|
|
126
|
+
await testAction('files', 'read', { action: 'read', projectPath: tempDir, filePath: 'README.md' });
|
|
127
|
+
await testAction('files', 'update', { action: 'update', projectPath: tempDir, filePath: 'README.md', content: '# Test Repo Updated' });
|
|
128
|
+
await testAction('files', 'list', { action: 'list', projectPath: tempDir });
|
|
129
|
+
await testAction('files', 'search', { action: 'search', projectPath: tempDir, searchText: 'Test' });
|
|
130
|
+
// --- 4. GitIgnore ---
|
|
131
|
+
await testAction('ignore', 'create', { action: 'create', projectPath: tempDir, template: 'node' });
|
|
132
|
+
await testAction('ignore', 'add', { action: 'add', projectPath: tempDir, patterns: ['*.log', 'temp/'] });
|
|
133
|
+
await testAction('ignore', 'read', { action: 'read', projectPath: tempDir });
|
|
134
|
+
await testAction('ignore', 'remove', { action: 'remove', projectPath: tempDir, patterns: ['*.log'] });
|
|
135
|
+
// Commit initial changes
|
|
136
|
+
await testAction('workflow', 'commit', { action: 'commit', projectPath: tempDir, message: 'Initial commit', files: ['.'] });
|
|
137
|
+
// --- 5. GitRemote ---
|
|
138
|
+
const githubUrl = `https://github.com/${process.env.GITHUB_USERNAME}/${repoName}.git`;
|
|
139
|
+
const giteaUrl = `${process.env.GITEA_URL}/${process.env.GITEA_USERNAME}/${repoName}.git`;
|
|
140
|
+
await testAction('remote', 'add', { action: 'add', projectPath: tempDir, remoteName: 'github', url: githubUrl });
|
|
141
|
+
await testAction('remote', 'add', { action: 'add', projectPath: tempDir, remoteName: 'gitea', url: giteaUrl });
|
|
142
|
+
await testAction('remote', 'list', { action: 'list', projectPath: tempDir });
|
|
143
|
+
await testAction('remote', 'get-url', { action: 'get-url', projectPath: tempDir, remoteName: 'github' });
|
|
144
|
+
// set-url and remove tested later or skipped to maintain state
|
|
145
|
+
// --- 6. GitPush ---
|
|
146
|
+
// Push to both
|
|
147
|
+
await testAction('push', 'push', { projectPath: tempDir, remote: 'github', branch: 'master' });
|
|
148
|
+
await testAction('push', 'push', { projectPath: tempDir, remote: 'gitea', branch: 'master' });
|
|
149
|
+
// --- 7. GitBranches ---
|
|
150
|
+
await testAction('branches', 'create', { action: 'create', projectPath: tempDir, branchName: 'feature/test' });
|
|
151
|
+
await testAction('branches', 'list', { action: 'list', projectPath: tempDir });
|
|
152
|
+
await testAction('branches', 'get', { action: 'get', projectPath: tempDir, branchName: 'feature/test' });
|
|
153
|
+
// Make changes in branch
|
|
154
|
+
await testAction('files', 'create', { action: 'create', projectPath: tempDir, filePath: 'feature.txt', content: 'Feature content' });
|
|
155
|
+
await testAction('workflow', 'commit', { action: 'commit', projectPath: tempDir, message: 'Feature commit', files: ['.'] });
|
|
156
|
+
// Compare (local)
|
|
157
|
+
await testAction('branches', 'compare', { action: 'compare', projectPath: tempDir, baseBranch: 'master', compareBranch: 'feature/test' });
|
|
158
|
+
// --- 8. GitLog ---
|
|
159
|
+
await testAction('log', 'log', { projectPath: tempDir, maxCount: 5 });
|
|
160
|
+
// --- 9. GitHistory ---
|
|
161
|
+
await testAction('history', 'track', { action: 'track', projectPath: tempDir, description: 'Feature implementation' });
|
|
162
|
+
await testAction('history', 'list', { action: 'list', projectPath: tempDir });
|
|
163
|
+
// sync and report
|
|
164
|
+
await testAction('history', 'sync', { action: 'sync', projectPath: tempDir });
|
|
165
|
+
await testAction('history', 'report', { action: 'report', projectPath: tempDir });
|
|
166
|
+
// --- 10. GitMonitor ---
|
|
167
|
+
await testAction('monitor', 'status', { action: 'status', projectPath: tempDir });
|
|
168
|
+
await testAction('monitor', 'log', { action: 'log', projectPath: tempDir });
|
|
169
|
+
await testAction('monitor', 'commits', { action: 'commits', projectPath: tempDir, branch: 'feature/test' });
|
|
170
|
+
await testAction('monitor', 'contributors', { action: 'contributors', projectPath: tempDir });
|
|
171
|
+
// --- 11. GitStash ---
|
|
172
|
+
await testAction('files', 'create', { action: 'create', projectPath: tempDir, filePath: 'stash.txt', content: 'Stash me' });
|
|
173
|
+
await testAction('stash', 'save', { action: 'save', projectPath: tempDir, message: 'Stash test', includeUntracked: true });
|
|
174
|
+
await testAction('stash', 'list', { action: 'list', projectPath: tempDir });
|
|
175
|
+
await testAction('stash', 'apply', { action: 'apply', projectPath: tempDir });
|
|
176
|
+
await testAction('stash', 'clear', { action: 'clear', projectPath: tempDir });
|
|
177
|
+
// --- 12. GitTags ---
|
|
178
|
+
await testAction('tags', 'create', { action: 'create', projectPath: tempDir, tagName: 'v1.0.0', message: 'Release 1.0.0', annotated: true });
|
|
179
|
+
await testAction('tags', 'list', { action: 'list', projectPath: tempDir });
|
|
180
|
+
await testAction('tags', 'get', { action: 'get', projectPath: tempDir, tagName: 'v1.0.0' });
|
|
181
|
+
await testAction('tags', 'push', { action: 'push', projectPath: tempDir, tagName: 'v1.0.0', remote: 'github' });
|
|
182
|
+
// --- 13. GitArchive ---
|
|
183
|
+
await testAction('archive', 'create', { action: 'create', projectPath: tempDir, outputPath: path.join(tempDir, 'archive.tar') });
|
|
184
|
+
// extract not tested to avoid cluttering temp dir too much, but logic is simple
|
|
185
|
+
// --- 14. GitBackup ---
|
|
186
|
+
await testAction('backup', 'create', { action: 'create', projectPath: tempDir });
|
|
187
|
+
await testAction('backup', 'list', { action: 'list', projectPath: tempDir });
|
|
188
|
+
// restore is manual info, delete is simple file deletion
|
|
189
|
+
// --- 15. GitIssues ---
|
|
190
|
+
const issue = await testAction('issues', 'create', {
|
|
191
|
+
action: 'create',
|
|
192
|
+
projectPath: tempDir,
|
|
193
|
+
title: 'Test Issue',
|
|
194
|
+
body: 'This is a test issue',
|
|
195
|
+
repo: repoName,
|
|
196
|
+
owner: process.env.GITHUB_USERNAME
|
|
197
|
+
});
|
|
198
|
+
if (issue && issue.providers.github && issue.providers.github.issue) {
|
|
199
|
+
const issueNum = issue.providers.github.issue.number;
|
|
200
|
+
await testAction('issues', 'list', { action: 'list', projectPath: tempDir, repo: repoName, owner: process.env.GITHUB_USERNAME });
|
|
201
|
+
await testAction('issues', 'get', { action: 'get', projectPath: tempDir, issueNumber: issueNum, repo: repoName, owner: process.env.GITHUB_USERNAME });
|
|
202
|
+
await testAction('issues', 'comment', { action: 'comment', projectPath: tempDir, issueNumber: issueNum, body: 'Test comment', repo: repoName, owner: process.env.GITHUB_USERNAME });
|
|
203
|
+
await testAction('issues', 'update', { action: 'update', projectPath: tempDir, issueNumber: issueNum, state: 'closed', repo: repoName, owner: process.env.GITHUB_USERNAME });
|
|
204
|
+
}
|
|
205
|
+
// --- 16. GitAnalytics ---
|
|
206
|
+
await testAction('analytics', 'stats', { action: 'stats', projectPath: tempDir, repo: repoName, owner: process.env.GITHUB_USERNAME });
|
|
207
|
+
await testAction('analytics', 'commits', { action: 'commits', projectPath: tempDir, repo: repoName, owner: process.env.GITHUB_USERNAME });
|
|
208
|
+
await testAction('analytics', 'contributors', { action: 'contributors', projectPath: tempDir, repo: repoName, owner: process.env.GITHUB_USERNAME });
|
|
209
|
+
// --- 17. GitRelease ---
|
|
210
|
+
// Let release:create handle tag creation
|
|
211
|
+
const release = await testAction('release', 'create', {
|
|
212
|
+
action: 'create',
|
|
213
|
+
projectPath: tempDir,
|
|
214
|
+
tagName: 'v2.0.0',
|
|
215
|
+
name: 'Release 2.0',
|
|
216
|
+
repo: repoName,
|
|
217
|
+
owner: process.env.GITHUB_USERNAME
|
|
218
|
+
});
|
|
219
|
+
if (release && release.providers.github && release.providers.github.release) {
|
|
220
|
+
await testAction('release', 'list', { action: 'list', projectPath: tempDir, repo: repoName, owner: process.env.GITHUB_USERNAME });
|
|
221
|
+
await testAction('release', 'get', { action: 'get', projectPath: tempDir, tagName: 'v2.0.0', repo: repoName, owner: process.env.GITHUB_USERNAME });
|
|
222
|
+
// update, delete, download skipped to save time/complexity but covered by create/list/get
|
|
223
|
+
}
|
|
224
|
+
// --- 18. GitPulls ---
|
|
225
|
+
// Create a PR (requires pushing the feature branch first)
|
|
226
|
+
await testAction('push', 'push', { projectPath: tempDir, remote: 'github', branch: 'feature/test' });
|
|
227
|
+
const pr = await testAction('pulls', 'create', {
|
|
228
|
+
action: 'create',
|
|
229
|
+
projectPath: tempDir,
|
|
230
|
+
title: 'Test PR',
|
|
231
|
+
head: 'feature/test',
|
|
232
|
+
base: 'master',
|
|
233
|
+
repo: repoName,
|
|
234
|
+
owner: process.env.GITHUB_USERNAME
|
|
235
|
+
});
|
|
236
|
+
console.log('PR Result:', JSON.stringify(pr, null, 2));
|
|
237
|
+
if (pr && pr.providers.github && pr.providers.github.pull) {
|
|
238
|
+
const prNum = pr.providers.github.pull.number;
|
|
239
|
+
console.log(`Extracted PR Number: ${prNum}`);
|
|
240
|
+
await testAction('pulls', 'list', { action: 'list', projectPath: tempDir, repo: repoName, owner: process.env.GITHUB_USERNAME });
|
|
241
|
+
await testAction('pulls', 'get', { action: 'get', projectPath: tempDir, pullNumber: prNum, repo: repoName, owner: process.env.GITHUB_USERNAME });
|
|
242
|
+
// merge, close, comment, review skipped to avoid messing up repo state too much
|
|
243
|
+
}
|
|
244
|
+
// --- 19. GitFix ---
|
|
245
|
+
await testAction('fix', 'fix', { projectPath: tempDir, autoDetect: true });
|
|
246
|
+
// --- 20. GitPackages ---
|
|
247
|
+
await testAction('files', 'create', { action: 'create', projectPath: tempDir, filePath: 'package.json', content: '{"name": "test", "version": "1.0.0"}' });
|
|
248
|
+
await testAction('packages', 'list', { action: 'list', projectPath: tempDir, packageType: 'npm' });
|
|
249
|
+
// --- 21. GitChangelog ---
|
|
250
|
+
await testAction('changelog', 'generate', { projectPath: tempDir });
|
|
251
|
+
// --- 22. GitReset ---
|
|
252
|
+
await testAction('reset', 'soft', { action: 'soft', projectPath: tempDir, ref: 'HEAD~1' });
|
|
253
|
+
// --- 23. GitSync ---
|
|
254
|
+
await testAction('sync', 'status', { action: 'status', projectPath: tempDir });
|
|
255
|
+
// fetch, pull, push, sync, one-shot covered implicitly or explicitly above
|
|
256
|
+
// --- 24. GitUpdate ---
|
|
257
|
+
// This is a composite tool
|
|
258
|
+
await testAction('update', 'update', { projectPath: tempDir, message: 'Update via tool' });
|
|
259
|
+
// --- 25. GitUpload ---
|
|
260
|
+
// This is a composite tool - usually for initial upload, but can run on existing
|
|
261
|
+
await testAction('upload', 'upload', { projectPath: tempDir, repoName: repoName });
|
|
262
|
+
// --- Cleanup ---
|
|
263
|
+
console.log('\nCleaning up remote repositories...');
|
|
264
|
+
await testAction('workflow', 'delete', { action: 'delete', projectPath: tempDir, repo: repoName, owner: process.env.GITHUB_USERNAME, confirmDestructive: true });
|
|
265
|
+
await testAction('workflow', 'delete', { action: 'delete', projectPath: tempDir, repo: repoName, owner: process.env.GITEA_USERNAME, confirmDestructive: true });
|
|
266
|
+
// Local Cleanup
|
|
267
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
console.error('\nβ FATAL ERROR:', error);
|
|
271
|
+
}
|
|
272
|
+
console.log('\nπ EXHAUSTIVE TEST RESULTS SUMMARY:');
|
|
273
|
+
console.table(results);
|
|
274
|
+
}
|
|
275
|
+
runExhaustiveTest().catch(console.error);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -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
|
|
44
|
-
lastCommit: string |
|
|
43
|
+
branch: string;
|
|
44
|
+
lastCommit: string | null;
|
|
45
45
|
files: number;
|
|
46
46
|
};
|
|
47
47
|
message: string;
|
package/dist/tools/gitBackup.js
CHANGED
|
@@ -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 =
|
|
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.
|
|
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));
|