@dexto/tools-filesystem 1.6.0 → 1.6.2

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 (73) hide show
  1. package/dist/directory-approval.cjs +44 -40
  2. package/dist/directory-approval.d.ts +8 -4
  3. package/dist/directory-approval.d.ts.map +1 -1
  4. package/dist/directory-approval.integration.test.cjs +107 -356
  5. package/dist/directory-approval.integration.test.d.ts +6 -6
  6. package/dist/directory-approval.integration.test.js +109 -360
  7. package/dist/directory-approval.js +45 -41
  8. package/dist/edit-file-tool.cjs +69 -47
  9. package/dist/edit-file-tool.d.ts.map +1 -1
  10. package/dist/edit-file-tool.js +77 -48
  11. package/dist/edit-file-tool.test.cjs +54 -11
  12. package/dist/edit-file-tool.test.js +54 -11
  13. package/dist/error-codes.cjs +4 -0
  14. package/dist/error-codes.d.ts +4 -0
  15. package/dist/error-codes.d.ts.map +1 -1
  16. package/dist/error-codes.js +4 -0
  17. package/dist/errors.cjs +48 -0
  18. package/dist/errors.d.ts +16 -0
  19. package/dist/errors.d.ts.map +1 -1
  20. package/dist/errors.js +48 -0
  21. package/dist/filesystem-service.cjs +307 -9
  22. package/dist/filesystem-service.d.ts +28 -1
  23. package/dist/filesystem-service.d.ts.map +1 -1
  24. package/dist/filesystem-service.js +308 -10
  25. package/dist/glob-files-tool.cjs +12 -1
  26. package/dist/glob-files-tool.d.ts.map +1 -1
  27. package/dist/glob-files-tool.js +13 -2
  28. package/dist/grep-content-tool.cjs +13 -1
  29. package/dist/grep-content-tool.d.ts.map +1 -1
  30. package/dist/grep-content-tool.js +14 -2
  31. package/dist/index.cjs +3 -0
  32. package/dist/index.d.cts +852 -16
  33. package/dist/index.d.ts +2 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +2 -0
  36. package/dist/path-validator.cjs +28 -2
  37. package/dist/path-validator.d.ts +14 -0
  38. package/dist/path-validator.d.ts.map +1 -1
  39. package/dist/path-validator.js +28 -2
  40. package/dist/read-file-tool.cjs +7 -1
  41. package/dist/read-file-tool.d.ts.map +1 -1
  42. package/dist/read-file-tool.js +8 -2
  43. package/dist/tool-factory.cjs +21 -0
  44. package/dist/tool-factory.d.ts.map +1 -1
  45. package/dist/tool-factory.js +21 -0
  46. package/dist/types.d.ts +65 -0
  47. package/dist/types.d.ts.map +1 -1
  48. package/dist/write-file-tool.cjs +60 -38
  49. package/dist/write-file-tool.d.ts +1 -1
  50. package/dist/write-file-tool.d.ts.map +1 -1
  51. package/dist/write-file-tool.js +67 -39
  52. package/dist/write-file-tool.test.cjs +75 -13
  53. package/dist/write-file-tool.test.js +75 -13
  54. package/package.json +6 -6
  55. package/dist/directory-approval.d.cts +0 -22
  56. package/dist/directory-approval.integration.test.d.cts +0 -2
  57. package/dist/edit-file-tool.d.cts +0 -34
  58. package/dist/edit-file-tool.test.d.cts +0 -2
  59. package/dist/error-codes.d.cts +0 -32
  60. package/dist/errors.d.cts +0 -112
  61. package/dist/file-tool-types.d.cts +0 -18
  62. package/dist/filesystem-service.d.cts +0 -117
  63. package/dist/filesystem-service.test.d.cts +0 -2
  64. package/dist/glob-files-tool.d.cts +0 -31
  65. package/dist/grep-content-tool.d.cts +0 -40
  66. package/dist/path-validator.d.cts +0 -97
  67. package/dist/path-validator.test.d.cts +0 -2
  68. package/dist/read-file-tool.d.cts +0 -31
  69. package/dist/tool-factory-config.d.cts +0 -63
  70. package/dist/tool-factory.d.cts +0 -7
  71. package/dist/types.d.cts +0 -178
  72. package/dist/write-file-tool.d.cts +0 -34
  73. package/dist/write-file-tool.test.d.cts +0 -2
