@andrebuzeli/git-mcp 6.2.3 → 6.3.1
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/dist/index.js +4 -2
- package/dist/tools/gitFix.d.ts +3 -0
- package/dist/tools/gitFix.js +169 -0
- package/dist/tools/gitFix.tool.d.ts +8 -0
- package/dist/tools/gitFix.tool.js +73 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -29,6 +29,7 @@ const gitAnalytics_1 = require("./tools/gitAnalytics");
|
|
|
29
29
|
const gitUpload_1 = require("./tools/gitUpload");
|
|
30
30
|
const gitUpdate_1 = require("./tools/gitUpdate");
|
|
31
31
|
const gitHistory_1 = require("./tools/gitHistory");
|
|
32
|
+
const gitFix_tool_1 = require("./tools/gitFix.tool");
|
|
32
33
|
const toolsGuide_1 = __importDefault(require("./resources/toolsGuide"));
|
|
33
34
|
async function main() {
|
|
34
35
|
// Load optional mcp.json configuration (will populate process.env if values present)
|
|
@@ -40,7 +41,7 @@ async function main() {
|
|
|
40
41
|
if (process.env.DEBUG) {
|
|
41
42
|
console.error('Provider validation:', JSON.stringify(validation, null, 2));
|
|
42
43
|
}
|
|
43
|
-
// Register all
|
|
44
|
+
// Register all 21 Git tools
|
|
44
45
|
const tools = [
|
|
45
46
|
new gitWorkflow_1.GitWorkflowTool(),
|
|
46
47
|
new gitFiles_1.GitFilesTool(),
|
|
@@ -62,6 +63,7 @@ async function main() {
|
|
|
62
63
|
new gitUpload_1.GitUploadTool(),
|
|
63
64
|
new gitUpdate_1.GitUpdateTool(),
|
|
64
65
|
new gitHistory_1.GitHistoryTool(),
|
|
66
|
+
new gitFix_tool_1.GitFixTool(),
|
|
65
67
|
];
|
|
66
68
|
// Register resources
|
|
67
69
|
const resources = [
|
|
@@ -75,7 +77,7 @@ async function main() {
|
|
|
75
77
|
// Create MCP Server with STDIO transport
|
|
76
78
|
const server = new index_js_1.Server({
|
|
77
79
|
name: '@andrebuzeli/git-mcp',
|
|
78
|
-
version: '6.
|
|
80
|
+
version: '6.3.1',
|
|
79
81
|
});
|
|
80
82
|
// Register tool list handler
|
|
81
83
|
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => {
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.handleGitFix = handleGitFix;
|
|
7
|
+
const simple_git_1 = __importDefault(require("simple-git"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
10
|
+
const fs_1 = require("fs");
|
|
11
|
+
async function handleGitFix(args) {
|
|
12
|
+
try {
|
|
13
|
+
const { projectPath, githubRepo, giteaRepo, autoDetect = true } = args;
|
|
14
|
+
if (!projectPath) {
|
|
15
|
+
throw new Error('projectPath é obrigatório');
|
|
16
|
+
}
|
|
17
|
+
const absolutePath = path_1.default.resolve(projectPath);
|
|
18
|
+
if (!(0, fs_1.existsSync)(absolutePath)) {
|
|
19
|
+
throw new Error(`Caminho não existe: ${absolutePath}`);
|
|
20
|
+
}
|
|
21
|
+
const git = (0, simple_git_1.default)(absolutePath);
|
|
22
|
+
const result = {
|
|
23
|
+
success: false,
|
|
24
|
+
projectPath: absolutePath,
|
|
25
|
+
fixed: [],
|
|
26
|
+
warnings: [],
|
|
27
|
+
errors: [],
|
|
28
|
+
remotes: {
|
|
29
|
+
before: [],
|
|
30
|
+
after: []
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
// Verificar se é um repositório Git
|
|
34
|
+
let isGitRepo = false;
|
|
35
|
+
try {
|
|
36
|
+
await git.status();
|
|
37
|
+
isGitRepo = true;
|
|
38
|
+
result.fixed.push('✅ Repositório Git válido encontrado');
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
if (err.message.includes('not a git repository')) {
|
|
42
|
+
result.warnings.push('⚠️ Não é um repositório Git - inicializando...');
|
|
43
|
+
await git.init();
|
|
44
|
+
result.fixed.push('✅ Git inicializado');
|
|
45
|
+
isGitRepo = true;
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
throw err;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Capturar remotes antes
|
|
52
|
+
const remotesBefore = await git.getRemotes(true);
|
|
53
|
+
result.remotes.before = remotesBefore.map(r => ({ name: r.name, url: r.refs.fetch || '' }));
|
|
54
|
+
// Obter usernames das env vars (OBRIGATÓRIO usar as credenciais fornecidas)
|
|
55
|
+
const githubUsername = process.env.GITHUB_USERNAME;
|
|
56
|
+
const giteaUsername = process.env.GITEA_USERNAME;
|
|
57
|
+
if (!githubUsername || !giteaUsername) {
|
|
58
|
+
throw new Error('GITHUB_USERNAME e GITEA_USERNAME são obrigatórios nas env vars');
|
|
59
|
+
}
|
|
60
|
+
// Auto-detectar repos se solicitado
|
|
61
|
+
let finalGithubRepo = githubRepo;
|
|
62
|
+
let finalGiteaRepo = giteaRepo;
|
|
63
|
+
let detectedRepoName = null;
|
|
64
|
+
if (autoDetect && remotesBefore.length > 0) {
|
|
65
|
+
for (const remote of remotesBefore) {
|
|
66
|
+
const url = remote.refs.fetch || '';
|
|
67
|
+
// Detectar APENAS o nome do repo (sem username)
|
|
68
|
+
if (url.includes('github.com') && !detectedRepoName) {
|
|
69
|
+
const match = url.match(/github\.com[:/][^/]+\/([^/.]+)/);
|
|
70
|
+
if (match) {
|
|
71
|
+
detectedRepoName = match[1];
|
|
72
|
+
result.fixed.push(`🔍 Nome do repo auto-detectado: ${detectedRepoName}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Detectar de Gitea
|
|
76
|
+
if ((url.includes('nas-ubuntu') || url.includes('gitea')) && !detectedRepoName) {
|
|
77
|
+
const match = url.match(/\/[^/]+\/([^/.]+)(?:\.git)?$/);
|
|
78
|
+
if (match) {
|
|
79
|
+
detectedRepoName = match[1];
|
|
80
|
+
result.fixed.push(`🔍 Nome do repo auto-detectado: ${detectedRepoName}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Construir repos usando os usernames CORRETOS das env vars
|
|
86
|
+
if (detectedRepoName) {
|
|
87
|
+
finalGithubRepo = finalGithubRepo || `${githubUsername}/${detectedRepoName}`;
|
|
88
|
+
finalGiteaRepo = finalGiteaRepo || `${giteaUsername}/${detectedRepoName}`;
|
|
89
|
+
result.fixed.push(`✅ GitHub repo: ${finalGithubRepo}`);
|
|
90
|
+
result.fixed.push(`✅ Gitea repo: ${finalGiteaRepo}`);
|
|
91
|
+
}
|
|
92
|
+
// Se não detectou repos, usar o nome da pasta
|
|
93
|
+
if (!finalGithubRepo || !finalGiteaRepo) {
|
|
94
|
+
const folderName = path_1.default.basename(absolutePath);
|
|
95
|
+
if (!finalGithubRepo) {
|
|
96
|
+
finalGithubRepo = `${githubUsername}/${folderName}`;
|
|
97
|
+
}
|
|
98
|
+
if (!finalGiteaRepo) {
|
|
99
|
+
finalGiteaRepo = `${giteaUsername}/${folderName}`;
|
|
100
|
+
}
|
|
101
|
+
result.warnings.push(`⚠️ Repo não detectado - usando nome da pasta: ${folderName}`);
|
|
102
|
+
result.fixed.push(`✅ GitHub repo: ${finalGithubRepo}`);
|
|
103
|
+
result.fixed.push(`✅ Gitea repo: ${finalGiteaRepo}`);
|
|
104
|
+
}
|
|
105
|
+
// Remover remotes antigos
|
|
106
|
+
const remotesToRemove = ['origin', 'github', 'gitea'];
|
|
107
|
+
for (const remoteName of remotesToRemove) {
|
|
108
|
+
const exists = remotesBefore.find(r => r.name === remoteName);
|
|
109
|
+
if (exists) {
|
|
110
|
+
await git.removeRemote(remoteName);
|
|
111
|
+
result.fixed.push(`🗑️ Removido remote antigo: ${remoteName}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Adicionar novos remotes no padrão dual
|
|
115
|
+
const giteaUrl = process.env.GITEA_URL || 'http://nas-ubuntu:9999';
|
|
116
|
+
const githubUrl = `https://github.com/${finalGithubRepo}.git`;
|
|
117
|
+
const giteaRepoUrl = `${giteaUrl}/${finalGiteaRepo}.git`;
|
|
118
|
+
await git.addRemote('github', githubUrl);
|
|
119
|
+
result.fixed.push(`✅ Adicionado remote GitHub: ${githubUrl}`);
|
|
120
|
+
await git.addRemote('gitea', giteaRepoUrl);
|
|
121
|
+
result.fixed.push(`✅ Adicionado remote Gitea: ${giteaRepoUrl}`);
|
|
122
|
+
// Configurar origin como push múltiplo
|
|
123
|
+
await git.addRemote('origin', githubUrl);
|
|
124
|
+
await git.addConfig('remote.origin.pushurl', giteaRepoUrl, false, 'local');
|
|
125
|
+
result.fixed.push(`✅ Configurado origin para push dual (GitHub + Gitea)`);
|
|
126
|
+
// Capturar remotes depois
|
|
127
|
+
const remotesAfter = await git.getRemotes(true);
|
|
128
|
+
result.remotes.after = remotesAfter.map(r => ({ name: r.name, url: r.refs.fetch || '' }));
|
|
129
|
+
// Verificar se há commits
|
|
130
|
+
try {
|
|
131
|
+
await git.log();
|
|
132
|
+
result.fixed.push('✅ Histórico de commits preservado');
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
result.warnings.push('⚠️ Sem commits ainda - faça o primeiro commit');
|
|
136
|
+
}
|
|
137
|
+
// Criar/atualizar .gitignore se necessário
|
|
138
|
+
const gitignorePath = path_1.default.join(absolutePath, '.gitignore');
|
|
139
|
+
if (!(0, fs_1.existsSync)(gitignorePath)) {
|
|
140
|
+
const defaultGitignore = `node_modules/
|
|
141
|
+
dist/
|
|
142
|
+
.env
|
|
143
|
+
*.log
|
|
144
|
+
.DS_Store
|
|
145
|
+
`;
|
|
146
|
+
await promises_1.default.writeFile(gitignorePath, defaultGitignore, 'utf-8');
|
|
147
|
+
result.fixed.push('✅ Criado .gitignore padrão');
|
|
148
|
+
}
|
|
149
|
+
result.success = true;
|
|
150
|
+
return {
|
|
151
|
+
content: [{
|
|
152
|
+
type: 'text',
|
|
153
|
+
text: JSON.stringify(result, null, 2)
|
|
154
|
+
}]
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
return {
|
|
159
|
+
content: [{
|
|
160
|
+
type: 'text',
|
|
161
|
+
text: JSON.stringify({
|
|
162
|
+
success: false,
|
|
163
|
+
error: error.message,
|
|
164
|
+
stack: error.stack
|
|
165
|
+
}, null, 2)
|
|
166
|
+
}]
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GitFixTool = void 0;
|
|
4
|
+
const gitFix_1 = require("./gitFix");
|
|
5
|
+
class GitFixTool {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.name = 'git-fix';
|
|
8
|
+
this.description = `Fix local Git repository to work with dual-provider system (GitHub + Gitea).
|
|
9
|
+
|
|
10
|
+
🔧 WHAT IT DOES:
|
|
11
|
+
- Converts existing Git repos to dual-provider system
|
|
12
|
+
- Auto-detects GitHub/Gitea remotes from existing configuration
|
|
13
|
+
- Removes old remotes and configures new dual-push system
|
|
14
|
+
- Initializes Git if not already a repository
|
|
15
|
+
- Creates standard .gitignore if missing
|
|
16
|
+
|
|
17
|
+
📋 PARAMETERS:
|
|
18
|
+
- projectPath (required): Absolute path to the Git repository
|
|
19
|
+
- githubRepo (optional): GitHub repo in format "owner/repo" (auto-detected if not provided)
|
|
20
|
+
- giteaRepo (optional): Gitea repo in format "owner/repo" (auto-detected if not provided)
|
|
21
|
+
- autoDetect (optional): Auto-detect repos from existing remotes (default: true)
|
|
22
|
+
|
|
23
|
+
🎯 WHEN TO USE:
|
|
24
|
+
- You have an existing Git repo not configured for dual-provider
|
|
25
|
+
- Need to migrate from single remote to dual-push system
|
|
26
|
+
- Want to standardize repo configuration for git-mcp tools
|
|
27
|
+
|
|
28
|
+
✅ WHAT GETS FIXED:
|
|
29
|
+
1. Verifies/initializes Git repository
|
|
30
|
+
2. Captures current remotes (before)
|
|
31
|
+
3. Auto-detects GitHub/Gitea repos from URLs
|
|
32
|
+
4. Removes old remotes (origin, github, gitea)
|
|
33
|
+
5. Adds new 'github' remote
|
|
34
|
+
6. Adds new 'gitea' remote
|
|
35
|
+
7. Configures 'origin' for dual-push (GitHub + Gitea)
|
|
36
|
+
8. Creates .gitignore if missing
|
|
37
|
+
9. Shows before/after comparison
|
|
38
|
+
|
|
39
|
+
📊 RESULT STRUCTURE:
|
|
40
|
+
{
|
|
41
|
+
success: boolean,
|
|
42
|
+
projectPath: string,
|
|
43
|
+
fixed: string[], // Actions performed
|
|
44
|
+
warnings: string[], // Non-critical issues
|
|
45
|
+
errors: string[], // Critical issues
|
|
46
|
+
remotes: {
|
|
47
|
+
before: [...], // Remotes before fix
|
|
48
|
+
after: [...] // Remotes after fix
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
💡 EXAMPLE:
|
|
53
|
+
{
|
|
54
|
+
"projectPath": "/path/to/my-project",
|
|
55
|
+
"autoDetect": true
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
🔍 AUTO-DETECTION:
|
|
59
|
+
- Scans existing remotes for github.com URLs → extracts owner/repo
|
|
60
|
+
- Scans existing remotes for Gitea URLs → extracts owner/repo
|
|
61
|
+
- Falls back to folder name if no remotes found
|
|
62
|
+
|
|
63
|
+
⚠️ REQUIREMENTS:
|
|
64
|
+
- GITHUB_USERNAME and GITEA_USERNAME env vars for fallback
|
|
65
|
+
- GITEA_URL env var (default: http://nas-ubuntu:9999)
|
|
66
|
+
- Git must be installed and accessible
|
|
67
|
+
`;
|
|
68
|
+
}
|
|
69
|
+
async handle(args) {
|
|
70
|
+
return (0, gitFix_1.handleGitFix)(args);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.GitFixTool = GitFixTool;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andrebuzeli/git-mcp",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.3.1",
|
|
4
4
|
"description": "Professional MCP server for Git operations - STDIO UNIVERSAL: works in ANY IDE (Cursor, VSCode, Claude Desktop). Fully autonomous with intelligent error handling. Auto-detects branches, owner, repo. User-friendly error messages. Dual-provider execution (GitHub + Gitea)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|