@mcp-monorepo/file-browser 1.0.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.
Files changed (65) hide show
  1. package/dist/index.d.ts +3 -0
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/index.js +26 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/lib/env.d.ts +10 -0
  6. package/dist/lib/env.d.ts.map +1 -0
  7. package/dist/lib/env.js +20 -0
  8. package/dist/lib/env.js.map +1 -0
  9. package/dist/lib/find-best-patch-match.d.ts +31 -0
  10. package/dist/lib/find-best-patch-match.d.ts.map +1 -0
  11. package/dist/lib/find-best-patch-match.js +74 -0
  12. package/dist/lib/find-best-patch-match.js.map +1 -0
  13. package/dist/lib/finder.d.ts +46 -0
  14. package/dist/lib/finder.d.ts.map +1 -0
  15. package/dist/lib/finder.js +90 -0
  16. package/dist/lib/finder.js.map +1 -0
  17. package/dist/lib/validators.d.ts +12 -0
  18. package/dist/lib/validators.d.ts.map +1 -0
  19. package/dist/lib/validators.js +57 -0
  20. package/dist/lib/validators.js.map +1 -0
  21. package/dist/lib/walker/better-walker.d.ts +52 -0
  22. package/dist/lib/walker/better-walker.d.ts.map +1 -0
  23. package/dist/lib/walker/better-walker.js +120 -0
  24. package/dist/lib/walker/better-walker.js.map +1 -0
  25. package/dist/lib/walker/ignore-file-service.d.ts +8 -0
  26. package/dist/lib/walker/ignore-file-service.d.ts.map +1 -0
  27. package/dist/lib/walker/ignore-file-service.js +128 -0
  28. package/dist/lib/walker/ignore-file-service.js.map +1 -0
  29. package/dist/tools/find-or-replace.d.ts +3 -0
  30. package/dist/tools/find-or-replace.d.ts.map +1 -0
  31. package/dist/tools/find-or-replace.js +102 -0
  32. package/dist/tools/find-or-replace.js.map +1 -0
  33. package/dist/tools/list-directory.d.ts +3 -0
  34. package/dist/tools/list-directory.d.ts.map +1 -0
  35. package/dist/tools/list-directory.js +75 -0
  36. package/dist/tools/list-directory.js.map +1 -0
  37. package/dist/tools/mk-dir.d.ts +3 -0
  38. package/dist/tools/mk-dir.d.ts.map +1 -0
  39. package/dist/tools/mk-dir.js +45 -0
  40. package/dist/tools/mk-dir.js.map +1 -0
  41. package/dist/tools/move-path.d.ts +3 -0
  42. package/dist/tools/move-path.d.ts.map +1 -0
  43. package/dist/tools/move-path.js +35 -0
  44. package/dist/tools/move-path.js.map +1 -0
  45. package/dist/tools/open-file.d.ts +3 -0
  46. package/dist/tools/open-file.d.ts.map +1 -0
  47. package/dist/tools/open-file.js +31 -0
  48. package/dist/tools/open-file.js.map +1 -0
  49. package/dist/tools/patch-file.d.ts +3 -0
  50. package/dist/tools/patch-file.d.ts.map +1 -0
  51. package/dist/tools/patch-file.js +80 -0
  52. package/dist/tools/patch-file.js.map +1 -0
  53. package/dist/tools/remove-file.d.ts +3 -0
  54. package/dist/tools/remove-file.d.ts.map +1 -0
  55. package/dist/tools/remove-file.js +52 -0
  56. package/dist/tools/remove-file.js.map +1 -0
  57. package/dist/tools/tree-directory.d.ts +3 -0
  58. package/dist/tools/tree-directory.d.ts.map +1 -0
  59. package/dist/tools/tree-directory.js +71 -0
  60. package/dist/tools/tree-directory.js.map +1 -0
  61. package/dist/tools/write-tool.d.ts +3 -0
  62. package/dist/tools/write-tool.d.ts.map +1 -0
  63. package/dist/tools/write-tool.js +45 -0
  64. package/dist/tools/write-tool.js.map +1 -0
  65. package/package.json +39 -0
