@andrebuzeli/git-mcp 11.0.4 → 12.0.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/README.md +39 -47
- package/package.json +24 -19
- package/src/index.js +84 -0
- package/src/providers/providerManager.js +107 -0
- package/src/tools/git-branches.js +63 -0
- package/src/tools/git-config.js +53 -0
- package/src/tools/git-files.js +41 -0
- package/src/tools/git-history.js +36 -0
- package/src/tools/git-ignore.js +48 -0
- package/src/tools/git-issues.js +58 -12
- package/src/tools/git-pulls.js +61 -0
- package/src/tools/git-remote.js +182 -29
- package/src/tools/git-reset.js +35 -0
- package/src/tools/git-stash.js +57 -0
- package/src/tools/git-sync.js +44 -40
- package/src/tools/git-tags.js +58 -0
- package/src/tools/git-workflow.js +85 -0
- package/src/utils/errors.js +22 -0
- package/src/utils/gitAdapter.js +116 -0
- package/src/utils/providerExec.js +13 -0
- package/src/utils/repoHelpers.js +25 -0
- package/src/utils/retry.js +12 -0
- package/bin/git-mcp.js +0 -21
- package/docs/TOOLS.md +0 -110
- package/mcp.json.template +0 -12
- package/src/local/git.js +0 -14
- package/src/providers/gitea.js +0 -13
- package/src/providers/github.js +0 -13
- package/src/server.js +0 -115
- package/src/tools/git-actions.js +0 -19
- package/src/tools/git-activity.js +0 -28
- package/src/tools/git-admin.js +0 -20
- package/src/tools/git-checks.js +0 -14
- package/src/tools/git-commits.js +0 -34
- package/src/tools/git-contents.js +0 -30
- package/src/tools/git-deployments.js +0 -21
- package/src/tools/git-gists.js +0 -15
- package/src/tools/git-gitdata.js +0 -19
- package/src/tools/git-issues-prs.js +0 -44
- package/src/tools/git-local.js +0 -66
- package/src/tools/git-meta.js +0 -19
- package/src/tools/git-misc.js +0 -21
- package/src/tools/git-orgs.js +0 -26
- package/src/tools/git-packages.js +0 -12
- package/src/tools/git-raw.js +0 -14
- package/src/tools/git-releases.js +0 -17
- package/src/tools/git-repos.js +0 -60
- package/src/tools/git-search.js +0 -18
- package/src/tools/git-user.js +0 -26
- package/src/tools/schema.js +0 -3
- package/src/utils/fs.js +0 -29
- package/src/utils/project.js +0 -7
- package/tests/errors.js +0 -26
- package/tests/full_suite.js +0 -98
- package/tests/run.js +0 -50
package/README.md
CHANGED
|
@@ -1,47 +1,39 @@
|
|
|
1
|
-
# @andrebuzeli/git-mcp
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
- `git-sync`: mirror (push simultâneo para Gitea e GitHub)
|
|
41
|
-
- `git-issues`: create
|
|
42
|
-
- `git-releases`: create
|
|
43
|
-
|
|
44
|
-
## Notas
|
|
45
|
-
|
|
46
|
-
- Tokens nunca são persistidos, somente lidos de variáveis de ambiente
|
|
47
|
-
- Operações remotas usam chamadas paralelas com relatório agregado
|
|
1
|
+
# @andrebuzeli/git-mcp
|
|
2
|
+
|
|
3
|
+
Servidor MCP (Model Context Protocol) para operações Git locais sem git instalado, com sincronização paralela para GitHub e Gitea.
|
|
4
|
+
|
|
5
|
+
## Configuração MCP
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"mcpServers": {
|
|
10
|
+
"git-mcp": {
|
|
11
|
+
"command": "npx",
|
|
12
|
+
"args": ["@andrebuzeli/git-mcp@latest"],
|
|
13
|
+
"env": {
|
|
14
|
+
"GITEA_URL": "https://seu-gitea",
|
|
15
|
+
"GITEA_TOKEN": "...",
|
|
16
|
+
"GITHUB_TOKEN": "..."
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Tools
|
|
24
|
+
|
|
25
|
+
- git-workflow: init, status, add, remove, commit, ensure-remotes, push
|
|
26
|
+
- git-remote: list, ensure
|
|
27
|
+
- git-branches: list, create, delete, rename, checkout
|
|
28
|
+
- git-tags: list, create, delete, push
|
|
29
|
+
- git-stash: list, save, apply, pop, drop, clear
|
|
30
|
+
- git-reset: soft, mixed, hard
|
|
31
|
+
- git-config: get, set, unset, list
|
|
32
|
+
- git-ignore: list, create, add, remove
|
|
33
|
+
- git-files: list, read
|
|
34
|
+
- git-history: log
|
|
35
|
+
- git-sync: fetch, pull
|
|
36
|
+
- git-issues: create, list, comment
|
|
37
|
+
- git-pulls: create, list, files
|
|
38
|
+
|
|
39
|
+
Todas as tools exigem `projectPath` e operam com derivação automática do nome do repositório a partir do caminho.
|
package/package.json
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@andrebuzeli/git-mcp",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
},
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@andrebuzeli/git-mcp",
|
|
3
|
+
"version": "12.0.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "MCP server para Git com operações locais e sincronização paralela GitHub/Gitea",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "andrebuzeli",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "src/index.js",
|
|
10
|
+
"bin": {
|
|
11
|
+
"git-mcpv2": "src/index.js"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "echo 'no build'",
|
|
15
|
+
"start": "node src/index.js"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"ajv": "^8.12.0",
|
|
19
|
+
"axios": "^1.7.7",
|
|
20
|
+
"@octokit/rest": "^20.0.0",
|
|
21
|
+
"isomorphic-git": "^1.27.5",
|
|
22
|
+
"@modelcontextprotocol/sdk": "^0.4.0"
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/dist/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/dist/server/stdio.js";
|
|
4
|
+
import Ajv from "ajv";
|
|
5
|
+
import { asToolError } from "./utils/errors.js";
|
|
6
|
+
import { ProviderManager } from "./providers/providerManager.js";
|
|
7
|
+
import { GitAdapter } from "./utils/gitAdapter.js";
|
|
8
|
+
import { createGitWorkflowTool } from "./tools/git-workflow.js";
|
|
9
|
+
import { createGitRemoteTool } from "./tools/git-remote.js";
|
|
10
|
+
import { createGitBranchesTool } from "./tools/git-branches.js";
|
|
11
|
+
import { createGitTagsTool } from "./tools/git-tags.js";
|
|
12
|
+
import { createGitStashTool } from "./tools/git-stash.js";
|
|
13
|
+
import { createGitResetTool } from "./tools/git-reset.js";
|
|
14
|
+
import { createGitConfigTool } from "./tools/git-config.js";
|
|
15
|
+
import { createGitIgnoreTool } from "./tools/git-ignore.js";
|
|
16
|
+
import { createGitFilesTool } from "./tools/git-files.js";
|
|
17
|
+
import { createGitHistoryTool } from "./tools/git-history.js";
|
|
18
|
+
import { createGitSyncTool } from "./tools/git-sync.js";
|
|
19
|
+
import { createGitIssuesTool } from "./tools/git-issues.js";
|
|
20
|
+
import { createGitPullsTool } from "./tools/git-pulls.js";
|
|
21
|
+
|
|
22
|
+
const transport = new StdioServerTransport();
|
|
23
|
+
const server = new Server({ name: "git-mcpv2", version: "0.0.0" });
|
|
24
|
+
server.connect(transport);
|
|
25
|
+
|
|
26
|
+
const pm = new ProviderManager();
|
|
27
|
+
const git = new GitAdapter(pm);
|
|
28
|
+
const ajv = new Ajv({ allErrors: true });
|
|
29
|
+
|
|
30
|
+
const tools = [
|
|
31
|
+
createGitWorkflowTool(pm, git),
|
|
32
|
+
createGitRemoteTool(pm, git),
|
|
33
|
+
createGitBranchesTool(git),
|
|
34
|
+
createGitTagsTool(git),
|
|
35
|
+
createGitStashTool(git),
|
|
36
|
+
createGitResetTool(git),
|
|
37
|
+
createGitConfigTool(git),
|
|
38
|
+
createGitIgnoreTool(git),
|
|
39
|
+
createGitFilesTool(git),
|
|
40
|
+
createGitHistoryTool(git),
|
|
41
|
+
createGitSyncTool(git),
|
|
42
|
+
createGitIssuesTool(pm),
|
|
43
|
+
createGitPullsTool(pm),
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
function listToolsResult() {
|
|
47
|
+
return {
|
|
48
|
+
nextCursor: undefined,
|
|
49
|
+
tools: tools.map(t => ({ name: t.name, description: t.description, inputSchema: t.inputSchema })),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
server.setRequestHandler(
|
|
54
|
+
// list tools
|
|
55
|
+
(await import("@modelcontextprotocol/sdk/dist/types.js")).ListToolsRequestSchema,
|
|
56
|
+
async () => listToolsResult()
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
server.setRequestHandler(
|
|
60
|
+
// call tool
|
|
61
|
+
(await import("@modelcontextprotocol/sdk/dist/types.js")).CallToolRequestSchema,
|
|
62
|
+
async (req) => {
|
|
63
|
+
const name = req.params?.name || "";
|
|
64
|
+
const args = req.params?.arguments || {};
|
|
65
|
+
const progressToken = req.params?._meta?.progressToken;
|
|
66
|
+
const tool = tools.find(t => t.name === name);
|
|
67
|
+
if (!tool) return { content: [{ type: "text", text: `Tool não encontrada: ${name}` }], isError: true };
|
|
68
|
+
try {
|
|
69
|
+
if (progressToken) {
|
|
70
|
+
await server.notification({ method: "notifications/progress", params: { progressToken, progress: 0 } });
|
|
71
|
+
}
|
|
72
|
+
const result = await tool.handle(args);
|
|
73
|
+
if (progressToken) {
|
|
74
|
+
await server.notification({ method: "notifications/progress", params: { progressToken, progress: 100 } });
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
} catch (e) {
|
|
78
|
+
return asToolError(e.code || "ERROR", e.message || String(e));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
// Keep process alive
|
|
84
|
+
process.stdin.resume();
|
|
@@ -0,0 +1,107 @@
|
|
|
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
|
+
}
|
|
16
|
+
|
|
17
|
+
async getGitHubOwner() {
|
|
18
|
+
if (!this.github) return "";
|
|
19
|
+
const now = Date.now();
|
|
20
|
+
if (this._githubOwner && now - this._ownerFetchedAt < 5 * 60 * 1000) return this._githubOwner;
|
|
21
|
+
try {
|
|
22
|
+
const me = await this.github.rest.users.getAuthenticated();
|
|
23
|
+
this._githubOwner = me.data.login || "";
|
|
24
|
+
this._ownerFetchedAt = now;
|
|
25
|
+
return this._githubOwner;
|
|
26
|
+
} catch {
|
|
27
|
+
return "";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async getGiteaOwner() {
|
|
32
|
+
if (!this.giteaUrl || !this.giteaToken) return "";
|
|
33
|
+
const now = Date.now();
|
|
34
|
+
if (this._giteaOwner && now - this._ownerFetchedAt < 5 * 60 * 1000) return this._giteaOwner;
|
|
35
|
+
try {
|
|
36
|
+
const r = await axios.get(`${this.giteaUrl}/api/v1/user`, {
|
|
37
|
+
headers: { Authorization: `token ${this.giteaToken}` },
|
|
38
|
+
timeout: 8000,
|
|
39
|
+
});
|
|
40
|
+
const d = r.data || {};
|
|
41
|
+
this._giteaOwner = d.login || d.username || "";
|
|
42
|
+
this._ownerFetchedAt = now;
|
|
43
|
+
return this._giteaOwner;
|
|
44
|
+
} catch {
|
|
45
|
+
return "";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async ensureRepos({ repoName, createIfMissing = true, description = "Managed by git-mcpv2" }) {
|
|
50
|
+
const results = { github: null, gitea: null };
|
|
51
|
+
// GitHub
|
|
52
|
+
if (this.github) {
|
|
53
|
+
const owner = await this.getGitHubOwner();
|
|
54
|
+
if (owner) {
|
|
55
|
+
try {
|
|
56
|
+
const full = `${owner}/${repoName}`;
|
|
57
|
+
try {
|
|
58
|
+
await this.github.rest.repos.get({ owner, repo: repoName });
|
|
59
|
+
results.github = { ok: true, repo: full, created: false };
|
|
60
|
+
} catch {
|
|
61
|
+
if (createIfMissing) {
|
|
62
|
+
const cr = await this.github.rest.repos.createForAuthenticatedUser({
|
|
63
|
+
name: repoName,
|
|
64
|
+
description,
|
|
65
|
+
private: false,
|
|
66
|
+
auto_init: true,
|
|
67
|
+
});
|
|
68
|
+
results.github = { ok: true, repo: cr.data.full_name, created: true };
|
|
69
|
+
} else {
|
|
70
|
+
results.github = { ok: false, error: "missing" };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
} catch (e) {
|
|
74
|
+
results.github = { ok: false, error: String(e?.message || e) };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Gitea
|
|
79
|
+
if (this.giteaUrl && this.giteaToken) {
|
|
80
|
+
const owner = await this.getGiteaOwner();
|
|
81
|
+
if (owner) {
|
|
82
|
+
try {
|
|
83
|
+
const base = this.giteaUrl.replace(/\/$/, "");
|
|
84
|
+
const getRepo = await axios.get(`${base}/api/v1/repos/${owner}/${repoName}`,
|
|
85
|
+
{ headers: { Authorization: `token ${this.giteaToken}` }, timeout: 8000 });
|
|
86
|
+
if (getRepo.status === 200) {
|
|
87
|
+
results.gitea = { ok: true, repo: `${owner}/${repoName}`, created: false };
|
|
88
|
+
}
|
|
89
|
+
} catch (e) {
|
|
90
|
+
if (createIfMissing) {
|
|
91
|
+
try {
|
|
92
|
+
const cr = await axios.post(`${this.giteaUrl}/api/v1/user/repos`,
|
|
93
|
+
{ name: repoName, description, private: false, auto_init: true },
|
|
94
|
+
{ headers: { Authorization: `token ${this.giteaToken}` }, timeout: 8000 });
|
|
95
|
+
results.gitea = { ok: true, repo: `${cr.data?.owner?.login || owner}/${repoName}`, created: true };
|
|
96
|
+
} catch (err) {
|
|
97
|
+
results.gitea = { ok: false, error: String(err?.message || err) };
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
results.gitea = { ok: false, error: "missing" };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return results;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import Ajv from "ajv";
|
|
2
|
+
import { asToolError, asToolResult } from "../utils/errors.js";
|
|
3
|
+
|
|
4
|
+
const ajv = new Ajv({ allErrors: true });
|
|
5
|
+
|
|
6
|
+
export function createGitBranchesTool(git) {
|
|
7
|
+
const inputSchema = {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
projectPath: { type: "string" },
|
|
11
|
+
action: { type: "string", enum: ["list", "create", "delete", "rename", "checkout"] },
|
|
12
|
+
branch: { type: "string" },
|
|
13
|
+
newBranch: { type: "string" },
|
|
14
|
+
force: { type: "boolean" }
|
|
15
|
+
},
|
|
16
|
+
required: ["projectPath", "action"],
|
|
17
|
+
additionalProperties: true
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
async function handle(args) {
|
|
21
|
+
const validate = ajv.compile(inputSchema);
|
|
22
|
+
if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
|
|
23
|
+
const { projectPath, action } = args;
|
|
24
|
+
try {
|
|
25
|
+
if (action === "list") {
|
|
26
|
+
const local = await git.listBranches(projectPath, false);
|
|
27
|
+
const remote = await git.listBranches(projectPath, true).catch(() => []);
|
|
28
|
+
return asToolResult({ local, remote });
|
|
29
|
+
}
|
|
30
|
+
if (action === "create") {
|
|
31
|
+
const ref = args.branch;
|
|
32
|
+
if (!ref) return asToolError("VALIDATION_ERROR", "branch é obrigatório");
|
|
33
|
+
await git.createBranch(projectPath, ref);
|
|
34
|
+
return asToolResult({ success: true, branch: ref });
|
|
35
|
+
}
|
|
36
|
+
if (action === "delete") {
|
|
37
|
+
const ref = args.branch;
|
|
38
|
+
if (!ref) return asToolError("VALIDATION_ERROR", "branch é obrigatório");
|
|
39
|
+
await git.deleteBranch(projectPath, ref, !!args.force);
|
|
40
|
+
return asToolResult({ success: true, branch: ref });
|
|
41
|
+
}
|
|
42
|
+
if (action === "rename") {
|
|
43
|
+
const oldName = args.branch;
|
|
44
|
+
const newName = args.newBranch;
|
|
45
|
+
if (!oldName || !newName) return asToolError("VALIDATION_ERROR", "branch e newBranch são obrigatórios");
|
|
46
|
+
await git.renameBranch(projectPath, oldName, newName);
|
|
47
|
+
return asToolResult({ success: true, from: oldName, to: newName });
|
|
48
|
+
}
|
|
49
|
+
if (action === "checkout") {
|
|
50
|
+
const ref = args.branch;
|
|
51
|
+
if (!ref) return asToolError("VALIDATION_ERROR", "branch é obrigatório");
|
|
52
|
+
await git.checkout(projectPath, ref);
|
|
53
|
+
return asToolResult({ success: true, branch: ref });
|
|
54
|
+
}
|
|
55
|
+
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}`);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
return asToolError(e.code || "ERROR", e.message || String(e));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return { name: "git-branches", description: "Gerencia branches", inputSchema, handle };
|
|
62
|
+
}
|
|
63
|
+
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import Ajv from "ajv";
|
|
2
|
+
import { asToolError, asToolResult } from "../utils/errors.js";
|
|
3
|
+
|
|
4
|
+
const ajv = new Ajv({ allErrors: true });
|
|
5
|
+
|
|
6
|
+
export function createGitConfigTool(git) {
|
|
7
|
+
const inputSchema = {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
projectPath: { type: "string" },
|
|
11
|
+
action: { type: "string", enum: ["get", "set", "unset", "list"] },
|
|
12
|
+
key: { type: "string" },
|
|
13
|
+
value: { type: "string" },
|
|
14
|
+
scope: { type: "string", enum: ["local", "global", "system"], default: "local" }
|
|
15
|
+
},
|
|
16
|
+
required: ["projectPath", "action"],
|
|
17
|
+
additionalProperties: true
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
async function handle(args) {
|
|
21
|
+
const validate = ajv.compile(inputSchema);
|
|
22
|
+
if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
|
|
23
|
+
const { projectPath, action } = args;
|
|
24
|
+
const scope = args.scope || "local";
|
|
25
|
+
try {
|
|
26
|
+
if (action === "get") {
|
|
27
|
+
if (!args.key) return asToolError("VALIDATION_ERROR", "key é obrigatório");
|
|
28
|
+
const val = await git.getConfig(projectPath, args.key, scope);
|
|
29
|
+
return asToolResult({ key: args.key, value: val });
|
|
30
|
+
}
|
|
31
|
+
if (action === "set") {
|
|
32
|
+
if (!args.key) return asToolError("VALIDATION_ERROR", "key é obrigatório");
|
|
33
|
+
await git.setConfig(projectPath, args.key, args.value ?? "", scope);
|
|
34
|
+
return asToolResult({ success: true, key: args.key, value: args.value ?? "" });
|
|
35
|
+
}
|
|
36
|
+
if (action === "unset") {
|
|
37
|
+
if (!args.key) return asToolError("VALIDATION_ERROR", "key é obrigatório");
|
|
38
|
+
await git.unsetConfig(projectPath, args.key, scope);
|
|
39
|
+
return asToolResult({ success: true, key: args.key });
|
|
40
|
+
}
|
|
41
|
+
if (action === "list") {
|
|
42
|
+
const items = await git.listConfig(projectPath, scope);
|
|
43
|
+
return asToolResult({ scope, items });
|
|
44
|
+
}
|
|
45
|
+
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}`);
|
|
46
|
+
} catch (e) {
|
|
47
|
+
return asToolError(e.code || "ERROR", e.message || String(e));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return { name: "git-config", description: "Gerencia configurações Git (local/global/system)", inputSchema, handle };
|
|
52
|
+
}
|
|
53
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import Ajv from "ajv";
|
|
2
|
+
import { asToolError, asToolResult } from "../utils/errors.js";
|
|
3
|
+
|
|
4
|
+
const ajv = new Ajv({ allErrors: true });
|
|
5
|
+
|
|
6
|
+
export function createGitFilesTool(git) {
|
|
7
|
+
const inputSchema = {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
projectPath: { type: "string" },
|
|
11
|
+
action: { type: "string", enum: ["list", "read"] },
|
|
12
|
+
filepath: { type: "string" },
|
|
13
|
+
ref: { type: "string" }
|
|
14
|
+
},
|
|
15
|
+
required: ["projectPath", "action"],
|
|
16
|
+
additionalProperties: true
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
async function handle(args) {
|
|
20
|
+
const validate = ajv.compile(inputSchema);
|
|
21
|
+
if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
|
|
22
|
+
const { projectPath, action } = args;
|
|
23
|
+
try {
|
|
24
|
+
if (action === "list") {
|
|
25
|
+
const files = await git.listFiles(projectPath, args.ref || "HEAD");
|
|
26
|
+
return asToolResult({ files });
|
|
27
|
+
}
|
|
28
|
+
if (action === "read") {
|
|
29
|
+
if (!args.filepath) return asToolError("VALIDATION_ERROR", "filepath é obrigatório");
|
|
30
|
+
const text = await git.readFile(projectPath, args.filepath, args.ref || "HEAD");
|
|
31
|
+
return { content: [{ type: "text", text }], isError: false };
|
|
32
|
+
}
|
|
33
|
+
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}`);
|
|
34
|
+
} catch (e) {
|
|
35
|
+
return asToolError(e.code || "ERROR", e.message || String(e));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return { name: "git-files", description: "Lista e lê arquivos do repo", inputSchema, handle };
|
|
40
|
+
}
|
|
41
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import Ajv from "ajv";
|
|
2
|
+
import { asToolError, asToolResult } from "../utils/errors.js";
|
|
3
|
+
|
|
4
|
+
const ajv = new Ajv({ allErrors: true });
|
|
5
|
+
|
|
6
|
+
export function createGitHistoryTool(git) {
|
|
7
|
+
const inputSchema = {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
projectPath: { type: "string" },
|
|
11
|
+
action: { type: "string", enum: ["log"] },
|
|
12
|
+
ref: { type: "string" },
|
|
13
|
+
maxCount: { type: "number" }
|
|
14
|
+
},
|
|
15
|
+
required: ["projectPath", "action"],
|
|
16
|
+
additionalProperties: true
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
async function handle(args) {
|
|
20
|
+
const validate = ajv.compile(inputSchema);
|
|
21
|
+
if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
|
|
22
|
+
const { projectPath, action } = args;
|
|
23
|
+
try {
|
|
24
|
+
if (action === "log") {
|
|
25
|
+
const items = await git.log(projectPath, { ref: args.ref || "HEAD", maxCount: args.maxCount || 50 });
|
|
26
|
+
return asToolResult({ commits: items });
|
|
27
|
+
}
|
|
28
|
+
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}`);
|
|
29
|
+
} catch (e) {
|
|
30
|
+
return asToolError(e.code || "ERROR", e.message || String(e));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return { name: "git-history", description: "Histórico de commits", inputSchema, handle };
|
|
35
|
+
}
|
|
36
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import Ajv from "ajv";
|
|
2
|
+
import { asToolError, asToolResult } from "../utils/errors.js";
|
|
3
|
+
|
|
4
|
+
const ajv = new Ajv({ allErrors: true });
|
|
5
|
+
|
|
6
|
+
export function createGitIgnoreTool(git) {
|
|
7
|
+
const inputSchema = {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
projectPath: { type: "string" },
|
|
11
|
+
action: { type: "string", enum: ["list", "create", "add", "remove"] },
|
|
12
|
+
patterns: { type: "array", items: { type: "string" } }
|
|
13
|
+
},
|
|
14
|
+
required: ["projectPath", "action"],
|
|
15
|
+
additionalProperties: true
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
async function handle(args) {
|
|
19
|
+
const validate = ajv.compile(inputSchema);
|
|
20
|
+
if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
|
|
21
|
+
const { projectPath, action } = args;
|
|
22
|
+
const patterns = Array.isArray(args.patterns) ? args.patterns : [];
|
|
23
|
+
try {
|
|
24
|
+
if (action === "list") {
|
|
25
|
+
const items = await git.listGitignore(projectPath);
|
|
26
|
+
return asToolResult({ items });
|
|
27
|
+
}
|
|
28
|
+
if (action === "create") {
|
|
29
|
+
await git.createGitignore(projectPath, patterns);
|
|
30
|
+
return asToolResult({ success: true });
|
|
31
|
+
}
|
|
32
|
+
if (action === "add") {
|
|
33
|
+
await git.addToGitignore(projectPath, patterns);
|
|
34
|
+
return asToolResult({ success: true });
|
|
35
|
+
}
|
|
36
|
+
if (action === "remove") {
|
|
37
|
+
await git.removeFromGitignore(projectPath, patterns);
|
|
38
|
+
return asToolResult({ success: true });
|
|
39
|
+
}
|
|
40
|
+
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${action}`);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
return asToolError(e.code || "ERROR", e.message || String(e));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return { name: "git-ignore", description: "Gerencia .gitignore", inputSchema, handle };
|
|
47
|
+
}
|
|
48
|
+
|
package/src/tools/git-issues.js
CHANGED
|
@@ -1,12 +1,58 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import Ajv from "ajv";
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
import { asToolError, asToolResult } from "../utils/errors.js";
|
|
4
|
+
import { getRepoNameFromPath } from "../utils/repoHelpers.js";
|
|
5
|
+
import { runBoth } from "../utils/providerExec.js";
|
|
6
|
+
|
|
7
|
+
const ajv = new Ajv({ allErrors: true });
|
|
8
|
+
|
|
9
|
+
export function createGitIssuesTool(pm) {
|
|
10
|
+
const inputSchema = {
|
|
11
|
+
type: "object",
|
|
12
|
+
properties: {
|
|
13
|
+
projectPath: { type: "string" },
|
|
14
|
+
action: { type: "string", enum: ["create", "list", "comment"] },
|
|
15
|
+
title: { type: "string" },
|
|
16
|
+
body: { type: "string" },
|
|
17
|
+
number: { type: "number" }
|
|
18
|
+
},
|
|
19
|
+
required: ["projectPath", "action"],
|
|
20
|
+
additionalProperties: true
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
async function handle(args) {
|
|
24
|
+
const validate = ajv.compile(inputSchema);
|
|
25
|
+
if (!validate(args || {})) return asToolError("VALIDATION_ERROR", "Parâmetros inválidos", validate.errors);
|
|
26
|
+
const repo = getRepoNameFromPath(args.projectPath);
|
|
27
|
+
try {
|
|
28
|
+
if (args.action === "create") {
|
|
29
|
+
const out = await runBoth(pm, {
|
|
30
|
+
github: async (owner) => { const r = await pm.github.rest.issues.create({ owner, repo, title: args.title || "", body: args.body || "" }); return { ok: true, number: r.data.number }; },
|
|
31
|
+
gitea: async (owner) => { const base = pm.giteaUrl.replace(/\/$/, ""); const r = await axios.post(`${base}/api/v1/repos/${owner}/${repo}/issues`, { title: args.title || "", body: args.body || "" }, { headers: { Authorization: `token ${pm.giteaToken}` } }); return { ok: true, number: r.data?.number }; }
|
|
32
|
+
});
|
|
33
|
+
return asToolResult({ success: !!(out.github?.ok || out.gitea?.ok), providers: out });
|
|
34
|
+
}
|
|
35
|
+
if (args.action === "list") {
|
|
36
|
+
const out = await runBoth(pm, {
|
|
37
|
+
github: async (owner) => { const r = await pm.github.rest.issues.listForRepo({ owner, repo, state: "all" }); return { ok: true, count: r.data.length }; },
|
|
38
|
+
gitea: async (owner) => { const base = pm.giteaUrl.replace(/\/$/, ""); const r = await axios.get(`${base}/api/v1/repos/${owner}/${repo}/issues`, { headers: { Authorization: `token ${pm.giteaToken}` } }); return { ok: true, count: (r.data||[]).length }; }
|
|
39
|
+
});
|
|
40
|
+
return asToolResult({ providers: out });
|
|
41
|
+
}
|
|
42
|
+
if (args.action === "comment") {
|
|
43
|
+
const num = args.number;
|
|
44
|
+
if (!num) return asToolError("VALIDATION_ERROR", "number é obrigatório");
|
|
45
|
+
const out = await runBoth(pm, {
|
|
46
|
+
github: async (owner) => { await pm.github.rest.issues.createComment({ owner, repo, issue_number: num, body: args.body || "" }); return { ok: true }; },
|
|
47
|
+
gitea: async (owner) => { const base = pm.giteaUrl.replace(/\/$/, ""); await axios.post(`${base}/api/v1/repos/${owner}/${repo}/issues/${num}/comments`, { body: args.body || "" }, { headers: { Authorization: `token ${pm.giteaToken}` } }); return { ok: true }; }
|
|
48
|
+
});
|
|
49
|
+
return asToolResult({ success: !!(out.github?.ok || out.gitea?.ok), providers: out });
|
|
50
|
+
}
|
|
51
|
+
return asToolError("VALIDATION_ERROR", `Ação não suportada: ${args.action}`);
|
|
52
|
+
} catch (e) {
|
|
53
|
+
return asToolError(e.code || "ERROR", e.message || String(e));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return { name: "git-issues", description: "Issues em paralelo (GitHub + Gitea)", inputSchema, handle };
|
|
58
|
+
}
|