explorbot 0.0.1 → 0.0.5

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 +679 -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 +19 -98
  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 +0 -1
  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 +0 -1
  41. package/dist/src/ai/planner.js +0 -1
  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 +0 -1
  45. package/dist/src/ai/researcher/coordinates.js +0 -1
  46. package/dist/src/ai/researcher/deep-analysis.js +0 -1
  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 +0 -1
  50. package/dist/src/ai/researcher/mixin.js +0 -1
  51. package/dist/src/ai/researcher/parser.js +0 -1
  52. package/dist/src/ai/researcher/research-result.js +0 -1
  53. package/dist/src/ai/researcher.js +0 -1
  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 +0 -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 +0 -1
  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 +2 -2
  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 +115 -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 +0 -1
  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 +0 -1
  119. package/dist/src/explorer.js +0 -1
  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 +0 -1
  150. package/dist/src/utils/xpath.js +0 -1
  151. package/package.json +27 -3
  152. package/src/action-result.ts +694 -0
  153. package/src/action.ts +445 -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 +141 -0
  172. package/src/ai/planner.ts +536 -0
  173. package/src/ai/provider.ts +613 -0
  174. package/src/ai/quartermaster.ts +286 -0
  175. package/src/ai/researcher/cache.ts +103 -0
  176. package/src/ai/researcher/coordinates.ts +238 -0
  177. package/src/ai/researcher/deep-analysis.ts +415 -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 +115 -0
  184. package/src/ai/researcher.ts +857 -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 +1117 -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 +128 -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 +41 -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 +490 -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 +713 -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 +145 -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,557 @@