@@ -0,0 +1,128 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { platform } from 'node:os';
3
+ import { dirname, relative } from 'node:path';
4
+ const isWindows = platform() === 'win32';
5
+ const toPosixPath = (p) => p.replace(/\\/g, '/');
6
+ // This function converts a simple glob pattern to a regex.
7
+ function globToRegex(pattern) {
8
+ const flags = isWindows ? 'i' : '';
9
+ const regexString = pattern
10
+ .replace(/[.+?^${}()|[\]\\]/g, '\\$&') // Escape special characters
11
+ .replace(/\*/g, '.*'); // `*` becomes `.*` for this simplified purpose
12
+ return new RegExp(`^${regexString}$`, flags);
13
+ }
14
+ export class IgnoreFileService {
15
+ rulesByDirectory = {};
16
+ async add(filePath) {
17
+ const directoryPath = dirname(filePath);
18
+ const fileContent = await readFile(filePath, 'utf-8');
19
+ this.addByContent(directoryPath, fileContent);
20
+ }
21
+ addByContent(directoryPath, fileContent) {
22
+ const posixDir = toPosixPath(directoryPath);
23
+ if (!this.rulesByDirectory[posixDir]) {
24
+ this.rulesByDirectory[posixDir] = [];
25
+ }
26
+ const newRules = fileContent
27
+ .split('\n')
28
+ .map((line) => line.trim())
29
+ .filter((line) => line && !line.startsWith('#'))
30
+ .map((line) => {
31
+ const isAllowRule = line.startsWith('!');
32
+ const pattern = isAllowRule ? line.slice(1) : line;
33
+ return {
34
+ originalPattern: pattern,
35
+ regex: globToRegex(pattern),
36
+ isAllowRule,
37
+ };
38
+ });
39
+ this.rulesByDirectory[posixDir] = this.rulesByDirectory[posixDir].concat(newRules);
40
+ }
41
+ isPathIgnored(absolutePath) {
42
+ const posixPath = toPosixPath(absolutePath);
43
+ let isIgnored = false;
44
+ const applicableDirs = Object.keys(this.rulesByDirectory)
45
+ .filter((dir) => posixPath.startsWith(dir))
46
+ .sort((a, b) => a.length - b.length);
47
+ for (const dir of applicableDirs) {
48
+ const relativePath = toPosixPath(relative(dir, posixPath));
49
+ const rules = this.rulesByDirectory[dir];
50
+ for (const rule of rules) {
51
+ let match = false;
52
+ // Patterns with a slash are matched relative to the .gitignore file.
53
+ if (rule.originalPattern.includes('/')) {
54
+ const patternToCheck = rule.originalPattern.endsWith('/')
55
+ ? rule.originalPattern.slice(0, -1)
56
+ : rule.originalPattern;
57
+ // Check if the relative path starts with or matches the pattern
58
+ if (relativePath === patternToCheck || relativePath.startsWith(patternToCheck + '/')) {
59
+ match = true;
60
+ }
61
+ else {
62
+ // For patterns with wildcards, use regex matching
63
+ const patternRegex = globToRegex(patternToCheck);
64
+ const pathSegments = relativePath.split('/');
65
+ // Check against full path and each potential subpath
66
+ for (let i = 0; i < pathSegments.length; i++) {
67
+ const testPath = pathSegments.slice(0, i + 1).join('/');
68
+ if (patternRegex.test(testPath)) {
69
+ match = true;
70
+ break;
71
+ }
72
+ }
73
+ }
74
+ }
75
+ // Patterns without a slash are matched against any path segment.
76
+ else {
77
+ const pathSegments = relativePath.split('/');
78
+ if (pathSegments.some((segment) => rule.regex.test(segment))) {
79
+ match = true;
80
+ }
81
+ }
82
+ if (match) {
83
+ isIgnored = !rule.isAllowRule;
84
+ }
85
+ }
86
+ }
87
+ return isIgnored;
88
+ }
89
+ couldDirectoryContainAllowedFiles(absolutePath) {
90
+ // If the directory itself is not ignored, we must enter it.
91
+ if (!this.isPathIgnored(absolutePath)) {
92
+ return true;
93
+ }
94
+ // If it IS ignored, we only need to enter if an "allow" rule could apply to a child.
95
+ const posixPath = toPosixPath(absolutePath);
96
+ const applicableDirs = Object.keys(this.rulesByDirectory)
97
+ .filter((dir) => posixPath.startsWith(dir))
98
+ .sort((a, b) => a.length - b.length); // Process from root to nested
99
+ for (const dir of applicableDirs) {
100
+ const relativePath = toPosixPath(relative(dir, posixPath));
101
+ const rules = this.rulesByDirectory[dir];
102
+ for (const rule of rules) {
103
+ if (!rule.isAllowRule)
104
+ continue;
105
+ // Check if this allow rule could apply to files within this directory
106
+ const pattern = rule.originalPattern;
107
+ // Remove leading slash if present for comparison
108
+ const normalizedPattern = pattern.startsWith('/') ? pattern.slice(1) : pattern;
109
+ const normalizedRelPath = relativePath;
110
+ // Check if the pattern could match something inside this directory
111
+ if (pattern.includes('/')) {
112
+ // Pattern with slash: check if it starts with or is within our path
113
+ if (normalizedPattern.startsWith(normalizedRelPath + '/') ||
114
+ normalizedPattern.startsWith(normalizedRelPath) ||
115
+ normalizedRelPath.startsWith(normalizedPattern.split('/')[0])) {
116
+ return true;
117
+ }
118
+ }
119
+ else {
120
+ // Pattern without slash: could match any file in any subdirectory
121
+ return true;
122
+ }
123
+ }
124
+ }
125
+ return false;
126
+ }
127
+ }
128
+ //# sourceMappingURL=ignore-file-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ignore-file-service.js","sourceRoot":"","sources":["../../../src/lib/walker/ignore-file-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAE7C,MAAM,SAAS,GAAG,QAAQ,EAAE,KAAK,OAAO,CAAA;AACxC,MAAM,WAAW,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;AAExD,2DAA2D;AAC3D,SAAS,WAAW,CAAC,OAAe;IAClC,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;IAClC,MAAM,WAAW,GAAG,OAAO;SACxB,OAAO,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC,4BAA4B;SAClE,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA,CAAC,+CAA+C;IACvE,OAAO,IAAI,MAAM,CAAC,IAAI,WAAW,GAAG,EAAE,KAAK,CAAC,CAAA;AAC9C,CAAC;AAQD,MAAM,OAAO,iBAAiB;IACpB,gBAAgB,GAA2B,EAAE,CAAA;IAErD,KAAK,CAAC,GAAG,CAAC,QAAgB;QACxB,MAAM,aAAa,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;QACvC,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;QACrD,IAAI,CAAC,YAAY,CAAC,aAAa,EAAE,WAAW,CAAC,CAAA;IAC/C,CAAC;IAED,YAAY,CAAC,aAAqB,EAAE,WAAmB;QACrD,MAAM,QAAQ,GAAG,WAAW,CAAC,aAAa,CAAC,CAAA;QAC3C,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAA;QACtC,CAAC;QAED,MAAM,QAAQ,GAAG,WAAW;aACzB,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;aAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;aAC/C,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACZ,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;YACxC,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;YAElD,OAAO;gBACL,eAAe,EAAE,OAAO;gBACxB,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC;gBAC3B,WAAW;aACZ,CAAA;QACH,CAAC,CAAC,CAAA;QAEJ,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IACpF,CAAC;IAED,aAAa,CAAC,YAAoB;QAChC,MAAM,SAAS,GAAG,WAAW,CAAC,YAAY,CAAC,CAAA;QAC3C,IAAI,SAAS,GAAG,KAAK,CAAA;QAErB,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC;aACtD,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;aAC1C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAA;QAEtC,KAAK,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;YACjC,MAAM,YAAY,GAAG,WAAW,CAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAA;YAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAA;YAExC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,KAAK,GAAG,KAAK,CAAA;gBAEjB,qEAAqE;gBACrE,IAAI,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;oBACvC,MAAM,cAAc,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC;wBACvD,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;wBACnC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAA;oBAExB,gEAAgE;oBAChE,IAAI,YAAY,KAAK,cAAc,IAAI,YAAY,CAAC,UAAU,CAAC,cAAc,GAAG,GAAG,CAAC,EAAE,CAAC;wBACrF,KAAK,GAAG,IAAI,CAAA;oBACd,CAAC;yBAAM,CAAC;wBACN,kDAAkD;wBAClD,MAAM,YAAY,GAAG,WAAW,CAAC,cAAc,CAAC,CAAA;wBAChD,MAAM,YAAY,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;wBAC5C,qDAAqD;wBACrD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;4BAC7C,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;4BACvD,IAAI,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gCAChC,KAAK,GAAG,IAAI,CAAA;gCACZ,MAAK;4BACP,CAAC;wBACH,CAAC;oBACH,CAAC;gBACH,CAAC;gBACD,iEAAiE;qBAC5D,CAAC;oBACJ,MAAM,YAAY,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;oBAC5C,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;wBAC7D,KAAK,GAAG,IAAI,CAAA;oBACd,CAAC;gBACH,CAAC;gBAED,IAAI,KAAK,EAAE,CAAC;oBACV,SAAS,GAAG,CAAC,IAAI,CAAC,WAAW,CAAA;gBAC/B,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,iCAAiC,CAAC,YAAoB;QACpD,4DAA4D;QAC5D,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,EAAE,CAAC;YACtC,OAAO,IAAI,CAAA;QACb,CAAC;QAED,qFAAqF;QACrF,MAAM,SAAS,GAAG,WAAW,CAAC,YAAY,CAAC,CAAA;QAC3C,MAAM,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC;aACtD,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;aAC1C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAA,CAAC,8BAA8B;QAErE,KAAK,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;YACjC,MAAM,YAAY,GAAG,WAAW,CAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAA;YAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAA;YAExC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,CAAC,IAAI,CAAC,WAAW;oBAAE,SAAQ;gBAE/B,sEAAsE;gBACtE,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAA;gBAEpC,iDAAiD;gBACjD,MAAM,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;gBAC9E,MAAM,iBAAiB,GAAG,YAAY,CAAA;gBAEtC,mEAAmE;gBACnE,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;oBAC1B,oEAAoE;oBACpE,IACE,iBAAiB,CAAC,UAAU,CAAC,iBAAiB,GAAG,GAAG,CAAC;wBACrD,iBAAiB,CAAC,UAAU,CAAC,iBAAiB,CAAC;wBAC/C,iBAAiB,CAAC,UAAU,CAAC,iBAAiB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAC7D,CAAC;wBACD,OAAO,IAAI,CAAA;oBACb,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,kEAAkE;oBAClE,OAAO,IAAI,CAAA;gBACb,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,KAAK,CAAA;IACd,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare const registerFindOrReplaceTool: (server: McpServer) => void;
3
+ //# sourceMappingURL=find-or-replace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-or-replace.d.ts","sourceRoot":"","sources":["../../src/tools/find-or-replace.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAUxE,eAAO,MAAM,yBAAyB,GAAI,QAAQ,SAAS,SAyGvD,CAAA"}
@@ -0,0 +1,102 @@
1
+ import { readFile, writeFile } from 'node:fs/promises';
2
+ import { posix, resolve } from 'node:path';
3
+ import { registerTool } from '@mcp-monorepo/shared';
4
+ import { z } from 'zod';
5
+ import { getWorkingDir } from '../lib/env.js';
6
+ import { getFormattedSubstring } from '../lib/finder.js';
7
+ import { validateWithinBasePath } from '../lib/validators.js';
8
+ import { traverseDirectoryBFS } from '../lib/walker/better-walker.js';
9
+ export const registerFindOrReplaceTool = (server) => registerTool(server, {
10
+ name: 'find-or-replace',
11
+ title: 'Find or Replace Text in Files',
12
+ description: 'Search for text patterns in files with optional replacement. Supports multi-line matches and provides context.',
13
+ inputSchema: {
14
+ dirpath: z
15
+ .string()
16
+ .optional()
17
+ .describe('Relative path to search directory. Defaults to current working directory.'),
18
+ fileRegexp: z.string().optional().describe('Regexp to filter file paths (e.g. ".*\\.ts$" for TypeScript files).'),
19
+ searchRegexp: z
20
+ .string()
21
+ .describe('Regexp pattern to search for in file contents. Can span multiple lines. The flags "gms" will be used.'),
22
+ replaceString: z
23
+ .string()
24
+ .optional()
25
+ .describe('Replacement string. Use $1, $2, etc. for capture groups. If provided, performs replacement instead of showing matches.'),
26
+ },
27
+ outputSchema: {
28
+ matches: z
29
+ .array(z.object({
30
+ filePath: z.string(),
31
+ matches: z.array(z.object({
32
+ lineNumber: z.number(),
33
+ string: z.string(),
34
+ })),
35
+ }))
36
+ .optional(),
37
+ message: z.string(),
38
+ totalMatches: z.number(),
39
+ filesProcessed: z.number(),
40
+ },
41
+ isReadOnly: false,
42
+ async fetcher({ dirpath, fileRegexp, searchRegexp, replaceString }) {
43
+ const workingDir = getWorkingDir();
44
+ const basePath = resolve(workingDir, dirpath ? posix.normalize(dirpath) : './');
45
+ validateWithinBasePath(workingDir, basePath);
46
+ const fileRegex = fileRegexp ? new RegExp(fileRegexp, 'i') : undefined;
47
+ const searchRegex = new RegExp(searchRegexp, 'gms'); // global, multiline, dotall
48
+ const matchResults = [];
49
+ const LIMIT = replaceString !== undefined ? Infinity : 200;
50
+ let isLimited = false;
51
+ for await (const { relPath, type } of traverseDirectoryBFS({
52
+ absoluteFolderPath: basePath,
53
+ })) {
54
+ const totalMatches = matchResults.map((m) => m.matches.length).reduce((a, b) => a + b, 0);
55
+ if (totalMatches > LIMIT) {
56
+ isLimited = true;
57
+ break;
58
+ }
59
+ if (type === 'file') {
60
+ if (!fileRegex || fileRegex.test(relPath)) {
61
+ const fullPath = resolve(basePath, relPath);
62
+ const content = await readFile(fullPath, 'utf-8');
63
+ const matches = [...content.matchAll(searchRegex)];
64
+ if (matches.length > 0) {
65
+ matchResults.push({
66
+ filePath: relPath,
67
+ matches: matches.map((m) => ({
68
+ lineNumber: m.index,
69
+ string: m[0],
70
+ context: getFormattedSubstring(content, m.index, m.index + m[0].length),
71
+ })),
72
+ });
73
+ if (replaceString !== undefined) {
74
+ const newContent = content.replace(searchRegex, replaceString);
75
+ if (newContent !== content) {
76
+ await writeFile(fullPath, newContent, 'utf-8');
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+ }
83
+ return {
84
+ matches: matchResults,
85
+ isLimited,
86
+ isReplaceMode: replaceString !== undefined,
87
+ limit: LIMIT,
88
+ };
89
+ },
90
+ async formatter({ matches, isLimited, isReplaceMode, limit }) {
91
+ const totalMatches = matches.map((m) => m.matches.length).reduce((a, b) => a + b, 0);
92
+ const filesProcessed = matches.length;
93
+ const action = isReplaceMode ? 'Found and replaced' : 'Found';
94
+ return {
95
+ message: `${action} ${totalMatches} occurrence${totalMatches !== 1 ? 's' : ''} in ${filesProcessed} file${filesProcessed !== 1 ? 's' : ''}.${isLimited ? ` Results exceeded ${limit} matches. Try a more narrow scope with another dirpath or regex filter to find more matched.` : ''}`,
96
+ totalMatches,
97
+ filesProcessed,
98
+ matches: isReplaceMode ? undefined : matches,
99
+ };
100
+ },
101
+ });
102
+ //# sourceMappingURL=find-or-replace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-or-replace.js","sourceRoot":"","sources":["../../src/tools/find-or-replace.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare const registerListDirectoryTool: (server: McpServer) => void;
3
+ //# sourceMappingURL=list-directory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-directory.d.ts","sourceRoot":"","sources":["../../src/tools/list-directory.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,eAAO,MAAM,yBAAyB,GAAI,QAAQ,SAAS,SAuEvD,CAAA"}
@@ -0,0 +1,75 @@
1
+ import { stat } from 'node:fs/promises';
2
+ import os from 'node:os';
3
+ import { basename, normalize, resolve } from 'node:path';
4
+ import { registerTool } from '@mcp-monorepo/shared';
5
+ import { z } from 'zod';
6
+ import { getWorkingDir } from '../lib/env.js';
7
+ import { validateIsDir, validateWithinBasePath } from '../lib/validators.js';
8
+ import { traverseDirectoryBFS } from '../lib/walker/better-walker.js';
9
+ export const registerListDirectoryTool = (server) => registerTool(server, {
10
+ name: 'list-directory',
11
+ title: 'List Directory Contents',
12
+ description: 'Lists the contents of a directory with details.',
13
+ inputSchema: {
14
+ dirpath: z.string().optional().describe('Relative path to the directory. Defaults to current working directory.'),
15
+ },
16
+ outputSchema: {
17
+ content: z.array(z.object({
18
+ name: z.string(),
19
+ type: z.enum([
20
+ 'file',
21
+ 'folder',
22
+ 'block device',
23
+ 'character device',
24
+ 'FIFO',
25
+ 'socket',
26
+ 'symbolic link',
27
+ 'unknown',
28
+ ]),
29
+ size: z.number(),
30
+ permissions: z.string().optional(),
31
+ })),
32
+ total: z.number(),
33
+ message: z.string().optional(),
34
+ },
35
+ isReadOnly: true,
36
+ async fetcher({ dirpath }) {
37
+ const workingDir = getWorkingDir();
38
+ const basePath = resolve(workingDir, normalize(dirpath ?? './'));
39
+ validateWithinBasePath(workingDir, basePath);
40
+ await validateIsDir(basePath);
41
+ const files = [];
42
+ let isLimited = false;
43
+ for await (const filePath of traverseDirectoryBFS({
44
+ absoluteFolderPath: basePath,
45
+ maxDepth: 1,
46
+ })) {
47
+ files.push(filePath);
48
+ if (files.length > 200) {
49
+ isLimited = true;
50
+ break;
51
+ }
52
+ }
53
+ return { files, basePath, isLimited };
54
+ },
55
+ async formatter({ files, basePath, isLimited }) {
56
+ const content = await Promise.all(files.map(async ({ relPath, type }) => {
57
+ const stats = await stat(resolve(basePath, normalize(relPath)));
58
+ const isUnix = os.platform() !== 'win32';
59
+ return {
60
+ name: basename(relPath),
61
+ type,
62
+ size: stats.size,
63
+ permissions: isUnix ? `0${(stats.mode & 0o777).toString(8)}` : undefined,
64
+ };
65
+ }));
66
+ return {
67
+ content,
68
+ total: files.length,
69
+ message: isLimited
70
+ ? 'Number of items exceedeed 200. Results limited in depth. Try a more narrow scope with another dirpath or regex filter.'
71
+ : undefined,
72
+ };
73
+ },
74
+ });
75
+ //# sourceMappingURL=list-directory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-directory.js","sourceRoot":"","sources":["../../src/tools/list-directory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare const registerMkDirTool: (server: McpServer) => void;
3
+ //# sourceMappingURL=mk-dir.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mk-dir.d.ts","sourceRoot":"","sources":["../../src/tools/mk-dir.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,eAAO,MAAM,iBAAiB,GAAI,QAAQ,SAAS,SA0C/C,CAAA"}
@@ -0,0 +1,45 @@
1
+ import { mkdir, stat } from 'node:fs/promises';
2
+ import { normalize, resolve } from 'node:path';
3
+ import { registerTool } from '@mcp-monorepo/shared';
4
+ import { z } from 'zod';
5
+ import { getWorkingDir } from '../lib/env.js';
6
+ import { validateWithinBasePath } from '../lib/validators.js';
7
+ export const registerMkDirTool = (server) => registerTool(server, {
8
+ name: 'mk-dir',
9
+ title: 'Make Directory',
10
+ description: 'Creates a directory and all necessary parent directories recursively.',
11
+ inputSchema: {
12
+ dirpath: z.string().nonempty('Directory path must be provided'),
13
+ },
14
+ outputSchema: {
15
+ created: z.string(),
16
+ existed: z.boolean(),
17
+ },
18
+ isReadOnly: false,
19
+ isIdempotent: true,
20
+ async fetcher({ dirpath }) {
21
+ const workingDir = getWorkingDir();
22
+ const path = resolve(workingDir, normalize(dirpath));
23
+ validateWithinBasePath(workingDir, path);
24
+ // Check if directory already exists
25
+ const existed = await stat(path)
26
+ .then((stat) => (stat.isDirectory() ? true : 'existed, but not a directory'))
27
+ .catch(() => false);
28
+ if (typeof existed === 'string')
29
+ throw new Error(`Cannot create directory ${dirpath} because it already exists and is not a directory.`);
30
+ if (!existed) {
31
+ await mkdir(path, { recursive: true });
32
+ }
33
+ return {
34
+ created: dirpath,
35
+ existed,
36
+ };
37
+ },
38
+ formatter({ created, existed }) {
39
+ return {
40
+ created,
41
+ existed,
42
+ };
43
+ },
44
+ });
45
+ //# sourceMappingURL=mk-dir.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mk-dir.js","sourceRoot":"","sources":["../../src/tools/mk-dir.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAA;AAI7D,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAiB,EAAE,EAAE,CACrD,YAAY,CAAC,MAAM,EAAE;IACnB,IAAI,EAAE,QAAQ;IACd,KAAK,EAAE,gBAAgB;IACvB,WAAW,EAAE,uEAAuE;IACpF,WAAW,EAAE;QACX,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iCAAiC,CAAC;KAChE;IACD,YAAY,EAAE;QACZ,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;QACnB,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;KACrB;IACD,UAAU,EAAE,KAAK;IACjB,YAAY,EAAE,IAAI;IAClB,KAAK,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE;QACvB,MAAM,UAAU,GAAG,aAAa,EAAE,CAAA;QAClC,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC,CAAA;QACpD,sBAAsB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAA;QAExC,oCAAoC;QAEpC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC;aAC7B,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAE,8BAAwC,CAAC,CAAC;aACvF,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAA;QAErB,IAAI,OAAO,OAAO,KAAK,QAAQ;YAC7B,MAAM,IAAI,KAAK,CAAC,2BAA2B,OAAO,oDAAoD,CAAC,CAAA;QACzG,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACxC,CAAC;QAED,OAAO;YACL,OAAO,EAAE,OAAO;YAChB,OAAO;SACR,CAAA;IACH,CAAC;IACD,SAAS,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE;QAC5B,OAAO;YACL,OAAO;YACP,OAAO;SACR,CAAA;IACH,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare const registerMovePathTool: (server: McpServer) => void;
3
+ //# sourceMappingURL=move-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"move-path.d.ts","sourceRoot":"","sources":["../../src/tools/move-path.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,eAAO,MAAM,oBAAoB,GAAI,QAAQ,SAAS,SAiClD,CAAA"}
@@ -0,0 +1,35 @@
1
+ import { rename } from 'node:fs/promises';
2
+ import { resolve, normalize } from 'node:path';
3
+ import { registerTool } from '@mcp-monorepo/shared';
4
+ import { z } from 'zod';
5
+ import { getWorkingDir } from '../lib/env.js';
6
+ import { validateDoesNotExists, validateExists, validateWithinBasePath } from '../lib/validators.js';
7
+ export const registerMovePathTool = (server) => registerTool(server, {
8
+ name: 'move-path',
9
+ title: 'Move Path',
10
+ description: 'Moves files or directories to new locations. Each source must exist, and each target must not.',
11
+ inputSchema: {
12
+ paths: z.array(z.object({
13
+ sourcePath: z.string().nonempty('Source path must be provided'),
14
+ targetPath: z.string().nonempty('Target path must be provided'),
15
+ })),
16
+ },
17
+ outputSchema: {},
18
+ isReadOnly: false,
19
+ async fetcher({ paths }) {
20
+ const workingDir = getWorkingDir();
21
+ for (const { sourcePath, targetPath } of paths) {
22
+ const source = resolve(workingDir, normalize(sourcePath));
23
+ const target = resolve(workingDir, normalize(targetPath));
24
+ validateWithinBasePath(workingDir, source);
25
+ validateWithinBasePath(workingDir, target);
26
+ await validateExists(source);
27
+ await validateDoesNotExists(target);
28
+ await rename(source, target);
29
+ }
30
+ },
31
+ formatter() {
32
+ return {};
33
+ },
34
+ });
35
+ //# sourceMappingURL=move-path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"move-path.js","sourceRoot":"","sources":["../../src/tools/move-path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAA;AAIpG,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,MAAiB,EAAE,EAAE,CACxD,YAAY,CAAC,MAAM,EAAE;IACnB,IAAI,EAAE,WAAW;IACjB,KAAK,EAAE,WAAW;IAClB,WAAW,EAAE,gGAAgG;IAC7G,WAAW,EAAE;QACX,KAAK,EAAE,CAAC,CAAC,KAAK,CACZ,CAAC,CAAC,MAAM,CAAC;YACP,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;YAC/D,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;SAChE,CAAC,CACH;KACF;IACD,YAAY,EAAE,EAAE;IAChB,UAAU,EAAE,KAAK;IACjB,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE;QACrB,MAAM,UAAU,GAAG,aAAa,EAAE,CAAA;QAElC,KAAK,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,KAAK,EAAE,CAAC;YAC/C,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC,CAAA;YACzD,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC,CAAA;YAEzD,sBAAsB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;YAC1C,sBAAsB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAA;YAC1C,MAAM,cAAc,CAAC,MAAM,CAAC,CAAA;YAC5B,MAAM,qBAAqB,CAAC,MAAM,CAAC,CAAA;YAEnC,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC9B,CAAC;IACH,CAAC;IACD,SAAS;QACP,OAAO,EAAE,CAAA;IACX,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare const registerOpenFileTool: (server: McpServer) => void;
3
+ //# sourceMappingURL=open-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-file.d.ts","sourceRoot":"","sources":["../../src/tools/open-file.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,eAAO,MAAM,oBAAoB,GAAI,QAAQ,SAAS,SAyBlD,CAAA"}
@@ -0,0 +1,31 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { normalize, resolve } from 'node:path';
3
+ import { registerTool } from '@mcp-monorepo/shared';
4
+ import { z } from 'zod';
5
+ import { getWorkingDir } from '../lib/env.js';
6
+ import { validateIsFile, validateWithinBasePath } from '../lib/validators.js';
7
+ export const registerOpenFileTool = (server) => registerTool(server, {
8
+ name: 'open-file',
9
+ title: 'Open File',
10
+ description: 'Opens a file by its relative filepath and returns its content.',
11
+ inputSchema: {
12
+ filepath: z.string().nonempty('Filepath must be provided'),
13
+ },
14
+ outputSchema: {
15
+ content: z.string(),
16
+ },
17
+ isReadOnly: true,
18
+ async fetcher({ filepath }) {
19
+ const workingDir = getWorkingDir();
20
+ const path = resolve(workingDir, normalize(filepath));
21
+ validateWithinBasePath(workingDir, path);
22
+ await validateIsFile(path);
23
+ return await readFile(path, 'utf-8');
24
+ },
25
+ formatter(content) {
26
+ return {
27
+ content,
28
+ };
29
+ },
30
+ });
31
+ //# sourceMappingURL=open-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-file.js","sourceRoot":"","sources":["../../src/tools/open-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAE9C,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AACnD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAA;AAI7E,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,MAAiB,EAAE,EAAE,CACxD,YAAY,CAAC,MAAM,EAAE;IACnB,IAAI,EAAE,WAAW;IACjB,KAAK,EAAE,WAAW;IAClB,WAAW,EAAE,gEAAgE;IAC7E,WAAW,EAAE;QACX,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,2BAA2B,CAAC;KAC3D;IACD,YAAY,EAAE;QACZ,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;KACpB;IACD,UAAU,EAAE,IAAI;IAChB,KAAK,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE;QACxB,MAAM,UAAU,GAAG,aAAa,EAAE,CAAA;QAClC,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAA;QACrD,sBAAsB,CAAC,UAAU,EAAE,IAAI,CAAC,CAAA;QACxC,MAAM,cAAc,CAAC,IAAI,CAAC,CAAA;QAE1B,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACtC,CAAC;IACD,SAAS,CAAC,OAAO;QACf,OAAO;YACL,OAAO;SACR,CAAA;IACH,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare const registerPatchFileTool: (server: McpServer) => void;
3
+ //# sourceMappingURL=patch-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch-file.d.ts","sourceRoot":"","sources":["../../src/tools/patch-file.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,eAAO,MAAM,qBAAqB,GAAI,QAAQ,SAAS,SAuFnD,CAAA"}
@@ -0,0 +1,80 @@
1
+ import { readFile, writeFile } from 'node:fs/promises';
2
+ import { normalize, resolve } from 'node:path';
3
+ import { registerTool } from '@mcp-monorepo/shared';
4
+ import { z } from 'zod';
5
+ import { getWorkingDir } from '../lib/env.js';
6
+ import { findPatchPositions } from '../lib/find-best-patch-match.js';
7
+ import { validateWithinBasePath } from '../lib/validators.js';
8
+ export const registerPatchFileTool = (server) => registerTool(server, {
9
+ name: 'patch-file',
10
+ title: 'Patch File',
11
+ description: 'Apply a patch to a file by replacing content between approximate start and end line numbers with context matching. Provide at least one line of unmodified content before and after the change.',
12
+ inputSchema: {
13
+ path: z.string().nonempty('File path must be provided'),
14
+ patch: z.string().nonempty('Patch content must be provided'),
15
+ approxStartLine: z.number().int().min(1).describe('Approximate start line number of the patch'),
16
+ approxEndLine: z
17
+ .number()
18
+ .int()
19
+ .min(1)
20
+ .describe('Approximate end line number of the patch. Must be equal or greater than start line.'),
21
+ },
22
+ outputSchema: {
23
+ actualStartPos: z.number(),
24
+ actualEndPos: z.number(),
25
+ startContextLength: z.number(),
26
+ endContextLength: z.number(),
27
+ bytesWritten: z.number(),
28
+ },
29
+ isReadOnly: false,
30
+ async fetcher({ path, patch, approxStartLine, approxEndLine }) {
31
+ const workingDir = getWorkingDir();
32
+ const fullPath = resolve(workingDir, normalize(path));
33
+ validateWithinBasePath(workingDir, fullPath);
34
+ // Read the current file content
35
+ const fileContent = await readFile(fullPath, 'utf-8');
36
+ // Find the patch positions using the provided function
37
+ const { patchStartPos, patchEndPos } = findPatchPositions(fileContent, patch, approxStartLine, approxEndLine);
38
+ if (!patchStartPos || !patchEndPos) {
39
+ throw new Error(`Could not find suitable patch positions. Start found: ${!!patchStartPos}, End found: ${!!patchEndPos}`);
40
+ }
41
+ const actualStartPos = patchStartPos.bestMatchPos;
42
+ const actualEndPos = patchEndPos.bestMatchPos;
43
+ const startContextLength = patchStartPos.maxOverlap;
44
+ const endContextLength = patchEndPos.maxOverlap;
45
+ // Validate minimum distance requirement
46
+ if (actualEndPos - actualStartPos < 5) {
47
+ throw new Error(`Derived actual start (${actualStartPos}) and end (${actualEndPos}) positions are less than 5 characters apart`);
48
+ }
49
+ // Validate minimum context length requirements
50
+ if (startContextLength < 5) {
51
+ throw new Error(`Start context length (${startContextLength}) is less than 5 characters`);
52
+ }
53
+ if (endContextLength < 5) {
54
+ throw new Error(`End context length (${endContextLength}) is less than 5 characters`);
55
+ }
56
+ // Apply the patch by replacing content between the found positions
57
+ const beforePatch = fileContent.slice(0, actualStartPos);
58
+ const afterPatch = fileContent.slice(actualEndPos);
59
+ const newContent = beforePatch + patch + afterPatch;
60
+ // Write the patched content back to the file
61
+ await writeFile(fullPath, newContent, 'utf-8');
62
+ return {
63
+ actualStartPos,
64
+ actualEndPos,
65
+ startContextLength,
66
+ endContextLength,
67
+ bytesWritten: Buffer.byteLength(newContent, 'utf-8'),
68
+ };
69
+ },
70
+ formatter({ actualStartPos, actualEndPos, startContextLength, endContextLength, bytesWritten }) {
71
+ return {
72
+ actualStartPos,
73
+ actualEndPos,
74
+ startContextLength,
75
+ endContextLength,
76
+ bytesWritten,
77
+ };
78
+ },
79
+ });
80
+ //# sourceMappingURL=patch-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch-file.js","sourceRoot":"","sources":["../../src/tools/patch-file.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare const registerRemoveFileTool: (server: McpServer) => void;
3
+ //# sourceMappingURL=remove-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remove-file.d.ts","sourceRoot":"","sources":["../../src/tools/remove-file.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAExE,eAAO,MAAM,sBAAsB,GAAI,QAAQ,SAAS,SA+CpD,CAAA"}