@push.rocks/smartagent 1.8.0 → 3.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 (87) hide show
  1. package/dist_ts/00_commitinfo_data.js +3 -3
  2. package/dist_ts/index.d.ts +8 -14
  3. package/dist_ts/index.js +8 -24
  4. package/dist_ts/plugins.d.ts +8 -9
  5. package/dist_ts/plugins.js +10 -12
  6. package/dist_ts/smartagent.classes.agent.d.ts +2 -0
  7. package/dist_ts/smartagent.classes.agent.js +173 -0
  8. package/dist_ts/smartagent.classes.toolregistry.d.ts +7 -70
  9. package/dist_ts/smartagent.classes.toolregistry.js +11 -155
  10. package/dist_ts/smartagent.interfaces.d.ts +47 -283
  11. package/dist_ts/smartagent.interfaces.js +6 -7
  12. package/dist_ts/smartagent.utils.truncation.d.ts +10 -0
  13. package/dist_ts/smartagent.utils.truncation.js +26 -0
  14. package/dist_ts_compaction/index.d.ts +1 -0
  15. package/dist_ts_compaction/index.js +2 -0
  16. package/dist_ts_compaction/plugins.d.ts +4 -0
  17. package/dist_ts_compaction/plugins.js +3 -0
  18. package/dist_ts_compaction/smartagent.compaction.d.ts +10 -0
  19. package/dist_ts_compaction/smartagent.compaction.js +46 -0
  20. package/dist_ts_tools/index.d.ts +8 -0
  21. package/dist_ts_tools/index.js +6 -0
  22. package/dist_ts_tools/plugins.d.ts +15 -0
  23. package/dist_ts_tools/plugins.js +19 -0
  24. package/dist_ts_tools/tool.filesystem.d.ts +6 -0
  25. package/dist_ts_tools/tool.filesystem.js +102 -0
  26. package/dist_ts_tools/tool.http.d.ts +2 -0
  27. package/dist_ts_tools/tool.http.js +65 -0
  28. package/dist_ts_tools/tool.json.d.ts +2 -0
  29. package/dist_ts_tools/tool.json.js +47 -0
  30. package/dist_ts_tools/tool.shell.d.ts +8 -0
  31. package/dist_ts_tools/tool.shell.js +40 -0
  32. package/npmextra.json +1 -1
  33. package/package.json +30 -18
  34. package/readme.hints.md +38 -84
  35. package/readme.md +254 -682
  36. package/ts/00_commitinfo_data.ts +2 -2
  37. package/ts/index.ts +10 -37
  38. package/ts/plugins.ts +22 -21
  39. package/ts/smartagent.classes.agent.ts +198 -0
  40. package/ts/smartagent.classes.toolregistry.ts +11 -179
  41. package/ts/smartagent.interfaces.ts +51 -363
  42. package/ts/smartagent.utils.truncation.ts +39 -0
  43. package/ts_compaction/index.ts +1 -0
  44. package/ts_compaction/plugins.ts +6 -0
  45. package/ts_compaction/smartagent.compaction.ts +51 -0
  46. package/ts_tools/index.ts +8 -0
  47. package/ts_tools/plugins.ts +30 -0
  48. package/ts_tools/tool.filesystem.ts +131 -0
  49. package/ts_tools/tool.http.ts +78 -0
  50. package/ts_tools/tool.json.ts +53 -0
  51. package/ts_tools/tool.shell.ts +62 -0
  52. package/dist_ts/smartagent.classes.driveragent.d.ts +0 -134
  53. package/dist_ts/smartagent.classes.driveragent.js +0 -671
  54. package/dist_ts/smartagent.classes.dualagent.d.ts +0 -93
  55. package/dist_ts/smartagent.classes.dualagent.js +0 -614
  56. package/dist_ts/smartagent.classes.guardianagent.d.ts +0 -46
  57. package/dist_ts/smartagent.classes.guardianagent.js +0 -201
  58. package/dist_ts/smartagent.tools.base.d.ts +0 -52
  59. package/dist_ts/smartagent.tools.base.js +0 -42
  60. package/dist_ts/smartagent.tools.browser.d.ts +0 -17
  61. package/dist_ts/smartagent.tools.browser.js +0 -229
  62. package/dist_ts/smartagent.tools.deno.d.ts +0 -21
  63. package/dist_ts/smartagent.tools.deno.js +0 -191
  64. package/dist_ts/smartagent.tools.expert.d.ts +0 -27
  65. package/dist_ts/smartagent.tools.expert.js +0 -126
  66. package/dist_ts/smartagent.tools.filesystem.d.ts +0 -40
  67. package/dist_ts/smartagent.tools.filesystem.js +0 -801
  68. package/dist_ts/smartagent.tools.http.d.ts +0 -16
  69. package/dist_ts/smartagent.tools.http.js +0 -264
  70. package/dist_ts/smartagent.tools.json.d.ts +0 -24
  71. package/dist_ts/smartagent.tools.json.js +0 -202
  72. package/dist_ts/smartagent.tools.search.d.ts +0 -29
  73. package/dist_ts/smartagent.tools.search.js +0 -215
  74. package/dist_ts/smartagent.tools.shell.d.ts +0 -17
  75. package/dist_ts/smartagent.tools.shell.js +0 -202
  76. package/ts/smartagent.classes.driveragent.ts +0 -775
  77. package/ts/smartagent.classes.dualagent.ts +0 -692
  78. package/ts/smartagent.classes.guardianagent.ts +0 -241
  79. package/ts/smartagent.tools.base.ts +0 -83
  80. package/ts/smartagent.tools.browser.ts +0 -253
  81. package/ts/smartagent.tools.deno.ts +0 -230
  82. package/ts/smartagent.tools.expert.ts +0 -144
  83. package/ts/smartagent.tools.filesystem.ts +0 -885
  84. package/ts/smartagent.tools.http.ts +0 -283
  85. package/ts/smartagent.tools.json.ts +0 -224
  86. package/ts/smartagent.tools.search.ts +0 -237
  87. package/ts/smartagent.tools.shell.ts +0 -230