@@ -2,12 +2,14 @@ import * as fs from "node:fs/promises";
2
2
  import * as path from "node:path";
3
3
  import { glob } from "glob";
4
4
  import safeRegex from "safe-regex";
5
- import { getDextoPath, DextoLogComponent } from "@dexto/core";
5
+ import { DextoRuntimeError, getDextoPath, DextoLogComponent } from "@dexto/core";
6
6
  import { PathValidator } from "./path-validator.js";
7
7
  import { FileSystemError } from "./errors.js";
8
8
  const DEFAULT_ENCODING = "utf-8";
9
9
  const DEFAULT_MAX_RESULTS = 1e3;
10
10
  const DEFAULT_MAX_SEARCH_RESULTS = 100;
11
+ const DEFAULT_MAX_LIST_RESULTS = 5e3;
12
+ const DEFAULT_LIST_CONCURRENCY = 16;
11
13
  class FileSystemService {
12
14
  config;
13
15
  pathValidator;
@@ -120,16 +122,14 @@ class FileSystemService {
120
122
  async isPathWithinConfigAllowed(filePath) {
121
123
  return this.pathValidator.isPathWithinAllowed(filePath);
122
124
  }
123
- /**
124
- * Read a file with validation and size limits
125
- */
126
- async readFile(filePath, options = {}) {
127
- await this.ensureInitialized();
128
- const validation = await this.pathValidator.validatePath(filePath);
125
+ async validateReadPath(filePath, mode) {
126
+ const validation = mode === "toolPreview" ? await this.pathValidator.validatePathForPreview(filePath) : await this.pathValidator.validatePath(filePath);
129
127
  if (!validation.isValid || !validation.normalizedPath) {
130
128
  throw FileSystemError.invalidPath(filePath, validation.error || "Unknown error");
131
129
  }
132
- const normalizedPath = validation.normalizedPath;
130
+ return validation.normalizedPath;
131
+ }
132
+ async readNormalizedFile(normalizedPath, options = {}) {
133
133
  try {
134
134
  const stats = await fs.stat(normalizedPath);
135
135
  if (!stats.isFile()) {
@@ -143,12 +143,18 @@ class FileSystemService {
143
143
  );
144
144
  }
145
145
  } catch (error) {
146
+ if (error instanceof DextoRuntimeError && error.scope === "filesystem") {
147
+ throw error;
148
+ }
146
149
  if (error.code === "ENOENT") {
147
150
  throw FileSystemError.fileNotFound(normalizedPath);
148
151
  }
149
152
  if (error.code === "EACCES") {
150
153
  throw FileSystemError.permissionDenied(normalizedPath, "read");
151
154
  }
155
+ if (error instanceof DextoRuntimeError) {
156
+ throw error;
157
+ }
152
158
  throw FileSystemError.readFailed(
153
159
  normalizedPath,
154
160
  error instanceof Error ? error.message : String(error)
@@ -170,20 +176,44 @@ class FileSystemService {
170
176
  } else {
171
177
  selectedLines = lines;
172
178
  }
179
+ const returnedContent = selectedLines.join("\n");
173
180
  return {
174
- content: selectedLines.join("\n"),
181
+ content: returnedContent,
175
182
  lines: selectedLines.length,
176
183
  encoding,
177
184
  truncated,
178
- size: Buffer.byteLength(content, encoding)
185
+ size: Buffer.byteLength(returnedContent, encoding)
179
186
  };
180
187
  } catch (error) {
188
+ if (error instanceof DextoRuntimeError && error.scope === "filesystem") {
189
+ throw error;
190
+ }
181
191
  throw FileSystemError.readFailed(
182
192
  normalizedPath,
183
193
  error instanceof Error ? error.message : String(error)
184
194
  );
185
195
  }
186
196
  }
197
+ /**
198
+ * Read a file with validation and size limits
199
+ */
200
+ async readFile(filePath, options = {}) {
201
+ await this.ensureInitialized();
202
+ const normalizedPath = await this.validateReadPath(filePath, "execute");
203
+ return await this.readNormalizedFile(normalizedPath, options);
204
+ }
205
+ /**
206
+ * Preview-only file read that bypasses config-allowed roots.
207
+ *
208
+ * This is intended for UI previews (diffs, create previews) shown BEFORE a user
209
+ * confirms directory access for the tool call. The returned content is UI-only
210
+ * and should not be forwarded to the LLM.
211
+ */
212
+ async readFileForToolPreview(filePath, options = {}) {
213
+ await this.ensureInitialized();
214
+ const normalizedPath = await this.validateReadPath(filePath, "toolPreview");
215
+ return await this.readNormalizedFile(normalizedPath, options);
216
+ }
187
217
  /**
188
218
  * Find files matching a glob pattern
189
219
  */
@@ -246,6 +276,274 @@ class FileSystemService {
246
276
  );
247
277
  }
248
278
  }
279
+ /**
280
+ * List contents of a directory (non-recursive)
281
+ */
282
+ async listDirectory(dirPath, options = {}) {
283
+ await this.ensureInitialized();
284
+ const validation = await this.pathValidator.validatePath(dirPath);
285
+ if (!validation.isValid || !validation.normalizedPath) {
286
+ throw FileSystemError.invalidPath(dirPath, validation.error || "Unknown error");
287
+ }
288
+ const normalizedPath = validation.normalizedPath;
289
+ try {
290
+ const stats = await fs.stat(normalizedPath);
291
+ if (!stats.isDirectory()) {
292
+ throw FileSystemError.invalidPath(normalizedPath, "Path is not a directory");
293
+ }
294
+ } catch (error) {
295
+ if (error instanceof DextoRuntimeError && error.scope === "filesystem") {
296
+ throw error;
297
+ }
298
+ if (error.code === "ENOENT") {
299
+ throw FileSystemError.directoryNotFound(normalizedPath);
300
+ }
301
+ if (error.code === "EACCES") {
302
+ throw FileSystemError.permissionDenied(normalizedPath, "read");
303
+ }
304
+ throw FileSystemError.listFailed(
305
+ normalizedPath,
306
+ error instanceof Error ? error.message : String(error)
307
+ );
308
+ }
309
+ const includeHidden = options.includeHidden ?? true;
310
+ const includeMetadata = options.includeMetadata !== false;
311
+ const maxEntries = options.maxEntries ?? DEFAULT_MAX_LIST_RESULTS;
312
+ try {
313
+ const dirEntries = await fs.readdir(normalizedPath, { withFileTypes: true });
314
+ const candidates = dirEntries.filter(
315
+ (entry) => includeHidden || !entry.name.startsWith(".")
316
+ );
317
+ const concurrency = DEFAULT_LIST_CONCURRENCY;
318
+ const validatedEntries = await this.mapWithConcurrency(
319
+ candidates,
320
+ concurrency,
321
+ async (entry) => {
322
+ const entryPath = path.join(normalizedPath, entry.name);
323
+ const entryValidation = await this.pathValidator.validatePath(entryPath);
324
+ if (!entryValidation.isValid || !entryValidation.normalizedPath) {
325
+ return null;
326
+ }
327
+ return {
328
+ entry,
329
+ normalizedPath: entryValidation.normalizedPath
330
+ };
331
+ }
332
+ );
333
+ const validEntries = validatedEntries.filter(Boolean);
334
+ if (maxEntries <= 0) {
335
+ return {
336
+ path: normalizedPath,
337
+ entries: [],
338
+ truncated: validEntries.length > 0,
339
+ totalEntries: validEntries.length
340
+ };
341
+ }
342
+ if (!includeMetadata) {
343
+ const entries2 = validEntries.slice(0, maxEntries).map((entry) => ({
344
+ name: entry.entry.name,
345
+ path: entry.normalizedPath,
346
+ isDirectory: entry.entry.isDirectory(),
347
+ size: 0,
348
+ modified: /* @__PURE__ */ new Date()
349
+ }));
350
+ return {
351
+ path: normalizedPath,
352
+ entries: entries2,
353
+ truncated: validEntries.length > maxEntries,
354
+ totalEntries: validEntries.length
355
+ };
356
+ }
357
+ const metadataEntries = await this.mapWithConcurrency(
358
+ validEntries,
359
+ concurrency,
360
+ async (entry) => {
361
+ try {
362
+ const stat = await fs.stat(entry.normalizedPath);
363
+ return {
364
+ name: entry.entry.name,
365
+ path: entry.normalizedPath,
366
+ isDirectory: entry.entry.isDirectory(),
367
+ size: stat.size,
368
+ modified: stat.mtime
369
+ };
370
+ } catch {
371
+ return null;
372
+ }
373
+ }
374
+ );
375
+ const entries = [];
376
+ let successfulStats = 0;
377
+ let cutoffIndex = -1;
378
+ for (let index = 0; index < metadataEntries.length; index += 1) {
379
+ const entry = metadataEntries[index];
380
+ if (!entry) {
381
+ continue;
382
+ }
383
+ successfulStats += 1;
384
+ if (entries.length < maxEntries) {
385
+ entries.push(entry);
386
+ }
387
+ if (successfulStats === maxEntries) {
388
+ cutoffIndex = index;
389
+ }
390
+ }
391
+ const remainingSuccessful = cutoffIndex >= 0 ? metadataEntries.slice(cutoffIndex + 1).filter((entry) => entry !== null).length : 0;
392
+ const totalEntries = successfulStats < maxEntries ? successfulStats : maxEntries + remainingSuccessful;
393
+ return {
394
+ path: normalizedPath,
395
+ entries,
396
+ truncated: totalEntries > maxEntries,
397
+ totalEntries
398
+ };
399
+ } catch (error) {
400
+ throw FileSystemError.listFailed(
401
+ normalizedPath,
402
+ error instanceof Error ? error.message : String(error)
403
+ );
404
+ }
405
+ }
406
+ async mapWithConcurrency(items, limit, mapper) {
407
+ if (items.length === 0) {
408
+ return [];
409
+ }
410
+ const results = new Array(items.length);
411
+ let nextIndex = 0;
412
+ const workerCount = Math.min(Math.max(1, limit), items.length);
413
+ const workers = Array.from({ length: workerCount }, async () => {
414
+ while (true) {
415
+ const current = nextIndex++;
416
+ if (current >= items.length) {
417
+ return;
418
+ }
419
+ const item = items[current];
420
+ if (item === void 0) {
421
+ continue;
422
+ }
423
+ results[current] = await mapper(item, current);
424
+ }
425
+ });
426
+ await Promise.all(workers);
427
+ return results;
428
+ }
429
+ /**
430
+ * Create a directory
431
+ */
432
+ async createDirectory(dirPath, options = {}) {
433
+ await this.ensureInitialized();
434
+ const validation = await this.pathValidator.validatePath(dirPath);
435
+ if (!validation.isValid || !validation.normalizedPath) {
436
+ throw FileSystemError.invalidPath(dirPath, validation.error || "Unknown error");
437
+ }
438
+ const normalizedPath = validation.normalizedPath;
439
+ const recursive = options.recursive ?? false;
440
+ try {
441
+ const firstCreated = await fs.mkdir(normalizedPath, { recursive });
442
+ const created = recursive ? typeof firstCreated === "string" : true;
443
+ return { path: normalizedPath, created };
444
+ } catch (error) {
445
+ const code = error.code;
446
+ if (code === "EEXIST") {
447
+ try {
448
+ const stat = await fs.stat(normalizedPath);
449
+ if (stat.isDirectory()) {
450
+ return { path: normalizedPath, created: false };
451
+ }
452
+ } catch {
453
+ }
454
+ }
455
+ if (code === "EACCES" || code === "EPERM") {
456
+ throw FileSystemError.permissionDenied(normalizedPath, "create directory");
457
+ }
458
+ throw FileSystemError.createDirFailed(
459
+ normalizedPath,
460
+ error instanceof Error ? error.message : String(error)
461
+ );
462
+ }
463
+ }
464
+ /**
465
+ * Delete a file or directory
466
+ */
467
+ async deletePath(targetPath, options = {}) {
468
+ await this.ensureInitialized();
469
+ const validation = await this.pathValidator.validatePath(targetPath);
470
+ if (!validation.isValid || !validation.normalizedPath) {
471
+ throw FileSystemError.invalidPath(targetPath, validation.error || "Unknown error");
472
+ }
473
+ const normalizedPath = validation.normalizedPath;
474
+ try {
475
+ await fs.rm(normalizedPath, { recursive: options.recursive ?? false, force: false });
476
+ return { path: normalizedPath, deleted: true };
477
+ } catch (error) {
478
+ const code = error.code;
479
+ if (code === "ENOENT") {
480
+ throw FileSystemError.fileNotFound(normalizedPath);
481
+ }
482
+ if (code === "EACCES" || code === "EPERM") {
483
+ throw FileSystemError.permissionDenied(normalizedPath, "delete");
484
+ }
485
+ throw FileSystemError.deleteFailed(
486
+ normalizedPath,
487
+ error instanceof Error ? error.message : String(error)
488
+ );
489
+ }
490
+ }
491
+ /**
492
+ * Rename or move a file or directory
493
+ */
494
+ async renamePath(fromPath, toPath) {
495
+ await this.ensureInitialized();
496
+ const fromValidation = await this.pathValidator.validatePath(fromPath);
497
+ if (!fromValidation.isValid || !fromValidation.normalizedPath) {
498
+ throw FileSystemError.invalidPath(fromPath, fromValidation.error || "Unknown error");
499
+ }
500
+ const toValidation = await this.pathValidator.validatePath(toPath);
501
+ if (!toValidation.isValid || !toValidation.normalizedPath) {
502
+ throw FileSystemError.invalidPath(toPath, toValidation.error || "Unknown error");
503
+ }
504
+ const normalizedFrom = fromValidation.normalizedPath;
505
+ const normalizedTo = toValidation.normalizedPath;
506
+ if (normalizedFrom === normalizedTo) {
507
+ return { from: normalizedFrom, to: normalizedTo };
508
+ }
509
+ try {
510
+ await fs.access(normalizedTo);
511
+ throw FileSystemError.renameFailed(
512
+ normalizedFrom,
513
+ `Target already exists: ${normalizedTo}`
514
+ );
515
+ } catch (error) {
516
+ const code = error.code;
517
+ if (!code) {
518
+ throw error;
519
+ }
520
+ if (code === "ENOENT") {
521
+ } else if (code === "EACCES" || code === "EPERM") {
522
+ throw FileSystemError.permissionDenied(normalizedTo, "rename");
523
+ } else {
524
+ throw FileSystemError.renameFailed(
525
+ normalizedFrom,
526
+ error instanceof Error ? error.message : String(error)
527
+ );
528
+ }
529
+ }
530
+ try {
531
+ await fs.rename(normalizedFrom, normalizedTo);
532
+ return { from: normalizedFrom, to: normalizedTo };
533
+ } catch (error) {
534
+ const code = error.code;
535
+ if (code === "ENOENT") {
536
+ throw FileSystemError.fileNotFound(normalizedFrom);
537
+ }
538
+ if (code === "EACCES" || code === "EPERM") {
539
+ throw FileSystemError.permissionDenied(normalizedFrom, "rename");
540
+ }
541
+ throw FileSystemError.renameFailed(
542
+ normalizedFrom,
543
+ error instanceof Error ? error.message : String(error)
544
+ );
545
+ }
546
+ }
249
547
  /**
250
548
  * Search for content in files (grep-like functionality)
251
549
  */
@@ -43,13 +43,24 @@ const GlobFilesInputSchema = import_zod.z.object({
43
43
  function createGlobFilesTool(getFileSystemService) {
44
44
  return (0, import_core.defineTool)({
45
45
  id: "glob_files",
46
- displayName: "Find Files",
47
46
  aliases: ["glob"],
48
47
  description: "Find files matching a glob pattern. Supports standard glob syntax like **/*.js for recursive matches, *.ts for files in current directory, and src/**/*.tsx for nested paths. Returns array of file paths with metadata (size, modified date). Results are limited to allowed paths only.",
49
48
  inputSchema: GlobFilesInputSchema,
49
+ presentation: {
50
+ describeHeader: (input) => {
51
+ const bits = [`pattern=${input.pattern}`];
52
+ if (input.path) bits.push(`path=${input.path}`);
53
+ if (typeof input.max_results === "number") bits.push(`max=${input.max_results}`);
54
+ return (0, import_core.createLocalToolCallHeader)({
55
+ title: "Find Files",
56
+ argsText: (0, import_core.truncateForHeader)(bits.join(", "), 140)
57
+ });
58
+ }
59
+ },
50
60
  ...(0, import_directory_approval.createDirectoryAccessApprovalHandlers)({
51
61
  toolName: "glob_files",
52
62
  operation: "read",
63
+ inputSchema: GlobFilesInputSchema,
53
64
  getFileSystemService,
54
65
  resolvePaths: (input, fileSystemService) => {
55
66
  const baseDir = fileSystemService.getWorkingDirectory();
@@ -1 +1 @@
1
- {"version":3,"file":"glob-files-tool.d.ts","sourceRoot":"","sources":["../src/glob-files-tool.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAGpE,QAAA,MAAM,oBAAoB;;;;;;;;;;;;EAiBb,CAAC;AAEd;;GAEG;AACH,wBAAgB,mBAAmB,CAC/B,oBAAoB,EAAE,uBAAuB,GAC9C,IAAI,CAAC,OAAO,oBAAoB,CAAC,CA8DnC"}
1
+ {"version":3,"file":"glob-files-tool.d.ts","sourceRoot":"","sources":["../src/glob-files-tool.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAGpE,QAAA,MAAM,oBAAoB;;;;;;;;;;;;EAiBb,CAAC;AAEd;;GAEG;AACH,wBAAgB,mBAAmB,CAC/B,oBAAoB,EAAE,uBAAuB,GAC9C,IAAI,CAAC,OAAO,oBAAoB,CAAC,CA2EnC"}
@@ -1,6 +1,6 @@
1
1
  import * as path from "node:path";
2
2
  import { z } from "zod";
3
- import { defineTool } from "@dexto/core";
3
+ import { createLocalToolCallHeader, defineTool, truncateForHeader } from "@dexto/core";
4
4
  import { createDirectoryAccessApprovalHandlers } from "./directory-approval.js";
5
5
  const GlobFilesInputSchema = z.object({
6
6
  pattern: z.string().describe('Glob pattern to match files (e.g., "**/*.ts", "src/**/*.js")'),
@@ -10,13 +10,24 @@ const GlobFilesInputSchema = z.object({
10
10
  function createGlobFilesTool(getFileSystemService) {
11
11
  return defineTool({
12
12
  id: "glob_files",
13
- displayName: "Find Files",
14
13
  aliases: ["glob"],
15
14
  description: "Find files matching a glob pattern. Supports standard glob syntax like **/*.js for recursive matches, *.ts for files in current directory, and src/**/*.tsx for nested paths. Returns array of file paths with metadata (size, modified date). Results are limited to allowed paths only.",
16
15
  inputSchema: GlobFilesInputSchema,
16
+ presentation: {
17
+ describeHeader: (input) => {
18
+ const bits = [`pattern=${input.pattern}`];
19
+ if (input.path) bits.push(`path=${input.path}`);
20
+ if (typeof input.max_results === "number") bits.push(`max=${input.max_results}`);
21
+ return createLocalToolCallHeader({
22
+ title: "Find Files",
23
+ argsText: truncateForHeader(bits.join(", "), 140)
24
+ });
25
+ }
26
+ },
17
27
  ...createDirectoryAccessApprovalHandlers({
18
28
  toolName: "glob_files",
19
29
  operation: "read",
30
+ inputSchema: GlobFilesInputSchema,
20
31
  getFileSystemService,
21
32
  resolvePaths: (input, fileSystemService) => {
22
33
  const baseDir = fileSystemService.getWorkingDirectory();
@@ -48,13 +48,25 @@ const GrepContentInputSchema = import_zod.z.object({
48
48
  function createGrepContentTool(getFileSystemService) {
49
49
  return (0, import_core.defineTool)({
50
50
  id: "grep_content",
51
- displayName: "Search Files",
52
51
  aliases: ["grep"],
53
52
  description: 'Search for text patterns in files using regular expressions. Returns matching lines with file path, line number, and optional context lines. Use glob parameter to filter specific file types (e.g., "*.ts"). Supports case-insensitive search. Great for finding code patterns, function definitions, or specific text across multiple files.',
54
53
  inputSchema: GrepContentInputSchema,
54
+ presentation: {
55
+ describeHeader: (input) => {
56
+ const bits = [`pattern=${input.pattern}`];
57
+ if (input.glob) bits.push(`glob=${input.glob}`);
58
+ if (input.path) bits.push(`path=${input.path}`);
59
+ if (typeof input.max_results === "number") bits.push(`max=${input.max_results}`);
60
+ return (0, import_core.createLocalToolCallHeader)({
61
+ title: "Search Files",
62
+ argsText: (0, import_core.truncateForHeader)(bits.join(", "), 140)
63
+ });
64
+ }
65
+ },
55
66
  ...(0, import_directory_approval.createDirectoryAccessApprovalHandlers)({
56
67
  toolName: "grep_content",
57
68
  operation: "read",
69
+ inputSchema: GrepContentInputSchema,
58
70
  getFileSystemService,
59
71
  resolvePaths: (input, fileSystemService) => {
60
72
  const baseDir = fileSystemService.getWorkingDirectory();
@@ -1 +1 @@
1
- {"version":3,"file":"grep-content-tool.d.ts","sourceRoot":"","sources":["../src/grep-content-tool.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAGpE,QAAA,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;EAiCf,CAAC;AAEd;;GAEG;AACH,wBAAgB,qBAAqB,CACjC,oBAAoB,EAAE,uBAAuB,GAC9C,IAAI,CAAC,OAAO,sBAAsB,CAAC,CA+ErC"}
1
+ {"version":3,"file":"grep-content-tool.d.ts","sourceRoot":"","sources":["../src/grep-content-tool.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,IAAI,EAAwB,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAGpE,QAAA,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;EAiCf,CAAC;AAEd;;GAEG;AACH,wBAAgB,qBAAqB,CACjC,oBAAoB,EAAE,uBAAuB,GAC9C,IAAI,CAAC,OAAO,sBAAsB,CAAC,CA6FrC"}
@@ -1,6 +1,6 @@
1
1
  import * as path from "node:path";
2
2
  import { z } from "zod";
3
- import { defineTool } from "@dexto/core";
3
+ import { createLocalToolCallHeader, defineTool, truncateForHeader } from "@dexto/core";
4
4
  import { createDirectoryAccessApprovalHandlers } from "./directory-approval.js";
5
5
  const GrepContentInputSchema = z.object({
6
6
  pattern: z.string().describe("Regular expression pattern to search for"),
@@ -15,13 +15,25 @@ const GrepContentInputSchema = z.object({
15
15
  function createGrepContentTool(getFileSystemService) {
16
16
  return defineTool({
17
17
  id: "grep_content",
18
- displayName: "Search Files",
19
18
  aliases: ["grep"],
20
19
  description: 'Search for text patterns in files using regular expressions. Returns matching lines with file path, line number, and optional context lines. Use glob parameter to filter specific file types (e.g., "*.ts"). Supports case-insensitive search. Great for finding code patterns, function definitions, or specific text across multiple files.',
21
20
  inputSchema: GrepContentInputSchema,
21
+ presentation: {
22
+ describeHeader: (input) => {
23
+ const bits = [`pattern=${input.pattern}`];
24
+ if (input.glob) bits.push(`glob=${input.glob}`);
25
+ if (input.path) bits.push(`path=${input.path}`);
26
+ if (typeof input.max_results === "number") bits.push(`max=${input.max_results}`);
27
+ return createLocalToolCallHeader({
28
+ title: "Search Files",
29
+ argsText: truncateForHeader(bits.join(", "), 140)
30
+ });
31
+ }
32
+ },
22
33
  ...createDirectoryAccessApprovalHandlers({
23
34
  toolName: "grep_content",
24
35
  operation: "read",
36
+ inputSchema: GrepContentInputSchema,
25
37
  getFileSystemService,
26
38
  resolvePaths: (input, fileSystemService) => {
27
39
  const baseDir = fileSystemService.getWorkingDirectory();
package/dist/index.cjs CHANGED
@@ -21,6 +21,7 @@ __export(index_exports, {
21
21
  FileSystemError: () => import_errors.FileSystemError,
22
22
  FileSystemErrorCode: () => import_error_codes.FileSystemErrorCode,
23
23
  FileSystemService: () => import_filesystem_service.FileSystemService,
24
+ FileSystemToolsConfigSchema: () => import_tool_factory_config.FileSystemToolsConfigSchema,
24
25
  PathValidator: () => import_path_validator.PathValidator,
25
26
  createEditFileTool: () => import_edit_file_tool.createEditFileTool,
26
27
  createGlobFilesTool: () => import_glob_files_tool.createGlobFilesTool,
@@ -31,6 +32,7 @@ __export(index_exports, {
31
32
  });
32
33
  module.exports = __toCommonJS(index_exports);
33
34
  var import_tool_factory = require("./tool-factory.js");
35
+ var import_tool_factory_config = require("./tool-factory-config.js");
34
36
  var import_filesystem_service = require("./filesystem-service.js");
35
37
  var import_path_validator = require("./path-validator.js");
36
38
  var import_errors = require("./errors.js");
@@ -45,6 +47,7 @@ var import_grep_content_tool = require("./grep-content-tool.js");
45
47
  FileSystemError,
46
48
  FileSystemErrorCode,
47
49
  FileSystemService,
50
+ FileSystemToolsConfigSchema,
48
51
  PathValidator,
49
52
  createEditFileTool,
50
53
  createGlobFilesTool,