onbuzz 4.9.13 → 4.10.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 (451) hide show
  1. package/node_modules/glob/README.md +31 -5
  2. package/node_modules/glob/dist/commonjs/glob.d.ts +8 -0
  3. package/node_modules/glob/dist/commonjs/glob.d.ts.map +1 -1
  4. package/node_modules/glob/dist/commonjs/glob.js +2 -1
  5. package/node_modules/glob/dist/commonjs/glob.js.map +1 -1
  6. package/node_modules/glob/dist/commonjs/index.min.js +3 -3
  7. package/node_modules/glob/dist/commonjs/index.min.js.map +4 -4
  8. package/node_modules/glob/dist/commonjs/pattern.d.ts +3 -0
  9. package/node_modules/glob/dist/commonjs/pattern.d.ts.map +1 -1
  10. package/node_modules/glob/dist/commonjs/pattern.js +4 -0
  11. package/node_modules/glob/dist/commonjs/pattern.js.map +1 -1
  12. package/node_modules/glob/dist/esm/glob.d.ts +8 -0
  13. package/node_modules/glob/dist/esm/glob.d.ts.map +1 -1
  14. package/node_modules/glob/dist/esm/glob.js +2 -1
  15. package/node_modules/glob/dist/esm/glob.js.map +1 -1
  16. package/node_modules/glob/dist/esm/index.min.js +3 -3
  17. package/node_modules/glob/dist/esm/index.min.js.map +4 -4
  18. package/node_modules/glob/dist/esm/pattern.d.ts +3 -0
  19. package/node_modules/glob/dist/esm/pattern.d.ts.map +1 -1
  20. package/node_modules/glob/dist/esm/pattern.js +4 -0
  21. package/node_modules/glob/dist/esm/pattern.js.map +1 -1
  22. package/node_modules/{@isaacs → glob/node_modules}/balanced-match/README.md +7 -10
  23. package/node_modules/{@isaacs → glob/node_modules}/balanced-match/package.json +7 -18
  24. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/README.md +3 -6
  25. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.js +6 -4
  26. package/node_modules/glob/node_modules/brace-expansion/dist/commonjs/index.js.map +1 -0
  27. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.js +6 -4
  28. package/node_modules/glob/node_modules/brace-expansion/dist/esm/index.js.map +1 -0
  29. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/package.json +11 -7
  30. package/node_modules/glob/node_modules/minimatch/README.md +76 -1
  31. package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts +1 -1
  32. package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts.map +1 -1
  33. package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.js.map +1 -1
  34. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.d.ts +4 -2
  35. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.d.ts.map +1 -1
  36. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.js +309 -55
  37. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.js.map +1 -1
  38. package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.d.ts.map +1 -1
  39. package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.js +2 -4
  40. package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.js.map +1 -1
  41. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.d.ts +1 -1
  42. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.d.ts.map +1 -1
  43. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.js +4 -4
  44. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.js.map +1 -1
  45. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.d.ts +81 -1
  46. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.d.ts.map +1 -1
  47. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.js +232 -134
  48. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.js.map +1 -1
  49. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.d.ts +1 -1
  50. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.d.ts.map +1 -1
  51. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.js +8 -8
  52. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.js.map +1 -1
  53. package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts +1 -1
  54. package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts.map +1 -1
  55. package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.js.map +1 -1
  56. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.d.ts +4 -2
  57. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.d.ts.map +1 -1
  58. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.js +309 -55
  59. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.js.map +1 -1
  60. package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.d.ts.map +1 -1
  61. package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.js +2 -4
  62. package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.js.map +1 -1
  63. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.d.ts +1 -1
  64. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.d.ts.map +1 -1
  65. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.js +4 -4
  66. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.js.map +1 -1
  67. package/node_modules/glob/node_modules/minimatch/dist/esm/index.d.ts +81 -1
  68. package/node_modules/glob/node_modules/minimatch/dist/esm/index.d.ts.map +1 -1
  69. package/node_modules/glob/node_modules/minimatch/dist/esm/index.js +232 -134
  70. package/node_modules/glob/node_modules/minimatch/dist/esm/index.js.map +1 -1
  71. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.d.ts +1 -1
  72. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.d.ts.map +1 -1
  73. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.js +8 -8
  74. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.js.map +1 -1
  75. package/node_modules/glob/node_modules/minimatch/package.json +17 -11
  76. package/node_modules/glob/package.json +10 -13
  77. package/node_modules/minipass/LICENSE.md +55 -0
  78. package/node_modules/minipass/dist/commonjs/index.d.ts +12 -16
  79. package/node_modules/minipass/dist/commonjs/index.d.ts.map +1 -1
  80. package/node_modules/minipass/dist/commonjs/index.js +13 -3
  81. package/node_modules/minipass/dist/commonjs/index.js.map +1 -1
  82. package/node_modules/minipass/dist/esm/index.d.ts +12 -16
  83. package/node_modules/minipass/dist/esm/index.d.ts.map +1 -1
  84. package/node_modules/minipass/dist/esm/index.js +3 -1
  85. package/node_modules/minipass/dist/esm/index.js.map +1 -1
  86. package/node_modules/minipass/package.json +9 -14
  87. package/node_modules/path-scurry/node_modules/lru-cache/README.md +96 -10
  88. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel-browser.d.ts.map +1 -0
  89. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel-browser.js.map +1 -0
  90. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel.d.ts +5 -0
  91. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel.js +7 -0
  92. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.d.ts +1400 -0
  93. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.d.ts.map +1 -0
  94. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.js +1726 -0
  95. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.js.map +1 -0
  96. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.min.js +2 -0
  97. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.min.js.map +7 -0
  98. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.d.ts +12 -0
  99. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.d.ts.map +1 -0
  100. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.js +10 -0
  101. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.js.map +1 -0
  102. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel-cjs.cjs.map +1 -0
  103. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel-cjs.d.cts.map +1 -0
  104. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel.d.ts +5 -0
  105. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel.js +7 -0
  106. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.d.ts +109 -32
  107. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.d.ts.map +1 -1
  108. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js +334 -197
  109. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js.map +1 -1
  110. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js +1 -1
  111. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js.map +4 -4
  112. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel-node.d.ts.map +1 -0
  113. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel-node.js.map +1 -0
  114. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel.d.ts +5 -0
  115. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel.js +9 -0
  116. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.d.ts +1400 -0
  117. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.d.ts.map +1 -0
  118. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.js +1726 -0
  119. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.js.map +1 -0
  120. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.min.js +2 -0
  121. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.min.js.map +7 -0
  122. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.d.ts +12 -0
  123. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.d.ts.map +1 -0
  124. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.js +10 -0
  125. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.js.map +1 -0
  126. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.d.ts +12 -0
  127. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.d.ts.map +1 -0
  128. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.js +10 -0
  129. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.js.map +1 -0
  130. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel-browser.d.ts.map +1 -0
  131. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel-browser.js.map +1 -0
  132. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.d.ts +5 -0
  133. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.js +4 -0
  134. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.d.ts +1400 -0
  135. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.d.ts.map +1 -0
  136. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.js +1722 -0
  137. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.js.map +1 -0
  138. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.min.js +2 -0
  139. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.min.js.map +7 -0
  140. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.d.ts +12 -0
  141. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.d.ts.map +1 -0
  142. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.js +7 -0
  143. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.js.map +1 -0
  144. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel-esm.d.mts.map +1 -0
  145. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel-esm.mjs.map +1 -0
  146. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel.d.ts +5 -0
  147. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel.js +19 -0
  148. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.d.ts +109 -32
  149. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.d.ts.map +1 -1
  150. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js +333 -196
  151. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js.map +1 -1
  152. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js +1 -1
  153. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js.map +4 -4
  154. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel-node.d.ts.map +1 -0
  155. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel-node.js.map +1 -0
  156. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel.d.ts +5 -0
  157. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel.js +6 -0
  158. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.d.ts +1400 -0
  159. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.d.ts.map +1 -0
  160. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.js +1722 -0
  161. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.js.map +1 -0
  162. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.min.js +2 -0
  163. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.min.js.map +7 -0
  164. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.d.ts +12 -0
  165. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.d.ts.map +1 -0
  166. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.js +7 -0
  167. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.js.map +1 -0
  168. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.d.ts +12 -0
  169. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.d.ts.map +1 -0
  170. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.js +7 -0
  171. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.js.map +1 -0
  172. package/node_modules/path-scurry/node_modules/lru-cache/package.json +71 -18
  173. package/node_modules/path-scurry/package.json +8 -24
  174. package/package.json +1 -1
  175. package/scripts/debug-balance-probe.mjs +35 -35
  176. package/scripts/push-image.sh +43 -43
  177. package/scripts/setup-acr.sh +65 -65
  178. package/scripts/verify-optional-deps.js +96 -1
  179. package/src/__tests__/composioCliFlags.test.js +239 -239
  180. package/src/analyzers/CSSAnalyzer.js +298 -297
  181. package/src/analyzers/ConfigValidator.js +691 -690
  182. package/src/analyzers/ESLintAnalyzer.js +320 -320
  183. package/src/analyzers/JavaScriptAnalyzer.js +260 -261
  184. package/src/analyzers/PrettierFormatter.js +246 -247
  185. package/src/analyzers/PythonAnalyzer.js +283 -283
  186. package/src/analyzers/SecurityAnalyzer.js +729 -729
  187. package/src/analyzers/SparrowAnalyzer.js +341 -341
  188. package/src/analyzers/TypeScriptAnalyzer.js +247 -247
  189. package/src/analyzers/__tests__/CSSAnalyzer.test.js +41 -41
  190. package/src/analyzers/__tests__/ConfigValidator.test.js +362 -362
  191. package/src/analyzers/__tests__/JavaScriptAnalyzer.test.js +40 -40
  192. package/src/analyzers/__tests__/PythonAnalyzer.test.js +205 -208
  193. package/src/analyzers/__tests__/SecurityAnalyzer.test.js +303 -303
  194. package/src/analyzers/__tests__/TypeScriptAnalyzer.test.js +187 -187
  195. package/src/analyzers/codeCloneDetector/analyzer.js +344 -344
  196. package/src/analyzers/codeCloneDetector/detector.js +250 -250
  197. package/src/analyzers/codeCloneDetector/index.js +194 -192
  198. package/src/analyzers/codeCloneDetector/parser.js +199 -199
  199. package/src/core/__tests__/agentPool.test.js +866 -866
  200. package/src/core/__tests__/agentPoolAutoResume.test.js +209 -209
  201. package/src/core/__tests__/agentPoolWakeOnMessage.test.js +315 -315
  202. package/src/core/__tests__/agentScheduler.emptyResponseChatStall.test.js +213 -213
  203. package/src/core/__tests__/agentScheduler.errorCategorisation.test.js +246 -246
  204. package/src/core/__tests__/agentScheduler.firstChunkTimeout.test.js +138 -138
  205. package/src/core/__tests__/agentScheduler.modeTransitions.test.js +233 -233
  206. package/src/core/__tests__/agentScheduler.nativePromptPick.test.js +319 -319
  207. package/src/core/__tests__/agentScheduler.taskLifecycleInstruction.test.js +78 -78
  208. package/src/core/__tests__/agentScheduler.visualizer.test.js +258 -258
  209. package/src/core/__tests__/flowCheckpointStore.test.js +140 -140
  210. package/src/core/__tests__/flowEndToEnd.test.js +565 -565
  211. package/src/core/__tests__/flowFieldMapping.test.js +188 -189
  212. package/src/core/__tests__/flowLintClientMirror.test.js +96 -98
  213. package/src/core/__tests__/flowSavePayload.test.js +170 -169
  214. package/src/core/__tests__/flowTemplates.test.js +311 -311
  215. package/src/core/__tests__/flowVersionStore.test.js +123 -123
  216. package/src/core/__tests__/messageProcessor.test.js +669 -669
  217. package/src/core/__tests__/stateManager.test.js +0 -1
  218. package/src/core/agentPool.js +2474 -2475
  219. package/src/core/agentScheduler.js +1 -4
  220. package/src/core/contextManager.js +708 -708
  221. package/src/core/flowExecutor.js +1510 -1510
  222. package/src/core/flowFieldMapping.js +136 -138
  223. package/src/core/messageProcessor.js +953 -954
  224. package/src/core/orchestrator.js +593 -595
  225. package/src/core/stateManager.js +1765 -1752
  226. package/src/index.js +1221 -1221
  227. package/src/interfaces/__tests__/archivedAgentDelete.test.js +207 -207
  228. package/src/interfaces/__tests__/bulkAgentRoute.test.js +361 -361
  229. package/src/interfaces/__tests__/imageServing.test.js +228 -228
  230. package/src/interfaces/__tests__/remoteSessionAuth.test.js +308 -308
  231. package/src/interfaces/__tests__/videoJobsRoutes.test.js +178 -179
  232. package/src/interfaces/__tests__/webServer.marketplace.test.js +629 -629
  233. package/src/interfaces/schedulerRoutes.js +50 -50
  234. package/src/interfaces/terminal/__tests__/smoke/connection.test.js +341 -350
  235. package/src/interfaces/terminal/__tests__/smoke/enhancements.test.js +156 -156
  236. package/src/interfaces/terminal/__tests__/smoke/imports.test.js +325 -330
  237. package/src/interfaces/terminal/__tests__/smoke/tools.test.js +385 -388
  238. package/src/interfaces/terminal/api/session.js +265 -266
  239. package/src/interfaces/terminal/api/websocket.js +496 -497
  240. package/src/interfaces/terminal/components/AgentCreator.js +691 -705
  241. package/src/interfaces/terminal/components/AgentEditor.js +676 -678
  242. package/src/interfaces/terminal/components/AgentSwitcher.js +331 -330
  243. package/src/interfaces/terminal/components/ErrorPanel.js +263 -264
  244. package/src/interfaces/terminal/components/Header.js +28 -28
  245. package/src/interfaces/terminal/components/Layout.js +598 -603
  246. package/src/interfaces/terminal/components/MessageList.js +280 -281
  247. package/src/interfaces/terminal/components/SettingsPanel.js +410 -415
  248. package/src/interfaces/terminal/components/StatusBar.js +2 -0
  249. package/src/interfaces/terminal/index.js +168 -168
  250. package/src/interfaces/terminal/state/useAgentControl.js +496 -496
  251. package/src/interfaces/terminal/state/useAgents.js +537 -537
  252. package/src/interfaces/terminal/state/useMessages.js +629 -630
  253. package/src/interfaces/terminal/state/useTools.js +554 -554
  254. package/src/interfaces/terminal/utils/debugLogger.js +44 -44
  255. package/src/interfaces/terminal/utils/settingsStorage.js +232 -232
  256. package/src/interfaces/webServer.js +7578 -7579
  257. package/src/interfaces/webServer.js.bak +7046 -7046
  258. package/src/modules/fileExplorer/__tests__/zipDownload.test.js +237 -237
  259. package/src/modules/fileExplorer/controller.js +470 -469
  260. package/src/modules/fileExplorer/routes.js +285 -286
  261. package/src/modules/widget/__tests__/isDisabled.test.js +41 -41
  262. package/src/modules/widget/__tests__/routes.test.js +677 -678
  263. package/src/modules/widget/__tests__/runtime.test.js +401 -401
  264. package/src/modules/widget/__tests__/versioning.test.js +309 -309
  265. package/src/modules/widget/__tests__/webComponentRuntime.test.js +565 -565
  266. package/src/modules/widget/__tests__/widgetTool.test.js +316 -316
  267. package/src/modules/widget/routes.js +435 -435
  268. package/src/modules/widget/runtime/bundle.js +640 -640
  269. package/src/modules/widget/runtime/webComponentBundle.js +470 -470
  270. package/src/modules/widget/schema.js +182 -181
  271. package/src/modules/widget/widgetTool.js +1389 -1389
  272. package/src/services/__tests__/agentActivityService.test.js +401 -402
  273. package/src/services/__tests__/benchmarkService.test.js +184 -184
  274. package/src/services/__tests__/contextInjectionService.test.js +246 -246
  275. package/src/services/__tests__/conversationQuery.test.js +721 -723
  276. package/src/services/__tests__/credentialVault.test.js +469 -469
  277. package/src/services/__tests__/discordService.integration.test.js +638 -639
  278. package/src/services/__tests__/flowContextService.test.js +590 -590
  279. package/src/services/__tests__/memoryService.test.js +1 -1
  280. package/src/services/__tests__/messageSource.test.js +380 -380
  281. package/src/services/__tests__/modelRouterNaming.test.js +111 -111
  282. package/src/services/__tests__/projectDetector.test.js +34 -34
  283. package/src/services/__tests__/promptService.test.js +242 -242
  284. package/src/services/__tests__/telegramService.test.js +941 -941
  285. package/src/services/__tests__/tokenCountingService.test.js +48 -48
  286. package/src/services/agentActivityService.js +419 -420
  287. package/src/services/aiService.js +2997 -3001
  288. package/src/services/apiKeyManager.js +359 -359
  289. package/src/services/benchmarkService.js +196 -196
  290. package/src/services/codebaseKnowledgeService.js +2 -2
  291. package/src/services/composioService.js +738 -738
  292. package/src/services/conversationCompactionService.js +1258 -1257
  293. package/src/services/credentialVault.js +685 -685
  294. package/src/services/discordService.js +792 -793
  295. package/src/services/embeddings/__tests__/azureCustomProvider.test.js +232 -232
  296. package/src/services/embeddings/__tests__/embeddingService.test.js +417 -417
  297. package/src/services/embeddings/__tests__/localProvider.test.js +263 -263
  298. package/src/services/embeddings/autoRecall.js +218 -219
  299. package/src/services/embeddings/indexers/__tests__/agentIndexer.test.js +232 -232
  300. package/src/services/embeddings/indexers/__tests__/memoryIndexer.test.js +418 -418
  301. package/src/services/embeddings/indexers/__tests__/reminisceIndexer.test.js +356 -357
  302. package/src/services/embeddings/indexers/__tests__/skillsIndexer.test.js +145 -145
  303. package/src/services/embeddings/indexers/__tests__/taskIndexer.test.js +146 -146
  304. package/src/services/embeddings/indexers/composioIndexer.js +279 -279
  305. package/src/services/embeddings/providerInterface.js +206 -206
  306. package/src/services/embeddings/providers/localProvider.js +11 -7
  307. package/src/services/embeddings/providers/openaiProvider.js +101 -101
  308. package/src/services/embeddings/vectorStore/inMemoryJsonStore.js +356 -356
  309. package/src/services/errorHandler.js +809 -809
  310. package/src/services/flowContextService.js +586 -586
  311. package/src/services/grounding/MockAdapter.js +125 -125
  312. package/src/services/modelRouterService.js +26 -31
  313. package/src/services/modelsService.js +322 -322
  314. package/src/services/ollamaService.js +452 -452
  315. package/src/services/projectDetector.js +403 -404
  316. package/src/services/promptService.js +418 -418
  317. package/src/services/qualityInspector.js +795 -795
  318. package/src/services/scheduleService.js +726 -726
  319. package/src/services/serviceRegistry.js +386 -386
  320. package/src/services/telegrafBot.js +174 -174
  321. package/src/services/telegramService.js +1972 -1972
  322. package/src/services/visualEditorBridge.js +1033 -1033
  323. package/src/services/visualEditorServer.js +1769 -1774
  324. package/src/services/whatsappService.js +667 -668
  325. package/src/tools/__tests__/agentCommunicationTool.findAgent.test.js +226 -226
  326. package/src/tools/__tests__/agentCommunicationTool.test.js +3 -3
  327. package/src/tools/__tests__/agentDelayTool.test.js +342 -342
  328. package/src/tools/__tests__/baseTool.test.js +3 -3
  329. package/src/tools/__tests__/codeMapTool.test.js +915 -915
  330. package/src/tools/__tests__/fileContentReplaceTool.test.js +309 -309
  331. package/src/tools/__tests__/fileTreeTool.test.js +274 -274
  332. package/src/tools/__tests__/filesystemTool.test.js +815 -815
  333. package/src/tools/__tests__/foundryWebSearchTool.test.js +252 -252
  334. package/src/tools/__tests__/imageTool.validator.test.js +194 -194
  335. package/src/tools/__tests__/jobDoneTool.test.js +580 -581
  336. package/src/tools/__tests__/memoryTool.forgetStale.test.js +272 -272
  337. package/src/tools/__tests__/memoryTool.reminisce.test.js +2 -2
  338. package/src/tools/__tests__/memoryTool.reminisceSemanticSearch.test.js +301 -301
  339. package/src/tools/__tests__/memoryTool.semanticSearch.test.js +405 -405
  340. package/src/tools/__tests__/memoryTool.teamPool.test.js +293 -293
  341. package/src/tools/__tests__/memoryTool.test.js +1 -1
  342. package/src/tools/__tests__/seekTool.test.js +282 -282
  343. package/src/tools/__tests__/skillsTool.search.test.js +164 -164
  344. package/src/tools/__tests__/skillsTool.test.js +226 -226
  345. package/src/tools/__tests__/staticAnalysisTool.test.js +509 -509
  346. package/src/tools/__tests__/taskManagerTool.discipline.test.js +137 -137
  347. package/src/tools/__tests__/taskManagerTool.search.test.js +143 -143
  348. package/src/tools/__tests__/taskManagerTool.test.js +866 -866
  349. package/src/tools/__tests__/terminalTool.test.js +448 -448
  350. package/src/tools/__tests__/toolShapeForgiveness.test.js +259 -260
  351. package/src/tools/__tests__/userPromptTool.test.js +297 -297
  352. package/src/tools/__tests__/videoTool.jobs.test.js +147 -147
  353. package/src/tools/__tests__/webTool.e2e.test.js +609 -603
  354. package/src/tools/__tests__/webTool.unit.test.js +195 -195
  355. package/src/tools/__tests__/webTool.visionModel.test.js +75 -75
  356. package/src/tools/agentCommunicationTool.js +8 -10
  357. package/src/tools/agentDelayTool.js +496 -497
  358. package/src/tools/asyncToolManager.js +602 -603
  359. package/src/tools/baseTool.js +12 -11
  360. package/src/tools/cloneDetectionTool.js +576 -581
  361. package/src/tools/codeMapTool.js +0 -6
  362. package/src/tools/composioTool.js +617 -617
  363. package/src/tools/dependencyResolverTool.js +1211 -1212
  364. package/src/tools/desktop/DesktopTool.js +629 -638
  365. package/src/tools/desktop/__tests__/DesktopTool.e2e.test.js +306 -306
  366. package/src/tools/desktop/__tests__/DesktopTool.test.js +507 -507
  367. package/src/tools/desktop/__tests__/osController.test.js +364 -364
  368. package/src/tools/desktop/osController.js +491 -491
  369. package/src/tools/docxTool.js +623 -623
  370. package/src/tools/excelTool.js +636 -636
  371. package/src/tools/fileContentReplaceTool.js +5 -7
  372. package/src/tools/fileSystemTool.js +12 -19
  373. package/src/tools/fileTreeTool.js +840 -840
  374. package/src/tools/foundryWebSearchTool.js +273 -273
  375. package/src/tools/helpTool.js +198 -198
  376. package/src/tools/imageTool.js +1397 -1397
  377. package/src/tools/importAnalyzerTool.js +1056 -1056
  378. package/src/tools/jobDoneTool.js +495 -495
  379. package/src/tools/memoryTool.js +1 -1
  380. package/src/tools/office/pres/__tests__/presSystem.test.js +365 -365
  381. package/src/tools/office/pres/archetypes/agenda.js +61 -61
  382. package/src/tools/office/pres/archetypes/bentoGrid.js +218 -219
  383. package/src/tools/office/pres/archetypes/bigStat.js +140 -142
  384. package/src/tools/office/pres/archetypes/closing.js +70 -70
  385. package/src/tools/office/pres/archetypes/hero.js +70 -70
  386. package/src/tools/office/pres/archetypes/productHero.js +93 -94
  387. package/src/tools/office/pres/archetypes/table.js +73 -74
  388. package/src/tools/office/pres/backgrounds/orb.js +66 -66
  389. package/src/tools/office/pres/components.js +422 -423
  390. package/src/tools/officeTool.js +441 -441
  391. package/src/tools/pdfTool.js +625 -627
  392. package/src/tools/platformControlTool.js +1081 -1081
  393. package/src/tools/seekTool.js +917 -918
  394. package/src/tools/skillsTool.js +1 -1
  395. package/src/tools/staticAnalysisTool.js +2143 -2146
  396. package/src/tools/taskManagerTool.js +3324 -3324
  397. package/src/tools/terminalTool.js +2615 -2618
  398. package/src/tools/videoTool.js +1303 -1303
  399. package/src/tools/visionTool.js +508 -508
  400. package/src/tools/visualEditorTool.js +1289 -1290
  401. package/src/tools/webTool.js +3368 -3368
  402. package/src/tools/whatsappTool.js +464 -464
  403. package/src/types/__tests__/agent.test.js +499 -499
  404. package/src/types/__tests__/contextReference.test.js +606 -606
  405. package/src/types/__tests__/conversation.test.js +555 -555
  406. package/src/types/__tests__/toolCommand.test.js +584 -584
  407. package/src/types/contextReference.js +974 -971
  408. package/src/types/conversation.js +729 -729
  409. package/src/types/toolCommand.js +746 -746
  410. package/src/utilities/__tests__/attachmentValidator.test.js +80 -80
  411. package/src/utilities/__tests__/auditReport.test.js +328 -328
  412. package/src/utilities/__tests__/directoryAccessManager.test.js +388 -388
  413. package/src/utilities/__tests__/jsonRepair.test.js +103 -104
  414. package/src/utilities/__tests__/modeTransitionReasons.test.js +105 -105
  415. package/src/utilities/__tests__/platformUtils.test.js +80 -87
  416. package/src/utilities/__tests__/structuredFileValidator.test.js +261 -263
  417. package/src/utilities/__tests__/toolConstants.test.js +92 -94
  418. package/src/utilities/__tests__/useIsTouchDevice.detect.test.js +114 -114
  419. package/src/utilities/__tests__/webUiUtilSync.test.js +117 -117
  420. package/src/utilities/attachmentValidator.js +284 -288
  421. package/src/utilities/authCache.js.backup-1779570472481 +121 -121
  422. package/src/utilities/browserStealth.js +631 -630
  423. package/src/utilities/configManager.js +616 -617
  424. package/src/utilities/directoryAccessManager.js +564 -565
  425. package/src/utilities/fileProcessor.js +308 -307
  426. package/src/utilities/humanBehavior.js +454 -453
  427. package/src/utilities/logger.js +479 -479
  428. package/src/utilities/structuredFileValidator.js +696 -699
  429. package/src/utilities/tagParser.js +5 -10
  430. package/src/utilities/userDataDir.js +308 -308
  431. package/node_modules/@isaacs/brace-expansion/dist/commonjs/index.js.map +0 -1
  432. package/node_modules/@isaacs/brace-expansion/dist/esm/index.js.map +0 -1
  433. package/node_modules/minipass/LICENSE +0 -15
  434. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/LICENSE.md +0 -0
  435. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.d.ts +0 -0
  436. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.d.ts.map +0 -0
  437. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.js +0 -0
  438. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.js.map +0 -0
  439. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/package.json +0 -0
  440. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.d.ts +0 -0
  441. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.d.ts.map +0 -0
  442. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.js +0 -0
  443. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.js.map +0 -0
  444. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/package.json +0 -0
  445. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/LICENSE +0 -0
  446. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.d.ts +0 -0
  447. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.d.ts.map +0 -0
  448. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/package.json +0 -0
  449. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.d.ts +0 -0
  450. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.d.ts.map +0 -0
  451. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/package.json +0 -0
