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,629 @@
1
+ const INTERACTIVE_ROLES = new Set(
2
+ [
3
+ 'button',
4
+ 'link',
5
+ 'textbox',
6
+ 'searchbox',
7
+ 'checkbox',
8
+ 'radio',
9
+ 'radiogroup',
10
+ 'switch',
11
+ 'combobox',
12
+ 'listbox',
13
+ 'listitem',
14
+ 'menu',
15
+ 'menubar',
16
+ 'menuitem',
17
+ 'menuitemcheckbox',
18
+ 'menuitemradio',
19
+ 'option',
20
+ 'tab',
21
+ 'tabpanel',
22
+ 'tablist',
23
+ 'slider',
24
+ 'spinbutton',
25
+ 'tree',
26
+ 'treeitem',
27
+ 'grid',
28
+ 'gridcell',
29
+ 'row',
30
+ 'rowheader',
31
+ 'columnheader',
32
+ 'toolbar',
33
+ 'progressbar',
34
+ 'buttonmenu',
35
+ 'comboboxbutton',
36
+ 'gridcellbutton',
37
+ ].map((role) => role.toLowerCase())
38
+ );
39
+
40
+ const IGNORED_CONTAINER_ROLES = new Set(['navigation']);
41
+
42
+ type AriaNode = {
43
+ role: string;
44
+ name?: string;
45
+ value?: string | boolean | null;
46
+ attributes: Record<string, string | boolean | null>;
47
+ children: AriaNode[];
48
+ rawChildren?: AriaNode[];
49
+ };
50
+
51
+ const serializeChildContent = (node: AriaNode): string => {
52
+ const children = node.rawChildren || node.children;
53
+ const parts: string[] = [];
54
+ for (const child of children) {
55
+ let part = child.role;
56
+ if (child.name) part += ` "${child.name}"`;
57
+ const nested = serializeChildContent(child);
58
+ if (nested) part += ` > ${nested}`;
59
+ parts.push(part);
60
+ }
61
+ return parts.join(', ');
62
+ };
63
+
64
+ const buildInteractiveEntry = (node: AriaNode): Record<string, unknown> | null => {
65
+ if (!INTERACTIVE_ROLES.has(node.role)) {
66
+ return null;
67
+ }
68
+ const entry: Record<string, unknown> = { role: node.role };
69
+ if (node.name && node.name.trim() !== '') {
70
+ entry.name = node.name.trim();
71
+ }
72
+ if (node.value !== undefined && node.value !== null) {
73
+ const valueText = `${node.value}`.trim();
74
+ if (valueText !== '') {
75
+ entry.value = node.value;
76
+ }
77
+ }
78
+ for (const [key, value] of Object.entries(node.attributes)) {
79
+ if (value === undefined || value === null) {
80
+ continue;
81
+ }
82
+ if (value === '') {
83
+ continue;
84
+ }
85
+ entry[key] = value;
86
+ }
87
+ const hasData = Object.keys(entry).some((key) => key !== 'role');
88
+ const entryName = typeof entry.name === 'string' ? entry.name : '';
89
+ const hasValue = Object.prototype.hasOwnProperty.call(entry, 'value');
90
+ const isButtonOrLink = node.role === 'button' || node.role === 'link';
91
+ let shouldInclude = hasData;
92
+ if (!shouldInclude && hasValue) {
93
+ shouldInclude = true;
94
+ }
95
+ if (isButtonOrLink && !entryName && !hasValue) {
96
+ const resolved = resolveDisplayName(node);
97
+ if (resolved) {
98
+ entry.name = resolved;
99
+ } else {
100
+ entry.unnamed = true;
101
+ }
102
+ shouldInclude = true;
103
+ }
104
+ if (!shouldInclude) {
105
+ return null;
106
+ }
107
+ return entry;
108
+ };
109
+
110
+ const normalizeScalar = (input: string): string | boolean | null => {
111
+ let value = input.trim();
112
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
113
+ value = value.slice(1, -1);
114
+ }
115
+ const lower = value.toLowerCase();
116
+ if (lower === 'true') {
117
+ return true;
118
+ }
119
+ if (lower === 'false') {
120
+ return false;
121
+ }
122
+ if (lower === 'null') {
123
+ return null;
124
+ }
125
+ return value;
126
+ };
127
+
128
+ const tokenizeAttributes = (input: string): string[] => {
129
+ const tokens: string[] = [];
130
+ let current = '';
131
+ let inQuotes = false;
132
+ let quoteChar = '';
133
+ for (let i = 0; i < input.length; i += 1) {
134
+ const char = input[i];
135
+ if ((char === '"' || char === "'") && input[i - 1] !== '\\') {
136
+ if (inQuotes && quoteChar === char) {
137
+ inQuotes = false;
138
+ quoteChar = '';
139
+ } else if (!inQuotes) {
140
+ inQuotes = true;
141
+ quoteChar = char;
142
+ }
143
+ current += char;
144
+ continue;
145
+ }
146
+ if (!inQuotes && (char === ' ' || char === ',')) {
147
+ if (current.trim() !== '') {
148
+ tokens.push(current.trim());
149
+ }
150
+ current = '';
151
+ continue;
152
+ }
153
+ current += char;
154
+ }
155
+ if (current.trim() !== '') {
156
+ tokens.push(current.trim());
157
+ }
158
+ return tokens;
159
+ };
160
+
161
+ const parseAttributes = (input: string): Record<string, string | boolean | null> => {
162
+ const attributes: Record<string, string | boolean | null> = {};
163
+ const tokens = tokenizeAttributes(input);
164
+ for (const token of tokens) {
165
+ if (token === '') {
166
+ continue;
167
+ }
168
+ const separatorIndex = token.indexOf('=');
169
+ if (separatorIndex === -1) {
170
+ attributes[token.toLowerCase()] = true;
171
+ continue;
172
+ }
173
+ const key = token.slice(0, separatorIndex).trim().toLowerCase();
174
+ const valueRaw = token.slice(separatorIndex + 1).trim();
175
+ attributes[key] = normalizeScalar(valueRaw);
176
+ }
177
+ return attributes;
178
+ };
179
+
180
+ const parseHeader = (header: string): { role: string; name?: string; attributes: Record<string, string | boolean | null> } | null => {
181
+ if (!header) {
182
+ return null;
183
+ }
184
+ let index = 0;
185
+ const length = header.length;
186
+ while (index < length && header[index] === ' ') {
187
+ index += 1;
188
+ }
189
+ let roleEnd = index;
190
+ while (roleEnd < length) {
191
+ const char = header[roleEnd];
192
+ if (char === ' ' || char === '[' || char === '"' || char === "'") {
193
+ break;
194
+ }
195
+ roleEnd += 1;
196
+ }
197
+ const role = header.slice(index, roleEnd).trim().toLowerCase();
198
+ if (!role) {
199
+ return null;
200
+ }
201
+ let name: string | undefined;
202
+ const attributes: Record<string, string | boolean | null> = {};
203
+ index = roleEnd;
204
+ while (index < length) {
205
+ const char = header[index];
206
+ if (char === ' ') {
207
+ index += 1;
208
+ continue;
209
+ }
210
+ if (char === '"' || char === "'") {
211
+ const quoteChar = char;
212
+ index += 1;
213
+ let value = '';
214
+ while (index < length) {
215
+ const currentChar = header[index];
216
+ if (currentChar === quoteChar && header[index - 1] !== '\\') {
217
+ index += 1;
218
+ break;
219
+ }
220
+ value += currentChar;
221
+ index += 1;
222
+ }
223
+ if (!name) {
224
+ name = value;
225
+ }
226
+ continue;
227
+ }
228
+ if (char === '[') {
229
+ const end = header.indexOf(']', index);
230
+ const content = end === -1 ? header.slice(index + 1) : header.slice(index + 1, end);
231
+ const parsed = parseAttributes(content);
232
+ for (const [key, value] of Object.entries(parsed)) {
233
+ attributes[key] = value;
234
+ }
235
+ index = end === -1 ? length : end + 1;
236
+ continue;
237
+ }
238
+ break;
239
+ }
240
+ return { role, name, attributes };
241
+ };
242
+
243
+ const splitHeaderValue = (content: string): { header: string; value: string | null } => {
244
+ let activeQuote: string | null = null;
245
+ let bracketDepth = 0;
246
+ for (let i = 0; i < content.length; i += 1) {
247
+ const char = content[i];
248
+ if ((char === '"' || char === "'") && content[i - 1] !== '\\') {
249
+ if (activeQuote === char) {
250
+ activeQuote = null;
251
+ } else if (!activeQuote) {
252
+ activeQuote = char;
253
+ }
254
+ continue;
255
+ }
256
+ if (!activeQuote) {
257
+ if (char === '[') {
258
+ bracketDepth += 1;
259
+ continue;
260
+ }
261
+ if (char === ']') {
262
+ if (bracketDepth > 0) {
263
+ bracketDepth -= 1;
264
+ }
265
+ continue;
266
+ }
267
+ if (char === ':') {
268
+ if (bracketDepth === 0) {
269
+ const header = content.slice(0, i).trimEnd();
270
+ const value = content.slice(i + 1).trimStart();
271
+ return { header, value: value === '' ? null : value };
272
+ }
273
+ }
274
+ }
275
+ }
276
+ return { header: content.trim(), value: null };
277
+ };
278
+
279
+ const pruneNodes = (nodes: AriaNode[], keepNamed = false): AriaNode[] => {
280
+ const result: AriaNode[] = [];
281
+ for (const node of nodes) {
282
+ const children = pruneNodes(node.children, keepNamed);
283
+ if (IGNORED_CONTAINER_ROLES.has(node.role)) {
284
+ result.push(...children);
285
+ continue;
286
+ }
287
+ const interactive = INTERACTIVE_ROLES.has(node.role);
288
+ if (!interactive && children.length === 0) {
289
+ if (!keepNamed || (!node.name && node.value === undefined)) {
290
+ continue;
291
+ }
292
+ }
293
+ result.push({ ...node, children, rawChildren: node.children });
294
+ }
295
+ return result;
296
+ };
297
+
298
+ const parseAriaSnapshot = (snapshot: string | null, keepNamed = false): AriaNode[] => {
299
+ if (!snapshot) {
300
+ return [];
301
+ }
302
+ const roots: AriaNode[] = [];
303
+ const stack: Array<{ depth: number; node: AriaNode }> = [];
304
+ const lines = snapshot.split(/\r?\n/);
305
+ for (const line of lines) {
306
+ if (!line.trim()) {
307
+ continue;
308
+ }
309
+ const indentMatch = line.match(/^\s*/);
310
+ const indent = indentMatch ? indentMatch[0].length : 0;
311
+ const trimmed = line.slice(indent);
312
+ if (!trimmed.startsWith('-')) {
313
+ continue;
314
+ }
315
+ const content = trimmed.slice(1).trim();
316
+ if (content === '') {
317
+ continue;
318
+ }
319
+ const { header, value } = splitHeaderValue(content);
320
+ const parsedHeader = parseHeader(header);
321
+ if (!parsedHeader) {
322
+ continue;
323
+ }
324
+ const node: AriaNode = {
325
+ role: parsedHeader.role,
326
+ attributes: { ...parsedHeader.attributes },
327
+ children: [],
328
+ };
329
+ if (parsedHeader.name && parsedHeader.name.trim() !== '') {
330
+ node.name = parsedHeader.name.trim();
331
+ }
332
+ if (value !== null) {
333
+ const normalizedValue = normalizeScalar(value);
334
+ if (normalizedValue !== '' && normalizedValue !== undefined) {
335
+ node.value = normalizedValue;
336
+ }
337
+ }
338
+ const depth = Math.floor(indent / 2);
339
+ while (stack.length > 0 && stack[stack.length - 1].depth >= depth) {
340
+ stack.pop();
341
+ }
342
+ if (stack.length === 0) {
343
+ roots.push(node);
344
+ } else {
345
+ stack[stack.length - 1].node.children.push(node);
346
+ }
347
+ stack.push({ depth, node });
348
+ }
349
+ return pruneNodes(roots, keepNamed);
350
+ };
351
+
352
+ export interface FocusAreaResult {
353
+ detected: boolean;
354
+ type: 'dialog' | 'modal' | null;
355
+ name: string | null;
356
+ }
357
+
358
+ export const detectFocusArea = (snapshot: string | null): FocusAreaResult => {
359
+ const nodes = parseAriaSnapshot(snapshot);
360
+
361
+ const findFocusArea = (nodeList: AriaNode[]): FocusAreaResult | null => {
362
+ for (const node of nodeList) {
363
+ if (node.role === 'dialog' || node.role === 'alertdialog') {
364
+ return {
365
+ detected: true,
366
+ type: 'dialog',
367
+ name: node.name || null,
368
+ };
369
+ }
370
+
371
+ if (node.attributes.modal === true || node.attributes.modal === 'true') {
372
+ return {
373
+ detected: true,
374
+ type: 'modal',
375
+ name: node.name || null,
376
+ };
377
+ }
378
+
379
+ const childResult = findFocusArea(node.children);
380
+ if (childResult) {
381
+ return childResult;
382
+ }
383
+ }
384
+ return null;
385
+ };
386
+
387
+ const result = findFocusArea(nodes);
388
+ return result || { detected: false, type: null, name: null };
389
+ };
390
+
391
+ export const collectInteractiveNodes = (snapshot: string | null): Array<Record<string, unknown>> => {
392
+ const nodes = parseAriaSnapshot(snapshot);
393
+ const result: Array<Record<string, unknown>> = [];
394
+ const visit = (node: AriaNode) => {
395
+ if (IGNORED_CONTAINER_ROLES.has(node.role)) {
396
+ node.children.forEach(visit);
397
+ return;
398
+ }
399
+ const entry = buildInteractiveEntry(node);
400
+ if (entry) {
401
+ result.push(entry);
402
+ }
403
+ node.children.forEach(visit);
404
+ };
405
+ nodes.forEach(visit);
406
+ return result;
407
+ };
408
+
409
+ const renderNodeLine = (role: string, name: string | undefined, attrs: Record<string, string | boolean | null>, value: string | boolean | null | undefined): string => {
410
+ let line = role;
411
+ const displayName = name?.trim();
412
+ if (displayName) line += ` "${displayName}"`;
413
+ const attrStr = Object.entries(attrs)
414
+ .filter(([, v]) => v !== undefined && v !== null && v !== '')
415
+ .map(([k, v]) => (v === true ? k : `${k}=${v}`))
416
+ .join(' ');
417
+ if (attrStr) line += ` [${attrStr}]`;
418
+ if (value !== undefined && value !== null) {
419
+ const text = `${value}`.trim();
420
+ if (text) line += `: ${text}`;
421
+ }
422
+ return line;
423
+ };
424
+
425
+ const formatSummary = (node: Record<string, unknown>): string => {
426
+ const role = typeof node.role === 'string' ? node.role : '';
427
+ if (!role) return '';
428
+ const name = typeof node.name === 'string' ? node.name : undefined;
429
+ const attrs: Record<string, string | boolean | null> = {};
430
+ for (const key of Object.keys(node)
431
+ .filter((k) => k !== 'role' && k !== 'name' && k !== 'value')
432
+ .sort()) {
433
+ attrs[key] = node[key] as string | boolean | null;
434
+ }
435
+ const value = Object.prototype.hasOwnProperty.call(node, 'value') ? (node.value as string | boolean | null) : undefined;
436
+ return renderNodeLine(role, name, attrs, value);
437
+ };
438
+
439
+ type FlatInteractiveNode = {
440
+ path: string;
441
+ summary: string;
442
+ };
443
+
444
+ const flattenInteractiveNodes = (snapshot: string | null): FlatInteractiveNode[] => {
445
+ const nodes = parseAriaSnapshot(snapshot);
446
+ const result: FlatInteractiveNode[] = [];
447
+ const visit = (node: AriaNode, path: string) => {
448
+ if (!IGNORED_CONTAINER_ROLES.has(node.role)) {
449
+ const entry = buildInteractiveEntry(node);
450
+ if (entry) {
451
+ const summary = formatSummary(entry);
452
+ if (summary !== '') {
453
+ result.push({ path, summary });
454
+ }
455
+ }
456
+ }
457
+ node.children.forEach((child, index) => {
458
+ const childPath = path === '' ? `${index}` : `${path}.${index}`;
459
+ visit(child, childPath);
460
+ });
461
+ };
462
+ nodes.forEach((node, index) => {
463
+ visit(node, `${index}`);
464
+ });
465
+ return result;
466
+ };
467
+
468
+ const buildCountMap = (items: string[]): Map<string, number> => {
469
+ const map = new Map<string, number>();
470
+ for (const item of items) {
471
+ if (item === '') {
472
+ continue;
473
+ }
474
+ map.set(item, (map.get(item) ?? 0) + 1);
475
+ }
476
+ return map;
477
+ };
478
+
479
+ const formatDiffItem = (item: string, count: number): string => {
480
+ if (count > 1) {
481
+ return `${item} (x${count})`;
482
+ }
483
+ return item;
484
+ };
485
+
486
+ export interface FocusedElementInfo {
487
+ role: string;
488
+ name: string;
489
+ value?: string;
490
+ attributes?: string[];
491
+ }
492
+
493
+ export function extractFocusedElement(ariaSnapshot: string | null): FocusedElementInfo | null {
494
+ if (!ariaSnapshot) return null;
495
+
496
+ const focusedMatch = ariaSnapshot.match(/-\s*(\w+)\s+"([^"]*)"([^:\n]*)\[focused\](?::\s*(.*))?/);
497
+ if (!focusedMatch) return null;
498
+
499
+ const [, role, name, attributesStr, value] = focusedMatch;
500
+
501
+ const attributes: string[] = [];
502
+ if (attributesStr) {
503
+ const attrMatches = attributesStr.matchAll(/\[([^\]]+)\]/g);
504
+ for (const match of attrMatches) {
505
+ if (match[1] !== 'focused') {
506
+ attributes.push(match[1]);
507
+ }
508
+ }
509
+ }
510
+
511
+ return {
512
+ role,
513
+ name,
514
+ ...(value && { value: value.trim() }),
515
+ ...(attributes.length > 0 && { attributes }),
516
+ };
517
+ }
518
+
519
+ export function parseAriaLocator(ariaStr: string): { role: string; text: string } | null {
520
+ const trimmed = ariaStr.trim();
521
+ if (trimmed === '-' || trimmed === '' || trimmed === '"-"') return null;
522
+
523
+ const match = trimmed.match(/\{\s*["']?role["']?\s*:\s*['"]([^'"]+)['"]\s*,\s*["']?text["']?\s*:\s*['"]([^'"]*)['"]\s*\}/);
524
+ if (!match) return null;
525
+
526
+ return { role: match[1], text: match[2] };
527
+ }
528
+
529
+ export const diffAriaSnapshots = (previous: string | null, current: string | null): string | null => {
530
+ const previousEntries = flattenInteractiveNodes(previous);
531
+ const currentEntries = flattenInteractiveNodes(current);
532
+ const previousTotals = buildCountMap(previousEntries.map((entry) => entry.summary));
533
+ const currentTotals = buildCountMap(currentEntries.map((entry) => entry.summary));
534
+ const added: string[] = [];
535
+ const removed: string[] = [];
536
+ const allSummaries = new Set<string>([...previousTotals.keys(), ...currentTotals.keys()]);
537
+ for (const summary of allSummaries) {
538
+ const before = previousTotals.get(summary) ?? 0;
539
+ const after = currentTotals.get(summary) ?? 0;
540
+ if (after > before) {
541
+ for (let i = 0; i < after - before; i += 1) {
542
+ added.push(summary);
543
+ }
544
+ }
545
+ if (before > after) {
546
+ for (let i = 0; i < before - after; i += 1) {
547
+ removed.push(summary);
548
+ }
549
+ }
550
+ }
551
+ const previousByPath = new Map<string, string>();
552
+ for (const entry of previousEntries) {
553
+ previousByPath.set(entry.path, entry.summary);
554
+ }
555
+ const currentByPath = new Map<string, string>();
556
+ for (const entry of currentEntries) {
557
+ currentByPath.set(entry.path, entry.summary);
558
+ }
559
+ for (const [path, beforeSummary] of previousByPath.entries()) {
560
+ const afterSummary = currentByPath.get(path);
561
+ if (!afterSummary || afterSummary === beforeSummary) {
562
+ continue;
563
+ }
564
+ const totalsEqualAfter = (currentTotals.get(afterSummary) ?? 0) === (previousTotals.get(afterSummary) ?? 0);
565
+ const totalsEqualBefore = (currentTotals.get(beforeSummary) ?? 0) === (previousTotals.get(beforeSummary) ?? 0);
566
+ if (!totalsEqualAfter || !totalsEqualBefore) {
567
+ continue;
568
+ }
569
+ const beforeExistsElsewhere = currentEntries.some((entry) => entry.path !== path && entry.summary === beforeSummary);
570
+ const afterExistsElsewhere = previousEntries.some((entry) => entry.path !== path && entry.summary === afterSummary);
571
+ if (beforeExistsElsewhere && afterExistsElsewhere) {
572
+ continue;
573
+ }
574
+ added.push(afterSummary);
575
+ removed.push(beforeSummary);
576
+ }
577
+ if (added.length === 0 && removed.length === 0) {
578
+ return null;
579
+ }
580
+ const lines: string[] = ['ariaDiff:'];
581
+ const addedSummary = buildCountMap(added);
582
+ if (addedSummary.size === 0) {
583
+ lines.push(' added: []');
584
+ } else {
585
+ lines.push(' added:');
586
+ Array.from(addedSummary.entries())
587
+ .sort(([a], [b]) => a.localeCompare(b))
588
+ .forEach(([item, count]) => {
589
+ lines.push(` - ${formatDiffItem(item, count)}`);
590
+ });
591
+ }
592
+ if (removed.length === 0) {
593
+ lines.push(' removed: []');
594
+ } else {
595
+ lines.push(` removed: ${removed.length} interactive elements`);
596
+ }
597
+ return lines.join('\n');
598
+ };
599
+
600
+ const resolveDisplayName = (node: AriaNode): string | undefined => {
601
+ if (node.name) return node.name;
602
+ const isButtonOrLink = node.role === 'button' || node.role === 'link';
603
+ if (!isButtonOrLink) return undefined;
604
+ const childContent = serializeChildContent(node);
605
+ if (childContent) return `{${childContent}}`;
606
+ return undefined;
607
+ };
608
+
609
+ const serializeAriaNodes = (nodes: AriaNode[], depth = 0): string => {
610
+ const lines: string[] = [];
611
+ for (const node of nodes) {
612
+ const indent = ' '.repeat(depth);
613
+ let line = `${indent}- ${renderNodeLine(node.role, resolveDisplayName(node), node.attributes, node.value)}`;
614
+ if (node.children.length > 0) {
615
+ line += ':';
616
+ }
617
+ lines.push(line);
618
+ if (node.children.length > 0) {
619
+ lines.push(serializeAriaNodes(node.children, depth + 1));
620
+ }
621
+ }
622
+ return lines.join('\n');
623
+ };
624
+
625
+ export const compactAriaSnapshot = (snapshot: string | null, keepNamed = false): string => {
626
+ if (!snapshot) return '';
627
+ const nodes = parseAriaSnapshot(snapshot, keepNamed);
628
+ return serializeAriaNodes(nodes);
629
+ };
@@ -0,0 +1,13 @@
1
+ import path from 'node:path';
2
+
3
+ let cached: string | undefined;
4
+
5
+ export function getCliName(): string {
6
+ if (cached) return cached;
7
+ const ua = process.env.npm_config_user_agent || '';
8
+ if (ua.includes('bun')) cached = 'bunx explorbot';
9
+ else if (ua.includes('npm')) cached = 'npx explorbot';
10
+ else if (process.argv[1]?.endsWith('.ts')) cached = `bun ${path.relative(process.cwd(), process.argv[1])}`;
11
+ else cached = 'explorbot';
12
+ return cached;
13
+ }
@@ -0,0 +1,22 @@
1
+ import { createDebug } from './logger.js';
2
+
3
+ const debugLog = createDebug('explorbot:code-extractor');
4
+
5
+ export function extractCodeBlocks(aiResponse: string): string[] {
6
+ const codeBlockRegex = /```(?:js|javascript)?\s*\n([\s\S]*?)\n```/g;
7
+ const codeBlocks: string[] = [];
8
+ let match: RegExpExecArray | null = null;
9
+
10
+ while ((match = codeBlockRegex.exec(aiResponse))) {
11
+ const code = match[1].trim();
12
+ if (!code) continue;
13
+ try {
14
+ new Function('I', code);
15
+ codeBlocks.push(code);
16
+ } catch {
17
+ debugLog('Invalid JavaScript code block skipped:', code);
18
+ }
19
+ }
20
+
21
+ return codeBlocks;
22
+ }