@j0hanz/filesystem-mcp 1.1.0 → 1.1.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/dist/schemas.d.ts +1 -0
- package/dist/schemas.js +4 -0
- package/dist/tools/edit-file.js +34 -2
- package/dist/tools/search-content.js +11 -1
- package/package.json +1 -1
package/dist/schemas.d.ts
CHANGED
|
@@ -403,6 +403,7 @@ export declare const EditFileOutputSchema: z.ZodObject<{
|
|
|
403
403
|
ok: z.ZodBoolean;
|
|
404
404
|
path: z.ZodOptional<z.ZodString>;
|
|
405
405
|
appliedEdits: z.ZodOptional<z.ZodNumber>;
|
|
406
|
+
lineRange: z.ZodOptional<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>;
|
|
406
407
|
unmatchedEdits: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
407
408
|
error: z.ZodOptional<z.ZodObject<{
|
|
408
409
|
code: z.ZodEnum<{
|
package/dist/schemas.js
CHANGED
|
@@ -510,6 +510,10 @@ export const EditFileOutputSchema = z.object({
|
|
|
510
510
|
ok: z.boolean(),
|
|
511
511
|
path: z.string().optional(),
|
|
512
512
|
appliedEdits: z.number().optional(),
|
|
513
|
+
lineRange: z
|
|
514
|
+
.tuple([z.number(), z.number()])
|
|
515
|
+
.optional()
|
|
516
|
+
.describe('Line range modified [start, end] (1-based)'),
|
|
513
517
|
unmatchedEdits: z
|
|
514
518
|
.array(z.string())
|
|
515
519
|
.optional()
|
package/dist/tools/edit-file.js
CHANGED
|
@@ -21,25 +21,45 @@ function applyEdits(content, edits) {
|
|
|
21
21
|
let newContent = content;
|
|
22
22
|
let appliedEdits = 0;
|
|
23
23
|
const unmatchedEdits = [];
|
|
24
|
+
let minLine;
|
|
25
|
+
let maxLine;
|
|
24
26
|
for (const edit of edits) {
|
|
25
27
|
if (!newContent.includes(edit.oldText)) {
|
|
26
28
|
unmatchedEdits.push(edit.oldText);
|
|
27
29
|
continue;
|
|
28
30
|
}
|
|
31
|
+
const index = newContent.indexOf(edit.oldText);
|
|
32
|
+
const linesBefore = newContent.slice(0, index).split('\n').length;
|
|
33
|
+
const newTextLines = edit.newText.split('\n').length;
|
|
34
|
+
const startLine = linesBefore;
|
|
35
|
+
const endLine = linesBefore + newTextLines - 1;
|
|
36
|
+
if (minLine === undefined || startLine < minLine)
|
|
37
|
+
minLine = startLine;
|
|
38
|
+
if (maxLine === undefined || endLine > maxLine)
|
|
39
|
+
maxLine = endLine;
|
|
29
40
|
newContent = newContent.replace(edit.oldText, edit.newText);
|
|
30
41
|
appliedEdits += 1;
|
|
31
42
|
}
|
|
32
|
-
|
|
43
|
+
const result = {
|
|
44
|
+
content: newContent,
|
|
45
|
+
appliedEdits,
|
|
46
|
+
unmatchedEdits,
|
|
47
|
+
};
|
|
48
|
+
if (minLine !== undefined && maxLine !== undefined) {
|
|
49
|
+
result.lineRange = [minLine, maxLine];
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
33
52
|
}
|
|
34
53
|
async function handleEditFile(args, signal) {
|
|
35
54
|
const validPath = await validateExistingPath(args.path, signal);
|
|
36
55
|
const content = await fs.readFile(validPath, { encoding: 'utf-8', signal });
|
|
37
|
-
const { content: newContent, appliedEdits, unmatchedEdits, } = applyEdits(content, args.edits);
|
|
56
|
+
const { content: newContent, appliedEdits, unmatchedEdits, lineRange, } = applyEdits(content, args.edits);
|
|
38
57
|
const structured = {
|
|
39
58
|
ok: true,
|
|
40
59
|
path: validPath,
|
|
41
60
|
appliedEdits,
|
|
42
61
|
...(unmatchedEdits.length > 0 ? { unmatchedEdits } : {}),
|
|
62
|
+
...(lineRange ? { lineRange } : {}),
|
|
43
63
|
};
|
|
44
64
|
if (args.dryRun) {
|
|
45
65
|
return buildToolResponse(`Dry run complete. ${appliedEdits} edits would be applied.`, structured);
|
|
@@ -68,5 +88,17 @@ export function registerEditFileTool(server, options = {}) {
|
|
|
68
88
|
const name = path.basename(args.path);
|
|
69
89
|
return `🛠 edit: ${name} (${args.edits.length} edits)`;
|
|
70
90
|
},
|
|
91
|
+
completionMessage: (args, result) => {
|
|
92
|
+
const name = path.basename(args.path);
|
|
93
|
+
if (result.isError)
|
|
94
|
+
return `🛠 edit: ${name} ➟ Failed`;
|
|
95
|
+
const sc = result.structuredContent;
|
|
96
|
+
if (!sc.ok)
|
|
97
|
+
return `🛠 edit: ${name} ➟ Failed`;
|
|
98
|
+
if (sc.lineRange) {
|
|
99
|
+
return `🛠 edit: ${name} ➟ [${sc.lineRange[0]}-${sc.lineRange[1]}]`;
|
|
100
|
+
}
|
|
101
|
+
return `🛠 edit: ${name} ➟ (${sc.appliedEdits ?? 0} edits)`;
|
|
102
|
+
},
|
|
71
103
|
}));
|
|
72
104
|
}
|
|
@@ -202,7 +202,17 @@ export function registerSearchContentTool(server, options = {}) {
|
|
|
202
202
|
});
|
|
203
203
|
const result = await handleSearchContent(normalizedArgs, extra.signal, options.resourceStore, createProgressReporter(extra));
|
|
204
204
|
const sc = result.structuredContent;
|
|
205
|
-
const
|
|
205
|
+
const count = sc.ok && sc.totalMatches ? sc.totalMatches : 0;
|
|
206
|
+
let suffix;
|
|
207
|
+
if (count === 0) {
|
|
208
|
+
suffix = 'No matches';
|
|
209
|
+
}
|
|
210
|
+
else if (count === 1) {
|
|
211
|
+
suffix = '1 match';
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
suffix = `${count} matches`;
|
|
215
|
+
}
|
|
206
216
|
const finalCurrent = (sc.filesScanned ?? 0) + 1;
|
|
207
217
|
notifyProgress(extra, {
|
|
208
218
|
current: finalCurrent,
|