@@ -1,918 +1,917 @@
1
- /**
2
- * SeekTool - Search for content within project files
3
- *
4
- * Purpose:
5
- * - Search for specific content/patterns within files
6
- * - Support glob patterns for file paths
7
- * - Verify imports, function usage, and variable references
8
- * - Provide detailed match information with line numbers
9
- */
10
-
11
- import { BaseTool } from './baseTool.js';
12
- import TagParser from '../utilities/tagParser.js';
13
- import { promises as fs } from 'fs';
14
- import path from 'path';
15
-
16
- // Configuration constants
17
- const SEEK_CONFIG = {
18
- // File size limits
19
- MAX_FILE_SIZE: 10 * 1024 * 1024, // 10MB - don't search files larger than this
20
-
21
- // Search limits
22
- MAX_FILES_PER_SEARCH: 1000, // Maximum files to search in one operation
23
- MAX_MATCHES_PER_FILE: 100, // Maximum matches to return per file
24
- MAX_TOTAL_MATCHES: 500, // Maximum total matches to return
25
-
26
- // Performance limits
27
- MAX_SEARCH_TERMS: 50, // Maximum number of search terms
28
- MAX_FILE_PATHS: 100, // Maximum number of file path patterns
29
-
30
- // Recursion limits
31
- MAX_DIRECTORY_DEPTH: 20, // Maximum directory recursion depth
32
-
33
- // Result formatting
34
- MAX_LINE_CONTENT_LENGTH: 200, // Maximum line content to show in results
35
- CONTEXT_LINES_BEFORE: 0, // Lines of context before match
36
- CONTEXT_LINES_AFTER: 0, // Lines of context after match
37
- };
38
-
39
- // File encoding
40
- const FILE_ENCODING = 'utf-8';
41
-
42
- // Common binary file extensions to skip
43
- const BINARY_EXTENSIONS = new Set([
44
- '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.ico', '.svg',
45
- '.pdf', '.zip', '.tar', '.gz', '.rar', '.7z',
46
- '.exe', '.dll', '.so', '.dylib',
47
- '.mp3', '.mp4', '.avi', '.mov', '.wmv',
48
- '.woff', '.woff2', '.ttf', '.eot',
49
- '.bin', '.dat', '.db', '.sqlite'
50
- ]);
51
-
52
- class SeekTool extends BaseTool {
53
- constructor(config = {}, logger = null) {
54
- super(config, logger);
55
-
56
- // Tool metadata
57
- this.requiresProject = true; // Requires project directory
58
- this.isAsync = true; // File operations are async
59
- this.timeout = config.timeout || 120000; // 2 minutes default
60
-
61
- // Merge config with defaults
62
- this.seekConfig = {
63
- ...SEEK_CONFIG,
64
- ...config.seekConfig
65
- };
66
- }
67
-
68
- /**
69
- * Get tool description for LLM consumption
70
- * @returns {string} Tool description
71
- */
72
- getDescription() {
73
- return `
74
- Seek Tool: Search for specific content within project files. Ideal for verifying imports, function usage, and variable references.
75
-
76
- USAGE:
77
- \`\`\`json
78
- {
79
- "toolId": "seek",
80
- "filePaths": ["src/**/*.js", "package.json"],
81
- "searchTerms": ["import React", "useState"]
82
- }
83
- \`\`\`
84
-
85
- PARAMETERS:
86
- - filePaths: Array of file paths or glob patterns
87
- - searchTerms: Array of search strings (case-sensitive, exact match)
88
-
89
- GLOB PATTERNS:
90
- - *.js - All .js files in directory
91
- - **/*.js - All .js files recursively
92
- - src/**/*.jsx - All .jsx files under src/
93
-
94
- EXAMPLES:
95
-
96
- Find function usage:
97
- \`\`\`json
98
- {
99
- "toolId": "seek",
100
- "filePaths": ["src/components/*.js", "src/utils/helpers.js"],
101
- "searchTerms": ["function handleSubmit", "useState("]
102
- }
103
- \`\`\`
104
-
105
- Verify imports:
106
- \`\`\`json
107
- {
108
- "toolId": "seek",
109
- "filePaths": ["src/components/Header.js"],
110
- "searchTerms": ["import React", "from 'react'"]
111
- }
112
- \`\`\`
113
-
114
- Search all JS files:
115
- \`\`\`json
116
- {
117
- "toolId": "seek",
118
- "filePaths": ["src/**/*.js"],
119
- "searchTerms": ["apiClient", "config.apiUrl"]
120
- }
121
- \`\`\`
122
-
123
- TIP: After finding matches, use code-map read-range to view surrounding context without reading entire files.
124
-
125
- LIMITS:
126
- - Max ${SEEK_CONFIG.MAX_FILES_PER_SEARCH} files per search
127
- - Max ${SEEK_CONFIG.MAX_SEARCH_TERMS} search terms
128
- - Max ${SEEK_CONFIG.MAX_FILE_SIZE / (1024 * 1024)}MB per file
129
- - Max ${SEEK_CONFIG.MAX_TOTAL_MATCHES} matches total
130
- `;
131
- }
132
-
133
- /**
134
- * Parse parameters from tool command content
135
- * @param {string} content - Raw tool command content
136
- * @returns {Object} Parsed parameters
137
- */
138
- parseParameters(content) {
139
- try {
140
- // Try JSON first
141
- if (content.trim().startsWith('{')) {
142
- const parsed = JSON.parse(content);
143
-
144
- return {
145
- filePaths: parsed.filePaths || [],
146
- searchTerms: parsed.searchTerms || []
147
- };
148
- }
149
-
150
- // XML parsing
151
- const params = {
152
- filePaths: [],
153
- searchTerms: []
154
- };
155
-
156
- // Extract in-files content
157
- const inFilesPattern = /<in-files>([\s\S]*?)<\/in-files>/i;
158
- const inFilesMatch = inFilesPattern.exec(content);
159
-
160
- if (inFilesMatch) {
161
- const filesContent = inFilesMatch[1];
162
- params.filePaths = filesContent
163
- .split('\n')
164
- .map(line => line.trim())
165
- .filter(line => line.length > 0);
166
- }
167
-
168
- // Extract search-terms content
169
- const searchTermsPattern = /<search-terms>([\s\S]*?)<\/search-terms>/i;
170
- const searchTermsMatch = searchTermsPattern.exec(content);
171
-
172
- if (searchTermsMatch) {
173
- const termsContent = searchTermsMatch[1];
174
-
175
- // Extract individual <term> tags
176
- const termPattern = /<term>(.*?)<\/term>/gi;
177
- let termMatch;
178
-
179
- while ((termMatch = termPattern.exec(termsContent)) !== null) {
180
- const term = termMatch[1].trim();
181
- if (term.length > 0) {
182
- params.searchTerms.push(term);
183
- }
184
- }
185
- }
186
-
187
- return params;
188
-
189
- } catch (error) {
190
- this.logger?.error('Failed to parse seek parameters', { error: error.message });
191
- return {
192
- filePaths: [],
193
- searchTerms: [],
194
- parseError: error.message
195
- };
196
- }
197
- }
198
-
199
- /**
200
- * Get required parameters
201
- * @returns {Array<string>} Array of required parameter names
202
- */
203
- getRequiredParameters() {
204
- return ['filePaths', 'searchTerms'];
205
- }
206
-
207
- /**
208
- * Custom parameter validation
209
- * @param {Object} params - Parameters to validate
210
- * @returns {Object} Validation result
211
- */
212
- customValidateParameters(params) {
213
- const errors = [];
214
-
215
- // Validate filePaths
216
- if (!params.filePaths || !Array.isArray(params.filePaths) || params.filePaths.length === 0) {
217
- errors.push('At least one file path is required');
218
- } else if (params.filePaths.length > this.seekConfig.MAX_FILE_PATHS) {
219
- errors.push(`Too many file paths (max ${this.seekConfig.MAX_FILE_PATHS})`);
220
- }
221
-
222
- // Validate searchTerms
223
- if (!params.searchTerms || !Array.isArray(params.searchTerms) || params.searchTerms.length === 0) {
224
- errors.push('At least one search term is required');
225
- } else if (params.searchTerms.length > this.seekConfig.MAX_SEARCH_TERMS) {
226
- errors.push(`Too many search terms (max ${this.seekConfig.MAX_SEARCH_TERMS})`);
227
- }
228
-
229
- // Validate search terms are non-empty strings
230
- if (params.searchTerms && Array.isArray(params.searchTerms)) {
231
- for (const [index, term] of params.searchTerms.entries()) {
232
- if (typeof term !== 'string' || term.trim().length === 0) {
233
- errors.push(`Search term ${index + 1}: must be a non-empty string`);
234
- }
235
- }
236
- }
237
-
238
- // Validate file paths are non-empty strings
239
- if (params.filePaths && Array.isArray(params.filePaths)) {
240
- for (const [index, filePath] of params.filePaths.entries()) {
241
- if (typeof filePath !== 'string' || filePath.trim().length === 0) {
242
- errors.push(`File path ${index + 1}: must be a non-empty string`);
243
- }
244
-
245
- // Check for path traversal attempts
246
- if (filePath.includes('..')) {
247
- errors.push(`File path ${index + 1}: path traversal (..) not allowed for security`);
248
- }
249
- }
250
- }
251
-
252
- return {
253
- valid: errors.length === 0,
254
- errors
255
- };
256
- }
257
-
258
- /**
259
- * Execute tool with parsed parameters
260
- * @param {Object} params - Parsed parameters
261
- * @param {Object} context - Execution context
262
- * @returns {Promise<Object>} Execution result
263
- */
264
- async execute(params, context) {
265
- // Forgiving normalization production agents (gpt-5.4, Kimi-K2.6,
266
- // gpt-4.1-mini) reliably call seek with the natural-feeling shape
267
- // `{query, filePattern}` instead of the documented
268
- // `{searchTerms: [], filePaths: []}`. Live trace evidence:
269
- // agent-agent-v2-v2-s2-multi-topic-1779570320941 hit
270
- // "Cannot read properties of undefined (reading 'length')" 3 times
271
- // and cascaded into task drift. Accept both shapes — coerce
272
- // singular strings to arrays, alternate names to canonical.
273
- const filePathsRaw = params.filePaths != null ? params.filePaths
274
- : params.filePath != null ? params.filePath
275
- : params.filePattern != null ? params.filePattern
276
- : params.paths != null ? params.paths
277
- : null;
278
- const searchTermsRaw = params.searchTerms != null ? params.searchTerms
279
- : params.searchTerm != null ? params.searchTerm
280
- : params.query != null ? params.query
281
- : params.terms != null ? params.terms
282
- : null;
283
- const filePaths = Array.isArray(filePathsRaw) ? filePathsRaw
284
- : (filePathsRaw != null && filePathsRaw !== '') ? [filePathsRaw] : [];
285
- const searchTerms = Array.isArray(searchTermsRaw) ? searchTermsRaw
286
- : (searchTermsRaw != null && searchTermsRaw !== '') ? [searchTermsRaw] : [];
287
-
288
- if (filePaths.length === 0) {
289
- return {
290
- success: false,
291
- error: 'seek requires `filePaths` (array of glob patterns, e.g. ["src/**/*.js"]). Singular `filePath` or `filePattern` also accepted.',
292
- };
293
- }
294
- if (searchTerms.length === 0) {
295
- return {
296
- success: false,
297
- error: 'seek requires `searchTerms` (array of strings to find). Singular `searchTerm` or `query` also accepted.',
298
- };
299
- }
300
-
301
- const { projectDir, agentId, directoryAccess } = context;
302
-
303
- // IMPORTANT: Get all accessible directories for the agent
304
- // This includes workingDirectory, readOnlyDirectories, and writeEnabledDirectories
305
- let workingDirectory = projectDir || process.cwd();
306
- let accessibleDirectories = [workingDirectory];
307
-
308
- if (directoryAccess && directoryAccess.workingDirectory) {
309
- workingDirectory = directoryAccess.workingDirectory;
310
-
311
- // Collect all accessible directories (for read operations)
312
- accessibleDirectories = this.getAllAccessibleDirectories(directoryAccess);
313
-
314
- this.logger?.info('Using agent configured directory access', {
315
- workingDirectory: directoryAccess.workingDirectory,
316
- totalAccessibleDirs: accessibleDirectories.length,
317
- readOnlyDirs: directoryAccess.readOnlyDirectories?.length || 0,
318
- writeEnabledDirs: directoryAccess.writeEnabledDirectories?.length || 0,
319
- agentId
320
- });
321
- }
322
-
323
- if (!workingDirectory) {
324
- throw new Error('Project directory is required for seek tool');
325
- }
326
-
327
- this.logger?.info('Executing seek tool', {
328
- filePathCount: filePaths.length,
329
- searchTermCount: searchTerms.length,
330
- workingDirectory,
331
- accessibleDirectories: accessibleDirectories.length,
332
- agentId
333
- });
334
-
335
- try {
336
- // Resolve file paths (expand globs)
337
- // Pass accessible directories for validation (if agent has directoryAccess configured)
338
- const resolveResult = await this.resolveFilePaths(
339
- filePaths,
340
- workingDirectory,
341
- directoryAccess ? accessibleDirectories : null
342
- );
343
-
344
- const resolvedFiles = resolveResult.found;
345
- const notFoundFiles = resolveResult.notFound;
346
-
347
- // Check file count limit
348
- if (resolvedFiles.length > this.seekConfig.MAX_FILES_PER_SEARCH) {
349
- return {
350
- success: false,
351
- error: `Too many files to search (${resolvedFiles.length}). Maximum is ${this.seekConfig.MAX_FILES_PER_SEARCH}. Use more specific file patterns.`,
352
- filesResolved: resolvedFiles.length,
353
- filesNotFound: notFoundFiles.length
354
- };
355
- }
356
-
357
- // Search in files
358
- const searchResult = await this.searchFiles(resolvedFiles, searchTerms, workingDirectory);
359
-
360
- // Format results
361
- const formattedResults = this.formatResults(
362
- searchResult.matches,
363
- searchResult.errorFiles,
364
- notFoundFiles,
365
- resolvedFiles.length
366
- );
367
-
368
- return {
369
- success: true,
370
- filesSearched: resolvedFiles.length,
371
- filesNotFound: notFoundFiles.length,
372
- filesWithErrors: searchResult.errorFiles.length,
373
- totalMatches: searchResult.matches.length,
374
- matchesByTerm: searchResult.matchesByTerm,
375
- formattedResults,
376
- toolUsed: 'seek',
377
- guidance: searchResult.matches.length > 0
378
- ? 'To view code context around matches, use code-map read-range with the file path and line numbers from above. Example: {"toolId":"code-map","parameters":{"action":"read-range","filePath":"<matched-file>","startLine":<line-5>,"endLine":<line+25>}}'
379
- : undefined
380
- };
381
-
382
- } catch (error) {
383
- this.logger?.error('Seek tool execution failed', { error: error.message });
384
- throw error;
385
- }
386
- }
387
-
388
- /**
389
- * Get all accessible directories for read operations
390
- * @param {Object} directoryAccess - Directory access configuration
391
- * @returns {Array<string>} Array of accessible directory paths
392
- * @private
393
- */
394
- getAllAccessibleDirectories(directoryAccess) {
395
- const directories = new Set();
396
-
397
- // Add working directory
398
- if (directoryAccess.workingDirectory) {
399
- directories.add(directoryAccess.workingDirectory);
400
- }
401
-
402
- // Add read-only directories
403
- if (directoryAccess.readOnlyDirectories && Array.isArray(directoryAccess.readOnlyDirectories)) {
404
- for (const dir of directoryAccess.readOnlyDirectories) {
405
- directories.add(dir);
406
- }
407
- }
408
-
409
- // Add write-enabled directories (if you can write, you can read)
410
- if (directoryAccess.writeEnabledDirectories && Array.isArray(directoryAccess.writeEnabledDirectories)) {
411
- for (const dir of directoryAccess.writeEnabledDirectories) {
412
- directories.add(dir);
413
- }
414
- }
415
-
416
- return Array.from(directories);
417
- }
418
-
419
- /**
420
- * Check if a path is within any accessible directory
421
- * @param {string} targetPath - Path to check
422
- * @param {Array<string>} accessibleDirs - Array of accessible directories
423
- * @returns {boolean} True if path is accessible
424
- * @private
425
- */
426
- isPathAccessible(targetPath, accessibleDirs) {
427
- for (const dir of accessibleDirs) {
428
- const relative = path.relative(dir, targetPath);
429
- const isWithin = !relative.startsWith('..') && !path.isAbsolute(relative);
430
- if (isWithin) {
431
- return true;
432
- }
433
- }
434
- return false;
435
- }
436
-
437
- /**
438
- * Resolve file paths, expanding glob patterns
439
- * @param {Array<string>} filePaths - File paths with possible glob patterns
440
- * @param {string} projectDir - Project directory
441
- * @param {Array<string>} accessibleDirs - Optional array of accessible directories
442
- * @returns {Promise<Object>} Object with found and notFound arrays
443
- * @private
444
- */
445
- async resolveFilePaths(filePaths, projectDir, accessibleDirs = null) {
446
- const result = {
447
- found: [],
448
- notFound: []
449
- };
450
-
451
- for (const filePath of filePaths) {
452
- const normalizedPath = filePath.trim();
453
-
454
- if (normalizedPath.includes('*')) {
455
- // Handle glob patterns
456
- const globResult = await this.expandGlobPattern(normalizedPath, projectDir);
457
-
458
- if (globResult.found.length > 0) {
459
- result.found.push(...globResult.found);
460
- } else {
461
- result.notFound.push(`${normalizedPath} (no matching files)`);
462
- }
463
- } else {
464
- // Handle direct file reference
465
- const absolutePath = path.isAbsolute(normalizedPath)
466
- ? normalizedPath
467
- : path.resolve(projectDir, normalizedPath);
468
-
469
- // Check if path is within accessible directories (if configured)
470
- if (accessibleDirs && accessibleDirs.length > 0) {
471
- if (!this.isPathAccessible(absolutePath, accessibleDirs)) {
472
- result.notFound.push(`${normalizedPath} (not in accessible directories)`);
473
- continue;
474
- }
475
- }
476
-
477
- try {
478
- const stats = await fs.stat(absolutePath);
479
-
480
- if (stats.isFile()) {
481
- result.found.push(absolutePath);
482
- } else if (stats.isDirectory()) {
483
- result.notFound.push(`${normalizedPath} (is a directory, not a file)`);
484
- } else {
485
- result.notFound.push(`${normalizedPath} (not a regular file)`);
486
- }
487
- } catch (error) {
488
- result.notFound.push(`${normalizedPath} (${error.code || error.message})`);
489
- }
490
- }
491
- }
492
-
493
- return result;
494
- }
495
-
496
- /**
497
- * Expand glob pattern to matching file paths
498
- * @param {string} pattern - Glob pattern
499
- * @param {string} projectDir - Project directory
500
- * @returns {Promise<Object>} Object with found files
501
- * @private
502
- */
503
- async expandGlobPattern(pattern, projectDir) {
504
- const result = { found: [] };
505
-
506
- // Handle recursive pattern: src/**/*.js
507
- if (pattern.includes('**/')) {
508
- const [baseDir, filePattern] = pattern.split('**/');
509
- const basePath = path.resolve(projectDir, baseDir);
510
-
511
- try {
512
- const stats = await fs.stat(basePath);
513
-
514
- if (stats.isDirectory()) {
515
- await this.findFilesRecursively(
516
- basePath,
517
- filePattern,
518
- result.found,
519
- 0,
520
- this.seekConfig.MAX_DIRECTORY_DEPTH
521
- );
522
- }
523
- } catch (error) {
524
- // Directory doesn't exist
525
- this.logger?.warn('Base directory not found for glob pattern', { basePath, error: error.message });
526
- }
527
- }
528
- // Handle simple pattern: src/*.js
529
- else if (pattern.includes('*')) {
530
- const dirPath = path.dirname(path.resolve(projectDir, pattern));
531
- const filePattern = path.basename(pattern);
532
-
533
- try {
534
- const stats = await fs.stat(dirPath);
535
-
536
- if (stats.isDirectory()) {
537
- const files = await fs.readdir(dirPath);
538
-
539
- for (const file of files) {
540
- const filePath = path.join(dirPath, file);
541
-
542
- try {
543
- const fileStats = await fs.stat(filePath);
544
-
545
- if (fileStats.isFile() && this.matchesPattern(file, filePattern)) {
546
- result.found.push(filePath);
547
- }
548
- } catch (error) {
549
- // Skip files we can't stat
550
- continue;
551
- }
552
- }
553
- }
554
- } catch (error) {
555
- // Directory doesn't exist
556
- this.logger?.warn('Directory not found for glob pattern', { dirPath, error: error.message });
557
- }
558
- }
559
-
560
- return result;
561
- }
562
-
563
- /**
564
- * Find files recursively matching a pattern
565
- * @param {string} dir - Directory to search
566
- * @param {string} filePattern - File pattern to match
567
- * @param {Array<string>} results - Results array
568
- * @param {number} currentDepth - Current recursion depth
569
- * @param {number} maxDepth - Maximum recursion depth
570
- * @returns {Promise<void>}
571
- * @private
572
- */
573
- async findFilesRecursively(dir, filePattern, results, currentDepth, maxDepth) {
574
- // Prevent infinite recursion
575
- if (currentDepth >= maxDepth) {
576
- this.logger?.warn('Maximum directory depth reached', { dir, currentDepth });
577
- return;
578
- }
579
-
580
- try {
581
- const entries = await fs.readdir(dir, { withFileTypes: true });
582
-
583
- for (const entry of entries) {
584
- // Skip hidden files and directories
585
- if (entry.name.startsWith('.')) {
586
- continue;
587
- }
588
-
589
- const fullPath = path.join(dir, entry.name);
590
-
591
- try {
592
- if (entry.isDirectory()) {
593
- // Skip common large directories
594
- if (this.shouldSkipDirectory(entry.name)) {
595
- continue;
596
- }
597
-
598
- await this.findFilesRecursively(
599
- fullPath,
600
- filePattern,
601
- results,
602
- currentDepth + 1,
603
- maxDepth
604
- );
605
- } else if (entry.isFile()) {
606
- if (this.matchesPattern(entry.name, filePattern)) {
607
- results.push(fullPath);
608
-
609
- // Stop if we've found too many files
610
- if (results.length >= this.seekConfig.MAX_FILES_PER_SEARCH) {
611
- return;
612
- }
613
- }
614
- }
615
- } catch (error) {
616
- // Skip entries we can't access
617
- continue;
618
- }
619
- }
620
- } catch (error) {
621
- this.logger?.warn('Error reading directory', { dir, error: error.message });
622
- }
623
- }
624
-
625
- /**
626
- * Check if filename matches a wildcard pattern
627
- * @param {string} filename - Filename to check
628
- * @param {string} pattern - Wildcard pattern
629
- * @returns {boolean} True if matches
630
- * @private
631
- */
632
- matchesPattern(filename, pattern) {
633
- // Simple wildcard matching
634
- const regexPattern = pattern
635
- .split('*')
636
- .map(part => this.escapeRegExp(part))
637
- .join('.*');
638
-
639
- const regex = new RegExp(`^${regexPattern}$`, 'i');
640
- return regex.test(filename);
641
- }
642
-
643
- /**
644
- * Escape special regex characters
645
- * @param {string} string - String to escape
646
- * @returns {string} Escaped string
647
- * @private
648
- */
649
- escapeRegExp(string) {
650
- return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
651
- }
652
-
653
- /**
654
- * Check if directory should be skipped during recursive search
655
- * @param {string} dirName - Directory name
656
- * @returns {boolean} True if should skip
657
- * @private
658
- */
659
- shouldSkipDirectory(dirName) {
660
- const skipDirs = new Set([
661
- 'node_modules',
662
- '.git',
663
- 'dist',
664
- 'build',
665
- 'coverage',
666
- '.next',
667
- '.nuxt',
668
- 'out',
669
- 'target',
670
- 'vendor',
671
- '__pycache__',
672
- '.cache',
673
- 'tmp',
674
- 'temp'
675
- ]);
676
-
677
- return skipDirs.has(dirName.toLowerCase());
678
- }
679
-
680
- /**
681
- * Check if file should be skipped (binary files)
682
- * @param {string} filePath - File path
683
- * @returns {boolean} True if should skip
684
- * @private
685
- */
686
- shouldSkipFile(filePath) {
687
- const ext = path.extname(filePath).toLowerCase();
688
- return BINARY_EXTENSIONS.has(ext);
689
- }
690
-
691
- /**
692
- * Search for terms in multiple files
693
- * @param {Array<string>} filePaths - File paths to search
694
- * @param {Array<string>} searchTerms - Search terms
695
- * @param {string} projectDir - Project directory for relative paths
696
- * @returns {Promise<Object>} Search results
697
- * @private
698
- */
699
- async searchFiles(filePaths, searchTerms, projectDir) {
700
- const allMatches = [];
701
- const errorFiles = [];
702
- let totalMatches = 0;
703
-
704
- for (const filePath of filePaths) {
705
- // Skip binary files
706
- if (this.shouldSkipFile(filePath)) {
707
- continue;
708
- }
709
-
710
- try {
711
- // Check file size
712
- const stats = await fs.stat(filePath);
713
-
714
- if (stats.size > this.seekConfig.MAX_FILE_SIZE) {
715
- errorFiles.push({
716
- filePath: path.relative(projectDir, filePath),
717
- error: `File too large (${Math.round(stats.size / (1024 * 1024))}MB, max ${Math.round(this.seekConfig.MAX_FILE_SIZE / (1024 * 1024))}MB)`
718
- });
719
- continue;
720
- }
721
-
722
- // Search in file
723
- const matches = await this.searchInFile(filePath, searchTerms, projectDir);
724
-
725
- allMatches.push(...matches);
726
- totalMatches += matches.length;
727
-
728
- // Stop if we've exceeded max total matches
729
- if (totalMatches >= this.seekConfig.MAX_TOTAL_MATCHES) {
730
- this.logger?.warn('Maximum total matches reached', { totalMatches });
731
- break;
732
- }
733
-
734
- } catch (error) {
735
- errorFiles.push({
736
- filePath: path.relative(projectDir, filePath),
737
- error: error.message
738
- });
739
- }
740
- }
741
-
742
- // Group matches by search term
743
- const matchesByTerm = {};
744
-
745
- for (const match of allMatches) {
746
- if (!matchesByTerm[match.term]) {
747
- matchesByTerm[match.term] = [];
748
- }
749
-
750
- matchesByTerm[match.term].push({
751
- filePath: match.filePath,
752
- lineNumber: match.lineNumber,
753
- lineContent: match.lineContent
754
- });
755
- }
756
-
757
- return {
758
- matches: allMatches,
759
- matchesByTerm,
760
- errorFiles
761
- };
762
- }
763
-
764
- /**
765
- * Search for terms in a single file
766
- * @param {string} filePath - File path
767
- * @param {Array<string>} searchTerms - Search terms
768
- * @param {string} projectDir - Project directory for relative paths
769
- * @returns {Promise<Array<Object>>} Matches found
770
- * @private
771
- */
772
- async searchInFile(filePath, searchTerms, projectDir) {
773
- const matches = [];
774
- let matchesInFile = 0;
775
-
776
- try {
777
- // Read file content
778
- const content = await fs.readFile(filePath, FILE_ENCODING);
779
-
780
- // Split into lines
781
- const lines = content.split('\n');
782
- const relativePath = path.relative(projectDir, filePath);
783
-
784
- // Search each line
785
- for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
786
- const line = lines[lineIndex];
787
- const lineNumber = lineIndex + 1; // 1-based line numbers
788
-
789
- // Check each search term
790
- for (const term of searchTerms) {
791
- if (line.includes(term)) {
792
- // Truncate line content if too long
793
- let lineContent = line;
794
- if (lineContent.length > this.seekConfig.MAX_LINE_CONTENT_LENGTH) {
795
- const termIndex = lineContent.indexOf(term);
796
- const start = Math.max(0, termIndex - 50);
797
- const end = Math.min(lineContent.length, termIndex + term.length + 50);
798
- lineContent = (start > 0 ? '...' : '') +
799
- lineContent.substring(start, end) +
800
- (end < lineContent.length ? '...' : '');
801
- }
802
-
803
- matches.push({
804
- term,
805
- filePath: relativePath,
806
- lineNumber,
807
- lineContent: lineContent.trim()
808
- });
809
-
810
- matchesInFile++;
811
-
812
- // Limit matches per file
813
- if (matchesInFile >= this.seekConfig.MAX_MATCHES_PER_FILE) {
814
- this.logger?.warn('Maximum matches per file reached', { filePath: relativePath });
815
- return matches;
816
- }
817
- }
818
- }
819
- }
820
-
821
- } catch (error) {
822
- throw new Error(`Failed to read file: ${error.message}`);
823
- }
824
-
825
- return matches;
826
- }
827
-
828
- /**
829
- * Format search results for display
830
- * @param {Array<Object>} matches - Search matches
831
- * @param {Array<Object>} errorFiles - Files with errors
832
- * @param {Array<string>} notFoundFiles - Files not found
833
- * @param {number} filesSearched - Number of files searched
834
- * @returns {string} Formatted results
835
- * @private
836
- */
837
- formatResults(matches, errorFiles, notFoundFiles, filesSearched) {
838
- let output = '';
839
-
840
- // Report files not found
841
- if (notFoundFiles.length > 0) {
842
- output += 'FILES NOT FOUND:\n';
843
- for (const file of notFoundFiles) {
844
- output += ` - ${file}\n`;
845
- }
846
- output += '\n';
847
- }
848
-
849
- // Report files with errors
850
- if (errorFiles.length > 0) {
851
- output += 'FILES WITH ERRORS:\n';
852
- for (const file of errorFiles) {
853
- output += ` - ${file.filePath}: ${file.error}\n`;
854
- }
855
- output += '\n';
856
- }
857
-
858
- // Report search results
859
- if (matches.length === 0) {
860
- output += `No matches found for the specified search terms in ${filesSearched} file(s).\n`;
861
- } else {
862
- // Group by term
863
- const matchesByTerm = {};
864
- for (const match of matches) {
865
- if (!matchesByTerm[match.term]) {
866
- matchesByTerm[match.term] = [];
867
- }
868
- matchesByTerm[match.term].push(match);
869
- }
870
-
871
- output += `SEARCH RESULTS (${matches.length} total matches in ${filesSearched} file(s)):\n\n`;
872
-
873
- for (const [term, termMatches] of Object.entries(matchesByTerm)) {
874
- output += `Search term: "${term}" (${termMatches.length} matches)\n`;
875
-
876
- for (const match of termMatches) {
877
- output += ` ${match.filePath}:${match.lineNumber} - ${match.lineContent}\n`;
878
- }
879
-
880
- output += '\n';
881
- }
882
-
883
- // Add warning if max matches reached
884
- if (matches.length >= this.seekConfig.MAX_TOTAL_MATCHES) {
885
- output += `⚠️ Maximum matches limit reached (${this.seekConfig.MAX_TOTAL_MATCHES}). Some matches may not be shown.\n`;
886
- }
887
- }
888
-
889
- return output.trim();
890
- }
891
-
892
- /**
893
- * Get supported file extensions
894
- * @returns {Array<string>} Array of supported extensions
895
- */
896
- getSupportedExtensions() {
897
- return [
898
- '.js', '.jsx', '.ts', '.tsx',
899
- '.json', '.xml', '.html', '.css', '.scss', '.sass', '.less',
900
- '.md', '.txt', '.log',
901
- '.py', '.rb', '.java', '.go', '.rs',
902
- '.c', '.cpp', '.h', '.hpp',
903
- '.sh', '.bash', '.zsh',
904
- '.yml', '.yaml', '.toml', '.ini', '.conf'
905
- ];
906
- }
907
-
908
- /**
909
- * Resource cleanup
910
- * @param {string} operationId - Operation identifier
911
- */
912
- async cleanup(operationId) {
913
- // No persistent resources to clean up
914
- this.logger?.info('Seek tool cleanup completed', { operationId });
915
- }
916
- }
917
-
918
- export default SeekTool;
1
+ /**
2
+ * SeekTool - Search for content within project files
3
+ *
4
+ * Purpose:
5
+ * - Search for specific content/patterns within files
6
+ * - Support glob patterns for file paths
7
+ * - Verify imports, function usage, and variable references
8
+ * - Provide detailed match information with line numbers
9
+ */
10
+
11
+ import { BaseTool } from './baseTool.js';
12
+ import { promises as fs } from 'fs';
13
+ import path from 'path';
14
+
15
+ // Configuration constants
16
+ const SEEK_CONFIG = {
17
+ // File size limits
18
+ MAX_FILE_SIZE: 10 * 1024 * 1024, // 10MB - don't search files larger than this
19
+
20
+ // Search limits
21
+ MAX_FILES_PER_SEARCH: 1000, // Maximum files to search in one operation
22
+ MAX_MATCHES_PER_FILE: 100, // Maximum matches to return per file
23
+ MAX_TOTAL_MATCHES: 500, // Maximum total matches to return
24
+
25
+ // Performance limits
26
+ MAX_SEARCH_TERMS: 50, // Maximum number of search terms
27
+ MAX_FILE_PATHS: 100, // Maximum number of file path patterns
28
+
29
+ // Recursion limits
30
+ MAX_DIRECTORY_DEPTH: 20, // Maximum directory recursion depth
31
+
32
+ // Result formatting
33
+ MAX_LINE_CONTENT_LENGTH: 200, // Maximum line content to show in results
34
+ CONTEXT_LINES_BEFORE: 0, // Lines of context before match
35
+ CONTEXT_LINES_AFTER: 0, // Lines of context after match
36
+ };
37
+
38
+ // File encoding
39
+ const FILE_ENCODING = 'utf-8';
40
+
41
+ // Common binary file extensions to skip
42
+ const BINARY_EXTENSIONS = new Set([
43
+ '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.ico', '.svg',
44
+ '.pdf', '.zip', '.tar', '.gz', '.rar', '.7z',
45
+ '.exe', '.dll', '.so', '.dylib',
46
+ '.mp3', '.mp4', '.avi', '.mov', '.wmv',
47
+ '.woff', '.woff2', '.ttf', '.eot',
48
+ '.bin', '.dat', '.db', '.sqlite'
49
+ ]);
50
+
51
+ class SeekTool extends BaseTool {
52
+ constructor(config = {}, logger = null) {
53
+ super(config, logger);
54
+
55
+ // Tool metadata
56
+ this.requiresProject = true; // Requires project directory
57
+ this.isAsync = true; // File operations are async
58
+ this.timeout = config.timeout || 120000; // 2 minutes default
59
+
60
+ // Merge config with defaults
61
+ this.seekConfig = {
62
+ ...SEEK_CONFIG,
63
+ ...config.seekConfig
64
+ };
65
+ }
66
+
67
+ /**
68
+ * Get tool description for LLM consumption
69
+ * @returns {string} Tool description
70
+ */
71
+ getDescription() {
72
+ return `
73
+ Seek Tool: Search for specific content within project files. Ideal for verifying imports, function usage, and variable references.
74
+
75
+ USAGE:
76
+ \`\`\`json
77
+ {
78
+ "toolId": "seek",
79
+ "filePaths": ["src/**/*.js", "package.json"],
80
+ "searchTerms": ["import React", "useState"]
81
+ }
82
+ \`\`\`
83
+
84
+ PARAMETERS:
85
+ - filePaths: Array of file paths or glob patterns
86
+ - searchTerms: Array of search strings (case-sensitive, exact match)
87
+
88
+ GLOB PATTERNS:
89
+ - *.js - All .js files in directory
90
+ - **/*.js - All .js files recursively
91
+ - src/**/*.jsx - All .jsx files under src/
92
+
93
+ EXAMPLES:
94
+
95
+ Find function usage:
96
+ \`\`\`json
97
+ {
98
+ "toolId": "seek",
99
+ "filePaths": ["src/components/*.js", "src/utils/helpers.js"],
100
+ "searchTerms": ["function handleSubmit", "useState("]
101
+ }
102
+ \`\`\`
103
+
104
+ Verify imports:
105
+ \`\`\`json
106
+ {
107
+ "toolId": "seek",
108
+ "filePaths": ["src/components/Header.js"],
109
+ "searchTerms": ["import React", "from 'react'"]
110
+ }
111
+ \`\`\`
112
+
113
+ Search all JS files:
114
+ \`\`\`json
115
+ {
116
+ "toolId": "seek",
117
+ "filePaths": ["src/**/*.js"],
118
+ "searchTerms": ["apiClient", "config.apiUrl"]
119
+ }
120
+ \`\`\`
121
+
122
+ TIP: After finding matches, use code-map read-range to view surrounding context without reading entire files.
123
+
124
+ LIMITS:
125
+ - Max ${SEEK_CONFIG.MAX_FILES_PER_SEARCH} files per search
126
+ - Max ${SEEK_CONFIG.MAX_SEARCH_TERMS} search terms
127
+ - Max ${SEEK_CONFIG.MAX_FILE_SIZE / (1024 * 1024)}MB per file
128
+ - Max ${SEEK_CONFIG.MAX_TOTAL_MATCHES} matches total
129
+ `;
130
+ }
131
+
132
+ /**
133
+ * Parse parameters from tool command content
134
+ * @param {string} content - Raw tool command content
135
+ * @returns {Object} Parsed parameters
136
+ */
137
+ parseParameters(content) {
138
+ try {
139
+ // Try JSON first
140
+ if (content.trim().startsWith('{')) {
141
+ const parsed = JSON.parse(content);
142
+
143
+ return {
144
+ filePaths: parsed.filePaths || [],
145
+ searchTerms: parsed.searchTerms || []
146
+ };
147
+ }
148
+
149
+ // XML parsing
150
+ const params = {
151
+ filePaths: [],
152
+ searchTerms: []
153
+ };
154
+
155
+ // Extract in-files content
156
+ const inFilesPattern = /<in-files>([\s\S]*?)<\/in-files>/i;
157
+ const inFilesMatch = inFilesPattern.exec(content);
158
+
159
+ if (inFilesMatch) {
160
+ const filesContent = inFilesMatch[1];
161
+ params.filePaths = filesContent
162
+ .split('\n')
163
+ .map(line => line.trim())
164
+ .filter(line => line.length > 0);
165
+ }
166
+
167
+ // Extract search-terms content
168
+ const searchTermsPattern = /<search-terms>([\s\S]*?)<\/search-terms>/i;
169
+ const searchTermsMatch = searchTermsPattern.exec(content);
170
+
171
+ if (searchTermsMatch) {
172
+ const termsContent = searchTermsMatch[1];
173
+
174
+ // Extract individual <term> tags
175
+ const termPattern = /<term>(.*?)<\/term>/gi;
176
+ let termMatch;
177
+
178
+ while ((termMatch = termPattern.exec(termsContent)) !== null) {
179
+ const term = termMatch[1].trim();
180
+ if (term.length > 0) {
181
+ params.searchTerms.push(term);
182
+ }
183
+ }
184
+ }
185
+
186
+ return params;
187
+
188
+ } catch (error) {
189
+ this.logger?.error('Failed to parse seek parameters', { error: error.message });
190
+ return {
191
+ filePaths: [],
192
+ searchTerms: [],
193
+ parseError: error.message
194
+ };
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Get required parameters
200
+ * @returns {Array<string>} Array of required parameter names
201
+ */
202
+ getRequiredParameters() {
203
+ return ['filePaths', 'searchTerms'];
204
+ }
205
+
206
+ /**
207
+ * Custom parameter validation
208
+ * @param {Object} params - Parameters to validate
209
+ * @returns {Object} Validation result
210
+ */
211
+ customValidateParameters(params) {
212
+ const errors = [];
213
+
214
+ // Validate filePaths
215
+ if (!params.filePaths || !Array.isArray(params.filePaths) || params.filePaths.length === 0) {
216
+ errors.push('At least one file path is required');
217
+ } else if (params.filePaths.length > this.seekConfig.MAX_FILE_PATHS) {
218
+ errors.push(`Too many file paths (max ${this.seekConfig.MAX_FILE_PATHS})`);
219
+ }
220
+
221
+ // Validate searchTerms
222
+ if (!params.searchTerms || !Array.isArray(params.searchTerms) || params.searchTerms.length === 0) {
223
+ errors.push('At least one search term is required');
224
+ } else if (params.searchTerms.length > this.seekConfig.MAX_SEARCH_TERMS) {
225
+ errors.push(`Too many search terms (max ${this.seekConfig.MAX_SEARCH_TERMS})`);
226
+ }
227
+
228
+ // Validate search terms are non-empty strings
229
+ if (params.searchTerms && Array.isArray(params.searchTerms)) {
230
+ for (const [index, term] of params.searchTerms.entries()) {
231
+ if (typeof term !== 'string' || term.trim().length === 0) {
232
+ errors.push(`Search term ${index + 1}: must be a non-empty string`);
233
+ }
234
+ }
235
+ }
236
+
237
+ // Validate file paths are non-empty strings
238
+ if (params.filePaths && Array.isArray(params.filePaths)) {
239
+ for (const [index, filePath] of params.filePaths.entries()) {
240
+ if (typeof filePath !== 'string' || filePath.trim().length === 0) {
241
+ errors.push(`File path ${index + 1}: must be a non-empty string`);
242
+ }
243
+
244
+ // Check for path traversal attempts
245
+ if (filePath.includes('..')) {
246
+ errors.push(`File path ${index + 1}: path traversal (..) not allowed for security`);
247
+ }
248
+ }
249
+ }
250
+
251
+ return {
252
+ valid: errors.length === 0,
253
+ errors
254
+ };
255
+ }
256
+
257
+ /**
258
+ * Execute tool with parsed parameters
259
+ * @param {Object} params - Parsed parameters
260
+ * @param {Object} context - Execution context
261
+ * @returns {Promise<Object>} Execution result
262
+ */
263
+ async execute(params, context) {
264
+ // Forgiving normalization — production agents (gpt-5.4, Kimi-K2.6,
265
+ // gpt-4.1-mini) reliably call seek with the natural-feeling shape
266
+ // `{query, filePattern}` instead of the documented
267
+ // `{searchTerms: [], filePaths: []}`. Live trace evidence:
268
+ // agent-agent-v2-v2-s2-multi-topic-1779570320941 hit
269
+ // "Cannot read properties of undefined (reading 'length')" 3 times
270
+ // and cascaded into task drift. Accept both shapes — coerce
271
+ // singular strings to arrays, alternate names to canonical.
272
+ const filePathsRaw = params.filePaths != null ? params.filePaths
273
+ : params.filePath != null ? params.filePath
274
+ : params.filePattern != null ? params.filePattern
275
+ : params.paths != null ? params.paths
276
+ : null;
277
+ const searchTermsRaw = params.searchTerms != null ? params.searchTerms
278
+ : params.searchTerm != null ? params.searchTerm
279
+ : params.query != null ? params.query
280
+ : params.terms != null ? params.terms
281
+ : null;
282
+ const filePaths = Array.isArray(filePathsRaw) ? filePathsRaw
283
+ : (filePathsRaw != null && filePathsRaw !== '') ? [filePathsRaw] : [];
284
+ const searchTerms = Array.isArray(searchTermsRaw) ? searchTermsRaw
285
+ : (searchTermsRaw != null && searchTermsRaw !== '') ? [searchTermsRaw] : [];
286
+
287
+ if (filePaths.length === 0) {
288
+ return {
289
+ success: false,
290
+ error: 'seek requires `filePaths` (array of glob patterns, e.g. ["src/**/*.js"]). Singular `filePath` or `filePattern` also accepted.',
291
+ };
292
+ }
293
+ if (searchTerms.length === 0) {
294
+ return {
295
+ success: false,
296
+ error: 'seek requires `searchTerms` (array of strings to find). Singular `searchTerm` or `query` also accepted.',
297
+ };
298
+ }
299
+
300
+ const { projectDir, agentId, directoryAccess } = context;
301
+
302
+ // IMPORTANT: Get all accessible directories for the agent
303
+ // This includes workingDirectory, readOnlyDirectories, and writeEnabledDirectories
304
+ let workingDirectory = projectDir || process.cwd();
305
+ let accessibleDirectories = [workingDirectory];
306
+
307
+ if (directoryAccess && directoryAccess.workingDirectory) {
308
+ workingDirectory = directoryAccess.workingDirectory;
309
+
310
+ // Collect all accessible directories (for read operations)
311
+ accessibleDirectories = this.getAllAccessibleDirectories(directoryAccess);
312
+
313
+ this.logger?.info('Using agent configured directory access', {
314
+ workingDirectory: directoryAccess.workingDirectory,
315
+ totalAccessibleDirs: accessibleDirectories.length,
316
+ readOnlyDirs: directoryAccess.readOnlyDirectories?.length || 0,
317
+ writeEnabledDirs: directoryAccess.writeEnabledDirectories?.length || 0,
318
+ agentId
319
+ });
320
+ }
321
+
322
+ if (!workingDirectory) {
323
+ throw new Error('Project directory is required for seek tool');
324
+ }
325
+
326
+ this.logger?.info('Executing seek tool', {
327
+ filePathCount: filePaths.length,
328
+ searchTermCount: searchTerms.length,
329
+ workingDirectory,
330
+ accessibleDirectories: accessibleDirectories.length,
331
+ agentId
332
+ });
333
+
334
+ try {
335
+ // Resolve file paths (expand globs)
336
+ // Pass accessible directories for validation (if agent has directoryAccess configured)
337
+ const resolveResult = await this.resolveFilePaths(
338
+ filePaths,
339
+ workingDirectory,
340
+ directoryAccess ? accessibleDirectories : null
341
+ );
342
+
343
+ const resolvedFiles = resolveResult.found;
344
+ const notFoundFiles = resolveResult.notFound;
345
+
346
+ // Check file count limit
347
+ if (resolvedFiles.length > this.seekConfig.MAX_FILES_PER_SEARCH) {
348
+ return {
349
+ success: false,
350
+ error: `Too many files to search (${resolvedFiles.length}). Maximum is ${this.seekConfig.MAX_FILES_PER_SEARCH}. Use more specific file patterns.`,
351
+ filesResolved: resolvedFiles.length,
352
+ filesNotFound: notFoundFiles.length
353
+ };
354
+ }
355
+
356
+ // Search in files
357
+ const searchResult = await this.searchFiles(resolvedFiles, searchTerms, workingDirectory);
358
+
359
+ // Format results
360
+ const formattedResults = this.formatResults(
361
+ searchResult.matches,
362
+ searchResult.errorFiles,
363
+ notFoundFiles,
364
+ resolvedFiles.length
365
+ );
366
+
367
+ return {
368
+ success: true,
369
+ filesSearched: resolvedFiles.length,
370
+ filesNotFound: notFoundFiles.length,
371
+ filesWithErrors: searchResult.errorFiles.length,
372
+ totalMatches: searchResult.matches.length,
373
+ matchesByTerm: searchResult.matchesByTerm,
374
+ formattedResults,
375
+ toolUsed: 'seek',
376
+ guidance: searchResult.matches.length > 0
377
+ ? 'To view code context around matches, use code-map read-range with the file path and line numbers from above. Example: {"toolId":"code-map","parameters":{"action":"read-range","filePath":"<matched-file>","startLine":<line-5>,"endLine":<line+25>}}'
378
+ : undefined
379
+ };
380
+
381
+ } catch (error) {
382
+ this.logger?.error('Seek tool execution failed', { error: error.message });
383
+ throw error;
384
+ }
385
+ }
386
+
387
+ /**
388
+ * Get all accessible directories for read operations
389
+ * @param {Object} directoryAccess - Directory access configuration
390
+ * @returns {Array<string>} Array of accessible directory paths
391
+ * @private
392
+ */
393
+ getAllAccessibleDirectories(directoryAccess) {
394
+ const directories = new Set();
395
+
396
+ // Add working directory
397
+ if (directoryAccess.workingDirectory) {
398
+ directories.add(directoryAccess.workingDirectory);
399
+ }
400
+
401
+ // Add read-only directories
402
+ if (directoryAccess.readOnlyDirectories && Array.isArray(directoryAccess.readOnlyDirectories)) {
403
+ for (const dir of directoryAccess.readOnlyDirectories) {
404
+ directories.add(dir);
405
+ }
406
+ }
407
+
408
+ // Add write-enabled directories (if you can write, you can read)
409
+ if (directoryAccess.writeEnabledDirectories && Array.isArray(directoryAccess.writeEnabledDirectories)) {
410
+ for (const dir of directoryAccess.writeEnabledDirectories) {
411
+ directories.add(dir);
412
+ }
413
+ }
414
+
415
+ return Array.from(directories);
416
+ }
417
+
418
+ /**
419
+ * Check if a path is within any accessible directory
420
+ * @param {string} targetPath - Path to check
421
+ * @param {Array<string>} accessibleDirs - Array of accessible directories
422
+ * @returns {boolean} True if path is accessible
423
+ * @private
424
+ */
425
+ isPathAccessible(targetPath, accessibleDirs) {
426
+ for (const dir of accessibleDirs) {
427
+ const relative = path.relative(dir, targetPath);
428
+ const isWithin = !relative.startsWith('..') && !path.isAbsolute(relative);
429
+ if (isWithin) {
430
+ return true;
431
+ }
432
+ }
433
+ return false;
434
+ }
435
+
436
+ /**
437
+ * Resolve file paths, expanding glob patterns
438
+ * @param {Array<string>} filePaths - File paths with possible glob patterns
439
+ * @param {string} projectDir - Project directory
440
+ * @param {Array<string>} accessibleDirs - Optional array of accessible directories
441
+ * @returns {Promise<Object>} Object with found and notFound arrays
442
+ * @private
443
+ */
444
+ async resolveFilePaths(filePaths, projectDir, accessibleDirs = null) {
445
+ const result = {
446
+ found: [],
447
+ notFound: []
448
+ };
449
+
450
+ for (const filePath of filePaths) {
451
+ const normalizedPath = filePath.trim();
452
+
453
+ if (normalizedPath.includes('*')) {
454
+ // Handle glob patterns
455
+ const globResult = await this.expandGlobPattern(normalizedPath, projectDir);
456
+
457
+ if (globResult.found.length > 0) {
458
+ result.found.push(...globResult.found);
459
+ } else {
460
+ result.notFound.push(`${normalizedPath} (no matching files)`);
461
+ }
462
+ } else {
463
+ // Handle direct file reference
464
+ const absolutePath = path.isAbsolute(normalizedPath)
465
+ ? normalizedPath
466
+ : path.resolve(projectDir, normalizedPath);
467
+
468
+ // Check if path is within accessible directories (if configured)
469
+ if (accessibleDirs && accessibleDirs.length > 0) {
470
+ if (!this.isPathAccessible(absolutePath, accessibleDirs)) {
471
+ result.notFound.push(`${normalizedPath} (not in accessible directories)`);
472
+ continue;
473
+ }
474
+ }
475
+
476
+ try {
477
+ const stats = await fs.stat(absolutePath);
478
+
479
+ if (stats.isFile()) {
480
+ result.found.push(absolutePath);
481
+ } else if (stats.isDirectory()) {
482
+ result.notFound.push(`${normalizedPath} (is a directory, not a file)`);
483
+ } else {
484
+ result.notFound.push(`${normalizedPath} (not a regular file)`);
485
+ }
486
+ } catch (error) {
487
+ result.notFound.push(`${normalizedPath} (${error.code || error.message})`);
488
+ }
489
+ }
490
+ }
491
+
492
+ return result;
493
+ }
494
+
495
+ /**
496
+ * Expand glob pattern to matching file paths
497
+ * @param {string} pattern - Glob pattern
498
+ * @param {string} projectDir - Project directory
499
+ * @returns {Promise<Object>} Object with found files
500
+ * @private
501
+ */
502
+ async expandGlobPattern(pattern, projectDir) {
503
+ const result = { found: [] };
504
+
505
+ // Handle recursive pattern: src/**/*.js
506
+ if (pattern.includes('**/')) {
507
+ const [baseDir, filePattern] = pattern.split('**/');
508
+ const basePath = path.resolve(projectDir, baseDir);
509
+
510
+ try {
511
+ const stats = await fs.stat(basePath);
512
+
513
+ if (stats.isDirectory()) {
514
+ await this.findFilesRecursively(
515
+ basePath,
516
+ filePattern,
517
+ result.found,
518
+ 0,
519
+ this.seekConfig.MAX_DIRECTORY_DEPTH
520
+ );
521
+ }
522
+ } catch (error) {
523
+ // Directory doesn't exist
524
+ this.logger?.warn('Base directory not found for glob pattern', { basePath, error: error.message });
525
+ }
526
+ }
527
+ // Handle simple pattern: src/*.js
528
+ else if (pattern.includes('*')) {
529
+ const dirPath = path.dirname(path.resolve(projectDir, pattern));
530
+ const filePattern = path.basename(pattern);
531
+
532
+ try {
533
+ const stats = await fs.stat(dirPath);
534
+
535
+ if (stats.isDirectory()) {
536
+ const files = await fs.readdir(dirPath);
537
+
538
+ for (const file of files) {
539
+ const filePath = path.join(dirPath, file);
540
+
541
+ try {
542
+ const fileStats = await fs.stat(filePath);
543
+
544
+ if (fileStats.isFile() && this.matchesPattern(file, filePattern)) {
545
+ result.found.push(filePath);
546
+ }
547
+ } catch {
548
+ // Skip files we can't stat
549
+ continue;
550
+ }
551
+ }
552
+ }
553
+ } catch (error) {
554
+ // Directory doesn't exist
555
+ this.logger?.warn('Directory not found for glob pattern', { dirPath, error: error.message });
556
+ }
557
+ }
558
+
559
+ return result;
560
+ }
561
+
562
+ /**
563
+ * Find files recursively matching a pattern
564
+ * @param {string} dir - Directory to search
565
+ * @param {string} filePattern - File pattern to match
566
+ * @param {Array<string>} results - Results array
567
+ * @param {number} currentDepth - Current recursion depth
568
+ * @param {number} maxDepth - Maximum recursion depth
569
+ * @returns {Promise<void>}
570
+ * @private
571
+ */
572
+ async findFilesRecursively(dir, filePattern, results, currentDepth, maxDepth) {
573
+ // Prevent infinite recursion
574
+ if (currentDepth >= maxDepth) {
575
+ this.logger?.warn('Maximum directory depth reached', { dir, currentDepth });
576
+ return;
577
+ }
578
+
579
+ try {
580
+ const entries = await fs.readdir(dir, { withFileTypes: true });
581
+
582
+ for (const entry of entries) {
583
+ // Skip hidden files and directories
584
+ if (entry.name.startsWith('.')) {
585
+ continue;
586
+ }
587
+
588
+ const fullPath = path.join(dir, entry.name);
589
+
590
+ try {
591
+ if (entry.isDirectory()) {
592
+ // Skip common large directories
593
+ if (this.shouldSkipDirectory(entry.name)) {
594
+ continue;
595
+ }
596
+
597
+ await this.findFilesRecursively(
598
+ fullPath,
599
+ filePattern,
600
+ results,
601
+ currentDepth + 1,
602
+ maxDepth
603
+ );
604
+ } else if (entry.isFile()) {
605
+ if (this.matchesPattern(entry.name, filePattern)) {
606
+ results.push(fullPath);
607
+
608
+ // Stop if we've found too many files
609
+ if (results.length >= this.seekConfig.MAX_FILES_PER_SEARCH) {
610
+ return;
611
+ }
612
+ }
613
+ }
614
+ } catch {
615
+ // Skip entries we can't access
616
+ continue;
617
+ }
618
+ }
619
+ } catch (error) {
620
+ this.logger?.warn('Error reading directory', { dir, error: error.message });
621
+ }
622
+ }
623
+
624
+ /**
625
+ * Check if filename matches a wildcard pattern
626
+ * @param {string} filename - Filename to check
627
+ * @param {string} pattern - Wildcard pattern
628
+ * @returns {boolean} True if matches
629
+ * @private
630
+ */
631
+ matchesPattern(filename, pattern) {
632
+ // Simple wildcard matching
633
+ const regexPattern = pattern
634
+ .split('*')
635
+ .map(part => this.escapeRegExp(part))
636
+ .join('.*');
637
+
638
+ const regex = new RegExp(`^${regexPattern}$`, 'i');
639
+ return regex.test(filename);
640
+ }
641
+
642
+ /**
643
+ * Escape special regex characters
644
+ * @param {string} string - String to escape
645
+ * @returns {string} Escaped string
646
+ * @private
647
+ */
648
+ escapeRegExp(string) {
649
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
650
+ }
651
+
652
+ /**
653
+ * Check if directory should be skipped during recursive search
654
+ * @param {string} dirName - Directory name
655
+ * @returns {boolean} True if should skip
656
+ * @private
657
+ */
658
+ shouldSkipDirectory(dirName) {
659
+ const skipDirs = new Set([
660
+ 'node_modules',
661
+ '.git',
662
+ 'dist',
663
+ 'build',
664
+ 'coverage',
665
+ '.next',
666
+ '.nuxt',
667
+ 'out',
668
+ 'target',
669
+ 'vendor',
670
+ '__pycache__',
671
+ '.cache',
672
+ 'tmp',
673
+ 'temp'
674
+ ]);
675
+
676
+ return skipDirs.has(dirName.toLowerCase());
677
+ }
678
+
679
+ /**
680
+ * Check if file should be skipped (binary files)
681
+ * @param {string} filePath - File path
682
+ * @returns {boolean} True if should skip
683
+ * @private
684
+ */
685
+ shouldSkipFile(filePath) {
686
+ const ext = path.extname(filePath).toLowerCase();
687
+ return BINARY_EXTENSIONS.has(ext);
688
+ }
689
+
690
+ /**
691
+ * Search for terms in multiple files
692
+ * @param {Array<string>} filePaths - File paths to search
693
+ * @param {Array<string>} searchTerms - Search terms
694
+ * @param {string} projectDir - Project directory for relative paths
695
+ * @returns {Promise<Object>} Search results
696
+ * @private
697
+ */
698
+ async searchFiles(filePaths, searchTerms, projectDir) {
699
+ const allMatches = [];
700
+ const errorFiles = [];
701
+ let totalMatches = 0;
702
+
703
+ for (const filePath of filePaths) {
704
+ // Skip binary files
705
+ if (this.shouldSkipFile(filePath)) {
706
+ continue;
707
+ }
708
+
709
+ try {
710
+ // Check file size
711
+ const stats = await fs.stat(filePath);
712
+
713
+ if (stats.size > this.seekConfig.MAX_FILE_SIZE) {
714
+ errorFiles.push({
715
+ filePath: path.relative(projectDir, filePath),
716
+ error: `File too large (${Math.round(stats.size / (1024 * 1024))}MB, max ${Math.round(this.seekConfig.MAX_FILE_SIZE / (1024 * 1024))}MB)`
717
+ });
718
+ continue;
719
+ }
720
+
721
+ // Search in file
722
+ const matches = await this.searchInFile(filePath, searchTerms, projectDir);
723
+
724
+ allMatches.push(...matches);
725
+ totalMatches += matches.length;
726
+
727
+ // Stop if we've exceeded max total matches
728
+ if (totalMatches >= this.seekConfig.MAX_TOTAL_MATCHES) {
729
+ this.logger?.warn('Maximum total matches reached', { totalMatches });
730
+ break;
731
+ }
732
+
733
+ } catch (error) {
734
+ errorFiles.push({
735
+ filePath: path.relative(projectDir, filePath),
736
+ error: error.message
737
+ });
738
+ }
739
+ }
740
+
741
+ // Group matches by search term
742
+ const matchesByTerm = {};
743
+
744
+ for (const match of allMatches) {
745
+ if (!matchesByTerm[match.term]) {
746
+ matchesByTerm[match.term] = [];
747
+ }
748
+
749
+ matchesByTerm[match.term].push({
750
+ filePath: match.filePath,
751
+ lineNumber: match.lineNumber,
752
+ lineContent: match.lineContent
753
+ });
754
+ }
755
+
756
+ return {
757
+ matches: allMatches,
758
+ matchesByTerm,
759
+ errorFiles
760
+ };
761
+ }
762
+
763
+ /**
764
+ * Search for terms in a single file
765
+ * @param {string} filePath - File path
766
+ * @param {Array<string>} searchTerms - Search terms
767
+ * @param {string} projectDir - Project directory for relative paths
768
+ * @returns {Promise<Array<Object>>} Matches found
769
+ * @private
770
+ */
771
+ async searchInFile(filePath, searchTerms, projectDir) {
772
+ const matches = [];
773
+ let matchesInFile = 0;
774
+
775
+ try {
776
+ // Read file content
777
+ const content = await fs.readFile(filePath, FILE_ENCODING);
778
+
779
+ // Split into lines
780
+ const lines = content.split('\n');
781
+ const relativePath = path.relative(projectDir, filePath);
782
+
783
+ // Search each line
784
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
785
+ const line = lines[lineIndex];
786
+ const lineNumber = lineIndex + 1; // 1-based line numbers
787
+
788
+ // Check each search term
789
+ for (const term of searchTerms) {
790
+ if (line.includes(term)) {
791
+ // Truncate line content if too long
792
+ let lineContent = line;
793
+ if (lineContent.length > this.seekConfig.MAX_LINE_CONTENT_LENGTH) {
794
+ const termIndex = lineContent.indexOf(term);
795
+ const start = Math.max(0, termIndex - 50);
796
+ const end = Math.min(lineContent.length, termIndex + term.length + 50);
797
+ lineContent = (start > 0 ? '...' : '') +
798
+ lineContent.substring(start, end) +
799
+ (end < lineContent.length ? '...' : '');
800
+ }
801
+
802
+ matches.push({
803
+ term,
804
+ filePath: relativePath,
805
+ lineNumber,
806
+ lineContent: lineContent.trim()
807
+ });
808
+
809
+ matchesInFile++;
810
+
811
+ // Limit matches per file
812
+ if (matchesInFile >= this.seekConfig.MAX_MATCHES_PER_FILE) {
813
+ this.logger?.warn('Maximum matches per file reached', { filePath: relativePath });
814
+ return matches;
815
+ }
816
+ }
817
+ }
818
+ }
819
+
820
+ } catch (error) {
821
+ throw new Error(`Failed to read file: ${error.message}`, { cause: error });
822
+ }
823
+
824
+ return matches;
825
+ }
826
+
827
+ /**
828
+ * Format search results for display
829
+ * @param {Array<Object>} matches - Search matches
830
+ * @param {Array<Object>} errorFiles - Files with errors
831
+ * @param {Array<string>} notFoundFiles - Files not found
832
+ * @param {number} filesSearched - Number of files searched
833
+ * @returns {string} Formatted results
834
+ * @private
835
+ */
836
+ formatResults(matches, errorFiles, notFoundFiles, filesSearched) {
837
+ let output = '';
838
+
839
+ // Report files not found
840
+ if (notFoundFiles.length > 0) {
841
+ output += 'FILES NOT FOUND:\n';
842
+ for (const file of notFoundFiles) {
843
+ output += ` - ${file}\n`;
844
+ }
845
+ output += '\n';
846
+ }
847
+
848
+ // Report files with errors
849
+ if (errorFiles.length > 0) {
850
+ output += 'FILES WITH ERRORS:\n';
851
+ for (const file of errorFiles) {
852
+ output += ` - ${file.filePath}: ${file.error}\n`;
853
+ }
854
+ output += '\n';
855
+ }
856
+
857
+ // Report search results
858
+ if (matches.length === 0) {
859
+ output += `No matches found for the specified search terms in ${filesSearched} file(s).\n`;
860
+ } else {
861
+ // Group by term
862
+ const matchesByTerm = {};
863
+ for (const match of matches) {
864
+ if (!matchesByTerm[match.term]) {
865
+ matchesByTerm[match.term] = [];
866
+ }
867
+ matchesByTerm[match.term].push(match);
868
+ }
869
+
870
+ output += `SEARCH RESULTS (${matches.length} total matches in ${filesSearched} file(s)):\n\n`;
871
+
872
+ for (const [term, termMatches] of Object.entries(matchesByTerm)) {
873
+ output += `Search term: "${term}" (${termMatches.length} matches)\n`;
874
+
875
+ for (const match of termMatches) {
876
+ output += ` ${match.filePath}:${match.lineNumber} - ${match.lineContent}\n`;
877
+ }
878
+
879
+ output += '\n';
880
+ }
881
+
882
+ // Add warning if max matches reached
883
+ if (matches.length >= this.seekConfig.MAX_TOTAL_MATCHES) {
884
+ output += `⚠️ Maximum matches limit reached (${this.seekConfig.MAX_TOTAL_MATCHES}). Some matches may not be shown.\n`;
885
+ }
886
+ }
887
+
888
+ return output.trim();
889
+ }
890
+
891
+ /**
892
+ * Get supported file extensions
893
+ * @returns {Array<string>} Array of supported extensions
894
+ */
895
+ getSupportedExtensions() {
896
+ return [
897
+ '.js', '.jsx', '.ts', '.tsx',
898
+ '.json', '.xml', '.html', '.css', '.scss', '.sass', '.less',
899
+ '.md', '.txt', '.log',
900
+ '.py', '.rb', '.java', '.go', '.rs',
901
+ '.c', '.cpp', '.h', '.hpp',
902
+ '.sh', '.bash', '.zsh',
903
+ '.yml', '.yaml', '.toml', '.ini', '.conf'
904
+ ];
905
+ }
906
+
907
+ /**
908
+ * Resource cleanup
909
+ * @param {string} operationId - Operation identifier
910
+ */
911
+ async cleanup(operationId) {
912
+ // No persistent resources to clean up
913
+ this.logger?.info('Seek tool cleanup completed', { operationId });
914
+ }
915
+ }
916
+
917
+ export default SeekTool;