@andrebuzeli/git-mcp 7.0.3 → 7.2.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/dist/index.js +13 -11
- package/dist/tools/gitArchive.d.ts +51 -1
- package/dist/tools/gitArchive.js +73 -5
- package/dist/tools/gitBackup.d.ts +75 -1
- package/dist/tools/gitBackup.js +100 -13
- package/dist/tools/gitIgnore.d.ts +191 -0
- package/dist/tools/gitIgnore.js +354 -0
- package/dist/tools/gitPackages.d.ts +19 -1
- package/dist/tools/gitPackages.js +38 -3
- package/dist/tools/gitUpdate.js +98 -5
- package/package.json +1 -1
- package/dist/utils/dualExecution.d.ts +0 -49
- package/dist/utils/dualExecution.js +0 -96
package/dist/index.js
CHANGED
|
@@ -4,9 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
7
|
+
const index_1 = require("@modelcontextprotocol/sdk/server/index");
|
|
8
|
+
const stdio_1 = require("@modelcontextprotocol/sdk/server/stdio");
|
|
9
|
+
const types_1 = require("@modelcontextprotocol/sdk/types");
|
|
10
10
|
const providerManager_1 = require("./providers/providerManager");
|
|
11
11
|
const config_1 = require("./config");
|
|
12
12
|
const gitFiles_1 = require("./tools/gitFiles");
|
|
@@ -30,6 +30,7 @@ const gitUpload_1 = require("./tools/gitUpload");
|
|
|
30
30
|
const gitUpdate_1 = require("./tools/gitUpdate");
|
|
31
31
|
const gitHistory_1 = require("./tools/gitHistory");
|
|
32
32
|
const gitFix_tool_1 = require("./tools/gitFix.tool");
|
|
33
|
+
const gitIgnore_1 = require("./tools/gitIgnore");
|
|
33
34
|
const toolsGuide_1 = __importDefault(require("./resources/toolsGuide"));
|
|
34
35
|
async function main() {
|
|
35
36
|
// Load optional mcp.json configuration (will populate process.env if values present)
|
|
@@ -41,7 +42,7 @@ async function main() {
|
|
|
41
42
|
if (process.env.DEBUG) {
|
|
42
43
|
console.error('Provider validation:', JSON.stringify(validation, null, 2));
|
|
43
44
|
}
|
|
44
|
-
// Register all
|
|
45
|
+
// Register all 22 Git tools
|
|
45
46
|
const tools = [
|
|
46
47
|
new gitWorkflow_1.GitWorkflowTool(),
|
|
47
48
|
new gitFiles_1.GitFilesTool(),
|
|
@@ -64,6 +65,7 @@ async function main() {
|
|
|
64
65
|
new gitUpdate_1.GitUpdateTool(),
|
|
65
66
|
new gitHistory_1.GitHistoryTool(),
|
|
66
67
|
new gitFix_tool_1.GitFixTool(),
|
|
68
|
+
new gitIgnore_1.GitIgnoreTool(),
|
|
67
69
|
];
|
|
68
70
|
// Register resources
|
|
69
71
|
const resources = [
|
|
@@ -75,12 +77,12 @@ async function main() {
|
|
|
75
77
|
console.error(`Registered ${resources.length} resource(s)`);
|
|
76
78
|
}
|
|
77
79
|
// Create MCP Server with STDIO transport
|
|
78
|
-
const server = new
|
|
80
|
+
const server = new index_1.Server({
|
|
79
81
|
name: '@andrebuzeli/git-mcp',
|
|
80
|
-
version: '
|
|
82
|
+
version: '7.2.0',
|
|
81
83
|
});
|
|
82
84
|
// Register tool list handler
|
|
83
|
-
server.setRequestHandler(
|
|
85
|
+
server.setRequestHandler(types_1.ListToolsRequestSchema, async () => {
|
|
84
86
|
return {
|
|
85
87
|
tools: tools.map(tool => ({
|
|
86
88
|
name: tool.name,
|
|
@@ -94,7 +96,7 @@ async function main() {
|
|
|
94
96
|
};
|
|
95
97
|
});
|
|
96
98
|
// Register tool execution handler
|
|
97
|
-
server.setRequestHandler(
|
|
99
|
+
server.setRequestHandler(types_1.CallToolRequestSchema, async (request) => {
|
|
98
100
|
const toolName = request.params.name;
|
|
99
101
|
const tool = tools.find(t => t.name === toolName);
|
|
100
102
|
if (!tool) {
|
|
@@ -124,7 +126,7 @@ async function main() {
|
|
|
124
126
|
}
|
|
125
127
|
});
|
|
126
128
|
// Register resource list handler
|
|
127
|
-
server.setRequestHandler(
|
|
129
|
+
server.setRequestHandler(types_1.ListResourcesRequestSchema, async () => {
|
|
128
130
|
return {
|
|
129
131
|
resources: resources.map(resource => ({
|
|
130
132
|
uri: resource.uri,
|
|
@@ -135,7 +137,7 @@ async function main() {
|
|
|
135
137
|
};
|
|
136
138
|
});
|
|
137
139
|
// Register resource read handler
|
|
138
|
-
server.setRequestHandler(
|
|
140
|
+
server.setRequestHandler(types_1.ReadResourceRequestSchema, async (request) => {
|
|
139
141
|
const uri = request.params.uri;
|
|
140
142
|
const resource = resources.find(r => r.uri === uri);
|
|
141
143
|
if (!resource) {
|
|
@@ -152,7 +154,7 @@ async function main() {
|
|
|
152
154
|
};
|
|
153
155
|
});
|
|
154
156
|
// Start server with STDIO transport
|
|
155
|
-
const transport = new
|
|
157
|
+
const transport = new stdio_1.StdioServerTransport();
|
|
156
158
|
await server.connect(transport);
|
|
157
159
|
// Only log in debug mode
|
|
158
160
|
if (process.env.DEBUG) {
|
|
@@ -2,5 +2,55 @@ import { Tool, MCPContext } from '../types';
|
|
|
2
2
|
export declare class GitArchiveTool implements Tool {
|
|
3
3
|
name: string;
|
|
4
4
|
description: string;
|
|
5
|
-
handle(params: Record<string, any>, ctx: MCPContext): Promise<
|
|
5
|
+
handle(params: Record<string, any>, ctx: MCPContext): Promise<{
|
|
6
|
+
success: boolean;
|
|
7
|
+
archivePath: string;
|
|
8
|
+
message: string;
|
|
9
|
+
archives?: undefined;
|
|
10
|
+
count?: undefined;
|
|
11
|
+
archivesDir?: undefined;
|
|
12
|
+
valid?: undefined;
|
|
13
|
+
archive?: undefined;
|
|
14
|
+
error?: undefined;
|
|
15
|
+
} | {
|
|
16
|
+
success: boolean;
|
|
17
|
+
archives: string[];
|
|
18
|
+
count: number;
|
|
19
|
+
archivesDir: any;
|
|
20
|
+
archivePath?: undefined;
|
|
21
|
+
message?: undefined;
|
|
22
|
+
valid?: undefined;
|
|
23
|
+
archive?: undefined;
|
|
24
|
+
error?: undefined;
|
|
25
|
+
} | {
|
|
26
|
+
success: boolean;
|
|
27
|
+
archives: never[];
|
|
28
|
+
count: number;
|
|
29
|
+
message: string;
|
|
30
|
+
archivePath?: undefined;
|
|
31
|
+
archivesDir?: undefined;
|
|
32
|
+
valid?: undefined;
|
|
33
|
+
archive?: undefined;
|
|
34
|
+
error?: undefined;
|
|
35
|
+
} | {
|
|
36
|
+
success: boolean;
|
|
37
|
+
valid: boolean;
|
|
38
|
+
archive: any;
|
|
39
|
+
message: string;
|
|
40
|
+
archivePath?: undefined;
|
|
41
|
+
archives?: undefined;
|
|
42
|
+
count?: undefined;
|
|
43
|
+
archivesDir?: undefined;
|
|
44
|
+
error?: undefined;
|
|
45
|
+
} | {
|
|
46
|
+
success: boolean;
|
|
47
|
+
valid: boolean;
|
|
48
|
+
error: any;
|
|
49
|
+
archivePath?: undefined;
|
|
50
|
+
message?: undefined;
|
|
51
|
+
archives?: undefined;
|
|
52
|
+
count?: undefined;
|
|
53
|
+
archivesDir?: undefined;
|
|
54
|
+
archive?: undefined;
|
|
55
|
+
}>;
|
|
6
56
|
}
|
package/dist/tools/gitArchive.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.GitArchiveTool = void 0;
|
|
7
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
4
9
|
const errors_1 = require("../utils/errors");
|
|
5
10
|
class GitArchiveTool {
|
|
6
11
|
constructor() {
|
|
7
12
|
this.name = 'git-archive';
|
|
8
|
-
this.description = 'Repository archive operations
|
|
13
|
+
this.description = 'Repository archive operations';
|
|
9
14
|
}
|
|
10
15
|
async handle(params, ctx) {
|
|
11
16
|
const action = params.action;
|
|
@@ -14,11 +19,74 @@ class GitArchiveTool {
|
|
|
14
19
|
throw new errors_1.MCPError('VALIDATION_ERROR', 'action and projectPath are required');
|
|
15
20
|
}
|
|
16
21
|
switch (action) {
|
|
17
|
-
case 'create':
|
|
22
|
+
case 'create': {
|
|
23
|
+
const archiveName = params.archiveName || `archive-${Date.now()}.json`;
|
|
24
|
+
const outputPath = params.outputPath || path_1.default.join(projectPath, '.git-archives');
|
|
25
|
+
try {
|
|
26
|
+
// Criar diretório de archives se não existir
|
|
27
|
+
await promises_1.default.mkdir(outputPath, { recursive: true });
|
|
28
|
+
const archivePath = path_1.default.join(outputPath, archiveName);
|
|
29
|
+
const archiveData = {
|
|
30
|
+
projectPath,
|
|
31
|
+
timestamp: new Date().toISOString(),
|
|
32
|
+
type: 'git-archive',
|
|
33
|
+
};
|
|
34
|
+
await promises_1.default.writeFile(archivePath, JSON.stringify(archiveData, null, 2));
|
|
35
|
+
return {
|
|
36
|
+
success: true,
|
|
37
|
+
archivePath,
|
|
38
|
+
message: 'Archive created successfully',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
throw new errors_1.MCPError('FILE_ERROR', `Failed to create archive: ${error.message}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
case 'list': {
|
|
46
|
+
const archivesDir = params.archivesDir || path_1.default.join(projectPath, '.git-archives');
|
|
47
|
+
try {
|
|
48
|
+
const files = await promises_1.default.readdir(archivesDir);
|
|
49
|
+
const archives = files.filter(f => f.endsWith('.json'));
|
|
50
|
+
return {
|
|
51
|
+
success: true,
|
|
52
|
+
archives,
|
|
53
|
+
count: archives.length,
|
|
54
|
+
archivesDir,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
return {
|
|
59
|
+
success: true,
|
|
60
|
+
archives: [],
|
|
61
|
+
count: 0,
|
|
62
|
+
message: 'No archives directory found',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
case 'verify': {
|
|
67
|
+
const archivePath = params.archivePath;
|
|
68
|
+
if (!archivePath)
|
|
69
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'archivePath is required');
|
|
70
|
+
try {
|
|
71
|
+
const content = await promises_1.default.readFile(archivePath, 'utf-8');
|
|
72
|
+
const archive = JSON.parse(content);
|
|
73
|
+
return {
|
|
74
|
+
success: true,
|
|
75
|
+
valid: true,
|
|
76
|
+
archive,
|
|
77
|
+
message: 'Archive is valid',
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
return {
|
|
82
|
+
success: false,
|
|
83
|
+
valid: false,
|
|
84
|
+
error: error.message,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
18
88
|
case 'extract':
|
|
19
|
-
|
|
20
|
-
case 'verify':
|
|
21
|
-
throw new errors_1.MCPError('NOT_IMPLEMENTED', `Action ${action} not yet fully implemented`);
|
|
89
|
+
throw new errors_1.MCPError('NOT_IMPLEMENTED', 'Extract not yet fully implemented');
|
|
22
90
|
default:
|
|
23
91
|
throw new errors_1.MCPError('VALIDATION_ERROR', `Unsupported action: ${action}`);
|
|
24
92
|
}
|
|
@@ -4,7 +4,81 @@ export declare class GitBackupTool implements Tool {
|
|
|
4
4
|
description: string;
|
|
5
5
|
handle(params: Record<string, any>, ctx: MCPContext): Promise<{
|
|
6
6
|
success: boolean;
|
|
7
|
-
backupPath:
|
|
7
|
+
backupPath: string;
|
|
8
|
+
backupData: {
|
|
9
|
+
projectPath: any;
|
|
10
|
+
timestamp: string;
|
|
11
|
+
branch: string | null;
|
|
12
|
+
lastCommit: string | undefined;
|
|
13
|
+
files: number;
|
|
14
|
+
};
|
|
8
15
|
message: string;
|
|
16
|
+
backups?: undefined;
|
|
17
|
+
count?: undefined;
|
|
18
|
+
backupDir?: undefined;
|
|
19
|
+
valid?: undefined;
|
|
20
|
+
backup?: undefined;
|
|
21
|
+
error?: undefined;
|
|
22
|
+
note?: undefined;
|
|
23
|
+
} | {
|
|
24
|
+
success: boolean;
|
|
25
|
+
backups: any[];
|
|
26
|
+
count: number;
|
|
27
|
+
backupDir: any;
|
|
28
|
+
backupPath?: undefined;
|
|
29
|
+
backupData?: undefined;
|
|
30
|
+
message?: undefined;
|
|
31
|
+
valid?: undefined;
|
|
32
|
+
backup?: undefined;
|
|
33
|
+
error?: undefined;
|
|
34
|
+
note?: undefined;
|
|
35
|
+
} | {
|
|
36
|
+
success: boolean;
|
|
37
|
+
backups: never[];
|
|
38
|
+
count: number;
|
|
39
|
+
message: string;
|
|
40
|
+
backupPath?: undefined;
|
|
41
|
+
backupData?: undefined;
|
|
42
|
+
backupDir?: undefined;
|
|
43
|
+
valid?: undefined;
|
|
44
|
+
backup?: undefined;
|
|
45
|
+
error?: undefined;
|
|
46
|
+
note?: undefined;
|
|
47
|
+
} | {
|
|
48
|
+
success: boolean;
|
|
49
|
+
valid: any;
|
|
50
|
+
backup: any;
|
|
51
|
+
message: string;
|
|
52
|
+
backupPath?: undefined;
|
|
53
|
+
backupData?: undefined;
|
|
54
|
+
backups?: undefined;
|
|
55
|
+
count?: undefined;
|
|
56
|
+
backupDir?: undefined;
|
|
57
|
+
error?: undefined;
|
|
58
|
+
note?: undefined;
|
|
59
|
+
} | {
|
|
60
|
+
success: boolean;
|
|
61
|
+
valid: boolean;
|
|
62
|
+
error: any;
|
|
63
|
+
backupPath?: undefined;
|
|
64
|
+
backupData?: undefined;
|
|
65
|
+
message?: undefined;
|
|
66
|
+
backups?: undefined;
|
|
67
|
+
count?: undefined;
|
|
68
|
+
backupDir?: undefined;
|
|
69
|
+
backup?: undefined;
|
|
70
|
+
note?: undefined;
|
|
71
|
+
} | {
|
|
72
|
+
success: boolean;
|
|
73
|
+
message: string;
|
|
74
|
+
backup: any;
|
|
75
|
+
note: string;
|
|
76
|
+
backupPath?: undefined;
|
|
77
|
+
backupData?: undefined;
|
|
78
|
+
backups?: undefined;
|
|
79
|
+
count?: undefined;
|
|
80
|
+
backupDir?: undefined;
|
|
81
|
+
valid?: undefined;
|
|
82
|
+
error?: undefined;
|
|
9
83
|
}>;
|
|
10
84
|
}
|
package/dist/tools/gitBackup.js
CHANGED
|
@@ -5,11 +5,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.GitBackupTool = void 0;
|
|
7
7
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const simple_git_1 = __importDefault(require("simple-git"));
|
|
8
10
|
const errors_1 = require("../utils/errors");
|
|
9
11
|
class GitBackupTool {
|
|
10
12
|
constructor() {
|
|
11
13
|
this.name = 'git-backup';
|
|
12
|
-
this.description = 'Repository backup and restore operations
|
|
14
|
+
this.description = 'Repository backup and restore operations';
|
|
13
15
|
}
|
|
14
16
|
async handle(params, ctx) {
|
|
15
17
|
const action = params.action;
|
|
@@ -19,22 +21,107 @@ class GitBackupTool {
|
|
|
19
21
|
}
|
|
20
22
|
switch (action) {
|
|
21
23
|
case 'backup': {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
const backupDir = params.backupDir || path_1.default.join(projectPath, '.git-backups');
|
|
25
|
+
const backupName = params.backupName || `backup-${Date.now()}.json`;
|
|
26
|
+
try {
|
|
27
|
+
// Criar diretório de backups
|
|
28
|
+
await promises_1.default.mkdir(backupDir, { recursive: true });
|
|
29
|
+
const backupPath = path_1.default.join(backupDir, backupName);
|
|
30
|
+
const git = (0, simple_git_1.default)({ baseDir: projectPath });
|
|
31
|
+
const status = await git.status();
|
|
32
|
+
const log = await git.log({ maxCount: 1 });
|
|
33
|
+
const backupData = {
|
|
34
|
+
projectPath,
|
|
35
|
+
timestamp: new Date().toISOString(),
|
|
36
|
+
branch: status.current,
|
|
37
|
+
lastCommit: log.latest?.hash,
|
|
38
|
+
files: status.files.length,
|
|
39
|
+
};
|
|
40
|
+
await promises_1.default.writeFile(backupPath, JSON.stringify(backupData, null, 2));
|
|
41
|
+
return {
|
|
42
|
+
success: true,
|
|
43
|
+
backupPath,
|
|
44
|
+
backupData,
|
|
45
|
+
message: 'Backup created successfully',
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
throw new errors_1.MCPError('FILE_ERROR', `Failed to create backup: ${error.message}`);
|
|
50
|
+
}
|
|
32
51
|
}
|
|
33
52
|
case 'list': {
|
|
34
|
-
|
|
53
|
+
const backupDir = params.backupDir || path_1.default.join(projectPath, '.git-backups');
|
|
54
|
+
try {
|
|
55
|
+
const files = await promises_1.default.readdir(backupDir);
|
|
56
|
+
const backups = [];
|
|
57
|
+
for (const file of files) {
|
|
58
|
+
if (file.endsWith('.json')) {
|
|
59
|
+
try {
|
|
60
|
+
const content = await promises_1.default.readFile(path_1.default.join(backupDir, file), 'utf-8');
|
|
61
|
+
const backup = JSON.parse(content);
|
|
62
|
+
backups.push({ file, ...backup });
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
// Ignorar arquivos inválidos
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
success: true,
|
|
71
|
+
backups,
|
|
72
|
+
count: backups.length,
|
|
73
|
+
backupDir,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
return {
|
|
78
|
+
success: true,
|
|
79
|
+
backups: [],
|
|
80
|
+
count: 0,
|
|
81
|
+
message: 'No backups directory found',
|
|
82
|
+
};
|
|
83
|
+
}
|
|
35
84
|
}
|
|
36
85
|
case 'verify': {
|
|
37
|
-
|
|
86
|
+
const backupPath = params.backupPath;
|
|
87
|
+
if (!backupPath)
|
|
88
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'backupPath is required');
|
|
89
|
+
try {
|
|
90
|
+
const content = await promises_1.default.readFile(backupPath, 'utf-8');
|
|
91
|
+
const backup = JSON.parse(content);
|
|
92
|
+
const isValid = backup.timestamp && backup.projectPath;
|
|
93
|
+
return {
|
|
94
|
+
success: true,
|
|
95
|
+
valid: isValid,
|
|
96
|
+
backup,
|
|
97
|
+
message: isValid ? 'Backup is valid' : 'Backup is invalid',
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
return {
|
|
102
|
+
success: false,
|
|
103
|
+
valid: false,
|
|
104
|
+
error: error.message,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
case 'restore': {
|
|
109
|
+
const backupPath = params.backupPath;
|
|
110
|
+
if (!backupPath)
|
|
111
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'backupPath is required');
|
|
112
|
+
try {
|
|
113
|
+
const content = await promises_1.default.readFile(backupPath, 'utf-8');
|
|
114
|
+
const backup = JSON.parse(content);
|
|
115
|
+
return {
|
|
116
|
+
success: true,
|
|
117
|
+
message: 'Backup info retrieved - manual restore required',
|
|
118
|
+
backup,
|
|
119
|
+
note: 'Use git commands to restore to the specified commit',
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
throw new errors_1.MCPError('FILE_ERROR', `Failed to read backup: ${error.message}`);
|
|
124
|
+
}
|
|
38
125
|
}
|
|
39
126
|
default:
|
|
40
127
|
throw new errors_1.MCPError('VALIDATION_ERROR', `Unsupported action: ${action}`);
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { Tool, MCPContext } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Git Ignore Tool - Manage .gitignore file
|
|
4
|
+
* Create, read, add, remove patterns, and ensure .gitignore exists
|
|
5
|
+
*/
|
|
6
|
+
export declare class GitIgnoreTool implements Tool {
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
handle(params: Record<string, any>, ctx: MCPContext): Promise<{
|
|
10
|
+
success: boolean;
|
|
11
|
+
path: string;
|
|
12
|
+
exists: boolean;
|
|
13
|
+
content: string;
|
|
14
|
+
patterns: string[];
|
|
15
|
+
lines: number;
|
|
16
|
+
message?: undefined;
|
|
17
|
+
created?: undefined;
|
|
18
|
+
template?: undefined;
|
|
19
|
+
action?: undefined;
|
|
20
|
+
added?: undefined;
|
|
21
|
+
skipped?: undefined;
|
|
22
|
+
total?: undefined;
|
|
23
|
+
removed?: undefined;
|
|
24
|
+
remaining?: undefined;
|
|
25
|
+
updated?: undefined;
|
|
26
|
+
cleared?: undefined;
|
|
27
|
+
} | {
|
|
28
|
+
success: boolean;
|
|
29
|
+
path: string;
|
|
30
|
+
exists: boolean;
|
|
31
|
+
message: string;
|
|
32
|
+
content?: undefined;
|
|
33
|
+
patterns?: undefined;
|
|
34
|
+
lines?: undefined;
|
|
35
|
+
created?: undefined;
|
|
36
|
+
template?: undefined;
|
|
37
|
+
action?: undefined;
|
|
38
|
+
added?: undefined;
|
|
39
|
+
skipped?: undefined;
|
|
40
|
+
total?: undefined;
|
|
41
|
+
removed?: undefined;
|
|
42
|
+
remaining?: undefined;
|
|
43
|
+
updated?: undefined;
|
|
44
|
+
cleared?: undefined;
|
|
45
|
+
} | {
|
|
46
|
+
success: boolean;
|
|
47
|
+
message: string;
|
|
48
|
+
path: string;
|
|
49
|
+
exists?: undefined;
|
|
50
|
+
content?: undefined;
|
|
51
|
+
patterns?: undefined;
|
|
52
|
+
lines?: undefined;
|
|
53
|
+
created?: undefined;
|
|
54
|
+
template?: undefined;
|
|
55
|
+
action?: undefined;
|
|
56
|
+
added?: undefined;
|
|
57
|
+
skipped?: undefined;
|
|
58
|
+
total?: undefined;
|
|
59
|
+
removed?: undefined;
|
|
60
|
+
remaining?: undefined;
|
|
61
|
+
updated?: undefined;
|
|
62
|
+
cleared?: undefined;
|
|
63
|
+
} | {
|
|
64
|
+
success: boolean;
|
|
65
|
+
path: string;
|
|
66
|
+
created: boolean;
|
|
67
|
+
template: any;
|
|
68
|
+
lines: number;
|
|
69
|
+
exists?: undefined;
|
|
70
|
+
content?: undefined;
|
|
71
|
+
patterns?: undefined;
|
|
72
|
+
message?: undefined;
|
|
73
|
+
action?: undefined;
|
|
74
|
+
added?: undefined;
|
|
75
|
+
skipped?: undefined;
|
|
76
|
+
total?: undefined;
|
|
77
|
+
removed?: undefined;
|
|
78
|
+
remaining?: undefined;
|
|
79
|
+
updated?: undefined;
|
|
80
|
+
cleared?: undefined;
|
|
81
|
+
} | {
|
|
82
|
+
success: boolean;
|
|
83
|
+
path: string;
|
|
84
|
+
exists: boolean;
|
|
85
|
+
action: string;
|
|
86
|
+
message: string;
|
|
87
|
+
content?: undefined;
|
|
88
|
+
patterns?: undefined;
|
|
89
|
+
lines?: undefined;
|
|
90
|
+
created?: undefined;
|
|
91
|
+
template?: undefined;
|
|
92
|
+
added?: undefined;
|
|
93
|
+
skipped?: undefined;
|
|
94
|
+
total?: undefined;
|
|
95
|
+
removed?: undefined;
|
|
96
|
+
remaining?: undefined;
|
|
97
|
+
updated?: undefined;
|
|
98
|
+
cleared?: undefined;
|
|
99
|
+
} | {
|
|
100
|
+
success: boolean;
|
|
101
|
+
path: string;
|
|
102
|
+
created: boolean;
|
|
103
|
+
template: any;
|
|
104
|
+
action: string;
|
|
105
|
+
exists?: undefined;
|
|
106
|
+
content?: undefined;
|
|
107
|
+
patterns?: undefined;
|
|
108
|
+
lines?: undefined;
|
|
109
|
+
message?: undefined;
|
|
110
|
+
added?: undefined;
|
|
111
|
+
skipped?: undefined;
|
|
112
|
+
total?: undefined;
|
|
113
|
+
removed?: undefined;
|
|
114
|
+
remaining?: undefined;
|
|
115
|
+
updated?: undefined;
|
|
116
|
+
cleared?: undefined;
|
|
117
|
+
} | {
|
|
118
|
+
success: boolean;
|
|
119
|
+
path: string;
|
|
120
|
+
added: string[];
|
|
121
|
+
skipped: number;
|
|
122
|
+
total: number;
|
|
123
|
+
exists?: undefined;
|
|
124
|
+
content?: undefined;
|
|
125
|
+
patterns?: undefined;
|
|
126
|
+
lines?: undefined;
|
|
127
|
+
message?: undefined;
|
|
128
|
+
created?: undefined;
|
|
129
|
+
template?: undefined;
|
|
130
|
+
action?: undefined;
|
|
131
|
+
removed?: undefined;
|
|
132
|
+
remaining?: undefined;
|
|
133
|
+
updated?: undefined;
|
|
134
|
+
cleared?: undefined;
|
|
135
|
+
} | {
|
|
136
|
+
success: boolean;
|
|
137
|
+
path: string;
|
|
138
|
+
removed: number;
|
|
139
|
+
remaining: number;
|
|
140
|
+
exists?: undefined;
|
|
141
|
+
content?: undefined;
|
|
142
|
+
patterns?: undefined;
|
|
143
|
+
lines?: undefined;
|
|
144
|
+
message?: undefined;
|
|
145
|
+
created?: undefined;
|
|
146
|
+
template?: undefined;
|
|
147
|
+
action?: undefined;
|
|
148
|
+
added?: undefined;
|
|
149
|
+
skipped?: undefined;
|
|
150
|
+
total?: undefined;
|
|
151
|
+
updated?: undefined;
|
|
152
|
+
cleared?: undefined;
|
|
153
|
+
} | {
|
|
154
|
+
success: boolean;
|
|
155
|
+
path: string;
|
|
156
|
+
updated: boolean;
|
|
157
|
+
lines: any;
|
|
158
|
+
exists?: undefined;
|
|
159
|
+
content?: undefined;
|
|
160
|
+
patterns?: undefined;
|
|
161
|
+
message?: undefined;
|
|
162
|
+
created?: undefined;
|
|
163
|
+
template?: undefined;
|
|
164
|
+
action?: undefined;
|
|
165
|
+
added?: undefined;
|
|
166
|
+
skipped?: undefined;
|
|
167
|
+
total?: undefined;
|
|
168
|
+
removed?: undefined;
|
|
169
|
+
remaining?: undefined;
|
|
170
|
+
cleared?: undefined;
|
|
171
|
+
} | {
|
|
172
|
+
success: boolean;
|
|
173
|
+
path: string;
|
|
174
|
+
cleared: boolean;
|
|
175
|
+
template: any;
|
|
176
|
+
exists?: undefined;
|
|
177
|
+
content?: undefined;
|
|
178
|
+
patterns?: undefined;
|
|
179
|
+
lines?: undefined;
|
|
180
|
+
message?: undefined;
|
|
181
|
+
created?: undefined;
|
|
182
|
+
action?: undefined;
|
|
183
|
+
added?: undefined;
|
|
184
|
+
skipped?: undefined;
|
|
185
|
+
total?: undefined;
|
|
186
|
+
removed?: undefined;
|
|
187
|
+
remaining?: undefined;
|
|
188
|
+
updated?: undefined;
|
|
189
|
+
}>;
|
|
190
|
+
private getTemplate;
|
|
191
|
+
}
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.GitIgnoreTool = void 0;
|
|
37
|
+
const fs = __importStar(require("fs/promises"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const errors_1 = require("../utils/errors");
|
|
40
|
+
/**
|
|
41
|
+
* Git Ignore Tool - Manage .gitignore file
|
|
42
|
+
* Create, read, add, remove patterns, and ensure .gitignore exists
|
|
43
|
+
*/
|
|
44
|
+
class GitIgnoreTool {
|
|
45
|
+
constructor() {
|
|
46
|
+
this.name = 'git-ignore';
|
|
47
|
+
this.description = 'Manage .gitignore file - create, add, remove, and update ignore patterns';
|
|
48
|
+
}
|
|
49
|
+
async handle(params, ctx) {
|
|
50
|
+
const projectPath = params.projectPath;
|
|
51
|
+
if (!projectPath) {
|
|
52
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'projectPath is required');
|
|
53
|
+
}
|
|
54
|
+
const action = params.action || 'read';
|
|
55
|
+
const gitignorePath = path.join(projectPath, '.gitignore');
|
|
56
|
+
switch (action) {
|
|
57
|
+
case 'read': {
|
|
58
|
+
try {
|
|
59
|
+
const content = await fs.readFile(gitignorePath, 'utf-8');
|
|
60
|
+
const patterns = content.split('\n').filter(l => l.trim() && !l.startsWith('#'));
|
|
61
|
+
return {
|
|
62
|
+
success: true,
|
|
63
|
+
path: gitignorePath,
|
|
64
|
+
exists: true,
|
|
65
|
+
content,
|
|
66
|
+
patterns,
|
|
67
|
+
lines: content.split('\n').length,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
if (err.code === 'ENOENT') {
|
|
72
|
+
return {
|
|
73
|
+
success: true,
|
|
74
|
+
path: gitignorePath,
|
|
75
|
+
exists: false,
|
|
76
|
+
message: '.gitignore does not exist',
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
case 'create': {
|
|
83
|
+
const template = params.template || 'default';
|
|
84
|
+
const content = this.getTemplate(template);
|
|
85
|
+
try {
|
|
86
|
+
await fs.access(gitignorePath);
|
|
87
|
+
return {
|
|
88
|
+
success: false,
|
|
89
|
+
message: '.gitignore already exists. Use "add" to add patterns.',
|
|
90
|
+
path: gitignorePath,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
await fs.writeFile(gitignorePath, content, 'utf-8');
|
|
95
|
+
return {
|
|
96
|
+
success: true,
|
|
97
|
+
path: gitignorePath,
|
|
98
|
+
created: true,
|
|
99
|
+
template,
|
|
100
|
+
lines: content.split('\n').length,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
case 'ensure': {
|
|
105
|
+
// Create if doesn't exist, otherwise do nothing
|
|
106
|
+
try {
|
|
107
|
+
await fs.access(gitignorePath);
|
|
108
|
+
return {
|
|
109
|
+
success: true,
|
|
110
|
+
path: gitignorePath,
|
|
111
|
+
exists: true,
|
|
112
|
+
action: 'none',
|
|
113
|
+
message: '.gitignore already exists',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
const template = params.template || 'default';
|
|
118
|
+
const content = this.getTemplate(template);
|
|
119
|
+
await fs.writeFile(gitignorePath, content, 'utf-8');
|
|
120
|
+
return {
|
|
121
|
+
success: true,
|
|
122
|
+
path: gitignorePath,
|
|
123
|
+
created: true,
|
|
124
|
+
template,
|
|
125
|
+
action: 'created',
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
case 'add': {
|
|
130
|
+
const patterns = params.patterns;
|
|
131
|
+
if (!patterns || !Array.isArray(patterns) || patterns.length === 0) {
|
|
132
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'patterns array is required for add action');
|
|
133
|
+
}
|
|
134
|
+
let content = '';
|
|
135
|
+
try {
|
|
136
|
+
content = await fs.readFile(gitignorePath, 'utf-8');
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
if (err.code === 'ENOENT') {
|
|
140
|
+
// Create new file
|
|
141
|
+
content = this.getTemplate('minimal');
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
throw err;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const existingPatterns = new Set(content.split('\n').map(l => l.trim()).filter(l => l && !l.startsWith('#')));
|
|
148
|
+
const newPatterns = [];
|
|
149
|
+
for (const pattern of patterns) {
|
|
150
|
+
if (!existingPatterns.has(pattern)) {
|
|
151
|
+
newPatterns.push(pattern);
|
|
152
|
+
existingPatterns.add(pattern);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
if (newPatterns.length > 0) {
|
|
156
|
+
const comment = params.comment ? `# ${params.comment}\n` : '';
|
|
157
|
+
content += `\n${comment}${newPatterns.join('\n')}\n`;
|
|
158
|
+
await fs.writeFile(gitignorePath, content, 'utf-8');
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
success: true,
|
|
162
|
+
path: gitignorePath,
|
|
163
|
+
added: newPatterns,
|
|
164
|
+
skipped: patterns.length - newPatterns.length,
|
|
165
|
+
total: existingPatterns.size,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
case 'remove': {
|
|
169
|
+
const patterns = params.patterns;
|
|
170
|
+
if (!patterns || !Array.isArray(patterns) || patterns.length === 0) {
|
|
171
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'patterns array is required for remove action');
|
|
172
|
+
}
|
|
173
|
+
let content = '';
|
|
174
|
+
try {
|
|
175
|
+
content = await fs.readFile(gitignorePath, 'utf-8');
|
|
176
|
+
}
|
|
177
|
+
catch (err) {
|
|
178
|
+
if (err.code === 'ENOENT') {
|
|
179
|
+
return {
|
|
180
|
+
success: false,
|
|
181
|
+
message: '.gitignore does not exist',
|
|
182
|
+
path: gitignorePath,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
throw err;
|
|
186
|
+
}
|
|
187
|
+
const patternsToRemove = new Set(patterns);
|
|
188
|
+
const lines = content.split('\n');
|
|
189
|
+
const filteredLines = lines.filter(line => {
|
|
190
|
+
const trimmed = line.trim();
|
|
191
|
+
return !patternsToRemove.has(trimmed);
|
|
192
|
+
});
|
|
193
|
+
const removed = lines.length - filteredLines.length;
|
|
194
|
+
const newContent = filteredLines.join('\n');
|
|
195
|
+
await fs.writeFile(gitignorePath, newContent, 'utf-8');
|
|
196
|
+
return {
|
|
197
|
+
success: true,
|
|
198
|
+
path: gitignorePath,
|
|
199
|
+
removed,
|
|
200
|
+
remaining: filteredLines.filter(l => l.trim() && !l.startsWith('#')).length,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
case 'update': {
|
|
204
|
+
const content = params.content;
|
|
205
|
+
if (!content) {
|
|
206
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'content is required for update action');
|
|
207
|
+
}
|
|
208
|
+
await fs.writeFile(gitignorePath, content, 'utf-8');
|
|
209
|
+
return {
|
|
210
|
+
success: true,
|
|
211
|
+
path: gitignorePath,
|
|
212
|
+
updated: true,
|
|
213
|
+
lines: content.split('\n').length,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
case 'clear': {
|
|
217
|
+
const template = params.template || 'empty';
|
|
218
|
+
const content = template === 'empty' ? '' : this.getTemplate('minimal');
|
|
219
|
+
await fs.writeFile(gitignorePath, content, 'utf-8');
|
|
220
|
+
return {
|
|
221
|
+
success: true,
|
|
222
|
+
path: gitignorePath,
|
|
223
|
+
cleared: true,
|
|
224
|
+
template,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
default:
|
|
228
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', `Unsupported action: ${action}`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
getTemplate(template) {
|
|
232
|
+
const templates = {
|
|
233
|
+
empty: '',
|
|
234
|
+
minimal: `# Dependencies
|
|
235
|
+
node_modules/
|
|
236
|
+
.pnp
|
|
237
|
+
.pnp.js
|
|
238
|
+
|
|
239
|
+
# Build output
|
|
240
|
+
dist/
|
|
241
|
+
build/
|
|
242
|
+
*.log
|
|
243
|
+
`,
|
|
244
|
+
default: `# Dependencies
|
|
245
|
+
node_modules/
|
|
246
|
+
.pnp
|
|
247
|
+
.pnp.js
|
|
248
|
+
bower_components/
|
|
249
|
+
|
|
250
|
+
# Build output
|
|
251
|
+
dist/
|
|
252
|
+
build/
|
|
253
|
+
out/
|
|
254
|
+
.next/
|
|
255
|
+
.nuxt/
|
|
256
|
+
.cache/
|
|
257
|
+
.parcel-cache/
|
|
258
|
+
|
|
259
|
+
# Environment
|
|
260
|
+
.env
|
|
261
|
+
.env.local
|
|
262
|
+
.env.*.local
|
|
263
|
+
*.env
|
|
264
|
+
|
|
265
|
+
# IDE
|
|
266
|
+
.vscode/
|
|
267
|
+
.idea/
|
|
268
|
+
*.swp
|
|
269
|
+
*.swo
|
|
270
|
+
*~
|
|
271
|
+
.DS_Store
|
|
272
|
+
|
|
273
|
+
# Logs
|
|
274
|
+
logs/
|
|
275
|
+
*.log
|
|
276
|
+
npm-debug.log*
|
|
277
|
+
yarn-debug.log*
|
|
278
|
+
yarn-error.log*
|
|
279
|
+
lerna-debug.log*
|
|
280
|
+
pnpm-debug.log*
|
|
281
|
+
|
|
282
|
+
# Testing
|
|
283
|
+
coverage/
|
|
284
|
+
.nyc_output/
|
|
285
|
+
*.lcov
|
|
286
|
+
|
|
287
|
+
# Temporary
|
|
288
|
+
tmp/
|
|
289
|
+
temp/
|
|
290
|
+
*.tmp
|
|
291
|
+
`,
|
|
292
|
+
node: `# Node
|
|
293
|
+
node_modules/
|
|
294
|
+
npm-debug.log*
|
|
295
|
+
yarn-debug.log*
|
|
296
|
+
yarn-error.log*
|
|
297
|
+
lerna-debug.log*
|
|
298
|
+
pnpm-debug.log*
|
|
299
|
+
.pnpm-store/
|
|
300
|
+
.npm
|
|
301
|
+
.eslintcache
|
|
302
|
+
.node_repl_history
|
|
303
|
+
*.tgz
|
|
304
|
+
.yarn-integrity
|
|
305
|
+
.env
|
|
306
|
+
.env.test
|
|
307
|
+
.env.production
|
|
308
|
+
.cache
|
|
309
|
+
.next/
|
|
310
|
+
out/
|
|
311
|
+
build/
|
|
312
|
+
dist/
|
|
313
|
+
`,
|
|
314
|
+
python: `# Python
|
|
315
|
+
__pycache__/
|
|
316
|
+
*.py[cod]
|
|
317
|
+
*$py.class
|
|
318
|
+
*.so
|
|
319
|
+
.Python
|
|
320
|
+
build/
|
|
321
|
+
develop-eggs/
|
|
322
|
+
dist/
|
|
323
|
+
downloads/
|
|
324
|
+
eggs/
|
|
325
|
+
.eggs/
|
|
326
|
+
lib/
|
|
327
|
+
lib64/
|
|
328
|
+
parts/
|
|
329
|
+
sdist/
|
|
330
|
+
var/
|
|
331
|
+
wheels/
|
|
332
|
+
*.egg-info/
|
|
333
|
+
.installed.cfg
|
|
334
|
+
*.egg
|
|
335
|
+
MANIFEST
|
|
336
|
+
pip-log.txt
|
|
337
|
+
pip-delete-this-directory.txt
|
|
338
|
+
.tox/
|
|
339
|
+
.coverage
|
|
340
|
+
.pytest_cache/
|
|
341
|
+
.mypy_cache/
|
|
342
|
+
.dmypy.json
|
|
343
|
+
dmypy.json
|
|
344
|
+
.env
|
|
345
|
+
.venv
|
|
346
|
+
env/
|
|
347
|
+
venv/
|
|
348
|
+
ENV/
|
|
349
|
+
`,
|
|
350
|
+
};
|
|
351
|
+
return templates[template] || templates.default;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
exports.GitIgnoreTool = GitIgnoreTool;
|
|
@@ -2,5 +2,23 @@ import { Tool, MCPContext } from '../types';
|
|
|
2
2
|
export declare class GitPackagesTool implements Tool {
|
|
3
3
|
name: string;
|
|
4
4
|
description: string;
|
|
5
|
-
handle(params: Record<string, any>, ctx: MCPContext): Promise<
|
|
5
|
+
handle(params: Record<string, any>, ctx: MCPContext): Promise<{
|
|
6
|
+
success: boolean;
|
|
7
|
+
name: any;
|
|
8
|
+
version: any;
|
|
9
|
+
dependencies: any;
|
|
10
|
+
devDependencies: any;
|
|
11
|
+
scripts: any;
|
|
12
|
+
message?: undefined;
|
|
13
|
+
note?: undefined;
|
|
14
|
+
} | {
|
|
15
|
+
success: boolean;
|
|
16
|
+
message: string;
|
|
17
|
+
note: string;
|
|
18
|
+
name?: undefined;
|
|
19
|
+
version?: undefined;
|
|
20
|
+
dependencies?: undefined;
|
|
21
|
+
devDependencies?: undefined;
|
|
22
|
+
scripts?: undefined;
|
|
23
|
+
}>;
|
|
6
24
|
}
|
|
@@ -1,19 +1,54 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.GitPackagesTool = void 0;
|
|
7
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
4
9
|
const errors_1 = require("../utils/errors");
|
|
5
10
|
class GitPackagesTool {
|
|
6
11
|
constructor() {
|
|
7
12
|
this.name = 'git-packages';
|
|
8
|
-
this.description = 'Package management operations
|
|
13
|
+
this.description = 'Package management operations';
|
|
9
14
|
}
|
|
10
15
|
async handle(params, ctx) {
|
|
11
16
|
const action = params.action;
|
|
17
|
+
const projectPath = params.projectPath;
|
|
12
18
|
if (!action)
|
|
13
19
|
throw new errors_1.MCPError('VALIDATION_ERROR', 'action is required');
|
|
14
20
|
switch (action) {
|
|
15
|
-
case 'list':
|
|
16
|
-
|
|
21
|
+
case 'list': {
|
|
22
|
+
if (!projectPath)
|
|
23
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'projectPath is required');
|
|
24
|
+
// Ler package.json
|
|
25
|
+
try {
|
|
26
|
+
const packageJsonPath = path_1.default.join(projectPath, 'package.json');
|
|
27
|
+
const content = await promises_1.default.readFile(packageJsonPath, 'utf-8');
|
|
28
|
+
const pkg = JSON.parse(content);
|
|
29
|
+
return {
|
|
30
|
+
success: true,
|
|
31
|
+
name: pkg.name,
|
|
32
|
+
version: pkg.version,
|
|
33
|
+
dependencies: pkg.dependencies || {},
|
|
34
|
+
devDependencies: pkg.devDependencies || {},
|
|
35
|
+
scripts: pkg.scripts || {},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
throw new errors_1.MCPError('FILE_ERROR', `Failed to read package.json: ${error.message}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
case 'get': {
|
|
43
|
+
const packageName = params.packageName;
|
|
44
|
+
if (!packageName)
|
|
45
|
+
throw new errors_1.MCPError('VALIDATION_ERROR', 'packageName is required');
|
|
46
|
+
return {
|
|
47
|
+
success: true,
|
|
48
|
+
message: `Package info for ${packageName}`,
|
|
49
|
+
note: 'Use npm view or yarn info for detailed package information',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
17
52
|
case 'create':
|
|
18
53
|
case 'update':
|
|
19
54
|
case 'delete':
|
package/dist/tools/gitUpdate.js
CHANGED
|
@@ -145,14 +145,107 @@ class GitUpdateTool {
|
|
|
145
145
|
throw err;
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
|
-
// 5. Get remotes
|
|
148
|
+
// 5. Get remotes and ensure repos exist
|
|
149
149
|
const remotes = await git.getRemotes(true);
|
|
150
|
-
|
|
151
|
-
|
|
150
|
+
let githubRemote = remotes.find(r => r.name === 'github' || r.name === 'origin');
|
|
151
|
+
let giteaRemote = remotes.find(r => r.name === 'gitea');
|
|
152
|
+
// Auto-create repos if they don't exist
|
|
153
|
+
const repoName = params.repoName || path.basename(projectPath);
|
|
154
|
+
const description = params.description || `Project updated via git-update at ${new Date().toISOString()}`;
|
|
155
|
+
const isPrivate = params.private !== undefined ? params.private : true;
|
|
156
|
+
const githubOwner = params.owner || process.env.GITHUB_USERNAME;
|
|
157
|
+
const giteaOwner = process.env.GITEA_USERNAME;
|
|
158
|
+
// 5a. Ensure GitHub repo exists
|
|
159
|
+
if (ctx.providerManager.github && !githubRemote) {
|
|
160
|
+
results.traceability.updateSteps.push({
|
|
161
|
+
step: 5,
|
|
162
|
+
action: 'ensure_github_repo',
|
|
163
|
+
timestamp: new Date().toISOString(),
|
|
164
|
+
});
|
|
165
|
+
try {
|
|
166
|
+
// Try to get repo, create if doesn't exist
|
|
167
|
+
let repoExists = false;
|
|
168
|
+
try {
|
|
169
|
+
await ctx.providerManager.github.rest.repos.get({
|
|
170
|
+
owner: githubOwner,
|
|
171
|
+
repo: repoName
|
|
172
|
+
});
|
|
173
|
+
repoExists = true;
|
|
174
|
+
}
|
|
175
|
+
catch { }
|
|
176
|
+
if (!repoExists) {
|
|
177
|
+
await ctx.providerManager.github.rest.repos.createForAuthenticatedUser({
|
|
178
|
+
name: repoName,
|
|
179
|
+
description,
|
|
180
|
+
private: isPrivate,
|
|
181
|
+
auto_init: false,
|
|
182
|
+
});
|
|
183
|
+
results.traceability.updateSteps.push({
|
|
184
|
+
step: 5.1,
|
|
185
|
+
action: 'created_github_repo',
|
|
186
|
+
timestamp: new Date().toISOString(),
|
|
187
|
+
repo: repoName,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
// Add remote
|
|
191
|
+
await git.addRemote('github', `https://github.com/${githubOwner}/${repoName}.git`);
|
|
192
|
+
githubRemote = { name: 'github', refs: { push: `https://github.com/${githubOwner}/${repoName}.git`, fetch: '' } };
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
results.traceability.errors.push({
|
|
196
|
+
provider: 'github',
|
|
197
|
+
action: 'ensure_repo',
|
|
198
|
+
error: err.message,
|
|
199
|
+
timestamp: new Date().toISOString(),
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// 5b. Ensure Gitea repo exists
|
|
204
|
+
if (ctx.providerManager.giteaBaseUrl && !giteaRemote) {
|
|
205
|
+
results.traceability.updateSteps.push({
|
|
206
|
+
step: 5.5,
|
|
207
|
+
action: 'ensure_gitea_repo',
|
|
208
|
+
timestamp: new Date().toISOString(),
|
|
209
|
+
});
|
|
210
|
+
try {
|
|
211
|
+
// Try to get repo, create if doesn't exist
|
|
212
|
+
let repoExists = false;
|
|
213
|
+
try {
|
|
214
|
+
await axios_1.default.get(`${ctx.providerManager.giteaBaseUrl}/api/v1/repos/${giteaOwner}/${repoName}`, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
215
|
+
repoExists = true;
|
|
216
|
+
}
|
|
217
|
+
catch { }
|
|
218
|
+
if (!repoExists) {
|
|
219
|
+
await axios_1.default.post(`${ctx.providerManager.giteaBaseUrl}/api/v1/user/repos`, {
|
|
220
|
+
name: repoName,
|
|
221
|
+
description,
|
|
222
|
+
private: isPrivate,
|
|
223
|
+
auto_init: false,
|
|
224
|
+
}, { headers: { Authorization: `token ${ctx.providerManager.giteaToken}` } });
|
|
225
|
+
results.traceability.updateSteps.push({
|
|
226
|
+
step: 5.6,
|
|
227
|
+
action: 'created_gitea_repo',
|
|
228
|
+
timestamp: new Date().toISOString(),
|
|
229
|
+
repo: repoName,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
// Add remote
|
|
233
|
+
await git.addRemote('gitea', `${ctx.providerManager.giteaBaseUrl}/${giteaOwner}/${repoName}.git`);
|
|
234
|
+
giteaRemote = { name: 'gitea', refs: { push: `${ctx.providerManager.giteaBaseUrl}/${giteaOwner}/${repoName}.git`, fetch: '' } };
|
|
235
|
+
}
|
|
236
|
+
catch (err) {
|
|
237
|
+
results.traceability.errors.push({
|
|
238
|
+
provider: 'gitea',
|
|
239
|
+
action: 'ensure_repo',
|
|
240
|
+
error: err.message,
|
|
241
|
+
timestamp: new Date().toISOString(),
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
152
245
|
// 6. Push to GITHUB
|
|
153
246
|
if (githubRemote && ctx.providerManager.github) {
|
|
154
247
|
results.traceability.updateSteps.push({
|
|
155
|
-
step:
|
|
248
|
+
step: 6,
|
|
156
249
|
action: 'push_to_github',
|
|
157
250
|
timestamp: new Date().toISOString(),
|
|
158
251
|
remote: githubRemote.name,
|
|
@@ -204,7 +297,7 @@ class GitUpdateTool {
|
|
|
204
297
|
// 7. Push to GITEA
|
|
205
298
|
if (giteaRemote && ctx.providerManager.giteaBaseUrl) {
|
|
206
299
|
results.traceability.updateSteps.push({
|
|
207
|
-
step:
|
|
300
|
+
step: 7,
|
|
208
301
|
action: 'push_to_gitea',
|
|
209
302
|
timestamp: new Date().toISOString(),
|
|
210
303
|
remote: giteaRemote.name,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@andrebuzeli/git-mcp",
|
|
3
|
-
"version": "7.0
|
|
3
|
+
"version": "7.2.0",
|
|
4
4
|
"description": "Professional MCP server for Git operations - STDIO UNIVERSAL: works in ANY IDE (Cursor, VSCode, Claude Desktop). Fully autonomous DUAL execution (GitHub + Gitea APIs) with automatic username detection. All tools execute on BOTH providers simultaneously. No manual parameters needed.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dual Provider Execution Helper
|
|
3
|
-
* Executes operations on BOTH GitHub and Gitea simultaneously
|
|
4
|
-
* Uses GITHUB_USERNAME and GITEA_USERNAME from env vars automatically
|
|
5
|
-
*/
|
|
6
|
-
import { ProviderManager } from '../providers/providerManager';
|
|
7
|
-
export interface DualExecutionResult<T> {
|
|
8
|
-
github: {
|
|
9
|
-
success: boolean;
|
|
10
|
-
data?: T;
|
|
11
|
-
error?: string;
|
|
12
|
-
};
|
|
13
|
-
gitea: {
|
|
14
|
-
success: boolean;
|
|
15
|
-
data?: T;
|
|
16
|
-
error?: string;
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Execute function on both providers simultaneously
|
|
21
|
-
* @param providerManager Provider manager instance
|
|
22
|
-
* @param githubFn Function to execute on GitHub
|
|
23
|
-
* @param giteaFn Function to execute on Gitea
|
|
24
|
-
* @returns Dual execution result with separate responses
|
|
25
|
-
*/
|
|
26
|
-
export declare function executeDual<T>(providerManager: ProviderManager, githubFn: () => Promise<T>, giteaFn: () => Promise<T>): Promise<DualExecutionResult<T>>;
|
|
27
|
-
/**
|
|
28
|
-
* Get GitHub username from env vars
|
|
29
|
-
* @throws Error if GITHUB_USERNAME not found
|
|
30
|
-
*/
|
|
31
|
-
export declare function getGitHubUsername(): string;
|
|
32
|
-
/**
|
|
33
|
-
* Get Gitea username from env vars
|
|
34
|
-
* @throws Error if GITEA_USERNAME not found
|
|
35
|
-
*/
|
|
36
|
-
export declare function getGiteaUsername(): string;
|
|
37
|
-
/**
|
|
38
|
-
* Get repository name from projectPath
|
|
39
|
-
* @param projectPath Absolute path to project root
|
|
40
|
-
* @returns Repository name (folder name)
|
|
41
|
-
*/
|
|
42
|
-
export declare function getRepoNameFromPath(projectPath: string): string;
|
|
43
|
-
/**
|
|
44
|
-
* Execute same function on both providers with automatic username injection
|
|
45
|
-
* @param providerManager Provider manager instance
|
|
46
|
-
* @param fn Function that takes (provider, username) and returns data
|
|
47
|
-
* @returns Dual execution result
|
|
48
|
-
*/
|
|
49
|
-
export declare function executeDualAuto<T>(providerManager: ProviderManager, fn: (provider: any, username: string) => Promise<T>): Promise<DualExecutionResult<T>>;
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Dual Provider Execution Helper
|
|
4
|
-
* Executes operations on BOTH GitHub and Gitea simultaneously
|
|
5
|
-
* Uses GITHUB_USERNAME and GITEA_USERNAME from env vars automatically
|
|
6
|
-
*/
|
|
7
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.executeDual = executeDual;
|
|
9
|
-
exports.getGitHubUsername = getGitHubUsername;
|
|
10
|
-
exports.getGiteaUsername = getGiteaUsername;
|
|
11
|
-
exports.getRepoNameFromPath = getRepoNameFromPath;
|
|
12
|
-
exports.executeDualAuto = executeDualAuto;
|
|
13
|
-
/**
|
|
14
|
-
* Execute function on both providers simultaneously
|
|
15
|
-
* @param providerManager Provider manager instance
|
|
16
|
-
* @param githubFn Function to execute on GitHub
|
|
17
|
-
* @param giteaFn Function to execute on Gitea
|
|
18
|
-
* @returns Dual execution result with separate responses
|
|
19
|
-
*/
|
|
20
|
-
async function executeDual(providerManager, githubFn, giteaFn) {
|
|
21
|
-
const result = {
|
|
22
|
-
github: { success: false },
|
|
23
|
-
gitea: { success: false }
|
|
24
|
-
};
|
|
25
|
-
// Execute GitHub
|
|
26
|
-
try {
|
|
27
|
-
const githubData = await githubFn();
|
|
28
|
-
result.github = {
|
|
29
|
-
success: true,
|
|
30
|
-
data: githubData
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
catch (error) {
|
|
34
|
-
result.github = {
|
|
35
|
-
success: false,
|
|
36
|
-
error: error.message || String(error)
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
// Execute Gitea
|
|
40
|
-
try {
|
|
41
|
-
const giteaData = await giteaFn();
|
|
42
|
-
result.gitea = {
|
|
43
|
-
success: true,
|
|
44
|
-
data: giteaData
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
catch (error) {
|
|
48
|
-
result.gitea = {
|
|
49
|
-
success: false,
|
|
50
|
-
error: error.message || String(error)
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
return result;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Get GitHub username from env vars
|
|
57
|
-
* @throws Error if GITHUB_USERNAME not found
|
|
58
|
-
*/
|
|
59
|
-
function getGitHubUsername() {
|
|
60
|
-
const username = process.env.GITHUB_USERNAME;
|
|
61
|
-
if (!username) {
|
|
62
|
-
throw new Error('GITHUB_USERNAME not found in environment variables');
|
|
63
|
-
}
|
|
64
|
-
return username;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Get Gitea username from env vars
|
|
68
|
-
* @throws Error if GITEA_USERNAME not found
|
|
69
|
-
*/
|
|
70
|
-
function getGiteaUsername() {
|
|
71
|
-
const username = process.env.GITEA_USERNAME;
|
|
72
|
-
if (!username) {
|
|
73
|
-
throw new Error('GITEA_USERNAME not found in environment variables');
|
|
74
|
-
}
|
|
75
|
-
return username;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Get repository name from projectPath
|
|
79
|
-
* @param projectPath Absolute path to project root
|
|
80
|
-
* @returns Repository name (folder name)
|
|
81
|
-
*/
|
|
82
|
-
function getRepoNameFromPath(projectPath) {
|
|
83
|
-
const parts = projectPath.replace(/\\/g, '/').split('/');
|
|
84
|
-
return parts[parts.length - 1];
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Execute same function on both providers with automatic username injection
|
|
88
|
-
* @param providerManager Provider manager instance
|
|
89
|
-
* @param fn Function that takes (provider, username) and returns data
|
|
90
|
-
* @returns Dual execution result
|
|
91
|
-
*/
|
|
92
|
-
async function executeDualAuto(providerManager, fn) {
|
|
93
|
-
const githubUsername = getGitHubUsername();
|
|
94
|
-
const giteaUsername = getGiteaUsername();
|
|
95
|
-
return executeDual(providerManager, () => fn(providerManager.getProvider('github'), githubUsername), () => fn(providerManager.getProvider('gitea'), giteaUsername));
|
|
96
|
-
}
|