explorbot 0.0.1 → 0.1.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 (423) hide show
  1. package/README.md +80 -26
  2. package/bin/explorbot-cli.ts +680 -0
  3. package/boat/api-tester/src/ai/chief/styles.ts +15 -0
  4. package/boat/api-tester/src/ai/chief.ts +335 -0
  5. package/boat/api-tester/src/ai/curler-tools.ts +278 -0
  6. package/boat/api-tester/src/ai/curler.ts +306 -0
  7. package/boat/api-tester/src/api-client.ts +28 -0
  8. package/boat/api-tester/src/apibot.ts +203 -0
  9. package/boat/api-tester/src/cli.ts +301 -0
  10. package/boat/api-tester/src/config.ts +190 -0
  11. package/dist/bin/explorbot-cli.js +23 -101
  12. package/dist/boat/api-tester/bin/apibot-cli.js +0 -1
  13. package/dist/boat/api-tester/src/ai/chief/styles.js +0 -1
  14. package/dist/boat/api-tester/src/ai/chief.js +0 -1
  15. package/dist/boat/api-tester/src/ai/curler-tools.js +0 -1
  16. package/dist/boat/api-tester/src/ai/curler.js +0 -1
  17. package/dist/boat/api-tester/src/api-client.js +0 -1
  18. package/dist/boat/api-tester/src/apibot.js +0 -1
  19. package/dist/boat/api-tester/src/cli.js +0 -1
  20. package/dist/boat/api-tester/src/config.js +0 -1
  21. package/dist/src/action-result.js +0 -1
  22. package/dist/src/action.js +14 -12
  23. package/dist/src/activity.js +0 -1
  24. package/dist/src/ai/agent.js +0 -1
  25. package/dist/src/ai/bosun.js +0 -1
  26. package/dist/src/ai/captain/idle-mode.js +0 -1
  27. package/dist/src/ai/captain/mixin.js +0 -1
  28. package/dist/src/ai/captain/test-mode.js +0 -1
  29. package/dist/src/ai/captain/web-mode.js +0 -1
  30. package/dist/src/ai/captain.js +0 -1
  31. package/dist/src/ai/conversation.js +0 -1
  32. package/dist/src/ai/experience-compactor.js +0 -1
  33. package/dist/src/ai/fisherman-tools.js +0 -1
  34. package/dist/src/ai/fisherman.js +0 -1
  35. package/dist/src/ai/historian.js +0 -1
  36. package/dist/src/ai/navigator.js +0 -1
  37. package/dist/src/ai/pilot.js +0 -1
  38. package/dist/src/ai/planner/session-dedup.js +0 -1
  39. package/dist/src/ai/planner/styles.js +0 -1
  40. package/dist/src/ai/planner/subpages.js +42 -7
  41. package/dist/src/ai/planner.js +15 -4
  42. package/dist/src/ai/provider.js +0 -1
  43. package/dist/src/ai/quartermaster.js +0 -1
  44. package/dist/src/ai/researcher/cache.js +13 -9
  45. package/dist/src/ai/researcher/coordinates.js +4 -3
  46. package/dist/src/ai/researcher/deep-analysis.js +16 -20
  47. package/dist/src/ai/researcher/fingerprint-worker.js +0 -1
  48. package/dist/src/ai/researcher/focus.js +0 -1
  49. package/dist/src/ai/researcher/locators.js +1 -2
  50. package/dist/src/ai/researcher/mixin.js +0 -1
  51. package/dist/src/ai/researcher/parser.js +4 -4
  52. package/dist/src/ai/researcher/research-result.js +2 -1
  53. package/dist/src/ai/researcher.js +6 -6
  54. package/dist/src/ai/rules.js +0 -1
  55. package/dist/src/ai/task-agent.js +0 -1
  56. package/dist/src/ai/tester.js +0 -1
  57. package/dist/src/ai/tools.js +4 -1
  58. package/dist/src/api/api-client.js +0 -1
  59. package/dist/src/api/request-result.js +0 -1
  60. package/dist/src/api/request-store.js +0 -1
  61. package/dist/src/api/spec-reader.js +0 -1
  62. package/dist/src/api/xhr-capture.js +0 -1
  63. package/dist/src/browser-server.js +0 -1
  64. package/dist/src/command-handler.js +0 -1
  65. package/dist/src/commands/add-rule-command.js +0 -1
  66. package/dist/src/commands/base-command.js +0 -1
  67. package/dist/src/commands/clean-command.js +0 -1
  68. package/dist/src/commands/context-aria-command.js +0 -1
  69. package/dist/src/commands/context-command.js +2 -3
  70. package/dist/src/commands/context-data-command.js +0 -1
  71. package/dist/src/commands/context-experience-command.js +0 -1
  72. package/dist/src/commands/context-html-command.js +0 -1
  73. package/dist/src/commands/context-knowledge-command.js +0 -1
  74. package/dist/src/commands/debug-command.js +0 -1
  75. package/dist/src/commands/drill-command.js +0 -1
  76. package/dist/src/commands/exit-command.js +0 -1
  77. package/dist/src/commands/explore-command.js +3 -3
  78. package/dist/src/commands/freesail-command.js +0 -1
  79. package/dist/src/commands/help-command.js +0 -1
  80. package/dist/src/commands/index.js +0 -1
  81. package/dist/src/commands/init-command.js +117 -0
  82. package/dist/src/commands/knows-command.js +0 -1
  83. package/dist/src/commands/learn-command.js +0 -1
  84. package/dist/src/commands/navigate-command.js +0 -1
  85. package/dist/src/commands/path-command.js +0 -1
  86. package/dist/src/commands/plan-clear-command.js +0 -1
  87. package/dist/src/commands/plan-command.js +6 -2
  88. package/dist/src/commands/plan-edit-command.js +0 -1
  89. package/dist/src/commands/plan-load-command.js +0 -1
  90. package/dist/src/commands/plan-reload-command.js +0 -1
  91. package/dist/src/commands/plan-save-command.js +0 -1
  92. package/dist/src/commands/research-command.js +0 -1
  93. package/dist/src/commands/start-command.js +0 -1
  94. package/dist/src/commands/status-command.js +0 -1
  95. package/dist/src/commands/test-command.js +0 -1
  96. package/dist/src/components/ActivityPane.js +0 -1
  97. package/dist/src/components/AddKnowledge.js +0 -1
  98. package/dist/src/components/AddRule.js +0 -1
  99. package/dist/src/components/App.js +0 -1
  100. package/dist/src/components/Autocomplete.js +0 -1
  101. package/dist/src/components/InputPane.js +0 -1
  102. package/dist/src/components/InputReadline.js +0 -1
  103. package/dist/src/components/LogPane.js +0 -1
  104. package/dist/src/components/PlanEditor.js +0 -1
  105. package/dist/src/components/PlanPane.js +0 -1
  106. package/dist/src/components/SessionTimer.js +0 -1
  107. package/dist/src/components/StateTransitionPane.js +0 -1
  108. package/dist/src/components/StatusPane.js +0 -1
  109. package/dist/src/components/TaskPane.js +0 -1
  110. package/dist/src/components/Welcome.js +0 -1
  111. package/dist/src/components/WelcomeChecklist.js +0 -1
  112. package/dist/src/components/WelcomeCommands.js +0 -1
  113. package/dist/src/components/autocomplete-store.js +0 -1
  114. package/dist/src/components/parse-keypress.js +0 -1
  115. package/dist/src/config.js +0 -1
  116. package/dist/src/execution-controller.js +0 -1
  117. package/dist/src/experience-tracker.js +0 -1
  118. package/dist/src/explorbot.js +1 -2
  119. package/dist/src/explorer.js +58 -17
  120. package/dist/src/index.js +0 -1
  121. package/dist/src/knowledge-tracker.js +2 -2
  122. package/dist/src/observability.js +0 -1
  123. package/dist/src/reporter.js +0 -1
  124. package/dist/src/state-manager.js +0 -1
  125. package/dist/src/stats.js +0 -1
  126. package/dist/src/test-plan.js +0 -1
  127. package/dist/src/utils/aria.js +0 -1
  128. package/dist/src/utils/cli-name.js +16 -0
  129. package/dist/src/utils/code-extractor.js +0 -1
  130. package/dist/src/utils/context-formatter.js +0 -1
  131. package/dist/src/utils/error-page.js +0 -1
  132. package/dist/src/utils/expandable.js +0 -1
  133. package/dist/src/utils/hooks-runner.js +0 -1
  134. package/dist/src/utils/html-diff.js +0 -1
  135. package/dist/src/utils/html.js +0 -1
  136. package/dist/src/utils/logger.js +0 -1
  137. package/dist/src/utils/loop.js +0 -1
  138. package/dist/src/utils/markdown-parser.js +0 -1
  139. package/dist/src/utils/markdown-query.js +0 -1
  140. package/dist/src/utils/markdown-terminal.js +0 -1
  141. package/dist/src/utils/research-parser.js +0 -1
  142. package/dist/src/utils/retry.js +0 -1
  143. package/dist/src/utils/rules-loader.js +0 -1
  144. package/dist/src/utils/strings.js +0 -1
  145. package/dist/src/utils/test-plan-markdown.js +0 -1
  146. package/dist/src/utils/throttle.js +0 -1
  147. package/dist/src/utils/unique-names.js +0 -1
  148. package/dist/src/utils/url-matcher.js +0 -1
  149. package/dist/src/utils/web-element.js +6 -5
  150. package/dist/src/utils/xpath.js +0 -1
  151. package/package.json +28 -4
  152. package/src/action-result.ts +694 -0
  153. package/src/action.ts +449 -0
  154. package/src/activity.ts +111 -0
  155. package/src/ai/agent.ts +3 -0
  156. package/src/ai/bosun.ts +557 -0
  157. package/src/ai/captain/idle-mode.ts +116 -0
  158. package/src/ai/captain/mixin.ts +22 -0
  159. package/src/ai/captain/test-mode.ts +262 -0
  160. package/src/ai/captain/web-mode.ts +136 -0
  161. package/src/ai/captain.ts +504 -0
  162. package/src/ai/conversation.ts +205 -0
  163. package/src/ai/experience-compactor.ts +284 -0
  164. package/src/ai/fisherman-tools.ts +181 -0
  165. package/src/ai/fisherman.ts +223 -0
  166. package/src/ai/historian.ts +457 -0
  167. package/src/ai/navigator.ts +572 -0
  168. package/src/ai/pilot.ts +776 -0
  169. package/src/ai/planner/session-dedup.ts +35 -0
  170. package/src/ai/planner/styles.ts +17 -0
  171. package/src/ai/planner/subpages.ts +171 -0
  172. package/src/ai/planner.ts +549 -0
  173. package/src/ai/provider.ts +613 -0
  174. package/src/ai/quartermaster.ts +286 -0
  175. package/src/ai/researcher/cache.ts +109 -0
  176. package/src/ai/researcher/coordinates.ts +239 -0
  177. package/src/ai/researcher/deep-analysis.ts +412 -0
  178. package/src/ai/researcher/fingerprint-worker.ts +59 -0
  179. package/src/ai/researcher/focus.ts +42 -0
  180. package/src/ai/researcher/locators.ts +282 -0
  181. package/src/ai/researcher/mixin.ts +4 -0
  182. package/src/ai/researcher/parser.ts +186 -0
  183. package/src/ai/researcher/research-result.ts +116 -0
  184. package/src/ai/researcher.ts +858 -0
  185. package/src/ai/rules.ts +376 -0
  186. package/src/ai/task-agent.ts +141 -0
  187. package/src/ai/tester.ts +939 -0
  188. package/src/ai/tools.ts +1122 -0
  189. package/src/api/api-client.ts +109 -0
  190. package/src/api/request-result.ts +212 -0
  191. package/src/api/request-store.ts +130 -0
  192. package/src/api/spec-reader.ts +174 -0
  193. package/src/api/xhr-capture.ts +100 -0
  194. package/src/browser-server.ts +74 -0
  195. package/src/command-handler.ts +454 -0
  196. package/src/commands/add-rule-command.ts +63 -0
  197. package/src/commands/base-command.ts +27 -0
  198. package/src/commands/clean-command.ts +73 -0
  199. package/src/commands/context-aria-command.ts +22 -0
  200. package/src/commands/context-command.ts +67 -0
  201. package/src/commands/context-data-command.ts +30 -0
  202. package/src/commands/context-experience-command.ts +48 -0
  203. package/src/commands/context-html-command.ts +33 -0
  204. package/src/commands/context-knowledge-command.ts +43 -0
  205. package/src/commands/debug-command.ts +13 -0
  206. package/src/commands/drill-command.ts +34 -0
  207. package/src/commands/exit-command.ts +32 -0
  208. package/src/commands/explore-command.ts +129 -0
  209. package/src/commands/freesail-command.ts +95 -0
  210. package/src/commands/help-command.ts +8 -0
  211. package/src/commands/index.ts +69 -0
  212. package/src/commands/init-command.ts +131 -0
  213. package/src/commands/knows-command.ts +68 -0
  214. package/src/commands/learn-command.ts +44 -0
  215. package/src/commands/navigate-command.ts +18 -0
  216. package/src/commands/path-command.ts +83 -0
  217. package/src/commands/plan-clear-command.ts +14 -0
  218. package/src/commands/plan-command.ts +46 -0
  219. package/src/commands/plan-edit-command.ts +9 -0
  220. package/src/commands/plan-load-command.ts +18 -0
  221. package/src/commands/plan-reload-command.ts +28 -0
  222. package/src/commands/plan-save-command.ts +25 -0
  223. package/src/commands/research-command.ts +45 -0
  224. package/src/commands/start-command.ts +13 -0
  225. package/src/commands/status-command.tsx +23 -0
  226. package/src/commands/test-command.ts +84 -0
  227. package/src/components/ActivityPane.tsx +80 -0
  228. package/src/components/AddKnowledge.tsx +169 -0
  229. package/src/components/AddRule.tsx +174 -0
  230. package/src/components/App.tsx +377 -0
  231. package/src/components/Autocomplete.tsx +63 -0
  232. package/src/components/InputPane.tsx +259 -0
  233. package/src/components/InputReadline.tsx +704 -0
  234. package/src/components/LogPane.tsx +187 -0
  235. package/src/components/PlanEditor.tsx +150 -0
  236. package/src/components/PlanPane.tsx +71 -0
  237. package/src/components/SessionTimer.tsx +35 -0
  238. package/src/components/StateTransitionPane.tsx +149 -0
  239. package/src/components/StatusPane.tsx +62 -0
  240. package/src/components/TaskPane.tsx +119 -0
  241. package/src/components/Welcome.tsx +83 -0
  242. package/src/components/WelcomeChecklist.tsx +118 -0
  243. package/src/components/WelcomeCommands.tsx +102 -0
  244. package/src/components/autocomplete-store.ts +35 -0
  245. package/src/components/parse-keypress.ts +170 -0
  246. package/src/config.ts +491 -0
  247. package/src/execution-controller.ts +109 -0
  248. package/src/experience-tracker.ts +350 -0
  249. package/src/explorbot.ts +405 -0
  250. package/src/explorer.ts +760 -0
  251. package/src/index.tsx +62 -0
  252. package/src/knowledge-tracker.ts +230 -0
  253. package/src/observability.ts +150 -0
  254. package/src/reporter.ts +224 -0
  255. package/src/state-manager.ts +556 -0
  256. package/src/stats.ts +53 -0
  257. package/src/test-plan.ts +432 -0
  258. package/src/utils/aria.ts +629 -0
  259. package/src/utils/cli-name.ts +13 -0
  260. package/src/utils/code-extractor.ts +22 -0
  261. package/src/utils/context-formatter.ts +239 -0
  262. package/src/utils/error-page.ts +23 -0
  263. package/src/utils/expandable.ts +38 -0
  264. package/src/utils/hooks-runner.ts +79 -0
  265. package/src/utils/html-diff.ts +918 -0
  266. package/src/utils/html.ts +1316 -0
  267. package/src/utils/logger.ts +534 -0
  268. package/src/utils/loop.ts +176 -0
  269. package/src/utils/markdown-parser.ts +127 -0
  270. package/src/utils/markdown-query.ts +466 -0
  271. package/src/utils/markdown-terminal.ts +43 -0
  272. package/src/utils/research-parser.ts +11 -0
  273. package/src/utils/retry.ts +73 -0
  274. package/src/utils/rules-loader.ts +118 -0
  275. package/src/utils/strings.ts +13 -0
  276. package/src/utils/test-plan-markdown.ts +332 -0
  277. package/src/utils/throttle.ts +18 -0
  278. package/src/utils/unique-names.ts +14 -0
  279. package/src/utils/url-matcher.ts +45 -0
  280. package/src/utils/web-element.ts +147 -0
  281. package/src/utils/xpath.ts +129 -0
  282. package/dist/bin/explorbot-cli.js.map +0 -1
  283. package/dist/boat/api-tester/bin/apibot-cli.js.map +0 -1
  284. package/dist/boat/api-tester/example/apibot.config.js +0 -31
  285. package/dist/boat/api-tester/example/apibot.config.js.map +0 -1
  286. package/dist/boat/api-tester/src/ai/chief/styles.js.map +0 -1
  287. package/dist/boat/api-tester/src/ai/chief.js.map +0 -1
  288. package/dist/boat/api-tester/src/ai/curler-tools.js.map +0 -1
  289. package/dist/boat/api-tester/src/ai/curler.js.map +0 -1
  290. package/dist/boat/api-tester/src/api-client.js.map +0 -1
  291. package/dist/boat/api-tester/src/apibot.js.map +0 -1
  292. package/dist/boat/api-tester/src/cli.js.map +0 -1
  293. package/dist/boat/api-tester/src/config.js.map +0 -1
  294. package/dist/prompts/audit-rules.md +0 -124
  295. package/dist/src/action-result.js.map +0 -1
  296. package/dist/src/action.js.map +0 -1
  297. package/dist/src/activity.js.map +0 -1
  298. package/dist/src/ai/agent.js.map +0 -1
  299. package/dist/src/ai/bosun.js.map +0 -1
  300. package/dist/src/ai/captain/idle-mode.js.map +0 -1
  301. package/dist/src/ai/captain/mixin.js.map +0 -1
  302. package/dist/src/ai/captain/test-mode.js.map +0 -1
  303. package/dist/src/ai/captain/web-mode.js.map +0 -1
  304. package/dist/src/ai/captain.js.map +0 -1
  305. package/dist/src/ai/conversation.js.map +0 -1
  306. package/dist/src/ai/experience-compactor.js.map +0 -1
  307. package/dist/src/ai/fisherman-tools.js.map +0 -1
  308. package/dist/src/ai/fisherman.js.map +0 -1
  309. package/dist/src/ai/historian.js.map +0 -1
  310. package/dist/src/ai/navigator.js.map +0 -1
  311. package/dist/src/ai/pilot.js.map +0 -1
  312. package/dist/src/ai/planner/session-dedup.js.map +0 -1
  313. package/dist/src/ai/planner/styles.js.map +0 -1
  314. package/dist/src/ai/planner/subpages.js.map +0 -1
  315. package/dist/src/ai/planner.js.map +0 -1
  316. package/dist/src/ai/provider.js.map +0 -1
  317. package/dist/src/ai/quartermaster.js.map +0 -1
  318. package/dist/src/ai/researcher/cache.js.map +0 -1
  319. package/dist/src/ai/researcher/coordinates.js.map +0 -1
  320. package/dist/src/ai/researcher/deep-analysis.js.map +0 -1
  321. package/dist/src/ai/researcher/fingerprint-worker.js.map +0 -1
  322. package/dist/src/ai/researcher/focus.js.map +0 -1
  323. package/dist/src/ai/researcher/locators.js.map +0 -1
  324. package/dist/src/ai/researcher/mixin.js.map +0 -1
  325. package/dist/src/ai/researcher/parser.js.map +0 -1
  326. package/dist/src/ai/researcher/research-result.js.map +0 -1
  327. package/dist/src/ai/researcher.js.map +0 -1
  328. package/dist/src/ai/rules.js.map +0 -1
  329. package/dist/src/ai/task-agent.js.map +0 -1
  330. package/dist/src/ai/tester.js.map +0 -1
  331. package/dist/src/ai/tools.js.map +0 -1
  332. package/dist/src/api/api-client.js.map +0 -1
  333. package/dist/src/api/request-result.js.map +0 -1
  334. package/dist/src/api/request-store.js.map +0 -1
  335. package/dist/src/api/spec-reader.js.map +0 -1
  336. package/dist/src/api/xhr-capture.js.map +0 -1
  337. package/dist/src/browser-server.js.map +0 -1
  338. package/dist/src/command-handler.js.map +0 -1
  339. package/dist/src/commands/add-rule-command.js.map +0 -1
  340. package/dist/src/commands/base-command.js.map +0 -1
  341. package/dist/src/commands/clean-command.js.map +0 -1
  342. package/dist/src/commands/context-aria-command.js.map +0 -1
  343. package/dist/src/commands/context-command.js.map +0 -1
  344. package/dist/src/commands/context-data-command.js.map +0 -1
  345. package/dist/src/commands/context-experience-command.js.map +0 -1
  346. package/dist/src/commands/context-html-command.js.map +0 -1
  347. package/dist/src/commands/context-knowledge-command.js.map +0 -1
  348. package/dist/src/commands/debug-command.js.map +0 -1
  349. package/dist/src/commands/drill-command.js.map +0 -1
  350. package/dist/src/commands/exit-command.js.map +0 -1
  351. package/dist/src/commands/explore-command.js.map +0 -1
  352. package/dist/src/commands/freesail-command.js.map +0 -1
  353. package/dist/src/commands/help-command.js.map +0 -1
  354. package/dist/src/commands/index.js.map +0 -1
  355. package/dist/src/commands/knows-command.js.map +0 -1
  356. package/dist/src/commands/learn-command.js.map +0 -1
  357. package/dist/src/commands/navigate-command.js.map +0 -1
  358. package/dist/src/commands/path-command.js.map +0 -1
  359. package/dist/src/commands/plan-clear-command.js.map +0 -1
  360. package/dist/src/commands/plan-command.js.map +0 -1
  361. package/dist/src/commands/plan-edit-command.js.map +0 -1
  362. package/dist/src/commands/plan-load-command.js.map +0 -1
  363. package/dist/src/commands/plan-reload-command.js.map +0 -1
  364. package/dist/src/commands/plan-save-command.js.map +0 -1
  365. package/dist/src/commands/research-command.js.map +0 -1
  366. package/dist/src/commands/start-command.js.map +0 -1
  367. package/dist/src/commands/status-command.js.map +0 -1
  368. package/dist/src/commands/test-command.js.map +0 -1
  369. package/dist/src/components/ActivityPane.js.map +0 -1
  370. package/dist/src/components/AddKnowledge.js.map +0 -1
  371. package/dist/src/components/AddRule.js.map +0 -1
  372. package/dist/src/components/App.js.map +0 -1
  373. package/dist/src/components/Autocomplete.js.map +0 -1
  374. package/dist/src/components/InputPane.js.map +0 -1
  375. package/dist/src/components/InputReadline.js.map +0 -1
  376. package/dist/src/components/LogPane.js.map +0 -1
  377. package/dist/src/components/PlanEditor.js.map +0 -1
  378. package/dist/src/components/PlanPane.js.map +0 -1
  379. package/dist/src/components/SessionTimer.js.map +0 -1
  380. package/dist/src/components/StateTransitionPane.js.map +0 -1
  381. package/dist/src/components/StatusPane.js.map +0 -1
  382. package/dist/src/components/TaskPane.js.map +0 -1
  383. package/dist/src/components/Welcome.js.map +0 -1
  384. package/dist/src/components/WelcomeChecklist.js.map +0 -1
  385. package/dist/src/components/WelcomeCommands.js.map +0 -1
  386. package/dist/src/components/autocomplete-store.js.map +0 -1
  387. package/dist/src/components/parse-keypress.js.map +0 -1
  388. package/dist/src/config.js.map +0 -1
  389. package/dist/src/execution-controller.js.map +0 -1
  390. package/dist/src/experience-tracker.js.map +0 -1
  391. package/dist/src/explorbot.js.map +0 -1
  392. package/dist/src/explorer.js.map +0 -1
  393. package/dist/src/index.js.map +0 -1
  394. package/dist/src/knowledge-tracker.js.map +0 -1
  395. package/dist/src/observability.js.map +0 -1
  396. package/dist/src/reporter.js.map +0 -1
  397. package/dist/src/state-manager.js.map +0 -1
  398. package/dist/src/stats.js.map +0 -1
  399. package/dist/src/test-plan.js.map +0 -1
  400. package/dist/src/utils/aria.js.map +0 -1
  401. package/dist/src/utils/code-extractor.js.map +0 -1
  402. package/dist/src/utils/context-formatter.js.map +0 -1
  403. package/dist/src/utils/error-page.js.map +0 -1
  404. package/dist/src/utils/expandable.js.map +0 -1
  405. package/dist/src/utils/hooks-runner.js.map +0 -1
  406. package/dist/src/utils/html-diff.js.map +0 -1
  407. package/dist/src/utils/html.js.map +0 -1
  408. package/dist/src/utils/logger.js.map +0 -1
  409. package/dist/src/utils/loop.js.map +0 -1
  410. package/dist/src/utils/markdown-parser.js.map +0 -1
  411. package/dist/src/utils/markdown-query.js.map +0 -1
  412. package/dist/src/utils/markdown-terminal.js.map +0 -1
  413. package/dist/src/utils/research-parser.js.map +0 -1
  414. package/dist/src/utils/retry.js.map +0 -1
  415. package/dist/src/utils/rules-loader.js.map +0 -1
  416. package/dist/src/utils/strings.js.map +0 -1
  417. package/dist/src/utils/test-plan-markdown.js.map +0 -1
  418. package/dist/src/utils/throttle.js.map +0 -1
  419. package/dist/src/utils/unique-names.js.map +0 -1
  420. package/dist/src/utils/url-matcher.js.map +0 -1
  421. package/dist/src/utils/web-element.js.map +0 -1
  422. package/dist/src/utils/xpath.js.map +0 -1
  423. package/prompts/audit-rules.md +0 -124
