@cyanheads/git-mcp-server 1.2.4
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 +201 -0
- package/README.md +383 -0
- package/build/index.js +54 -0
- package/build/resources/descriptors.js +77 -0
- package/build/resources/diff.js +241 -0
- package/build/resources/file.js +222 -0
- package/build/resources/history.js +242 -0
- package/build/resources/index.js +99 -0
- package/build/resources/repository.js +286 -0
- package/build/server.js +120 -0
- package/build/services/error-service.js +73 -0
- package/build/services/git-service.js +965 -0
- package/build/tools/advanced.js +526 -0
- package/build/tools/branch.js +296 -0
- package/build/tools/index.js +29 -0
- package/build/tools/remote.js +279 -0
- package/build/tools/repository.js +170 -0
- package/build/tools/workdir.js +445 -0
- package/build/types/git.js +7 -0
- package/build/utils/global-settings.js +64 -0
- package/build/utils/validation.js +108 -0
- package/package.json +39 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resource Descriptors
|
|
3
|
+
* ===================
|
|
4
|
+
*
|
|
5
|
+
* This module defines descriptors for Git MCP resources.
|
|
6
|
+
* These descriptions help both users and LLMs understand
|
|
7
|
+
* what each resource does and what data it returns.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Map of resource descriptors keyed by resource ID
|
|
11
|
+
*/
|
|
12
|
+
export const resourceDescriptors = {
|
|
13
|
+
// Repository resources
|
|
14
|
+
"repository-info": {
|
|
15
|
+
name: "Repository Information",
|
|
16
|
+
description: "Basic Git repository information including current branch, status, and reference details",
|
|
17
|
+
mimeType: "application/json"
|
|
18
|
+
},
|
|
19
|
+
"repository-branches": {
|
|
20
|
+
name: "Repository Branches",
|
|
21
|
+
description: "List of all branches in the repository with current branch indicator",
|
|
22
|
+
mimeType: "application/json"
|
|
23
|
+
},
|
|
24
|
+
"repository-remotes": {
|
|
25
|
+
name: "Repository Remotes",
|
|
26
|
+
description: "List of all configured remote repositories with their URLs",
|
|
27
|
+
mimeType: "application/json"
|
|
28
|
+
},
|
|
29
|
+
"repository-tags": {
|
|
30
|
+
name: "Repository Tags",
|
|
31
|
+
description: "List of all tags in the repository with their references",
|
|
32
|
+
mimeType: "application/json"
|
|
33
|
+
},
|
|
34
|
+
// File resources
|
|
35
|
+
"file-at-ref": {
|
|
36
|
+
name: "File Content",
|
|
37
|
+
description: "The content of a specific file at a given Git reference",
|
|
38
|
+
mimeType: "text/plain"
|
|
39
|
+
},
|
|
40
|
+
"directory-listing": {
|
|
41
|
+
name: "Directory Listing",
|
|
42
|
+
description: "List of files and directories at a specific path and reference",
|
|
43
|
+
mimeType: "application/json"
|
|
44
|
+
},
|
|
45
|
+
// Diff resources
|
|
46
|
+
"diff-refs": {
|
|
47
|
+
name: "Reference Diff",
|
|
48
|
+
description: "Diff between two Git references (commits, branches, tags)",
|
|
49
|
+
mimeType: "text/plain"
|
|
50
|
+
},
|
|
51
|
+
"diff-unstaged": {
|
|
52
|
+
name: "Unstaged Changes Diff",
|
|
53
|
+
description: "Diff of all unstaged changes in the working directory",
|
|
54
|
+
mimeType: "text/plain"
|
|
55
|
+
},
|
|
56
|
+
"diff-staged": {
|
|
57
|
+
name: "Staged Changes Diff",
|
|
58
|
+
description: "Diff of all staged changes in the index",
|
|
59
|
+
mimeType: "text/plain"
|
|
60
|
+
},
|
|
61
|
+
// History resources
|
|
62
|
+
"commit-log": {
|
|
63
|
+
name: "Commit History",
|
|
64
|
+
description: "Commit history log with author, date, and message details",
|
|
65
|
+
mimeType: "application/json"
|
|
66
|
+
},
|
|
67
|
+
"file-blame": {
|
|
68
|
+
name: "File Blame",
|
|
69
|
+
description: "Line-by-line attribution showing which commit last modified each line",
|
|
70
|
+
mimeType: "text/plain"
|
|
71
|
+
},
|
|
72
|
+
"commit-show": {
|
|
73
|
+
name: "Commit Details",
|
|
74
|
+
description: "Detailed information about a specific commit including diff changes",
|
|
75
|
+
mimeType: "text/plain"
|
|
76
|
+
}
|
|
77
|
+
};
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diff Resources
|
|
3
|
+
* =============
|
|
4
|
+
*
|
|
5
|
+
* MCP resources for exposing Git diff information.
|
|
6
|
+
*/
|
|
7
|
+
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
8
|
+
import { GitService } from '../services/git-service.js';
|
|
9
|
+
import { PathValidation } from '../utils/validation.js';
|
|
10
|
+
/**
|
|
11
|
+
* Helper function to ensure a variable is treated as a string
|
|
12
|
+
*
|
|
13
|
+
* @param value - The value to convert to string
|
|
14
|
+
* @returns A string representation of the value
|
|
15
|
+
*/
|
|
16
|
+
function ensureString(value) {
|
|
17
|
+
return Array.isArray(value) ? value[0] : value;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Registers diff resources with the MCP server
|
|
21
|
+
*
|
|
22
|
+
* @param server - MCP server instance
|
|
23
|
+
* @param resourceDescriptors - Resource descriptors for metadata
|
|
24
|
+
*/
|
|
25
|
+
export function setupDiffResources(server, resourceDescriptors) {
|
|
26
|
+
// Diff between two refs
|
|
27
|
+
server.resource("diff-refs", new ResourceTemplate("git://repo/{repoPath}/diff/{fromRef}/{toRef}?path={path}", { list: undefined }), {
|
|
28
|
+
name: "Reference Diff",
|
|
29
|
+
description: "Returns a diff between two Git references (commits, branches, tags)",
|
|
30
|
+
mimeType: "application/json" // Corrected MIME type
|
|
31
|
+
},
|
|
32
|
+
// Returns a diff between two references (branches, tags, or commits) with optional path filter
|
|
33
|
+
async (uri, variables) => {
|
|
34
|
+
try {
|
|
35
|
+
// Handle variables which might be arrays
|
|
36
|
+
const repoPathStr = ensureString(variables.repoPath);
|
|
37
|
+
const fromRefStr = ensureString(variables.fromRef);
|
|
38
|
+
const toRefStr = variables.toRef ? ensureString(variables.toRef) : 'HEAD';
|
|
39
|
+
const pathStr = variables.path ? ensureString(variables.path) : undefined;
|
|
40
|
+
// Normalize paths
|
|
41
|
+
const normalizedRepoPath = PathValidation.normalizePath(decodeURIComponent(repoPathStr));
|
|
42
|
+
const gitService = new GitService(normalizedRepoPath);
|
|
43
|
+
// Check if the path is a Git repository
|
|
44
|
+
const isRepo = await gitService.isGitRepository();
|
|
45
|
+
if (!isRepo) {
|
|
46
|
+
return {
|
|
47
|
+
contents: [{
|
|
48
|
+
uri: uri.href,
|
|
49
|
+
text: JSON.stringify({
|
|
50
|
+
error: "Not a Git repository",
|
|
51
|
+
repoPath: normalizedRepoPath,
|
|
52
|
+
fromRef: fromRefStr,
|
|
53
|
+
toRef: toRefStr,
|
|
54
|
+
path: pathStr
|
|
55
|
+
}, null, 2),
|
|
56
|
+
mimeType: "application/json"
|
|
57
|
+
}]
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// Get diff
|
|
61
|
+
const diffResult = await gitService.getDiff(fromRefStr, toRefStr, pathStr);
|
|
62
|
+
if (!diffResult.resultSuccessful) {
|
|
63
|
+
return {
|
|
64
|
+
contents: [{
|
|
65
|
+
uri: uri.href,
|
|
66
|
+
text: JSON.stringify({
|
|
67
|
+
error: diffResult.resultError.errorMessage,
|
|
68
|
+
repoPath: normalizedRepoPath,
|
|
69
|
+
fromRef: fromRefStr,
|
|
70
|
+
toRef: toRefStr,
|
|
71
|
+
path: pathStr
|
|
72
|
+
}, null, 2),
|
|
73
|
+
mimeType: "application/json"
|
|
74
|
+
}]
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
contents: [{
|
|
79
|
+
uri: uri.href,
|
|
80
|
+
text: JSON.stringify({
|
|
81
|
+
repoPath: normalizedRepoPath,
|
|
82
|
+
fromRef: fromRefStr,
|
|
83
|
+
toRef: toRefStr,
|
|
84
|
+
path: pathStr,
|
|
85
|
+
diff: diffResult.resultData
|
|
86
|
+
}, null, 2),
|
|
87
|
+
mimeType: "application/json"
|
|
88
|
+
}]
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
return {
|
|
93
|
+
contents: [{
|
|
94
|
+
uri: uri.href,
|
|
95
|
+
text: JSON.stringify({
|
|
96
|
+
error: error instanceof Error ? error.message : String(error),
|
|
97
|
+
repoPath: ensureString(variables.repoPath),
|
|
98
|
+
fromRef: ensureString(variables.fromRef),
|
|
99
|
+
toRef: variables.toRef ? ensureString(variables.toRef) : 'HEAD',
|
|
100
|
+
path: variables.path ? ensureString(variables.path) : undefined
|
|
101
|
+
}, null, 2),
|
|
102
|
+
mimeType: "application/json"
|
|
103
|
+
}]
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
// Diff in working directory (unstaged changes)
|
|
108
|
+
server.resource("diff-unstaged", new ResourceTemplate("git://repo/{repoPath}/diff-unstaged?path={path}", { list: undefined }), {
|
|
109
|
+
name: "Unstaged Changes Diff",
|
|
110
|
+
description: "Returns a diff of all unstaged changes in the working directory",
|
|
111
|
+
mimeType: "text/plain"
|
|
112
|
+
},
|
|
113
|
+
// Returns a diff of all unstaged changes (between working directory and index) with optional path filter
|
|
114
|
+
async (uri, variables) => {
|
|
115
|
+
try {
|
|
116
|
+
// Handle variables which might be arrays
|
|
117
|
+
const repoPathStr = ensureString(variables.repoPath);
|
|
118
|
+
const pathStr = variables.path ? ensureString(variables.path) : undefined;
|
|
119
|
+
// Normalize paths
|
|
120
|
+
const normalizedRepoPath = PathValidation.normalizePath(decodeURIComponent(repoPathStr));
|
|
121
|
+
const gitService = new GitService(normalizedRepoPath);
|
|
122
|
+
// Check if the path is a Git repository
|
|
123
|
+
const isRepo = await gitService.isGitRepository();
|
|
124
|
+
if (!isRepo) {
|
|
125
|
+
return {
|
|
126
|
+
contents: [{
|
|
127
|
+
uri: uri.href,
|
|
128
|
+
text: JSON.stringify({
|
|
129
|
+
error: "Not a Git repository",
|
|
130
|
+
repoPath: normalizedRepoPath,
|
|
131
|
+
path: pathStr
|
|
132
|
+
}, null, 2),
|
|
133
|
+
mimeType: "application/json"
|
|
134
|
+
}]
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
// Get unstaged diff
|
|
138
|
+
const diffResult = await gitService.getUnstagedDiff(pathStr);
|
|
139
|
+
if (!diffResult.resultSuccessful) {
|
|
140
|
+
return {
|
|
141
|
+
contents: [{
|
|
142
|
+
uri: uri.href,
|
|
143
|
+
text: JSON.stringify({
|
|
144
|
+
error: diffResult.resultError.errorMessage,
|
|
145
|
+
repoPath: normalizedRepoPath,
|
|
146
|
+
path: pathStr
|
|
147
|
+
}, null, 2),
|
|
148
|
+
mimeType: "application/json"
|
|
149
|
+
}]
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
contents: [{
|
|
154
|
+
uri: uri.href,
|
|
155
|
+
text: diffResult.resultData,
|
|
156
|
+
mimeType: "text/plain"
|
|
157
|
+
}]
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
return {
|
|
162
|
+
contents: [{
|
|
163
|
+
uri: uri.href,
|
|
164
|
+
text: JSON.stringify({
|
|
165
|
+
error: error instanceof Error ? error.message : String(error),
|
|
166
|
+
repoPath: ensureString(variables.repoPath),
|
|
167
|
+
path: variables.path ? ensureString(variables.path) : undefined
|
|
168
|
+
}, null, 2),
|
|
169
|
+
mimeType: "application/json"
|
|
170
|
+
}]
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
// Diff staged changes
|
|
175
|
+
server.resource("diff-staged", new ResourceTemplate("git://repo/{repoPath}/diff-staged?path={path}", { list: undefined }), {
|
|
176
|
+
name: "Staged Changes Diff",
|
|
177
|
+
description: "Returns a diff of all staged changes in the index",
|
|
178
|
+
mimeType: "text/plain"
|
|
179
|
+
},
|
|
180
|
+
// Returns a diff of all staged changes (between index and HEAD) with optional path filter
|
|
181
|
+
async (uri, variables) => {
|
|
182
|
+
try {
|
|
183
|
+
// Handle variables which might be arrays
|
|
184
|
+
const repoPathStr = ensureString(variables.repoPath);
|
|
185
|
+
const pathStr = variables.path ? ensureString(variables.path) : undefined;
|
|
186
|
+
// Normalize paths
|
|
187
|
+
const normalizedRepoPath = PathValidation.normalizePath(decodeURIComponent(repoPathStr));
|
|
188
|
+
const gitService = new GitService(normalizedRepoPath);
|
|
189
|
+
// Check if the path is a Git repository
|
|
190
|
+
const isRepo = await gitService.isGitRepository();
|
|
191
|
+
if (!isRepo) {
|
|
192
|
+
return {
|
|
193
|
+
contents: [{
|
|
194
|
+
uri: uri.href,
|
|
195
|
+
text: JSON.stringify({
|
|
196
|
+
error: "Not a Git repository",
|
|
197
|
+
repoPath: normalizedRepoPath,
|
|
198
|
+
path: pathStr
|
|
199
|
+
}, null, 2),
|
|
200
|
+
mimeType: "application/json"
|
|
201
|
+
}]
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
// Get staged diff
|
|
205
|
+
const diffResult = await gitService.getStagedDiff(pathStr);
|
|
206
|
+
if (!diffResult.resultSuccessful) {
|
|
207
|
+
return {
|
|
208
|
+
contents: [{
|
|
209
|
+
uri: uri.href,
|
|
210
|
+
text: JSON.stringify({
|
|
211
|
+
error: diffResult.resultError.errorMessage,
|
|
212
|
+
repoPath: normalizedRepoPath,
|
|
213
|
+
path: pathStr
|
|
214
|
+
}, null, 2),
|
|
215
|
+
mimeType: "application/json"
|
|
216
|
+
}]
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
contents: [{
|
|
221
|
+
uri: uri.href,
|
|
222
|
+
text: diffResult.resultData,
|
|
223
|
+
mimeType: "text/plain"
|
|
224
|
+
}]
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
return {
|
|
229
|
+
contents: [{
|
|
230
|
+
uri: uri.href,
|
|
231
|
+
text: JSON.stringify({
|
|
232
|
+
error: error instanceof Error ? error.message : String(error),
|
|
233
|
+
repoPath: ensureString(variables.repoPath),
|
|
234
|
+
path: variables.path ? ensureString(variables.path) : undefined
|
|
235
|
+
}, null, 2),
|
|
236
|
+
mimeType: "application/json"
|
|
237
|
+
}]
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Resources
|
|
3
|
+
* =============
|
|
4
|
+
*
|
|
5
|
+
* MCP resources for exposing Git file contents at specific references.
|
|
6
|
+
*/
|
|
7
|
+
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
8
|
+
import { GitService } from '../services/git-service.js';
|
|
9
|
+
import { PathValidation } from '../utils/validation.js';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
/**
|
|
12
|
+
* Helper function to ensure a variable is treated as a string
|
|
13
|
+
*
|
|
14
|
+
* @param value - The value to convert to string
|
|
15
|
+
* @returns A string representation of the value
|
|
16
|
+
*/
|
|
17
|
+
function ensureString(value) {
|
|
18
|
+
return Array.isArray(value) ? value[0] : value;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Registers file resources with the MCP server
|
|
22
|
+
*
|
|
23
|
+
* @param server - MCP server instance
|
|
24
|
+
* @param resourceDescriptors - Resource descriptors for metadata
|
|
25
|
+
*/
|
|
26
|
+
export function setupFileResources(server, resourceDescriptors) {
|
|
27
|
+
// File contents at a specific reference
|
|
28
|
+
server.resource("file-at-ref", new ResourceTemplate("git://repo/{repoPath}/file/{filePath}?ref={ref}", { list: undefined }), {
|
|
29
|
+
name: "File Content",
|
|
30
|
+
description: "Returns the content of a specific file at a given Git reference",
|
|
31
|
+
mimeType: "text/plain"
|
|
32
|
+
},
|
|
33
|
+
// Returns the content of a specific file at a given reference (branch, tag, or commit)
|
|
34
|
+
async (uri, variables) => {
|
|
35
|
+
try {
|
|
36
|
+
// Handle variables which might be arrays
|
|
37
|
+
const repoPathStr = ensureString(variables.repoPath);
|
|
38
|
+
const filePathStr = ensureString(variables.filePath);
|
|
39
|
+
const refStr = variables.ref ? ensureString(variables.ref) : 'HEAD';
|
|
40
|
+
// Normalize paths
|
|
41
|
+
const normalizedRepoPath = PathValidation.normalizePath(decodeURIComponent(repoPathStr));
|
|
42
|
+
const normalizedFilePath = PathValidation.normalizePath(decodeURIComponent(filePathStr));
|
|
43
|
+
const gitService = new GitService(normalizedRepoPath);
|
|
44
|
+
// Check if the path is a Git repository
|
|
45
|
+
const isRepo = await gitService.isGitRepository();
|
|
46
|
+
if (!isRepo) {
|
|
47
|
+
return {
|
|
48
|
+
contents: [{
|
|
49
|
+
uri: uri.href,
|
|
50
|
+
text: JSON.stringify({
|
|
51
|
+
error: "Not a Git repository",
|
|
52
|
+
repoPath: normalizedRepoPath,
|
|
53
|
+
filePath: normalizedFilePath,
|
|
54
|
+
ref: refStr
|
|
55
|
+
}, null, 2),
|
|
56
|
+
mimeType: "application/json"
|
|
57
|
+
}]
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// Get file content at reference
|
|
61
|
+
const fileResult = await gitService.getFileAtRef(normalizedFilePath, refStr);
|
|
62
|
+
if (!fileResult.resultSuccessful) {
|
|
63
|
+
return {
|
|
64
|
+
contents: [{
|
|
65
|
+
uri: uri.href,
|
|
66
|
+
text: JSON.stringify({
|
|
67
|
+
error: fileResult.resultError.errorMessage,
|
|
68
|
+
repoPath: normalizedRepoPath,
|
|
69
|
+
filePath: normalizedFilePath,
|
|
70
|
+
ref: refStr
|
|
71
|
+
}, null, 2),
|
|
72
|
+
mimeType: "application/json"
|
|
73
|
+
}]
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
// Detect MIME type based on file extension
|
|
77
|
+
const fileExtension = path.extname(normalizedFilePath).toLowerCase();
|
|
78
|
+
let mimeType = "text/plain";
|
|
79
|
+
// Simple MIME type detection
|
|
80
|
+
if (['.js', '.ts', '.jsx', '.tsx'].includes(fileExtension)) {
|
|
81
|
+
mimeType = "application/javascript";
|
|
82
|
+
}
|
|
83
|
+
else if (['.html', '.htm'].includes(fileExtension)) {
|
|
84
|
+
mimeType = "text/html";
|
|
85
|
+
}
|
|
86
|
+
else if (fileExtension === '.css') {
|
|
87
|
+
mimeType = "text/css";
|
|
88
|
+
}
|
|
89
|
+
else if (fileExtension === '.json') {
|
|
90
|
+
mimeType = "application/json";
|
|
91
|
+
}
|
|
92
|
+
else if (['.md', '.markdown'].includes(fileExtension)) {
|
|
93
|
+
mimeType = "text/markdown";
|
|
94
|
+
}
|
|
95
|
+
else if (['.xml', '.svg'].includes(fileExtension)) {
|
|
96
|
+
mimeType = "application/xml";
|
|
97
|
+
}
|
|
98
|
+
else if (['.yml', '.yaml'].includes(fileExtension)) {
|
|
99
|
+
mimeType = "text/yaml";
|
|
100
|
+
}
|
|
101
|
+
// Return the file content directly
|
|
102
|
+
return {
|
|
103
|
+
contents: [{
|
|
104
|
+
uri: uri.href,
|
|
105
|
+
text: fileResult.resultData,
|
|
106
|
+
mimeType
|
|
107
|
+
}]
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
return {
|
|
112
|
+
contents: [{
|
|
113
|
+
uri: uri.href,
|
|
114
|
+
text: JSON.stringify({
|
|
115
|
+
error: error instanceof Error ? error.message : String(error),
|
|
116
|
+
repoPath: ensureString(variables.repoPath),
|
|
117
|
+
filePath: ensureString(variables.filePath),
|
|
118
|
+
ref: variables.ref ? ensureString(variables.ref) : 'HEAD'
|
|
119
|
+
}, null, 2),
|
|
120
|
+
mimeType: "application/json"
|
|
121
|
+
}]
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
// List files in a directory at a specific reference
|
|
126
|
+
server.resource("directory-listing", new ResourceTemplate("git://repo/{repoPath}/ls/{dirPath}?ref={ref}", { list: undefined }), {
|
|
127
|
+
name: "Directory Listing",
|
|
128
|
+
description: "Returns a list of files and directories at a specific path and reference",
|
|
129
|
+
mimeType: "application/json"
|
|
130
|
+
},
|
|
131
|
+
// Returns a list of files and directories within a specific directory at a given reference
|
|
132
|
+
async (uri, variables) => {
|
|
133
|
+
try {
|
|
134
|
+
// Handle variables which might be arrays
|
|
135
|
+
const repoPathStr = ensureString(variables.repoPath);
|
|
136
|
+
const dirPathStr = ensureString(variables.dirPath || '');
|
|
137
|
+
const refStr = variables.ref ? ensureString(variables.ref) : 'HEAD';
|
|
138
|
+
// Normalize paths
|
|
139
|
+
const normalizedRepoPath = PathValidation.normalizePath(decodeURIComponent(repoPathStr));
|
|
140
|
+
const normalizedDirPath = PathValidation.normalizePath(decodeURIComponent(dirPathStr));
|
|
141
|
+
const gitService = new GitService(normalizedRepoPath);
|
|
142
|
+
// Check if the path is a Git repository
|
|
143
|
+
const isRepo = await gitService.isGitRepository();
|
|
144
|
+
if (!isRepo) {
|
|
145
|
+
return {
|
|
146
|
+
contents: [{
|
|
147
|
+
uri: uri.href,
|
|
148
|
+
text: JSON.stringify({
|
|
149
|
+
error: "Not a Git repository",
|
|
150
|
+
repoPath: normalizedRepoPath,
|
|
151
|
+
dirPath: normalizedDirPath,
|
|
152
|
+
ref: refStr
|
|
153
|
+
}, null, 2),
|
|
154
|
+
mimeType: "application/json"
|
|
155
|
+
}]
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
// Use Git command to get directory listing
|
|
159
|
+
try {
|
|
160
|
+
// Use the listFilesAtRef method from GitService
|
|
161
|
+
const filesResult = await gitService.listFilesAtRef(normalizedDirPath, refStr);
|
|
162
|
+
if (!filesResult.resultSuccessful) {
|
|
163
|
+
return {
|
|
164
|
+
contents: [{
|
|
165
|
+
uri: uri.href,
|
|
166
|
+
text: JSON.stringify({
|
|
167
|
+
error: filesResult.resultError.errorMessage,
|
|
168
|
+
repoPath: normalizedRepoPath,
|
|
169
|
+
dirPath: normalizedDirPath,
|
|
170
|
+
ref: refStr
|
|
171
|
+
}, null, 2),
|
|
172
|
+
mimeType: "application/json"
|
|
173
|
+
}]
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
// The listFilesAtRef method now returns only immediate children,
|
|
177
|
+
// so no need to strip prefixes here.
|
|
178
|
+
const files = filesResult.resultData;
|
|
179
|
+
return {
|
|
180
|
+
contents: [{
|
|
181
|
+
uri: uri.href,
|
|
182
|
+
text: JSON.stringify({
|
|
183
|
+
repoPath: normalizedRepoPath,
|
|
184
|
+
dirPath: normalizedDirPath,
|
|
185
|
+
ref: refStr,
|
|
186
|
+
files
|
|
187
|
+
}, null, 2),
|
|
188
|
+
mimeType: "application/json"
|
|
189
|
+
}]
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
catch (gitError) {
|
|
193
|
+
return {
|
|
194
|
+
contents: [{
|
|
195
|
+
uri: uri.href,
|
|
196
|
+
text: JSON.stringify({
|
|
197
|
+
error: gitError instanceof Error ? gitError.message : String(gitError),
|
|
198
|
+
repoPath: normalizedRepoPath,
|
|
199
|
+
dirPath: normalizedDirPath,
|
|
200
|
+
ref: refStr
|
|
201
|
+
}, null, 2),
|
|
202
|
+
mimeType: "application/json"
|
|
203
|
+
}]
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
return {
|
|
209
|
+
contents: [{
|
|
210
|
+
uri: uri.href,
|
|
211
|
+
text: JSON.stringify({
|
|
212
|
+
error: error instanceof Error ? error.message : String(error),
|
|
213
|
+
repoPath: ensureString(variables.repoPath),
|
|
214
|
+
dirPath: variables.dirPath ? ensureString(variables.dirPath) : '',
|
|
215
|
+
ref: variables.ref ? ensureString(variables.ref) : 'HEAD'
|
|
216
|
+
}, null, 2),
|
|
217
|
+
mimeType: "application/json"
|
|
218
|
+
}]
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|