@andre.buzeli/git-mcp 15.12.5
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/README.md +40 -0
- package/package.json +29 -0
- package/src/index.js +147 -0
- package/src/prompts/index.js +870 -0
- package/src/providers/providerManager.js +317 -0
- package/src/resources/index.js +276 -0
- package/src/tools/git-branches.js +126 -0
- package/src/tools/git-clone.js +137 -0
- package/src/tools/git-config.js +94 -0
- package/src/tools/git-diff.js +137 -0
- package/src/tools/git-files.js +82 -0
- package/src/tools/git-help.js +284 -0
- package/src/tools/git-history.js +90 -0
- package/src/tools/git-ignore.js +98 -0
- package/src/tools/git-issues.js +101 -0
- package/src/tools/git-merge.js +152 -0
- package/src/tools/git-pulls.js +115 -0
- package/src/tools/git-remote.js +492 -0
- package/src/tools/git-reset.js +105 -0
- package/src/tools/git-stash.js +120 -0
- package/src/tools/git-sync.js +129 -0
- package/src/tools/git-tags.js +113 -0
- package/src/tools/git-workflow.js +443 -0
- package/src/utils/env.js +104 -0
- package/src/utils/errors.js +431 -0
- package/src/utils/gitAdapter.js +996 -0
- package/src/utils/hooks.js +255 -0
- package/src/utils/metrics.js +198 -0
- package/src/utils/providerExec.js +61 -0
- package/src/utils/repoHelpers.js +216 -0
- package/src/utils/retry.js +123 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { Octokit } from "@octokit/rest";
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
import { getProvidersEnv } from "../utils/repoHelpers.js";
|
|
4
|
+
|
|
5
|
+
export class ProviderManager {
|
|
6
|
+
constructor() {
|
|
7
|
+
const { githubToken, giteaUrl, giteaToken } = getProvidersEnv();
|
|
8
|
+
this.githubToken = githubToken || "";
|
|
9
|
+
this.giteaUrl = giteaUrl || "";
|
|
10
|
+
this.giteaToken = giteaToken || "";
|
|
11
|
+
this.github = this.githubToken ? new Octokit({ auth: this.githubToken }) : null;
|
|
12
|
+
this._githubOwner = "";
|
|
13
|
+
this._giteaOwner = "";
|
|
14
|
+
this._ownerFetchedAt = 0;
|
|
15
|
+
this._remoteUrlsCache = new Map(); // Cache para URLs de remotes
|
|
16
|
+
this._cacheExpiry = 10 * 60 * 1000; // 10 minutos
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async getGitHubOwner() {
|
|
20
|
+
if (!this.github) return "";
|
|
21
|
+
const now = Date.now();
|
|
22
|
+
if (this._githubOwner && now - this._ownerFetchedAt < 5 * 60 * 1000) return this._githubOwner;
|
|
23
|
+
try {
|
|
24
|
+
const me = await this.github.rest.users.getAuthenticated();
|
|
25
|
+
this._githubOwner = me.data.login || "";
|
|
26
|
+
this._ownerFetchedAt = now;
|
|
27
|
+
return this._githubOwner;
|
|
28
|
+
} catch {
|
|
29
|
+
return "";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async getGiteaOwner() {
|
|
34
|
+
if (!this.giteaUrl || !this.giteaToken) return "";
|
|
35
|
+
const now = Date.now();
|
|
36
|
+
if (this._giteaOwner && now - this._ownerFetchedAt < 5 * 60 * 1000) return this._giteaOwner;
|
|
37
|
+
try {
|
|
38
|
+
const r = await axios.get(`${this.giteaUrl}/api/v1/user`, {
|
|
39
|
+
headers: { Authorization: `token ${this.giteaToken}` },
|
|
40
|
+
timeout: 8000,
|
|
41
|
+
});
|
|
42
|
+
const d = r.data || {};
|
|
43
|
+
this._giteaOwner = d.login || d.username || "";
|
|
44
|
+
this._ownerFetchedAt = now;
|
|
45
|
+
return this._giteaOwner;
|
|
46
|
+
} catch {
|
|
47
|
+
return "";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async getPrioritizedUser() {
|
|
52
|
+
// 1. Gitea
|
|
53
|
+
if (this.giteaUrl && this.giteaToken) {
|
|
54
|
+
try {
|
|
55
|
+
const r = await axios.get(`${this.giteaUrl}/api/v1/user`, {
|
|
56
|
+
headers: { Authorization: `token ${this.giteaToken}` },
|
|
57
|
+
timeout: 5000
|
|
58
|
+
});
|
|
59
|
+
if (r.data) {
|
|
60
|
+
return {
|
|
61
|
+
username: r.data.login || r.data.username,
|
|
62
|
+
email: r.data.email,
|
|
63
|
+
provider: 'gitea'
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
} catch (e) { /* ignore */ }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 2. GitHub
|
|
70
|
+
if (this.github) {
|
|
71
|
+
try {
|
|
72
|
+
const me = await this.github.rest.users.getAuthenticated();
|
|
73
|
+
let email = me.data.email;
|
|
74
|
+
|
|
75
|
+
// Se email for null (privado), tenta buscar lista de emails
|
|
76
|
+
if (!email) {
|
|
77
|
+
try {
|
|
78
|
+
const emailsLog = await this.github.rest.users.listEmailsForAuthenticatedUser();
|
|
79
|
+
const primary = emailsLog.data.find(e => e.primary) || emailsLog.data[0];
|
|
80
|
+
if (primary) email = primary.email;
|
|
81
|
+
} catch { /* ignore scope errors */ }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
username: me.data.login,
|
|
86
|
+
email,
|
|
87
|
+
provider: 'github'
|
|
88
|
+
};
|
|
89
|
+
} catch (e) { /* ignore */ }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async getRemoteUrls(repoName, organization) {
|
|
96
|
+
const cacheKey = `urls_${organization || ""}_${repoName}`;
|
|
97
|
+
const now = Date.now();
|
|
98
|
+
|
|
99
|
+
// Verificar cache
|
|
100
|
+
if (this._remoteUrlsCache.has(cacheKey)) {
|
|
101
|
+
const cached = this._remoteUrlsCache.get(cacheKey);
|
|
102
|
+
if (now - cached.timestamp < this._cacheExpiry) {
|
|
103
|
+
return cached.urls;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const urls = {};
|
|
108
|
+
|
|
109
|
+
// GitHub URL
|
|
110
|
+
if (this.github) {
|
|
111
|
+
const owner = organization || await this.getGitHubOwner();
|
|
112
|
+
if (owner) {
|
|
113
|
+
urls.github = `https://github.com/${owner}/${repoName}.git`;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Gitea URL
|
|
118
|
+
if (this.giteaUrl && this.giteaToken) {
|
|
119
|
+
const owner = organization || await this.getGiteaOwner();
|
|
120
|
+
if (owner) {
|
|
121
|
+
const base = this.giteaUrl.replace(/\/$/, "");
|
|
122
|
+
urls.gitea = `${base}/${owner}/${repoName}.git`;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Cachear resultado
|
|
127
|
+
this._remoteUrlsCache.set(cacheKey, { urls, timestamp: now });
|
|
128
|
+
|
|
129
|
+
return urls;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async ensureRepos({ repoName, createIfMissing = true, description = "Managed by git-mcpv2", isPublic = false, organization }) {
|
|
133
|
+
// Por padrão, repositórios são PRIVADOS. Use isPublic=true para público.
|
|
134
|
+
const isPrivate = !isPublic;
|
|
135
|
+
const results = { github: null, gitea: null };
|
|
136
|
+
// GitHub
|
|
137
|
+
if (this.github) {
|
|
138
|
+
const owner = organization || await this.getGitHubOwner();
|
|
139
|
+
if (owner) {
|
|
140
|
+
try {
|
|
141
|
+
const full = `${owner}/${repoName}`;
|
|
142
|
+
try {
|
|
143
|
+
await this.github.rest.repos.get({ owner, repo: repoName });
|
|
144
|
+
results.github = { ok: true, repo: full, created: false };
|
|
145
|
+
} catch {
|
|
146
|
+
if (createIfMissing) {
|
|
147
|
+
let cr;
|
|
148
|
+
if (organization) {
|
|
149
|
+
// Criar repo na organização
|
|
150
|
+
cr = await this.github.rest.repos.createInOrg({
|
|
151
|
+
org: organization,
|
|
152
|
+
name: repoName,
|
|
153
|
+
description,
|
|
154
|
+
private: isPrivate,
|
|
155
|
+
auto_init: false,
|
|
156
|
+
});
|
|
157
|
+
} else {
|
|
158
|
+
// Criar repo no usuário pessoal
|
|
159
|
+
cr = await this.github.rest.repos.createForAuthenticatedUser({
|
|
160
|
+
name: repoName,
|
|
161
|
+
description,
|
|
162
|
+
private: isPrivate,
|
|
163
|
+
auto_init: false,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
results.github = { ok: true, repo: cr.data.full_name, created: true };
|
|
167
|
+
} else {
|
|
168
|
+
results.github = { ok: false, error: "missing" };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
} catch (e) {
|
|
172
|
+
results.github = { ok: false, error: String(e?.message || e) };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Gitea
|
|
177
|
+
if (this.giteaUrl && this.giteaToken) {
|
|
178
|
+
const owner = organization || await this.getGiteaOwner();
|
|
179
|
+
if (owner) {
|
|
180
|
+
try {
|
|
181
|
+
const base = this.giteaUrl.replace(/\/$/, "");
|
|
182
|
+
const getRepo = await axios.get(`${base}/api/v1/repos/${owner}/${repoName}`,
|
|
183
|
+
{ headers: { Authorization: `token ${this.giteaToken}` }, timeout: 8000 });
|
|
184
|
+
if (getRepo.status === 200) {
|
|
185
|
+
results.gitea = { ok: true, repo: `${owner}/${repoName}`, created: false };
|
|
186
|
+
}
|
|
187
|
+
} catch (e) {
|
|
188
|
+
if (createIfMissing) {
|
|
189
|
+
try {
|
|
190
|
+
const createUrl = organization
|
|
191
|
+
? `${this.giteaUrl}/api/v1/orgs/${organization}/repos`
|
|
192
|
+
: `${this.giteaUrl}/api/v1/user/repos`;
|
|
193
|
+
const cr = await axios.post(createUrl,
|
|
194
|
+
{ name: repoName, description, private: isPrivate, auto_init: false },
|
|
195
|
+
{ headers: { Authorization: `token ${this.giteaToken}` }, timeout: 8000 });
|
|
196
|
+
results.gitea = { ok: true, repo: `${cr.data?.owner?.login || owner}/${repoName}`, created: true };
|
|
197
|
+
} catch (err) {
|
|
198
|
+
results.gitea = { ok: false, error: String(err?.message || err) };
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
results.gitea = { ok: false, error: "missing" };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return results;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async listAllRepos(options = {}) {
|
|
210
|
+
const { maxPages = 10, perPage = 100, organization } = options;
|
|
211
|
+
const results = { github: [], gitea: [] };
|
|
212
|
+
|
|
213
|
+
// GitHub - com paginação
|
|
214
|
+
if (this.github) {
|
|
215
|
+
try {
|
|
216
|
+
let page = 1;
|
|
217
|
+
let hasMore = true;
|
|
218
|
+
while (hasMore && page <= maxPages) {
|
|
219
|
+
let data;
|
|
220
|
+
if (organization) {
|
|
221
|
+
// Listar repos da organização
|
|
222
|
+
const resp = await this.github.rest.repos.listForOrg({
|
|
223
|
+
org: organization,
|
|
224
|
+
per_page: perPage,
|
|
225
|
+
page,
|
|
226
|
+
type: "all"
|
|
227
|
+
});
|
|
228
|
+
data = resp.data;
|
|
229
|
+
} else {
|
|
230
|
+
// Listar repos do usuário
|
|
231
|
+
const resp = await this.github.rest.repos.listForAuthenticatedUser({
|
|
232
|
+
per_page: perPage,
|
|
233
|
+
page,
|
|
234
|
+
visibility: "all"
|
|
235
|
+
});
|
|
236
|
+
data = resp.data;
|
|
237
|
+
}
|
|
238
|
+
results.github.push(...data.map(r => ({
|
|
239
|
+
name: r.name,
|
|
240
|
+
fullName: r.full_name,
|
|
241
|
+
url: r.html_url,
|
|
242
|
+
isPrivate: r.private
|
|
243
|
+
})));
|
|
244
|
+
hasMore = data.length === perPage;
|
|
245
|
+
page++;
|
|
246
|
+
}
|
|
247
|
+
} catch (e) {
|
|
248
|
+
console.error("GitHub List Error:", e.message);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Gitea - com paginação
|
|
253
|
+
if (this.giteaUrl && this.giteaToken) {
|
|
254
|
+
try {
|
|
255
|
+
let page = 1;
|
|
256
|
+
let hasMore = true;
|
|
257
|
+
const base = this.giteaUrl.replace(/\/$/, "");
|
|
258
|
+
while (hasMore && page <= maxPages) {
|
|
259
|
+
const endpoint = organization
|
|
260
|
+
? `${base}/api/v1/orgs/${organization}/repos?limit=${perPage}&page=${page}`
|
|
261
|
+
: `${base}/api/v1/user/repos?limit=${perPage}&page=${page}`;
|
|
262
|
+
const { data } = await axios.get(endpoint, {
|
|
263
|
+
headers: { Authorization: `token ${this.giteaToken}` }
|
|
264
|
+
});
|
|
265
|
+
results.gitea.push(...data.map(r => ({
|
|
266
|
+
name: r.name,
|
|
267
|
+
fullName: r.full_name,
|
|
268
|
+
url: r.html_url,
|
|
269
|
+
isPrivate: r.private
|
|
270
|
+
})));
|
|
271
|
+
hasMore = data.length === perPage;
|
|
272
|
+
page++;
|
|
273
|
+
}
|
|
274
|
+
} catch (e) {
|
|
275
|
+
console.error("Gitea List Error:", e.message);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return results;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async listOrgs() {
|
|
282
|
+
const results = { github: [], gitea: [] };
|
|
283
|
+
|
|
284
|
+
// GitHub
|
|
285
|
+
if (this.github) {
|
|
286
|
+
try {
|
|
287
|
+
const { data } = await this.github.rest.orgs.listForAuthenticatedUser({ per_page: 100 });
|
|
288
|
+
results.github = data.map(o => ({
|
|
289
|
+
name: o.login,
|
|
290
|
+
description: o.description || "",
|
|
291
|
+
url: o.html_url
|
|
292
|
+
}));
|
|
293
|
+
} catch (e) {
|
|
294
|
+
console.error("GitHub ListOrgs Error:", e.message);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Gitea
|
|
299
|
+
if (this.giteaUrl && this.giteaToken) {
|
|
300
|
+
try {
|
|
301
|
+
const base = this.giteaUrl.replace(/\/$/, "");
|
|
302
|
+
const { data } = await axios.get(`${base}/api/v1/user/orgs`, {
|
|
303
|
+
headers: { Authorization: `token ${this.giteaToken}` }
|
|
304
|
+
});
|
|
305
|
+
results.gitea = (data || []).map(o => ({
|
|
306
|
+
name: o.username || o.name,
|
|
307
|
+
description: o.description || "",
|
|
308
|
+
url: o.html_url || `${base}/${o.username || o.name}`
|
|
309
|
+
}));
|
|
310
|
+
} catch (e) {
|
|
311
|
+
console.error("Gitea ListOrgs Error:", e.message);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return results;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
// Resource único com documentação completa do git-mcp
|
|
2
|
+
|
|
3
|
+
const TOOLS_GUIDE = `# git-mcp - Guia Completo
|
|
4
|
+
|
|
5
|
+
## ⭐ Fluxo Recomendado (Automatizado)
|
|
6
|
+
\`\`\`
|
|
7
|
+
git-workflow action="update" message="sua mensagem" → Executa status, add, commit e push automaticamente
|
|
8
|
+
\`\`\`
|
|
9
|
+
|
|
10
|
+
## Fluxo Manual (se preferir controle individual)
|
|
11
|
+
\`\`\`
|
|
12
|
+
1. git-workflow status → ver arquivos modificados
|
|
13
|
+
2. git-workflow add files=["."] → adicionar ao staging
|
|
14
|
+
3. git-workflow commit message="msg" → criar commit
|
|
15
|
+
4. git-workflow push → enviar para GitHub e Gitea
|
|
16
|
+
\`\`\`
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## git-workflow
|
|
21
|
+
Operações Git essenciais.
|
|
22
|
+
|
|
23
|
+
| Action | Descrição | Parâmetros |
|
|
24
|
+
|--------|-----------|------------|
|
|
25
|
+
| **update** | **⭐ RECOMENDADO - Fluxo completo automatizado** | **message, files=["."], gitignore=["pattern"], force, skipIfClean, organization** |
|
|
26
|
+
| status | Ver arquivos modificados/staged | - |
|
|
27
|
+
| add | Adicionar ao staging | files=["."] ou ["arquivo.js"] |
|
|
28
|
+
| commit | Criar commit | message="descrição" (obrigatório) |
|
|
29
|
+
| push | Enviar para GitHub+Gitea | force=true se histórico divergir |
|
|
30
|
+
| init | Inicializar repo + criar remotes | createGitignore=true, organization |
|
|
31
|
+
| ensure-remotes | Configurar remotes | organization |
|
|
32
|
+
| clean | Remover arquivos não rastreados | - |
|
|
33
|
+
|
|
34
|
+
**Exemplo update:** \`{ "projectPath": "/path", "action": "update", "message": "feat: nova func", "gitignore": ["*.log"] }\`
|
|
35
|
+
**Com organização:** \`{ "projectPath": "/path", "action": "init", "organization": "automacao-casa" }\`
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## git-branches
|
|
40
|
+
Gerenciar branches.
|
|
41
|
+
|
|
42
|
+
| Action | Descrição | Parâmetros |
|
|
43
|
+
|--------|-----------|------------|
|
|
44
|
+
| list | Listar branches | - |
|
|
45
|
+
| create | Criar branch | branch="feature/nome" |
|
|
46
|
+
| checkout | Trocar de branch | branch="nome" |
|
|
47
|
+
| delete | Deletar branch | branch="nome", force=true |
|
|
48
|
+
| rename | Renomear | branch="old", newBranch="new" |
|
|
49
|
+
|
|
50
|
+
**Convenções:** feature/*, bugfix/*, hotfix/*, release/*
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## git-merge (NOVO)
|
|
55
|
+
Mesclar branches.
|
|
56
|
+
|
|
57
|
+
| Action | Descrição | Parâmetros |
|
|
58
|
+
|--------|-----------|------------|
|
|
59
|
+
| merge | Mesclar branch | sourceBranch="feature/x", targetBranch="main" |
|
|
60
|
+
| status | Verificar conflitos | - |
|
|
61
|
+
| abort | Abortar merge | - |
|
|
62
|
+
|
|
63
|
+
**Opções:** fastForwardOnly=true (só FF), squash=true (um commit)
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## git-diff (NOVO)
|
|
68
|
+
Visualizar diferenças.
|
|
69
|
+
|
|
70
|
+
| Action | Descrição | Parâmetros |
|
|
71
|
+
|--------|-----------|------------|
|
|
72
|
+
| show | Ver mudanças locais | filepath="src/file.js" (opcional) |
|
|
73
|
+
| compare | Comparar commits | fromRef="HEAD~1", toRef="HEAD" |
|
|
74
|
+
| stat | Estatísticas | fromRef="HEAD~1", toRef="HEAD" |
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## git-clone (NOVO)
|
|
79
|
+
Clonar repositórios.
|
|
80
|
+
|
|
81
|
+
| Action | Descrição | Parâmetros |
|
|
82
|
+
|--------|-----------|------------|
|
|
83
|
+
| clone | Clonar repo | url="https://...", targetPath="./pasta" |
|
|
84
|
+
|
|
85
|
+
**Opções:** branch="main", depth=1 (shallow clone)
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## git-tags
|
|
90
|
+
Versionamento semântico.
|
|
91
|
+
|
|
92
|
+
| Action | Descrição | Parâmetros |
|
|
93
|
+
|--------|-----------|------------|
|
|
94
|
+
| list | Listar tags | - |
|
|
95
|
+
| create | Criar tag | tag="v1.0.0", message="opcional" |
|
|
96
|
+
| delete | Deletar tag | tag="v1.0.0" |
|
|
97
|
+
| push | Enviar para remotes | tag="v1.0.0" |
|
|
98
|
+
|
|
99
|
+
**Versões:** v1.0.0 (inicial), v1.1.0 (feature), v1.0.1 (bugfix), v2.0.0 (breaking)
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## git-stash
|
|
104
|
+
Salvar mudanças temporariamente.
|
|
105
|
+
|
|
106
|
+
| Action | Descrição | Parâmetros |
|
|
107
|
+
|--------|-----------|------------|
|
|
108
|
+
| list | Ver stashes salvos | - |
|
|
109
|
+
| save | Salvar mudanças | message="WIP", includeUntracked=true |
|
|
110
|
+
| pop | Restaurar e remover | ref="stash@{0}" |
|
|
111
|
+
| apply | Restaurar sem remover | ref="stash@{0}" |
|
|
112
|
+
| drop | Remover sem restaurar | ref="stash@{0}" |
|
|
113
|
+
| clear | Remover todos | - |
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## git-reset
|
|
118
|
+
Desfazer commits. ⚠️ CUIDADO
|
|
119
|
+
|
|
120
|
+
| Action | Descrição | Parâmetros |
|
|
121
|
+
|--------|-----------|------------|
|
|
122
|
+
| soft | Mantém mudanças staged | ref="HEAD~1" |
|
|
123
|
+
| mixed | Mantém arquivos, remove staging | ref="HEAD~1" |
|
|
124
|
+
| hard | ⚠️ DESCARTA tudo | ref="HEAD~1" |
|
|
125
|
+
| hard-clean | ⚠️⚠️ DESCARTA + remove untracked | ref="HEAD" |
|
|
126
|
+
|
|
127
|
+
**Refs:** HEAD~1 (1 commit), HEAD~2 (2 commits), SHA específico
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## git-remote
|
|
132
|
+
Operações em repos remotos GitHub/Gitea.
|
|
133
|
+
|
|
134
|
+
| Action | Descrição | Parâmetros |
|
|
135
|
+
|--------|-----------|------------|
|
|
136
|
+
| list | Ver remotes configurados | - |
|
|
137
|
+
| list-all | Listar todos repos da conta ou org | organization |
|
|
138
|
+
| list-orgs | Listar organizações disponíveis | - |
|
|
139
|
+
| ensure | Criar repos + configurar remotes | organization, isPublic |
|
|
140
|
+
| release-create | Criar release | tag="v1.0.0", name="", body="", organization |
|
|
141
|
+
| topics-set | Definir tópicos | topics=["js","mcp"], organization |
|
|
142
|
+
| label-create | Criar label | name="bug", color="ff0000", organization |
|
|
143
|
+
| repo-delete | ⚠️ Deletar repo | organization |
|
|
144
|
+
| milestone-create | Criar milestone | title="v1.0", organization |
|
|
145
|
+
| fork-create | Criar fork | organization |
|
|
146
|
+
| fork-list | Listar forks | organization |
|
|
147
|
+
| star-set | Dar estrela | - |
|
|
148
|
+
| star-unset | Remover estrela | - |
|
|
149
|
+
| contents-create | Criar arquivo via API | path="", content="", branch="", organization |
|
|
150
|
+
|
|
151
|
+
**Exemplo com organização:** \`{ "projectPath": "/path", "action": "ensure", "organization": "minha-org" }\`
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## 🏢 Organizações (Suporte Opcional)
|
|
156
|
+
Criar repos dentro de organizações GitHub/Gitea.
|
|
157
|
+
|
|
158
|
+
**Parâmetro:** \`organization\` (opcional em git-workflow, git-remote, git-issues, git-pulls)
|
|
159
|
+
|
|
160
|
+
| Uso | Exemplo |
|
|
161
|
+
|-----|---------|
|
|
162
|
+
| Conta pessoal (padrão) | \`{ "action": "init", ... }\` → usuario/repo |
|
|
163
|
+
| Em organização | \`{ "action": "init", "organization": "minha-org" }\` → minha-org/repo |
|
|
164
|
+
| Listar orgs | \`{ "action": "list-orgs" }\` via git-remote |
|
|
165
|
+
|
|
166
|
+
**Nota:** O token precisa ter permissão de criar repos na organização.
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
## git-issues
|
|
170
|
+
Gerenciar issues.
|
|
171
|
+
|
|
172
|
+
| Action | Descrição | Parâmetros |
|
|
173
|
+
|--------|-----------|------------|
|
|
174
|
+
| create | Criar issue | title="Bug: ...", body="Descrição" |
|
|
175
|
+
| list | Listar issues | - |
|
|
176
|
+
| comment | Comentar | number=1, body="Comentário" |
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## git-pulls
|
|
181
|
+
Gerenciar Pull Requests.
|
|
182
|
+
|
|
183
|
+
| Action | Descrição | Parâmetros |
|
|
184
|
+
|--------|-----------|------------|
|
|
185
|
+
| create | Criar PR | head="feature/x", base="main", title="" |
|
|
186
|
+
| list | Listar PRs | - |
|
|
187
|
+
| files | Ver arquivos do PR | number=1 |
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## git-sync
|
|
192
|
+
Sincronizar com remotes.
|
|
193
|
+
|
|
194
|
+
| Action | Descrição | Parâmetros |
|
|
195
|
+
|--------|-----------|------------|
|
|
196
|
+
| fetch | Baixar sem aplicar | remote="github", branch="" |
|
|
197
|
+
| pull | Baixar e aplicar | remote="github", branch="" |
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## git-config
|
|
202
|
+
Configurações Git.
|
|
203
|
+
|
|
204
|
+
| Action | Descrição | Parâmetros |
|
|
205
|
+
|--------|-----------|------------|
|
|
206
|
+
| get | Obter valor | key="user.name" |
|
|
207
|
+
| set | Definir valor | key="user.name", value="Nome" |
|
|
208
|
+
| unset | Remover | key="user.name" |
|
|
209
|
+
| list | Listar todas | scope="local" |
|
|
210
|
+
|
|
211
|
+
**Keys:** user.name, user.email, core.autocrlf
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## git-files
|
|
216
|
+
Ler arquivos do histórico Git.
|
|
217
|
+
|
|
218
|
+
| Action | Descrição | Parâmetros |
|
|
219
|
+
|--------|-----------|------------|
|
|
220
|
+
| list | Listar arquivos | ref="HEAD" |
|
|
221
|
+
| read | Ler conteúdo | filepath="package.json", ref="HEAD" |
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## git-history
|
|
226
|
+
Ver histórico de commits.
|
|
227
|
+
|
|
228
|
+
| Action | Descrição | Parâmetros |
|
|
229
|
+
|--------|-----------|------------|
|
|
230
|
+
| log | Listar commits | ref="HEAD", maxCount=50 |
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## git-ignore
|
|
235
|
+
Gerenciar .gitignore.
|
|
236
|
+
|
|
237
|
+
| Action | Descrição | Parâmetros |
|
|
238
|
+
|--------|-----------|------------|
|
|
239
|
+
| list | Ver padrões | - |
|
|
240
|
+
| create | Criar .gitignore | patterns=["node_modules/","*.log"] |
|
|
241
|
+
| add | Adicionar padrões | patterns=["dist/"] |
|
|
242
|
+
| remove | Remover padrões | patterns=["*.log"] |
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Erros Comuns
|
|
247
|
+
|
|
248
|
+
| Erro | Causa | Solução |
|
|
249
|
+
|------|-------|---------|
|
|
250
|
+
| PUSH_REJECTED | Histórico divergente | push com force=true |
|
|
251
|
+
| REMOTE_NOT_FOUND | Remote não configurado | git-remote ensure |
|
|
252
|
+
| BRANCH_NOT_FOUND | Branch não existe | git-branches list |
|
|
253
|
+
| TAG_ALREADY_EXISTS | Tag duplicada | Deletar ou usar outro nome |
|
|
254
|
+
| NOTHING_TO_STASH | Working tree limpa | Nada a fazer |
|
|
255
|
+
| INSUFFICIENT_HISTORY | HEAD~N muito grande | Verificar git-history log |
|
|
256
|
+
| AUTH_*_INVALID | Token inválido | Verificar GITHUB_TOKEN/GITEA_TOKEN |
|
|
257
|
+
| MERGE_CONFLICT | Conflito durante merge | Resolver manualmente ou abort |
|
|
258
|
+
| INVALID_PATH | Path traversal detectado | Usar caminhos absolutos válidos |
|
|
259
|
+
| CLONE_FAILED | Erro ao clonar | Verificar URL e autenticação |
|
|
260
|
+
`;
|
|
261
|
+
|
|
262
|
+
export function getResources() {
|
|
263
|
+
return [{
|
|
264
|
+
uri: "git://tools-guide",
|
|
265
|
+
name: "git-mcp Tools Guide",
|
|
266
|
+
description: "Documentação completa de todas as tools e actions do git-mcp",
|
|
267
|
+
mimeType: "text/markdown"
|
|
268
|
+
}];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function readResource(uri) {
|
|
272
|
+
if (uri === "git://tools-guide") {
|
|
273
|
+
return TOOLS_GUIDE;
|
|
274
|
+
}
|
|
275
|
+
return null;
|
|
276
|
+
}
|