@probelabs/probe 0.6.0-rc115 → 0.6.0-rc117

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.
@@ -213,45 +213,99 @@ export function createWrappedTools(baseTools) {
213
213
  return wrappedTools;
214
214
  }
215
215
 
216
- // Simple file listing tool
216
+ // Simple file listing tool with ls-like formatted output
217
217
  export const listFilesTool = {
218
218
  execute: async (params) => {
219
219
  const { directory = '.', workingDirectory } = params;
220
-
220
+
221
221
  // Use the provided working directory, or fall back to process.cwd()
222
222
  const baseCwd = workingDirectory || process.cwd();
223
-
223
+
224
224
  // Security: Validate path to prevent traversal attacks
225
225
  const secureBaseDir = path.resolve(baseCwd);
226
226
  const targetDir = path.resolve(secureBaseDir, directory);
227
227
  if (!targetDir.startsWith(secureBaseDir + path.sep) && targetDir !== secureBaseDir) {
228
228
  throw new Error('Path traversal attempt detected. Access denied.');
229
229
  }
230
-
230
+
231
+ const debug = process.env.DEBUG === '1';
232
+
233
+ if (debug) {
234
+ console.log(`[DEBUG] Listing files in directory: ${targetDir}`);
235
+ }
236
+
231
237
  try {
232
- const files = await listFilesByLevel({
233
- directory: targetDir,
234
- maxFiles: 100,
235
- respectGitignore: !process.env.PROBE_NO_GITIGNORE || process.env.PROBE_NO_GITIGNORE === '',
236
- cwd: secureBaseDir
238
+ // Read the directory contents
239
+ const files = await fsPromises.readdir(targetDir, { withFileTypes: true });
240
+
241
+ // Format size for human readability
242
+ const formatSize = (size) => {
243
+ if (size < 1024) return `${size}B`;
244
+ if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)}K`;
245
+ if (size < 1024 * 1024 * 1024) return `${(size / (1024 * 1024)).toFixed(1)}M`;
246
+ return `${(size / (1024 * 1024 * 1024)).toFixed(1)}G`;
247
+ };
248
+
249
+ // Format the results as ls-style output
250
+ const entries = await Promise.all(files.map(async (file) => {
251
+ const isDirectory = file.isDirectory();
252
+ const fullPath = path.join(targetDir, file.name);
253
+
254
+ let size = 0;
255
+ try {
256
+ const stats = await fsPromises.stat(fullPath);
257
+ size = stats.size;
258
+ } catch (statError) {
259
+ if (debug) {
260
+ console.log(`[DEBUG] Could not stat file ${file.name}:`, statError.message);
261
+ }
262
+ }
263
+
264
+ return {
265
+ name: file.name,
266
+ isDirectory,
267
+ size
268
+ };
269
+ }));
270
+
271
+ // Sort: directories first, then files, both alphabetically
272
+ entries.sort((a, b) => {
273
+ if (a.isDirectory && !b.isDirectory) return -1;
274
+ if (!a.isDirectory && b.isDirectory) return 1;
275
+ return a.name.localeCompare(b.name);
237
276
  });
238
-
239
- return files;
277
+
278
+ // Format entries
279
+ const formatted = entries.map(entry => {
280
+ const type = entry.isDirectory ? 'dir ' : 'file';
281
+ const sizeStr = formatSize(entry.size).padStart(8);
282
+ return `${type} ${sizeStr} ${entry.name}`;
283
+ });
284
+
285
+ if (debug) {
286
+ console.log(`[DEBUG] Found ${entries.length} files/directories in ${targetDir}`);
287
+ }
288
+
289
+ // Return as formatted text output
290
+ const header = `${targetDir}:\n`;
291
+ const output = header + formatted.join('\n');
292
+
293
+ return output;
240
294
  } catch (error) {
241
295
  throw new Error(`Failed to list files: ${error.message}`);
242
296
  }
243
297
  }
244
298
  };
245
299
 
246
- // Simple file search tool
300
+ // Simple file search tool with timeout protection
247
301
  export const searchFilesTool = {
248
302
  execute: async (params) => {
249
303
  const { pattern, directory = '.', recursive = true, workingDirectory } = params;
250
-
304
+
251
305
  if (!pattern) {
252
306
  throw new Error('Pattern is required for file search');
253
307
  }
254
-
308
+
255
309
  // Security: Validate path to prevent traversal attacks
256
310
  const baseCwd = workingDirectory || process.cwd();
257
311
  const secureBaseDir = path.resolve(baseCwd);
@@ -259,19 +313,40 @@ export const searchFilesTool = {
259
313
  if (!targetDir.startsWith(secureBaseDir + path.sep) && targetDir !== secureBaseDir) {
260
314
  throw new Error('Path traversal attempt detected. Access denied.');
261
315
  }
262
-
316
+
317
+ // Validate pattern complexity to prevent DoS
318
+ if (pattern.includes('**/**') || pattern.split('*').length > 10) {
319
+ throw new Error('Pattern too complex. Please use a simpler glob pattern.');
320
+ }
321
+
263
322
  try {
264
323
  const options = {
265
324
  cwd: targetDir,
266
325
  ignore: ['node_modules/**', '.git/**'],
267
326
  absolute: false
268
327
  };
269
-
328
+
270
329
  if (!recursive) {
271
330
  options.deep = 1;
272
331
  }
273
-
274
- const files = await glob(pattern, options);
332
+
333
+ // Create a timeout promise (10 seconds)
334
+ const timeoutPromise = new Promise((_, reject) => {
335
+ setTimeout(() => reject(new Error('Search operation timed out after 10 seconds')), 10000);
336
+ });
337
+
338
+ // Race glob against timeout
339
+ const files = await Promise.race([
340
+ glob(pattern, options),
341
+ timeoutPromise
342
+ ]);
343
+
344
+ // Limit results to prevent memory issues
345
+ const maxResults = 1000;
346
+ if (files.length > maxResults) {
347
+ return files.slice(0, maxResults);
348
+ }
349
+
275
350
  return files;
276
351
  } catch (error) {
277
352
  throw new Error(`Failed to search files: ${error.message}`);
@@ -813,7 +813,7 @@ When presented with a broken Mermaid diagram, analyze it thoroughly and provide
813
813
  debug: this.options.debug,
814
814
  tracer: this.options.tracer,
815
815
  allowEdit: this.options.allowEdit,
816
- maxIterations: 2, // Limit mermaid fixing to 2 iterations to prevent long loops
816
+ maxIterations: 10, // Allow more iterations for mermaid fixing to handle complex diagrams
817
817
  disableMermaidValidation: true // CRITICAL: Disable mermaid validation in nested agent to prevent infinite recursion
818
818
  });
819
819
  }
package/build/index.js CHANGED
@@ -34,6 +34,7 @@ import { searchTool, queryTool, extractTool, delegateTool } from './tools/vercel
34
34
  import { bashTool } from './tools/bash.js';
35
35
  import { ProbeAgent } from './agent/ProbeAgent.js';
36
36
  import { SimpleTelemetry, SimpleAppTracer, initializeSimpleTelemetryFromOptions } from './agent/simpleTelemetry.js';
37
+ import { listFilesToolInstance, searchFilesToolInstance } from './agent/probeTool.js';
37
38
 
38
39
  export {
39
40
  search,
@@ -57,6 +58,9 @@ export {
57
58
  extractTool,
58
59
  delegateTool,
59
60
  bashTool,
61
+ // Export tool instances
62
+ listFilesToolInstance,
63
+ searchFilesToolInstance,
60
64
  // Export schemas
61
65
  searchSchema,
62
66
  querySchema,