@probelabs/probe 0.6.0-rc116 → 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.
package/cjs/index.cjs CHANGED
@@ -30339,14 +30339,53 @@ var init_probeTool = __esm({
30339
30339
  if (!targetDir.startsWith(secureBaseDir + import_path7.default.sep) && targetDir !== secureBaseDir) {
30340
30340
  throw new Error("Path traversal attempt detected. Access denied.");
30341
30341
  }
30342
+ const debug = process.env.DEBUG === "1";
30343
+ if (debug) {
30344
+ console.log(`[DEBUG] Listing files in directory: ${targetDir}`);
30345
+ }
30342
30346
  try {
30343
- const files = await listFilesByLevel({
30344
- directory: targetDir,
30345
- maxFiles: 100,
30346
- respectGitignore: !process.env.PROBE_NO_GITIGNORE || process.env.PROBE_NO_GITIGNORE === "",
30347
- cwd: secureBaseDir
30347
+ const files = await import_fs4.promises.readdir(targetDir, { withFileTypes: true });
30348
+ const formatSize = (size) => {
30349
+ if (size < 1024) return `${size}B`;
30350
+ if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)}K`;
30351
+ if (size < 1024 * 1024 * 1024) return `${(size / (1024 * 1024)).toFixed(1)}M`;
30352
+ return `${(size / (1024 * 1024 * 1024)).toFixed(1)}G`;
30353
+ };
30354
+ const entries = await Promise.all(files.map(async (file) => {
30355
+ const isDirectory = file.isDirectory();
30356
+ const fullPath = import_path7.default.join(targetDir, file.name);
30357
+ let size = 0;
30358
+ try {
30359
+ const stats = await import_fs4.promises.stat(fullPath);
30360
+ size = stats.size;
30361
+ } catch (statError) {
30362
+ if (debug) {
30363
+ console.log(`[DEBUG] Could not stat file ${file.name}:`, statError.message);
30364
+ }
30365
+ }
30366
+ return {
30367
+ name: file.name,
30368
+ isDirectory,
30369
+ size
30370
+ };
30371
+ }));
30372
+ entries.sort((a3, b3) => {
30373
+ if (a3.isDirectory && !b3.isDirectory) return -1;
30374
+ if (!a3.isDirectory && b3.isDirectory) return 1;
30375
+ return a3.name.localeCompare(b3.name);
30348
30376
  });
30349
- return files;
30377
+ const formatted = entries.map((entry) => {
30378
+ const type = entry.isDirectory ? "dir " : "file";
30379
+ const sizeStr = formatSize(entry.size).padStart(8);
30380
+ return `${type} ${sizeStr} ${entry.name}`;
30381
+ });
30382
+ if (debug) {
30383
+ console.log(`[DEBUG] Found ${entries.length} files/directories in ${targetDir}`);
30384
+ }
30385
+ const header = `${targetDir}:
30386
+ `;
30387
+ const output = header + formatted.join("\n");
30388
+ return output;
30350
30389
  } catch (error2) {
30351
30390
  throw new Error(`Failed to list files: ${error2.message}`);
30352
30391
  }
@@ -30364,6 +30403,9 @@ var init_probeTool = __esm({
30364
30403
  if (!targetDir.startsWith(secureBaseDir + import_path7.default.sep) && targetDir !== secureBaseDir) {
30365
30404
  throw new Error("Path traversal attempt detected. Access denied.");
30366
30405
  }
30406
+ if (pattern.includes("**/**") || pattern.split("*").length > 10) {
30407
+ throw new Error("Pattern too complex. Please use a simpler glob pattern.");
30408
+ }
30367
30409
  try {
30368
30410
  const options = {
30369
30411
  cwd: targetDir,
@@ -30373,7 +30415,17 @@ var init_probeTool = __esm({
30373
30415
  if (!recursive) {
30374
30416
  options.deep = 1;
30375
30417
  }
30376
- const files = await (0, import_glob.glob)(pattern, options);
30418
+ const timeoutPromise = new Promise((_2, reject2) => {
30419
+ setTimeout(() => reject2(new Error("Search operation timed out after 10 seconds")), 1e4);
30420
+ });
30421
+ const files = await Promise.race([
30422
+ (0, import_glob.glob)(pattern, options),
30423
+ timeoutPromise
30424
+ ]);
30425
+ const maxResults = 1e3;
30426
+ if (files.length > maxResults) {
30427
+ return files.slice(0, maxResults);
30428
+ }
30377
30429
  return files;
30378
30430
  } catch (error2) {
30379
30431
  throw new Error(`Failed to search files: ${error2.message}`);
@@ -57777,8 +57829,8 @@ When presented with a broken Mermaid diagram, analyze it thoroughly and provide
57777
57829
  debug: this.options.debug,
57778
57830
  tracer: this.options.tracer,
57779
57831
  allowEdit: this.options.allowEdit,
57780
- maxIterations: 2,
57781
- // Limit mermaid fixing to 2 iterations to prevent long loops
57832
+ maxIterations: 10,
57833
+ // Allow more iterations for mermaid fixing to handle complex diagrams
57782
57834
  disableMermaidValidation: true
57783
57835
  // CRITICAL: Disable mermaid validation in nested agent to prevent infinite recursion
57784
57836
  });
@@ -58678,6 +58730,18 @@ var init_ProbeAgent = __esm({
58678
58730
  this.toolImplementations.bash = wrappedTools.bashToolInstance;
58679
58731
  }
58680
58732
  this.wrappedTools = wrappedTools;
58733
+ if (this.debug) {
58734
+ console.error("\n[DEBUG] ========================================");
58735
+ console.error("[DEBUG] ProbeAgent Tools Initialized");
58736
+ console.error("[DEBUG] Session ID:", this.sessionId);
58737
+ console.error("[DEBUG] Available tools:");
58738
+ for (const toolName of Object.keys(this.toolImplementations)) {
58739
+ console.error(`[DEBUG] - ${toolName}`);
58740
+ }
58741
+ console.error("[DEBUG] Allowed folders:", this.allowedFolders);
58742
+ console.error("[DEBUG] Outline mode:", this.outline);
58743
+ console.error("[DEBUG] ========================================\n");
58744
+ }
58681
58745
  }
58682
58746
  /**
58683
58747
  * Initialize the AI model based on available API keys and forced provider setting
@@ -59448,12 +59512,28 @@ You are working with a repository located at: ${searchDirectory}
59448
59512
  const { type } = parsedTool;
59449
59513
  if (type === "mcp" && this.mcpBridge && this.mcpBridge.isMcpTool(toolName)) {
59450
59514
  try {
59451
- if (this.debug) console.log(`[DEBUG] Executing MCP tool '${toolName}' with params:`, params);
59515
+ if (this.debug) {
59516
+ console.error(`
59517
+ [DEBUG] ========================================`);
59518
+ console.error(`[DEBUG] Executing MCP tool: ${toolName}`);
59519
+ console.error(`[DEBUG] Arguments:`);
59520
+ for (const [key, value] of Object.entries(params)) {
59521
+ const displayValue = typeof value === "string" && value.length > 100 ? value.substring(0, 100) + "..." : value;
59522
+ console.error(`[DEBUG] ${key}: ${JSON.stringify(displayValue)}`);
59523
+ }
59524
+ console.error(`[DEBUG] ========================================
59525
+ `);
59526
+ }
59452
59527
  const executionResult = await this.mcpBridge.mcpTools[toolName].execute(params);
59453
59528
  const toolResultContent = typeof executionResult === "string" ? executionResult : JSON.stringify(executionResult, null, 2);
59454
- const preview = createMessagePreview(toolResultContent);
59455
59529
  if (this.debug) {
59456
- console.log(`[DEBUG] MCP tool '${toolName}' executed successfully. Result preview: ${preview}`);
59530
+ const preview = toolResultContent.length > 500 ? toolResultContent.substring(0, 500) + "..." : toolResultContent;
59531
+ console.error(`[DEBUG] ========================================`);
59532
+ console.error(`[DEBUG] MCP tool '${toolName}' completed successfully`);
59533
+ console.error(`[DEBUG] Result preview:`);
59534
+ console.error(preview);
59535
+ console.error(`[DEBUG] ========================================
59536
+ `);
59457
59537
  }
59458
59538
  currentMessages.push({ role: "user", content: `<tool_result>
59459
59539
  ${toolResultContent}
@@ -59461,7 +59541,13 @@ ${toolResultContent}
59461
59541
  } catch (error2) {
59462
59542
  console.error(`Error executing MCP tool ${toolName}:`, error2);
59463
59543
  const toolResultContent = `Error executing MCP tool ${toolName}: ${error2.message}`;
59464
- if (this.debug) console.log(`[DEBUG] MCP tool '${toolName}' execution FAILED.`);
59544
+ if (this.debug) {
59545
+ console.error(`[DEBUG] ========================================`);
59546
+ console.error(`[DEBUG] MCP tool '${toolName}' failed with error:`);
59547
+ console.error(`[DEBUG] ${error2.message}`);
59548
+ console.error(`[DEBUG] ========================================
59549
+ `);
59550
+ }
59465
59551
  currentMessages.push({ role: "user", content: `<tool_result>
59466
59552
  ${toolResultContent}
59467
59553
  </tool_result>` });
@@ -59473,6 +59559,18 @@ ${toolResultContent}
59473
59559
  sessionId: this.sessionId,
59474
59560
  workingDirectory: this.allowedFolders && this.allowedFolders[0] || process.cwd()
59475
59561
  };
59562
+ if (this.debug) {
59563
+ console.error(`
59564
+ [DEBUG] ========================================`);
59565
+ console.error(`[DEBUG] Executing tool: ${toolName}`);
59566
+ console.error(`[DEBUG] Arguments:`);
59567
+ for (const [key, value] of Object.entries(params)) {
59568
+ const displayValue = typeof value === "string" && value.length > 100 ? value.substring(0, 100) + "..." : value;
59569
+ console.error(`[DEBUG] ${key}: ${JSON.stringify(displayValue)}`);
59570
+ }
59571
+ console.error(`[DEBUG] ========================================
59572
+ `);
59573
+ }
59476
59574
  this.events.emit("toolCall", {
59477
59575
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
59478
59576
  name: toolName,
@@ -59514,6 +59612,15 @@ ${toolResultContent}
59514
59612
  } else {
59515
59613
  toolResult = await executeToolCall();
59516
59614
  }
59615
+ if (this.debug) {
59616
+ const resultPreview = typeof toolResult === "string" ? toolResult.length > 500 ? toolResult.substring(0, 500) + "..." : toolResult : toolResult ? JSON.stringify(toolResult, null, 2).substring(0, 500) + "..." : "No Result";
59617
+ console.error(`[DEBUG] ========================================`);
59618
+ console.error(`[DEBUG] Tool '${toolName}' completed successfully`);
59619
+ console.error(`[DEBUG] Result preview:`);
59620
+ console.error(resultPreview);
59621
+ console.error(`[DEBUG] ========================================
59622
+ `);
59623
+ }
59517
59624
  this.events.emit("toolCall", {
59518
59625
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
59519
59626
  name: toolName,
@@ -59522,6 +59629,13 @@ ${toolResultContent}
59522
59629
  status: "completed"
59523
59630
  });
59524
59631
  } catch (toolError) {
59632
+ if (this.debug) {
59633
+ console.error(`[DEBUG] ========================================`);
59634
+ console.error(`[DEBUG] Tool '${toolName}' failed with error:`);
59635
+ console.error(`[DEBUG] ${toolError.message}`);
59636
+ console.error(`[DEBUG] ========================================
59637
+ `);
59638
+ }
59525
59639
  this.events.emit("toolCall", {
59526
59640
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
59527
59641
  name: toolName,
@@ -59571,6 +59685,16 @@ Error: Unknown tool '${toolName}'. Available tools: ${allAvailableTools.join(",
59571
59685
  }
59572
59686
  }
59573
59687
  } else {
59688
+ const hasMermaidCodeBlock = /```mermaid\s*\n[\s\S]*?\n```/.test(assistantResponseContent);
59689
+ const hasNoSchemaOrTools = !options.schema && validTools.length === 0;
59690
+ if (hasMermaidCodeBlock && hasNoSchemaOrTools) {
59691
+ finalResult = assistantResponseContent;
59692
+ completionAttempted = true;
59693
+ if (this.debug) {
59694
+ console.error(`[DEBUG] Accepting mermaid code block as valid completion (no schema, no tools)`);
59695
+ }
59696
+ break;
59697
+ }
59574
59698
  currentMessages.push({ role: "assistant", content: assistantResponseContent });
59575
59699
  let reminderContent;
59576
59700
  if (options.schema) {
@@ -60284,12 +60408,14 @@ __export(index_exports, {
60284
60408
  getBinaryPath: () => getBinaryPath,
60285
60409
  initializeSimpleTelemetryFromOptions: () => initializeSimpleTelemetryFromOptions,
60286
60410
  listFilesByLevel: () => listFilesByLevel,
60411
+ listFilesToolInstance: () => listFilesToolInstance,
60287
60412
  parseXmlToolCall: () => parseXmlToolCall,
60288
60413
  query: () => query,
60289
60414
  querySchema: () => querySchema,
60290
60415
  queryTool: () => queryTool,
60291
60416
  queryToolDefinition: () => queryToolDefinition,
60292
60417
  search: () => search,
60418
+ searchFilesToolInstance: () => searchFilesToolInstance,
60293
60419
  searchSchema: () => searchSchema,
60294
60420
  searchTool: () => searchTool,
60295
60421
  searchToolDefinition: () => searchToolDefinition,
@@ -60312,6 +60438,7 @@ var init_index = __esm({
60312
60438
  init_bash();
60313
60439
  init_ProbeAgent();
60314
60440
  init_simpleTelemetry();
60441
+ init_probeTool();
60315
60442
  }
60316
60443
  });
60317
60444
  init_index();
@@ -60337,12 +60464,14 @@ init_index();
60337
60464
  getBinaryPath,
60338
60465
  initializeSimpleTelemetryFromOptions,
60339
60466
  listFilesByLevel,
60467
+ listFilesToolInstance,
60340
60468
  parseXmlToolCall,
60341
60469
  query,
60342
60470
  querySchema,
60343
60471
  queryTool,
60344
60472
  queryToolDefinition,
60345
60473
  search,
60474
+ searchFilesToolInstance,
60346
60475
  searchSchema,
60347
60476
  searchTool,
60348
60477
  searchToolDefinition,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@probelabs/probe",
3
- "version": "0.6.0-rc116",
3
+ "version": "0.6.0-rc117",
4
4
  "description": "Node.js wrapper for the probe code search tool",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
@@ -188,9 +188,23 @@ export class ProbeAgent {
188
188
  if (this.enableBash && wrappedTools.bashToolInstance) {
189
189
  this.toolImplementations.bash = wrappedTools.bashToolInstance;
190
190
  }
191
-
191
+
192
192
  // Store wrapped tools for ACP system
193
193
  this.wrappedTools = wrappedTools;
194
+
195
+ // Log available tools in debug mode
196
+ if (this.debug) {
197
+ console.error('\n[DEBUG] ========================================');
198
+ console.error('[DEBUG] ProbeAgent Tools Initialized');
199
+ console.error('[DEBUG] Session ID:', this.sessionId);
200
+ console.error('[DEBUG] Available tools:');
201
+ for (const toolName of Object.keys(this.toolImplementations)) {
202
+ console.error(`[DEBUG] - ${toolName}`);
203
+ }
204
+ console.error('[DEBUG] Allowed folders:', this.allowedFolders);
205
+ console.error('[DEBUG] Outline mode:', this.outline);
206
+ console.error('[DEBUG] ========================================\n');
207
+ }
194
208
  }
195
209
 
196
210
  /**
@@ -1117,34 +1131,74 @@ When troubleshooting:
1117
1131
  if (type === 'mcp' && this.mcpBridge && this.mcpBridge.isMcpTool(toolName)) {
1118
1132
  // Execute MCP tool
1119
1133
  try {
1120
- if (this.debug) console.log(`[DEBUG] Executing MCP tool '${toolName}' with params:`, params);
1134
+ // Log MCP tool execution in debug mode
1135
+ if (this.debug) {
1136
+ console.error(`\n[DEBUG] ========================================`);
1137
+ console.error(`[DEBUG] Executing MCP tool: ${toolName}`);
1138
+ console.error(`[DEBUG] Arguments:`);
1139
+ for (const [key, value] of Object.entries(params)) {
1140
+ const displayValue = typeof value === 'string' && value.length > 100
1141
+ ? value.substring(0, 100) + '...'
1142
+ : value;
1143
+ console.error(`[DEBUG] ${key}: ${JSON.stringify(displayValue)}`);
1144
+ }
1145
+ console.error(`[DEBUG] ========================================\n`);
1146
+ }
1121
1147
 
1122
1148
  // Execute MCP tool through the bridge
1123
1149
  const executionResult = await this.mcpBridge.mcpTools[toolName].execute(params);
1124
1150
 
1125
1151
  const toolResultContent = typeof executionResult === 'string' ? executionResult : JSON.stringify(executionResult, null, 2);
1126
- const preview = createMessagePreview(toolResultContent);
1152
+
1153
+ // Log MCP tool result in debug mode
1127
1154
  if (this.debug) {
1128
- console.log(`[DEBUG] MCP tool '${toolName}' executed successfully. Result preview: ${preview}`);
1155
+ const preview = toolResultContent.length > 500 ? toolResultContent.substring(0, 500) + '...' : toolResultContent;
1156
+ console.error(`[DEBUG] ========================================`);
1157
+ console.error(`[DEBUG] MCP tool '${toolName}' completed successfully`);
1158
+ console.error(`[DEBUG] Result preview:`);
1159
+ console.error(preview);
1160
+ console.error(`[DEBUG] ========================================\n`);
1129
1161
  }
1130
1162
 
1131
1163
  currentMessages.push({ role: 'user', content: `<tool_result>\n${toolResultContent}\n</tool_result>` });
1132
1164
  } catch (error) {
1133
1165
  console.error(`Error executing MCP tool ${toolName}:`, error);
1134
1166
  const toolResultContent = `Error executing MCP tool ${toolName}: ${error.message}`;
1135
- if (this.debug) console.log(`[DEBUG] MCP tool '${toolName}' execution FAILED.`);
1167
+
1168
+ // Log MCP tool error in debug mode
1169
+ if (this.debug) {
1170
+ console.error(`[DEBUG] ========================================`);
1171
+ console.error(`[DEBUG] MCP tool '${toolName}' failed with error:`);
1172
+ console.error(`[DEBUG] ${error.message}`);
1173
+ console.error(`[DEBUG] ========================================\n`);
1174
+ }
1175
+
1136
1176
  currentMessages.push({ role: 'user', content: `<tool_result>\n${toolResultContent}\n</tool_result>` });
1137
1177
  }
1138
1178
  } else if (this.toolImplementations[toolName]) {
1139
1179
  // Execute native tool
1140
1180
  try {
1141
1181
  // Add sessionId and workingDirectory to params for tool execution
1142
- const toolParams = {
1143
- ...params,
1182
+ const toolParams = {
1183
+ ...params,
1144
1184
  sessionId: this.sessionId,
1145
1185
  workingDirectory: (this.allowedFolders && this.allowedFolders[0]) || process.cwd()
1146
1186
  };
1147
-
1187
+
1188
+ // Log tool execution in debug mode
1189
+ if (this.debug) {
1190
+ console.error(`\n[DEBUG] ========================================`);
1191
+ console.error(`[DEBUG] Executing tool: ${toolName}`);
1192
+ console.error(`[DEBUG] Arguments:`);
1193
+ for (const [key, value] of Object.entries(params)) {
1194
+ const displayValue = typeof value === 'string' && value.length > 100
1195
+ ? value.substring(0, 100) + '...'
1196
+ : value;
1197
+ console.error(`[DEBUG] ${key}: ${JSON.stringify(displayValue)}`);
1198
+ }
1199
+ console.error(`[DEBUG] ========================================\n`);
1200
+ }
1201
+
1148
1202
  // Emit tool start event
1149
1203
  this.events.emit('toolCall', {
1150
1204
  timestamp: new Date().toISOString(),
@@ -1196,6 +1250,18 @@ When troubleshooting:
1196
1250
  toolResult = await executeToolCall();
1197
1251
  }
1198
1252
 
1253
+ // Log tool result in debug mode
1254
+ if (this.debug) {
1255
+ const resultPreview = typeof toolResult === 'string'
1256
+ ? (toolResult.length > 500 ? toolResult.substring(0, 500) + '...' : toolResult)
1257
+ : (toolResult ? JSON.stringify(toolResult, null, 2).substring(0, 500) + '...' : 'No Result');
1258
+ console.error(`[DEBUG] ========================================`);
1259
+ console.error(`[DEBUG] Tool '${toolName}' completed successfully`);
1260
+ console.error(`[DEBUG] Result preview:`);
1261
+ console.error(resultPreview);
1262
+ console.error(`[DEBUG] ========================================\n`);
1263
+ }
1264
+
1199
1265
  // Emit tool success event
1200
1266
  this.events.emit('toolCall', {
1201
1267
  timestamp: new Date().toISOString(),
@@ -1206,8 +1272,16 @@ When troubleshooting:
1206
1272
  : (toolResult ? JSON.stringify(toolResult).substring(0, 200) + '...' : 'No Result'),
1207
1273
  status: 'completed'
1208
1274
  });
1209
-
1275
+
1210
1276
  } catch (toolError) {
1277
+ // Log tool error in debug mode
1278
+ if (this.debug) {
1279
+ console.error(`[DEBUG] ========================================`);
1280
+ console.error(`[DEBUG] Tool '${toolName}' failed with error:`);
1281
+ console.error(`[DEBUG] ${toolError.message}`);
1282
+ console.error(`[DEBUG] ========================================\n`);
1283
+ }
1284
+
1211
1285
  // Emit tool error event
1212
1286
  this.events.emit('toolCall', {
1213
1287
  timestamp: new Date().toISOString(),
@@ -1262,7 +1336,23 @@ When troubleshooting:
1262
1336
  }
1263
1337
  }
1264
1338
  } else {
1265
- // No tool call found, add assistant response and ask for tool usage
1339
+ // No tool call found
1340
+ // Special case: If response contains a mermaid code block and no schema was provided,
1341
+ // treat it as a valid completion (for mermaid diagram fixing workflow)
1342
+ const hasMermaidCodeBlock = /```mermaid\s*\n[\s\S]*?\n```/.test(assistantResponseContent);
1343
+ const hasNoSchemaOrTools = !options.schema && validTools.length === 0;
1344
+
1345
+ if (hasMermaidCodeBlock && hasNoSchemaOrTools) {
1346
+ // Accept mermaid code block as final answer for diagram fixing
1347
+ finalResult = assistantResponseContent;
1348
+ completionAttempted = true;
1349
+ if (this.debug) {
1350
+ console.error(`[DEBUG] Accepting mermaid code block as valid completion (no schema, no tools)`);
1351
+ }
1352
+ break;
1353
+ }
1354
+
1355
+ // Add assistant response and ask for tool usage
1266
1356
  currentMessages.push({ role: 'assistant', content: assistantResponseContent });
1267
1357
 
1268
1358
  // Build appropriate reminder message based on whether schema is provided
@@ -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/src/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,