@fastino-ai/pioneer-cli 0.2.9 → 0.2.11

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.
@@ -1,444 +0,0 @@
1
- /**
2
- * Filesystem tools - Read/write files, list directories, search
3
- */
4
-
5
- import * as fs from "fs";
6
- import * as path from "path";
7
- import type { Tool, ToolResult, ToolParameter } from "./types.js";
8
-
9
- export interface FilesystemOptions {
10
- basePath?: string;
11
- allowedPaths?: string[];
12
- maxFileSize?: number; // bytes
13
- }
14
-
15
- function isPathAllowed(filePath: string, options: FilesystemOptions): boolean {
16
- if (!options.allowedPaths || options.allowedPaths.length === 0) {
17
- return true;
18
- }
19
-
20
- const resolved = path.resolve(filePath);
21
- return options.allowedPaths.some((allowed) => resolved.startsWith(path.resolve(allowed)));
22
- }
23
-
24
- export function createReadFileTool(options: FilesystemOptions = {}): Tool {
25
- const { maxFileSize = 1024 * 1024 } = options; // 1MB default
26
-
27
- return {
28
- name: "read_file",
29
- description: "Read the contents of a file. Returns the file content as text.",
30
- parameters: [
31
- {
32
- name: "path",
33
- type: "string",
34
- description: "Path to the file to read",
35
- required: true,
36
- },
37
- {
38
- name: "start_line",
39
- type: "number",
40
- description: "Starting line number (1-based, optional)",
41
- required: false,
42
- },
43
- {
44
- name: "end_line",
45
- type: "number",
46
- description: "Ending line number (1-based, inclusive, optional)",
47
- required: false,
48
- },
49
- ],
50
-
51
- async execute(args: Record<string, unknown>): Promise<ToolResult> {
52
- const filePath = args.path as string;
53
- const startLine = args.start_line as number | undefined;
54
- const endLine = args.end_line as number | undefined;
55
-
56
- if (!filePath) {
57
- return { toolCallId: "", output: "", success: false, error: "Path is required" };
58
- }
59
-
60
- if (!isPathAllowed(filePath, options)) {
61
- return { toolCallId: "", output: "", success: false, error: "Path not allowed" };
62
- }
63
-
64
- try {
65
- const stats = fs.statSync(filePath);
66
- if (stats.size > maxFileSize) {
67
- return {
68
- toolCallId: "",
69
- output: "",
70
- success: false,
71
- error: `File too large (${stats.size} bytes). Max: ${maxFileSize} bytes`,
72
- };
73
- }
74
-
75
- let content = fs.readFileSync(filePath, "utf-8");
76
-
77
- if (startLine !== undefined || endLine !== undefined) {
78
- const lines = content.split("\n");
79
- const start = Math.max(1, startLine || 1) - 1;
80
- const end = endLine ? Math.min(lines.length, endLine) : lines.length;
81
- content = lines.slice(start, end).join("\n");
82
- }
83
-
84
- return { toolCallId: "", output: content, success: true };
85
- } catch (err) {
86
- return {
87
- toolCallId: "",
88
- output: "",
89
- success: false,
90
- error: err instanceof Error ? err.message : String(err),
91
- };
92
- }
93
- },
94
- };
95
- }
96
-
97
- export function createWriteFileTool(options: FilesystemOptions = {}): Tool {
98
- return {
99
- name: "write_file",
100
- description: "Write content to a file. Creates the file if it doesn't exist, overwrites if it does.",
101
- parameters: [
102
- {
103
- name: "path",
104
- type: "string",
105
- description: "Path to the file to write",
106
- required: true,
107
- },
108
- {
109
- name: "content",
110
- type: "string",
111
- description: "Content to write to the file",
112
- required: true,
113
- },
114
- ],
115
-
116
- async execute(args: Record<string, unknown>): Promise<ToolResult> {
117
- const filePath = args.path as string;
118
- const content = args.content as string;
119
-
120
- if (!filePath) {
121
- return { toolCallId: "", output: "", success: false, error: "Path is required" };
122
- }
123
-
124
- if (content === undefined) {
125
- return { toolCallId: "", output: "", success: false, error: "Content is required" };
126
- }
127
-
128
- if (!isPathAllowed(filePath, options)) {
129
- return { toolCallId: "", output: "", success: false, error: "Path not allowed" };
130
- }
131
-
132
- try {
133
- const dir = path.dirname(filePath);
134
- if (!fs.existsSync(dir)) {
135
- fs.mkdirSync(dir, { recursive: true });
136
- }
137
-
138
- fs.writeFileSync(filePath, content, "utf-8");
139
- return {
140
- toolCallId: "",
141
- output: `Successfully wrote ${content.length} characters to ${filePath}`,
142
- success: true,
143
- };
144
- } catch (err) {
145
- return {
146
- toolCallId: "",
147
- output: "",
148
- success: false,
149
- error: err instanceof Error ? err.message : String(err),
150
- };
151
- }
152
- },
153
- };
154
- }
155
-
156
- export function createListDirTool(options: FilesystemOptions = {}): Tool {
157
- return {
158
- name: "list_directory",
159
- description: "List files and directories in a given path.",
160
- parameters: [
161
- {
162
- name: "path",
163
- type: "string",
164
- description: "Path to the directory to list",
165
- required: true,
166
- },
167
- {
168
- name: "recursive",
169
- type: "boolean",
170
- description: "Whether to list recursively (default: false)",
171
- required: false,
172
- },
173
- {
174
- name: "max_depth",
175
- type: "number",
176
- description: "Maximum depth for recursive listing (default: 3)",
177
- required: false,
178
- },
179
- ],
180
-
181
- async execute(args: Record<string, unknown>): Promise<ToolResult> {
182
- const dirPath = args.path as string;
183
- const recursive = args.recursive as boolean || false;
184
- const maxDepth = (args.max_depth as number) || 3;
185
-
186
- if (!dirPath) {
187
- return { toolCallId: "", output: "", success: false, error: "Path is required" };
188
- }
189
-
190
- if (!isPathAllowed(dirPath, options)) {
191
- return { toolCallId: "", output: "", success: false, error: "Path not allowed" };
192
- }
193
-
194
- try {
195
- const entries = listDir(dirPath, recursive, maxDepth, 0);
196
- return { toolCallId: "", output: entries.join("\n"), success: true };
197
- } catch (err) {
198
- return {
199
- toolCallId: "",
200
- output: "",
201
- success: false,
202
- error: err instanceof Error ? err.message : String(err),
203
- };
204
- }
205
- },
206
- };
207
- }
208
-
209
- function listDir(dirPath: string, recursive: boolean, maxDepth: number, currentDepth: number): string[] {
210
- const results: string[] = [];
211
- const entries = fs.readdirSync(dirPath, { withFileTypes: true });
212
-
213
- for (const entry of entries) {
214
- const fullPath = path.join(dirPath, entry.name);
215
- const prefix = " ".repeat(currentDepth);
216
-
217
- if (entry.isDirectory()) {
218
- results.push(`${prefix}${entry.name}/`);
219
- if (recursive && currentDepth < maxDepth) {
220
- try {
221
- results.push(...listDir(fullPath, recursive, maxDepth, currentDepth + 1));
222
- } catch {
223
- // Skip inaccessible directories
224
- }
225
- }
226
- } else {
227
- results.push(`${prefix}${entry.name}`);
228
- }
229
- }
230
-
231
- return results;
232
- }
233
-
234
- export function createSearchFilesTool(options: FilesystemOptions = {}): Tool {
235
- return {
236
- name: "search_files",
237
- description: "Search for files by name pattern or content using grep.",
238
- parameters: [
239
- {
240
- name: "path",
241
- type: "string",
242
- description: "Directory to search in",
243
- required: true,
244
- },
245
- {
246
- name: "pattern",
247
- type: "string",
248
- description: "Search pattern (regex for content, glob for filename)",
249
- required: true,
250
- },
251
- {
252
- name: "type",
253
- type: "string",
254
- description: "Search type: 'content' (grep) or 'filename' (find)",
255
- required: false,
256
- },
257
- ],
258
-
259
- async execute(args: Record<string, unknown>): Promise<ToolResult> {
260
- const searchPath = args.path as string;
261
- const pattern = args.pattern as string;
262
- const type = (args.type as string) || "content";
263
-
264
- if (!searchPath || !pattern) {
265
- return { toolCallId: "", output: "", success: false, error: "Path and pattern are required" };
266
- }
267
-
268
- if (!isPathAllowed(searchPath, options)) {
269
- return { toolCallId: "", output: "", success: false, error: "Path not allowed" };
270
- }
271
-
272
- try {
273
- if (type === "filename") {
274
- const results = searchByFilename(searchPath, pattern);
275
- return { toolCallId: "", output: results.join("\n") || "No matches found", success: true };
276
- } else {
277
- const results = searchByContent(searchPath, pattern);
278
- return { toolCallId: "", output: results.join("\n") || "No matches found", success: true };
279
- }
280
- } catch (err) {
281
- return {
282
- toolCallId: "",
283
- output: "",
284
- success: false,
285
- error: err instanceof Error ? err.message : String(err),
286
- };
287
- }
288
- },
289
- };
290
- }
291
-
292
- function searchByFilename(dirPath: string, pattern: string, results: string[] = []): string[] {
293
- try {
294
- const entries = fs.readdirSync(dirPath, { withFileTypes: true });
295
- const regex = new RegExp(pattern.replace(/\*/g, ".*").replace(/\?/g, "."));
296
-
297
- for (const entry of entries) {
298
- const fullPath = path.join(dirPath, entry.name);
299
-
300
- if (regex.test(entry.name)) {
301
- results.push(fullPath);
302
- }
303
-
304
- if (entry.isDirectory()) {
305
- try {
306
- searchByFilename(fullPath, pattern, results);
307
- } catch {
308
- // Skip inaccessible directories
309
- }
310
- }
311
- }
312
- } catch {
313
- // Skip inaccessible directories
314
- }
315
-
316
- return results;
317
- }
318
-
319
- function searchByContent(dirPath: string, pattern: string, results: string[] = []): string[] {
320
- try {
321
- const entries = fs.readdirSync(dirPath, { withFileTypes: true });
322
- const regex = new RegExp(pattern, "gm");
323
-
324
- for (const entry of entries) {
325
- const fullPath = path.join(dirPath, entry.name);
326
-
327
- if (entry.isDirectory()) {
328
- try {
329
- searchByContent(fullPath, pattern, results);
330
- } catch {
331
- // Skip inaccessible directories
332
- }
333
- } else {
334
- try {
335
- const content = fs.readFileSync(fullPath, "utf-8");
336
- const matches = content.match(regex);
337
- if (matches) {
338
- const lines = content.split("\n");
339
- for (let i = 0; i < lines.length; i++) {
340
- if (regex.test(lines[i])) {
341
- results.push(`${fullPath}:${i + 1}: ${lines[i].trim()}`);
342
- }
343
- // Reset regex state
344
- regex.lastIndex = 0;
345
- }
346
- }
347
- } catch {
348
- // Skip binary or inaccessible files
349
- }
350
- }
351
- }
352
- } catch {
353
- // Skip inaccessible directories
354
- }
355
-
356
- return results.slice(0, 100); // Limit results
357
- }
358
-
359
- export function createEditFileTool(options: FilesystemOptions = {}): Tool {
360
- return {
361
- name: "edit_file",
362
- description: "Edit a file by replacing specific text. Use this for targeted edits.",
363
- parameters: [
364
- {
365
- name: "path",
366
- type: "string",
367
- description: "Path to the file to edit",
368
- required: true,
369
- },
370
- {
371
- name: "old_text",
372
- type: "string",
373
- description: "The exact text to replace (must match exactly)",
374
- required: true,
375
- },
376
- {
377
- name: "new_text",
378
- type: "string",
379
- description: "The new text to insert",
380
- required: true,
381
- },
382
- ],
383
-
384
- async execute(args: Record<string, unknown>): Promise<ToolResult> {
385
- const filePath = args.path as string;
386
- const oldText = args.old_text as string;
387
- const newText = args.new_text as string;
388
-
389
- if (!filePath || oldText === undefined || newText === undefined) {
390
- return {
391
- toolCallId: "",
392
- output: "",
393
- success: false,
394
- error: "Path, old_text, and new_text are required",
395
- };
396
- }
397
-
398
- if (!isPathAllowed(filePath, options)) {
399
- return { toolCallId: "", output: "", success: false, error: "Path not allowed" };
400
- }
401
-
402
- try {
403
- let content = fs.readFileSync(filePath, "utf-8");
404
-
405
- if (!content.includes(oldText)) {
406
- return {
407
- toolCallId: "",
408
- output: "",
409
- success: false,
410
- error: "old_text not found in file. Make sure it matches exactly.",
411
- };
412
- }
413
-
414
- content = content.replace(oldText, newText);
415
- fs.writeFileSync(filePath, content, "utf-8");
416
-
417
- return {
418
- toolCallId: "",
419
- output: `Successfully edited ${filePath}`,
420
- success: true,
421
- };
422
- } catch (err) {
423
- return {
424
- toolCallId: "",
425
- output: "",
426
- success: false,
427
- error: err instanceof Error ? err.message : String(err),
428
- };
429
- }
430
- },
431
- };
432
- }
433
-
434
- // Export all filesystem tools
435
- export function createFilesystemTools(options: FilesystemOptions = {}): Tool[] {
436
- return [
437
- createReadFileTool(options),
438
- createWriteFileTool(options),
439
- createListDirTool(options),
440
- createSearchFilesTool(options),
441
- createEditFileTool(options),
442
- ];
443
- }
444
-
@@ -1,22 +0,0 @@
1
- /**
2
- * Tools module exports
3
- */
4
-
5
- export type { Tool, ToolResult, ToolParameter } from "./types.js";
6
-
7
- export { createBashTool, executeBashStream } from "./bash.js";
8
- export type { BashOptions } from "./bash.js";
9
-
10
- export {
11
- createReadFileTool,
12
- createWriteFileTool,
13
- createListDirTool,
14
- createSearchFilesTool,
15
- createEditFileTool,
16
- createFilesystemTools,
17
- } from "./filesystem.js";
18
- export type { FilesystemOptions } from "./filesystem.js";
19
-
20
- export { Sandbox, createSandboxTool, isDockerAvailable } from "./sandbox.js";
21
- export type { SandboxOptions, SandboxResult } from "./sandbox.js";
22
-