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,100 @@
1
+ import { RequestResult, generateRequestId } from './request-result.ts';
2
+ import type { RequestStore } from './request-store.ts';
3
+
4
+ const WRITE_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);
5
+ const JSON_CONTENT_TYPES = /application\/json|application\/.*\+json/i;
6
+
7
+ export class XhrCapture {
8
+ private store: RequestStore;
9
+ private baseOrigin: string;
10
+ private handler: ((response: any) => Promise<void>) | null = null;
11
+
12
+ constructor(store: RequestStore, baseUrl: string) {
13
+ this.store = store;
14
+ this.baseOrigin = new URL(baseUrl).origin;
15
+ }
16
+
17
+ attach(page: any): void {
18
+ this.handler = async (response: any) => {
19
+ try {
20
+ await this.captureResponse(response);
21
+ } catch {
22
+ // ignore capture errors
23
+ }
24
+ };
25
+ page.on('response', this.handler);
26
+ }
27
+
28
+ detach(page: any): void {
29
+ if (!this.handler) return;
30
+ page.off('response', this.handler);
31
+ this.handler = null;
32
+ }
33
+
34
+ private async captureResponse(response: any): Promise<void> {
35
+ const request = response.request();
36
+ const resourceType = request.resourceType();
37
+
38
+ if (resourceType !== 'xhr' && resourceType !== 'fetch') return;
39
+
40
+ const method = request.method();
41
+ if (!WRITE_METHODS.has(method)) return;
42
+
43
+ const url = request.url();
44
+ if (!url.startsWith(this.baseOrigin)) return;
45
+
46
+ const contentType = response.headers()['content-type'] || '';
47
+ if (!JSON_CONTENT_TYPES.test(contentType)) return;
48
+
49
+ const status = response.status();
50
+ if (status === 304) return;
51
+
52
+ const parsedUrl = new URL(url);
53
+ const origin = parsedUrl.pathname + parsedUrl.search;
54
+ const id = generateRequestId(method, parsedUrl.pathname, 'xhr_');
55
+
56
+ const requestHeaders: Record<string, string> = {};
57
+ for (const [k, v] of Object.entries(request.headers())) {
58
+ requestHeaders[k] = String(v);
59
+ }
60
+
61
+ const responseHeaders: Record<string, string> = {};
62
+ for (const [k, v] of Object.entries(response.headers())) {
63
+ responseHeaders[k] = String(v);
64
+ }
65
+
66
+ let rawBody = '';
67
+ try {
68
+ rawBody = await response.text();
69
+ } catch {
70
+ return;
71
+ }
72
+
73
+ let requestBody: any;
74
+ try {
75
+ const postData = request.postData();
76
+ if (postData) {
77
+ requestBody = JSON.parse(postData);
78
+ }
79
+ } catch {
80
+ requestBody = request.postData() || undefined;
81
+ }
82
+
83
+ const result = new RequestResult({
84
+ id,
85
+ method,
86
+ path: parsedUrl.pathname,
87
+ fullUrl: origin,
88
+ requestHeaders,
89
+ requestBody,
90
+ status,
91
+ statusText: response.statusText(),
92
+ responseHeaders,
93
+ timing: 0,
94
+ timestamp: new Date(),
95
+ });
96
+ result.rawResponseBodyValue = rawBody;
97
+
98
+ this.store.addCapturedRequest(result);
99
+ }
100
+ }
@@ -0,0 +1,74 @@
1
+ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { chromium, firefox, webkit } from 'playwright-core';
4
+ import { ConfigParser } from './config.js';
5
+ import { log, tag } from './utils/logger.js';
6
+
7
+ const ENDPOINT_FILENAME = '.browser-endpoint';
8
+
9
+ function getEndpointFilePath(): string {
10
+ const configParser = ConfigParser.getInstance();
11
+ const outputDir = configParser.getOutputDir();
12
+ return path.join(outputDir, ENDPOINT_FILENAME);
13
+ }
14
+
15
+ function readEndpoint(): string | null {
16
+ const filePath = getEndpointFilePath();
17
+ if (!existsSync(filePath)) return null;
18
+ return readFileSync(filePath, 'utf8').trim();
19
+ }
20
+
21
+ function writeEndpoint(wsEndpoint: string): void {
22
+ const filePath = getEndpointFilePath();
23
+ const dir = path.dirname(filePath);
24
+ if (!existsSync(dir)) {
25
+ mkdirSync(dir, { recursive: true });
26
+ }
27
+ writeFileSync(filePath, wsEndpoint, 'utf8');
28
+ }
29
+
30
+ function removeEndpointFile(): void {
31
+ const filePath = getEndpointFilePath();
32
+ if (existsSync(filePath)) unlinkSync(filePath);
33
+ }
34
+
35
+ async function isServerRunning(wsEndpoint: string): Promise<boolean> {
36
+ try {
37
+ const browser = await chromium.connect(wsEndpoint, { timeout: 3000 });
38
+ await browser.close();
39
+ return true;
40
+ } catch {
41
+ return false;
42
+ }
43
+ }
44
+
45
+ const BROWSER_LAUNCHERS = { chromium, firefox, webkit } as const;
46
+
47
+ async function launchServer(opts: { browser?: string; show?: boolean }): Promise<any> {
48
+ const browserName = (opts.browser || 'chromium') as keyof typeof BROWSER_LAUNCHERS;
49
+ const launcher = BROWSER_LAUNCHERS[browserName];
50
+ if (!launcher) throw new Error(`Unsupported browser: ${browserName}`);
51
+
52
+ const server = await launcher.launchServer({
53
+ headless: !opts.show,
54
+ });
55
+
56
+ const wsEndpoint = server.wsEndpoint();
57
+ writeEndpoint(wsEndpoint);
58
+
59
+ log(`Browser server started: ${browserName} (${opts.show ? 'headed' : 'headless'})`);
60
+ tag('info').log(`WebSocket endpoint: ${wsEndpoint}`);
61
+ tag('info').log(`Endpoint saved to: ${getEndpointFilePath()}`);
62
+
63
+ return server;
64
+ }
65
+
66
+ async function getAliveEndpoint(): Promise<string | null> {
67
+ const endpoint = readEndpoint();
68
+ if (!endpoint) return null;
69
+ if (await isServerRunning(endpoint)) return endpoint;
70
+ removeEndpointFile();
71
+ return null;
72
+ }
73
+
74
+ export { readEndpoint, removeEndpointFile, isServerRunning, launchServer, getEndpointFilePath, getAliveEndpoint };
@@ -0,0 +1,454 @@
1
+ import { recommendedCodeceptCommands } from './ai/rules.js';
2
+ import { type BaseCommand, createCommands } from './commands/index.js';
3
+ import type { ExplorBot } from './explorbot.js';
4
+ import { tag } from './utils/logger.js';
5
+
6
+ export type InputSubmitCallback = (input: string) => Promise<void>;
7
+
8
+ export interface InputManager {
9
+ registerInputPane(addLog: (entry: string) => void, onSubmit: InputSubmitCallback): () => void;
10
+ getAvailableCommands(): string[];
11
+ getFilteredCommands(input: string): string[];
12
+ getAutocomplete(input: string, cursor: number): CommandAutocomplete;
13
+ setExitOnEmptyInput(enabled: boolean): void;
14
+ }
15
+
16
+ export interface ParsedCommand {
17
+ name: string;
18
+ args: string[];
19
+ }
20
+
21
+ export interface CommandAutocompleteSuggestion {
22
+ value: string;
23
+ display: string;
24
+ description: string;
25
+ argumentHint?: string;
26
+ }
27
+
28
+ export interface CommandAutocomplete {
29
+ suggestions: CommandAutocompleteSuggestion[];
30
+ replaceFrom: number;
31
+ replaceTo: number;
32
+ visible: boolean;
33
+ argumentHint?: string;
34
+ }
35
+
36
+ function parseCommand(input: string): ParsedCommand | null {
37
+ const trimmed = input.trim();
38
+
39
+ if (!trimmed.startsWith('/')) {
40
+ return null;
41
+ }
42
+
43
+ const parts = trimmed.slice(1).split(/\s+/);
44
+ const name = parts[0]?.toLowerCase();
45
+ const args = parts.slice(1);
46
+
47
+ return { name, args };
48
+ }
49
+
50
+ export class CommandHandler implements InputManager {
51
+ private explorBot: ExplorBot;
52
+ private commands: BaseCommand[];
53
+ private registeredInputPanes: Set<{
54
+ addLog: (entry: string) => void;
55
+ onSubmit: InputSubmitCallback;
56
+ }> = new Set();
57
+ private exitOnEmptyInput = false;
58
+ private runningCommands = new Set<string>();
59
+
60
+ constructor(explorBot: ExplorBot) {
61
+ this.explorBot = explorBot;
62
+ this.commands = createCommands(explorBot);
63
+ explorBot.agentCaptain().setCommandExecutor((cmd) => this.executeCommand(cmd), this.getCommandDescriptions());
64
+ }
65
+
66
+ private findCommand(name: string): BaseCommand | undefined {
67
+ return this.commands.find((cmd) => cmd.matches(name));
68
+ }
69
+
70
+ getAvailableCommands(): string[] {
71
+ const slashCommands = this.commands.map((cmd) => `/${cmd.name}`);
72
+ for (const cmd of this.commands) {
73
+ for (const alias of cmd.aliases) {
74
+ if (!slashCommands.includes(`/${alias}`)) {
75
+ slashCommands.push(`/${alias}`);
76
+ }
77
+ }
78
+ }
79
+ return [...slashCommands, ...recommendedCodeceptCommands];
80
+ }
81
+
82
+ getAutocomplete(input: string, cursor: number): CommandAutocomplete {
83
+ const safeCursor = Math.max(0, Math.min(cursor, input.length));
84
+ const slashAutocomplete = this.getSlashAutocomplete(input, safeCursor);
85
+ if (slashAutocomplete) {
86
+ return slashAutocomplete;
87
+ }
88
+
89
+ const codeceptAutocomplete = this.getCodeceptAutocomplete(input, safeCursor);
90
+ if (codeceptAutocomplete) {
91
+ return codeceptAutocomplete;
92
+ }
93
+
94
+ const exitAutocomplete = this.getExitAutocomplete(input, safeCursor);
95
+ if (exitAutocomplete) {
96
+ return exitAutocomplete;
97
+ }
98
+
99
+ return {
100
+ suggestions: [],
101
+ replaceFrom: safeCursor,
102
+ replaceTo: safeCursor,
103
+ visible: false,
104
+ };
105
+ }
106
+
107
+ getCommandDescriptions(): { name: string; description: string; options: string }[] {
108
+ const descriptions = this.commands.map((cmd) => ({
109
+ name: `/${cmd.name}`,
110
+ description: cmd.description,
111
+ options: cmd.options.map((o) => `${o.flags}: ${o.description}`).join(', '),
112
+ }));
113
+ descriptions.push({ name: 'I.click / I.type / I.fillField / I.see / I.seeElement', description: 'Recommended CodeceptJS interaction commands', options: '' });
114
+ return descriptions;
115
+ }
116
+
117
+ async executeCommand(input: string): Promise<void> {
118
+ const trimmedInput = input.trim();
119
+ const lowered = trimmedInput.toLowerCase();
120
+
121
+ if (trimmedInput.startsWith('I.') || trimmedInput.startsWith('page.') || trimmedInput.startsWith('await ')) {
122
+ try {
123
+ await this.executeBrowserCommand(trimmedInput);
124
+ } catch (error) {
125
+ tag('error').log(`Browser command failed: ${error instanceof Error ? error.message : String(error)}`);
126
+ }
127
+ return;
128
+ }
129
+
130
+ if (lowered === 'exit' || lowered === '/exit' || lowered === 'quit' || lowered === '/quit') {
131
+ const exitCommand = this.findCommand('exit');
132
+ if (exitCommand) {
133
+ try {
134
+ await exitCommand.execute('');
135
+ } catch (error) {
136
+ tag('error').log(`Exit command failed: ${error instanceof Error ? error.message : String(error)}`);
137
+ }
138
+ }
139
+ return;
140
+ }
141
+
142
+ if (trimmedInput === '/') {
143
+ return;
144
+ }
145
+
146
+ const parsed = parseCommand(trimmedInput);
147
+ if (parsed) {
148
+ const command = this.findCommand(parsed.name);
149
+ if (command) {
150
+ if (this.runningCommands.has(command.name)) {
151
+ tag('warning').log(`/${command.name} is already running, skipping`);
152
+ return;
153
+ }
154
+ const argsString = parsed.args.join(' ');
155
+ this.runningCommands.add(command.name);
156
+ try {
157
+ await command.execute(argsString);
158
+ command.suggestions.forEach((s) => tag('step').log(s));
159
+ } catch (error: any) {
160
+ if (error?.name === 'AbortError') throw error;
161
+ tag('error').log(`/${command.name} failed: ${error instanceof Error ? error.message : String(error)}`);
162
+ } finally {
163
+ this.runningCommands.delete(command.name);
164
+ }
165
+ return;
166
+ }
167
+ }
168
+
169
+ try {
170
+ const response = await this.explorBot.agentCaptain().handle(trimmedInput);
171
+ if (response) {
172
+ tag('multiline').log(response);
173
+ }
174
+ } catch (error: any) {
175
+ if (error?.name === 'AbortError') throw error;
176
+ tag('error').log(`Captain failed: ${error instanceof Error ? error.message : String(error)}`);
177
+ }
178
+ }
179
+
180
+ private async executeBrowserCommand(input: string): Promise<void> {
181
+ const action = this.explorBot.getExplorer().createAction();
182
+ await action.execute(input);
183
+ }
184
+
185
+ isCommand(input: string): boolean {
186
+ const trimmedInput = input.trim();
187
+ const lowered = trimmedInput.toLowerCase();
188
+
189
+ if (trimmedInput.startsWith('I.') || trimmedInput.startsWith('page.') || trimmedInput.startsWith('await ')) {
190
+ return true;
191
+ }
192
+
193
+ if (lowered === 'exit' || lowered === 'quit' || lowered === '/exit' || lowered === '/quit') {
194
+ return true;
195
+ }
196
+
197
+ const parsed = parseCommand(trimmedInput);
198
+ if (parsed) {
199
+ return !!this.findCommand(parsed.name);
200
+ }
201
+
202
+ return false;
203
+ }
204
+
205
+ registerInputPane(addLog: (entry: string) => void, onSubmit: InputSubmitCallback): () => void {
206
+ const pane = { addLog, onSubmit };
207
+ this.registeredInputPanes.add(pane);
208
+
209
+ return () => {
210
+ this.registeredInputPanes.delete(pane);
211
+ };
212
+ }
213
+
214
+ getFilteredCommands(input: string): string[] {
215
+ return this.getAutocomplete(input, input.length).suggestions.map((suggestion) => suggestion.value);
216
+ }
217
+
218
+ setExitOnEmptyInput(enabled: boolean): void {
219
+ this.exitOnEmptyInput = enabled;
220
+ }
221
+
222
+ async submitInput(input: string): Promise<void> {
223
+ const trimmedInput = input.trim();
224
+
225
+ if (!trimmedInput) {
226
+ if (this.exitOnEmptyInput) {
227
+ process.exit(0);
228
+ }
229
+ return;
230
+ }
231
+
232
+ const isCommand = trimmedInput.startsWith('/') || trimmedInput.startsWith('I.') || trimmedInput.startsWith('page.') || trimmedInput.startsWith('await ');
233
+
234
+ if (isCommand) {
235
+ try {
236
+ await this.executeCommand(trimmedInput);
237
+ } catch (error) {
238
+ const firstPane = this.registeredInputPanes.values().next().value;
239
+ firstPane?.addLog(`Command failed: ${error}`);
240
+ }
241
+ } else {
242
+ const firstPane = this.registeredInputPanes.values().next().value;
243
+ if (firstPane) {
244
+ await firstPane.onSubmit(trimmedInput);
245
+ }
246
+ const response = await this.explorBot.agentCaptain().handle(trimmedInput);
247
+ if (response) {
248
+ if (firstPane) {
249
+ firstPane.addLog(response);
250
+ } else {
251
+ console.log(response);
252
+ }
253
+ }
254
+ }
255
+ }
256
+
257
+ private getSlashAutocomplete(input: string, cursor: number): CommandAutocomplete | null {
258
+ if (!input.startsWith('/')) {
259
+ return null;
260
+ }
261
+
262
+ const commandEnd = input.search(/\s/);
263
+ const replaceTo = commandEnd === -1 ? input.length : commandEnd;
264
+ const query = input.slice(0, replaceTo);
265
+ const insideCommand = cursor <= replaceTo;
266
+ const parsed = parseCommand(query);
267
+ const exactCommand = parsed ? this.findCommand(parsed.name) : undefined;
268
+ const hasArguments = commandEnd !== -1 && input.slice(commandEnd + 1).trim().length > 0;
269
+ const commandEntries = this.getSlashCommandEntries();
270
+ let suggestions: CommandAutocompleteSuggestion[] = [];
271
+ if (insideCommand) {
272
+ if (query === '/') {
273
+ suggestions = commandEntries.slice(0, 20).map((entry) => ({
274
+ value: entry.value,
275
+ display: entry.value,
276
+ description: entry.description,
277
+ argumentHint: entry.argumentHint,
278
+ }));
279
+ } else {
280
+ suggestions = this.rankSuggestions(query, commandEntries);
281
+ }
282
+ }
283
+ const argumentHint = !insideCommand && exactCommand && !hasArguments && exactCommand.options.length > 0 ? exactCommand.options.map((option) => option.flags).join(' ') : undefined;
284
+
285
+ return {
286
+ suggestions,
287
+ replaceFrom: 0,
288
+ replaceTo,
289
+ visible: insideCommand && suggestions.length > 0,
290
+ argumentHint,
291
+ };
292
+ }
293
+
294
+ private getCodeceptAutocomplete(input: string, cursor: number): CommandAutocomplete | null {
295
+ if (!input.startsWith('I.')) {
296
+ return null;
297
+ }
298
+
299
+ const query = input.slice(0, cursor).split(/\s+/).pop() || 'I.';
300
+ const replaceFrom = cursor - query.length;
301
+ const firstWhitespace = input.search(/\s/);
302
+ const tokenEnd = firstWhitespace === -1 ? input.length : firstWhitespace;
303
+ const insideCommand = cursor <= tokenEnd;
304
+ const suggestions = insideCommand ? this.rankSuggestions(query, this.getCodeceptEntries()) : [];
305
+
306
+ return {
307
+ suggestions,
308
+ replaceFrom,
309
+ replaceTo: tokenEnd,
310
+ visible: insideCommand && suggestions.length > 0,
311
+ };
312
+ }
313
+
314
+ private getExitAutocomplete(input: string, cursor: number): CommandAutocomplete | null {
315
+ if (input.includes(' ')) {
316
+ return null;
317
+ }
318
+
319
+ const query = input.slice(0, cursor).trim();
320
+ if (!query) {
321
+ return null;
322
+ }
323
+
324
+ const exitCommand = this.findCommand('exit');
325
+ if (!exitCommand) {
326
+ return null;
327
+ }
328
+
329
+ const entries = [
330
+ {
331
+ aliases: exitCommand.aliases,
332
+ argumentHint: undefined,
333
+ canonical: exitCommand.name,
334
+ description: exitCommand.description,
335
+ value: exitCommand.name,
336
+ },
337
+ ];
338
+ const suggestions = this.rankSuggestions(query, entries);
339
+
340
+ return {
341
+ suggestions,
342
+ replaceFrom: 0,
343
+ replaceTo: input.length,
344
+ visible: suggestions.length > 0,
345
+ };
346
+ }
347
+
348
+ private getSlashCommandEntries(): AutocompleteEntry[] {
349
+ const entries: AutocompleteEntry[] = [];
350
+
351
+ for (const command of this.commands) {
352
+ if (!command.tuiEnabled) {
353
+ continue;
354
+ }
355
+
356
+ entries.push({
357
+ aliases: command.aliases.map((alias) => `/${alias}`),
358
+ argumentHint: command.options.length > 0 ? command.options.map((option) => option.flags).join(' ') : undefined,
359
+ canonical: `/${command.name}`,
360
+ description: command.description,
361
+ value: `/${command.name}`,
362
+ });
363
+ }
364
+
365
+ return entries;
366
+ }
367
+
368
+ private getCodeceptEntries(): AutocompleteEntry[] {
369
+ return recommendedCodeceptCommands.map((value) => ({
370
+ aliases: [],
371
+ argumentHint: undefined,
372
+ canonical: value,
373
+ description: 'Recommended CodeceptJS interaction command',
374
+ value,
375
+ }));
376
+ }
377
+
378
+ private rankSuggestions(query: string, entries: AutocompleteEntry[]): CommandAutocompleteSuggestion[] {
379
+ if (!query) {
380
+ return entries.slice(0, 20).map((entry) => ({
381
+ value: entry.value,
382
+ display: entry.value,
383
+ description: entry.description,
384
+ argumentHint: entry.argumentHint,
385
+ }));
386
+ }
387
+
388
+ const ranked = entries
389
+ .map((entry) => {
390
+ const matches = [entry.canonical, ...entry.aliases]
391
+ .map((candidate) => ({ candidate, score: this.getMatchScore(query, candidate) }))
392
+ .filter((candidate) => candidate.score > 0)
393
+ .sort((left, right) => right.score - left.score);
394
+
395
+ const match = matches[0];
396
+ if (!match) {
397
+ return null;
398
+ }
399
+
400
+ const display = match.candidate === entry.canonical ? entry.value : `${entry.value} (${match.candidate})`;
401
+
402
+ return {
403
+ value: match.candidate,
404
+ display,
405
+ description: entry.description,
406
+ argumentHint: entry.argumentHint,
407
+ score: match.score,
408
+ };
409
+ })
410
+ .filter((entry): entry is CommandAutocompleteSuggestion & { score: number } => !!entry)
411
+ .sort((left, right) => {
412
+ if (left.score !== right.score) {
413
+ return right.score - left.score;
414
+ }
415
+ return left.display.localeCompare(right.display);
416
+ });
417
+
418
+ return ranked.slice(0, 20).map(({ score: _score, ...suggestion }) => suggestion);
419
+ }
420
+
421
+ private getMatchScore(query: string, candidate: string): number {
422
+ const normalizedQuery = query.toLowerCase();
423
+ const normalizedCandidate = candidate.toLowerCase();
424
+
425
+ if (normalizedCandidate === normalizedQuery) {
426
+ return 500;
427
+ }
428
+
429
+ if (normalizedCandidate.startsWith(normalizedQuery)) {
430
+ return 400 - normalizedCandidate.length;
431
+ }
432
+
433
+ const candidateParts = normalizedCandidate.split(/[:.\-]/).filter(Boolean);
434
+ const queryCore = normalizedQuery.replace(/^\//, '');
435
+ if (candidateParts.some((part) => part.startsWith(queryCore))) {
436
+ return 300 - normalizedCandidate.length;
437
+ }
438
+
439
+ const index = normalizedCandidate.indexOf(normalizedQuery);
440
+ if (index !== -1) {
441
+ return 200 - index;
442
+ }
443
+
444
+ return 0;
445
+ }
446
+ }
447
+
448
+ type AutocompleteEntry = {
449
+ aliases: string[];
450
+ argumentHint?: string;
451
+ canonical: string;
452
+ description: string;
453
+ value: string;
454
+ };
@@ -0,0 +1,63 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { render } from 'ink';
4
+ import React from 'react';
5
+ import { tag } from '../utils/logger.js';
6
+ import { BaseCommand } from './base-command.js';
7
+
8
+ export class AddRuleCommand extends BaseCommand {
9
+ name = 'rules:add';
10
+ aliases = ['add-rule'];
11
+ description = 'Create a rule file for an agent';
12
+ suggestions = ['/add-rule researcher check-tooltips'];
13
+
14
+ async execute(args: string): Promise<void> {
15
+ const parts = args.trim().split(/\s+/);
16
+ const agentName = parts[0] || '';
17
+ const ruleName = parts[1] || '';
18
+ const ruleContent = parts.slice(2).join(' ');
19
+
20
+ if (!agentName || !ruleName) {
21
+ const AddRule = (await import('../components/AddRule.js')).default;
22
+
23
+ const { unmount } = render(
24
+ React.createElement(AddRule, {
25
+ initialAgent: agentName,
26
+ initialName: ruleName,
27
+ onComplete: () => unmount(),
28
+ onCancel: () => unmount(),
29
+ }),
30
+ {
31
+ exitOnCtrlC: false,
32
+ patchConsole: false,
33
+ }
34
+ );
35
+ return;
36
+ }
37
+
38
+ AddRuleCommand.createRuleFile(agentName, ruleName, { content: ruleContent });
39
+ }
40
+
41
+ static createRuleFile(agentName: string, ruleName: string, opts?: { content?: string; urlPattern?: string }): string | null {
42
+ const rulesDir = join(process.cwd(), 'rules', agentName);
43
+ mkdirSync(rulesDir, { recursive: true });
44
+
45
+ const filePath = join(rulesDir, `${ruleName}.md`);
46
+ if (existsSync(filePath)) {
47
+ tag('warning').log(`Rule file already exists: ${filePath}`);
48
+ return null;
49
+ }
50
+
51
+ const content = opts?.content || `Instructions for ${agentName} agent.`;
52
+ writeFileSync(filePath, `${content.trim()}\n`);
53
+ tag('success').log(`Rule created: ${filePath}`);
54
+
55
+ if (opts?.urlPattern) {
56
+ tag('info').log(`Add to config: ai.agents.${agentName}.rules: [{ '${opts.urlPattern}': '${ruleName}' }]`);
57
+ } else {
58
+ tag('info').log(`Add to config: ai.agents.${agentName}.rules: ['${ruleName}']`);
59
+ }
60
+
61
+ return filePath;
62
+ }
63
+ }