@@ -1,885 +0,0 @@
1
- import * as plugins from './plugins.js';
2
- import * as interfaces from './smartagent.interfaces.js';
3
- import { BaseToolWrapper } from './smartagent.tools.base.js';
4
-
5
- /**
6
- * Options for FilesystemTool
7
- */
8
- export interface IFilesystemToolOptions {
9
- /** Base path to scope all operations to. If set, all paths must be within this directory. */
10
- basePath?: string;
11
- /** Glob patterns to exclude from listings (e.g., ['.nogit/**', 'node_modules/**']) */
12
- excludePatterns?: string[];
13
- }
14
-
15
- /**
16
- * Filesystem tool for file and directory operations
17
- * Wraps @push.rocks/smartfs
18
- */
19
- export class FilesystemTool extends BaseToolWrapper {
20
- public name = 'filesystem';
21
- public description = 'Read, write, list, and delete files and directories';
22
-
23
- /** Base path to scope all operations to */
24
- private basePath?: string;
25
- /** Glob patterns to exclude from listings */
26
- private excludePatterns: string[];
27
-
28
- constructor(options?: IFilesystemToolOptions) {
29
- super();
30
- if (options?.basePath) {
31
- this.basePath = plugins.path.resolve(options.basePath);
32
- }
33
- this.excludePatterns = options?.excludePatterns || [];
34
- }
35
-
36
- /**
37
- * Check if a relative path should be excluded based on exclude patterns
38
- */
39
- private isExcluded(relativePath: string): boolean {
40
- if (this.excludePatterns.length === 0) return false;
41
- return this.excludePatterns.some(pattern =>
42
- plugins.minimatch(relativePath, pattern, { dot: true })
43
- );
44
- }
45
-
46
- /**
47
- * Validate that a path is within the allowed base path
48
- * @throws Error if path is outside allowed directory
49
- */
50
- private validatePath(pathArg: string): string {
51
- const resolved = plugins.path.resolve(pathArg);
52
- if (this.basePath) {
53
- // Ensure the resolved path starts with the base path
54
- if (!resolved.startsWith(this.basePath + plugins.path.sep) && resolved !== this.basePath) {
55
- throw new Error(`Access denied: path "${pathArg}" is outside allowed directory "${this.basePath}"`);
56
- }
57
- }
58
- return resolved;
59
- }
60
-
61
- public actions: interfaces.IToolAction[] = [
62
- {
63
- name: 'read',
64
- description: 'Read file contents (full or specific line range)',
65
- parameters: {
66
- type: 'object',
67
- properties: {
68
- path: { type: 'string', description: 'Path to the file' },
69
- encoding: {
70
- type: 'string',
71
- enum: ['utf8', 'binary', 'base64'],
72
- default: 'utf8',
73
- description: 'File encoding',
74
- },
75
- startLine: {
76
- type: 'number',
77
- description: 'First line to read (1-indexed, inclusive). If omitted, reads from beginning.',
78
- },
79
- endLine: {
80
- type: 'number',
81
- description: 'Last line to read (1-indexed, inclusive). If omitted, reads to end.',
82
- },
83
- },
84
- required: ['path'],
85
- },
86
- },
87
- {
88
- name: 'write',
89
- description: 'Write content to a file (creates or overwrites)',
90
- parameters: {
91
- type: 'object',
92
- properties: {
93
- path: { type: 'string', description: 'Absolute path to the file' },
94
- content: { type: 'string', description: 'Content to write' },
95
- encoding: {
96
- type: 'string',
97
- enum: ['utf8', 'binary', 'base64'],
98
- default: 'utf8',
99
- description: 'File encoding',
100
- },
101
- },
102
- required: ['path', 'content'],
103
- },
104
- },
105
- {
106
- name: 'append',
107
- description: 'Append content to a file',
108
- parameters: {
109
- type: 'object',
110
- properties: {
111
- path: { type: 'string', description: 'Absolute path to the file' },
112
- content: { type: 'string', description: 'Content to append' },
113
- },
114
- required: ['path', 'content'],
115
- },
116
- },
117
- {
118
- name: 'list',
119
- description: 'List files and directories in a path',
120
- parameters: {
121
- type: 'object',
122
- properties: {
123
- path: { type: 'string', description: 'Directory path to list' },
124
- recursive: { type: 'boolean', default: false, description: 'List recursively' },
125
- filter: { type: 'string', description: 'Glob pattern to filter results (e.g., "*.ts")' },
126
- },
127
- required: ['path'],
128
- },
129
- },
130
- {
131
- name: 'delete',
132
- description: 'Delete a file or directory',
133
- parameters: {
134
- type: 'object',
135
- properties: {
136
- path: { type: 'string', description: 'Path to delete' },
137
- recursive: {
138
- type: 'boolean',
139
- default: false,
140
- description: 'For directories, delete recursively',
141
- },
142
- },
143
- required: ['path'],
144
- },
145
- },
146
- {
147
- name: 'exists',
148
- description: 'Check if a file or directory exists',
149
- parameters: {
150
- type: 'object',
151
- properties: {
152
- path: { type: 'string', description: 'Path to check' },
153
- },
154
- required: ['path'],
155
- },
156
- },
157
- {
158
- name: 'stat',
159
- description: 'Get file or directory statistics (size, dates, etc.)',
160
- parameters: {
161
- type: 'object',
162
- properties: {
163
- path: { type: 'string', description: 'Path to get stats for' },
164
- },
165
- required: ['path'],
166
- },
167
- },
168
- {
169
- name: 'copy',
170
- description: 'Copy a file to a new location',
171
- parameters: {
172
- type: 'object',
173
- properties: {
174
- source: { type: 'string', description: 'Source file path' },
175
- destination: { type: 'string', description: 'Destination file path' },
176
- },
177
- required: ['source', 'destination'],
178
- },
179
- },
180
- {
181
- name: 'move',
182
- description: 'Move a file to a new location',
183
- parameters: {
184
- type: 'object',
185
- properties: {
186
- source: { type: 'string', description: 'Source file path' },
187
- destination: { type: 'string', description: 'Destination file path' },
188
- },
189
- required: ['source', 'destination'],
190
- },
191
- },
192
- {
193
- name: 'mkdir',
194
- description: 'Create a directory',
195
- parameters: {
196
- type: 'object',
197
- properties: {
198
- path: { type: 'string', description: 'Directory path to create' },
199
- recursive: {
200
- type: 'boolean',
201
- default: true,
202
- description: 'Create parent directories if needed',
203
- },
204
- },
205
- required: ['path'],
206
- },
207
- },
208
- {
209
- name: 'tree',
210
- description: 'Show directory structure as a tree (no file contents)',
211
- parameters: {
212
- type: 'object',
213
- properties: {
214
- path: { type: 'string', description: 'Root directory path' },
215
- maxDepth: {
216
- type: 'number',
217
- default: 3,
218
- description: 'Maximum depth to traverse (default: 3)',
219
- },
220
- filter: {
221
- type: 'string',
222
- description: 'Glob pattern to filter files (e.g., "*.ts")',
223
- },
224
- showSizes: {
225
- type: 'boolean',
226
- default: false,
227
- description: 'Include file sizes in output',
228
- },
229
- format: {
230
- type: 'string',
231
- enum: ['string', 'json'],
232
- default: 'string',
233
- description: 'Output format: "string" for human-readable tree, "json" for structured array',
234
- },
235
- },
236
- required: ['path'],
237
- },
238
- },
239
- {
240
- name: 'glob',
241
- description: 'Find files matching a glob pattern',
242
- parameters: {
243
- type: 'object',
244
- properties: {
245
- pattern: {
246
- type: 'string',
247
- description: 'Glob pattern (e.g., "**/*.ts", "src/**/*.js")',
248
- },
249
- path: {
250
- type: 'string',
251
- description: 'Base path to search from (defaults to current directory)',
252
- },
253
- },
254
- required: ['pattern'],
255
- },
256
- },
257
- ];
258
-
259
- private smartfs!: plugins.smartfs.SmartFs;
260
-
261
- public async initialize(): Promise<void> {
262
- this.smartfs = new plugins.smartfs.SmartFs(new plugins.smartfs.SmartFsProviderNode());
263
- this.isInitialized = true;
264
- }
265
-
266
- public async cleanup(): Promise<void> {
267
- this.isInitialized = false;
268
- }
269
-
270
- public async execute(
271
- action: string,
272
- params: Record<string, unknown>
273
- ): Promise<interfaces.IToolExecutionResult> {
274
- this.validateAction(action);
275
- this.ensureInitialized();
276
-
277
- try {
278
- switch (action) {
279
- case 'read': {
280
- const validatedPath = this.validatePath(params.path as string);
281
- const encoding = (params.encoding as string) || 'utf8';
282
- const startLine = params.startLine as number | undefined;
283
- const endLine = params.endLine as number | undefined;
284
-
285
- const fullContent = await this.smartfs
286
- .file(validatedPath)
287
- .encoding(encoding as 'utf8' | 'binary' | 'base64')
288
- .read();
289
-
290
- const contentStr = fullContent.toString();
291
- const lines = contentStr.split('\n');
292
- const totalLines = lines.length;
293
-
294
- // Apply line range if specified
295
- let resultContent: string;
296
- let resultStartLine = 1;
297
- let resultEndLine = totalLines;
298
-
299
- if (startLine !== undefined || endLine !== undefined) {
300
- const start = Math.max(1, startLine ?? 1);
301
- const end = Math.min(totalLines, endLine ?? totalLines);
302
- resultStartLine = start;
303
- resultEndLine = end;
304
-
305
- // Convert to 0-indexed for array slicing
306
- const selectedLines = lines.slice(start - 1, end);
307
-
308
- // Add line numbers to output for context
309
- resultContent = selectedLines
310
- .map((line, idx) => `${String(start + idx).padStart(5)}│ ${line}`)
311
- .join('\n');
312
- } else {
313
- // No range specified - return full content but warn if large
314
- const MAX_LINES_WITHOUT_RANGE = 500;
315
- if (totalLines > MAX_LINES_WITHOUT_RANGE) {
316
- // Return first portion with warning
317
- const selectedLines = lines.slice(0, MAX_LINES_WITHOUT_RANGE);
318
- resultContent = selectedLines
319
- .map((line, idx) => `${String(idx + 1).padStart(5)}│ ${line}`)
320
- .join('\n');
321
- resultContent += `\n\n[... ${totalLines - MAX_LINES_WITHOUT_RANGE} more lines. Use startLine/endLine to read specific ranges.]`;
322
- resultEndLine = MAX_LINES_WITHOUT_RANGE;
323
- } else {
324
- resultContent = contentStr;
325
- }
326
- }
327
-
328
- return {
329
- success: true,
330
- result: {
331
- path: params.path,
332
- content: resultContent,
333
- encoding,
334
- totalLines,
335
- startLine: resultStartLine,
336
- endLine: resultEndLine,
337
- },
338
- };
339
- }
340
-
341
- case 'write': {
342
- const validatedPath = this.validatePath(params.path as string);
343
- const encoding = (params.encoding as string) || 'utf8';
344
- await this.smartfs
345
- .file(validatedPath)
346
- .encoding(encoding as 'utf8' | 'binary' | 'base64')
347
- .write(params.content as string);
348
- return {
349
- success: true,
350
- result: {
351
- path: params.path,
352
- written: true,
353
- bytesWritten: (params.content as string).length,
354
- },
355
- };
356
- }
357
-
358
- case 'append': {
359
- const validatedPath = this.validatePath(params.path as string);
360
- await this.smartfs.file(validatedPath).append(params.content as string);
361
- return {
362
- success: true,
363
- result: {
364
- path: params.path,
365
- appended: true,
366
- },
367
- };
368
- }
369
-
370
- case 'list': {
371
- const validatedPath = this.validatePath(params.path as string);
372
- let dir = this.smartfs.directory(validatedPath);
373
- if (params.recursive) {
374
- dir = dir.recursive();
375
- }
376
- if (params.filter) {
377
- dir = dir.filter(params.filter as string);
378
- }
379
- let entries = await dir.list();
380
-
381
- // Filter out excluded paths
382
- if (this.excludePatterns.length > 0) {
383
- entries = entries.filter(entry => {
384
- const relativePath = plugins.path.relative(validatedPath, entry.path);
385
- return !this.isExcluded(relativePath) && !this.isExcluded(entry.name);
386
- });
387
- }
388
-
389
- return {
390
- success: true,
391
- result: {
392
- path: params.path,
393
- entries,
394
- count: entries.length,
395
- },
396
- };
397
- }
398
-
399
- case 'delete': {
400
- const validatedPath = this.validatePath(params.path as string);
401
- // Check if it's a directory or file
402
- const exists = await this.smartfs.file(validatedPath).exists();
403
- if (exists) {
404
- // Try to get stats to check if it's a directory
405
- try {
406
- const stats = await this.smartfs.file(validatedPath).stat();
407
- if (stats.isDirectory && params.recursive) {
408
- await this.smartfs.directory(validatedPath).recursive().delete();
409
- } else {
410
- await this.smartfs.file(validatedPath).delete();
411
- }
412
- } catch {
413
- await this.smartfs.file(validatedPath).delete();
414
- }
415
- }
416
- return {
417
- success: true,
418
- result: {
419
- path: params.path,
420
- deleted: true,
421
- },
422
- };
423
- }
424
-
425
- case 'exists': {
426
- const validatedPath = this.validatePath(params.path as string);
427
- const exists = await this.smartfs.file(validatedPath).exists();
428
- return {
429
- success: true,
430
- result: {
431
- path: params.path,
432
- exists,
433
- },
434
- };
435
- }
436
-
437
- case 'stat': {
438
- const validatedPath = this.validatePath(params.path as string);
439
- const stats = await this.smartfs.file(validatedPath).stat();
440
- return {
441
- success: true,
442
- result: {
443
- path: params.path,
444
- stats,
445
- },
446
- };
447
- }
448
-
449
- case 'copy': {
450
- const validatedSource = this.validatePath(params.source as string);
451
- const validatedDest = this.validatePath(params.destination as string);
452
- await this.smartfs.file(validatedSource).copy(validatedDest);
453
- return {
454
- success: true,
455
- result: {
456
- source: params.source,
457
- destination: params.destination,
458
- copied: true,
459
- },
460
- };
461
- }
462
-
463
- case 'move': {
464
- const validatedSource = this.validatePath(params.source as string);
465
- const validatedDest = this.validatePath(params.destination as string);
466
- await this.smartfs.file(validatedSource).move(validatedDest);
467
- return {
468
- success: true,
469
- result: {
470
- source: params.source,
471
- destination: params.destination,
472
- moved: true,
473
- },
474
- };
475
- }
476
-
477
- case 'mkdir': {
478
- const validatedPath = this.validatePath(params.path as string);
479
- let dir = this.smartfs.directory(validatedPath);
480
- if (params.recursive !== false) {
481
- dir = dir.recursive();
482
- }
483
- await dir.create();
484
- return {
485
- success: true,
486
- result: {
487
- path: params.path,
488
- created: true,
489
- },
490
- };
491
- }
492
-
493
- case 'tree': {
494
- const validatedPath = this.validatePath(params.path as string);
495
- const maxDepth = (params.maxDepth as number) ?? 3;
496
- const filter = params.filter as string | undefined;
497
- const showSizes = (params.showSizes as boolean) ?? false;
498
- const format = (params.format as 'string' | 'json') ?? 'string';
499
-
500
- // Collect all entries recursively up to maxDepth
501
- interface ITreeEntry {
502
- path: string;
503
- relativePath: string;
504
- isDir: boolean;
505
- depth: number;
506
- size?: number;
507
- }
508
-
509
- const entries: ITreeEntry[] = [];
510
-
511
- const collectEntries = async (dirPath: string, depth: number, relativePath: string) => {
512
- if (depth > maxDepth) return;
513
-
514
- let dir = this.smartfs.directory(dirPath);
515
- if (filter) {
516
- dir = dir.filter(filter);
517
- }
518
- const items = await dir.list();
519
-
520
- for (const item of items) {
521
- // item is IDirectoryEntry with name, path, isFile, isDirectory properties
522
- const itemPath = item.path;
523
- const itemRelPath = relativePath ? `${relativePath}/${item.name}` : item.name;
524
- const isDir = item.isDirectory;
525
-
526
- // Skip excluded paths
527
- if (this.isExcluded(itemRelPath) || this.isExcluded(item.name)) {
528
- continue;
529
- }
530
-
531
- const entry: ITreeEntry = {
532
- path: itemPath,
533
- relativePath: itemRelPath,
534
- isDir,
535
- depth,
536
- };
537
-
538
- if (showSizes && !isDir && item.stats) {
539
- entry.size = item.stats.size;
540
- }
541
-
542
- entries.push(entry);
543
-
544
- // Recurse into directories
545
- if (isDir && depth < maxDepth) {
546
- await collectEntries(itemPath, depth + 1, itemRelPath);
547
- }
548
- }
549
- };
550
-
551
- await collectEntries(validatedPath, 0, '');
552
-
553
- // Sort entries by path for consistent output
554
- entries.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
555
-
556
- if (format === 'json') {
557
- return {
558
- success: true,
559
- result: {
560
- path: params.path,
561
- entries: entries.map((e) => ({
562
- path: e.relativePath,
563
- isDir: e.isDir,
564
- depth: e.depth,
565
- ...(e.size !== undefined ? { size: e.size } : {}),
566
- })),
567
- count: entries.length,
568
- },
569
- };
570
- }
571
-
572
- // Format as string tree
573
- const formatSize = (bytes: number): string => {
574
- if (bytes < 1024) return `${bytes}B`;
575
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
576
- return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
577
- };
578
-
579
- // Build tree string with proper indentation
580
- let treeStr = `${params.path}/\n`;
581
- const pathParts = new Map<string, number>(); // Track which paths are last in their parent
582
-
583
- // Group by parent to determine last child
584
- const parentChildCount = new Map<string, number>();
585
- const parentCurrentChild = new Map<string, number>();
586
-
587
- for (const entry of entries) {
588
- const parentPath = entry.relativePath.includes('/')
589
- ? entry.relativePath.substring(0, entry.relativePath.lastIndexOf('/'))
590
- : '';
591
- parentChildCount.set(parentPath, (parentChildCount.get(parentPath) || 0) + 1);
592
- }
593
-
594
- for (const entry of entries) {
595
- const parentPath = entry.relativePath.includes('/')
596
- ? entry.relativePath.substring(0, entry.relativePath.lastIndexOf('/'))
597
- : '';
598
- parentCurrentChild.set(parentPath, (parentCurrentChild.get(parentPath) || 0) + 1);
599
- const isLast = parentCurrentChild.get(parentPath) === parentChildCount.get(parentPath);
600
-
601
- // Build prefix based on depth
602
- let prefix = '';
603
- const parts = entry.relativePath.split('/');
604
- for (let i = 0; i < parts.length - 1; i++) {
605
- prefix += '│ ';
606
- }
607
- prefix += isLast ? '└── ' : '├── ';
608
-
609
- const name = parts[parts.length - 1];
610
- const suffix = entry.isDir ? '/' : '';
611
- const sizeStr = showSizes && entry.size !== undefined ? ` (${formatSize(entry.size)})` : '';
612
-
613
- treeStr += `${prefix}${name}${suffix}${sizeStr}\n`;
614
- }
615
-
616
- return {
617
- success: true,
618
- result: {
619
- path: params.path,
620
- tree: treeStr,
621
- count: entries.length,
622
- },
623
- };
624
- }
625
-
626
- case 'glob': {
627
- const pattern = params.pattern as string;
628
- const basePath = params.path ? this.validatePath(params.path as string) : (this.basePath || process.cwd());
629
-
630
- // Use smartfs to list with filter
631
- const dir = this.smartfs.directory(basePath).recursive().filter(pattern);
632
- const matches = await dir.list();
633
-
634
- // Return file paths relative to base path for readability
635
- // Filter out excluded paths
636
- const files = matches
637
- .map((entry) => ({
638
- path: entry.path,
639
- relativePath: plugins.path.relative(basePath, entry.path),
640
- isDirectory: entry.isDirectory,
641
- }))
642
- .filter((file) => !this.isExcluded(file.relativePath));
643
-
644
- return {
645
- success: true,
646
- result: {
647
- pattern,
648
- basePath,
649
- files,
650
- count: files.length,
651
- },
652
- };
653
- }
654
-
655
- default:
656
- return {
657
- success: false,
658
- error: `Unknown action: ${action}`,
659
- };
660
- }
661
- } catch (error) {
662
- return {
663
- success: false,
664
- error: error instanceof Error ? error.message : String(error),
665
- };
666
- }
667
- }
668
-
669
- public getToolExplanation(): string {
670
- return `## Tool: filesystem
671
- Read, write, list, and delete files and directories.
672
-
673
- ### Actions:
674
-
675
- **read** - Read file contents (full or specific line range)
676
- Parameters:
677
- - path (required): Path to the file
678
- - encoding (optional): File encoding - "utf8" (default), "binary", or "base64"
679
- - startLine (optional): First line to read (1-indexed, inclusive)
680
- - endLine (optional): Last line to read (1-indexed, inclusive)
681
-
682
- Example:
683
- <tool_call>
684
- <tool>filesystem</tool>
685
- <action>read</action>
686
- <params>{"path": "/path/to/file.txt"}</params>
687
- </tool_call>
688
-
689
- Example with line range:
690
- <tool_call>
691
- <tool>filesystem</tool>
692
- <action>read</action>
693
- <params>{"path": "/path/to/file.txt", "startLine": 10, "endLine": 20}</params>
694
- </tool_call>
695
-
696
- **write** - Write content to a file (creates or overwrites)
697
- Parameters:
698
- - path (required): Absolute path to the file
699
- - content (required): Content to write
700
- - encoding (optional): File encoding - "utf8" (default), "binary", or "base64"
701
-
702
- Example:
703
- <tool_call>
704
- <tool>filesystem</tool>
705
- <action>write</action>
706
- <params>{"path": "/path/to/output.txt", "content": "Hello, World!"}</params>
707
- </tool_call>
708
-
709
- **list** - List files and directories in a path
710
- Parameters:
711
- - path (required): Directory path to list
712
- - recursive (optional): List recursively (default: false)
713
- - filter (optional): Glob pattern to filter results (e.g., "*.ts")
714
-
715
- Example:
716
- <tool_call>
717
- <tool>filesystem</tool>
718
- <action>list</action>
719
- <params>{"path": "/path/to/dir", "recursive": true, "filter": "*.ts"}</params>
720
- </tool_call>
721
-
722
- **exists** - Check if a file or directory exists
723
- Parameters:
724
- - path (required): Path to check
725
-
726
- Example:
727
- <tool_call>
728
- <tool>filesystem</tool>
729
- <action>exists</action>
730
- <params>{"path": "/path/to/check"}</params>
731
- </tool_call>
732
-
733
- **mkdir** - Create a directory
734
- Parameters:
735
- - path (required): Directory path to create
736
- - recursive (optional): Create parent directories if needed (default: true)
737
-
738
- Example:
739
- <tool_call>
740
- <tool>filesystem</tool>
741
- <action>mkdir</action>
742
- <params>{"path": "/path/to/new/dir"}</params>
743
- </tool_call>
744
-
745
- **delete** - Delete a file or directory
746
- Parameters:
747
- - path (required): Path to delete
748
- - recursive (optional): For directories, delete recursively (default: false)
749
-
750
- Example:
751
- <tool_call>
752
- <tool>filesystem</tool>
753
- <action>delete</action>
754
- <params>{"path": "/path/to/delete", "recursive": true}</params>
755
- </tool_call>
756
-
757
- **copy** - Copy a file to a new location
758
- Parameters:
759
- - source (required): Source file path
760
- - destination (required): Destination file path
761
-
762
- Example:
763
- <tool_call>
764
- <tool>filesystem</tool>
765
- <action>copy</action>
766
- <params>{"source": "/path/to/source.txt", "destination": "/path/to/dest.txt"}</params>
767
- </tool_call>
768
-
769
- **move** - Move a file to a new location
770
- Parameters:
771
- - source (required): Source file path
772
- - destination (required): Destination file path
773
-
774
- Example:
775
- <tool_call>
776
- <tool>filesystem</tool>
777
- <action>move</action>
778
- <params>{"source": "/path/to/old.txt", "destination": "/path/to/new.txt"}</params>
779
- </tool_call>
780
-
781
- **stat** - Get file or directory statistics (size, dates, etc.)
782
- Parameters:
783
- - path (required): Path to get stats for
784
-
785
- Example:
786
- <tool_call>
787
- <tool>filesystem</tool>
788
- <action>stat</action>
789
- <params>{"path": "/path/to/file.txt"}</params>
790
- </tool_call>
791
-
792
- **append** - Append content to a file
793
- Parameters:
794
- - path (required): Absolute path to the file
795
- - content (required): Content to append
796
-
797
- Example:
798
- <tool_call>
799
- <tool>filesystem</tool>
800
- <action>append</action>
801
- <params>{"path": "/path/to/log.txt", "content": "New log entry\\n"}</params>
802
- </tool_call>
803
-
804
- **tree** - Show directory structure as a tree
805
- Parameters:
806
- - path (required): Root directory path
807
- - maxDepth (optional): Maximum depth to traverse (default: 3)
808
- - filter (optional): Glob pattern to filter files
809
- - showSizes (optional): Include file sizes in output (default: false)
810
- - format (optional): Output format - "string" (default) or "json"
811
-
812
- Example:
813
- <tool_call>
814
- <tool>filesystem</tool>
815
- <action>tree</action>
816
- <params>{"path": "/path/to/dir", "maxDepth": 2}</params>
817
- </tool_call>
818
-
819
- **glob** - Find files matching a glob pattern
820
- Parameters:
821
- - pattern (required): Glob pattern (e.g., "**/*.ts", "src/**/*.js")
822
- - path (optional): Base path to search from
823
-
824
- Example:
825
- <tool_call>
826
- <tool>filesystem</tool>
827
- <action>glob</action>
828
- <params>{"pattern": "**/*.ts", "path": "/path/to/project"}</params>
829
- </tool_call>
830
- `;
831
- }
832
-
833
- public getCallSummary(action: string, params: Record<string, unknown>): string {
834
- switch (action) {
835
- case 'read': {
836
- const lineRange = params.startLine || params.endLine
837
- ? ` lines ${params.startLine || 1}-${params.endLine || 'end'}`
838
- : '';
839
- return `Read file "${params.path}"${lineRange}`;
840
- }
841
-
842
- case 'write': {
843
- const content = params.content as string;
844
- const preview = content.length > 100 ? content.substring(0, 100) + '...' : content;
845
- return `Write ${content.length} bytes to "${params.path}". Content preview: "${preview}"`;
846
- }
847
-
848
- case 'append': {
849
- const content = params.content as string;
850
- const preview = content.length > 100 ? content.substring(0, 100) + '...' : content;
851
- return `Append ${content.length} bytes to "${params.path}". Content preview: "${preview}"`;
852
- }
853
-
854
- case 'list':
855
- return `List directory "${params.path}"${params.recursive ? ' recursively' : ''}${params.filter ? ` with filter "${params.filter}"` : ''}`;
856
-
857
- case 'delete':
858
- return `Delete "${params.path}"${params.recursive ? ' recursively' : ''}`;
859
-
860
- case 'exists':
861
- return `Check if "${params.path}" exists`;
862
-
863
- case 'stat':
864
- return `Get statistics for "${params.path}"`;
865
-
866
- case 'copy':
867
- return `Copy "${params.source}" to "${params.destination}"`;
868
-
869
- case 'move':
870
- return `Move "${params.source}" to "${params.destination}"`;
871
-
872
- case 'mkdir':
873
- return `Create directory "${params.path}"${params.recursive !== false ? ' (with parents)' : ''}`;
874
-
875
- case 'tree':
876
- return `Show tree of "${params.path}" (depth: ${params.maxDepth ?? 3}, format: ${params.format ?? 'string'})`;
877
-
878
- case 'glob':
879
- return `Find files matching "${params.pattern}"${params.path ? ` in "${params.path}"` : ''}`;
880
-
881
- default:
882
- return `Unknown action: ${action}`;
883
- }
884
- }
885
- }