@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.
- package/dist_ts/00_commitinfo_data.js +3 -3
- package/dist_ts/index.d.ts +8 -14
- package/dist_ts/index.js +8 -24
- package/dist_ts/plugins.d.ts +8 -9
- package/dist_ts/plugins.js +10 -12
- package/dist_ts/smartagent.classes.agent.d.ts +2 -0
- package/dist_ts/smartagent.classes.agent.js +173 -0
- package/dist_ts/smartagent.classes.toolregistry.d.ts +7 -70
- package/dist_ts/smartagent.classes.toolregistry.js +11 -155
- package/dist_ts/smartagent.interfaces.d.ts +47 -283
- package/dist_ts/smartagent.interfaces.js +6 -7
- package/dist_ts/smartagent.utils.truncation.d.ts +10 -0
- package/dist_ts/smartagent.utils.truncation.js +26 -0
- package/dist_ts_compaction/index.d.ts +1 -0
- package/dist_ts_compaction/index.js +2 -0
- package/dist_ts_compaction/plugins.d.ts +4 -0
- package/dist_ts_compaction/plugins.js +3 -0
- package/dist_ts_compaction/smartagent.compaction.d.ts +10 -0
- package/dist_ts_compaction/smartagent.compaction.js +46 -0
- package/dist_ts_tools/index.d.ts +8 -0
- package/dist_ts_tools/index.js +6 -0
- package/dist_ts_tools/plugins.d.ts +15 -0
- package/dist_ts_tools/plugins.js +19 -0
- package/dist_ts_tools/tool.filesystem.d.ts +6 -0
- package/dist_ts_tools/tool.filesystem.js +102 -0
- package/dist_ts_tools/tool.http.d.ts +2 -0
- package/dist_ts_tools/tool.http.js +65 -0
- package/dist_ts_tools/tool.json.d.ts +2 -0
- package/dist_ts_tools/tool.json.js +47 -0
- package/dist_ts_tools/tool.shell.d.ts +8 -0
- package/dist_ts_tools/tool.shell.js +40 -0
- package/npmextra.json +1 -1
- package/package.json +30 -18
- package/readme.hints.md +38 -84
- package/readme.md +254 -682
- package/ts/00_commitinfo_data.ts +2 -2
- package/ts/index.ts +10 -37
- package/ts/plugins.ts +22 -21
- package/ts/smartagent.classes.agent.ts +198 -0
- package/ts/smartagent.classes.toolregistry.ts +11 -179
- package/ts/smartagent.interfaces.ts +51 -363
- package/ts/smartagent.utils.truncation.ts +39 -0
- package/ts_compaction/index.ts +1 -0
- package/ts_compaction/plugins.ts +6 -0
- package/ts_compaction/smartagent.compaction.ts +51 -0
- package/ts_tools/index.ts +8 -0
- package/ts_tools/plugins.ts +30 -0
- package/ts_tools/tool.filesystem.ts +131 -0
- package/ts_tools/tool.http.ts +78 -0
- package/ts_tools/tool.json.ts +53 -0
- package/ts_tools/tool.shell.ts +62 -0
- package/dist_ts/smartagent.classes.driveragent.d.ts +0 -134
- package/dist_ts/smartagent.classes.driveragent.js +0 -671
- package/dist_ts/smartagent.classes.dualagent.d.ts +0 -93
- package/dist_ts/smartagent.classes.dualagent.js +0 -614
- package/dist_ts/smartagent.classes.guardianagent.d.ts +0 -46
- package/dist_ts/smartagent.classes.guardianagent.js +0 -201
- package/dist_ts/smartagent.tools.base.d.ts +0 -52
- package/dist_ts/smartagent.tools.base.js +0 -42
- package/dist_ts/smartagent.tools.browser.d.ts +0 -17
- package/dist_ts/smartagent.tools.browser.js +0 -229
- package/dist_ts/smartagent.tools.deno.d.ts +0 -21
- package/dist_ts/smartagent.tools.deno.js +0 -191
- package/dist_ts/smartagent.tools.expert.d.ts +0 -27
- package/dist_ts/smartagent.tools.expert.js +0 -126
- package/dist_ts/smartagent.tools.filesystem.d.ts +0 -40
- package/dist_ts/smartagent.tools.filesystem.js +0 -801
- package/dist_ts/smartagent.tools.http.d.ts +0 -16
- package/dist_ts/smartagent.tools.http.js +0 -264
- package/dist_ts/smartagent.tools.json.d.ts +0 -24
- package/dist_ts/smartagent.tools.json.js +0 -202
- package/dist_ts/smartagent.tools.search.d.ts +0 -29
- package/dist_ts/smartagent.tools.search.js +0 -215
- package/dist_ts/smartagent.tools.shell.d.ts +0 -17
- package/dist_ts/smartagent.tools.shell.js +0 -202
- package/ts/smartagent.classes.driveragent.ts +0 -775
- package/ts/smartagent.classes.dualagent.ts +0 -692
- package/ts/smartagent.classes.guardianagent.ts +0 -241
- package/ts/smartagent.tools.base.ts +0 -83
- package/ts/smartagent.tools.browser.ts +0 -253
- package/ts/smartagent.tools.deno.ts +0 -230
- package/ts/smartagent.tools.expert.ts +0 -144
- package/ts/smartagent.tools.filesystem.ts +0 -885
- package/ts/smartagent.tools.http.ts +0 -283
- package/ts/smartagent.tools.json.ts +0 -224
- package/ts/smartagent.tools.search.ts +0 -237
- 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
|
-
}
|