@andrebuzeli/git-mcp 10.0.7 → 10.0.9
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/LICENSE +0 -0
- package/README.md +1 -1
- package/dist/providers/providerManager.js +2 -4
- package/dist/scripts/test_e2e.d.ts +1 -0
- package/dist/scripts/test_e2e.js +199 -0
- package/dist/scripts/test_exhaustive.d.ts +1 -0
- package/dist/scripts/test_exhaustive.js +275 -0
- package/dist/scripts/test_gitea_creation.d.ts +1 -0
- package/dist/scripts/test_gitea_creation.js +116 -0
- package/dist/scripts/verify_setup.d.ts +1 -0
- package/dist/scripts/verify_setup.js +61 -0
- package/dist/tools/gitAnalytics.js +3 -3
- package/dist/tools/gitBackup.d.ts +2 -2
- package/dist/tools/gitBackup.js +5 -5
- package/dist/tools/gitBranches.js +40 -29
- package/dist/tools/gitConfig.d.ts +2 -2
- package/dist/tools/gitConfig.js +12 -13
- package/dist/tools/gitFix.d.ts +2 -1
- package/dist/tools/gitFix.js +20 -24
- package/dist/tools/gitFix.tool.d.ts +2 -2
- package/dist/tools/gitFix.tool.js +2 -2
- package/dist/tools/gitHistory.js +4 -5
- package/dist/tools/gitIssues.js +8 -8
- package/dist/tools/gitMonitor.js +28 -33
- package/dist/tools/gitPulls.js +8 -8
- package/dist/tools/gitRelease.js +5 -6
- package/dist/tools/gitRemote.d.ts +1 -11
- package/dist/tools/gitRemote.js +14 -20
- package/dist/tools/gitReset.js +6 -7
- package/dist/tools/gitStash.d.ts +1 -12
- package/dist/tools/gitStash.js +11 -22
- package/dist/tools/gitSync.js +44 -36
- package/dist/tools/gitTags.d.ts +8 -0
- package/dist/tools/gitTags.js +44 -34
- package/dist/tools/gitUpdate.d.ts +27 -0
- package/dist/tools/gitUpdate.js +61 -34
- package/dist/tools/gitUpload.js +29 -44
- package/dist/tools/gitWorkflow.js +27 -46
- package/dist/utils/gitAdapter.js +10 -0
- package/dist/utils/repoHelpers.js +7 -12
- package/package.json +2 -3
- package/dist/server-new.d.ts +0 -2
- package/dist/server-new.js +0 -224
- package/dist/tools/gitFiles-new.d.ts +0 -89
- package/dist/tools/gitFiles-new.js +0 -335
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { Tool } from '../types.js';
|
|
2
|
-
export declare class GitFilesTool implements Tool {
|
|
3
|
-
name: string;
|
|
4
|
-
description: string;
|
|
5
|
-
inputSchema: {
|
|
6
|
-
type: "object";
|
|
7
|
-
properties: {
|
|
8
|
-
action: {
|
|
9
|
-
type: string;
|
|
10
|
-
enum: string[];
|
|
11
|
-
description: string;
|
|
12
|
-
};
|
|
13
|
-
projectPath: {
|
|
14
|
-
type: string;
|
|
15
|
-
description: string;
|
|
16
|
-
};
|
|
17
|
-
filePath: {
|
|
18
|
-
type: string;
|
|
19
|
-
description: string;
|
|
20
|
-
};
|
|
21
|
-
directoryPath: {
|
|
22
|
-
type: string;
|
|
23
|
-
description: string;
|
|
24
|
-
};
|
|
25
|
-
content: {
|
|
26
|
-
type: string;
|
|
27
|
-
description: string;
|
|
28
|
-
};
|
|
29
|
-
comment: {
|
|
30
|
-
type: string;
|
|
31
|
-
description: string;
|
|
32
|
-
};
|
|
33
|
-
patterns: {
|
|
34
|
-
type: string;
|
|
35
|
-
items: {
|
|
36
|
-
type: string;
|
|
37
|
-
};
|
|
38
|
-
description: string;
|
|
39
|
-
};
|
|
40
|
-
searchText: {
|
|
41
|
-
type: string;
|
|
42
|
-
description: string;
|
|
43
|
-
};
|
|
44
|
-
query: {
|
|
45
|
-
type: string;
|
|
46
|
-
description: string;
|
|
47
|
-
};
|
|
48
|
-
searchPath: {
|
|
49
|
-
type: string;
|
|
50
|
-
description: string;
|
|
51
|
-
};
|
|
52
|
-
};
|
|
53
|
-
required: string[];
|
|
54
|
-
additionalProperties: boolean;
|
|
55
|
-
};
|
|
56
|
-
handle(params: Record<string, any>): Promise<any>;
|
|
57
|
-
/**
|
|
58
|
-
* Lê arquivo com cache
|
|
59
|
-
*/
|
|
60
|
-
private handleRead;
|
|
61
|
-
/**
|
|
62
|
-
* Lista diretório com cache
|
|
63
|
-
*/
|
|
64
|
-
private handleList;
|
|
65
|
-
/**
|
|
66
|
-
* Cria arquivo com validação de segurança
|
|
67
|
-
*/
|
|
68
|
-
private handleCreate;
|
|
69
|
-
/**
|
|
70
|
-
* Atualiza arquivo com validação de segurança
|
|
71
|
-
*/
|
|
72
|
-
private handleUpdate;
|
|
73
|
-
/**
|
|
74
|
-
* Deleta arquivo com validação de segurança
|
|
75
|
-
*/
|
|
76
|
-
private handleDelete;
|
|
77
|
-
/**
|
|
78
|
-
* Busca texto em arquivos com cache
|
|
79
|
-
*/
|
|
80
|
-
private handleSearch;
|
|
81
|
-
/**
|
|
82
|
-
* Busca recursivamente em diretórios
|
|
83
|
-
*/
|
|
84
|
-
private searchInDirectory;
|
|
85
|
-
/**
|
|
86
|
-
* Verifica se arquivo é buscável
|
|
87
|
-
*/
|
|
88
|
-
private isSearchableFile;
|
|
89
|
-
}
|
|
@@ -1,335 +0,0 @@
|
|
|
1
|
-
import fs from 'fs/promises';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { MCPError } from '../utils/errors.js';
|
|
4
|
-
import { cacheHelpers } from '../utils/cache.js';
|
|
5
|
-
import { Logger } from '../utils/logger.js';
|
|
6
|
-
export class GitFilesTool {
|
|
7
|
-
constructor() {
|
|
8
|
-
this.name = 'git-files';
|
|
9
|
-
this.description = 'Read-only file operations for repositories - local operations only with caching and rate limiting';
|
|
10
|
-
this.inputSchema = {
|
|
11
|
-
type: "object",
|
|
12
|
-
properties: {
|
|
13
|
-
action: {
|
|
14
|
-
type: "string",
|
|
15
|
-
enum: ["read", "list", "create", "update", "delete", "search"],
|
|
16
|
-
description: "File operation to perform"
|
|
17
|
-
},
|
|
18
|
-
projectPath: {
|
|
19
|
-
type: "string",
|
|
20
|
-
description: "Absolute path to the project directory (REQUIRED)"
|
|
21
|
-
},
|
|
22
|
-
filePath: {
|
|
23
|
-
type: "string",
|
|
24
|
-
description: "Relative path to the file within projectPath - required for read, create, update, delete"
|
|
25
|
-
},
|
|
26
|
-
directoryPath: {
|
|
27
|
-
type: "string",
|
|
28
|
-
description: "Relative path to directory for listing - optional, defaults to projectPath root"
|
|
29
|
-
},
|
|
30
|
-
content: {
|
|
31
|
-
type: "string",
|
|
32
|
-
description: "File content to write - required for create and update actions"
|
|
33
|
-
},
|
|
34
|
-
comment: {
|
|
35
|
-
type: "string",
|
|
36
|
-
description: "Comment to add when appending patterns to .gitignore - optional for add action"
|
|
37
|
-
},
|
|
38
|
-
patterns: {
|
|
39
|
-
type: "array",
|
|
40
|
-
items: { type: "string" },
|
|
41
|
-
description: "Array of file patterns - required for add and remove actions"
|
|
42
|
-
},
|
|
43
|
-
searchText: {
|
|
44
|
-
type: "string",
|
|
45
|
-
description: "Text to search for in files - required for search action"
|
|
46
|
-
},
|
|
47
|
-
query: {
|
|
48
|
-
type: "string",
|
|
49
|
-
description: "Alternative name for searchText - required for search action"
|
|
50
|
-
},
|
|
51
|
-
searchPath: {
|
|
52
|
-
type: "string",
|
|
53
|
-
description: "Relative path to search within - optional for search, defaults to projectPath root"
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
required: ["projectPath", "action"],
|
|
57
|
-
additionalProperties: true
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
async handle(params) {
|
|
61
|
-
const logger = Logger.getInstance();
|
|
62
|
-
const startTime = Date.now();
|
|
63
|
-
try {
|
|
64
|
-
const action = params.action;
|
|
65
|
-
const projectPath = params.projectPath;
|
|
66
|
-
if (!action || !projectPath) {
|
|
67
|
-
throw new MCPError('VALIDATION_ERROR', 'action and projectPath are required');
|
|
68
|
-
}
|
|
69
|
-
logger.debug(`Executing git-files:${action}`, { projectPath, filePath: params.filePath });
|
|
70
|
-
let result;
|
|
71
|
-
switch (action) {
|
|
72
|
-
case 'read':
|
|
73
|
-
result = await this.handleRead(projectPath, params.filePath);
|
|
74
|
-
break;
|
|
75
|
-
case 'list':
|
|
76
|
-
result = await this.handleList(projectPath, params.directoryPath);
|
|
77
|
-
break;
|
|
78
|
-
case 'create':
|
|
79
|
-
result = await this.handleCreate(projectPath, params.filePath, params.content || '');
|
|
80
|
-
// Invalidar cache após modificação
|
|
81
|
-
cacheHelpers.invalidateRepoCache(projectPath);
|
|
82
|
-
break;
|
|
83
|
-
case 'update':
|
|
84
|
-
result = await this.handleUpdate(projectPath, params.filePath, params.content);
|
|
85
|
-
// Invalidar cache após modificação
|
|
86
|
-
cacheHelpers.invalidateRepoCache(projectPath);
|
|
87
|
-
break;
|
|
88
|
-
case 'delete':
|
|
89
|
-
result = await this.handleDelete(projectPath, params.filePath);
|
|
90
|
-
// Invalidar cache após modificação
|
|
91
|
-
cacheHelpers.invalidateRepoCache(projectPath);
|
|
92
|
-
break;
|
|
93
|
-
case 'search':
|
|
94
|
-
result = await this.handleSearch(projectPath, params.searchText || params.query, params.searchPath);
|
|
95
|
-
break;
|
|
96
|
-
default:
|
|
97
|
-
throw new MCPError('VALIDATION_ERROR', `Unknown action: ${action}`);
|
|
98
|
-
}
|
|
99
|
-
const duration = Date.now() - startTime;
|
|
100
|
-
logger.debug(`git-files:${action} completed in ${duration}ms`);
|
|
101
|
-
return result;
|
|
102
|
-
}
|
|
103
|
-
catch (error) {
|
|
104
|
-
const duration = Date.now() - startTime;
|
|
105
|
-
logger.error(`git-files operation failed`, {
|
|
106
|
-
action: params.action,
|
|
107
|
-
projectPath: params.projectPath,
|
|
108
|
-
duration,
|
|
109
|
-
error: error.message
|
|
110
|
-
});
|
|
111
|
-
throw error;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Lê arquivo com cache
|
|
116
|
-
*/
|
|
117
|
-
async handleRead(projectPath, filePath) {
|
|
118
|
-
if (!filePath) {
|
|
119
|
-
throw new MCPError('VALIDATION_ERROR', 'filePath is required for read action');
|
|
120
|
-
}
|
|
121
|
-
const fullPath = path.resolve(projectPath, filePath);
|
|
122
|
-
// Verificar segurança: path traversal
|
|
123
|
-
if (!fullPath.startsWith(path.resolve(projectPath))) {
|
|
124
|
-
throw new MCPError('SECURITY_ERROR', 'File path must be within project directory');
|
|
125
|
-
}
|
|
126
|
-
// Leitura de arquivo sem cache por enquanto
|
|
127
|
-
try {
|
|
128
|
-
const content = await fs.readFile(fullPath, 'utf8');
|
|
129
|
-
return { success: true, path: fullPath, content, size: content.length };
|
|
130
|
-
}
|
|
131
|
-
catch (error) {
|
|
132
|
-
if (error.code === 'ENOENT') {
|
|
133
|
-
throw new MCPError('FILE_NOT_FOUND', `File not found: ${filePath}`);
|
|
134
|
-
}
|
|
135
|
-
throw new MCPError('FILE_ERROR', `Failed to read file: ${error.message}`);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Lista diretório com cache
|
|
140
|
-
*/
|
|
141
|
-
async handleList(projectPath, directoryPath) {
|
|
142
|
-
const dirPath = directoryPath ? path.join(projectPath, directoryPath) : projectPath;
|
|
143
|
-
// Verificar segurança: path traversal
|
|
144
|
-
if (!dirPath.startsWith(path.resolve(projectPath))) {
|
|
145
|
-
throw new MCPError('SECURITY_ERROR', 'Directory path must be within project directory');
|
|
146
|
-
}
|
|
147
|
-
// Usar cache para listagem de diretórios
|
|
148
|
-
// Listar diretório sem cache por enquanto
|
|
149
|
-
try {
|
|
150
|
-
const files = await fs.readdir(dirPath, { withFileTypes: true });
|
|
151
|
-
return {
|
|
152
|
-
success: true,
|
|
153
|
-
files: files.map(entry => ({
|
|
154
|
-
name: entry.name,
|
|
155
|
-
isDirectory: entry.isDirectory(),
|
|
156
|
-
isFile: entry.isFile(),
|
|
157
|
-
path: path.join(directoryPath || '', entry.name)
|
|
158
|
-
}))
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
catch (error) {
|
|
162
|
-
if (error.code === 'ENOENT') {
|
|
163
|
-
throw new MCPError('DIRECTORY_NOT_FOUND', `Directory not found: ${directoryPath || 'root'}`);
|
|
164
|
-
}
|
|
165
|
-
throw new MCPError('DIRECTORY_ERROR', `Failed to list directory: ${error.message}`);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Cria arquivo com validação de segurança
|
|
170
|
-
*/
|
|
171
|
-
async handleCreate(projectPath, filePath, content = '') {
|
|
172
|
-
if (!filePath) {
|
|
173
|
-
throw new MCPError('VALIDATION_ERROR', 'filePath is required for create action');
|
|
174
|
-
}
|
|
175
|
-
const targetPath = path.resolve(projectPath, filePath);
|
|
176
|
-
// Verificar segurança: path traversal
|
|
177
|
-
if (!targetPath.startsWith(path.resolve(projectPath))) {
|
|
178
|
-
throw new MCPError('SECURITY_ERROR', 'File path must be within project directory');
|
|
179
|
-
}
|
|
180
|
-
try {
|
|
181
|
-
// Criar diretório se necessário
|
|
182
|
-
const dir = path.dirname(targetPath);
|
|
183
|
-
await fs.mkdir(dir, { recursive: true });
|
|
184
|
-
await fs.writeFile(targetPath, content, 'utf-8');
|
|
185
|
-
return {
|
|
186
|
-
success: true,
|
|
187
|
-
created: true,
|
|
188
|
-
path: targetPath,
|
|
189
|
-
size: content.length,
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
catch (error) {
|
|
193
|
-
throw new MCPError('FILE_ERROR', `Failed to create file: ${error.message}`);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
/**
|
|
197
|
-
* Atualiza arquivo com validação de segurança
|
|
198
|
-
*/
|
|
199
|
-
async handleUpdate(projectPath, filePath, content) {
|
|
200
|
-
if (!filePath) {
|
|
201
|
-
throw new MCPError('VALIDATION_ERROR', 'filePath is required for update action');
|
|
202
|
-
}
|
|
203
|
-
if (content === undefined) {
|
|
204
|
-
throw new MCPError('VALIDATION_ERROR', 'content is required for update action');
|
|
205
|
-
}
|
|
206
|
-
const targetPath = path.resolve(projectPath, filePath);
|
|
207
|
-
// Verificar segurança: path traversal
|
|
208
|
-
if (!targetPath.startsWith(path.resolve(projectPath))) {
|
|
209
|
-
throw new MCPError('SECURITY_ERROR', 'File path must be within project directory');
|
|
210
|
-
}
|
|
211
|
-
try {
|
|
212
|
-
// Verificar se arquivo existe
|
|
213
|
-
await fs.access(targetPath);
|
|
214
|
-
await fs.writeFile(targetPath, content, 'utf-8');
|
|
215
|
-
return {
|
|
216
|
-
success: true,
|
|
217
|
-
updated: true,
|
|
218
|
-
path: targetPath,
|
|
219
|
-
size: content.length,
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
catch (error) {
|
|
223
|
-
if (error.code === 'ENOENT') {
|
|
224
|
-
throw new MCPError('FILE_NOT_FOUND', 'File does not exist. Use create action.');
|
|
225
|
-
}
|
|
226
|
-
throw new MCPError('FILE_ERROR', `Failed to update file: ${error.message}`);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Deleta arquivo com validação de segurança
|
|
231
|
-
*/
|
|
232
|
-
async handleDelete(projectPath, filePath) {
|
|
233
|
-
if (!filePath) {
|
|
234
|
-
throw new MCPError('VALIDATION_ERROR', 'filePath is required for delete action');
|
|
235
|
-
}
|
|
236
|
-
const targetPath = path.resolve(projectPath, filePath);
|
|
237
|
-
// Verificar segurança: path traversal
|
|
238
|
-
if (!targetPath.startsWith(path.resolve(projectPath))) {
|
|
239
|
-
throw new MCPError('SECURITY_ERROR', 'File path must be within project directory');
|
|
240
|
-
}
|
|
241
|
-
try {
|
|
242
|
-
await fs.unlink(targetPath);
|
|
243
|
-
return {
|
|
244
|
-
success: true,
|
|
245
|
-
deleted: true,
|
|
246
|
-
path: targetPath,
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
catch (error) {
|
|
250
|
-
if (error.code === 'ENOENT') {
|
|
251
|
-
throw new MCPError('FILE_NOT_FOUND', `File not found: ${filePath}`);
|
|
252
|
-
}
|
|
253
|
-
throw new MCPError('FILE_ERROR', `Failed to delete file: ${error.message}`);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* Busca texto em arquivos com cache
|
|
258
|
-
*/
|
|
259
|
-
async handleSearch(projectPath, searchText, searchPath) {
|
|
260
|
-
if (!searchText) {
|
|
261
|
-
throw new MCPError('VALIDATION_ERROR', 'searchText or query is required for search action');
|
|
262
|
-
}
|
|
263
|
-
const searchDir = searchPath ? path.join(projectPath, searchPath) : projectPath;
|
|
264
|
-
// Verificar segurança: path traversal
|
|
265
|
-
if (!searchDir.startsWith(path.resolve(projectPath))) {
|
|
266
|
-
throw new MCPError('SECURITY_ERROR', 'Search path must be within project directory');
|
|
267
|
-
}
|
|
268
|
-
// Busca sem cache por enquanto
|
|
269
|
-
try {
|
|
270
|
-
const results = await this.searchInDirectory(searchDir, searchText);
|
|
271
|
-
return {
|
|
272
|
-
success: true,
|
|
273
|
-
searchText,
|
|
274
|
-
searchPath: searchPath || 'root',
|
|
275
|
-
results: results.slice(0, 50), // Limitar resultados para performance
|
|
276
|
-
totalResults: results.length,
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
catch (error) {
|
|
280
|
-
throw new MCPError('SEARCH_ERROR', `Failed to search files: ${error.message}`);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
/**
|
|
284
|
-
* Busca recursivamente em diretórios
|
|
285
|
-
*/
|
|
286
|
-
async searchInDirectory(dir, searchText) {
|
|
287
|
-
const results = [];
|
|
288
|
-
try {
|
|
289
|
-
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
290
|
-
for (const entry of entries) {
|
|
291
|
-
const fullPath = path.join(dir, entry.name);
|
|
292
|
-
if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
|
|
293
|
-
// Recursivamente buscar em subdiretórios
|
|
294
|
-
const subResults = await this.searchInDirectory(fullPath, searchText);
|
|
295
|
-
results.push(...subResults);
|
|
296
|
-
}
|
|
297
|
-
else if (entry.isFile() && this.isSearchableFile(entry.name)) {
|
|
298
|
-
// Buscar em arquivos
|
|
299
|
-
try {
|
|
300
|
-
const content = await fs.readFile(fullPath, 'utf8');
|
|
301
|
-
const lines = content.split('\n');
|
|
302
|
-
lines.forEach((line, index) => {
|
|
303
|
-
if (line.toLowerCase().includes(searchText.toLowerCase())) {
|
|
304
|
-
results.push({
|
|
305
|
-
file: fullPath,
|
|
306
|
-
line: index + 1,
|
|
307
|
-
text: line.trim(),
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
catch (error) {
|
|
313
|
-
// Ignorar erros de leitura (arquivos binários, etc.)
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
catch (error) {
|
|
319
|
-
// Ignorar erros de acesso
|
|
320
|
-
}
|
|
321
|
-
return results;
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* Verifica se arquivo é buscável
|
|
325
|
-
*/
|
|
326
|
-
isSearchableFile(filename) {
|
|
327
|
-
const searchableExtensions = [
|
|
328
|
-
'.js', '.ts', '.jsx', '.tsx', '.json', '.md', '.txt', '.yml', '.yaml',
|
|
329
|
-
'.html', '.css', '.scss', '.sass', '.less', '.xml', '.sh', '.bat',
|
|
330
|
-
'.py', '.java', '.cpp', '.c', '.h', '.php', '.rb', '.go', '.rs'
|
|
331
|
-
];
|
|
332
|
-
const ext = path.extname(filename).toLowerCase();
|
|
333
|
-
return searchableExtensions.includes(ext);
|
|
334
|
-
}
|
|
335
|
-
}
|