@saber2pr/ai-agent 0.0.43 → 0.0.44
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/lib/tools/filesystem/index.js +136 -82
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getFilesystemTools = void 0;
|
|
7
|
+
/* eslint-disable no-continue */
|
|
7
8
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
8
9
|
const minimatch_1 = require("minimatch");
|
|
9
10
|
const path_1 = __importDefault(require("path"));
|
|
@@ -14,13 +15,13 @@ const lib_1 = require("./lib");
|
|
|
14
15
|
const ReadTextFileArgsSchema = zod_1.z.object({
|
|
15
16
|
path: zod_1.z.string(),
|
|
16
17
|
tail: zod_1.z.number().optional().describe('If provided, returns only the last N lines of the file'),
|
|
17
|
-
head: zod_1.z.number().optional().describe('If provided, returns only the first N lines of the file')
|
|
18
|
+
head: zod_1.z.number().optional().describe('If provided, returns only the first N lines of the file'),
|
|
18
19
|
});
|
|
19
20
|
const ReadMultipleFilesArgsSchema = zod_1.z.object({
|
|
20
21
|
paths: zod_1.z
|
|
21
22
|
.array(zod_1.z.string())
|
|
22
|
-
.min(1,
|
|
23
|
-
.describe(
|
|
23
|
+
.min(1, 'At least one file path must be provided')
|
|
24
|
+
.describe('Array of file paths to read. Each path must be a string pointing to a valid file within allowed directories.'),
|
|
24
25
|
});
|
|
25
26
|
const WriteFileArgsSchema = zod_1.z.object({
|
|
26
27
|
path: zod_1.z.string(),
|
|
@@ -28,12 +29,16 @@ const WriteFileArgsSchema = zod_1.z.object({
|
|
|
28
29
|
});
|
|
29
30
|
const EditOperation = zod_1.z.object({
|
|
30
31
|
oldText: zod_1.z.string().describe('Text to search for - must match exactly'),
|
|
31
|
-
newText: zod_1.z.string().describe('Text to replace with')
|
|
32
|
+
newText: zod_1.z.string().describe('Text to replace with'),
|
|
32
33
|
});
|
|
33
34
|
const EditFileArgsSchema = zod_1.z.object({
|
|
34
35
|
path: zod_1.z.string(),
|
|
35
36
|
edits: zod_1.z.array(EditOperation),
|
|
36
|
-
dryRun: zod_1.z
|
|
37
|
+
dryRun: zod_1.z
|
|
38
|
+
.boolean()
|
|
39
|
+
.optional()
|
|
40
|
+
.default(false)
|
|
41
|
+
.describe('Preview changes using git-style diff format'),
|
|
37
42
|
});
|
|
38
43
|
const CreateDirectoryArgsSchema = zod_1.z.object({
|
|
39
44
|
path: zod_1.z.string(),
|
|
@@ -43,11 +48,15 @@ const ListDirectoryArgsSchema = zod_1.z.object({
|
|
|
43
48
|
});
|
|
44
49
|
const ListDirectoryWithSizesArgsSchema = zod_1.z.object({
|
|
45
50
|
path: zod_1.z.string(),
|
|
46
|
-
sortBy: zod_1.z
|
|
51
|
+
sortBy: zod_1.z
|
|
52
|
+
.enum(['name', 'size'])
|
|
53
|
+
.optional()
|
|
54
|
+
.default('name')
|
|
55
|
+
.describe('Sort entries by name or size'),
|
|
47
56
|
});
|
|
48
57
|
const DirectoryTreeArgsSchema = zod_1.z.object({
|
|
49
58
|
path: zod_1.z.string(),
|
|
50
|
-
excludePatterns: zod_1.z.array(zod_1.z.string()).optional().default([])
|
|
59
|
+
excludePatterns: zod_1.z.array(zod_1.z.string()).optional().default([]),
|
|
51
60
|
});
|
|
52
61
|
const MoveFileArgsSchema = zod_1.z.object({
|
|
53
62
|
source: zod_1.z.string(),
|
|
@@ -56,18 +65,23 @@ const MoveFileArgsSchema = zod_1.z.object({
|
|
|
56
65
|
const SearchFilesArgsSchema = zod_1.z.object({
|
|
57
66
|
path: zod_1.z.string(),
|
|
58
67
|
pattern: zod_1.z.string(),
|
|
59
|
-
excludePatterns: zod_1.z.array(zod_1.z.string()).optional().default([])
|
|
68
|
+
excludePatterns: zod_1.z.array(zod_1.z.string()).optional().default([]),
|
|
60
69
|
});
|
|
61
70
|
const GetFileInfoArgsSchema = zod_1.z.object({
|
|
62
71
|
path: zod_1.z.string(),
|
|
63
72
|
});
|
|
73
|
+
const GrepSearchArgsSchema = zod_1.z.object({
|
|
74
|
+
path: zod_1.z.string().describe('The starting directory path for searching content'),
|
|
75
|
+
query: zod_1.z.string().describe('The text keyword to search for'),
|
|
76
|
+
includePattern: zod_1.z.string().optional().default('**/*').describe('Only search in files matching this pattern, for example "**/*.ts"'),
|
|
77
|
+
});
|
|
64
78
|
const getFilesystemTools = (targetDir) => {
|
|
65
79
|
(0, lib_1.setAllowedDirectories)([targetDir]);
|
|
66
80
|
// read_file (deprecated) and read_text_file
|
|
67
81
|
const readTextFileHandler = async (args) => {
|
|
68
82
|
const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
|
|
69
83
|
if (args.head && args.tail) {
|
|
70
|
-
throw new Error(
|
|
84
|
+
throw new Error('Cannot specify both head and tail parameters simultaneously');
|
|
71
85
|
}
|
|
72
86
|
let content;
|
|
73
87
|
if (args.tail) {
|
|
@@ -82,23 +96,23 @@ const getFilesystemTools = (targetDir) => {
|
|
|
82
96
|
return content;
|
|
83
97
|
};
|
|
84
98
|
const readTextFileTool = (0, createTool_1.createTool)({
|
|
85
|
-
name:
|
|
86
|
-
description:
|
|
87
|
-
|
|
88
|
-
|
|
99
|
+
name: 'read_text_file',
|
|
100
|
+
description: 'Read the complete contents of a file from the file system as text. ' +
|
|
101
|
+
'Handles various text encodings and provides detailed error messages ' +
|
|
102
|
+
'if the file cannot be read. Use this tool when you need to examine ' +
|
|
89
103
|
"the contents of a single file. Use the 'head' parameter to read only " +
|
|
90
104
|
"the first N lines of a file, or the 'tail' parameter to read only " +
|
|
91
|
-
|
|
105
|
+
'the last N lines of a file. Operates on the file as text regardless of extension.',
|
|
92
106
|
parameters: ReadTextFileArgsSchema,
|
|
93
|
-
handler: readTextFileHandler
|
|
107
|
+
handler: readTextFileHandler,
|
|
94
108
|
});
|
|
95
109
|
const readMultipleFilesTool = (0, createTool_1.createTool)({
|
|
96
|
-
name:
|
|
97
|
-
description:
|
|
98
|
-
|
|
110
|
+
name: 'read_multiple_files',
|
|
111
|
+
description: 'Read the contents of multiple files simultaneously. This is more ' +
|
|
112
|
+
'efficient than reading files one by one when you need to analyze ' +
|
|
99
113
|
"or compare multiple files. Each file's content is returned with its " +
|
|
100
114
|
"path as a reference. Failed reads for individual files won't stop " +
|
|
101
|
-
|
|
115
|
+
'the entire operation. Only works within allowed directories.',
|
|
102
116
|
parameters: ReadMultipleFilesArgsSchema,
|
|
103
117
|
handler: async (args) => {
|
|
104
118
|
const results = await Promise.all(args.paths.map(async (filePath) => {
|
|
@@ -112,71 +126,71 @@ const getFilesystemTools = (targetDir) => {
|
|
|
112
126
|
return `${filePath}: Error - ${errorMessage}`;
|
|
113
127
|
}
|
|
114
128
|
}));
|
|
115
|
-
const text = results.join(
|
|
129
|
+
const text = results.join('\n---\n');
|
|
116
130
|
return text;
|
|
117
|
-
}
|
|
131
|
+
},
|
|
118
132
|
});
|
|
119
133
|
const writeFileTool = (0, createTool_1.createTool)({
|
|
120
|
-
name:
|
|
121
|
-
description:
|
|
122
|
-
|
|
123
|
-
|
|
134
|
+
name: 'write_file',
|
|
135
|
+
description: 'Create a new file or completely overwrite an existing file with new content. ' +
|
|
136
|
+
'Use with caution as it will overwrite existing files without warning. ' +
|
|
137
|
+
'Handles text content with proper encoding. Only works within allowed directories.',
|
|
124
138
|
parameters: WriteFileArgsSchema,
|
|
125
139
|
handler: async (args) => {
|
|
126
140
|
const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
|
|
127
141
|
await (0, lib_1.writeFileContent)(validPath, args.content);
|
|
128
142
|
const text = `Successfully wrote to ${args.path}`;
|
|
129
143
|
return text;
|
|
130
|
-
}
|
|
144
|
+
},
|
|
131
145
|
});
|
|
132
146
|
const editFileTool = (0, createTool_1.createTool)({
|
|
133
|
-
name:
|
|
134
|
-
description:
|
|
135
|
-
|
|
136
|
-
|
|
147
|
+
name: 'edit_file',
|
|
148
|
+
description: 'Make line-based edits to a text file. Each edit replaces exact line sequences ' +
|
|
149
|
+
'with new content. Returns a git-style diff showing the changes made. ' +
|
|
150
|
+
'Only works within allowed directories.',
|
|
137
151
|
parameters: EditFileArgsSchema,
|
|
138
152
|
handler: async (args) => {
|
|
139
153
|
const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
|
|
140
154
|
const result = await (0, lib_1.applyFileEdits)(validPath, args.edits, args.dryRun);
|
|
141
155
|
return result;
|
|
142
|
-
}
|
|
156
|
+
},
|
|
143
157
|
});
|
|
144
158
|
const createDirectoryTool = (0, createTool_1.createTool)({
|
|
145
|
-
name:
|
|
146
|
-
description:
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
159
|
+
name: 'create_directory',
|
|
160
|
+
description: 'Create a new directory or ensure a directory exists. Can create multiple ' +
|
|
161
|
+
'nested directories in one operation. If the directory already exists, ' +
|
|
162
|
+
'this operation will succeed silently. Perfect for setting up directory ' +
|
|
163
|
+
'structures for projects or ensuring required paths exist. Only works within allowed directories.',
|
|
150
164
|
parameters: CreateDirectoryArgsSchema,
|
|
151
165
|
handler: async (args) => {
|
|
152
166
|
const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
|
|
153
167
|
await promises_1.default.mkdir(validPath, { recursive: true });
|
|
154
168
|
const text = `Successfully created directory ${args.path}`;
|
|
155
169
|
return text;
|
|
156
|
-
}
|
|
170
|
+
},
|
|
157
171
|
});
|
|
158
172
|
const listDirectoryTool = (0, createTool_1.createTool)({
|
|
159
|
-
name:
|
|
160
|
-
description:
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
173
|
+
name: 'list_directory',
|
|
174
|
+
description: 'Get a detailed listing of all files and directories in a specified path. ' +
|
|
175
|
+
'Results clearly distinguish between files and directories with [FILE] and [DIR] ' +
|
|
176
|
+
'prefixes. This tool is essential for understanding directory structure and ' +
|
|
177
|
+
'finding specific files within a directory. Only works within allowed directories.',
|
|
164
178
|
parameters: ListDirectoryArgsSchema,
|
|
165
179
|
handler: async (args) => {
|
|
166
180
|
const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
|
|
167
181
|
const entries = await promises_1.default.readdir(validPath, { withFileTypes: true });
|
|
168
182
|
const formatted = entries
|
|
169
|
-
.map(
|
|
170
|
-
.join(
|
|
183
|
+
.map(entry => `${entry.isDirectory() ? '[DIR]' : '[FILE]'} ${entry.name}`)
|
|
184
|
+
.join('\n');
|
|
171
185
|
return formatted;
|
|
172
|
-
}
|
|
186
|
+
},
|
|
173
187
|
});
|
|
174
188
|
const listDirectoryWithSizesTool = (0, createTool_1.createTool)({
|
|
175
|
-
name:
|
|
176
|
-
description:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
189
|
+
name: 'list_directory_with_sizes',
|
|
190
|
+
description: 'Get a detailed listing of all files and directories in a specified path, including sizes. ' +
|
|
191
|
+
'Results clearly distinguish between files and directories with [FILE] and [DIR] ' +
|
|
192
|
+
'prefixes. This tool is useful for understanding directory structure and ' +
|
|
193
|
+
'finding specific files within a directory. Only works within allowed directories.',
|
|
180
194
|
parameters: ListDirectoryWithSizesArgsSchema,
|
|
181
195
|
handler: async (args) => {
|
|
182
196
|
const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
|
|
@@ -190,7 +204,7 @@ const getFilesystemTools = (targetDir) => {
|
|
|
190
204
|
name: entry.name,
|
|
191
205
|
isDirectory: entry.isDirectory(),
|
|
192
206
|
size: stats.size,
|
|
193
|
-
mtime: stats.mtime
|
|
207
|
+
mtime: stats.mtime,
|
|
194
208
|
};
|
|
195
209
|
}
|
|
196
210
|
catch (error) {
|
|
@@ -198,7 +212,7 @@ const getFilesystemTools = (targetDir) => {
|
|
|
198
212
|
name: entry.name,
|
|
199
213
|
isDirectory: entry.isDirectory(),
|
|
200
214
|
size: 0,
|
|
201
|
-
mtime: new Date(0)
|
|
215
|
+
mtime: new Date(0),
|
|
202
216
|
};
|
|
203
217
|
}
|
|
204
218
|
}));
|
|
@@ -211,26 +225,26 @@ const getFilesystemTools = (targetDir) => {
|
|
|
211
225
|
return a.name.localeCompare(b.name);
|
|
212
226
|
});
|
|
213
227
|
// Format the output
|
|
214
|
-
const formattedEntries = sortedEntries.map(entry => `${entry.isDirectory ?
|
|
228
|
+
const formattedEntries = sortedEntries.map(entry => `${entry.isDirectory ? '[DIR]' : '[FILE]'} ${entry.name.padEnd(30)} ${entry.isDirectory ? '' : (0, lib_1.formatSize)(entry.size).padStart(10)}`);
|
|
215
229
|
// Add summary
|
|
216
230
|
const totalFiles = detailedEntries.filter(e => !e.isDirectory).length;
|
|
217
231
|
const totalDirs = detailedEntries.filter(e => e.isDirectory).length;
|
|
218
232
|
const totalSize = detailedEntries.reduce((sum, entry) => sum + (entry.isDirectory ? 0 : entry.size), 0);
|
|
219
233
|
const summary = [
|
|
220
|
-
|
|
234
|
+
'',
|
|
221
235
|
`Total: ${totalFiles} files, ${totalDirs} directories`,
|
|
222
|
-
`Combined size: ${(0, lib_1.formatSize)(totalSize)}
|
|
236
|
+
`Combined size: ${(0, lib_1.formatSize)(totalSize)}`,
|
|
223
237
|
];
|
|
224
|
-
const text = [...formattedEntries, ...summary].join(
|
|
238
|
+
const text = [...formattedEntries, ...summary].join('\n');
|
|
225
239
|
return text;
|
|
226
|
-
}
|
|
240
|
+
},
|
|
227
241
|
});
|
|
228
242
|
const directoryTreeTool = (0, createTool_1.createTool)({
|
|
229
|
-
name:
|
|
230
|
-
description:
|
|
243
|
+
name: 'directory_tree',
|
|
244
|
+
description: 'Get a recursive tree view of files and directories as a JSON structure. ' +
|
|
231
245
|
"Each entry includes 'name', 'type' (file/directory), and 'children' for directories. " +
|
|
232
|
-
|
|
233
|
-
|
|
246
|
+
'Files have no children array, while directories always have a children array (which may be empty). ' +
|
|
247
|
+
'The output is formatted with 2-space indentation for readability. Only works within allowed directories.',
|
|
234
248
|
parameters: DirectoryTreeArgsSchema,
|
|
235
249
|
handler: async (args) => {
|
|
236
250
|
const rootPath = args.path;
|
|
@@ -246,15 +260,15 @@ const getFilesystemTools = (targetDir) => {
|
|
|
246
260
|
}
|
|
247
261
|
// For files: match exact name or as part of path
|
|
248
262
|
// For directories: match as directory path
|
|
249
|
-
return (0, minimatch_1.minimatch)(relativePath, pattern, { dot: true }) ||
|
|
263
|
+
return ((0, minimatch_1.minimatch)(relativePath, pattern, { dot: true }) ||
|
|
250
264
|
(0, minimatch_1.minimatch)(relativePath, `**/${pattern}`, { dot: true }) ||
|
|
251
|
-
(0, minimatch_1.minimatch)(relativePath, `**/${pattern}/**`, { dot: true });
|
|
265
|
+
(0, minimatch_1.minimatch)(relativePath, `**/${pattern}/**`, { dot: true }));
|
|
252
266
|
});
|
|
253
267
|
if (shouldExclude)
|
|
254
268
|
continue;
|
|
255
269
|
const entryData = {
|
|
256
270
|
name: entry.name,
|
|
257
|
-
type: entry.isDirectory() ? 'directory' : 'file'
|
|
271
|
+
type: entry.isDirectory() ? 'directory' : 'file',
|
|
258
272
|
};
|
|
259
273
|
if (entry.isDirectory()) {
|
|
260
274
|
const subPath = path_1.default.join(currentPath, entry.name);
|
|
@@ -267,14 +281,14 @@ const getFilesystemTools = (targetDir) => {
|
|
|
267
281
|
const treeData = await buildTree(rootPath, args.excludePatterns);
|
|
268
282
|
const text = JSON.stringify(treeData, null, 2);
|
|
269
283
|
return text;
|
|
270
|
-
}
|
|
284
|
+
},
|
|
271
285
|
});
|
|
272
286
|
const moveFileTool = (0, createTool_1.createTool)({
|
|
273
|
-
name:
|
|
274
|
-
description:
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
287
|
+
name: 'move_file',
|
|
288
|
+
description: 'Move or rename files and directories. Can move files between directories ' +
|
|
289
|
+
'and rename them in a single operation. If the destination exists, the ' +
|
|
290
|
+
'operation will fail. Works across different directories and can be used ' +
|
|
291
|
+
'for simple renaming within the same directory. Both source and destination must be within allowed directories.',
|
|
278
292
|
parameters: MoveFileArgsSchema,
|
|
279
293
|
handler: async (args) => {
|
|
280
294
|
const validSourcePath = await (0, lib_1.validatePath)(targetDir, args.source);
|
|
@@ -282,33 +296,72 @@ const getFilesystemTools = (targetDir) => {
|
|
|
282
296
|
await promises_1.default.rename(validSourcePath, validDestPath);
|
|
283
297
|
const text = `Successfully moved ${args.source} to ${args.destination}`;
|
|
284
298
|
return text;
|
|
285
|
-
}
|
|
299
|
+
},
|
|
286
300
|
});
|
|
287
301
|
const searchFilesTool = (0, createTool_1.createTool)({
|
|
288
|
-
name:
|
|
289
|
-
description:
|
|
290
|
-
|
|
302
|
+
name: 'search_files',
|
|
303
|
+
description: 'Search for files matching a specific pattern in a specified path. ' +
|
|
304
|
+
'Returns a list of files that match the pattern. Only works within allowed directories.',
|
|
291
305
|
parameters: SearchFilesArgsSchema,
|
|
292
306
|
handler: async (args) => {
|
|
293
307
|
const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
|
|
294
|
-
const results = await (0, lib_1.searchFilesWithValidation)(targetDir, validPath, args.pattern, [], {
|
|
295
|
-
|
|
308
|
+
const results = await (0, lib_1.searchFilesWithValidation)(targetDir, validPath, args.pattern, [targetDir], {
|
|
309
|
+
excludePatterns: ['node_modules', ...((args === null || args === void 0 ? void 0 : args.excludePatterns) || [])],
|
|
310
|
+
});
|
|
311
|
+
const text = results.length > 0 ? results.join('\n') : 'No matches found';
|
|
296
312
|
return text;
|
|
297
|
-
}
|
|
313
|
+
},
|
|
298
314
|
});
|
|
299
315
|
const getFileInfoTool = (0, createTool_1.createTool)({
|
|
300
|
-
name:
|
|
301
|
-
description:
|
|
302
|
-
|
|
316
|
+
name: 'get_file_info',
|
|
317
|
+
description: 'Get detailed information about a file, including its size, last modified time, and type. ' +
|
|
318
|
+
'Only works within allowed directories.',
|
|
303
319
|
parameters: GetFileInfoArgsSchema,
|
|
304
320
|
handler: async (args) => {
|
|
305
321
|
const validPath = await (0, lib_1.validatePath)(targetDir, args.path);
|
|
306
322
|
const info = await (0, lib_1.getFileStats)(validPath);
|
|
307
323
|
const text = Object.entries(info)
|
|
308
324
|
.map(([key, value]) => `${key}: ${value}`)
|
|
309
|
-
.join(
|
|
325
|
+
.join('\n');
|
|
310
326
|
return text;
|
|
311
|
-
}
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
const grepSearchTool = (0, createTool_1.createTool)({
|
|
330
|
+
name: 'grep_search',
|
|
331
|
+
description: 'Search for a specific keyword in the content of files in a specified directory. ' +
|
|
332
|
+
'This tool will recursively read files and return the paths of all files containing the keyword. ' +
|
|
333
|
+
'Useful for finding variable definitions, function calls, or specific text.',
|
|
334
|
+
parameters: GrepSearchArgsSchema,
|
|
335
|
+
handler: async (args) => {
|
|
336
|
+
const startPath = await (0, lib_1.validatePath)(targetDir, args.path);
|
|
337
|
+
// 1. 利用你现有的工具先筛选出文件列表
|
|
338
|
+
const allFiles = await (0, lib_1.searchFilesWithValidation)(targetDir, startPath, args.includePattern, [targetDir], { excludePatterns: ['node_modules', 'dist', '.git'] });
|
|
339
|
+
const matches = [];
|
|
340
|
+
// 2. 并发读取文件内容进行关键词匹配 (限制并发数为 10,防止内存或句柄爆炸)
|
|
341
|
+
const concurrencyLimit = 10;
|
|
342
|
+
for (let i = 0; i < allFiles.length; i += concurrencyLimit) {
|
|
343
|
+
const chunk = allFiles.slice(i, i + concurrencyLimit);
|
|
344
|
+
await Promise.all(chunk.map(async (filePath) => {
|
|
345
|
+
try {
|
|
346
|
+
// 注意:这里确保只搜索文件,不搜索目录
|
|
347
|
+
const stats = await promises_1.default.stat(filePath);
|
|
348
|
+
if (!stats.isFile())
|
|
349
|
+
return;
|
|
350
|
+
const content = await (0, lib_1.readFileContent)(filePath);
|
|
351
|
+
if (content.includes(args.query)) {
|
|
352
|
+
// 返回相对路径,方便查看
|
|
353
|
+
matches.push(path_1.default.relative(targetDir, filePath));
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
catch {
|
|
357
|
+
// 忽略读取失败的文件(如二进制文件或无权限文件)
|
|
358
|
+
}
|
|
359
|
+
}));
|
|
360
|
+
}
|
|
361
|
+
return matches.length > 0
|
|
362
|
+
? `Found keyword "${args.query}" in the following files:\n${matches.join('\n')}`
|
|
363
|
+
: `No content containing "${args.query}" found in the specified range.`;
|
|
364
|
+
},
|
|
312
365
|
});
|
|
313
366
|
return [
|
|
314
367
|
readTextFileTool,
|
|
@@ -322,6 +375,7 @@ const getFilesystemTools = (targetDir) => {
|
|
|
322
375
|
moveFileTool,
|
|
323
376
|
searchFilesTool,
|
|
324
377
|
getFileInfoTool,
|
|
378
|
+
grepSearchTool,
|
|
325
379
|
];
|
|
326
380
|
};
|
|
327
381
|
exports.getFilesystemTools = getFilesystemTools;
|