@testdriverai/agent 7.8.0-test.38

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 (528) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/.env.example +4 -0
  3. package/.prettierignore +4 -0
  4. package/.prettierrc +1 -0
  5. package/CHANGELOG.md +953 -0
  6. package/README.md +81 -0
  7. package/agent/events.js +135 -0
  8. package/agent/index.js +2450 -0
  9. package/agent/interface.js +35 -0
  10. package/agent/lib/analytics.js +22 -0
  11. package/agent/lib/censorship.js +75 -0
  12. package/agent/lib/commander.js +246 -0
  13. package/agent/lib/commands.js +1684 -0
  14. package/agent/lib/config.js +60 -0
  15. package/agent/lib/generator.js +91 -0
  16. package/agent/lib/http.js +144 -0
  17. package/agent/lib/logger.js +56 -0
  18. package/agent/lib/outputs.js +29 -0
  19. package/agent/lib/parser.js +209 -0
  20. package/agent/lib/redraw.js +386 -0
  21. package/agent/lib/resources/cursor-2.png +0 -0
  22. package/agent/lib/sandbox.js +1104 -0
  23. package/agent/lib/sdk.js +633 -0
  24. package/agent/lib/session.js +25 -0
  25. package/agent/lib/source-mapper.js +342 -0
  26. package/agent/lib/subimage/index.js +77 -0
  27. package/agent/lib/subimage/opencv.js +69 -0
  28. package/agent/lib/system.js +204 -0
  29. package/agent/lib/theme.js +14 -0
  30. package/agent/lib/valid-version.js +21 -0
  31. package/agent/lib/validation.js +169 -0
  32. package/ai/.claude-plugin/plugin.json +9 -0
  33. package/ai/agents/testdriver.md +638 -0
  34. package/ai/skills/testdriver-ai/SKILL.md +204 -0
  35. package/ai/skills/testdriver-assert/SKILL.md +315 -0
  36. package/ai/skills/testdriver-aws-setup/SKILL.md +448 -0
  37. package/ai/skills/testdriver-cache/SKILL.md +221 -0
  38. package/ai/skills/testdriver-caching/SKILL.md +124 -0
  39. package/ai/skills/testdriver-captcha/SKILL.md +158 -0
  40. package/ai/skills/testdriver-ci-cd/SKILL.md +602 -0
  41. package/ai/skills/testdriver-click/SKILL.md +286 -0
  42. package/ai/skills/testdriver-client/SKILL.md +477 -0
  43. package/ai/skills/testdriver-cloud/SKILL.md +119 -0
  44. package/ai/skills/testdriver-customizing-devices/SKILL.md +319 -0
  45. package/ai/skills/testdriver-dashcam/SKILL.md +418 -0
  46. package/ai/skills/testdriver-debugging-with-screenshots/SKILL.md +401 -0
  47. package/ai/skills/testdriver-device-config/SKILL.md +317 -0
  48. package/ai/skills/testdriver-double-click/SKILL.md +102 -0
  49. package/ai/skills/testdriver-elements/SKILL.md +605 -0
  50. package/ai/skills/testdriver-enterprise/SKILL.md +114 -0
  51. package/ai/skills/testdriver-errors/SKILL.md +246 -0
  52. package/ai/skills/testdriver-events/SKILL.md +356 -0
  53. package/ai/skills/testdriver-examples/SKILL.md +7 -0
  54. package/ai/skills/testdriver-exec/SKILL.md +317 -0
  55. package/ai/skills/testdriver-find/SKILL.md +829 -0
  56. package/ai/skills/testdriver-focus-application/SKILL.md +293 -0
  57. package/ai/skills/testdriver-generating-tests/SKILL.md +36 -0
  58. package/ai/skills/testdriver-hover/SKILL.md +278 -0
  59. package/ai/skills/testdriver-locating-elements/SKILL.md +71 -0
  60. package/ai/skills/testdriver-making-assertions/SKILL.md +32 -0
  61. package/ai/skills/testdriver-mcp/SKILL.md +7 -0
  62. package/ai/skills/testdriver-mcp-workflow/SKILL.md +410 -0
  63. package/ai/skills/testdriver-mouse-down/SKILL.md +161 -0
  64. package/ai/skills/testdriver-mouse-up/SKILL.md +164 -0
  65. package/ai/skills/testdriver-parse/SKILL.md +236 -0
  66. package/ai/skills/testdriver-performing-actions/SKILL.md +54 -0
  67. package/ai/skills/testdriver-press-keys/SKILL.md +348 -0
  68. package/ai/skills/testdriver-provision/SKILL.md +331 -0
  69. package/ai/skills/testdriver-quickstart/SKILL.md +144 -0
  70. package/ai/skills/testdriver-redraw/SKILL.md +214 -0
  71. package/ai/skills/testdriver-reusable-code/SKILL.md +249 -0
  72. package/ai/skills/testdriver-right-click/SKILL.md +123 -0
  73. package/ai/skills/testdriver-running-tests/SKILL.md +185 -0
  74. package/ai/skills/testdriver-screenshot/SKILL.md +248 -0
  75. package/ai/skills/testdriver-screenshots/SKILL.md +184 -0
  76. package/ai/skills/testdriver-scroll/SKILL.md +335 -0
  77. package/ai/skills/testdriver-secrets/SKILL.md +115 -0
  78. package/ai/skills/testdriver-self-hosted/SKILL.md +65 -0
  79. package/ai/skills/testdriver-test-writer/SKILL.md +448 -0
  80. package/ai/skills/testdriver-testdriver/SKILL.md +628 -0
  81. package/ai/skills/testdriver-testdriver-mechanic/SKILL.md +165 -0
  82. package/ai/skills/testdriver-type/SKILL.md +357 -0
  83. package/ai/skills/testdriver-variables/SKILL.md +111 -0
  84. package/ai/skills/testdriver-wait/SKILL.md +50 -0
  85. package/ai/skills/testdriver-waiting-for-elements/SKILL.md +90 -0
  86. package/ai/skills/testdriver-what-is-testdriver/SKILL.md +54 -0
  87. package/bin/testdriverai.js +22 -0
  88. package/debugger/bg.png +0 -0
  89. package/debugger/icon.png +0 -0
  90. package/debugger/index.html +469 -0
  91. package/debugger/td.png +0 -0
  92. package/debugger/tray-buffered.png +0 -0
  93. package/debugger/tray.png +0 -0
  94. package/docs/GITHUB_COMMENTS.md +330 -0
  95. package/docs/GITHUB_COMMENTS_ANNOUNCEMENT.md +167 -0
  96. package/docs/QUICK-START-GITHUB-COMMENTS.md +84 -0
  97. package/docs/TEST-GITHUB-COMMENTS.md +129 -0
  98. package/docs/_data/examples-manifest.json +177 -0
  99. package/docs/_data/examples-manifest.schema.json +41 -0
  100. package/docs/_scripts/extract-example-urls.js +165 -0
  101. package/docs/_scripts/generate-examples.js +560 -0
  102. package/docs/_scripts/generate-skills.js +154 -0
  103. package/docs/_scripts/link-replacer.js +164 -0
  104. package/docs/_scripts/upload-docs-to-openai.js +284 -0
  105. package/docs/changelog.mdx +161 -0
  106. package/docs/claude-mcp-plugin.mdx +160 -0
  107. package/docs/docs.json +442 -0
  108. package/docs/github-integration-setup.md +266 -0
  109. package/docs/guide/best-practices-polling.mdx +174 -0
  110. package/docs/images/content/account/newprojectsettings.png +0 -0
  111. package/docs/images/content/account/projectpage.png +0 -0
  112. package/docs/images/content/account/projectreplays.png +0 -0
  113. package/docs/images/content/account/team-manage.png +0 -0
  114. package/docs/images/content/account/teampage.png +0 -0
  115. package/docs/images/content/extension/cursor.svg +1 -0
  116. package/docs/images/content/extension/vscode.svg +57 -0
  117. package/docs/images/content/extension/windsurf.svg +3 -0
  118. package/docs/images/content/parse/output.png +0 -0
  119. package/docs/images/content/self-hosted/launchtemplateid.png +0 -0
  120. package/docs/images/content/side-by-side.png +0 -0
  121. package/docs/images/content/vscode/ide-full.png +0 -0
  122. package/docs/images/content/vscode/running.png +0 -0
  123. package/docs/images/content/vscode/v7-chat.png +0 -0
  124. package/docs/images/content/vscode/v7-choose-agent.png +0 -0
  125. package/docs/images/content/vscode/v7-full.png +0 -0
  126. package/docs/images/content/vscode/v7-onboarding.png +0 -0
  127. package/docs/images/content/vscode/vscode-2-assert.png +0 -0
  128. package/docs/images/content/vscode/vscode-agent-preview.png +0 -0
  129. package/docs/images/content/vscode/vscode-copilot-ask.png +0 -0
  130. package/docs/images/content/vscode/vscode-file-creation.png +0 -0
  131. package/docs/images/content/vscode/vscode-install.png +0 -0
  132. package/docs/images/content/vscode/vscode-overview.png +0 -0
  133. package/docs/images/content/vscode/vscode-setup-walkthrough.png +0 -0
  134. package/docs/images/content/vscode/vscode-stopchat.png +0 -0
  135. package/docs/images/content/vscode/vscode-stoptest.png +0 -0
  136. package/docs/images/content/vscode/vscode-tdservice.png +0 -0
  137. package/docs/images/content/vscode/vscode-test-output.png +0 -0
  138. package/docs/images/content/vscode/vscode-testhistory.png +0 -0
  139. package/docs/images/content/vscode/vscode-testpane-runtests.png +0 -0
  140. package/docs/images/content/vscode/vscode-testpane.png +0 -0
  141. package/docs/images/template/dark.png +0 -0
  142. package/docs/images/template/icon.png +0 -0
  143. package/docs/images/template/light.png +0 -0
  144. package/docs/snippets/calendar-link.mdx +4 -0
  145. package/docs/snippets/gitignore-warning.mdx +7 -0
  146. package/docs/snippets/lifecycle-warning.mdx +6 -0
  147. package/docs/snippets/test-prereqs.mdx +12 -0
  148. package/docs/snippets/tests/assert-replay.mdx +7 -0
  149. package/docs/snippets/tests/assert-yaml.mdx +8 -0
  150. package/docs/snippets/tests/exec-js-replay.mdx +7 -0
  151. package/docs/snippets/tests/exec-js-yaml.mdx +32 -0
  152. package/docs/snippets/tests/exec-shell-replay.mdx +7 -0
  153. package/docs/snippets/tests/exec-shell-yaml.mdx +15 -0
  154. package/docs/snippets/tests/hover-image-replay.mdx +7 -0
  155. package/docs/snippets/tests/hover-image-yaml.mdx +17 -0
  156. package/docs/snippets/tests/hover-text-replay.mdx +7 -0
  157. package/docs/snippets/tests/hover-text-with-description-replay.mdx +7 -0
  158. package/docs/snippets/tests/hover-text-with-description-yaml.mdx +24 -0
  159. package/docs/snippets/tests/hover-text-yaml.mdx +14 -0
  160. package/docs/snippets/tests/match-image-replay.mdx +7 -0
  161. package/docs/snippets/tests/match-image-yaml.mdx +17 -0
  162. package/docs/snippets/tests/press-keys-replay.mdx +7 -0
  163. package/docs/snippets/tests/press-keys-yaml.mdx +36 -0
  164. package/docs/snippets/tests/remember-replay.mdx +7 -0
  165. package/docs/snippets/tests/remember-yaml.mdx +28 -0
  166. package/docs/snippets/tests/scroll-replay.mdx +7 -0
  167. package/docs/snippets/tests/scroll-until-image-replay.mdx +7 -0
  168. package/docs/snippets/tests/scroll-until-image-yaml.mdx +14 -0
  169. package/docs/snippets/tests/scroll-until-text-replay.mdx +7 -0
  170. package/docs/snippets/tests/scroll-until-text-yaml.mdx +17 -0
  171. package/docs/snippets/tests/scroll-yaml.mdx +30 -0
  172. package/docs/snippets/tests/type-repeated-replay.mdx +7 -0
  173. package/docs/snippets/tests/type-repeated-yaml.mdx +22 -0
  174. package/docs/snippets/tests/type-replay.mdx +7 -0
  175. package/docs/snippets/tests/type-yaml.mdx +28 -0
  176. package/docs/snippets/tests/wait-for-image-replay.mdx +7 -0
  177. package/docs/snippets/tests/wait-for-image-yaml.mdx +18 -0
  178. package/docs/snippets/tests/wait-for-text-replay.mdx +7 -0
  179. package/docs/snippets/tests/wait-for-text-yaml.mdx +18 -0
  180. package/docs/snippets/tests/wait-replay.mdx +7 -0
  181. package/docs/snippets/tests/wait-yaml.mdx +13 -0
  182. package/docs/styles.css +65 -0
  183. package/docs/v6/account/dashboard.mdx +16 -0
  184. package/docs/v6/account/enterprise.mdx +110 -0
  185. package/docs/v6/account/pricing.mdx +33 -0
  186. package/docs/v6/account/projects.mdx +33 -0
  187. package/docs/v6/account/team.mdx +35 -0
  188. package/docs/v6/action/ami.mdx +109 -0
  189. package/docs/v6/action/performance.mdx +105 -0
  190. package/docs/v6/action/secrets.mdx +93 -0
  191. package/docs/v6/apps/chrome-extensions.mdx +48 -0
  192. package/docs/v6/apps/desktop-apps.mdx +93 -0
  193. package/docs/v6/apps/mobile-apps.mdx +26 -0
  194. package/docs/v6/apps/static-websites.mdx +54 -0
  195. package/docs/v6/apps/tauri-apps.mdx +361 -0
  196. package/docs/v6/bugs/jira.mdx +232 -0
  197. package/docs/v6/cli/overview.mdx +66 -0
  198. package/docs/v6/commands/assert.mdx +45 -0
  199. package/docs/v6/commands/exec.mdx +276 -0
  200. package/docs/v6/commands/focus-application.mdx +44 -0
  201. package/docs/v6/commands/hover-image.mdx +69 -0
  202. package/docs/v6/commands/hover-text.mdx +47 -0
  203. package/docs/v6/commands/if.mdx +53 -0
  204. package/docs/v6/commands/match-image.mdx +67 -0
  205. package/docs/v6/commands/press-keys.mdx +87 -0
  206. package/docs/v6/commands/remember.mdx +49 -0
  207. package/docs/v6/commands/run.mdx +44 -0
  208. package/docs/v6/commands/scroll-until-image.mdx +66 -0
  209. package/docs/v6/commands/scroll-until-text.mdx +60 -0
  210. package/docs/v6/commands/scroll.mdx +69 -0
  211. package/docs/v6/commands/type.mdx +45 -0
  212. package/docs/v6/commands/wait-for-image.mdx +54 -0
  213. package/docs/v6/commands/wait-for-text.mdx +48 -0
  214. package/docs/v6/commands/wait.mdx +45 -0
  215. package/docs/v6/exporting/junit.mdx +218 -0
  216. package/docs/v6/exporting/playwright.mdx +197 -0
  217. package/docs/v6/features/auto-healing.mdx +144 -0
  218. package/docs/v6/features/generation.mdx +116 -0
  219. package/docs/v6/features/parallel-testing.mdx +151 -0
  220. package/docs/v6/features/reusable-snippets.mdx +131 -0
  221. package/docs/v6/features/selectorless.mdx +80 -0
  222. package/docs/v6/features/visual-assertions.mdx +139 -0
  223. package/docs/v6/getting-started/ci.mdx +146 -0
  224. package/docs/v6/getting-started/cli.mdx +91 -0
  225. package/docs/v6/getting-started/editing.mdx +100 -0
  226. package/docs/v6/getting-started/playwright.mdx +342 -0
  227. package/docs/v6/getting-started/running.mdx +48 -0
  228. package/docs/v6/getting-started/self-hosting.mdx +408 -0
  229. package/docs/v6/getting-started/vscode.mdx +88 -0
  230. package/docs/v6/guide/assertions.mdx +189 -0
  231. package/docs/v6/guide/authentication.mdx +136 -0
  232. package/docs/v6/guide/code.mdx +65 -0
  233. package/docs/v6/guide/dashcam.mdx +118 -0
  234. package/docs/v6/guide/environment-variables.mdx +26 -0
  235. package/docs/v6/guide/lifecycle.mdx +242 -0
  236. package/docs/v6/guide/locating.mdx +141 -0
  237. package/docs/v6/guide/protips.mdx +43 -0
  238. package/docs/v6/guide/variables.mdx +143 -0
  239. package/docs/v6/guide/waiting.mdx +130 -0
  240. package/docs/v6/importing/csv.mdx +196 -0
  241. package/docs/v6/importing/gherkin.mdx +143 -0
  242. package/docs/v6/importing/jira.mdx +164 -0
  243. package/docs/v6/importing/testrail.mdx +162 -0
  244. package/docs/v6/integrations/electron.mdx +146 -0
  245. package/docs/v6/integrations/netlify.mdx +100 -0
  246. package/docs/v6/integrations/vercel.mdx +125 -0
  247. package/docs/v6/interactive/explore.mdx +99 -0
  248. package/docs/v6/interactive/run.mdx +52 -0
  249. package/docs/v6/interactive/save.mdx +63 -0
  250. package/docs/v6/overview/comparison.mdx +101 -0
  251. package/docs/v6/overview/faq.mdx +162 -0
  252. package/docs/v6/overview/performance.mdx +52 -0
  253. package/docs/v6/overview/quickstart.mdx +137 -0
  254. package/docs/v6/overview/what-is-testdriver.mdx +85 -0
  255. package/docs/v6/scenarios/ai-chatbot.mdx +28 -0
  256. package/docs/v6/scenarios/cookie-banner.mdx +32 -0
  257. package/docs/v6/scenarios/file-upload.mdx +33 -0
  258. package/docs/v6/scenarios/form-filling.mdx +32 -0
  259. package/docs/v6/scenarios/log-in.mdx +75 -0
  260. package/docs/v6/scenarios/pdf-generation.mdx +25 -0
  261. package/docs/v6/scenarios/spell-check.mdx +22 -0
  262. package/docs/v6/security/action.mdx +84 -0
  263. package/docs/v6/security/agent.mdx +73 -0
  264. package/docs/v6/security/platform.mdx +77 -0
  265. package/docs/v6/tutorials/advanced-test.mdx +81 -0
  266. package/docs/v6/tutorials/basic-test.mdx +45 -0
  267. package/docs/v7/_drafts/agents.mdx +843 -0
  268. package/docs/v7/_drafts/architecture.mdx +399 -0
  269. package/docs/v7/_drafts/auto-cache-key.mdx +167 -0
  270. package/docs/v7/_drafts/awesome-logs-quick-ref.mdx +100 -0
  271. package/docs/v7/_drafts/best-practices.mdx +486 -0
  272. package/docs/v7/_drafts/caching-ai.mdx +215 -0
  273. package/docs/v7/_drafts/caching-selectors.mdx +424 -0
  274. package/docs/v7/_drafts/caching.mdx +366 -0
  275. package/docs/v7/_drafts/cli-to-sdk-migration.mdx +425 -0
  276. package/docs/v7/_drafts/commands/assert.mdx +45 -0
  277. package/docs/v7/_drafts/commands/exec.mdx +276 -0
  278. package/docs/v7/_drafts/commands/focus-application.mdx +44 -0
  279. package/docs/v7/_drafts/commands/hover-image.mdx +69 -0
  280. package/docs/v7/_drafts/commands/hover-text.mdx +47 -0
  281. package/docs/v7/_drafts/commands/if.mdx +53 -0
  282. package/docs/v7/_drafts/commands/match-image.mdx +67 -0
  283. package/docs/v7/_drafts/commands/press-keys.mdx +87 -0
  284. package/docs/v7/_drafts/commands/remember.mdx +49 -0
  285. package/docs/v7/_drafts/commands/run.mdx +44 -0
  286. package/docs/v7/_drafts/commands/scroll-until-image.mdx +66 -0
  287. package/docs/v7/_drafts/commands/scroll-until-text.mdx +60 -0
  288. package/docs/v7/_drafts/commands/scroll.mdx +69 -0
  289. package/docs/v7/_drafts/commands/type.mdx +45 -0
  290. package/docs/v7/_drafts/commands/wait-for-image.mdx +54 -0
  291. package/docs/v7/_drafts/commands/wait-for-text.mdx +48 -0
  292. package/docs/v7/_drafts/commands/wait.mdx +45 -0
  293. package/docs/v7/_drafts/configuration.mdx +378 -0
  294. package/docs/v7/_drafts/contributing.mdx +174 -0
  295. package/docs/v7/_drafts/core.mdx +458 -0
  296. package/docs/v7/_drafts/dashcam-title-feature.mdx +89 -0
  297. package/docs/v7/_drafts/debugging.mdx +349 -0
  298. package/docs/v7/_drafts/error-handling.mdx +501 -0
  299. package/docs/v7/_drafts/faq.mdx +393 -0
  300. package/docs/v7/_drafts/hooks.mdx +360 -0
  301. package/docs/v7/_drafts/init-command.mdx +95 -0
  302. package/docs/v7/_drafts/installation.mdx +420 -0
  303. package/docs/v7/_drafts/migration.mdx +562 -0
  304. package/docs/v7/_drafts/observable.mdx +604 -0
  305. package/docs/v7/_drafts/playwright.mdx +342 -0
  306. package/docs/v7/_drafts/plugin-migration.mdx +220 -0
  307. package/docs/v7/_drafts/powerful.mdx +419 -0
  308. package/docs/v7/_drafts/presets.mdx +210 -0
  309. package/docs/v7/_drafts/progressive-disclosure.mdx +230 -0
  310. package/docs/v7/_drafts/prompt-cache.mdx +200 -0
  311. package/docs/v7/_drafts/provision.mdx +390 -0
  312. package/docs/v7/_drafts/quick-start-test-recording.mdx +214 -0
  313. package/docs/v7/_drafts/readme.mdx +135 -0
  314. package/docs/v7/_drafts/reports.mdx +414 -0
  315. package/docs/v7/_drafts/scalable.mdx +763 -0
  316. package/docs/v7/_drafts/screenshot.mdx +155 -0
  317. package/docs/v7/_drafts/sdk-awesome-logs.mdx +468 -0
  318. package/docs/v7/_drafts/sdk-browser-rendering.mdx +167 -0
  319. package/docs/v7/_drafts/sdk-migration.mdx +474 -0
  320. package/docs/v7/_drafts/sdk-v7-complete.mdx +345 -0
  321. package/docs/v7/_drafts/self-hosting.mdx +369 -0
  322. package/docs/v7/_drafts/test-recording.mdx +382 -0
  323. package/docs/v7/_drafts/troubleshooting.mdx +526 -0
  324. package/docs/v7/_drafts/vitest-plugin.mdx +477 -0
  325. package/docs/v7/_drafts/vitest.mdx +535 -0
  326. package/docs/v7/_drafts/writing-tests.mdx +25 -0
  327. package/docs/v7/ai.mdx +205 -0
  328. package/docs/v7/assert.mdx +316 -0
  329. package/docs/v7/aws-setup.mdx +449 -0
  330. package/docs/v7/cache.mdx +223 -0
  331. package/docs/v7/caching.mdx +128 -0
  332. package/docs/v7/captcha.mdx +159 -0
  333. package/docs/v7/ci-cd.mdx +603 -0
  334. package/docs/v7/click.mdx +287 -0
  335. package/docs/v7/client.mdx +478 -0
  336. package/docs/v7/copilot/auto-healing.mdx +265 -0
  337. package/docs/v7/copilot/creating-tests.mdx +156 -0
  338. package/docs/v7/copilot/github.mdx +143 -0
  339. package/docs/v7/copilot/running-tests.mdx +149 -0
  340. package/docs/v7/copilot/setup.mdx +143 -0
  341. package/docs/v7/customizing-devices.mdx +319 -0
  342. package/docs/v7/dashcam.mdx +419 -0
  343. package/docs/v7/debugging-with-screenshots.mdx +402 -0
  344. package/docs/v7/device-config.mdx +317 -0
  345. package/docs/v7/double-click.mdx +102 -0
  346. package/docs/v7/elements.mdx +606 -0
  347. package/docs/v7/enterprise.mdx +9 -0
  348. package/docs/v7/errors.mdx +248 -0
  349. package/docs/v7/events.mdx +358 -0
  350. package/docs/v7/examples/ai.mdx +72 -0
  351. package/docs/v7/examples/assert.mdx +72 -0
  352. package/docs/v7/examples/captcha-api.mdx +92 -0
  353. package/docs/v7/examples/chrome-extension.mdx +132 -0
  354. package/docs/v7/examples/drag-and-drop.mdx +100 -0
  355. package/docs/v7/examples/element-not-found.mdx +67 -0
  356. package/docs/v7/examples/exec-output.mdx +85 -0
  357. package/docs/v7/examples/exec-pwsh.mdx +83 -0
  358. package/docs/v7/examples/focus-window.mdx +62 -0
  359. package/docs/v7/examples/hover-image.mdx +94 -0
  360. package/docs/v7/examples/hover-text.mdx +69 -0
  361. package/docs/v7/examples/installer.mdx +91 -0
  362. package/docs/v7/examples/launch-vscode-linux.mdx +101 -0
  363. package/docs/v7/examples/match-image.mdx +96 -0
  364. package/docs/v7/examples/press-keys.mdx +92 -0
  365. package/docs/v7/examples/scroll-keyboard.mdx +79 -0
  366. package/docs/v7/examples/scroll-until-image.mdx +81 -0
  367. package/docs/v7/examples/scroll-until-text.mdx +109 -0
  368. package/docs/v7/examples/scroll.mdx +81 -0
  369. package/docs/v7/examples/type.mdx +92 -0
  370. package/docs/v7/examples/windows-installer.mdx +89 -0
  371. package/docs/v7/exec.mdx +318 -0
  372. package/docs/v7/find.mdx +830 -0
  373. package/docs/v7/focus-application.mdx +294 -0
  374. package/docs/v7/generating-tests.mdx +36 -0
  375. package/docs/v7/hosted.mdx +158 -0
  376. package/docs/v7/hover.mdx +279 -0
  377. package/docs/v7/locating-elements.mdx +71 -0
  378. package/docs/v7/making-assertions.mdx +32 -0
  379. package/docs/v7/mcp.mdx +9 -0
  380. package/docs/v7/mouse-down.mdx +161 -0
  381. package/docs/v7/mouse-up.mdx +164 -0
  382. package/docs/v7/parse.mdx +237 -0
  383. package/docs/v7/performing-actions.mdx +54 -0
  384. package/docs/v7/press-keys.mdx +349 -0
  385. package/docs/v7/provision.mdx +333 -0
  386. package/docs/v7/quickstart.mdx +173 -0
  387. package/docs/v7/redraw.mdx +216 -0
  388. package/docs/v7/reusable-code.mdx +249 -0
  389. package/docs/v7/right-click.mdx +123 -0
  390. package/docs/v7/running-tests.mdx +185 -0
  391. package/docs/v7/screenshot.mdx +249 -0
  392. package/docs/v7/screenshots.mdx +186 -0
  393. package/docs/v7/scroll.mdx +336 -0
  394. package/docs/v7/secrets.mdx +115 -0
  395. package/docs/v7/self-hosted.mdx +149 -0
  396. package/docs/v7/type.mdx +358 -0
  397. package/docs/v7/variables.mdx +111 -0
  398. package/docs/v7/wait.mdx +52 -0
  399. package/docs/v7/waiting-for-elements.mdx +90 -0
  400. package/docs/v7/what-is-testdriver.mdx +54 -0
  401. package/eslint.config.js +67 -0
  402. package/examples/ai.test.mjs +31 -0
  403. package/examples/assert.test.mjs +47 -0
  404. package/examples/chrome-extension.test.mjs +97 -0
  405. package/examples/config.mjs +5 -0
  406. package/examples/element-not-found.test.mjs +27 -0
  407. package/examples/exec-output.test.mjs +60 -0
  408. package/examples/exec-pwsh.test.mjs +58 -0
  409. package/examples/findall-coffee-icons.test.mjs +42 -0
  410. package/examples/focus-window.test.mjs +37 -0
  411. package/examples/formatted-logging.test.mjs +27 -0
  412. package/examples/hover-image.test.mjs +53 -0
  413. package/examples/hover-text-with-description.test.mjs +57 -0
  414. package/examples/hover-text.test.mjs +28 -0
  415. package/examples/installer.test.mjs +50 -0
  416. package/examples/launch-vscode-linux.test.mjs +55 -0
  417. package/examples/match-image.test.mjs +55 -0
  418. package/examples/parse.test.mjs +19 -0
  419. package/examples/press-keys.test.mjs +44 -0
  420. package/examples/prompt.test.mjs +34 -0
  421. package/examples/scroll-keyboard.test.mjs +38 -0
  422. package/examples/scroll-until-image.test.mjs +40 -0
  423. package/examples/scroll.test.mjs +42 -0
  424. package/examples/type.test.mjs +46 -0
  425. package/examples/windows-installer.test.mjs +54 -0
  426. package/index.js +2 -0
  427. package/interfaces/cli/commands/init.js +438 -0
  428. package/interfaces/cli/commands/setup.js +382 -0
  429. package/interfaces/cli/lib/base.js +285 -0
  430. package/interfaces/cli.js +20 -0
  431. package/interfaces/junit-reporter.js +290 -0
  432. package/interfaces/logger.js +388 -0
  433. package/interfaces/readline.js +234 -0
  434. package/interfaces/shared-test-state.mjs +64 -0
  435. package/interfaces/vitest-plugin.d.ts +115 -0
  436. package/interfaces/vitest-plugin.mjs +1698 -0
  437. package/lib/captcha/solver.js +358 -0
  438. package/lib/core/Dashcam.js +533 -0
  439. package/lib/core/index.d.ts +172 -0
  440. package/lib/core/index.js +12 -0
  441. package/lib/environments.json +18 -0
  442. package/lib/github-comment-formatter.js +263 -0
  443. package/lib/github-comment.mjs +452 -0
  444. package/lib/init-project.js +575 -0
  445. package/lib/presets/index.mjs +331 -0
  446. package/lib/resolve-channel.js +46 -0
  447. package/lib/sentry.js +417 -0
  448. package/lib/vitest/hooks.d.ts +57 -0
  449. package/lib/vitest/hooks.mjs +674 -0
  450. package/lib/vitest/setup-aws.mjs +247 -0
  451. package/lib/vitest/setup-self-hosted.mjs +151 -0
  452. package/lib/vitest/setup.mjs +46 -0
  453. package/manual/captcha-api.test.mjs +51 -0
  454. package/manual/drag-and-drop.test.mjs +59 -0
  455. package/manual/flake-diffthreshold-001.test.mjs +9 -0
  456. package/manual/flake-diffthreshold-01.test.mjs +9 -0
  457. package/manual/flake-diffthreshold-05.test.mjs +9 -0
  458. package/manual/flake-noredraw-cache.test.mjs +9 -0
  459. package/manual/flake-noredraw-nocache.test.mjs +9 -0
  460. package/manual/flake-redraw-cache.test.mjs +9 -0
  461. package/manual/flake-redraw-nocache.test.mjs +9 -0
  462. package/manual/flake-rocket-match.test.mjs +30 -0
  463. package/manual/flake-shared.mjs +51 -0
  464. package/manual/no-provision.test.mjs +31 -0
  465. package/manual/packer-hover-image.test.mjs +176 -0
  466. package/manual/scroll-until-text.test.mjs +68 -0
  467. package/manual/test-init-command.js +223 -0
  468. package/mcp-server/README.md +322 -0
  469. package/mcp-server/dist/codegen.d.ts +9 -0
  470. package/mcp-server/dist/codegen.js +165 -0
  471. package/mcp-server/dist/mcp-app.html +114 -0
  472. package/mcp-server/dist/package.json +1 -0
  473. package/mcp-server/dist/provision-types.d.ts +290 -0
  474. package/mcp-server/dist/provision-types.js +174 -0
  475. package/mcp-server/dist/server.d.ts +6 -0
  476. package/mcp-server/dist/server.mjs +1925 -0
  477. package/mcp-server/dist/session.d.ts +85 -0
  478. package/mcp-server/dist/session.js +152 -0
  479. package/mcp-server/mcp-app.html +28 -0
  480. package/mcp-server/mcp-config.example.json +19 -0
  481. package/mcp-server/package-lock.json +4027 -0
  482. package/mcp-server/package.json +31 -0
  483. package/mcp-server/src/codegen.ts +189 -0
  484. package/mcp-server/src/mcp-app.css +360 -0
  485. package/mcp-server/src/mcp-app.ts +547 -0
  486. package/mcp-server/src/provision-types.ts +209 -0
  487. package/mcp-server/src/server.ts +2391 -0
  488. package/mcp-server/src/session.ts +194 -0
  489. package/mcp-server/tsconfig.json +16 -0
  490. package/mcp-server/vite.config.ts +23 -0
  491. package/package.json +158 -0
  492. package/schema.json +1046 -0
  493. package/scripts/generate-skills.js +94 -0
  494. package/sdk-log-formatter.js +1157 -0
  495. package/sdk.d.ts +1486 -0
  496. package/sdk.js +4336 -0
  497. package/setup/aws/cloudformation.yaml +463 -0
  498. package/setup/aws/disable-defender.sh +42 -0
  499. package/setup/aws/install-dev-runner.sh +79 -0
  500. package/setup/aws/spawn-runner.sh +289 -0
  501. package/test/captcha-solver.test.mjs +152 -0
  502. package/test/chrome-remote-debugging.test.mjs +66 -0
  503. package/test/duckduckgo/experiment.test.mjs +28 -0
  504. package/test/duckduckgo/setup.test.mjs +29 -0
  505. package/test/manual/debug-locate-response.js +82 -0
  506. package/test/manual/reconnect-provision.test.mjs +49 -0
  507. package/test/manual/test-console-logs.test.mjs +42 -0
  508. package/test/manual/test-find-api.js +73 -0
  509. package/test/manual/test-init.sh +54 -0
  510. package/test/manual/test-prompt-cache.js +97 -0
  511. package/test/manual/test-provision-auth.mjs +22 -0
  512. package/test/manual/test-sandbox-render.js +29 -0
  513. package/test/manual/test-sdk-methods.js +15 -0
  514. package/test/manual/test-sdk-refactor.js +53 -0
  515. package/test/manual/test-stack-trace.mjs +57 -0
  516. package/test/manual/verify-element-api.js +89 -0
  517. package/test/manual/verify-types.js +0 -0
  518. package/test/manual-unawaited-promise.test.mjs +31 -0
  519. package/vitest.config.mjs +58 -0
  520. package/vitest.runner.config.mjs +33 -0
  521. package/vscode-extension/.vscodeignore +12 -0
  522. package/vscode-extension/README.md +94 -0
  523. package/vscode-extension/media/icon.png +0 -0
  524. package/vscode-extension/package-lock.json +4126 -0
  525. package/vscode-extension/package.json +86 -0
  526. package/vscode-extension/src/extension.ts +829 -0
  527. package/vscode-extension/testdriverai-0.1.0.vsix +0 -0
  528. package/vscode-extension/tsconfig.json +16 -0
