@j0hanz/filesystem-mcp 1.13.2 → 1.14.1
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 +162 -145
- package/dist/cli.js +2 -2
- package/dist/completions.js +54 -51
- package/dist/config.d.ts +13 -14
- package/dist/config.js +12 -12
- package/dist/index.js +1 -1
- package/dist/lib/abort.d.ts +7 -0
- package/dist/lib/abort.js +81 -0
- package/dist/lib/constants.d.ts +3 -1
- package/dist/lib/constants.js +8 -2
- package/dist/lib/errors.d.ts +7 -3
- package/dist/lib/errors.js +64 -41
- package/dist/lib/file-operations/core.d.ts +3 -3
- package/dist/lib/file-operations/core.js +23 -20
- package/dist/lib/file-operations/metadata.d.ts +2 -2
- package/dist/lib/file-operations/metadata.js +69 -22
- package/dist/lib/file-operations/search.d.ts +0 -1
- package/dist/lib/file-operations/search.js +87 -95
- package/dist/lib/file-operations/traversal.js +13 -15
- package/dist/lib/fs-helpers.d.ts +3 -10
- package/dist/lib/fs-helpers.js +29 -108
- package/dist/lib/globs.d.ts +2 -0
- package/dist/lib/globs.js +19 -0
- package/dist/lib/logger.d.ts +28 -0
- package/dist/lib/logger.js +91 -0
- package/dist/lib/observability.d.ts +7 -0
- package/dist/lib/observability.js +19 -9
- package/dist/lib/paths.js +55 -55
- package/dist/lib/resource-store.js +4 -4
- package/dist/lib/utils.d.ts +0 -12
- package/dist/lib/utils.js +0 -13
- package/dist/lib/zod-codecs.d.ts +2 -0
- package/dist/lib/zod-codecs.js +18 -0
- package/dist/pkg-info.d.ts +1 -0
- package/dist/pkg-info.js +2 -2
- package/dist/prompts.js +3 -3
- package/dist/resources/generated-instructions.js +41 -41
- package/dist/resources/tool-catalog.js +33 -58
- package/dist/resources/tool-info.d.ts +0 -1
- package/dist/resources/tool-info.js +44 -67
- package/dist/resources/workflows.js +47 -19
- package/dist/resources.d.ts +1 -1
- package/dist/resources.js +4 -4
- package/dist/schemas.d.ts +185 -465
- package/dist/schemas.js +174 -206
- package/dist/server/bootstrap.d.ts +12 -11
- package/dist/server/bootstrap.js +95 -86
- package/dist/server/roots-manager.d.ts +5 -2
- package/dist/server/roots-manager.js +9 -7
- package/dist/server/task-store.d.ts +10 -0
- package/dist/server/task-store.js +73 -0
- package/dist/tools/apply-patch.js +39 -20
- package/dist/tools/calculate-hash.js +14 -27
- package/dist/tools/create-directory.js +11 -9
- package/dist/tools/delete-file.js +19 -19
- package/dist/tools/diff-files.js +16 -18
- package/dist/tools/edit-file.js +11 -5
- package/dist/tools/list-directory.js +16 -21
- package/dist/tools/move-file.js +105 -100
- package/dist/tools/read-multiple.js +15 -10
- package/dist/tools/read.js +6 -7
- package/dist/tools/replace-in-files.js +76 -115
- package/dist/tools/roots.js +3 -7
- package/dist/tools/search-content.js +158 -203
- package/dist/tools/search-files.js +59 -50
- package/dist/tools/shared.d.ts +10 -0
- package/dist/tools/shared.js +105 -36
- package/dist/tools/stat-many.js +15 -9
- package/dist/tools/stat.js +6 -6
- package/dist/tools/task-support.d.ts +10 -9
- package/dist/tools/task-support.js +94 -23
- package/dist/tools/tree.js +4 -4
- package/dist/tools/write-file.js +11 -12
- package/package.json +10 -9
package/dist/schemas.js
CHANGED
|
@@ -1,31 +1,32 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { DEFAULT_LIST_MAX_ENTRIES, DEFAULT_SEARCH_CONTENT_RESULTS, DEFAULT_SEARCH_RESULTS, DEFAULT_TREE_DEPTH, DEFAULT_TREE_ENTRIES, MAX_LIST_ENTRIES, MAX_SEARCH_DEPTH, MAX_SEARCH_RESULTS, MAX_TREE_DEPTH, MAX_TREE_ENTRIES, } from './lib/constants.js';
|
|
3
3
|
import { ErrorCode } from './lib/errors.js';
|
|
4
|
-
|
|
5
|
-
if (value.length === 0)
|
|
6
|
-
return false;
|
|
7
|
-
if (value.includes('**/**/**'))
|
|
8
|
-
return false;
|
|
9
|
-
const absolutePattern = /^([/\\]|[A-Za-z]:[/\\]|\\\\)/u;
|
|
10
|
-
if (absolutePattern.test(value)) {
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
|
-
if (/[\\/]\.\.(?:[/\\]|$)/u.test(value) || value.startsWith('..')) {
|
|
14
|
-
return false;
|
|
15
|
-
}
|
|
16
|
-
return true;
|
|
17
|
-
}
|
|
4
|
+
import { isSafeGlobPattern } from './lib/globs.js';
|
|
18
5
|
const MAX_PATH_LENGTH = 4096;
|
|
19
6
|
const DESC_PATH_ROOT = 'Base directory (default: root). Absolute path required if multiple roots.';
|
|
20
7
|
const DESC_PATH_REQUIRED = 'Absolute path to file or directory.';
|
|
21
8
|
function defaultFalseBoolean(description) {
|
|
22
9
|
return z.boolean().optional().default(false).describe(description);
|
|
23
10
|
}
|
|
11
|
+
const SuccessFlagSchema = z.literal(true);
|
|
12
|
+
const NonNegativeIntegerSchema = z.int().min(0, 'Min: 0');
|
|
13
|
+
const PositiveIntegerSchema = z.int().min(1, 'Min: 1');
|
|
14
|
+
const IsoDateTimeSchema = z.iso.datetime();
|
|
15
|
+
const Sha256HexSchema = z
|
|
16
|
+
.string()
|
|
17
|
+
.regex(/^[a-f0-9]{64}$/u, 'Expected SHA-256 hex digest');
|
|
24
18
|
const PathSchemaBase = z
|
|
25
19
|
.string()
|
|
26
20
|
.max(MAX_PATH_LENGTH, `Path too long (max ${MAX_PATH_LENGTH} chars)`);
|
|
27
21
|
const OptionalPathSchema = PathSchemaBase.optional();
|
|
28
22
|
const RequiredPathSchema = PathSchemaBase.min(1, 'Path required');
|
|
23
|
+
const SafeGlobPatternSchema = z
|
|
24
|
+
.string()
|
|
25
|
+
.min(1, 'Pattern required')
|
|
26
|
+
.max(1000, 'Max 1000 chars')
|
|
27
|
+
.refine((val) => isSafeGlobPattern(val), {
|
|
28
|
+
error: 'Invalid glob or unsafe path (absolute/.. forbidden)',
|
|
29
|
+
});
|
|
29
30
|
const FileTypeSchema = z.enum(['file', 'directory', 'symlink', 'other']);
|
|
30
31
|
const ListDirectorySortSchema = z.enum(['name', 'size', 'modified', 'type']);
|
|
31
32
|
const SearchFilesSortSchema = z.enum(['name', 'size', 'modified', 'path']);
|
|
@@ -43,7 +44,7 @@ const TreeEntrySchema = z.lazy(() => z.strictObject({
|
|
|
43
44
|
children: z.array(TreeEntrySchema).optional().describe('Children'),
|
|
44
45
|
}));
|
|
45
46
|
const ErrorSchema = z.strictObject({
|
|
46
|
-
code: z.enum(ErrorCode).describe('Error code (e.g.
|
|
47
|
+
code: z.enum(ErrorCode).describe('Error code (e.g. NOT_FOUND)'),
|
|
47
48
|
message: z.string().describe('Human-readable message'),
|
|
48
49
|
path: z.string().optional().describe('Relevant path'),
|
|
49
50
|
suggestion: z.string().optional().describe('Fix suggestion'),
|
|
@@ -61,11 +62,12 @@ const TailLinesSchema = z
|
|
|
61
62
|
.optional()
|
|
62
63
|
.describe('Read last N lines');
|
|
63
64
|
const LineNumberSchema = z.int({ error: 'Must be integer' }).min(1, 'Min: 1');
|
|
64
|
-
function addReadRangeIssue(ctx, path, message) {
|
|
65
|
+
function addReadRangeIssue(ctx, input, path, message) {
|
|
65
66
|
ctx.addIssue({
|
|
66
67
|
code: 'custom',
|
|
67
68
|
path: [path],
|
|
68
69
|
message,
|
|
70
|
+
input,
|
|
69
71
|
});
|
|
70
72
|
}
|
|
71
73
|
const validateReadRange = (value, ctx) => {
|
|
@@ -74,18 +76,14 @@ const validateReadRange = (value, ctx) => {
|
|
|
74
76
|
const hasStart = value.startLine !== undefined;
|
|
75
77
|
const hasEnd = value.endLine !== undefined;
|
|
76
78
|
if (hasHead && (hasStart || hasEnd)) {
|
|
77
|
-
addReadRangeIssue(ctx, 'head', "Cannot use 'head' with 'startLine'/'endLine'");
|
|
79
|
+
addReadRangeIssue(ctx, value, 'head', "Cannot use 'head' with 'startLine'/'endLine'");
|
|
78
80
|
}
|
|
79
81
|
if (hasTail && (hasHead || hasStart || hasEnd)) {
|
|
80
|
-
addReadRangeIssue(ctx, 'tail', "Cannot use 'tail' with 'head'/'startLine'/'endLine'");
|
|
82
|
+
addReadRangeIssue(ctx, value, 'tail', "Cannot use 'tail' with 'head'/'startLine'/'endLine'");
|
|
81
83
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if (value.startLine !== undefined &&
|
|
86
|
-
value.endLine !== undefined &&
|
|
87
|
-
value.endLine < value.startLine) {
|
|
88
|
-
addReadRangeIssue(ctx, 'endLine', "'endLine' must be >= 'startLine'");
|
|
84
|
+
const effectiveStart = value.startLine ?? 1;
|
|
85
|
+
if (value.endLine !== undefined && value.endLine < effectiveStart) {
|
|
86
|
+
addReadRangeIssue(ctx, value, 'endLine', "'endLine' must be >= 'startLine'");
|
|
89
87
|
}
|
|
90
88
|
};
|
|
91
89
|
function createReadRangeInputFields(descriptions) {
|
|
@@ -100,20 +98,20 @@ const FileInfoSchema = z.strictObject({
|
|
|
100
98
|
name: z.string().describe('Name'),
|
|
101
99
|
path: z.string().describe('Absolute path'),
|
|
102
100
|
type: FileTypeSchema.describe('Type'),
|
|
103
|
-
size:
|
|
104
|
-
tokenEstimate:
|
|
105
|
-
created:
|
|
106
|
-
modified:
|
|
107
|
-
accessed:
|
|
101
|
+
size: NonNegativeIntegerSchema.describe('Size (bytes)'),
|
|
102
|
+
tokenEstimate: NonNegativeIntegerSchema.optional().describe('Est. tokens (size/4)'),
|
|
103
|
+
created: IsoDateTimeSchema.describe('Created'),
|
|
104
|
+
modified: IsoDateTimeSchema.describe('Modified'),
|
|
105
|
+
accessed: IsoDateTimeSchema.describe('Accessed'),
|
|
108
106
|
permissions: z.string().describe('Permissions'),
|
|
109
107
|
isHidden: z.boolean().describe('Hidden?'),
|
|
110
108
|
mimeType: z.string().optional().describe('MIME type'),
|
|
111
109
|
symlinkTarget: z.string().optional().describe('Target (symlink)'),
|
|
112
110
|
});
|
|
113
111
|
const OperationSummarySchema = z.strictObject({
|
|
114
|
-
total:
|
|
115
|
-
succeeded:
|
|
116
|
-
failed:
|
|
112
|
+
total: NonNegativeIntegerSchema.describe('Total'),
|
|
113
|
+
succeeded: NonNegativeIntegerSchema.describe('Succeeded'),
|
|
114
|
+
failed: NonNegativeIntegerSchema.describe('Failed'),
|
|
117
115
|
});
|
|
118
116
|
export const ListDirectoryInputSchema = z.strictObject({
|
|
119
117
|
path: OptionalPathSchema.describe(DESC_PATH_ROOT),
|
|
@@ -135,12 +133,7 @@ export const ListDirectoryInputSchema = z.strictObject({
|
|
|
135
133
|
sortBy: ListDirectorySortSchema.optional()
|
|
136
134
|
.default('name')
|
|
137
135
|
.describe('Sort field (name, size, modified, type)'),
|
|
138
|
-
pattern:
|
|
139
|
-
.string()
|
|
140
|
-
.min(1, 'Pattern required')
|
|
141
|
-
.max(1000, 'Max 1000 chars')
|
|
142
|
-
.optional()
|
|
143
|
-
.describe('Optional glob pattern filter (e.g. "**/*.ts")'),
|
|
136
|
+
pattern: SafeGlobPatternSchema.optional().describe('Optional glob pattern filter (e.g. "**/*.ts")'),
|
|
144
137
|
includeSymlinkTargets: defaultFalseBoolean('Resolve and include symlink targets in results'),
|
|
145
138
|
cursor: z
|
|
146
139
|
.string()
|
|
@@ -152,14 +145,7 @@ export const ListAllowedDirectoriesInputSchema = z
|
|
|
152
145
|
.describe('No input parameters.');
|
|
153
146
|
export const SearchFilesInputSchema = z.strictObject({
|
|
154
147
|
path: OptionalPathSchema.describe(DESC_PATH_ROOT),
|
|
155
|
-
pattern:
|
|
156
|
-
.string()
|
|
157
|
-
.min(1, 'Pattern required')
|
|
158
|
-
.max(1000, 'Max 1000 chars')
|
|
159
|
-
.refine((val) => isSafeGlobPattern(val), {
|
|
160
|
-
error: 'Invalid glob or unsafe path (absolute/.. forbidden)',
|
|
161
|
-
})
|
|
162
|
-
.describe('Glob pattern (e.g. "**/*.ts", "src/*.js")'),
|
|
148
|
+
pattern: SafeGlobPatternSchema.describe('Glob pattern (e.g. "**/*.ts", "src/*.js")'),
|
|
163
149
|
maxResults: z
|
|
164
150
|
.int({ error: 'Must be integer' })
|
|
165
151
|
.min(1, 'Min: 1')
|
|
@@ -227,16 +213,11 @@ export const SearchContentInputSchema = z.strictObject({
|
|
|
227
213
|
.optional()
|
|
228
214
|
.default(DEFAULT_SEARCH_CONTENT_RESULTS)
|
|
229
215
|
.describe(`Maximum match rows to return. Default: ${DEFAULT_SEARCH_CONTENT_RESULTS}`),
|
|
230
|
-
filePattern:
|
|
231
|
-
.string()
|
|
232
|
-
.min(1, 'Pattern required')
|
|
233
|
-
.max(1000, 'Max 1000 chars')
|
|
234
|
-
.optional()
|
|
216
|
+
filePattern: SafeGlobPatternSchema.optional()
|
|
235
217
|
.default('**/*')
|
|
236
218
|
.describe('Glob for candidate files (e.g. "**/*.ts")'),
|
|
237
219
|
includeHidden: defaultFalseBoolean('Include hidden items (starting with .)'),
|
|
238
220
|
includeIgnored: defaultFalseBoolean('Include ignored items (node_modules, etc).'),
|
|
239
|
-
multiline: defaultFalseBoolean('Multi-line mode. ^ and $ match line boundaries when isRegex=true.'),
|
|
240
221
|
});
|
|
241
222
|
export const ReadFileInputSchema = z
|
|
242
223
|
.strictObject({
|
|
@@ -244,13 +225,13 @@ export const ReadFileInputSchema = z
|
|
|
244
225
|
...createReadRangeInputFields({
|
|
245
226
|
head: 'Read first N lines (preview)',
|
|
246
227
|
tail: 'Read last N lines',
|
|
247
|
-
startLine: 'Start line (1-based, inclusive)',
|
|
248
|
-
endLine: 'End line (1-based, inclusive).
|
|
228
|
+
startLine: 'Start line (1-based, inclusive). Defaults to 1 when endLine is set.',
|
|
229
|
+
endLine: 'End line (1-based, inclusive). Defaults to last line when startLine is set.',
|
|
249
230
|
}),
|
|
250
231
|
includeHash: defaultFalseBoolean('Include SHA-256 hash of full file content'),
|
|
251
232
|
})
|
|
252
233
|
.superRefine(validateReadRange)
|
|
253
|
-
.describe("Use one read mode only: 'head', 'tail', or 'startLine'/'endLine'.
|
|
234
|
+
.describe("Use one read mode only: 'head', 'tail', or 'startLine'/'endLine'.");
|
|
254
235
|
export const ReadMultipleFilesInputSchema = z
|
|
255
236
|
.strictObject({
|
|
256
237
|
paths: z
|
|
@@ -261,12 +242,12 @@ export const ReadMultipleFilesInputSchema = z
|
|
|
261
242
|
...createReadRangeInputFields({
|
|
262
243
|
head: 'Read first N lines of each file',
|
|
263
244
|
tail: 'Read last N lines of each file',
|
|
264
|
-
startLine: 'Start line (1-based, inclusive) per file',
|
|
265
|
-
endLine: 'End line (1-based, inclusive) per file.
|
|
245
|
+
startLine: 'Start line (1-based, inclusive) per file. Defaults to 1 when endLine is set.',
|
|
246
|
+
endLine: 'End line (1-based, inclusive) per file. Defaults to last line when startLine is set.',
|
|
266
247
|
}),
|
|
267
248
|
})
|
|
268
249
|
.superRefine(validateReadRange)
|
|
269
|
-
.describe("Use one read mode only: 'head', 'tail', or 'startLine'/'endLine'.
|
|
250
|
+
.describe("Use one read mode only: 'head', 'tail', or 'startLine'/'endLine'.");
|
|
270
251
|
export const GetFileInfoInputSchema = z.strictObject({
|
|
271
252
|
path: RequiredPathSchema.describe(DESC_PATH_REQUIRED),
|
|
272
253
|
});
|
|
@@ -278,62 +259,49 @@ export const GetMultipleFileInfoInputSchema = z.strictObject({
|
|
|
278
259
|
.describe('File/directory paths. e.g. ["src", "lib"]'),
|
|
279
260
|
});
|
|
280
261
|
export const ListAllowedDirectoriesOutputSchema = z.strictObject({
|
|
281
|
-
ok:
|
|
262
|
+
ok: SuccessFlagSchema,
|
|
282
263
|
directories: z.array(z.string()).optional().describe('Allowed directories'),
|
|
283
|
-
rootsCount: z.number().optional().describe('Number of roots'),
|
|
284
|
-
hasMultipleRoots: z
|
|
285
|
-
.boolean()
|
|
286
|
-
.optional()
|
|
287
|
-
.describe('Multiple roots configured'),
|
|
288
|
-
error: ErrorSchema.optional(),
|
|
289
264
|
});
|
|
290
265
|
export const ListDirectoryOutputSchema = z.strictObject({
|
|
291
|
-
ok:
|
|
266
|
+
ok: SuccessFlagSchema,
|
|
292
267
|
path: z.string().optional(),
|
|
293
268
|
entries: z
|
|
294
269
|
.array(z.strictObject({
|
|
295
270
|
name: z.string().describe('Entry name'),
|
|
296
271
|
relativePath: z.string().optional(),
|
|
297
272
|
type: FileTypeSchema,
|
|
298
|
-
size:
|
|
299
|
-
modified:
|
|
273
|
+
size: NonNegativeIntegerSchema.optional(),
|
|
274
|
+
modified: IsoDateTimeSchema.optional(),
|
|
300
275
|
}))
|
|
301
276
|
.optional(),
|
|
302
|
-
totalEntries:
|
|
277
|
+
totalEntries: NonNegativeIntegerSchema.optional(),
|
|
303
278
|
truncated: z.boolean().optional(),
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
totalFiles: z.number().optional(),
|
|
307
|
-
totalDirectories: z.number().optional(),
|
|
308
|
-
maxDepthReached: z.number().optional(),
|
|
279
|
+
totalFiles: NonNegativeIntegerSchema.optional(),
|
|
280
|
+
totalDirectories: NonNegativeIntegerSchema.optional(),
|
|
309
281
|
stoppedReason: ListDirectoryStopReasonSchema.optional(),
|
|
310
|
-
skippedInaccessible:
|
|
311
|
-
symlinksNotFollowed: z.number().optional(),
|
|
282
|
+
skippedInaccessible: NonNegativeIntegerSchema.optional(),
|
|
312
283
|
nextCursor: z
|
|
313
284
|
.string()
|
|
314
285
|
.optional()
|
|
315
286
|
.describe('Cursor for the next page; absent on the final page'),
|
|
316
|
-
error: ErrorSchema.optional(),
|
|
317
287
|
});
|
|
318
288
|
const SearchSummarySchema = z.strictObject({
|
|
319
|
-
totalMatches:
|
|
289
|
+
totalMatches: NonNegativeIntegerSchema.optional().describe('Total matches found'),
|
|
320
290
|
truncated: z.boolean().optional().describe('Results truncated?'),
|
|
321
291
|
resourceUri: z.string().optional().describe('Full results URI'),
|
|
322
|
-
error: ErrorSchema.optional(),
|
|
323
292
|
});
|
|
324
293
|
export const SearchFilesOutputSchema = SearchSummarySchema.extend({
|
|
325
|
-
ok:
|
|
294
|
+
ok: SuccessFlagSchema,
|
|
326
295
|
root: z.string().optional().describe('Search root'),
|
|
327
|
-
pattern: z.string().optional().describe('Glob pattern used'),
|
|
328
296
|
results: z
|
|
329
297
|
.array(z.strictObject({
|
|
330
298
|
path: z.string().describe('Relative path'),
|
|
331
|
-
size:
|
|
332
|
-
modified:
|
|
299
|
+
size: NonNegativeIntegerSchema.optional(),
|
|
300
|
+
modified: IsoDateTimeSchema.optional(),
|
|
333
301
|
}))
|
|
334
302
|
.optional(),
|
|
335
|
-
filesScanned:
|
|
336
|
-
skippedInaccessible:
|
|
303
|
+
filesScanned: NonNegativeIntegerSchema.optional().describe('Files scanned'),
|
|
304
|
+
skippedInaccessible: NonNegativeIntegerSchema.optional().describe('Inaccessible files'),
|
|
337
305
|
stoppedReason: SearchStopReasonSchema.optional().describe('Why search stopped'),
|
|
338
306
|
nextCursor: z
|
|
339
307
|
.string()
|
|
@@ -341,70 +309,49 @@ export const SearchFilesOutputSchema = SearchSummarySchema.extend({
|
|
|
341
309
|
.describe('Cursor for the next page; absent on the final page'),
|
|
342
310
|
});
|
|
343
311
|
export const SearchContentOutputSchema = SearchSummarySchema.extend({
|
|
344
|
-
ok:
|
|
345
|
-
patternType: z
|
|
346
|
-
.enum(['literal', 'regex'])
|
|
347
|
-
.optional()
|
|
348
|
-
.describe('Pattern interpretation'),
|
|
349
|
-
caseSensitive: z.boolean().optional().describe('Case-sensitive matching'),
|
|
312
|
+
ok: SuccessFlagSchema,
|
|
350
313
|
matches: z
|
|
351
314
|
.array(z.strictObject({
|
|
352
315
|
file: z.string().describe('Relative path'),
|
|
353
|
-
line:
|
|
354
|
-
column:
|
|
355
|
-
.number()
|
|
356
|
-
.optional()
|
|
357
|
-
.describe('Column of first match (0-based)'),
|
|
316
|
+
line: PositiveIntegerSchema,
|
|
317
|
+
column: NonNegativeIntegerSchema.optional().describe('Column of first match (0-based)'),
|
|
358
318
|
content: z.string(),
|
|
359
|
-
matchCount:
|
|
319
|
+
matchCount: PositiveIntegerSchema,
|
|
360
320
|
contextBefore: z.array(z.string()).optional(),
|
|
361
321
|
contextAfter: z.array(z.string()).optional(),
|
|
362
322
|
}))
|
|
363
323
|
.optional(),
|
|
364
|
-
filesScanned:
|
|
365
|
-
filesMatched:
|
|
366
|
-
skippedTooLarge:
|
|
367
|
-
skippedBinary:
|
|
368
|
-
skippedInaccessible:
|
|
369
|
-
.number()
|
|
370
|
-
.optional()
|
|
371
|
-
.describe('Files skipped: inaccessible'),
|
|
372
|
-
linesSkippedDueToRegexTimeout: z
|
|
373
|
-
.number()
|
|
374
|
-
.optional()
|
|
375
|
-
.describe('Lines skipped due to regex timeout'),
|
|
324
|
+
filesScanned: NonNegativeIntegerSchema.optional().describe('Files scanned'),
|
|
325
|
+
filesMatched: NonNegativeIntegerSchema.optional().describe('Files with matches'),
|
|
326
|
+
skippedTooLarge: NonNegativeIntegerSchema.optional().describe('Files skipped: too large'),
|
|
327
|
+
skippedBinary: NonNegativeIntegerSchema.optional().describe('Files skipped: binary'),
|
|
328
|
+
skippedInaccessible: NonNegativeIntegerSchema.optional().describe('Files skipped: inaccessible'),
|
|
376
329
|
stoppedReason: SearchStopReasonSchema.optional().describe('Why search stopped'),
|
|
377
330
|
});
|
|
378
331
|
export const TreeOutputSchema = z.strictObject({
|
|
379
|
-
ok:
|
|
332
|
+
ok: SuccessFlagSchema,
|
|
380
333
|
root: z.string().optional(),
|
|
381
334
|
tree: TreeEntrySchema.optional(),
|
|
382
335
|
ascii: z.string().optional(),
|
|
383
336
|
truncated: z.boolean().optional(),
|
|
384
|
-
totalEntries:
|
|
385
|
-
error: ErrorSchema.optional(),
|
|
337
|
+
totalEntries: NonNegativeIntegerSchema.optional(),
|
|
386
338
|
});
|
|
387
339
|
const ReadResultSchema = z.strictObject({
|
|
388
340
|
content: z.string().optional().describe('Content'),
|
|
389
341
|
truncated: z.boolean().optional().describe('Truncated?'),
|
|
390
342
|
resourceUri: z.string().optional().describe('Full content URI'),
|
|
391
|
-
totalLines:
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
tail: z.number().optional().describe('Tail lines'),
|
|
398
|
-
startLine: z.number().optional().describe('Start line'),
|
|
399
|
-
endLine: z.number().optional().describe('End line'),
|
|
400
|
-
linesRead: z.number().optional().describe('Lines read'),
|
|
343
|
+
totalLines: NonNegativeIntegerSchema.optional().describe('Total lines'),
|
|
344
|
+
head: PositiveIntegerSchema.optional().describe('Head lines'),
|
|
345
|
+
tail: PositiveIntegerSchema.optional().describe('Tail lines'),
|
|
346
|
+
startLine: PositiveIntegerSchema.optional().describe('Start line'),
|
|
347
|
+
endLine: PositiveIntegerSchema.optional().describe('End line'),
|
|
348
|
+
linesRead: NonNegativeIntegerSchema.optional().describe('Lines read'),
|
|
401
349
|
hasMoreLines: z.boolean().optional().describe('More lines?'),
|
|
402
350
|
});
|
|
403
351
|
export const ReadFileOutputSchema = ReadResultSchema.extend({
|
|
404
|
-
ok:
|
|
352
|
+
ok: SuccessFlagSchema,
|
|
405
353
|
path: z.string().optional(),
|
|
406
|
-
contentHash:
|
|
407
|
-
error: ErrorSchema.optional(),
|
|
354
|
+
contentHash: Sha256HexSchema.optional().describe('SHA-256 of full file content'),
|
|
408
355
|
});
|
|
409
356
|
const ReadMultipleFileResultSchema = ReadResultSchema.extend({
|
|
410
357
|
path: z.string().describe('File path'),
|
|
@@ -412,60 +359,77 @@ const ReadMultipleFileResultSchema = ReadResultSchema.extend({
|
|
|
412
359
|
.enum(['head', 'tail', 'range', 'externalized'])
|
|
413
360
|
.optional()
|
|
414
361
|
.describe('Why content was truncated'),
|
|
415
|
-
|
|
416
|
-
error: z.string().optional().describe('Error message'),
|
|
362
|
+
error: ErrorSchema.optional().describe('Structured error details'),
|
|
417
363
|
});
|
|
418
364
|
export const ReadMultipleFilesOutputSchema = z.strictObject({
|
|
419
|
-
ok:
|
|
365
|
+
ok: SuccessFlagSchema,
|
|
420
366
|
results: z.array(ReadMultipleFileResultSchema).optional(),
|
|
421
367
|
summary: OperationSummarySchema.optional(),
|
|
422
|
-
error: ErrorSchema.optional(),
|
|
423
368
|
});
|
|
424
369
|
export const GetFileInfoOutputSchema = z.strictObject({
|
|
425
|
-
ok:
|
|
370
|
+
ok: SuccessFlagSchema,
|
|
426
371
|
info: FileInfoSchema.optional(),
|
|
427
|
-
error: ErrorSchema.optional(),
|
|
428
372
|
});
|
|
429
373
|
export const GetMultipleFileInfoOutputSchema = z.strictObject({
|
|
430
|
-
ok:
|
|
374
|
+
ok: SuccessFlagSchema,
|
|
431
375
|
results: z
|
|
432
376
|
.array(z.strictObject({
|
|
433
377
|
path: z.string(),
|
|
434
378
|
info: FileInfoSchema.optional(),
|
|
435
|
-
error:
|
|
379
|
+
error: ErrorSchema.optional(),
|
|
436
380
|
}))
|
|
437
381
|
.optional(),
|
|
438
382
|
summary: OperationSummarySchema.optional(),
|
|
439
|
-
error: ErrorSchema.optional(),
|
|
440
383
|
});
|
|
441
384
|
export const CreateDirectoryInputSchema = z
|
|
442
385
|
.strictObject({
|
|
443
386
|
path: RequiredPathSchema.optional().describe(DESC_PATH_REQUIRED),
|
|
444
387
|
paths: z
|
|
445
388
|
.array(RequiredPathSchema)
|
|
389
|
+
.min(1, 'Min 1 path required')
|
|
446
390
|
.optional()
|
|
447
391
|
.describe('Absolute paths to directories to create'),
|
|
448
392
|
})
|
|
449
|
-
.
|
|
450
|
-
|
|
451
|
-
|
|
393
|
+
.superRefine((data, ctx) => {
|
|
394
|
+
const hasPath = data.path !== undefined;
|
|
395
|
+
const hasPaths = data.paths !== undefined;
|
|
396
|
+
if (!hasPath && !hasPaths) {
|
|
397
|
+
ctx.addIssue({
|
|
398
|
+
code: 'custom',
|
|
399
|
+
path: ['path'],
|
|
400
|
+
message: "Either 'path' or 'paths' must be provided",
|
|
401
|
+
input: data,
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
if (hasPath && hasPaths) {
|
|
405
|
+
ctx.addIssue({
|
|
406
|
+
code: 'custom',
|
|
407
|
+
path: ['path'],
|
|
408
|
+
message: "Provide either 'path' or 'paths', not both",
|
|
409
|
+
input: data,
|
|
410
|
+
});
|
|
411
|
+
ctx.addIssue({
|
|
412
|
+
code: 'custom',
|
|
413
|
+
path: ['paths'],
|
|
414
|
+
message: "Provide either 'path' or 'paths', not both",
|
|
415
|
+
input: data,
|
|
416
|
+
});
|
|
417
|
+
}
|
|
452
418
|
})
|
|
453
419
|
.describe("Provide either 'path' or 'paths'.");
|
|
454
420
|
export const CreateDirectoryOutputSchema = z.strictObject({
|
|
455
|
-
ok:
|
|
421
|
+
ok: SuccessFlagSchema,
|
|
456
422
|
path: z.string().optional(),
|
|
457
423
|
paths: z.array(z.string()).optional(),
|
|
458
|
-
error: ErrorSchema.optional(),
|
|
459
424
|
});
|
|
460
425
|
export const WriteFileInputSchema = z.strictObject({
|
|
461
426
|
path: RequiredPathSchema.describe(DESC_PATH_REQUIRED),
|
|
462
427
|
content: z.string().describe('Content to write'),
|
|
463
428
|
});
|
|
464
429
|
export const WriteFileOutputSchema = z.strictObject({
|
|
465
|
-
ok:
|
|
430
|
+
ok: SuccessFlagSchema,
|
|
466
431
|
path: z.string().optional(),
|
|
467
|
-
bytesWritten:
|
|
468
|
-
error: ErrorSchema.optional(),
|
|
432
|
+
bytesWritten: NonNegativeIntegerSchema.optional(),
|
|
469
433
|
});
|
|
470
434
|
export const EditFileInputSchema = z.strictObject({
|
|
471
435
|
path: RequiredPathSchema.describe(DESC_PATH_REQUIRED),
|
|
@@ -473,6 +437,8 @@ export const EditFileInputSchema = z.strictObject({
|
|
|
473
437
|
.array(z.strictObject({
|
|
474
438
|
oldText: z
|
|
475
439
|
.string()
|
|
440
|
+
.min(1, 'oldText required')
|
|
441
|
+
.max(102400, 'Max 100KB')
|
|
476
442
|
.describe('Exact literal string to replace (character-for-character). Include 3–5 lines of context for unique targeting.'),
|
|
477
443
|
newText: z
|
|
478
444
|
.string()
|
|
@@ -486,11 +452,11 @@ export const EditFileInputSchema = z.strictObject({
|
|
|
486
452
|
export const EditFileOutputSchema = z.strictObject({
|
|
487
453
|
ok: z.boolean(),
|
|
488
454
|
path: z.string().optional(),
|
|
489
|
-
appliedEdits:
|
|
490
|
-
linesAdded:
|
|
491
|
-
linesRemoved:
|
|
455
|
+
appliedEdits: NonNegativeIntegerSchema.optional(),
|
|
456
|
+
linesAdded: NonNegativeIntegerSchema.optional().describe('Lines added'),
|
|
457
|
+
linesRemoved: NonNegativeIntegerSchema.optional().describe('Lines removed'),
|
|
492
458
|
lineRange: z
|
|
493
|
-
.tuple([
|
|
459
|
+
.tuple([PositiveIntegerSchema, PositiveIntegerSchema])
|
|
494
460
|
.optional()
|
|
495
461
|
.describe('Line range modified [start, end] (1-based)'),
|
|
496
462
|
unmatchedEdits: z
|
|
@@ -498,17 +464,42 @@ export const EditFileOutputSchema = z.strictObject({
|
|
|
498
464
|
.optional()
|
|
499
465
|
.describe('Edits that could not be applied'),
|
|
500
466
|
diff: z.string().optional().describe('Unified diff of changes (dryRun)'),
|
|
501
|
-
error: ErrorSchema.optional(),
|
|
502
467
|
});
|
|
503
468
|
export const MoveFileInputSchema = z
|
|
504
469
|
.strictObject({
|
|
505
470
|
source: RequiredPathSchema.optional().describe('Path to move (deprecated: use sources)'),
|
|
506
|
-
sources: z
|
|
471
|
+
sources: z
|
|
472
|
+
.array(RequiredPathSchema)
|
|
473
|
+
.min(1, 'Min 1 source required')
|
|
474
|
+
.optional()
|
|
475
|
+
.describe('Paths to move'),
|
|
507
476
|
destination: RequiredPathSchema.describe('New path'),
|
|
508
477
|
})
|
|
509
|
-
.
|
|
510
|
-
|
|
511
|
-
|
|
478
|
+
.superRefine((data, ctx) => {
|
|
479
|
+
const hasSource = data.source !== undefined;
|
|
480
|
+
const hasSources = data.sources !== undefined;
|
|
481
|
+
if (!hasSource && !hasSources) {
|
|
482
|
+
ctx.addIssue({
|
|
483
|
+
code: 'custom',
|
|
484
|
+
path: ['source'],
|
|
485
|
+
message: "Either 'source' or 'sources' must be provided",
|
|
486
|
+
input: data,
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
if (hasSource && hasSources) {
|
|
490
|
+
ctx.addIssue({
|
|
491
|
+
code: 'custom',
|
|
492
|
+
path: ['source'],
|
|
493
|
+
message: "Provide either 'source' or 'sources', not both",
|
|
494
|
+
input: data,
|
|
495
|
+
});
|
|
496
|
+
ctx.addIssue({
|
|
497
|
+
code: 'custom',
|
|
498
|
+
path: ['sources'],
|
|
499
|
+
message: "Provide either 'source' or 'sources', not both",
|
|
500
|
+
input: data,
|
|
501
|
+
});
|
|
502
|
+
}
|
|
512
503
|
})
|
|
513
504
|
.describe("Provide either 'source' or 'sources'.");
|
|
514
505
|
export const MoveFileOutputSchema = z.strictObject({
|
|
@@ -519,11 +510,10 @@ export const MoveFileOutputSchema = z.strictObject({
|
|
|
519
510
|
failed: z
|
|
520
511
|
.array(z.strictObject({
|
|
521
512
|
source: z.string().describe('Source path'),
|
|
522
|
-
error:
|
|
513
|
+
error: ErrorSchema.describe('Structured error details'),
|
|
523
514
|
}))
|
|
524
515
|
.optional()
|
|
525
516
|
.describe('List of files that failed to move'),
|
|
526
|
-
error: ErrorSchema.optional(),
|
|
527
517
|
});
|
|
528
518
|
export const DeleteFileInputSchema = z.strictObject({
|
|
529
519
|
path: RequiredPathSchema.describe(DESC_PATH_REQUIRED),
|
|
@@ -531,23 +521,18 @@ export const DeleteFileInputSchema = z.strictObject({
|
|
|
531
521
|
ignoreIfNotExists: defaultFalseBoolean('No error if missing'),
|
|
532
522
|
});
|
|
533
523
|
export const DeleteFileOutputSchema = z.strictObject({
|
|
534
|
-
ok:
|
|
524
|
+
ok: SuccessFlagSchema,
|
|
535
525
|
path: z.string().optional(),
|
|
536
|
-
error: ErrorSchema.optional(),
|
|
537
526
|
});
|
|
538
527
|
export const CalculateHashInputSchema = z.strictObject({
|
|
539
528
|
path: RequiredPathSchema.describe(DESC_PATH_REQUIRED),
|
|
540
529
|
});
|
|
541
530
|
export const CalculateHashOutputSchema = z.strictObject({
|
|
542
|
-
ok:
|
|
531
|
+
ok: SuccessFlagSchema,
|
|
543
532
|
path: z.string().optional(),
|
|
544
|
-
hash:
|
|
533
|
+
hash: Sha256HexSchema.optional().describe('SHA-256 hash'),
|
|
545
534
|
isDirectory: z.boolean().optional().describe('True if path is a directory'),
|
|
546
|
-
fileCount:
|
|
547
|
-
.number()
|
|
548
|
-
.optional()
|
|
549
|
-
.describe('Number of files hashed (directories only)'),
|
|
550
|
-
error: ErrorSchema.optional(),
|
|
535
|
+
fileCount: NonNegativeIntegerSchema.optional().describe('Number of files hashed (directories only)'),
|
|
551
536
|
});
|
|
552
537
|
export const DiffFilesInputSchema = z.strictObject({
|
|
553
538
|
original: RequiredPathSchema.describe('Path to original file'),
|
|
@@ -570,15 +555,14 @@ export const DiffFilesInputSchema = z.strictObject({
|
|
|
570
555
|
.describe('Strip trailing carriage returns before diffing'),
|
|
571
556
|
});
|
|
572
557
|
export const DiffFilesOutputSchema = z.strictObject({
|
|
573
|
-
ok:
|
|
558
|
+
ok: SuccessFlagSchema,
|
|
574
559
|
diff: z.string().optional().describe('Unified diff content'),
|
|
575
560
|
isIdentical: z.boolean().optional().describe('True if files are identical'),
|
|
576
|
-
linesAdded:
|
|
577
|
-
linesRemoved:
|
|
578
|
-
hunksCount:
|
|
561
|
+
linesAdded: NonNegativeIntegerSchema.optional().describe('Lines added'),
|
|
562
|
+
linesRemoved: NonNegativeIntegerSchema.optional().describe('Lines removed'),
|
|
563
|
+
hunksCount: NonNegativeIntegerSchema.optional().describe('Number of diff hunks'),
|
|
579
564
|
truncated: z.boolean().optional().describe('Diff content truncated?'),
|
|
580
565
|
resourceUri: z.string().optional().describe('Full diff content URI'),
|
|
581
|
-
error: ErrorSchema.optional(),
|
|
582
566
|
});
|
|
583
567
|
export const ApplyPatchInputSchema = z.strictObject({
|
|
584
568
|
path: RequiredPathSchema.describe('Path to file to patch'),
|
|
@@ -610,33 +594,25 @@ export const ApplyPatchOutputSchema = z.strictObject({
|
|
|
610
594
|
ok: z.boolean(),
|
|
611
595
|
path: z.string().optional(),
|
|
612
596
|
applied: z.boolean().optional(),
|
|
613
|
-
hunksApplied:
|
|
614
|
-
linesAdded:
|
|
615
|
-
linesRemoved:
|
|
597
|
+
hunksApplied: NonNegativeIntegerSchema.optional().describe('Hunks applied'),
|
|
598
|
+
linesAdded: NonNegativeIntegerSchema.optional().describe('Lines added'),
|
|
599
|
+
linesRemoved: NonNegativeIntegerSchema.optional().describe('Lines removed'),
|
|
616
600
|
results: z
|
|
617
601
|
.array(z.strictObject({
|
|
618
602
|
path: z.string().describe('File path'),
|
|
619
603
|
applied: z.boolean().describe('Patch applied successfully'),
|
|
620
|
-
hunksApplied:
|
|
621
|
-
linesAdded:
|
|
622
|
-
linesRemoved:
|
|
623
|
-
error:
|
|
604
|
+
hunksApplied: NonNegativeIntegerSchema.optional().describe('Hunks applied'),
|
|
605
|
+
linesAdded: NonNegativeIntegerSchema.optional().describe('Lines added'),
|
|
606
|
+
linesRemoved: NonNegativeIntegerSchema.optional().describe('Lines removed'),
|
|
607
|
+
error: ErrorSchema.optional().describe('Structured error details'),
|
|
624
608
|
}))
|
|
625
609
|
.optional()
|
|
626
610
|
.describe('Per-file results for multi-file patches'),
|
|
627
|
-
error: ErrorSchema.optional(),
|
|
628
611
|
});
|
|
629
612
|
export const SearchAndReplaceInputSchema = z.strictObject({
|
|
630
613
|
path: OptionalPathSchema.describe(DESC_PATH_ROOT),
|
|
631
|
-
filePattern:
|
|
632
|
-
.string()
|
|
633
|
-
.min(1, 'Pattern required')
|
|
634
|
-
.max(1000, 'Max 1000 chars')
|
|
635
|
-
.optional()
|
|
614
|
+
filePattern: SafeGlobPatternSchema.optional()
|
|
636
615
|
.default('**/*')
|
|
637
|
-
.refine((val) => isSafeGlobPattern(val), {
|
|
638
|
-
error: 'Invalid glob or unsafe path (absolute/.. forbidden)',
|
|
639
|
-
})
|
|
640
616
|
.describe('Glob to filter files. Default: **/*'),
|
|
641
617
|
searchPattern: z
|
|
642
618
|
.string()
|
|
@@ -651,18 +627,9 @@ export const SearchAndReplaceInputSchema = z.strictObject({
|
|
|
651
627
|
.default(true)
|
|
652
628
|
.describe('Case-sensitive matching. Default: true.'),
|
|
653
629
|
dryRun: defaultFalseBoolean('Preview matches without writing. Check changedFiles and matches in the response before committing.'),
|
|
654
|
-
includeHidden:
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
.describe('Include hidden files/directories (starting with .). Default: false.'),
|
|
658
|
-
includeIgnored: z
|
|
659
|
-
.boolean()
|
|
660
|
-
.optional()
|
|
661
|
-
.describe('Include .gitignore-ignored files (node_modules, dist). Default: false.'),
|
|
662
|
-
returnDiff: z
|
|
663
|
-
.boolean()
|
|
664
|
-
.optional()
|
|
665
|
-
.describe('Return unified diff of changes even if dryRun is false. Default: false.'),
|
|
630
|
+
includeHidden: defaultFalseBoolean('Include hidden files/directories (starting with .). Default: false.'),
|
|
631
|
+
includeIgnored: defaultFalseBoolean('Include .gitignore-ignored files (node_modules, dist). Default: false.'),
|
|
632
|
+
returnDiff: defaultFalseBoolean('Return unified diff of changes even if dryRun is false. Default: false.'),
|
|
666
633
|
maxFiles: z
|
|
667
634
|
.int({ error: 'Must be integer' })
|
|
668
635
|
.min(1, 'Min: 1')
|
|
@@ -671,22 +638,22 @@ export const SearchAndReplaceInputSchema = z.strictObject({
|
|
|
671
638
|
.describe('Max files to process before stopping'),
|
|
672
639
|
});
|
|
673
640
|
export const SearchAndReplaceOutputSchema = z.strictObject({
|
|
674
|
-
ok:
|
|
675
|
-
matches:
|
|
676
|
-
filesChanged:
|
|
677
|
-
processedFiles:
|
|
678
|
-
failedFiles:
|
|
641
|
+
ok: SuccessFlagSchema,
|
|
642
|
+
matches: NonNegativeIntegerSchema.optional().describe('Total matches found'),
|
|
643
|
+
filesChanged: NonNegativeIntegerSchema.optional().describe('Files modified'),
|
|
644
|
+
processedFiles: NonNegativeIntegerSchema.optional().describe('Files processed'),
|
|
645
|
+
failedFiles: NonNegativeIntegerSchema.optional().describe('Files skipped due to errors'),
|
|
679
646
|
failures: z
|
|
680
647
|
.array(z.strictObject({
|
|
681
648
|
path: z.string().describe('File path'),
|
|
682
|
-
error:
|
|
649
|
+
error: ErrorSchema.describe('Structured error details'),
|
|
683
650
|
}))
|
|
684
651
|
.optional()
|
|
685
652
|
.describe('Sample of per-file errors'),
|
|
686
653
|
changedFiles: z
|
|
687
654
|
.array(z.strictObject({
|
|
688
655
|
path: z.string().describe('File path'),
|
|
689
|
-
matches:
|
|
656
|
+
matches: PositiveIntegerSchema.describe('Matches in file'),
|
|
690
657
|
}))
|
|
691
658
|
.optional()
|
|
692
659
|
.describe('Sample of changed files'),
|
|
@@ -694,7 +661,10 @@ export const SearchAndReplaceOutputSchema = z.strictObject({
|
|
|
694
661
|
.boolean()
|
|
695
662
|
.optional()
|
|
696
663
|
.describe('Changed file list truncated'),
|
|
697
|
-
diff: z
|
|
664
|
+
diff: z
|
|
665
|
+
.string()
|
|
666
|
+
.optional()
|
|
667
|
+
.describe('Unified diff of changes when `dryRun` or `returnDiff` is enabled'),
|
|
698
668
|
diffTruncated: z
|
|
699
669
|
.boolean()
|
|
700
670
|
.optional()
|
|
@@ -703,6 +673,4 @@ export const SearchAndReplaceOutputSchema = z.strictObject({
|
|
|
703
673
|
.enum(['maxFiles'])
|
|
704
674
|
.optional()
|
|
705
675
|
.describe('Why processing stopped early'),
|
|
706
|
-
dryRun: z.boolean().optional(),
|
|
707
|
-
error: ErrorSchema.optional(),
|
|
708
676
|
});
|