1
+ import { tool } from 'ai';
2
+ import dedent from 'dedent';
3
+ import { z } from 'zod';
4
+ import { ActionResult } from '../action-result.ts';
5
+ import { setActivity } from '../activity.ts';
6
+ import type { ExperienceTracker } from '../experience-tracker.ts';
7
+ import type Explorer from '../explorer.ts';
8
+ import type { KnowledgeTracker } from '../knowledge-tracker.ts';
9
+ import { Observability } from '../observability.ts';
10
+ import { Plan, Task, Test, TestResult } from '../test-plan.ts';
11
+ import { diffAriaSnapshots } from '../utils/aria.ts';
12
+ import { HooksRunner } from '../utils/hooks-runner.ts';
13
+ import { createDebug, tag } from '../utils/logger.ts';
14
+ import { loop, pause } from '../utils/loop.ts';
15
+ import type { Agent } from './agent.ts';
16
+ import type { Conversation } from './conversation.ts';
17
+ import type { Navigator } from './navigator.ts';
18
+ import type { Provider } from './provider.ts';
19
+ import type { Researcher } from './researcher.ts';
20
+ import { locatorRule } from './rules.ts';
21
+ import { TaskAgent, isInteractive } from './task-agent.ts';
22
+ import { createAgentTools, createCodeceptJSTools } from './tools.ts';
23
+
24
+ const debugLog = createDebug('explorbot:bosun');
25
+
26
+ interface ComponentInfo {
27
+ name: string;
28
+ role: string;
29
+ locator: string;
30
+ section?: string;
31
+ }
32
+
33
+ interface InteractionResult {
34
+ component: string;
35
+ action: string;
36
+ result: 'success' | 'failed' | 'unknown';
37
+ description: string;
38
+ code?: string;
39
+ }
40
+
41
+ interface ComponentTest extends Test {
42
+ component?: ComponentInfo;
43
+ interactions?: InteractionResult[];
44
+ }
45
+
46
+ interface DrillOptions {
47
+ knowledgePath?: string;
48
+ maxComponents?: number;
49
+ interactive?: boolean;
50
+ }
51
+
52
+ export class Bosun extends TaskAgent implements Agent {
53
+ protected readonly ACTION_TOOLS = ['click', 'pressKey', 'form'];
54
+ emoji = '⚓';
55
+ private explorer: Explorer;
56
+ private provider: Provider;
57
+ private researcher: Researcher;
58
+ private navigator: Navigator;
59
+ private hooksRunner: HooksRunner;
60
+ private currentPlan?: Plan;
61
+ private currentConversation: Conversation | null = null;
62
+ private allResults: InteractionResult[] = [];
63
+ private agentTools: any;
64
+
65
+ MAX_ITERATIONS = 50;
66
+
67
+ constructor(explorer: Explorer, provider: Provider, researcher: Researcher, navigator: Navigator, agentTools?: any) {
68
+ super();
69
+ this.explorer = explorer;
70
+ this.provider = provider;
71
+ this.researcher = researcher;
72
+ this.navigator = navigator;
73
+ this.hooksRunner = new HooksRunner(explorer, explorer.getConfig());
74
+ this.agentTools = agentTools;
75
+ }
76
+
77
+ protected getNavigator(): Navigator {
78
+ return this.navigator;
79
+ }
80
+
81
+ protected getExperienceTracker(): ExperienceTracker {
82
+ return this.explorer.getStateManager().getExperienceTracker();
83
+ }
84
+
85
+ protected getKnowledgeTracker(): KnowledgeTracker {
86
+ return this.explorer.getKnowledgeTracker();
87
+ }
88
+
89
+ protected getProvider(): Provider {
90
+ return this.provider;
91
+ }
92
+
93
+ getSystemMessage(): string {
94
+ const currentUrl = this.explorer.getStateManager().getCurrentState()?.url;
95
+ const customPrompt = this.provider.getSystemPromptForAgent('bosun', currentUrl);
96
+ return dedent`
97
+ <role>
98
+ You are a senior QA automation engineer focused on learning how to interact with UI components.
99
+ Your goal is to systematically discover all possible interactions with each component and document what works.
100
+ </role>
101
+
102
+ <approach>
103
+ 1. Review the UI map to understand all available components
104
+ 2. Create a plan listing all components to drill using drill_plan tool
105
+ 3. For each component, try appropriate interactions using click, form tools
106
+ 4. Use drill_record to document successful interactions
107
+ 5. If an interaction fails multiple times, use drill_ask for help (in interactive mode)
108
+ 6. Call drill_finish when all components have been tested
109
+ </approach>
110
+
111
+ <rules>
112
+ - Focus on one component at a time
113
+ - Try multiple locator strategies if one fails
114
+ - Document what each interaction does (opens modal, navigates, etc.)
115
+ - Skip decorative or non-interactive elements
116
+ - Restore page state after each interaction (press Escape or navigate back)
117
+ </rules>
118
+
119
+ ${locatorRule}
120
+
121
+ ${customPrompt || ''}
122
+ `;
123
+ }
124
+
125
+ async drill(opts: DrillOptions = {}): Promise<Plan> {
126
+ const { knowledgePath, maxComponents = 20, interactive = isInteractive() } = opts;
127
+ const state = this.explorer.getStateManager().getCurrentState();
128
+ if (!state) throw new Error('No page state available');
129
+
130
+ const sessionName = `bosun_${Date.now().toString(36)}`;
131
+ this.allResults = [];
132
+
133
+ return Observability.run(`bosun: ${state.url}`, { tags: ['bosun'], sessionId: sessionName }, async () => {
134
+ tag('info').log(`Bosun starting drill on ${state.url}`);
135
+ setActivity(`${this.emoji} Researching page for drilling...`, 'action');
136
+
137
+ await this.hooksRunner.runBeforeHook('bosun', state.url);
138
+
139
+ const research = await this.researcher.research(state, { screenshot: true, force: true });
140
+
141
+ this.currentPlan = new Plan(`Drill: ${state.url}`);
142
+ this.currentPlan.url = state.url;
143
+
144
+ const conversation = this.provider.startConversation(this.getSystemMessage(), 'bosun');
145
+ this.currentConversation = conversation;
146
+
147
+ const initialPrompt = await this.buildInitialPrompt(state, research, maxComponents);
148
+ conversation.addUserText(initialPrompt);
149
+
150
+ const drillTask = new Task(`Drill session: ${state.url}`, state.url);
151
+ const codeceptjsTools = createCodeceptJSTools(this.explorer, drillTask);
152
+ const drillFlowTools = this.createDrillFlowTools(state, interactive);
153
+
154
+ const tools = {
155
+ ...codeceptjsTools,
156
+ ...drillFlowTools,
157
+ ...this.agentTools,
158
+ };
159
+
160
+ let drillFinished = false;
161
+
162
+ await loop(
163
+ async ({ stop, iteration }) => {
164
+ debugLog(`Drill iteration ${iteration}`);
165
+ setActivity(`${this.emoji} Drilling components...`, 'action');
166
+
167
+ const currentState = ActionResult.fromState(this.explorer.getStateManager().getCurrentState()!);
168
+
169
+ if (iteration > 1) {
170
+ conversation.cleanupTag('page_aria', '...cleaned aria snapshot...', 2);
171
+ const contextUpdate = await this.buildContextUpdate(currentState);
172
+ conversation.addUserText(contextUpdate);
173
+ }
174
+
175
+ const result = await this.provider.invokeConversation(conversation, tools, {
176
+ maxToolRoundtrips: 5,
177
+ toolChoice: 'required',
178
+ });
179
+
180
+ if (!result) throw new Error('Failed to get response from provider');
181
+
182
+ const toolExecutions = result.toolExecutions || [];
183
+ this.trackToolExecutions(toolExecutions);
184
+
185
+ for (const execution of toolExecutions) {
186
+ if (execution.wasSuccessful && this.ACTION_TOOLS.includes(execution.toolName)) {
187
+ const componentName = execution.input?.explanation || 'unknown';
188
+ this.allResults.push({
189
+ component: componentName,
190
+ action: execution.toolName,
191
+ result: 'success',
192
+ description: execution.output?.message || 'Action completed',
193
+ code: execution.output?.code,
194
+ });
195
+ }
196
+ }
197
+
198
+ const finishExecution = toolExecutions.find((e: any) => e.toolName === 'drill_finish');
199
+ if (finishExecution) {
200
+ drillFinished = true;
201
+ stop();
202
+ return;
203
+ }
204
+
205
+ if (iteration >= this.MAX_ITERATIONS) {
206
+ tag('warning').log('Max iterations reached');
207
+ stop();
208
+ }
209
+ },
210
+ {
211
+ maxAttempts: this.MAX_ITERATIONS,
212
+ interruptPrompt: 'Drill interrupted. Enter instruction (or "stop" to end):',
213
+ observability: {
214
+ agent: 'bosun',
215
+ sessionId: sessionName,
216
+ },
217
+ catch: async ({ error, stop }) => {
218
+ tag('error').log(`Drill error: ${error}`);
219
+ stop();
220
+ },
221
+ }
222
+ );
223
+
224
+ await this.saveToExperience(state, this.allResults);
225
+
226
+ if (knowledgePath) {
227
+ await this.saveToKnowledge(knowledgePath, state, this.allResults);
228
+ }
229
+
230
+ await this.hooksRunner.runAfterHook('bosun', state.url);
231
+ this.logSummary();
232
+
233
+ return this.currentPlan;
234
+ });
235
+ }
236
+
237
+ private async buildInitialPrompt(state: any, research: string, maxComponents: number): Promise<string> {
238
+ const actionResult = ActionResult.fromState(state);
239
+ const knowledge = this.getKnowledge(actionResult);
240
+ const experience = this.getExperience(actionResult);
241
+
242
+ return dedent`
243
+ <task>
244
+ Drill all interactive components on this page to learn how to interact with them.
245
+ Maximum components to drill: ${maxComponents}
246
+ </task>
247
+
248
+ <page>
249
+ URL: ${state.url}
250
+ Title: ${state.title || 'Unknown'}
251
+ </page>
252
+
253
+ <page_ui_map>
254
+ ${research}
255
+ </page_ui_map>
256
+
257
+ <page_aria>
258
+ ${actionResult.getInteractiveARIA()}
259
+ </page_aria>
260
+
261
+ ${knowledge}
262
+ ${experience}
263
+
264
+ <instructions>
265
+ 1. First, call drill_plan to create a list of components to test
266
+ 2. Then systematically test each component using click or form tools
267
+ 3. Use drill_record to save observations about what each component does
268
+ 4. Press Escape or use drill_restore to reset state between tests
269
+ 5. Call drill_finish when all components have been tested
270
+ </instructions>
271
+ `;
272
+ }
273
+
274
+ private async buildContextUpdate(currentState: ActionResult): Promise<string> {
275
+ const remainingComponents = this.currentPlan?.tests.filter((t) => !t.hasFinished).length || 0;
276
+
277
+ return dedent`
278
+ <context_update>
279
+ Current URL: ${currentState.url}
280
+ Components remaining: ${remainingComponents}
281
+ Successful interactions so far: ${this.allResults.filter((r) => r.result === 'success').length}
282
+ </context_update>
283
+
284
+ <page_aria>
285
+ ${currentState.getInteractiveARIA()}
286
+ </page_aria>
287
+
288
+ Continue drilling components. Test each one and record what it does.
289
+ `;
290
+ }
291
+
292
+ private createDrillFlowTools(originalState: any, interactive: boolean) {
293
+ const originalUrl = originalState.url;
294
+
295
+ return {
296
+ drill_plan: tool({
297
+ description: 'Create a plan of components to drill. Call this first to identify all testable components from the UI map.',
298
+ inputSchema: z.object({
299
+ components: z.array(
300
+ z.object({
301
+ name: z.string().describe('Display name of the component'),
302
+ role: z.string().describe('ARIA role (button, link, textbox, combobox, etc.)'),
303
+ locator: z.string().describe('Best locator for this component'),
304
+ section: z.string().optional().describe('Section of the page where component is located'),
305
+ })
306
+ ),
307
+ }),
308
+ execute: async ({ components }) => {
309
+ for (const comp of components) {
310
+ const task = new Test(`Learn: ${comp.name} (${comp.role})`, 'normal', [`Discover interactions for ${comp.name}`], originalUrl) as ComponentTest;
311
+ task.component = comp;
312
+ task.interactions = [];
313
+ this.currentPlan!.addTest(task);
314
+ }
315
+
316
+ tag('info').log(`Created drill plan with ${components.length} components`);
317
+
318
+ return {
319
+ success: true,
320
+ message: `Plan created with ${components.length} components`,
321
+ components: components.map((c) => `${c.name} (${c.role})`),
322
+ instruction: 'Now test each component using click or form tools. Record observations with drill_record.',
323
+ };
324
+ },
325
+ }),
326
+
327
+ drill_record: tool({
328
+ description: 'Record what a component does after testing it. Call this after each successful interaction.',
329
+ inputSchema: z.object({
330
+ component: z.string().describe('Component name that was tested'),
331
+ action: z.string().describe('Action performed (click, form)'),
332
+ result: z.string().describe('What happened (opened modal, navigated to X, showed dropdown, etc.)'),
333
+ code: z.string().optional().describe('The CodeceptJS code that worked'),
334
+ }),
335
+ execute: async ({ component, action, result, code }) => {
336
+ const task = this.findComponentTask(component);
337
+ if (task) {
338
+ task.addNote(`${action}: ${result}`, TestResult.PASSED);
339
+ task.finish(TestResult.PASSED);
340
+ }
341
+
342
+ this.allResults.push({
343
+ component,
344
+ action,
345
+ result: 'success',
346
+ description: result,
347
+ code,
348
+ });
349
+
350
+ tag('success').log(`${component}: ${action} -> ${result}`);
351
+
352
+ return {
353
+ success: true,
354
+ recorded: `${component}: ${action} -> ${result}`,
355
+ instruction: 'Continue testing other components or call drill_finish when done.',
356
+ };
357
+ },
358
+ }),
359
+
360
+ drill_restore: tool({
361
+ description: 'Restore page state after testing a component. Use when page navigated away or modal opened.',
362
+ inputSchema: z.object({
363
+ reason: z.string().describe('Why restoration is needed'),
364
+ }),
365
+ execute: async ({ reason }) => {
366
+ const currentState = this.explorer.getStateManager().getCurrentState();
367
+ const action = this.explorer.createAction();
368
+
369
+ if (currentState?.url !== originalUrl) {
370
+ await action.execute(`I.amOnPage("${originalUrl}")`);
371
+ return { success: true, action: 'navigated back', url: originalUrl };
372
+ }
373
+
374
+ await action.execute('I.pressKey("Escape")');
375
+ return { success: true, action: 'pressed Escape' };
376
+ },
377
+ }),
378
+
379
+ drill_skip: tool({
380
+ description: 'Skip a component that cannot be drilled.',
381
+ inputSchema: z.object({
382
+ component: z.string().describe('Component to skip'),
383
+ reason: z.string().describe('Why this component is being skipped'),
384
+ }),
385
+ execute: async ({ component, reason }) => {
386
+ const task = this.findComponentTask(component);
387
+ if (task) {
388
+ task.addNote(`Skipped: ${reason}`, TestResult.FAILED);
389
+ task.finish(TestResult.FAILED);
390
+ }
391
+
392
+ this.allResults.push({
393
+ component,
394
+ action: 'skip',
395
+ result: 'unknown',
396
+ description: reason,
397
+ });
398
+
399
+ tag('warning').log(`Skipped ${component}: ${reason}`);
400
+ return { success: true, skipped: component, reason };
401
+ },
402
+ }),
403
+
404
+ drill_ask: tool({
405
+ description: 'Ask the user for help when stuck on a component. Only available in interactive mode.',
406
+ inputSchema: z.object({
407
+ component: z.string().describe('Component you need help with'),
408
+ question: z.string().describe('What you need help with'),
409
+ triedLocators: z.array(z.string()).optional().describe('Locators already tried'),
410
+ }),
411
+ execute: async ({ component, question, triedLocators }) => {
412
+ if (!interactive) {
413
+ return { success: false, message: 'Not in interactive mode. Skip this component.' };
414
+ }
415
+
416
+ let prompt = `Help needed for "${component}"\n${question}`;
417
+ if (triedLocators?.length) {
418
+ prompt += `\n\nAlready tried:\n${triedLocators.map((l) => ` - ${l}`).join('\n')}`;
419
+ }
420
+ prompt += '\n\nYour CodeceptJS command ("skip" to continue):';
421
+
422
+ const userInput = await pause(prompt);
423
+
424
+ if (!userInput || userInput.toLowerCase() === 'skip') {
425
+ return { success: false, skipped: true, instruction: 'Use drill_skip to skip this component.' };
426
+ }
427
+
428
+ return {
429
+ success: true,
430
+ userSuggestion: userInput,
431
+ instruction: `Try this command: ${userInput}`,
432
+ };
433
+ },
434
+ }),
435
+
436
+ drill_finish: tool({
437
+ description: 'Finish the drill session. Call when all components have been tested.',
438
+ inputSchema: z.object({
439
+ summary: z.string().describe('Summary of what was learned during drilling'),
440
+ }),
441
+ execute: async ({ summary }) => {
442
+ for (const test of this.currentPlan!.tests) {
443
+ if (!test.hasFinished) {
444
+ test.addNote('Not tested');
445
+ test.finish(TestResult.FAILED);
446
+ }
447
+ }
448
+
449
+ tag('info').log(`Drill completed: ${summary}`);
450
+
451
+ return {
452
+ success: true,
453
+ totalComponents: this.currentPlan!.tests.length,
454
+ successfulInteractions: this.allResults.filter((r) => r.result === 'success').length,
455
+ summary,
456
+ };
457
+ },
458
+ }),
459
+ };
460
+ }
461
+
462
+ private findComponentTask(componentName: string): ComponentTest | undefined {
463
+ return this.currentPlan?.tests.find((t) => {
464
+ const ct = t as ComponentTest;
465
+ return ct.component?.name === componentName || t.scenario.includes(componentName);
466
+ }) as ComponentTest | undefined;
467
+ }
468
+
469
+ private async saveToExperience(state: any, results: InteractionResult[]): Promise<void> {
470
+ const experienceTracker = this.getExperienceTracker();
471
+ const actionResult = ActionResult.fromState(state);
472
+
473
+ const successfulInteractions = results.filter((r) => r.result === 'success' && r.code);
474
+
475
+ for (const interaction of successfulInteractions) {
476
+ await experienceTracker.saveSuccessfulResolution(actionResult, `Drill ${interaction.action}: ${interaction.component}`, interaction.code!, interaction.description);
477
+ }
478
+
479
+ if (successfulInteractions.length > 0) {
480
+ tag('success').log(`Saved ${successfulInteractions.length} interactions to experience`);
481
+ }
482
+ }
483
+
484
+ private async saveToKnowledge(knowledgePath: string, state: any, results: InteractionResult[]): Promise<void> {
485
+ const knowledgeTracker = this.getKnowledgeTracker();
486
+ const successfulInteractions = results.filter((r) => r.result === 'success');
487
+
488
+ if (successfulInteractions.length === 0) {
489
+ tag('warning').log('No successful interactions to save to knowledge');
490
+ return;
491
+ }
492
+
493
+ const content = this.generateKnowledgeContent(state, successfulInteractions);
494
+ const result = knowledgeTracker.addKnowledge(knowledgePath, content);
495
+
496
+ tag('success').log(`Knowledge saved to: ${result.filePath}`);
497
+ }
498
+
499
+ private generateKnowledgeContent(state: any, interactions: InteractionResult[]): string {
500
+ const lines: string[] = [];
501
+ lines.push('# Component Interactions\n');
502
+ lines.push(`Learned interactions from drilling ${state.url}\n`);
503
+
504
+ const groupedByComponent = new Map<string, InteractionResult[]>();
505
+ for (const interaction of interactions) {
506
+ const existing = groupedByComponent.get(interaction.component) || [];
507
+ existing.push(interaction);
508
+ groupedByComponent.set(interaction.component, existing);
509
+ }
510
+
511
+ for (const [component, items] of groupedByComponent) {
512
+ lines.push(`\n## ${component}\n`);
513
+ for (const item of items) {
514
+ lines.push(`- **${item.action}**: ${item.description}`);
515
+ if (item.code) {
516
+ lines.push('```js');
517
+ lines.push(item.code);
518
+ lines.push('```');
519
+ }
520
+ }
521
+ }
522
+
523
+ return lines.join('\n');
524
+ }
525
+
526
+ private logSummary(): void {
527
+ if (!this.currentPlan) return;
528
+
529
+ const total = this.currentPlan.tests.length;
530
+ const passed = this.currentPlan.tests.filter((t) => t.isSuccessful).length;
531
+ const failed = this.currentPlan.tests.filter((t) => t.hasFailed).length;
532
+ const successfulInteractions = this.allResults.filter((r) => r.result === 'success').length;
533
+
534
+ tag('info').log('\nDrill Summary:');
535
+ tag('info').log(` Total components: ${total}`);
536
+ tag('success').log(` Successful: ${passed}`);
537
+ if (failed > 0) {
538
+ tag('warning').log(` Failed: ${failed}`);
539
+ }
540
+ tag('info').log(` Total interactions learned: ${successfulInteractions}`);
541
+
542
+ for (const test of this.currentPlan.tests) {
543
+ const componentTask = test as ComponentTest;
544
+ const status = test.isSuccessful ? '✓' : '✗';
545
+ const successCount = componentTask.interactions?.filter((i) => i.result === 'success').length || 0;
546
+ tag('step').log(` ${status} ${componentTask.component?.name || test.scenario}: ${successCount} interactions`);
547
+ }
548
+ }
549
+
550
+ getCurrentPlan(): Plan | undefined {
551
+ return this.currentPlan;
552
+ }
553
+
554
+ getConversation(): Conversation | null {
555
+ return this.currentConversation;
556
+ }
557
+ }
@@ -0,0 +1,116 @@
1
+ import { tool } from 'ai';
2
+ import { createBashTool } from 'bash-tool';
3
+ import dedent from 'dedent';
4
+ import { z } from 'zod';
5
+ import { ConfigParser } from '../../config.ts';
6
+ import { Test } from '../../test-plan.ts';
7
+ import { type Constructor, type ModeContext, resolveProjectRoot } from './mixin.ts';
8
+
9
+ let cachedBashTool: Awaited<ReturnType<typeof createBashTool>> | null = null;
10
+
11
+ export function WithIdleMode<T extends Constructor>(Base: T) {
12
+ return class extends Base {
13
+ async idleModeTools(ctx: ModeContext): Promise<Record<string, any>> {
14
+ const projectRoot = resolveProjectRoot();
15
+ const config = ConfigParser.getInstance().getConfig();
16
+ const knowledgeDir = config.dirs?.knowledge || 'knowledge';
17
+ const experienceDir = config.dirs?.experience || 'experience';
18
+
19
+ if (!cachedBashTool && projectRoot) {
20
+ cachedBashTool = await createBashTool({
21
+ destination: projectRoot,
22
+ onBeforeBashCall: ({ command }) => {
23
+ if (/\b(sudo|chmod|chown)\b/.test(command)) {
24
+ return { command: 'echo "Command not allowed" >&2 && exit 1' };
25
+ }
26
+ const writePatterns = [/>[^>]/, />>/, /\btee\b/, /\bmv\b/, /\bcp\b/, /\brm\b/];
27
+ if (writePatterns.some((p) => p.test(command)) && /\boutput[/\\]/.test(command)) {
28
+ return { command: 'echo "Write access to output/ is restricted" >&2 && exit 1' };
29
+ }
30
+ return { command };
31
+ },
32
+ });
33
+ }
34
+
35
+ const tools: Record<string, any> = {
36
+ updatePlan: tool({
37
+ description: 'Update the current plan by replacing or appending tests',
38
+ inputSchema: z.object({
39
+ action: z.enum(['replace', 'append']).optional().describe('replace clears existing tests, append keeps them'),
40
+ title: z.string().optional().describe('New plan title'),
41
+ tests: z
42
+ .array(
43
+ z.object({
44
+ scenario: z.string(),
45
+ priority: z.enum(['critical', 'important', 'high', 'normal', 'low']).optional(),
46
+ expected: z.array(z.string()).optional(),
47
+ })
48
+ )
49
+ .optional(),
50
+ }),
51
+ execute: async ({ action, title, tests }) => {
52
+ let plan = ctx.explorBot.getCurrentPlan();
53
+ if (!plan) {
54
+ plan = await ctx.explorBot.plan();
55
+ }
56
+ if (!plan) {
57
+ return { success: false, message: 'Plan unavailable' };
58
+ }
59
+ if (title) {
60
+ plan.title = title;
61
+ }
62
+ if (tests?.length) {
63
+ if (!action || action === 'replace') {
64
+ plan.tests.length = 0;
65
+ }
66
+ const currentUrl = ctx.explorBot.getExplorer().getStateManager().getCurrentState()?.url || '';
67
+ for (const testInput of tests) {
68
+ const priority = testInput.priority || 'normal';
69
+ const expected = testInput.expected?.length ? testInput.expected : [];
70
+ const test = new Test(testInput.scenario, priority, expected, currentUrl);
71
+ plan.addTest(test);
72
+ }
73
+ }
74
+ plan.updateStatus();
75
+ return { success: true, tests: plan.tests.length };
76
+ },
77
+ }),
78
+ };
79
+
80
+ if (cachedBashTool) {
81
+ tools.bash = cachedBashTool.bash;
82
+ }
83
+
84
+ return tools;
85
+ }
86
+
87
+ idleModePrompt(): string {
88
+ const config = ConfigParser.getInstance().getConfig();
89
+ const knowledgeDir = config.dirs?.knowledge || 'knowledge';
90
+ const experienceDir = config.dirs?.experience || 'experience';
91
+
92
+ return dedent`
93
+ <idle_capabilities>
94
+ - Plan management: updatePlan() — replace or append tests in the current plan
95
+ - bash() — run shell commands for file operations
96
+ - READ from: ${knowledgeDir}/, ${experienceDir}/, output/
97
+ - WRITE to: ${knowledgeDir}/, ${experienceDir}/ only (NOT output/)
98
+ - Use ls to list files, cat to read small files
99
+ - Use head/tail for large files to avoid excessive output
100
+ - Use grep to search file contents
101
+ </idle_capabilities>
102
+
103
+ <knowledge_saving>
104
+ When user shares credentials, selectors, or important domain info during conversation,
105
+ suggest saving it to a knowledge file using bash tool.
106
+ Format: YAML frontmatter with url pattern, then content.
107
+ </knowledge_saving>
108
+ `;
109
+ }
110
+ };
111
+ }
112
+
113
+ export interface IdleModeMethods {
114
+ idleModeTools(ctx: ModeContext): Promise<Record<string, any>>;
115
+ idleModePrompt(): string;
116
+ }
@@ -0,0 +1,22 @@
1
+ import { dirname } from 'node:path';
2
+ import { ConfigParser } from '../../config.ts';
3
+ import type { ExplorBot } from '../../explorbot.ts';
4
+ import type { Task } from '../../test-plan.ts';
5
+ import { createDebug } from '../../utils/logger.js';
6
+
7
+ export type Constructor<T = object> = new (...args: any[]) => T;
8
+
9
+ export const debugLog = createDebug('explorbot:captain');
10
+
11
+ export type CaptainMode = 'idle' | 'web' | 'test';
12
+
13
+ export interface ModeContext {
14
+ explorBot: ExplorBot;
15
+ task: Task;
16
+ }
17
+
18
+ export function resolveProjectRoot(): string | null {
19
+ const configPath = ConfigParser.getInstance().getConfigPath();
20
+ if (!configPath) return null;
21
+ return dirname(configPath);
22
+ }