@@ -0,0 +1,1157 @@
1
+ const chalk = require("chalk");
2
+
3
+ /**
4
+ * AWESOME Log formatter for TestDriver SDK ๐ŸŽจ
5
+ * Provides beautiful, emoji-rich formatting with great DX for logs sent to dashcam
6
+ * ANSI codes are preserved through the log pipeline: SDK โ†’ sandbox โ†’ /tmp/testdriver.log โ†’ dashcam
7
+ *
8
+ * Now with full UTF-8 and emoji support! ๐Ÿš€
9
+ */
10
+
11
+ // Duration threshold configurations for different contexts
12
+ const DURATION_THRESHOLDS = {
13
+ default: { fast: 3000, medium: 10000 },
14
+ action: { fast: 100, medium: 500 },
15
+ redraw: { fast: 5000, medium: 10000 },
16
+ quickAction: { fast: 50, medium: 200 },
17
+ test: { fast: 1000, medium: 5000 },
18
+ };
19
+
20
+ class SDKLogFormatter {
21
+ constructor(options = {}) {
22
+ this.testContext = {
23
+ currentTest: null,
24
+ currentFile: null,
25
+ startTime: null,
26
+ };
27
+ this.eventCount = 0;
28
+ this.useColors = options.colors !== false;
29
+ this.useEmojis = options.emojis !== false;
30
+ }
31
+
32
+ /**
33
+ * Set the current test context from Vitest
34
+ * @param {Object} context - Test context with file, test name, etc.
35
+ */
36
+ setTestContext(context) {
37
+ if (context.file) this.testContext.currentFile = context.file;
38
+ if (context.test) this.testContext.currentTest = context.test;
39
+ if (context.startTime) this.testContext.startTime = context.startTime;
40
+ }
41
+
42
+ /**
43
+ * Get elapsed time since test start
44
+ * @returns {string} Formatted elapsed time
45
+ */
46
+ getElapsedTime() {
47
+ if (!this.testContext.startTime) return "";
48
+ const elapsed = Date.now() - this.testContext.startTime;
49
+ const seconds = (elapsed / 1000).toFixed(2);
50
+ return `[${seconds}s]`;
51
+ }
52
+
53
+ /**
54
+ * Add elapsed time to parts array if available
55
+ * @param {Array} parts - Array to push time string to
56
+ * @param {boolean} dim - Whether to dim the time string
57
+ */
58
+ addTimestamp(parts, dim = true) {
59
+ const timeStr = this.getElapsedTime();
60
+ if (timeStr) {
61
+ parts.push(dim ? chalk.dim(timeStr) : timeStr);
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Get color function based on duration and thresholds
67
+ * @param {number} durationMs - Duration in milliseconds
68
+ * @param {string} thresholdKey - Key from DURATION_THRESHOLDS
69
+ * @returns {Function} Chalk color function
70
+ */
71
+ getDurationColor(durationMs, thresholdKey = "default") {
72
+ const thresholds = DURATION_THRESHOLDS[thresholdKey] || DURATION_THRESHOLDS.default;
73
+ if (durationMs < thresholds.fast) return chalk.green;
74
+ if (durationMs < thresholds.medium) return chalk.yellow;
75
+ return chalk.red;
76
+ }
77
+
78
+ /**
79
+ * Format duration with appropriate color
80
+ * @param {number|string} duration - Duration in ms
81
+ * @param {string} thresholdKey - Key from DURATION_THRESHOLDS
82
+ * @param {boolean} showSeconds - Show as seconds (true) or raw (false)
83
+ * @returns {string} Formatted duration string
84
+ */
85
+ formatDurationColored(duration, thresholdKey = "default", showSeconds = true) {
86
+ const durationMs = parseInt(duration);
87
+ const color = this.getDurationColor(durationMs, thresholdKey);
88
+ const display = showSeconds ? `(${(durationMs / 1000).toFixed(1)}s)` : `(${duration})`;
89
+ return color(display);
90
+ }
91
+
92
+ /**
93
+ * Join metadata parts with separator
94
+ * @param {Array} metaParts - Array of metadata strings
95
+ * @returns {string} Joined metadata string with separators
96
+ */
97
+ joinMetaParts(metaParts) {
98
+ if (metaParts.length === 0) return "";
99
+ return chalk.dim("ยท") + " " + metaParts.join(chalk.dim(" ยท "));
100
+ }
101
+
102
+ /**
103
+ * Create an indented result line prefix (for child results)
104
+ * @returns {string} Indented arrow prefix
105
+ */
106
+ getResultPrefix() {
107
+ return " " + chalk.dim("โ†’");
108
+ }
109
+
110
+ /**
111
+ * Format a nested action result line (scrolled, clicked, typed, pressed keys, etc.)
112
+ * @param {string} message - The action message (e.g., "scrolled down 300px", "pressed keys: tab")
113
+ * @param {number} durationMs - Duration in milliseconds
114
+ * @returns {string} Formatted nested result line
115
+ */
116
+ formatNestedAction(message, durationMs) {
117
+ return this.getResultPrefix() + " " + chalk.dim(message) + " " + this.formatDurationColored(durationMs);
118
+ }
119
+
120
+ /**
121
+ * Format a redraw/idle wait completion line
122
+ * @param {number} durationMs - Duration in milliseconds
123
+ * @returns {string} Formatted redraw complete line
124
+ */
125
+ formatRedrawComplete(durationMs) {
126
+ return this.formatNestedAction("flake protection", durationMs);
127
+ }
128
+
129
+ /**
130
+ * Format a scroll action result
131
+ * @param {string} direction - Scroll direction (up, down, left, right)
132
+ * @param {number} amount - Scroll amount in pixels
133
+ * @param {number} durationMs - Duration in milliseconds
134
+ * @returns {string} Formatted scroll result line
135
+ */
136
+ formatScrollResult(direction, amount, durationMs) {
137
+ return this.formatNestedAction(`scrolled ${direction} ${amount}px`, durationMs);
138
+ }
139
+
140
+ /**
141
+ * Format a click action result
142
+ * @param {string} button - Button type (left, right, middle)
143
+ * @param {number} x - X coordinate
144
+ * @param {number} y - Y coordinate
145
+ * @param {number} durationMs - Duration in milliseconds
146
+ * @returns {string} Formatted click result line
147
+ */
148
+ formatClickResult(button, x, y, durationMs) {
149
+ return this.formatNestedAction(`click ${button} clicking at ${x}, ${y}`, durationMs);
150
+ }
151
+
152
+ /**
153
+ * Format a type action result
154
+ * @param {string} text - Text that was typed (or "****" for secrets)
155
+ * @param {boolean} isSecret - Whether the text is a secret
156
+ * @param {number} durationMs - Duration in milliseconds
157
+ * @returns {string} Formatted type result line
158
+ */
159
+ formatTypeResult(text, isSecret, durationMs) {
160
+ const displayText = isSecret ? "secret ****" : `"${text}"`;
161
+ return this.formatNestedAction(`typed ${displayText}`, durationMs);
162
+ }
163
+
164
+ /**
165
+ * Format a press keys action result
166
+ * @param {string} keysDisplay - Keys that were pressed (comma-separated)
167
+ * @param {number} durationMs - Duration in milliseconds
168
+ * @returns {string} Formatted press keys result line
169
+ */
170
+ formatPressKeysResult(keysDisplay, durationMs) {
171
+ return this.formatNestedAction(`pressed keys: ${keysDisplay}`, durationMs);
172
+ }
173
+
174
+ /**
175
+ * Format a nested code display line (for exec commands)
176
+ * @param {string} codeDisplay - The code to display
177
+ * @returns {string} Formatted code line
178
+ */
179
+ formatCodeLine(codeDisplay) {
180
+ return this.getResultPrefix() + " " + chalk.dim(codeDisplay);
181
+ }
182
+
183
+ /**
184
+ * Format an exec complete result
185
+ * @param {number} exitCode - The exit code
186
+ * @param {number} durationMs - Duration in milliseconds
187
+ * @returns {string} Formatted exec result line
188
+ */
189
+ formatExecComplete(exitCode, durationMs) {
190
+ const statusText = exitCode !== 0
191
+ ? `failed (exit code ${exitCode})`
192
+ : `complete (exit code 0)`;
193
+ const statusColor = exitCode !== 0 ? chalk.red : chalk.green;
194
+
195
+ return this.formatResultLine(
196
+ statusText,
197
+ statusColor,
198
+ { duration: durationMs },
199
+ "action"
200
+ );
201
+ }
202
+
203
+ /**
204
+ * Format a log message in Vitest style
205
+ * @param {string} type - Log type (info, success, error, action, debug)
206
+ * @param {string} message - The message to format
207
+ * @param {Object} meta - Additional metadata
208
+ * @returns {string} Formatted log message
209
+ */
210
+ format(type, message, meta = {}) {
211
+ this.eventCount++;
212
+
213
+ const parts = [];
214
+
215
+ // Add timestamp/elapsed time
216
+ this.addTimestamp(parts, false);
217
+
218
+ // Add type prefix with color
219
+ const prefix = this.getPrefix(type);
220
+ if (prefix) parts.push(prefix);
221
+
222
+ // Add message
223
+ parts.push(this.formatMessage(type, message));
224
+
225
+ // Add metadata if present
226
+ if (meta.duration) {
227
+ parts.push(chalk.dim(`(${meta.duration})`));
228
+ }
229
+
230
+ return parts.join(" ");
231
+ }
232
+
233
+ /**
234
+ * Get prefix for log type with AWESOME colors and emojis ๐ŸŽจ
235
+ * @param {string} type - Log type
236
+ * @returns {string} Colored prefix with emoji
237
+ */
238
+ getPrefix(type) {
239
+ if (!this.useEmojis) {
240
+ // Fallback to simple symbols without emojis
241
+ const simplePrefixes = {
242
+ info: chalk.blue("โ„น"),
243
+ success: chalk.green("โœ“"),
244
+ error: chalk.red("โœ–"),
245
+ action: chalk.cyan("โ†’"),
246
+ debug: chalk.gray("โš™"),
247
+ find: chalk.magenta("โŒ•"),
248
+ click: chalk.cyan("โ–ธ"),
249
+ type: chalk.yellow("โŒจ"),
250
+ assert: chalk.green("โœ“"),
251
+ scroll: chalk.blue("โ†•"),
252
+ hover: chalk.cyan("โ†’"),
253
+ wait: chalk.yellow("โฑ"),
254
+ connect: chalk.green("โšก"),
255
+ disconnect: chalk.red("โน"),
256
+ };
257
+ return simplePrefixes[type] || chalk.gray("โ€ข");
258
+ }
259
+
260
+ const prefixes = {
261
+ // Core actions - hand gestures
262
+ info: chalk.blue("โ„น๏ธ"),
263
+ success: chalk.green("โœ…"),
264
+ error: chalk.red("โŒ"),
265
+ warning: chalk.yellow("โš ๏ธ"),
266
+
267
+ // Finding elements
268
+ find: chalk.magenta("๐Ÿ”"),
269
+ findAll: chalk.magenta("๐Ÿ”Ž"),
270
+
271
+ // Mouse actions
272
+ click: chalk.cyan("๐Ÿ‘†"),
273
+ doubleClick: chalk.cyan("๐Ÿ‘†๐Ÿ‘†"),
274
+ rightClick: chalk.cyan("๐Ÿ–ฑ๏ธ"),
275
+ hover: chalk.cyan("๐Ÿ‘‰"),
276
+ drag: chalk.cyan("โœŠ"),
277
+
278
+ // Keyboard actions
279
+ type: chalk.yellow("โŒจ๏ธ "),
280
+ pressKeys: chalk.yellow("๐ŸŽน"),
281
+
282
+ // Navigation
283
+ scroll: chalk.blue("๐Ÿ“œ"),
284
+ scrollUp: chalk.blue("โฌ†๏ธ"),
285
+ scrollDown: chalk.blue("โฌ‡๏ธ"),
286
+ navigate: chalk.blue("๐Ÿงญ"),
287
+
288
+ // Validation
289
+ assert: chalk.green("โœ…"),
290
+ verify: chalk.green("๐Ÿ”"),
291
+ extract: chalk.blue("๐Ÿง "),
292
+
293
+ // System
294
+ connect: chalk.green("๐Ÿ”Œ"),
295
+ disconnect: chalk.red("๐Ÿ”Œ"),
296
+ screenshot: chalk.blue("๐Ÿ“ธ"),
297
+ wait: chalk.yellow("โณ"),
298
+
299
+ // Focus & Windows
300
+ focusApplication: chalk.cyan("๐ŸŽฏ"),
301
+
302
+ // Cache
303
+ cacheHit: chalk.yellow("โšก"),
304
+ cacheMiss: chalk.gray("๐Ÿ’ค"),
305
+
306
+ // Debug
307
+ debug: chalk.gray("๐Ÿ”ง"),
308
+
309
+ // Default
310
+ action: chalk.cyan("โ–ถ๏ธ "),
311
+ };
312
+ return prefixes[type] || chalk.gray("โ€ข");
313
+ }
314
+
315
+ /**
316
+ * Format the message content with appropriate styling
317
+ * @param {string} type - Log type
318
+ * @param {string} message - Raw message
319
+ * @returns {string} Formatted message
320
+ */
321
+ formatMessage(type, message) {
322
+ if (!this.useColors) return message;
323
+
324
+ const formatters = {
325
+ success: (msg) => chalk.green(msg),
326
+ error: (msg) => chalk.red(msg),
327
+ debug: (msg) => chalk.dim(msg),
328
+ };
329
+
330
+ return formatters[type] ? formatters[type](message) : message;
331
+ }
332
+
333
+ /**
334
+ * Format a "finding" style message (when search starts) ๐Ÿ”
335
+ * @param {string} prefixType - Prefix type for getPrefix
336
+ * @param {string} label - Action label (e.g., "Finding", "Finding All", "Asserting")
337
+ * @param {string} description - Element/assertion description
338
+ * @returns {string} Formatted message
339
+ */
340
+ formatFindingStyle(prefixType, label, description) {
341
+ const parts = [];
342
+ this.addTimestamp(parts);
343
+ parts.push(this.getPrefix(prefixType));
344
+ parts.push(chalk.bold.cyan(label));
345
+ parts.push(chalk.cyan(`"${description}"`));
346
+ return parts.join(" ");
347
+ }
348
+
349
+ /**
350
+ * Format an element finding message (when search starts) ๐Ÿ”
351
+ * @param {string} description - Element description
352
+ * @returns {string} Formatted message
353
+ */
354
+ formatElementFinding(description) {
355
+ return this.formatFindingStyle("find", "Finding", description);
356
+ }
357
+
358
+ /**
359
+ * Build common metadata parts for result messages
360
+ * @param {Object} meta - Metadata object
361
+ * @param {string} thresholdKey - Duration threshold key
362
+ * @returns {Array} Array of formatted metadata strings
363
+ */
364
+ buildResultMetaParts(meta, thresholdKey = "default") {
365
+ const metaParts = [];
366
+
367
+ if (meta.x !== undefined && meta.y !== undefined) {
368
+ metaParts.push(chalk.dim.gray(`๐Ÿ“ (${meta.x}, ${meta.y})`));
369
+ }
370
+ if (meta.selectorId && meta.consoleUrl) {
371
+ const cacheUrl = `${meta.consoleUrl}/cache/${meta.selectorId}`;
372
+ metaParts.push(chalk.blue.underline(cacheUrl));
373
+ }
374
+ if (meta.error) {
375
+ metaParts.push(chalk.dim.red(meta.error));
376
+ }
377
+ if (meta.cacheHit) {
378
+ metaParts.push(chalk.bold.yellow("โšก cached"));
379
+ if (meta.validated) {
380
+ const confStr = meta.validationConfidence !== null && meta.validationConfidence !== undefined
381
+ ? ` ${(meta.validationConfidence * 100).toFixed(1)}%`
382
+ : '';
383
+ metaParts.push(chalk.green(`โœ… validated${confStr}`));
384
+ if (meta.coordsUpdated) {
385
+ metaParts.push(chalk.dim.yellow(`โ†— coords shifted`));
386
+ }
387
+ }
388
+ }
389
+ if (meta.confidence !== undefined && meta.confidence !== null) {
390
+ metaParts.push(chalk.dim.gray(`confidence: ${meta.confidence}`));
391
+ }
392
+ if (meta.reasoning) {
393
+ metaParts.push(chalk.dim.gray(`reasoning: ${meta.reasoning}`));
394
+ }
395
+ // Duration always last
396
+ if (meta.duration) {
397
+ metaParts.push(this.formatDurationColored(meta.duration, thresholdKey));
398
+ }
399
+
400
+ return metaParts;
401
+ }
402
+
403
+ /**
404
+ * Format a result line (indented child result)
405
+ * @param {string} statusText - Status text (e.g., "found", "not found")
406
+ * @param {Function} statusColor - Chalk color function for status
407
+ * @param {Object} meta - Metadata object
408
+ * @param {string} thresholdKey - Duration threshold key
409
+ * @returns {string} Formatted result line
410
+ */
411
+ formatResultLine(statusText, statusColor, meta = {}, thresholdKey = "default") {
412
+ const parts = [];
413
+ this.addTimestamp(parts);
414
+ parts.push(this.getResultPrefix());
415
+ parts.push(statusColor(statusText));
416
+
417
+ const metaParts = this.buildResultMetaParts(meta, thresholdKey);
418
+ if (metaParts.length > 0) {
419
+ parts.push(this.joinMetaParts(metaParts));
420
+ }
421
+
422
+ return parts.join(" ");
423
+ }
424
+
425
+ /**
426
+ * Format an element found message with AWESOME styling ๐ŸŽฏ
427
+ * @param {string} description - Element description
428
+ * @param {Object} meta - Element metadata (coordinates, duration, cache hit)
429
+ * @returns {string} Formatted message
430
+ */
431
+ formatElementFound(description, meta = {}) {
432
+ return this.formatResultLine("found", chalk.green, meta);
433
+ }
434
+
435
+ /**
436
+ * Format an element not found message with styling โŒ
437
+ * @param {string} description - Element description
438
+ * @param {Object} meta - Metadata (duration, error)
439
+ * @returns {string} Formatted message
440
+ */
441
+ formatElementNotFound(description, meta = {}) {
442
+ return this.formatResultLine("not found", chalk.red, meta);
443
+ }
444
+
445
+ /**
446
+ * Format a finding all message (when search starts) ๐Ÿ”Ž
447
+ * @param {string} description - Element description
448
+ * @returns {string} Formatted message
449
+ */
450
+ formatElementsFinding(description) {
451
+ return this.formatFindingStyle("findAll", "Finding All", description);
452
+ }
453
+
454
+ /**
455
+ * Format a found all message with AWESOME styling ๐ŸŽฏ
456
+ * @param {string} description - Element description
457
+ * @param {number} count - Number of elements found
458
+ * @param {Object} meta - Metadata (duration, cache hit)
459
+ * @returns {string} Formatted message
460
+ */
461
+ formatElementsFound(description, count, meta = {}) {
462
+ const parts = [];
463
+ this.addTimestamp(parts);
464
+ parts.push(this.getResultPrefix());
465
+ parts.push(chalk.green(`found ${count} elements`));
466
+
467
+ const metaParts = [];
468
+ if (meta.cacheHit) {
469
+ metaParts.push(chalk.bold.yellow("โšก cached"));
470
+ }
471
+ if (meta.duration) {
472
+ metaParts.push(this.formatDurationColored(meta.duration));
473
+ }
474
+
475
+ if (metaParts.length > 0) {
476
+ parts.push(this.joinMetaParts(metaParts));
477
+ }
478
+
479
+ return parts.join(" ");
480
+ }
481
+
482
+ /**
483
+ * Format a single-line findAll message (combines finding + result) ๐Ÿ”Ž
484
+ * @param {string} description - Element description
485
+ * @param {number} count - Number of elements found
486
+ * @param {Object} meta - Metadata (duration, cache hit)
487
+ * @returns {string} Formatted message
488
+ */
489
+ formatFindAllSingleLine(description, count, meta = {}) {
490
+ const parts = [];
491
+ this.addTimestamp(parts);
492
+ parts.push(this.getPrefix("findAll"));
493
+ parts.push(chalk.bold.magenta("Finding All"));
494
+ parts.push(chalk.cyan(`"${description}"`));
495
+
496
+ const metaParts = [];
497
+
498
+ // Add count with appropriate coloring
499
+ if (count > 0) {
500
+ metaParts.push(chalk.green(`found ${count}`));
501
+ } else {
502
+ metaParts.push(chalk.yellow("found 0"));
503
+ }
504
+
505
+ // Add cache hit indicator
506
+ if (meta.cacheHit) {
507
+ metaParts.push(chalk.bold.yellow("โšก cached"));
508
+ }
509
+
510
+ // Add duration
511
+ if (meta.duration) {
512
+ metaParts.push(this.formatDurationColored(meta.duration));
513
+ }
514
+
515
+ if (metaParts.length > 0) {
516
+ parts.push(this.joinMetaParts(metaParts));
517
+ }
518
+
519
+ return parts.join(" ");
520
+ }
521
+
522
+ /**
523
+ * Format an asserting message (when assertion starts) โœ“
524
+ * @param {string} assertion - What is being asserted
525
+ * @returns {string} Formatted message
526
+ */
527
+ formatAsserting(assertion) {
528
+ return this.formatFindingStyle("assert", "Asserting", assertion);
529
+ }
530
+ /**
531
+ * Format the assertion result as a subtask line
532
+ * @param {boolean} passed - Whether assertion passed
533
+ * @param {string} response - The AI response message
534
+ * @param {number} durationMs - Duration in milliseconds
535
+ * @param {boolean} cacheHit - Whether the result was from cache
536
+ * @returns {string} Formatted result line
537
+ */
538
+ formatAssertResult(passed, response, durationMs, cacheHit = false) {
539
+ const parts = [];
540
+ this.addTimestamp(parts);
541
+ parts.push(this.getResultPrefix());
542
+
543
+ if (passed) {
544
+ parts.push(chalk.green("passed"));
545
+ } else {
546
+ parts.push(chalk.red("failed"));
547
+ }
548
+
549
+ // Add cache hit indicator (like find does)
550
+ if (cacheHit) {
551
+ parts.push(chalk.dim("ยท"));
552
+ parts.push(chalk.bold.yellow("โšก cached"));
553
+ }
554
+
555
+ // Add the response message (trimmed)
556
+ if (response) {
557
+ const trimmedResponse = response.trim().split('\n')[0]; // First line only
558
+ parts.push(chalk.dim(trimmedResponse));
559
+ }
560
+
561
+ // Add duration
562
+ if (durationMs) {
563
+ parts.push(this.formatDurationColored(durationMs, "action"));
564
+ }
565
+
566
+ return parts.join(" ");
567
+ }
568
+
569
+ // Action color mapping (shared between formatAction and formatActionComplete)
570
+ static ACTION_COLORS = {
571
+ click: chalk.bold.cyan,
572
+ hover: chalk.bold.blue,
573
+ type: chalk.bold.yellow,
574
+ scroll: chalk.bold.magenta,
575
+ assert: chalk.bold.green,
576
+ wait: chalk.bold.yellow,
577
+ };
578
+
579
+ /**
580
+ * Build action message parts (shared logic for formatAction and formatActionComplete)
581
+ * @param {string} action - Action type
582
+ * @param {string} description - Description or target
583
+ * @returns {Array} Array of formatted parts
584
+ */
585
+ buildActionParts(action, description) {
586
+ const parts = [];
587
+ this.addTimestamp(parts);
588
+
589
+ const actionKey = action.toLowerCase().replace(/\s+/g, "");
590
+ parts.push(this.getPrefix(actionKey));
591
+
592
+ const actionText = action.charAt(0).toUpperCase() + action.slice(1).toLowerCase();
593
+ const colorFn = SDKLogFormatter.ACTION_COLORS[actionKey] || chalk.bold.white;
594
+ parts.push(colorFn(actionText));
595
+
596
+ if (description) {
597
+ parts.push(chalk.cyan(`"${description}"`));
598
+ }
599
+
600
+ return { parts, actionKey };
601
+ }
602
+
603
+ /**
604
+ * Format an action message with AWESOME emojis! ๐ŸŽฌ
605
+ * @param {string} action - Action type
606
+ * @param {string} description - Description or target
607
+ * @param {Object} meta - Action metadata
608
+ * @returns {string} Formatted message
609
+ */
610
+ formatAction(action, description, meta = {}) {
611
+ const { parts } = this.buildActionParts(action, description);
612
+
613
+ const metaParts = [];
614
+ if (meta.text) {
615
+ metaParts.push(chalk.gray(`โ†’ ${chalk.white(meta.text)}`));
616
+ }
617
+ if (meta.duration) {
618
+ metaParts.push(chalk.dim(`โฑ๏ธ ${this.formatDurationColored(meta.duration, "quickAction", false)}`));
619
+ }
620
+
621
+ if (metaParts.length > 0) {
622
+ parts.push(this.joinMetaParts(metaParts));
623
+ }
624
+
625
+ return parts.join(" ");
626
+ }
627
+
628
+ /**
629
+ * Format an action complete message with separate action and redraw durations ๐ŸŽฌ
630
+ * @param {string} action - Action type
631
+ * @param {string} description - Description or target
632
+ * @param {Object} meta - Action metadata
633
+ * @param {number} meta.actionDuration - Duration of the action itself in ms
634
+ * @param {number} meta.redrawDuration - Duration of the redraw wait in ms
635
+ * @param {boolean} meta.cacheHit - Whether cache was hit
636
+ * @returns {string} Formatted message
637
+ */
638
+ formatActionComplete(action, description, meta = {}) {
639
+ const { parts } = this.buildActionParts(action, description);
640
+
641
+ const metaParts = [];
642
+
643
+ if (meta.actionDuration !== undefined) {
644
+ const durationMs = parseInt(meta.actionDuration);
645
+ const durationSec = (durationMs / 1000).toFixed(1) + 's';
646
+ const color = this.getDurationColor(durationMs, "action");
647
+ metaParts.push(chalk.dim(`โšก ${color(durationSec)}`));
648
+ }
649
+
650
+ if (meta.redrawDuration !== undefined) {
651
+ const durationMs = parseInt(meta.redrawDuration);
652
+ const durationSec = (durationMs / 1000).toFixed(1) + 's';
653
+ const color = this.getDurationColor(durationMs, "redraw");
654
+ metaParts.push(chalk.dim(`๐Ÿ”„ ${color(durationSec)}`));
655
+ }
656
+
657
+ if (meta.cacheHit) {
658
+ metaParts.push(chalk.bold.yellow("โšก cached"));
659
+ }
660
+
661
+ if (metaParts.length > 0) {
662
+ parts.push(this.joinMetaParts(metaParts));
663
+ }
664
+
665
+ return parts.join(" ");
666
+ }
667
+
668
+ /**
669
+ * Format an assertion message with beautiful status indicators ๐ŸŽฏ
670
+ * @param {string} assertion - What is being asserted
671
+ * @param {boolean} passed - Whether assertion passed
672
+ * @param {Object} meta - Assertion metadata
673
+ * @returns {string} Formatted message
674
+ */
675
+ formatAssertion(assertion, passed, meta = {}) {
676
+ const parts = [];
677
+ this.addTimestamp(parts);
678
+
679
+ if (passed) {
680
+ parts.push(this.getPrefix("success"));
681
+ parts.push(chalk.bold.green("Assert"));
682
+ parts.push(chalk.cyan(`"${assertion}"`));
683
+ parts.push(chalk.dim("ยท"));
684
+ parts.push(chalk.bold.green("โœ“ PASSED"));
685
+ } else {
686
+ parts.push(this.getPrefix("error"));
687
+ parts.push(chalk.bold.red("Assert"));
688
+ parts.push(chalk.cyan(`"${assertion}"`));
689
+ parts.push(chalk.dim("ยท"));
690
+ parts.push(chalk.bold.red("โœ— FAILED"));
691
+ }
692
+
693
+ if (meta.duration) {
694
+ parts.push(chalk.dim("ยท"));
695
+ parts.push(chalk.dim(`โฑ๏ธ ${this.formatDurationColored(meta.duration, "action", false)}`));
696
+ }
697
+
698
+ return parts.join(" ");
699
+ }
700
+
701
+ /**
702
+ * Format an error message with clear visual indicators ๐Ÿšจ
703
+ * @param {string} message - Error message
704
+ * @param {Error} error - Error object
705
+ * @returns {string} Formatted error
706
+ */
707
+ formatError(message, error) {
708
+ const parts = [];
709
+ this.addTimestamp(parts);
710
+ parts.push(this.getPrefix("error"));
711
+ parts.push(chalk.red.bold(message));
712
+
713
+ if (error && error.message) {
714
+ parts.push(chalk.dim("โ†’"));
715
+ parts.push(chalk.red(error.message));
716
+ }
717
+
718
+ return parts.join(" ");
719
+ }
720
+
721
+ /**
722
+ * Format a connection/disconnection message ๐Ÿ”Œ
723
+ * @param {string} type - 'connect' or 'disconnect'
724
+ * @param {Object} meta - Connection metadata
725
+ * @returns {string} Formatted message
726
+ */
727
+ formatConnection(type, meta = {}) {
728
+ const parts = [];
729
+ this.addTimestamp(parts);
730
+ parts.push(this.getPrefix(type));
731
+
732
+ if (type === "connect") {
733
+ parts.push(chalk.bold.green("Connected"));
734
+ if (meta.sandboxId) {
735
+ parts.push(chalk.dim("ยท"));
736
+ parts.push(chalk.cyan(`Sandbox: ${meta.sandboxId}`));
737
+ }
738
+ if (meta.os) {
739
+ parts.push(chalk.dim("ยท"));
740
+ parts.push(chalk.gray(`OS: ${meta.os}`));
741
+ }
742
+ } else {
743
+ parts.push(chalk.bold.yellow("Disconnected"));
744
+ }
745
+
746
+ return parts.join(" ");
747
+ }
748
+
749
+ /**
750
+ * Format a screenshot message ๐Ÿ“ธ
751
+ * @param {Object} meta - Screenshot metadata
752
+ * @returns {string} Formatted message
753
+ */
754
+ formatScreenshot(meta = {}) {
755
+ const parts = [];
756
+ this.addTimestamp(parts);
757
+ parts.push(this.getPrefix("screenshot"));
758
+ parts.push(chalk.bold.blue("Screenshot"));
759
+
760
+ if (meta.path) {
761
+ parts.push(chalk.dim("ยท"));
762
+ parts.push(chalk.cyan(meta.path));
763
+ }
764
+
765
+ if (meta.size) {
766
+ parts.push(chalk.dim("ยท"));
767
+ parts.push(chalk.gray(`${meta.size}`));
768
+ }
769
+
770
+ return parts.join(" ");
771
+ }
772
+
773
+ /**
774
+ * Format a cache status message โšก
775
+ * @param {boolean} hit - Whether it was a cache hit
776
+ * @param {Object} meta - Cache metadata
777
+ * @returns {string} Formatted message
778
+ */
779
+ formatCacheStatus(hit, meta = {}) {
780
+ const parts = [];
781
+ parts.push(this.getPrefix(hit ? "cacheHit" : "cacheMiss"));
782
+
783
+ if (hit) {
784
+ parts.push(chalk.bold.yellow("Cache HIT"));
785
+ if (meta.similarity !== undefined) {
786
+ const similarity = (meta.similarity * 100).toFixed(1);
787
+ parts.push(chalk.dim("ยท"));
788
+ parts.push(chalk.green(`${similarity}% similar`));
789
+ }
790
+ } else {
791
+ parts.push(chalk.dim.gray("Cache MISS"));
792
+ }
793
+
794
+ if (meta.strategy) {
795
+ parts.push(chalk.dim("ยท"));
796
+ parts.push(chalk.gray(meta.strategy));
797
+ }
798
+
799
+ return parts.join(" ");
800
+ }
801
+
802
+ /**
803
+ * Create a beautiful section header with box drawing ๐Ÿ“ฆ
804
+ * @param {string} title - Section title
805
+ * @param {string} emoji - Optional emoji to prefix
806
+ * @returns {string} Formatted header
807
+ */
808
+ formatHeader(title, emoji = "โœจ") {
809
+ const width = Math.min(60, Math.max(title.length + 4, 40));
810
+ const topLine = chalk.dim("โ•ญ" + "โ”€".repeat(width - 2) + "โ•ฎ");
811
+ const titleLine =
812
+ `${chalk.dim("โ”‚")} ${emoji} ${chalk.bold.white(title)}`.padEnd(width + 20) + chalk.dim("โ”‚");
813
+ const bottomLine = chalk.dim("โ•ฐ" + "โ”€".repeat(width - 2) + "โ•ฏ");
814
+ return `\n${topLine}\n${titleLine}\n${bottomLine}\n`;
815
+ }
816
+
817
+ /**
818
+ * Format a simple divider
819
+ * @param {string} char - Character to use for divider
820
+ * @returns {string} Formatted divider
821
+ */
822
+ formatDivider(char = "โ”€") {
823
+ return chalk.dim(char.repeat(60));
824
+ }
825
+
826
+ /**
827
+ * Format a beautiful summary line with stats ๐Ÿ“Š
828
+ * @param {Object} stats - Test statistics
829
+ * @returns {string} Formatted summary
830
+ */
831
+ formatSummary(stats) {
832
+ const parts = [];
833
+
834
+ if (stats.passed > 0) {
835
+ parts.push(chalk.bold.green(`โœ“ ${stats.passed} passed`));
836
+ }
837
+ if (stats.failed > 0) {
838
+ parts.push(chalk.bold.red(`โœ— ${stats.failed} failed`));
839
+ }
840
+ if (stats.skipped > 0) {
841
+ parts.push(chalk.yellow(`โŠ˜ ${stats.skipped} skipped`));
842
+ }
843
+ if (stats.total > 0) {
844
+ parts.push(chalk.dim(`${stats.total} total`));
845
+ }
846
+ if (stats.duration) {
847
+ parts.push(chalk.dim(`โฑ๏ธ ${stats.duration}`));
848
+ }
849
+
850
+ const divider = this.formatDivider();
851
+ const separator = chalk.dim(" โ”‚ ");
852
+ return `\n${divider}\n${parts.join(separator)}\n${divider}\n`;
853
+ }
854
+
855
+ /**
856
+ * Format a progress indicator ๐Ÿ“ˆ
857
+ * @param {number} current - Current step
858
+ * @param {number} total - Total steps
859
+ * @param {string} message - Progress message
860
+ * @returns {string} Formatted progress
861
+ */
862
+ formatProgress(current, total, message = "") {
863
+ const percentage = Math.round((current / total) * 100);
864
+ const barWidth = 20;
865
+ const filled = Math.round((current / total) * barWidth);
866
+ const empty = barWidth - filled;
867
+
868
+ const bar = chalk.green("โ–ˆ".repeat(filled)) + chalk.dim("โ–‘".repeat(empty));
869
+ const stats = chalk.dim(`${current}/${total}`);
870
+
871
+ const parts = [
872
+ chalk.bold("Progress"),
873
+ bar,
874
+ chalk.cyan(`${percentage}%`),
875
+ stats,
876
+ ];
877
+
878
+ if (message) {
879
+ parts.push(chalk.dim("ยท"));
880
+ parts.push(chalk.gray(message));
881
+ }
882
+
883
+ return parts.join(" ");
884
+ }
885
+
886
+ /**
887
+ * Format a waiting/loading message โณ
888
+ * @param {string} message - What we're waiting for
889
+ * @param {number} elapsed - Elapsed time in ms
890
+ * @returns {string} Formatted waiting message
891
+ */
892
+ formatWaiting(message, elapsed) {
893
+ const parts = [];
894
+ parts.push(this.getPrefix("wait"));
895
+ parts.push(chalk.bold.yellow("Waiting"));
896
+ parts.push(chalk.cyan(message));
897
+
898
+ if (elapsed) {
899
+ const seconds = (elapsed / 1000).toFixed(1);
900
+ parts.push(chalk.dim("ยท"));
901
+ parts.push(chalk.gray(`${seconds}s`));
902
+ }
903
+
904
+ return parts.join(" ");
905
+ }
906
+
907
+ /**
908
+ * Format test start message ๐Ÿš€
909
+ * @param {string} testName - Name of the test
910
+ * @returns {string} Formatted test start
911
+ */
912
+ formatTestStart(testName) {
913
+ return `\n${chalk.bold.cyan("โ–ถ๏ธ Running:")} ${chalk.white(testName)}\n`;
914
+ }
915
+
916
+ /**
917
+ * Format test end message with result ๐Ÿ
918
+ * @param {string} testName - Name of the test
919
+ * @param {boolean} passed - Whether test passed
920
+ * @param {number} duration - Test duration in ms
921
+ * @returns {string} Formatted test end
922
+ */
923
+ formatTestEnd(testName, passed, duration) {
924
+ const parts = [];
925
+
926
+ if (passed) {
927
+ parts.push(chalk.bold.green("โœ… PASSED"));
928
+ } else {
929
+ parts.push(chalk.bold.red("โŒ FAILED"));
930
+ }
931
+
932
+ parts.push(chalk.white(testName));
933
+
934
+ if (duration) {
935
+ const seconds = (duration / 1000).toFixed(2);
936
+ const color = this.getDurationColor(duration, "test");
937
+ parts.push(chalk.dim("ยท"));
938
+ parts.push(color(`${seconds}s`));
939
+ }
940
+
941
+ return `\n${parts.join(" ")}\n`;
942
+ }
943
+
944
+ /**
945
+ * Format ai() start message - provides visual scope boundary
946
+ * @param {string} task - The task being executed
947
+ * @returns {string} Formatted ai start message
948
+ */
949
+ formatAIStart(task) {
950
+ const parts = [];
951
+ this.addTimestamp(parts);
952
+ parts.push(this.getPrefix("action"));
953
+ parts.push(chalk.bold.cyan("AI"));
954
+ parts.push(chalk.cyan(`"${task}"`));
955
+ return parts.join(" ");
956
+ }
957
+
958
+ /**
959
+ * Format ai() completion message - provides visual scope boundary
960
+ * @param {number} durationMs - Duration in milliseconds
961
+ * @param {boolean} success - Whether the ai completed successfully
962
+ * @param {string} [error] - Error message if failed
963
+ * @returns {string} Formatted ai complete message
964
+ */
965
+ formatAIComplete(durationMs, success, error = null) {
966
+ const parts = [];
967
+ this.addTimestamp(parts);
968
+ parts.push(this.getResultPrefix());
969
+
970
+ if (success) {
971
+ parts.push(chalk.green("complete"));
972
+ } else {
973
+ parts.push(chalk.red("failed"));
974
+ if (error) {
975
+ parts.push(chalk.dim("ยท"));
976
+ parts.push(chalk.red(error));
977
+ }
978
+ }
979
+
980
+ parts.push(this.formatDurationColored(durationMs, "default"));
981
+
982
+ return parts.join(" ");
983
+ }
984
+
985
+ /**
986
+ * Format act() start message - provides visual scope boundary
987
+ * @param {string} task - The task being executed
988
+ * @returns {string} Formatted act start message
989
+ */
990
+ formatActStart(task) {
991
+ const parts = [];
992
+ this.addTimestamp(parts);
993
+ parts.push(this.getPrefix("action"));
994
+ parts.push(chalk.bold.cyan("Act"));
995
+ parts.push(chalk.cyan(`"${task}"`));
996
+ return parts.join(" ");
997
+ }
998
+
999
+ /**
1000
+ * Format act() completion message - provides visual scope boundary
1001
+ * @param {number} durationMs - Duration in milliseconds
1002
+ * @param {boolean} success - Whether the act completed successfully
1003
+ * @param {string} [error] - Error message if failed
1004
+ * @returns {string} Formatted act complete message
1005
+ */
1006
+ formatActComplete(durationMs, success, error = null) {
1007
+ const parts = [];
1008
+ this.addTimestamp(parts);
1009
+ parts.push(this.getResultPrefix());
1010
+
1011
+ if (success) {
1012
+ parts.push(chalk.green("complete"));
1013
+ } else {
1014
+ parts.push(chalk.red("failed"));
1015
+ if (error) {
1016
+ parts.push(chalk.dim("ยท"));
1017
+ parts.push(chalk.red(error));
1018
+ }
1019
+ }
1020
+
1021
+ parts.push(this.formatDurationColored(durationMs, "default"));
1022
+
1023
+ return parts.join(" ");
1024
+ }
1025
+
1026
+ /**
1027
+ * Format parse() elements as a formatted table for console output ๐Ÿ“‹
1028
+ * @param {Array} elements - Array of parsed elements from parse()
1029
+ * @param {Object} options - Formatting options
1030
+ * @param {number} options.maxContentLength - Max length for content column (default: 30)
1031
+ * @param {number} options.maxRows - Max number of rows to display (default: 50)
1032
+ * @returns {string} Formatted table string
1033
+ */
1034
+ formatParseElements(elements, options = {}) {
1035
+ if (!elements || elements.length === 0) {
1036
+ return chalk.dim(" No elements found");
1037
+ }
1038
+
1039
+ const maxContentLength = options.maxContentLength || 30;
1040
+ const maxRows = options.maxRows || 50;
1041
+
1042
+ // Column widths
1043
+ const idxWidth = 5;
1044
+ const typeWidth = 10;
1045
+ const contentWidth = maxContentLength + 2;
1046
+ const interactWidth = 14;
1047
+ const posWidth = 18;
1048
+
1049
+ const lines = [];
1050
+
1051
+ // Header
1052
+ const headerLine = [
1053
+ chalk.bold.cyan(this._padRight("Idx", idxWidth)),
1054
+ chalk.bold.cyan(this._padRight("Type", typeWidth)),
1055
+ chalk.bold.cyan(this._padRight("Content", contentWidth)),
1056
+ chalk.bold.cyan(this._padRight("Interactive", interactWidth)),
1057
+ chalk.bold.cyan("Position"),
1058
+ ].join(chalk.dim(" โ”‚ "));
1059
+
1060
+ lines.push(" " + headerLine);
1061
+
1062
+ // Separator line
1063
+ const separatorLine = [
1064
+ chalk.dim("โ”€".repeat(idxWidth)),
1065
+ chalk.dim("โ”€".repeat(typeWidth)),
1066
+ chalk.dim("โ”€".repeat(contentWidth)),
1067
+ chalk.dim("โ”€".repeat(interactWidth)),
1068
+ chalk.dim("โ”€".repeat(posWidth)),
1069
+ ].join(chalk.dim("โ”€โ”ผโ”€"));
1070
+
1071
+ lines.push(" " + separatorLine);
1072
+
1073
+ // Data rows
1074
+ const displayElements = elements.slice(0, maxRows);
1075
+ for (const el of displayElements) {
1076
+ const idx = String(el.index ?? "?");
1077
+ const type = el.type || "unknown";
1078
+ const content = this._truncate(el.content || "", maxContentLength);
1079
+
1080
+ // Format interactivity with color
1081
+ // Note: interactivity can be boolean (true/false) or string ("clickable", "non-interactive")
1082
+ let interactivity = el.interactivity || "-";
1083
+ let interactivityDisplay;
1084
+ if (interactivity === "clickable" || interactivity === true) {
1085
+ interactivityDisplay = chalk.green("โœ“ clickable");
1086
+ } else if (interactivity === false || interactivity === "non-interactive") {
1087
+ interactivityDisplay = chalk.dim("-");
1088
+ } else {
1089
+ interactivityDisplay = chalk.dim(String(interactivity));
1090
+ }
1091
+
1092
+ // Format position from bbox
1093
+ let position = "-";
1094
+ if (el.bbox) {
1095
+ position = `(${el.bbox.x0}, ${el.bbox.y0})`;
1096
+ }
1097
+
1098
+ // For interactivity column, we need to pad based on visible text, not chalk codes
1099
+ // "โœ“ clickable" = 11 chars, "-" = 1 char, so we pad manually after getting visible length
1100
+ const interactPadded = this._padRight(interactivityDisplay, interactWidth);
1101
+
1102
+ const dataLine = [
1103
+ chalk.yellow(this._padRight(idx, idxWidth)),
1104
+ chalk.white(this._padRight(type, typeWidth)),
1105
+ chalk.gray(this._padRight(content, contentWidth)),
1106
+ interactPadded,
1107
+ chalk.dim(position),
1108
+ ].join(chalk.dim(" โ”‚ "));
1109
+
1110
+ lines.push(" " + dataLine);
1111
+ }
1112
+
1113
+ // Show truncation message if needed
1114
+ if (elements.length > maxRows) {
1115
+ lines.push(chalk.dim(` ... and ${elements.length - maxRows} more elements (showing first ${maxRows})`));
1116
+ }
1117
+
1118
+ return lines.join("\n");
1119
+ }
1120
+
1121
+ /**
1122
+ * Truncate a string to a maximum length with ellipsis
1123
+ * @private
1124
+ * @param {string} str - String to truncate
1125
+ * @param {number} maxLength - Maximum length
1126
+ * @returns {string} Truncated string
1127
+ */
1128
+ _truncate(str, maxLength) {
1129
+ if (!str) return "";
1130
+ // Remove newlines and extra whitespace
1131
+ const cleaned = str.replace(/\s+/g, " ").trim();
1132
+ if (cleaned.length <= maxLength) return cleaned;
1133
+ return cleaned.substring(0, maxLength - 1) + "โ€ฆ";
1134
+ }
1135
+
1136
+ /**
1137
+ * Pad a string to the right to a fixed width
1138
+ * @private
1139
+ * @param {string} str - String to pad
1140
+ * @param {number} width - Target width
1141
+ * @returns {string} Padded string
1142
+ */
1143
+ _padRight(str, width) {
1144
+ // Handle chalk strings by getting visible length
1145
+ const visibleStr = String(str).replace(/\x1b\[[0-9;]*m/g, "");
1146
+ const padding = Math.max(0, width - visibleStr.length);
1147
+ return str + " ".repeat(padding);
1148
+ }
1149
+ }
1150
+
1151
+ // Export singleton instance
1152
+ const formatter = new SDKLogFormatter();
1153
+
1154
+ module.exports = {
1155
+ SDKLogFormatter,
1156
+ formatter,
1157
+ };