@@ -0,0 +1,284 @@
1
+ import { readFileSync, unlinkSync, writeFileSync } from 'node:fs';
2
+ import dedent from 'dedent';
3
+ import matter from 'gray-matter';
4
+ import { z } from 'zod';
5
+ import type { ExperienceTracker } from '../experience-tracker.js';
6
+ import { Observability } from '../observability.js';
7
+ import { createDebug, log, tag } from '../utils/logger.js';
8
+ import type { Agent } from './agent.js';
9
+ import type { Provider } from './provider.js';
10
+
11
+ const debugLog = createDebug('explorbot:experience-compactor');
12
+
13
+ interface ExperienceFile {
14
+ filePath: string;
15
+ data: { url?: string; title?: string; [key: string]: any };
16
+ content: string;
17
+ }
18
+
19
+ interface MergeGroup {
20
+ pattern: string;
21
+ files: ExperienceFile[];
22
+ }
23
+
24
+ export class ExperienceCompactor implements Agent {
25
+ emoji = '🗜️';
26
+ private provider: Provider;
27
+ private experienceTracker: ExperienceTracker;
28
+ private MAX_LENGTH = 5000;
29
+
30
+ constructor(provider: Provider, experienceTracker: ExperienceTracker) {
31
+ this.provider = provider;
32
+ this.experienceTracker = experienceTracker;
33
+ }
34
+
35
+ async compactExperience(experience: string): Promise<string> {
36
+ if (experience.length < this.MAX_LENGTH) {
37
+ return experience;
38
+ }
39
+
40
+ const prompt = this.buildCompactionPrompt(experience);
41
+ const model = this.provider.getModelForAgent('experience-compactor');
42
+ const response = await this.provider.chat(
43
+ [
44
+ { role: 'user', content: this.getSystemPrompt() },
45
+ { role: 'user', content: prompt },
46
+ ],
47
+ model,
48
+ { telemetryFunctionId: 'experience.compact' }
49
+ );
50
+ return response.text;
51
+ }
52
+
53
+ async compactAllExperiences(): Promise<number> {
54
+ return Observability.run('experience-compactor.compactAll', { tags: ['experience-compactor'] }, async () => {
55
+ await this.mergeSimilarExperiences();
56
+
57
+ const experienceFiles = this.experienceTracker.getAllExperience();
58
+ let compactedCount = 0;
59
+
60
+ for (const experience of experienceFiles) {
61
+ const prevContent = experience.content;
62
+ const frontmatter = experience.data;
63
+ const compactedContent = await this.compactExperienceFile(experience.filePath);
64
+
65
+ if (prevContent !== compactedContent) {
66
+ const stateHash = experience.filePath.split('/').pop()?.replace('.md', '') || '';
67
+ this.experienceTracker.writeExperienceFile(stateHash, compactedContent, frontmatter);
68
+ debugLog('Experience file compacted:', experience.filePath);
69
+ compactedCount++;
70
+ }
71
+ }
72
+
73
+ return compactedCount;
74
+ });
75
+ }
76
+
77
+ async mergeSimilarExperiences(): Promise<number> {
78
+ return Observability.run('experience-compactor.merge', { tags: ['experience-compactor'] }, async () => {
79
+ const experienceFiles = this.experienceTracker.getAllExperience();
80
+ if (experienceFiles.length < 2) {
81
+ return 0;
82
+ }
83
+
84
+ const mergeGroups = await this.identifyMergeGroups(experienceFiles);
85
+ let mergedCount = 0;
86
+
87
+ for (const group of mergeGroups) {
88
+ if (group.files.length < 2) {
89
+ continue;
90
+ }
91
+
92
+ await this.mergeExperienceGroup(group);
93
+ mergedCount += group.files.length - 1;
94
+ }
95
+
96
+ return mergedCount;
97
+ });
98
+ }
99
+
100
+ private async identifyMergeGroups(files: ExperienceFile[]): Promise<MergeGroup[]> {
101
+ const filesWithUrl = files.filter((f) => f.data.url && !f.data.url.startsWith('~'));
102
+ if (filesWithUrl.length < 2) {
103
+ return [];
104
+ }
105
+
106
+ const urlList = filesWithUrl.map((f) => f.data.url as string);
107
+ const mergeDecisions = await this.askAiForMergeDecisions(urlList);
108
+
109
+ const groups: MergeGroup[] = [];
110
+ const usedFiles = new Set<string>();
111
+
112
+ for (const decision of mergeDecisions) {
113
+ const matchingFiles = filesWithUrl.filter((f) => decision.urls.includes(f.data.url as string) && !usedFiles.has(f.filePath));
114
+
115
+ if (matchingFiles.length >= 2) {
116
+ groups.push({
117
+ pattern: decision.pattern,
118
+ files: matchingFiles,
119
+ });
120
+ for (const f of matchingFiles) {
121
+ usedFiles.add(f.filePath);
122
+ }
123
+ }
124
+ }
125
+
126
+ return groups;
127
+ }
128
+
129
+ private async askAiForMergeDecisions(urls: string[]): Promise<{ urls: string[]; pattern: string }[]> {
130
+ const prompt = this.buildMergePrompt(urls);
131
+ const model = this.provider.getModelForAgent('experience-compactor');
132
+
133
+ const schema = z.object({
134
+ mergeGroups: z.array(
135
+ z.object({
136
+ urls: z.array(z.string()).describe('URLs that should be merged together'),
137
+ pattern: z.string().describe('Regex pattern that matches all URLs in the group, wrapped in ~ delimiters'),
138
+ })
139
+ ),
140
+ });
141
+
142
+ try {
143
+ const response = await this.provider.generateObject([{ role: 'user', content: prompt }], schema, model, {
144
+ telemetryFunctionId: 'experience.mergeDecisions',
145
+ });
146
+ debugLog('AI merge decisions:', response.object);
147
+ return response.object.mergeGroups || [];
148
+ } catch (error) {
149
+ debugLog('Error getting merge decisions from AI:', error);
150
+ return [];
151
+ }
152
+ }
153
+
154
+ private buildMergePrompt(urls: string[]): string {
155
+ return dedent`
156
+ Analyze these experience file URLs and identify groups that represent the same page type with dynamic URL parameters.
157
+
158
+ <urls>
159
+ ${urls.map((u, i) => `${i + 1}. ${u}`).join('\n')}
160
+ </urls>
161
+
162
+ <rules>
163
+ - Identify URLs that represent the same page structure but with different dynamic values (IDs, slugs, etc.)
164
+ - Example: /item/101, /item/102, /item/105 should be grouped with pattern ~/item/\\d+~
165
+ - Example: /user/john, /user/jane should be grouped with pattern ~/user/[^/]+~
166
+ - Only group URLs that are clearly the same page type with dynamic segments
167
+ - Do NOT group unrelated pages
168
+ - Return regex patterns wrapped in ~ delimiters (e.g., ~/path/\\d+~)
169
+ - If no URLs should be merged, return empty mergeGroups array
170
+ </rules>
171
+
172
+ Return JSON with mergeGroups array. Each group should have:
173
+ - urls: array of URLs that should be merged
174
+ - pattern: regex pattern (wrapped in ~) that matches all URLs in the group
175
+ `;
176
+ }
177
+
178
+ private async mergeExperienceGroup(group: MergeGroup): Promise<void> {
179
+ const [targetFile, ...sourceFiles] = group.files;
180
+ const combinedContent = group.files.map((f) => f.content).join('\n\n---\n\n');
181
+
182
+ const targetStateHash = targetFile.filePath.split('/').pop()?.replace('.md', '') || '';
183
+ const newFrontmatter = {
184
+ ...targetFile.data,
185
+ url: group.pattern,
186
+ mergedFrom: group.files.map((f) => f.data.url),
187
+ };
188
+
189
+ this.experienceTracker.writeExperienceFile(targetStateHash, combinedContent, newFrontmatter);
190
+ log(`Merged ${group.files.length} experience files into ${targetFile.filePath}`);
191
+ debugLog('New URL pattern:', group.pattern);
192
+
193
+ for (const sourceFile of sourceFiles) {
194
+ try {
195
+ unlinkSync(sourceFile.filePath);
196
+ debugLog('Deleted merged source file:', sourceFile.filePath);
197
+ } catch (error) {
198
+ debugLog('Error deleting source file:', error);
199
+ }
200
+ }
201
+ }
202
+
203
+ async compactExperienceFile(filePath: string): Promise<string> {
204
+ try {
205
+ const fileContent = readFileSync(filePath, 'utf8');
206
+ const parsed = matter(fileContent);
207
+
208
+ if (parsed.content.length < this.MAX_LENGTH) {
209
+ return parsed.content;
210
+ }
211
+ debugLog('Experience file to compact:', filePath);
212
+
213
+ const text = await this.compactExperience(parsed.content);
214
+
215
+ tag('substep').log('Experience file compacted:', filePath);
216
+ debugLog('Experience file compacted:', text);
217
+
218
+ return text;
219
+ } catch (error) {
220
+ debugLog('Error compacting experience file:', error);
221
+ return '';
222
+ }
223
+ }
224
+
225
+ private getSystemPrompt(): string {
226
+ const customPrompt = this.provider.getSystemPromptForAgent('experience-compactor', '*');
227
+ return dedent`
228
+ You are an expert test automation engineer specializing in CodeceptJS.
229
+ Your task is to compact experience data from test automation attempts into clean markdown format.
230
+
231
+ ${customPrompt || ''}
232
+ `;
233
+ }
234
+
235
+ private buildCompactionPrompt(content: string): string {
236
+ return dedent`
237
+ <rules>
238
+ - Use markdown headers only (##, ###) - NO XML tags or wrappers in output
239
+ - Merge similar flows to remove duplicates
240
+ - Keep output under ${this.MAX_LENGTH} characters
241
+ - Be explicit and short - no proposals or explanations
242
+
243
+ KEEP only:
244
+ - Positive flows that complete a business action (create, edit, delete, navigate)
245
+ - Reusable interaction patterns (opening dropdowns, filling forms, using pickers)
246
+ - Discovery lines (> prefixed) that document UI elements appearing at each step — keep at most 5 per flow step
247
+ - Working CodeceptJS code for common interactions
248
+
249
+ REMOVE:
250
+ - All FAILED entries
251
+ - Edge-case and negative flows (empty values, disabled buttons, validation errors, boundary testing)
252
+ - Flows whose purpose is to test what happens when something goes wrong
253
+ - All I.amOnPage, I.grab, I.see, I.seeElement, I.dontSee calls
254
+ - Duplicate approaches that achieve the same goal
255
+ </rules>
256
+
257
+ <output_format>
258
+ Use this markdown structure:
259
+
260
+ ## Flows
261
+
262
+ For each unique positive flow (merge duplicates):
263
+ - Purpose: what was accomplished
264
+ \`\`\`js
265
+ // working code
266
+ \`\`\`
267
+
268
+ ## Reusable Actions
269
+
270
+ For each reusable interaction pattern not covered by flows:
271
+ - Purpose: what was accomplished
272
+ \`\`\`js
273
+ // working code
274
+ \`\`\`
275
+ </output_format>
276
+
277
+ <context>
278
+ ${content}
279
+ </context>
280
+
281
+ Compact this experience data following the format above.
282
+ `;
283
+ }
284
+ }
@@ -0,0 +1,181 @@
1
+ import { tool } from 'ai';
2
+ import dedent from 'dedent';
3
+ import { z } from 'zod';
4
+ import type { ApiClient } from '../api/api-client.ts';
5
+ import type { RequestStore } from '../api/request-store.ts';
6
+ import { extractEndpointDefinition } from '../api/spec-reader.ts';
7
+ import { tag } from '../utils/logger.ts';
8
+
9
+ export function createFishermanTools(apiClient: ApiClient, requestStore: RequestStore, opts: { spec?: any; baseEndpoint?: string }) {
10
+ let finished = false;
11
+ let result: FishermanResult = { success: false, summary: '', created: [], failed: [] };
12
+
13
+ const getResult = () => result;
14
+ const isFinished = () => finished;
15
+
16
+ const tools = {
17
+ getEndpointSpec: tool({
18
+ description: dedent`
19
+ Get the request specification for an endpoint.
20
+ Returns the request body example from a previously captured request, or OpenAPI spec definition.
21
+ Call this before making a request to an endpoint you haven't used before.
22
+ `,
23
+ inputSchema: z.object({
24
+ method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']).describe('HTTP method'),
25
+ path: z.string().describe('Endpoint path, e.g. /suites'),
26
+ }),
27
+ execute: async ({ method, path }) => {
28
+ tag('step').log(`Fisherman: spec lookup ${method} ${path}`);
29
+
30
+ const captured = requestStore.findCapturedRequest(method, path);
31
+ if (captured) {
32
+ return {
33
+ source: 'captured',
34
+ method: captured.method,
35
+ path: captured.path,
36
+ status: captured.status,
37
+ requestBody: captured.requestBody || 'no body',
38
+ };
39
+ }
40
+
41
+ if (opts.spec) {
42
+ try {
43
+ const definition = extractEndpointDefinition(opts.spec, path, opts.baseEndpoint);
44
+ return { source: 'spec', definition };
45
+ } catch (err: any) {
46
+ return { source: 'none', error: err.message };
47
+ }
48
+ }
49
+
50
+ return { source: 'none', error: `No spec found for ${method} ${path}` };
51
+ },
52
+ }),
53
+
54
+ request: tool({
55
+ description: dedent`
56
+ Make an HTTP request to the API.
57
+ Returns status, timing, and auto-extracted IDs and names from the response.
58
+ `,
59
+ inputSchema: z.object({
60
+ method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']).describe('HTTP method'),
61
+ path: z.string().describe('API path (e.g., /suites, /suites/1)'),
62
+ body: z.any().optional().describe('Request body (JSON object)'),
63
+ queryParams: z.record(z.string(), z.string()).optional().describe('Query parameters'),
64
+ }),
65
+ execute: async (input) => {
66
+ tag('step').log(`Fisherman: ${input.method} ${input.path}`);
67
+
68
+ const reqResult = await apiClient.request({
69
+ method: input.method,
70
+ path: input.path,
71
+ body: input.body,
72
+ queryParams: input.queryParams,
73
+ });
74
+
75
+ requestStore.addMadeRequest(reqResult);
76
+
77
+ if (reqResult.error) {
78
+ tag('error').log(`Fisherman: ${input.method} ${input.path} > Network error: ${reqResult.error}`);
79
+ return { success: false, error: reqResult.error };
80
+ }
81
+
82
+ const statusLine = `${reqResult.status} ${reqResult.statusText}`;
83
+
84
+ if (reqResult.status >= 400) {
85
+ tag('error').log(`Fisherman: ${input.method} ${input.path} > ${statusLine}`);
86
+ return {
87
+ success: false,
88
+ status: reqResult.status,
89
+ statusText: reqResult.statusText,
90
+ errorPreview: reqResult.rawResponseBody.substring(0, 300),
91
+ };
92
+ }
93
+
94
+ const extracted = extractKeyFields(reqResult.responseBody);
95
+ tag('success').log(`Fisherman: ${input.method} ${input.path} > ${statusLine}`);
96
+ return {
97
+ success: true,
98
+ status: reqResult.status,
99
+ ...extracted,
100
+ };
101
+ },
102
+ }),
103
+
104
+ finish: tool({
105
+ description: 'Report completion of data preparation. Call when all requested items have been created.',
106
+ inputSchema: z.object({
107
+ summary: z.string().describe('Summary of what was created'),
108
+ created: z
109
+ .array(
110
+ z.object({
111
+ type: z.string().describe('Item type (e.g., suite, test, label)'),
112
+ id: z.union([z.string(), z.number()]).optional().describe('Created item ID'),
113
+ title: z.string().optional().describe('Created item name/title'),
114
+ })
115
+ )
116
+ .describe('List of successfully created items'),
117
+ failed: z
118
+ .array(
119
+ z.object({
120
+ type: z.string().describe('Item type that failed'),
121
+ reason: z.string().describe('Why it failed'),
122
+ })
123
+ )
124
+ .optional()
125
+ .describe('List of items that could not be created'),
126
+ }),
127
+ execute: async ({ summary, created, failed }) => {
128
+ tag('success').log(`Fisherman done: ${summary}`);
129
+ finished = true;
130
+ result = { success: true, summary, created, failed: failed || [] };
131
+ return { finished: true };
132
+ },
133
+ }),
134
+
135
+ stop: tool({
136
+ description: 'Abort data preparation when it cannot be completed.',
137
+ inputSchema: z.object({
138
+ reason: z.string().describe('Why preparation cannot continue'),
139
+ }),
140
+ execute: async ({ reason }) => {
141
+ tag('warning').log(`Fisherman stopped: ${reason}`);
142
+ finished = true;
143
+ result = { success: false, summary: reason, created: [], failed: [] };
144
+ return { stopped: true };
145
+ },
146
+ }),
147
+ };
148
+
149
+ return { tools, getResult, isFinished };
150
+ }
151
+
152
+ function extractKeyFields(body: any, result: Record<string, any> = {}, depth = 0): Record<string, any> {
153
+ if (!body || typeof body !== 'object' || depth > 5) return result;
154
+
155
+ if (Array.isArray(body)) {
156
+ if (body.length > 0) extractKeyFields(body[0], result, depth + 1);
157
+ return result;
158
+ }
159
+
160
+ for (const [key, value] of Object.entries(body)) {
161
+ if (value === null || value === undefined) continue;
162
+ if (typeof value === 'object') {
163
+ extractKeyFields(value, result, depth + 1);
164
+ continue;
165
+ }
166
+ if (key === 'id' || key === '_id' || key === 'uuid' || key.endsWith('_id')) {
167
+ result[key] ??= value;
168
+ }
169
+ if (key === 'name' || key === 'title' || key === 'status') {
170
+ result[key] ??= String(value).slice(0, 100);
171
+ }
172
+ }
173
+ return result;
174
+ }
175
+
176
+ export interface FishermanResult {
177
+ success: boolean;
178
+ summary: string;
179
+ created: Array<{ type: string; id?: string | number; title?: string }>;
180
+ failed: Array<{ type: string; reason: string }>;
181
+ }
@@ -0,0 +1,223 @@
1
+ import dedent from 'dedent';
2
+ import type { ApiClient } from '../api/api-client.ts';
3
+ import type { RequestStore } from '../api/request-store.ts';
4
+ import { listAllEndpoints } from '../api/spec-reader.ts';
5
+ import { createDebug, tag } from '../utils/logger.ts';
6
+
7
+ const debugLog = createDebug('explorbot:fisherman');
8
+ import { loop } from '../utils/loop.ts';
9
+ import type { Agent } from './agent.ts';
10
+ import { type FishermanResult, createFishermanTools } from './fisherman-tools.ts';
11
+ import type { Provider } from './provider.ts';
12
+
13
+ const MAX_ITERATIONS = 15;
14
+ const MAX_TOOL_ROUNDTRIPS = 5;
15
+
16
+ export class Fisherman implements Agent {
17
+ emoji = '🎣';
18
+ private provider: Provider;
19
+ private apiClient: ApiClient;
20
+ private requestStore: RequestStore;
21
+ private specLoader: () => Promise<any | null>;
22
+ private cookieProvider: () => Promise<Record<string, string>>;
23
+ private configHeaders: Record<string, string>;
24
+ private sessionName?: string;
25
+ private baseEndpoint: string;
26
+ private spec: any | null = null;
27
+ private mode: 'replicate' | 'achieve' | 'disabled' = 'disabled';
28
+ private hasApiConfig: boolean;
29
+
30
+ constructor(provider: Provider, apiClient: ApiClient, requestStore: RequestStore, specLoader: () => Promise<any | null>, baseEndpoint: string, cookieProvider: () => Promise<Record<string, string>>, configHeaders: Record<string, string> = {}, hasApiConfig = false) {
31
+ this.provider = provider;
32
+ this.apiClient = apiClient;
33
+ this.requestStore = requestStore;
34
+ this.specLoader = specLoader;
35
+ this.baseEndpoint = baseEndpoint;
36
+ this.cookieProvider = cookieProvider;
37
+ this.configHeaders = configHeaders;
38
+ this.hasApiConfig = hasApiConfig;
39
+ this.mode = hasApiConfig ? 'achieve' : 'replicate';
40
+ }
41
+
42
+ isAvailable(): boolean {
43
+ return this.mode !== 'disabled';
44
+ }
45
+
46
+ getMode(): string {
47
+ return this.mode;
48
+ }
49
+
50
+ async ensureReady(scopeUrl?: string): Promise<void> {
51
+ await this.detectMode(scopeUrl);
52
+ this.spec ??= await this.specLoader();
53
+ debugLog(`ensureReady: mode=${this.mode}, scope=${scopeUrl}`);
54
+ }
55
+
56
+ getEndpointList(scopeUrl?: string): string {
57
+ return this.buildEndpointList(scopeUrl);
58
+ }
59
+
60
+ async prepareData(instructions: string, scopeUrl?: string, sessionName?: string): Promise<FishermanResult> {
61
+ this.sessionName = sessionName;
62
+ tag('info').log(`Fisherman [${this.mode}]: preparing data — ${instructions}`);
63
+
64
+ await this.ensureReady(scopeUrl);
65
+
66
+ if (this.mode === 'disabled') {
67
+ debugLog('disabled — no data for scope');
68
+ return { success: false, summary: 'No API data available for this scope', created: [], failed: [] };
69
+ }
70
+
71
+ const endpointList = this.buildEndpointList(scopeUrl);
72
+ debugLog(`endpoints:\n${endpointList || '(none)'}`);
73
+
74
+ if (!endpointList) {
75
+ tag('warning').log('Fisherman: no endpoints available');
76
+ this.mode = 'disabled';
77
+ return { success: false, summary: 'No API endpoints available', created: [], failed: [] };
78
+ }
79
+
80
+ await this.refreshAuth();
81
+ debugLog(`auth headers: ${Object.keys(this.apiClient.getHeaders()).join(', ')}`);
82
+
83
+ const { tools, getResult, isFinished } = createFishermanTools(this.apiClient, this.requestStore, {
84
+ spec: this.spec,
85
+ baseEndpoint: this.baseEndpoint,
86
+ });
87
+
88
+ const conversation = this.provider.startConversation(this.buildSystemPrompt(endpointList, scopeUrl), 'fisherman');
89
+ conversation.addUserText(this.buildTaskPrompt(instructions));
90
+
91
+ await loop(
92
+ async ({ stop, iteration }) => {
93
+ debugLog(`iteration ${iteration}`);
94
+ const invokeResult = await this.provider.invokeConversation(conversation, tools, {
95
+ maxToolRoundtrips: MAX_TOOL_ROUNDTRIPS,
96
+ toolChoice: 'required',
97
+ agentName: 'fisherman',
98
+ });
99
+ debugLog(`iteration ${iteration} done, text: ${invokeResult?.response?.text?.slice(0, 200) || '(none)'}`);
100
+
101
+ if (isFinished()) {
102
+ stop();
103
+ return;
104
+ }
105
+
106
+ if (iteration >= MAX_ITERATIONS) {
107
+ tag('warning').log('Fisherman: max iterations reached');
108
+ stop();
109
+ }
110
+ },
111
+ {
112
+ maxAttempts: MAX_ITERATIONS,
113
+ observability: {
114
+ name: `fisherman: ${instructions.slice(0, 50)}`,
115
+ agent: 'fisherman',
116
+ sessionId: this.sessionName,
117
+ },
118
+ catch: async ({ error, stop }) => {
119
+ debugLog(`error: ${error.message}`);
120
+ tag('warning').log(`Fisherman error: ${error.message}`);
121
+ stop();
122
+ },
123
+ }
124
+ );
125
+
126
+ const result = getResult();
127
+ tag('info').log(`Fisherman result: ${result.summary}`);
128
+ return result;
129
+ }
130
+
131
+ private async detectMode(scopeUrl?: string): Promise<void> {
132
+ if (this.hasApiConfig) {
133
+ this.mode = 'achieve';
134
+ debugLog('achieve mode — api config present');
135
+ return;
136
+ }
137
+
138
+ this.requestStore.loadFromDisk();
139
+ const allRequests = this.requestStore.getCapturedRequests();
140
+ debugLog(`total stored requests: ${allRequests.length}, scope: ${scopeUrl}`);
141
+
142
+ if (allRequests.length > 0) {
143
+ this.mode = 'replicate';
144
+ return;
145
+ }
146
+
147
+ this.mode = 'disabled';
148
+ }
149
+
150
+ private async refreshAuth(): Promise<void> {
151
+ const cookies = await this.cookieProvider();
152
+ if (Object.keys(cookies).length > 0) {
153
+ this.apiClient.setHeaders(cookies);
154
+ }
155
+
156
+ const xhrHeaders = this.requestStore.extractAuthHeaders();
157
+ if (Object.keys(xhrHeaders).length > 0) {
158
+ this.apiClient.setHeaders(xhrHeaders);
159
+ }
160
+
161
+ if (Object.keys(this.configHeaders).length > 0) {
162
+ this.apiClient.setHeaders(this.configHeaders);
163
+ }
164
+ }
165
+
166
+ private buildEndpointList(scopeUrl?: string): string {
167
+ if (this.mode === 'achieve' && this.spec) {
168
+ const specEndpoints = listAllEndpoints(this.spec, this.baseEndpoint);
169
+ if (specEndpoints) return specEndpoints;
170
+ }
171
+
172
+ let writeRequests = this.requestStore.getWriteRequestsForScope(scopeUrl || '/');
173
+ if (writeRequests.length === 0) {
174
+ writeRequests = this.requestStore.getWriteRequestsForScope('/');
175
+ }
176
+
177
+ const seen = new Set<string>();
178
+ const lines: string[] = [];
179
+
180
+ for (const req of writeRequests) {
181
+ const key = `${req.method} ${req.path}`;
182
+ if (seen.has(key)) continue;
183
+ seen.add(key);
184
+ lines.push(key);
185
+ }
186
+
187
+ return lines.join('\n');
188
+ }
189
+
190
+ private buildSystemPrompt(endpointList: string, scopeUrl?: string): string {
191
+ const scopeBlock = scopeUrl ? `\n\nSCOPE: You are operating within ${scopeUrl}.\nAll created items must belong to this scope.` : '';
192
+
193
+ return dedent`
194
+ You are Fisherman — a data preparation agent. You create test data by making API requests.
195
+
196
+ AVAILABLE ENDPOINTS:
197
+ ${endpointList}
198
+ ${scopeBlock}
199
+
200
+ WORKFLOW:
201
+ 1. Call getEndpointSpec to see the request body example for the endpoint
202
+ 2. Make requests — the response automatically extracts IDs, names, and status fields
203
+ 3. Use extracted IDs to chain requests (e.g., create suite, use its id to create tests in it)
204
+ 4. Call finish with a summary and IDs of all created items
205
+
206
+ RULES:
207
+ - Always call getEndpointSpec before your first request to an unfamiliar endpoint
208
+ - Chain requests logically — create parent resources before children
209
+ - If a request fails, try once more with adjusted data before reporting failure
210
+ - Use realistic but unique data for each item (vary names, titles)
211
+ `;
212
+ }
213
+
214
+ private buildTaskPrompt(instructions: string): string {
215
+ return dedent`
216
+ Prepare the following test data:
217
+
218
+ ${instructions}
219
+
220
+ Execute the necessary API requests to create this data. When done, call finish with the summary.
221
+ `;
222
+ }
223
+ }