@andrebuzeli/git-mcp 12.0.0 → 13.6.0
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/package.json +1 -1
- package/src/index.js +4 -4
- package/src/tools/git-branches.js +21 -7
- package/src/tools/git-config.js +8 -9
- package/src/tools/git-files.js +18 -8
- package/src/tools/git-history.js +22 -5
- package/src/tools/git-ignore.js +19 -8
- package/src/tools/git-issues.js +3 -3
- package/src/tools/git-pulls.js +3 -3
- package/src/tools/git-remote.js +5 -3
- package/src/tools/git-reset.js +20 -5
- package/src/tools/git-stash.js +31 -10
- package/src/tools/git-sync.js +21 -5
- package/src/tools/git-tags.js +34 -13
- package/src/tools/git-workflow.js +10 -6
- package/src/utils/errors.js +276 -1
- package/src/utils/gitAdapter.js +392 -9
- package/src/utils/retry.js +58 -9
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Server } from "@modelcontextprotocol/sdk/
|
|
3
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import Ajv from "ajv";
|
|
5
5
|
import { asToolError } from "./utils/errors.js";
|
|
6
6
|
import { ProviderManager } from "./providers/providerManager.js";
|
|
@@ -52,13 +52,13 @@ function listToolsResult() {
|
|
|
52
52
|
|
|
53
53
|
server.setRequestHandler(
|
|
54
54
|
// list tools
|
|
55
|
-
(await import("@modelcontextprotocol/sdk/
|
|
55
|
+
(await import("@modelcontextprotocol/sdk/types.js")).ListToolsRequestSchema,
|
|
56
56
|
async () => listToolsResult()
|
|
57
57
|
);
|
|
58
58
|
|
|
59
59
|
server.setRequestHandler(
|
|
60
60
|
// call tool
|
|
61
|
-
(await import("@modelcontextprotocol/sdk/
|
|
61
|
+
(await import("@modelcontextprotocol/sdk/types.js")).CallToolRequestSchema,
|
|
62
62
|
async (req) => {
|
|
63
63
|
const name = req.params?.name || "";
|
|
64
64
|
const args = req.params?.arguments || {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Ajv from "ajv";
|
|
2
|
-
import { asToolError, asToolResult } from "../utils/errors.js";
|
|
2
|
+
import { asToolError, asToolResult, errorToResponse } from "../utils/errors.js";
|
|
3
3
|
|
|
4
4
|
const ajv = new Ajv({ allErrors: true });
|
|
5
5
|
|
|
@@ -29,32 +29,46 @@ export function createGitBranchesTool(git) {
|
|
|
29
29
|
}
|
|
30
30
|
if (action === "create") {
|
|
31
31
|
const ref = args.branch;
|
|
32
|
-
if (!ref) return asToolError("
|
|
32
|
+
if (!ref) return asToolError("MISSING_PARAMETER", "branch é obrigatório", { parameter: "branch" });
|
|
33
|
+
// Auto-correção: verificar se já existe
|
|
34
|
+
const existing = await git.listBranches(projectPath, false);
|
|
35
|
+
if (existing.includes(ref)) {
|
|
36
|
+
return asToolError("BRANCH_ALREADY_EXISTS", `Branch '${ref}' já existe`, { branch: ref, existingBranches: existing });
|
|
37
|
+
}
|
|
33
38
|
await git.createBranch(projectPath, ref);
|
|
34
39
|
return asToolResult({ success: true, branch: ref });
|
|
35
40
|
}
|
|
36
41
|
if (action === "delete") {
|
|
37
42
|
const ref = args.branch;
|
|
38
|
-
if (!ref) return asToolError("
|
|
43
|
+
if (!ref) return asToolError("MISSING_PARAMETER", "branch é obrigatório", { parameter: "branch" });
|
|
44
|
+
// Auto-correção: verificar se existe e não é a atual
|
|
45
|
+
const existing = await git.listBranches(projectPath, false);
|
|
46
|
+
if (!existing.includes(ref)) {
|
|
47
|
+
return asToolError("BRANCH_NOT_FOUND", `Branch '${ref}' não encontrada`, { branch: ref, availableBranches: existing });
|
|
48
|
+
}
|
|
49
|
+
const current = await git.getCurrentBranch(projectPath);
|
|
50
|
+
if (ref === current) {
|
|
51
|
+
return asToolError("CANNOT_DELETE_CURRENT", `Não pode deletar branch atual '${ref}'`, { branch: ref, suggestion: "Faça checkout para outra branch primeiro" });
|
|
52
|
+
}
|
|
39
53
|
await git.deleteBranch(projectPath, ref, !!args.force);
|
|
40
54
|
return asToolResult({ success: true, branch: ref });
|
|
41
55
|
}
|
|
42
56
|
if (action === "rename") {
|
|
43
57
|
const oldName = args.branch;
|
|
44
58
|
const newName = args.newBranch;
|
|
45
|
-
if (!oldName || !newName) return asToolError("
|
|
59
|
+
if (!oldName || !newName) return asToolError("MISSING_PARAMETER", "branch e newBranch são obrigatórios", { parameters: ["branch", "newBranch"] });
|
|
46
60
|
await git.renameBranch(projectPath, oldName, newName);
|
|
47
61
|
return asToolResult({ success: true, from: oldName, to: newName });
|
|
48
62
|
}
|
|
49
63
|
if (action === "checkout") {
|
|
50
64
|
const ref = args.branch;
|
|
51
|
-
if (!ref) return asToolError("
|
|
65
|
+
if (!ref) return asToolError("MISSING_PARAMETER", "branch é obrigatório", { parameter: "branch" });
|
|
52
66
|
await git.checkout(projectPath, ref);
|
|
53
67
|
return asToolResult({ success: true, branch: ref });
|
|
54
68
|
}
|
|
55
|
-
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}
|
|
69
|
+
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}`, { availableActions: ["list", "create", "delete", "rename", "checkout"] });
|
|
56
70
|
} catch (e) {
|
|
57
|
-
return
|
|
71
|
+
return errorToResponse(e);
|
|
58
72
|
}
|
|
59
73
|
}
|
|
60
74
|
|
package/src/tools/git-config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Ajv from "ajv";
|
|
2
|
-
import { asToolError, asToolResult } from "../utils/errors.js";
|
|
2
|
+
import { asToolError, asToolResult, errorToResponse } from "../utils/errors.js";
|
|
3
3
|
|
|
4
4
|
const ajv = new Ajv({ allErrors: true });
|
|
5
5
|
|
|
@@ -24,30 +24,29 @@ export function createGitConfigTool(git) {
|
|
|
24
24
|
const scope = args.scope || "local";
|
|
25
25
|
try {
|
|
26
26
|
if (action === "get") {
|
|
27
|
-
if (!args.key) return asToolError("
|
|
27
|
+
if (!args.key) return asToolError("MISSING_PARAMETER", "key é obrigatório", { parameter: "key" });
|
|
28
28
|
const val = await git.getConfig(projectPath, args.key, scope);
|
|
29
|
-
return asToolResult({ key: args.key, value: val });
|
|
29
|
+
return asToolResult({ key: args.key, value: val, found: val !== undefined });
|
|
30
30
|
}
|
|
31
31
|
if (action === "set") {
|
|
32
|
-
if (!args.key) return asToolError("
|
|
32
|
+
if (!args.key) return asToolError("MISSING_PARAMETER", "key é obrigatório", { parameter: "key" });
|
|
33
33
|
await git.setConfig(projectPath, args.key, args.value ?? "", scope);
|
|
34
34
|
return asToolResult({ success: true, key: args.key, value: args.value ?? "" });
|
|
35
35
|
}
|
|
36
36
|
if (action === "unset") {
|
|
37
|
-
if (!args.key) return asToolError("
|
|
37
|
+
if (!args.key) return asToolError("MISSING_PARAMETER", "key é obrigatório", { parameter: "key" });
|
|
38
38
|
await git.unsetConfig(projectPath, args.key, scope);
|
|
39
39
|
return asToolResult({ success: true, key: args.key });
|
|
40
40
|
}
|
|
41
41
|
if (action === "list") {
|
|
42
42
|
const items = await git.listConfig(projectPath, scope);
|
|
43
|
-
return asToolResult({ scope, items });
|
|
43
|
+
return asToolResult({ scope, items, count: Object.keys(items).length });
|
|
44
44
|
}
|
|
45
|
-
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}
|
|
45
|
+
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}`, { availableActions: ["get", "set", "unset", "list"] });
|
|
46
46
|
} catch (e) {
|
|
47
|
-
return
|
|
47
|
+
return errorToResponse(e);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
return { name: "git-config", description: "Gerencia configurações Git (local/global/system)", inputSchema, handle };
|
|
52
52
|
}
|
|
53
|
-
|
package/src/tools/git-files.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Ajv from "ajv";
|
|
2
|
-
import { asToolError, asToolResult } from "../utils/errors.js";
|
|
2
|
+
import { asToolError, asToolResult, errorToResponse } from "../utils/errors.js";
|
|
3
3
|
|
|
4
4
|
const ajv = new Ajv({ allErrors: true });
|
|
5
5
|
|
|
@@ -23,19 +23,29 @@ export function createGitFilesTool(git) {
|
|
|
23
23
|
try {
|
|
24
24
|
if (action === "list") {
|
|
25
25
|
const files = await git.listFiles(projectPath, args.ref || "HEAD");
|
|
26
|
-
return asToolResult({ files });
|
|
26
|
+
return asToolResult({ files, count: files.length });
|
|
27
27
|
}
|
|
28
28
|
if (action === "read") {
|
|
29
|
-
if (!args.filepath) return asToolError("
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
if (!args.filepath) return asToolError("MISSING_PARAMETER", "filepath é obrigatório", { parameter: "filepath" });
|
|
30
|
+
try {
|
|
31
|
+
const text = await git.readFile(projectPath, args.filepath, args.ref || "HEAD");
|
|
32
|
+
return { content: [{ type: "text", text }], isError: false };
|
|
33
|
+
} catch (e) {
|
|
34
|
+
// Auto-diagnóstico: listar arquivos disponíveis se não encontrar
|
|
35
|
+
const files = await git.listFiles(projectPath, args.ref || "HEAD").catch(() => []);
|
|
36
|
+
return asToolError("FILE_NOT_FOUND", `Arquivo '${args.filepath}' não encontrado`, {
|
|
37
|
+
filepath: args.filepath,
|
|
38
|
+
ref: args.ref || "HEAD",
|
|
39
|
+
availableFiles: files.slice(0, 20),
|
|
40
|
+
totalFiles: files.length
|
|
41
|
+
});
|
|
42
|
+
}
|
|
32
43
|
}
|
|
33
|
-
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}
|
|
44
|
+
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}`, { availableActions: ["list", "read"] });
|
|
34
45
|
} catch (e) {
|
|
35
|
-
return
|
|
46
|
+
return errorToResponse(e);
|
|
36
47
|
}
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
return { name: "git-files", description: "Lista e lê arquivos do repo", inputSchema, handle };
|
|
40
51
|
}
|
|
41
|
-
|
package/src/tools/git-history.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Ajv from "ajv";
|
|
2
|
-
import { asToolError, asToolResult } from "../utils/errors.js";
|
|
2
|
+
import { asToolError, asToolResult, errorToResponse } from "../utils/errors.js";
|
|
3
3
|
|
|
4
4
|
const ajv = new Ajv({ allErrors: true });
|
|
5
5
|
|
|
@@ -23,14 +23,31 @@ export function createGitHistoryTool(git) {
|
|
|
23
23
|
try {
|
|
24
24
|
if (action === "log") {
|
|
25
25
|
const items = await git.log(projectPath, { ref: args.ref || "HEAD", maxCount: args.maxCount || 50 });
|
|
26
|
-
|
|
26
|
+
if (items.length === 0) {
|
|
27
|
+
return asToolResult({
|
|
28
|
+
commits: [],
|
|
29
|
+
count: 0,
|
|
30
|
+
message: "Nenhum commit encontrado. Use action='commit' para criar o primeiro commit."
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return asToolResult({
|
|
34
|
+
commits: items.map(c => ({
|
|
35
|
+
sha: c.sha,
|
|
36
|
+
shortSha: c.sha.substring(0, 7),
|
|
37
|
+
message: c.message.split("\n")[0],
|
|
38
|
+
fullMessage: c.message,
|
|
39
|
+
author: c.author.name,
|
|
40
|
+
email: c.author.email,
|
|
41
|
+
date: c.date
|
|
42
|
+
})),
|
|
43
|
+
count: items.length
|
|
44
|
+
});
|
|
27
45
|
}
|
|
28
|
-
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}
|
|
46
|
+
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}`, { availableActions: ["log"] });
|
|
29
47
|
} catch (e) {
|
|
30
|
-
return
|
|
48
|
+
return errorToResponse(e);
|
|
31
49
|
}
|
|
32
50
|
}
|
|
33
51
|
|
|
34
52
|
return { name: "git-history", description: "Histórico de commits", inputSchema, handle };
|
|
35
53
|
}
|
|
36
|
-
|
package/src/tools/git-ignore.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Ajv from "ajv";
|
|
2
|
-
import { asToolError, asToolResult } from "../utils/errors.js";
|
|
2
|
+
import { asToolError, asToolResult, errorToResponse } from "../utils/errors.js";
|
|
3
3
|
|
|
4
4
|
const ajv = new Ajv({ allErrors: true });
|
|
5
5
|
|
|
@@ -23,26 +23,37 @@ export function createGitIgnoreTool(git) {
|
|
|
23
23
|
try {
|
|
24
24
|
if (action === "list") {
|
|
25
25
|
const items = await git.listGitignore(projectPath);
|
|
26
|
-
return asToolResult({ items });
|
|
26
|
+
return asToolResult({ items, count: items.length });
|
|
27
27
|
}
|
|
28
28
|
if (action === "create") {
|
|
29
|
+
if (patterns.length === 0) {
|
|
30
|
+
return asToolError("MISSING_PARAMETER", "patterns é obrigatório para criar .gitignore", {
|
|
31
|
+
parameter: "patterns",
|
|
32
|
+
suggestion: "Forneça um array de padrões, ex: ['node_modules/', '*.log', '.env']"
|
|
33
|
+
});
|
|
34
|
+
}
|
|
29
35
|
await git.createGitignore(projectPath, patterns);
|
|
30
|
-
return asToolResult({ success: true });
|
|
36
|
+
return asToolResult({ success: true, patterns });
|
|
31
37
|
}
|
|
32
38
|
if (action === "add") {
|
|
39
|
+
if (patterns.length === 0) {
|
|
40
|
+
return asToolError("MISSING_PARAMETER", "patterns é obrigatório para adicionar ao .gitignore", { parameter: "patterns" });
|
|
41
|
+
}
|
|
33
42
|
await git.addToGitignore(projectPath, patterns);
|
|
34
|
-
return asToolResult({ success: true });
|
|
43
|
+
return asToolResult({ success: true, added: patterns });
|
|
35
44
|
}
|
|
36
45
|
if (action === "remove") {
|
|
46
|
+
if (patterns.length === 0) {
|
|
47
|
+
return asToolError("MISSING_PARAMETER", "patterns é obrigatório para remover do .gitignore", { parameter: "patterns" });
|
|
48
|
+
}
|
|
37
49
|
await git.removeFromGitignore(projectPath, patterns);
|
|
38
|
-
return asToolResult({ success: true });
|
|
50
|
+
return asToolResult({ success: true, removed: patterns });
|
|
39
51
|
}
|
|
40
|
-
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}
|
|
52
|
+
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}`, { availableActions: ["list", "create", "add", "remove"] });
|
|
41
53
|
} catch (e) {
|
|
42
|
-
return
|
|
54
|
+
return errorToResponse(e);
|
|
43
55
|
}
|
|
44
56
|
}
|
|
45
57
|
|
|
46
58
|
return { name: "git-ignore", description: "Gerencia .gitignore", inputSchema, handle };
|
|
47
59
|
}
|
|
48
|
-
|
package/src/tools/git-issues.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Ajv from "ajv";
|
|
2
2
|
import axios from "axios";
|
|
3
|
-
import { asToolError, asToolResult } from "../utils/errors.js";
|
|
3
|
+
import { asToolError, asToolResult, errorToResponse } from "../utils/errors.js";
|
|
4
4
|
import { getRepoNameFromPath } from "../utils/repoHelpers.js";
|
|
5
5
|
import { runBoth } from "../utils/providerExec.js";
|
|
6
6
|
|
|
@@ -48,9 +48,9 @@ export function createGitIssuesTool(pm) {
|
|
|
48
48
|
});
|
|
49
49
|
return asToolResult({ success: !!(out.github?.ok || out.gitea?.ok), providers: out });
|
|
50
50
|
}
|
|
51
|
-
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${args.action}
|
|
51
|
+
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${args.action}`, { availableActions: ["create", "list", "comment"] });
|
|
52
52
|
} catch (e) {
|
|
53
|
-
return
|
|
53
|
+
return errorToResponse(e);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
package/src/tools/git-pulls.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Ajv from "ajv";
|
|
2
2
|
import axios from "axios";
|
|
3
|
-
import { asToolError, asToolResult } from "../utils/errors.js";
|
|
3
|
+
import { asToolError, asToolResult, errorToResponse } from "../utils/errors.js";
|
|
4
4
|
import { getRepoNameFromPath } from "../utils/repoHelpers.js";
|
|
5
5
|
import { runBoth } from "../utils/providerExec.js";
|
|
6
6
|
|
|
@@ -51,9 +51,9 @@ export function createGitPullsTool(pm) {
|
|
|
51
51
|
});
|
|
52
52
|
return asToolResult({ providers: out });
|
|
53
53
|
}
|
|
54
|
-
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${args.action}
|
|
54
|
+
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${args.action}`, { availableActions: ["create", "list", "files"] });
|
|
55
55
|
} catch (e) {
|
|
56
|
-
return
|
|
56
|
+
return errorToResponse(e);
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
package/src/tools/git-remote.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Ajv from "ajv";
|
|
2
2
|
import axios from "axios";
|
|
3
|
-
import { asToolError, asToolResult } from "../utils/errors.js";
|
|
3
|
+
import { asToolError, asToolResult, errorToResponse, mapExternalError } from "../utils/errors.js";
|
|
4
4
|
import { getRepoNameFromPath } from "../utils/repoHelpers.js";
|
|
5
5
|
import { runBoth } from "../utils/providerExec.js";
|
|
6
6
|
|
|
@@ -167,9 +167,11 @@ export function createGitRemoteTool(pm, git) {
|
|
|
167
167
|
});
|
|
168
168
|
return asToolResult({ success: !!(out.github?.ok || out.gitea?.ok), path: filePath, providers: out });
|
|
169
169
|
}
|
|
170
|
-
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}
|
|
170
|
+
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}`, {
|
|
171
|
+
availableActions: ["list", "ensure", "set-url", "repo-delete", "release-create", "topics-set", "milestone-create", "label-create", "fork-create", "fork-list", "star-set", "star-unset", "star-check", "subscription-set", "subscription-unset", "contents-create"]
|
|
172
|
+
});
|
|
171
173
|
} catch (e) {
|
|
172
|
-
return
|
|
174
|
+
return errorToResponse(e);
|
|
173
175
|
}
|
|
174
176
|
}
|
|
175
177
|
|
package/src/tools/git-reset.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Ajv from "ajv";
|
|
2
|
-
import { asToolError, asToolResult } from "../utils/errors.js";
|
|
2
|
+
import { asToolError, asToolResult, errorToResponse } from "../utils/errors.js";
|
|
3
3
|
|
|
4
4
|
const ajv = new Ajv({ allErrors: true });
|
|
5
5
|
|
|
@@ -20,16 +20,31 @@ export function createGitResetTool(git) {
|
|
|
20
20
|
if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
|
|
21
21
|
const { projectPath, action, ref } = args;
|
|
22
22
|
try {
|
|
23
|
+
// Auto-verificação: checar se há commits suficientes para HEAD~N
|
|
24
|
+
if (ref.match(/HEAD~(\d+)/)) {
|
|
25
|
+
const match = ref.match(/HEAD~(\d+)/);
|
|
26
|
+
const steps = parseInt(match[1], 10);
|
|
27
|
+
const log = await git.log(projectPath, { maxCount: steps + 2 });
|
|
28
|
+
if (log.length <= steps) {
|
|
29
|
+
return asToolError("INSUFFICIENT_HISTORY", `Histórico insuficiente para ${ref}`, {
|
|
30
|
+
requestedSteps: steps,
|
|
31
|
+
availableCommits: log.length,
|
|
32
|
+
suggestion: `Use HEAD~${log.length - 1} no máximo, ou um SHA específico`,
|
|
33
|
+
recentCommits: log.slice(0, 5).map(c => ({ sha: c.sha.substring(0, 7), message: c.message.split("\n")[0] }))
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
23
38
|
if (action === "soft") await git.resetSoft(projectPath, ref);
|
|
24
39
|
else if (action === "mixed") await git.resetMixed(projectPath, ref);
|
|
25
40
|
else if (action === "hard") await git.resetHard(projectPath, ref);
|
|
26
|
-
else return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}
|
|
27
|
-
|
|
41
|
+
else return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}`, { availableActions: ["soft", "mixed", "hard"] });
|
|
42
|
+
|
|
43
|
+
return asToolResult({ success: true, action, ref, message: `Reset ${action} para ${ref} realizado` });
|
|
28
44
|
} catch (e) {
|
|
29
|
-
return
|
|
45
|
+
return errorToResponse(e);
|
|
30
46
|
}
|
|
31
47
|
}
|
|
32
48
|
|
|
33
49
|
return { name: "git-reset", description: "Reset soft/mixed/hard", inputSchema, handle };
|
|
34
50
|
}
|
|
35
|
-
|
package/src/tools/git-stash.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Ajv from "ajv";
|
|
2
|
-
import { asToolError, asToolResult } from "../utils/errors.js";
|
|
2
|
+
import { asToolError, asToolResult, errorToResponse } from "../utils/errors.js";
|
|
3
3
|
|
|
4
4
|
const ajv = new Ajv({ allErrors: true });
|
|
5
5
|
|
|
@@ -24,34 +24,55 @@ export function createGitStashTool(git) {
|
|
|
24
24
|
try {
|
|
25
25
|
if (action === "list") {
|
|
26
26
|
const items = await git.listStash(projectPath);
|
|
27
|
-
return asToolResult({
|
|
27
|
+
return asToolResult({
|
|
28
|
+
items: items.map((s, i) => ({ index: i, ref: `stash@{${i}}`, message: s.message, timestamp: s.timestamp })),
|
|
29
|
+
count: items.length
|
|
30
|
+
});
|
|
28
31
|
}
|
|
29
32
|
if (action === "save") {
|
|
33
|
+
// Auto-verificação: checar se há mudanças
|
|
34
|
+
const status = await git.status(projectPath);
|
|
35
|
+
if (status.isClean && !args.includeUntracked) {
|
|
36
|
+
return asToolError("NOTHING_TO_STASH", "Working tree limpa, nada para stash", { status });
|
|
37
|
+
}
|
|
30
38
|
await git.saveStash(projectPath, args.message || "WIP", !!args.includeUntracked);
|
|
31
|
-
return asToolResult({ success: true });
|
|
39
|
+
return asToolResult({ success: true, message: args.message || "WIP" });
|
|
32
40
|
}
|
|
33
41
|
if (action === "apply") {
|
|
42
|
+
// Auto-verificação: checar se stash existe
|
|
43
|
+
const items = await git.listStash(projectPath);
|
|
44
|
+
if (items.length === 0) {
|
|
45
|
+
return asToolError("STASH_NOT_FOUND", "Nenhum stash disponível", { availableStashes: [] });
|
|
46
|
+
}
|
|
34
47
|
await git.applyStash(projectPath, args.ref);
|
|
35
|
-
return asToolResult({ success: true });
|
|
48
|
+
return asToolResult({ success: true, ref: args.ref || "stash@{0}" });
|
|
36
49
|
}
|
|
37
50
|
if (action === "pop") {
|
|
51
|
+
// Auto-verificação: checar se stash existe
|
|
52
|
+
const items = await git.listStash(projectPath);
|
|
53
|
+
if (items.length === 0) {
|
|
54
|
+
return asToolError("STASH_NOT_FOUND", "Nenhum stash disponível", { availableStashes: [] });
|
|
55
|
+
}
|
|
38
56
|
await git.popStash(projectPath, args.ref);
|
|
39
|
-
return asToolResult({ success: true });
|
|
57
|
+
return asToolResult({ success: true, ref: args.ref || "stash@{0}" });
|
|
40
58
|
}
|
|
41
59
|
if (action === "drop") {
|
|
60
|
+
const items = await git.listStash(projectPath);
|
|
61
|
+
if (items.length === 0) {
|
|
62
|
+
return asToolError("STASH_NOT_FOUND", "Nenhum stash disponível", { availableStashes: [] });
|
|
63
|
+
}
|
|
42
64
|
await git.dropStash(projectPath, args.ref);
|
|
43
|
-
return asToolResult({ success: true });
|
|
65
|
+
return asToolResult({ success: true, ref: args.ref || "stash@{0}" });
|
|
44
66
|
}
|
|
45
67
|
if (action === "clear") {
|
|
46
68
|
await git.clearStash(projectPath);
|
|
47
|
-
return asToolResult({ success: true });
|
|
69
|
+
return asToolResult({ success: true, message: "Todos os stashes removidos" });
|
|
48
70
|
}
|
|
49
|
-
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}
|
|
71
|
+
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}`, { availableActions: ["list", "save", "apply", "pop", "drop", "clear"] });
|
|
50
72
|
} catch (e) {
|
|
51
|
-
return
|
|
73
|
+
return errorToResponse(e);
|
|
52
74
|
}
|
|
53
75
|
}
|
|
54
76
|
|
|
55
77
|
return { name: "git-stash", description: "Gerencia stash", inputSchema, handle };
|
|
56
78
|
}
|
|
57
|
-
|
package/src/tools/git-sync.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Ajv from "ajv";
|
|
2
|
-
import { asToolError, asToolResult } from "../utils/errors.js";
|
|
2
|
+
import { asToolError, asToolResult, errorToResponse } from "../utils/errors.js";
|
|
3
3
|
|
|
4
4
|
const ajv = new Ajv({ allErrors: true });
|
|
5
5
|
|
|
@@ -26,17 +26,33 @@ export function createGitSyncTool(git) {
|
|
|
26
26
|
const remotes = args.remote ? [args.remote] : ["origin", "github", "gitea"];
|
|
27
27
|
const promises = remotes.map(r => git.fetch(projectPath, r, branch).then(() => ({ remote: r, ok: true })).catch(e => ({ remote: r, ok: false, error: String(e?.message || e) })));
|
|
28
28
|
const results = await Promise.all(promises);
|
|
29
|
-
|
|
29
|
+
const successful = results.filter(x => x.ok);
|
|
30
|
+
const failed = results.filter(x => !x.ok);
|
|
31
|
+
return asToolResult({
|
|
32
|
+
success: successful.length > 0,
|
|
33
|
+
branch,
|
|
34
|
+
fetched: successful.map(x => x.remote),
|
|
35
|
+
failed: failed.map(x => ({ remote: x.remote, error: x.error })),
|
|
36
|
+
message: successful.length === 0 ? "Nenhum remote alcançado. Verifique conexão e configuração." : undefined
|
|
37
|
+
});
|
|
30
38
|
}
|
|
31
39
|
if (action === "pull") {
|
|
32
40
|
const remotes = args.remote ? [args.remote] : ["origin", "github", "gitea"];
|
|
33
41
|
const promises = remotes.map(r => git.pull(projectPath, r, branch).then(() => ({ remote: r, ok: true })).catch(e => ({ remote: r, ok: false, error: String(e?.message || e) })));
|
|
34
42
|
const results = await Promise.all(promises);
|
|
35
|
-
|
|
43
|
+
const successful = results.filter(x => x.ok);
|
|
44
|
+
const failed = results.filter(x => !x.ok);
|
|
45
|
+
return asToolResult({
|
|
46
|
+
success: successful.length > 0,
|
|
47
|
+
branch,
|
|
48
|
+
pulled: successful.map(x => x.remote),
|
|
49
|
+
failed: failed.map(x => ({ remote: x.remote, error: x.error })),
|
|
50
|
+
message: successful.length === 0 ? "Nenhum remote alcançado. Verifique conexão e configuração." : undefined
|
|
51
|
+
});
|
|
36
52
|
}
|
|
37
|
-
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}
|
|
53
|
+
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}`, { availableActions: ["pull", "fetch"] });
|
|
38
54
|
} catch (e) {
|
|
39
|
-
return
|
|
55
|
+
return errorToResponse(e);
|
|
40
56
|
}
|
|
41
57
|
}
|
|
42
58
|
|
package/src/tools/git-tags.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Ajv from "ajv";
|
|
2
|
-
import { asToolError, asToolResult } from "../utils/errors.js";
|
|
2
|
+
import { asToolError, asToolResult, errorToResponse } from "../utils/errors.js";
|
|
3
3
|
|
|
4
4
|
const ajv = new Ajv({ allErrors: true });
|
|
5
5
|
|
|
@@ -24,35 +24,56 @@ export function createGitTagsTool(git) {
|
|
|
24
24
|
try {
|
|
25
25
|
if (action === "list") {
|
|
26
26
|
const tags = await git.listTags(projectPath);
|
|
27
|
-
return asToolResult({ tags });
|
|
27
|
+
return asToolResult({ tags, count: tags.length });
|
|
28
28
|
}
|
|
29
29
|
if (action === "create") {
|
|
30
30
|
const tag = args.tag;
|
|
31
|
-
if (!tag) return asToolError("
|
|
31
|
+
if (!tag) return asToolError("MISSING_PARAMETER", "tag é obrigatório", { parameter: "tag" });
|
|
32
|
+
// Auto-verificação: checar se já existe
|
|
33
|
+
const existing = await git.listTags(projectPath);
|
|
34
|
+
if (existing.includes(tag)) {
|
|
35
|
+
return asToolError("TAG_ALREADY_EXISTS", `Tag '${tag}' já existe`, { tag, existingTags: existing });
|
|
36
|
+
}
|
|
32
37
|
await git.createTag(projectPath, tag, args.ref || "HEAD", args.message);
|
|
33
|
-
return asToolResult({ success: true, tag });
|
|
38
|
+
return asToolResult({ success: true, tag, ref: args.ref || "HEAD" });
|
|
34
39
|
}
|
|
35
40
|
if (action === "delete") {
|
|
36
41
|
const tag = args.tag;
|
|
37
|
-
if (!tag) return asToolError("
|
|
42
|
+
if (!tag) return asToolError("MISSING_PARAMETER", "tag é obrigatório", { parameter: "tag" });
|
|
43
|
+
// Auto-verificação: checar se existe
|
|
44
|
+
const existing = await git.listTags(projectPath);
|
|
45
|
+
if (!existing.includes(tag)) {
|
|
46
|
+
return asToolError("TAG_NOT_FOUND", `Tag '${tag}' não encontrada`, { tag, existingTags: existing });
|
|
47
|
+
}
|
|
38
48
|
await git.deleteTag(projectPath, tag);
|
|
39
49
|
return asToolResult({ success: true, tag });
|
|
40
50
|
}
|
|
41
51
|
if (action === "push") {
|
|
42
52
|
const tag = args.tag;
|
|
43
|
-
if (!tag) return asToolError("
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
if (!tag) return asToolError("MISSING_PARAMETER", "tag é obrigatório", { parameter: "tag" });
|
|
54
|
+
// Auto-verificação: checar se existe
|
|
55
|
+
const existing = await git.listTags(projectPath);
|
|
56
|
+
if (!existing.includes(tag)) {
|
|
57
|
+
return asToolError("TAG_NOT_FOUND", `Tag '${tag}' não existe localmente. Crie primeiro com action='create'`, { tag, existingTags: existing });
|
|
58
|
+
}
|
|
59
|
+
const results = await Promise.allSettled([
|
|
60
|
+
git.pushTag(projectPath, "github", tag),
|
|
61
|
+
git.pushTag(projectPath, "gitea", tag)
|
|
47
62
|
]);
|
|
48
|
-
|
|
63
|
+
const pushed = [];
|
|
64
|
+
const failed = [];
|
|
65
|
+
if (results[0].status === "fulfilled") pushed.push("github");
|
|
66
|
+
else failed.push({ remote: "github", error: results[0].reason?.message });
|
|
67
|
+
if (results[1].status === "fulfilled") pushed.push("gitea");
|
|
68
|
+
else failed.push({ remote: "gitea", error: results[1].reason?.message });
|
|
69
|
+
|
|
70
|
+
return asToolResult({ success: pushed.length > 0, tag, pushed, failed });
|
|
49
71
|
}
|
|
50
|
-
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}
|
|
72
|
+
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}`, { availableActions: ["list", "create", "delete", "push"] });
|
|
51
73
|
} catch (e) {
|
|
52
|
-
return
|
|
74
|
+
return errorToResponse(e);
|
|
53
75
|
}
|
|
54
76
|
}
|
|
55
77
|
|
|
56
78
|
return { name: "git-tags", description: "Gerencia tags e push paralelo", inputSchema, handle };
|
|
57
79
|
}
|
|
58
|
-
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Ajv from "ajv";
|
|
2
|
-
import { MCPError, asToolError, asToolResult } from "../utils/errors.js";
|
|
2
|
+
import { MCPError, asToolError, asToolResult, errorToResponse, createError } from "../utils/errors.js";
|
|
3
3
|
import { getRepoNameFromPath } from "../utils/repoHelpers.js";
|
|
4
4
|
|
|
5
5
|
const ajv = new Ajv({ allErrors: true });
|
|
@@ -11,7 +11,8 @@ export function createGitWorkflowTool(pm, git) {
|
|
|
11
11
|
projectPath: { type: "string", description: "Caminho absoluto do projeto" },
|
|
12
12
|
action: { type: "string", enum: ["init", "status", "add", "remove", "commit", "push", "pull", "sync", "ensure-remotes"], description: "Ação" },
|
|
13
13
|
files: { type: "array", items: { type: "string" } },
|
|
14
|
-
message: { type: "string" }
|
|
14
|
+
message: { type: "string" },
|
|
15
|
+
force: { type: "boolean", description: "Force push (para resolver divergências de histórico)" }
|
|
15
16
|
},
|
|
16
17
|
required: ["projectPath", "action"],
|
|
17
18
|
additionalProperties: true
|
|
@@ -62,16 +63,19 @@ export function createGitWorkflowTool(pm, git) {
|
|
|
62
63
|
}
|
|
63
64
|
if (action === "push") {
|
|
64
65
|
const branch = await git.getCurrentBranch(projectPath);
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
const force = !!args.force;
|
|
67
|
+
await git.pushParallel(projectPath, branch, force);
|
|
68
|
+
return asToolResult({ success: true, branch, remotes: ["github", "gitea"], force });
|
|
67
69
|
}
|
|
68
70
|
if (action === "sync" || action === "pull") {
|
|
69
71
|
// Basic implementation: fetch + status; full merge/pull can be added
|
|
70
72
|
return asToolResult({ success: true, message: "Operação de pull/sync não-op realizada" });
|
|
71
73
|
}
|
|
72
|
-
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}
|
|
74
|
+
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}`, {
|
|
75
|
+
availableActions: ["init", "status", "add", "remove", "commit", "push", "pull", "sync", "ensure-remotes"]
|
|
76
|
+
});
|
|
73
77
|
} catch (e) {
|
|
74
|
-
return
|
|
78
|
+
return errorToResponse(e);
|
|
75
79
|
}
|
|
76
80
|
}
|
|
77
81
|
|