testdriverai 7.2.64 → 7.2.65

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 (360) hide show
  1. package/agent/index.js +4 -3
  2. package/agent/interface.js +11 -251
  3. package/agent/lib/debugger-server.js +2 -1
  4. package/agent/lib/logger.js +56 -0
  5. package/agent/lib/sandbox.js +6 -7
  6. package/ai/agents/test-writer.md +457 -0
  7. package/{docs/v7/ai.mdx → ai/skills/testdriver:ai/SKILL.md} +3 -5
  8. package/{docs/v7/assert.mdx → ai/skills/testdriver:assert/SKILL.md} +3 -4
  9. package/{docs/v7/aws-setup.mdx → ai/skills/testdriver:aws-setup/SKILL.md} +3 -4
  10. package/{docs/v7/caching.mdx → ai/skills/testdriver:caching/SKILL.md} +3 -7
  11. package/{docs/v7/captcha.mdx → ai/skills/testdriver:captcha/SKILL.md} +4 -5
  12. package/{docs/v7/ci-cd.mdx → ai/skills/testdriver:ci-cd/SKILL.md} +3 -4
  13. package/{docs/v7/click.mdx → ai/skills/testdriver:click/SKILL.md} +3 -4
  14. package/{docs/v7/client.mdx → ai/skills/testdriver:client/SKILL.md} +11 -5
  15. package/{docs/v7/cloud.mdx → ai/skills/testdriver:cloud/SKILL.md} +3 -4
  16. package/{docs/v7/customizing-devices.mdx → ai/skills/testdriver:customizing-devices/SKILL.md} +36 -26
  17. package/{docs/v7/dashcam.mdx → ai/skills/testdriver:dashcam/SKILL.md} +3 -4
  18. package/{docs/v7/device-config.mdx → ai/skills/testdriver:device-config/SKILL.md} +3 -3
  19. package/{docs/v7/double-click.mdx → ai/skills/testdriver:double-click/SKILL.md} +3 -3
  20. package/{docs/v7/elements.mdx → ai/skills/testdriver:elements/SKILL.md} +3 -4
  21. package/{docs/v7/enterprise.mdx → ai/skills/testdriver:enterprise/SKILL.md} +3 -5
  22. package/ai/skills/testdriver:examples/SKILL.md +7 -0
  23. package/{docs/v7/exec.mdx → ai/skills/testdriver:exec/SKILL.md} +3 -4
  24. package/{docs/v7/find.mdx → ai/skills/testdriver:find/SKILL.md} +81 -4
  25. package/{docs/v7/focus-application.mdx → ai/skills/testdriver:focus-application/SKILL.md} +3 -4
  26. package/{docs/v7/generating-tests.mdx → ai/skills/testdriver:generating-tests/SKILL.md} +3 -3
  27. package/{docs/v7/hover.mdx → ai/skills/testdriver:hover/SKILL.md} +3 -4
  28. package/{docs/v7/locating-elements.mdx → ai/skills/testdriver:locating-elements/SKILL.md} +3 -3
  29. package/{docs/v7/making-assertions.mdx → ai/skills/testdriver:making-assertions/SKILL.md} +3 -3
  30. package/ai/skills/testdriver:mcp-workflow/SKILL.md +410 -0
  31. package/{docs/v7/mouse-down.mdx → ai/skills/testdriver:mouse-down/SKILL.md} +3 -3
  32. package/{docs/v7/mouse-up.mdx → ai/skills/testdriver:mouse-up/SKILL.md} +3 -3
  33. package/{docs/v7/performing-actions.mdx → ai/skills/testdriver:performing-actions/SKILL.md} +3 -3
  34. package/{docs/v7/press-keys.mdx → ai/skills/testdriver:press-keys/SKILL.md} +3 -4
  35. package/{docs/v7/quickstart.mdx → ai/skills/testdriver:quickstart/SKILL.md} +3 -4
  36. package/{docs/v7/reusable-code.mdx → ai/skills/testdriver:reusable-code/SKILL.md} +3 -3
  37. package/{docs/v7/right-click.mdx → ai/skills/testdriver:right-click/SKILL.md} +3 -3
  38. package/{docs/v7/running-tests.mdx → ai/skills/testdriver:running-tests/SKILL.md} +3 -3
  39. package/{docs/v7/screenshot.mdx → ai/skills/testdriver:screenshot/SKILL.md} +3 -4
  40. package/{docs/v7/scroll.mdx → ai/skills/testdriver:scroll/SKILL.md} +3 -4
  41. package/{docs/v7/secrets.mdx → ai/skills/testdriver:secrets/SKILL.md} +3 -3
  42. package/{docs/v7/self-hosted.mdx → ai/skills/testdriver:self-hosted/SKILL.md} +3 -4
  43. package/ai/skills/testdriver:testdriver/SKILL.md +31 -0
  44. package/{docs/v7/type.mdx → ai/skills/testdriver:type/SKILL.md} +3 -4
  45. package/{docs/v7/variables.mdx → ai/skills/testdriver:variables/SKILL.md} +3 -3
  46. package/{docs/v7/waiting-for-elements.mdx → ai/skills/testdriver:waiting-for-elements/SKILL.md} +3 -3
  47. package/{docs/v7/what-is-testdriver.mdx → ai/skills/testdriver:what-is-testdriver/SKILL.md} +3 -3
  48. package/interfaces/cli/commands/init.js +278 -1
  49. package/interfaces/cli/commands/setup.js +382 -0
  50. package/interfaces/vitest-plugin.mjs +190 -122
  51. package/lib/sentry.js +4 -3
  52. package/lib/vitest/hooks.mjs +70 -16
  53. package/package.json +29 -9
  54. package/sdk.d.ts +29 -2
  55. package/sdk.js +1 -0
  56. package/.env.example +0 -4
  57. package/.github/workflows/acceptance-linux-scheduled.yaml +0 -45
  58. package/.github/workflows/acceptance-windows-scheduled.yaml +0 -54
  59. package/.github/workflows/acceptance.yaml +0 -87
  60. package/.github/workflows/publish.yaml +0 -68
  61. package/.github/workflows/test-init.yml +0 -145
  62. package/.github/workflows/testdriver.yml +0 -170
  63. package/.github/workflows/windows-self-hosted.yaml +0 -82
  64. package/.prettierignore +0 -4
  65. package/.prettierrc +0 -1
  66. package/CHANGELOG.md +0 -34
  67. package/agents.md +0 -455
  68. package/debugger/bg.png +0 -0
  69. package/debugger/icon.png +0 -0
  70. package/debugger/index.html +0 -797
  71. package/debugger/td.png +0 -0
  72. package/debugger/tray-buffered.png +0 -0
  73. package/debugger/tray.png +0 -0
  74. package/docs/GITHUB_COMMENTS.md +0 -330
  75. package/docs/GITHUB_COMMENTS_ANNOUNCEMENT.md +0 -167
  76. package/docs/QUICK-START-GITHUB-COMMENTS.md +0 -84
  77. package/docs/TEST-GITHUB-COMMENTS.md +0 -129
  78. package/docs/_scripts/link-replacer.js +0 -164
  79. package/docs/_scripts/upload-docs-to-openai.js +0 -284
  80. package/docs/docs.json +0 -393
  81. package/docs/github-integration-setup.md +0 -266
  82. package/docs/guide/best-practices-polling.mdx +0 -154
  83. package/docs/images/content/account/newprojectsettings.png +0 -0
  84. package/docs/images/content/account/projectpage.png +0 -0
  85. package/docs/images/content/account/projectreplays.png +0 -0
  86. package/docs/images/content/account/team-manage.png +0 -0
  87. package/docs/images/content/account/teampage.png +0 -0
  88. package/docs/images/content/extension/cursor.svg +0 -1
  89. package/docs/images/content/extension/vscode.svg +0 -57
  90. package/docs/images/content/extension/windsurf.svg +0 -3
  91. package/docs/images/content/self-hosted/launchtemplateid.png +0 -0
  92. package/docs/images/content/side-by-side.png +0 -0
  93. package/docs/images/content/vscode/ide-full.png +0 -0
  94. package/docs/images/content/vscode/running.png +0 -0
  95. package/docs/images/content/vscode/vscode-2-assert.png +0 -0
  96. package/docs/images/content/vscode/vscode-agent-preview.png +0 -0
  97. package/docs/images/content/vscode/vscode-copilot-ask.png +0 -0
  98. package/docs/images/content/vscode/vscode-file-creation.png +0 -0
  99. package/docs/images/content/vscode/vscode-install.png +0 -0
  100. package/docs/images/content/vscode/vscode-overview.png +0 -0
  101. package/docs/images/content/vscode/vscode-setup-walkthrough.png +0 -0
  102. package/docs/images/content/vscode/vscode-stopchat.png +0 -0
  103. package/docs/images/content/vscode/vscode-stoptest.png +0 -0
  104. package/docs/images/content/vscode/vscode-tdservice.png +0 -0
  105. package/docs/images/content/vscode/vscode-test-output.png +0 -0
  106. package/docs/images/content/vscode/vscode-testhistory.png +0 -0
  107. package/docs/images/content/vscode/vscode-testpane-runtests.png +0 -0
  108. package/docs/images/content/vscode/vscode-testpane.png +0 -0
  109. package/docs/images/template/dark.png +0 -0
  110. package/docs/images/template/icon.png +0 -0
  111. package/docs/images/template/light.png +0 -0
  112. package/docs/snippets/calendar-link.mdx +0 -4
  113. package/docs/snippets/gitignore-warning.mdx +0 -7
  114. package/docs/snippets/lifecycle-warning.mdx +0 -6
  115. package/docs/snippets/test-prereqs.mdx +0 -12
  116. package/docs/snippets/tests/assert-replay.mdx +0 -7
  117. package/docs/snippets/tests/assert-yaml.mdx +0 -8
  118. package/docs/snippets/tests/exec-js-replay.mdx +0 -7
  119. package/docs/snippets/tests/exec-js-yaml.mdx +0 -32
  120. package/docs/snippets/tests/exec-shell-replay.mdx +0 -7
  121. package/docs/snippets/tests/exec-shell-yaml.mdx +0 -15
  122. package/docs/snippets/tests/hover-image-replay.mdx +0 -7
  123. package/docs/snippets/tests/hover-image-yaml.mdx +0 -17
  124. package/docs/snippets/tests/hover-text-replay.mdx +0 -7
  125. package/docs/snippets/tests/hover-text-with-description-replay.mdx +0 -7
  126. package/docs/snippets/tests/hover-text-with-description-yaml.mdx +0 -24
  127. package/docs/snippets/tests/hover-text-yaml.mdx +0 -14
  128. package/docs/snippets/tests/match-image-replay.mdx +0 -7
  129. package/docs/snippets/tests/match-image-yaml.mdx +0 -17
  130. package/docs/snippets/tests/press-keys-replay.mdx +0 -7
  131. package/docs/snippets/tests/press-keys-yaml.mdx +0 -36
  132. package/docs/snippets/tests/remember-replay.mdx +0 -7
  133. package/docs/snippets/tests/remember-yaml.mdx +0 -28
  134. package/docs/snippets/tests/scroll-replay.mdx +0 -7
  135. package/docs/snippets/tests/scroll-until-image-replay.mdx +0 -7
  136. package/docs/snippets/tests/scroll-until-image-yaml.mdx +0 -14
  137. package/docs/snippets/tests/scroll-until-text-replay.mdx +0 -7
  138. package/docs/snippets/tests/scroll-until-text-yaml.mdx +0 -17
  139. package/docs/snippets/tests/scroll-yaml.mdx +0 -30
  140. package/docs/snippets/tests/type-repeated-replay.mdx +0 -7
  141. package/docs/snippets/tests/type-repeated-yaml.mdx +0 -22
  142. package/docs/snippets/tests/type-replay.mdx +0 -7
  143. package/docs/snippets/tests/type-yaml.mdx +0 -28
  144. package/docs/snippets/tests/wait-for-image-replay.mdx +0 -7
  145. package/docs/snippets/tests/wait-for-image-yaml.mdx +0 -18
  146. package/docs/snippets/tests/wait-for-text-replay.mdx +0 -7
  147. package/docs/snippets/tests/wait-for-text-yaml.mdx +0 -18
  148. package/docs/snippets/tests/wait-replay.mdx +0 -7
  149. package/docs/snippets/tests/wait-yaml.mdx +0 -13
  150. package/docs/styles.css +0 -65
  151. package/docs/v6/account/dashboard.mdx +0 -16
  152. package/docs/v6/account/enterprise.mdx +0 -110
  153. package/docs/v6/account/pricing.mdx +0 -33
  154. package/docs/v6/account/projects.mdx +0 -33
  155. package/docs/v6/account/team.mdx +0 -35
  156. package/docs/v6/action/ami.mdx +0 -109
  157. package/docs/v6/action/performance.mdx +0 -105
  158. package/docs/v6/action/secrets.mdx +0 -93
  159. package/docs/v6/apps/chrome-extensions.mdx +0 -48
  160. package/docs/v6/apps/desktop-apps.mdx +0 -93
  161. package/docs/v6/apps/mobile-apps.mdx +0 -26
  162. package/docs/v6/apps/static-websites.mdx +0 -54
  163. package/docs/v6/apps/tauri-apps.mdx +0 -361
  164. package/docs/v6/bugs/jira.mdx +0 -232
  165. package/docs/v6/cli/overview.mdx +0 -66
  166. package/docs/v6/commands/assert.mdx +0 -45
  167. package/docs/v6/commands/exec.mdx +0 -282
  168. package/docs/v6/commands/focus-application.mdx +0 -44
  169. package/docs/v6/commands/hover-image.mdx +0 -69
  170. package/docs/v6/commands/hover-text.mdx +0 -47
  171. package/docs/v6/commands/if.mdx +0 -53
  172. package/docs/v6/commands/match-image.mdx +0 -67
  173. package/docs/v6/commands/press-keys.mdx +0 -87
  174. package/docs/v6/commands/remember.mdx +0 -49
  175. package/docs/v6/commands/run.mdx +0 -44
  176. package/docs/v6/commands/scroll-until-image.mdx +0 -66
  177. package/docs/v6/commands/scroll-until-text.mdx +0 -60
  178. package/docs/v6/commands/scroll.mdx +0 -69
  179. package/docs/v6/commands/type.mdx +0 -45
  180. package/docs/v6/commands/wait-for-image.mdx +0 -54
  181. package/docs/v6/commands/wait-for-text.mdx +0 -48
  182. package/docs/v6/commands/wait.mdx +0 -45
  183. package/docs/v6/exporting/junit.mdx +0 -218
  184. package/docs/v6/exporting/playwright.mdx +0 -197
  185. package/docs/v6/features/auto-healing.mdx +0 -144
  186. package/docs/v6/features/generation.mdx +0 -116
  187. package/docs/v6/features/parallel-testing.mdx +0 -151
  188. package/docs/v6/features/reusable-snippets.mdx +0 -131
  189. package/docs/v6/features/selectorless.mdx +0 -80
  190. package/docs/v6/features/visual-assertions.mdx +0 -139
  191. package/docs/v6/getting-started/ci.mdx +0 -146
  192. package/docs/v6/getting-started/cli.mdx +0 -91
  193. package/docs/v6/getting-started/editing.mdx +0 -100
  194. package/docs/v6/getting-started/playwright.mdx +0 -342
  195. package/docs/v6/getting-started/running.mdx +0 -48
  196. package/docs/v6/getting-started/self-hosting.mdx +0 -408
  197. package/docs/v6/getting-started/vscode.mdx +0 -89
  198. package/docs/v6/guide/assertions.mdx +0 -189
  199. package/docs/v6/guide/authentication.mdx +0 -136
  200. package/docs/v6/guide/code.mdx +0 -65
  201. package/docs/v6/guide/dashcam.mdx +0 -118
  202. package/docs/v6/guide/environment-variables.mdx +0 -26
  203. package/docs/v6/guide/lifecycle.mdx +0 -242
  204. package/docs/v6/guide/locating.mdx +0 -141
  205. package/docs/v6/guide/protips.mdx +0 -43
  206. package/docs/v6/guide/variables.mdx +0 -143
  207. package/docs/v6/guide/waiting.mdx +0 -130
  208. package/docs/v6/importing/csv.mdx +0 -196
  209. package/docs/v6/importing/gherkin.mdx +0 -143
  210. package/docs/v6/importing/jira.mdx +0 -164
  211. package/docs/v6/importing/testrail.mdx +0 -162
  212. package/docs/v6/integrations/electron.mdx +0 -146
  213. package/docs/v6/integrations/netlify.mdx +0 -100
  214. package/docs/v6/integrations/vercel.mdx +0 -125
  215. package/docs/v6/interactive/explore.mdx +0 -99
  216. package/docs/v6/interactive/run.mdx +0 -52
  217. package/docs/v6/interactive/save.mdx +0 -63
  218. package/docs/v6/overview/comparison.mdx +0 -101
  219. package/docs/v6/overview/faq.mdx +0 -162
  220. package/docs/v6/overview/performance.mdx +0 -52
  221. package/docs/v6/overview/quickstart.mdx +0 -137
  222. package/docs/v6/overview/what-is-testdriver.mdx +0 -85
  223. package/docs/v6/scenarios/ai-chatbot.mdx +0 -28
  224. package/docs/v6/scenarios/cookie-banner.mdx +0 -32
  225. package/docs/v6/scenarios/file-upload.mdx +0 -33
  226. package/docs/v6/scenarios/form-filling.mdx +0 -32
  227. package/docs/v6/scenarios/log-in.mdx +0 -75
  228. package/docs/v6/scenarios/pdf-generation.mdx +0 -25
  229. package/docs/v6/scenarios/spell-check.mdx +0 -22
  230. package/docs/v6/security/action.mdx +0 -84
  231. package/docs/v6/security/agent.mdx +0 -73
  232. package/docs/v6/security/platform.mdx +0 -77
  233. package/docs/v6/tutorials/advanced-test.mdx +0 -81
  234. package/docs/v6/tutorials/basic-test.mdx +0 -45
  235. package/docs/v7/_drafts/agents.mdx +0 -852
  236. package/docs/v7/_drafts/architecture.mdx +0 -399
  237. package/docs/v7/_drafts/auto-cache-key.mdx +0 -167
  238. package/docs/v7/_drafts/awesome-logs-quick-ref.mdx +0 -100
  239. package/docs/v7/_drafts/best-practices.mdx +0 -486
  240. package/docs/v7/_drafts/caching-ai.mdx +0 -215
  241. package/docs/v7/_drafts/caching-selectors.mdx +0 -424
  242. package/docs/v7/_drafts/caching.mdx +0 -366
  243. package/docs/v7/_drafts/cli-to-sdk-migration.mdx +0 -425
  244. package/docs/v7/_drafts/commands/assert.mdx +0 -45
  245. package/docs/v7/_drafts/commands/exec.mdx +0 -282
  246. package/docs/v7/_drafts/commands/focus-application.mdx +0 -44
  247. package/docs/v7/_drafts/commands/hover-image.mdx +0 -69
  248. package/docs/v7/_drafts/commands/hover-text.mdx +0 -47
  249. package/docs/v7/_drafts/commands/if.mdx +0 -53
  250. package/docs/v7/_drafts/commands/match-image.mdx +0 -67
  251. package/docs/v7/_drafts/commands/press-keys.mdx +0 -87
  252. package/docs/v7/_drafts/commands/remember.mdx +0 -49
  253. package/docs/v7/_drafts/commands/run.mdx +0 -44
  254. package/docs/v7/_drafts/commands/scroll-until-image.mdx +0 -66
  255. package/docs/v7/_drafts/commands/scroll-until-text.mdx +0 -60
  256. package/docs/v7/_drafts/commands/scroll.mdx +0 -69
  257. package/docs/v7/_drafts/commands/type.mdx +0 -45
  258. package/docs/v7/_drafts/commands/wait-for-image.mdx +0 -54
  259. package/docs/v7/_drafts/commands/wait-for-text.mdx +0 -48
  260. package/docs/v7/_drafts/commands/wait.mdx +0 -45
  261. package/docs/v7/_drafts/configuration.mdx +0 -378
  262. package/docs/v7/_drafts/contributing.mdx +0 -174
  263. package/docs/v7/_drafts/core.mdx +0 -458
  264. package/docs/v7/_drafts/dashcam-title-feature.mdx +0 -89
  265. package/docs/v7/_drafts/debugging.mdx +0 -349
  266. package/docs/v7/_drafts/error-handling.mdx +0 -501
  267. package/docs/v7/_drafts/faq.mdx +0 -393
  268. package/docs/v7/_drafts/hooks.mdx +0 -360
  269. package/docs/v7/_drafts/init-command.mdx +0 -95
  270. package/docs/v7/_drafts/installation.mdx +0 -420
  271. package/docs/v7/_drafts/migration.mdx +0 -562
  272. package/docs/v7/_drafts/observable.mdx +0 -604
  273. package/docs/v7/_drafts/playwright.mdx +0 -342
  274. package/docs/v7/_drafts/plugin-migration.mdx +0 -220
  275. package/docs/v7/_drafts/powerful.mdx +0 -419
  276. package/docs/v7/_drafts/presets.mdx +0 -210
  277. package/docs/v7/_drafts/progressive-disclosure.mdx +0 -230
  278. package/docs/v7/_drafts/prompt-cache.mdx +0 -200
  279. package/docs/v7/_drafts/provision.mdx +0 -390
  280. package/docs/v7/_drafts/quick-start-test-recording.mdx +0 -214
  281. package/docs/v7/_drafts/readme.mdx +0 -135
  282. package/docs/v7/_drafts/reports.mdx +0 -414
  283. package/docs/v7/_drafts/scalable.mdx +0 -754
  284. package/docs/v7/_drafts/screenshot.mdx +0 -155
  285. package/docs/v7/_drafts/sdk-awesome-logs.mdx +0 -468
  286. package/docs/v7/_drafts/sdk-browser-rendering.mdx +0 -167
  287. package/docs/v7/_drafts/sdk-migration.mdx +0 -474
  288. package/docs/v7/_drafts/sdk-v7-complete.mdx +0 -345
  289. package/docs/v7/_drafts/self-hosting.mdx +0 -369
  290. package/docs/v7/_drafts/test-recording.mdx +0 -382
  291. package/docs/v7/_drafts/troubleshooting.mdx +0 -526
  292. package/docs/v7/_drafts/vitest-plugin.mdx +0 -477
  293. package/docs/v7/_drafts/vitest.mdx +0 -535
  294. package/docs/v7/_drafts/writing-tests.mdx +0 -25
  295. package/docs/v7/examples.mdx +0 -5
  296. package/eslint.config.js +0 -67
  297. package/examples/ai.test.mjs +0 -30
  298. package/examples/assert.test.mjs +0 -46
  299. package/examples/captcha-api.test.mjs +0 -50
  300. package/examples/chrome-extension.test.mjs +0 -94
  301. package/examples/drag-and-drop.test.mjs +0 -58
  302. package/examples/element-not-found.test.mjs +0 -26
  303. package/examples/exec-output.test.mjs +0 -59
  304. package/examples/exec-pwsh.test.mjs +0 -57
  305. package/examples/focus-window.test.mjs +0 -36
  306. package/examples/formatted-logging.test.mjs +0 -26
  307. package/examples/hover-image.test.mjs +0 -52
  308. package/examples/hover-text-with-description.test.mjs +0 -56
  309. package/examples/hover-text.test.mjs +0 -27
  310. package/examples/installer.test.mjs +0 -49
  311. package/examples/launch-vscode-linux.test.mjs +0 -54
  312. package/examples/match-image.test.mjs +0 -54
  313. package/examples/no-provision.test.mjs +0 -23
  314. package/examples/press-keys.test.mjs +0 -50
  315. package/examples/prompt.test.mjs +0 -33
  316. package/examples/scroll-keyboard.test.mjs +0 -37
  317. package/examples/scroll-until-image.test.mjs +0 -39
  318. package/examples/scroll-until-text.test.mjs +0 -67
  319. package/examples/scroll.test.mjs +0 -41
  320. package/examples/type.test.mjs +0 -45
  321. package/examples/windows-installer.test.mjs +0 -53
  322. package/interfaces/cli/commands/edit.js +0 -3
  323. package/interfaces/cli/commands/generate.js +0 -3
  324. package/interfaces/cli/commands/run.js +0 -3
  325. package/interfaces/cli/utils/factory.js +0 -71
  326. package/jsconfig.json +0 -26
  327. package/manual/test-init-command.js +0 -223
  328. package/sdk-log-formatter.js +0 -930
  329. package/setup/aws/cloudformation.yaml +0 -470
  330. package/setup/aws/spawn-runner.sh +0 -190
  331. package/test/api-resilience.test.mjs +0 -0
  332. package/test/captcha-solver.test.mjs +0 -70
  333. package/test/chrome-remote-debugging.test.mjs +0 -66
  334. package/test/manual/debug-locate-response.js +0 -82
  335. package/test/manual/reconnect-provision.test.mjs +0 -49
  336. package/test/manual/test-console-logs.test.mjs +0 -42
  337. package/test/manual/test-find-api.js +0 -73
  338. package/test/manual/test-init.sh +0 -54
  339. package/test/manual/test-prompt-cache.js +0 -96
  340. package/test/manual/test-provision-auth.mjs +0 -22
  341. package/test/manual/test-sandbox-render.js +0 -28
  342. package/test/manual/test-sdk-methods.js +0 -15
  343. package/test/manual/test-sdk-refactor.js +0 -53
  344. package/test/manual/test-stack-trace.mjs +0 -57
  345. package/test/manual/verify-element-api.js +0 -89
  346. package/test/manual/verify-types.js +0 -0
  347. package/test/manual-unawaited-promise.test.mjs +0 -31
  348. package/testdriver-plugin/skills/actions/SKILL.md +0 -93
  349. package/testdriver-plugin/skills/assertions/SKILL.md +0 -77
  350. package/testdriver-plugin/skills/caching/SKILL.md +0 -66
  351. package/testdriver-plugin/skills/creating-tests/SKILL.md +0 -104
  352. package/testdriver-plugin/skills/finding-elements/SKILL.md +0 -77
  353. package/testdriver-plugin/skills/github-actions/SKILL.md +0 -100
  354. package/testdriver-plugin/skills/running-tests/SKILL.md +0 -77
  355. package/testdriver-plugin/skills/secrets/SKILL.md +0 -87
  356. package/testdriver-plugin/skills/self-hosting/SKILL.md +0 -89
  357. package/testdriver-plugin/skills/setup/SKILL.md +0 -76
  358. package/testdriver-plugin/skills/variables/SKILL.md +0 -88
  359. package/testdriver-plugin/skills/waiting/SKILL.md +0 -72
  360. package/vitest.config.mjs +0 -29
@@ -5,6 +5,7 @@ const path = require("path");
5
5
  const chalk = require("chalk");
6
6
  const { execSync } = require("child_process");
7
7
  const readline = require("readline");
8
+ const os = require("os");
8
9
 
9
10
  /**
10
11
  * Init command - scaffolds Vitest SDK example tests for TestDriver
@@ -19,7 +20,10 @@ class InitCommand extends BaseCommand {
19
20
  await this.createVitestExample();
20
21
  await this.createGitHubWorkflow();
21
22
  await this.createGitignore();
23
+ await this.createVscodeMcpConfig();
22
24
  await this.installDependencies();
25
+ await this.copySkills();
26
+ await this.createAgents();
23
27
  await this.promptForApiKey();
24
28
 
25
29
  console.log(chalk.green("\n✅ Project initialized successfully!\n"));
@@ -78,7 +82,11 @@ class InitCommand extends BaseCommand {
78
82
  : "";
79
83
 
80
84
  fs.writeFileSync(envPath, envContent + `TD_API_KEY=${apiKey.trim()}\n`);
85
+ process.env.TD_API_KEY = apiKey.trim();
81
86
  console.log(chalk.green("\n ✓ API key saved to .env\n"));
87
+
88
+ // Also persist to shell profile so it's available in all terminals
89
+ this.addToShellProfile("TD_API_KEY", apiKey.trim());
82
90
  } else {
83
91
  console.log(
84
92
  chalk.yellow(
@@ -132,6 +140,63 @@ class InitCommand extends BaseCommand {
132
140
  });
133
141
  }
134
142
 
143
+ /**
144
+ * Add an environment variable export to the user's shell profile
145
+ */
146
+ addToShellProfile(key, value) {
147
+ if (process.platform === "win32") {
148
+ // On Windows, set a persistent user environment variable via setx
149
+ try {
150
+ execSync(`setx ${key} "${value}"`, { stdio: "ignore" });
151
+ console.log(
152
+ chalk.green(` ✓ Set ${key} as user environment variable\n`),
153
+ );
154
+ } catch (error) {
155
+ console.log(
156
+ chalk.yellow(` ⚠️ Could not set ${key} via setx. You can set it manually:\n`),
157
+ );
158
+ console.log(chalk.gray(` setx ${key} "your_api_key"\n`));
159
+ }
160
+ return;
161
+ }
162
+
163
+ // Unix: append export to shell profile
164
+ const shell = process.env.SHELL || "/bin/bash";
165
+ const home = os.homedir();
166
+ let profilePath;
167
+
168
+ if (shell.includes("zsh")) {
169
+ profilePath = path.join(home, ".zshrc");
170
+ } else {
171
+ profilePath = path.join(home, ".bashrc");
172
+ }
173
+
174
+ const exportLine = `export ${key}="${value}"`;
175
+
176
+ // Check if already present
177
+ if (fs.existsSync(profilePath)) {
178
+ const content = fs.readFileSync(profilePath, "utf8");
179
+ if (content.includes(`export ${key}=`)) {
180
+ // Replace existing line
181
+ const updated = content.replace(
182
+ new RegExp(`^export ${key}=.*$`, "m"),
183
+ exportLine,
184
+ );
185
+ fs.writeFileSync(profilePath, updated);
186
+ console.log(
187
+ chalk.green(` ✓ Updated ${key} in ${profilePath}\n`),
188
+ );
189
+ return;
190
+ }
191
+ }
192
+
193
+ // Append to profile
194
+ fs.appendFileSync(profilePath, `\n${exportLine}\n`);
195
+ console.log(
196
+ chalk.green(` ✓ Added ${key} to ${profilePath}\n`),
197
+ );
198
+ }
199
+
135
200
  /**
136
201
  * Ask a yes/no question
137
202
  */
@@ -398,6 +463,214 @@ jobs:
398
463
  }
399
464
  }
400
465
 
466
+ /**
467
+ * Create VSCode MCP configuration
468
+ */
469
+ async createVscodeMcpConfig() {
470
+ const vscodeDir = path.join(process.cwd(), ".vscode");
471
+ const mcpConfigFile = path.join(vscodeDir, "mcp.json");
472
+
473
+ // Create .vscode directory if it doesn't exist
474
+ if (!fs.existsSync(vscodeDir)) {
475
+ fs.mkdirSync(vscodeDir, { recursive: true });
476
+ console.log(chalk.gray(` Created directory: ${vscodeDir}`));
477
+ }
478
+
479
+ if (!fs.existsSync(mcpConfigFile)) {
480
+ const mcpConfig = {
481
+ mcpServers: {
482
+ testdriver: {
483
+ command: "npx",
484
+ args: ["testdriverai-mcp"],
485
+ env: {
486
+ TD_API_KEY: "${TD_API_KEY}",
487
+ },
488
+ },
489
+ },
490
+ };
491
+
492
+ fs.writeFileSync(
493
+ mcpConfigFile,
494
+ JSON.stringify(mcpConfig, null, 2) + "\n",
495
+ );
496
+ console.log(chalk.green(` Created MCP config: ${mcpConfigFile}`));
497
+ } else {
498
+ console.log(chalk.gray(" MCP config already exists, skipping..."));
499
+ }
500
+ }
501
+
502
+ /**
503
+ * Copy TestDriver skills from the package to the project
504
+ */
505
+ async copySkills() {
506
+ const skillsDestDir = path.join(process.cwd(), ".github", "skills");
507
+
508
+ // Try to find skills in node_modules
509
+ const possibleSkillsSources = [
510
+ path.join(process.cwd(), "node_modules", "testdriverai", "ai", "skills"),
511
+ path.join(__dirname, "..", "..", "..", "ai", "skills"),
512
+ ];
513
+
514
+ let skillsSourceDir = null;
515
+ for (const source of possibleSkillsSources) {
516
+ if (fs.existsSync(source)) {
517
+ skillsSourceDir = source;
518
+ break;
519
+ }
520
+ }
521
+
522
+ if (!skillsSourceDir) {
523
+ console.log(chalk.yellow(" ⚠️ Skills directory not found, skipping skills copy..."));
524
+ return;
525
+ }
526
+
527
+ // Create .github/skills directory if it doesn't exist
528
+ if (!fs.existsSync(skillsDestDir)) {
529
+ fs.mkdirSync(skillsDestDir, { recursive: true });
530
+ console.log(chalk.gray(` Created directory: ${skillsDestDir}`));
531
+ }
532
+
533
+ // Copy all skill directories
534
+ const skillDirs = fs.readdirSync(skillsSourceDir);
535
+ let copiedCount = 0;
536
+
537
+ for (const skillDir of skillDirs) {
538
+ const sourcePath = path.join(skillsSourceDir, skillDir);
539
+ const destPath = path.join(skillsDestDir, skillDir);
540
+
541
+ if (fs.statSync(sourcePath).isDirectory()) {
542
+ // Create skill directory
543
+ if (!fs.existsSync(destPath)) {
544
+ fs.mkdirSync(destPath, { recursive: true });
545
+ }
546
+
547
+ // Copy SKILL.md file
548
+ const skillFile = path.join(sourcePath, "SKILL.md");
549
+ if (fs.existsSync(skillFile)) {
550
+ fs.copyFileSync(skillFile, path.join(destPath, "SKILL.md"));
551
+ copiedCount++;
552
+ }
553
+ }
554
+ }
555
+
556
+ console.log(chalk.green(` Copied ${copiedCount} skills to ${skillsDestDir}`));
557
+ }
558
+
559
+ /**
560
+ * Create TestDriver agents in GitHub Copilot format
561
+ */
562
+ async createAgents() {
563
+ const agentsDestDir = path.join(process.cwd(), ".github", "agents");
564
+
565
+ // Try to find agents in node_modules or local package
566
+ const possibleAgentsSources = [
567
+ path.join(process.cwd(), "node_modules", "testdriverai", "ai", "agents"),
568
+ path.join(__dirname, "..", "..", "..", "ai", "agents"),
569
+ ];
570
+
571
+ let agentsSourceDir = null;
572
+ for (const source of possibleAgentsSources) {
573
+ if (fs.existsSync(source)) {
574
+ agentsSourceDir = source;
575
+ break;
576
+ }
577
+ }
578
+
579
+ // Create .github/agents directory if it doesn't exist
580
+ if (!fs.existsSync(agentsDestDir)) {
581
+ fs.mkdirSync(agentsDestDir, { recursive: true });
582
+ console.log(chalk.gray(` Created directory: ${agentsDestDir}`));
583
+ }
584
+
585
+ // If we found source agents, convert them to .agent.md format
586
+ if (agentsSourceDir) {
587
+ const agentFiles = fs.readdirSync(agentsSourceDir).filter(f => f.endsWith(".md"));
588
+
589
+ for (const agentFile of agentFiles) {
590
+ const sourcePath = path.join(agentsSourceDir, agentFile);
591
+ const agentName = agentFile.replace(".md", "");
592
+ const destPath = path.join(agentsDestDir, `${agentName}.agent.md`);
593
+
594
+ if (!fs.existsSync(destPath)) {
595
+ const sourceContent = fs.readFileSync(sourcePath, "utf8");
596
+
597
+ // Parse the source frontmatter and body
598
+ const frontmatterMatch = sourceContent.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
599
+
600
+ if (frontmatterMatch) {
601
+ const frontmatterText = frontmatterMatch[1];
602
+ const body = frontmatterMatch[2];
603
+
604
+ // Extract description from frontmatter
605
+ const descMatch = frontmatterText.match(/description:\s*["']?(.*?)["']?$/m);
606
+ const description = descMatch ? descMatch[1] : `TestDriver ${agentName} agent`;
607
+
608
+ // Create GitHub Copilot agent format
609
+ const agentContent = `---
610
+ name: ${agentName}
611
+ description: ${description}
612
+ tools:
613
+ - testdriver/*
614
+ mcp-servers:
615
+ testdriver:
616
+ command: npx
617
+ args:
618
+ - testdriverai-mcp
619
+ env:
620
+ TD_API_KEY: \${TD_API_KEY}
621
+ ---
622
+ ${body}`;
623
+
624
+ fs.writeFileSync(destPath, agentContent);
625
+ console.log(chalk.green(` Created agent: ${destPath}`));
626
+ }
627
+ } else {
628
+ console.log(chalk.gray(` Agent ${agentName}.agent.md already exists, skipping...`));
629
+ }
630
+ }
631
+ } else {
632
+ // Create a default test-writer agent if no source found
633
+ const defaultAgentPath = path.join(agentsDestDir, "test-writer.agent.md");
634
+
635
+ if (!fs.existsSync(defaultAgentPath)) {
636
+ const defaultAgentContent = `---
637
+ name: test-writer
638
+ description: An expert at creating and refining automated tests using TestDriver.ai
639
+ tools:
640
+ - testdriver/*
641
+ mcp-servers:
642
+ testdriver:
643
+ command: npx
644
+ args:
645
+ - testdriverai-mcp
646
+ env:
647
+ TD_API_KEY: \${TD_API_KEY}
648
+ ---
649
+ # TestDriver Expert
650
+
651
+ You are an expert at writing automated tests using the TestDriver library. Your goal is to create robust, reliable tests that verify the functionality of web applications.
652
+
653
+ ## Workflow
654
+
655
+ 1. **Start Session**: Use \`session_start\` to provision a sandbox with browser
656
+ 2. **Interact**: Use \`find\`, \`click\`, \`type\` etc. - each returns a screenshot
657
+ 3. **Verify**: Use \`check\` after actions and \`assert\` for test conditions
658
+ 4. **Build Test**: Append generated code to your test file
659
+ 5. **Validate**: Use \`verify\` to run the test from scratch
660
+
661
+ ## Tips
662
+
663
+ - Be specific with element descriptions: "blue Sign In button in the header" > "button"
664
+ - Use \`check\` after actions to verify they succeeded
665
+ - Start simple - get one step working before adding more
666
+ `;
667
+
668
+ fs.writeFileSync(defaultAgentPath, defaultAgentContent);
669
+ console.log(chalk.green(` Created default agent: ${defaultAgentPath}`));
670
+ }
671
+ }
672
+ }
673
+
401
674
  /**
402
675
  * Install dependencies
403
676
  */
@@ -431,8 +704,12 @@ jobs:
431
704
  console.log(chalk.cyan("Next steps:\n"));
432
705
  console.log(" 1. Run your tests:");
433
706
  console.log(chalk.gray(" npx vitest run\n"));
707
+ console.log(" 2. Use AI agents to write tests:");
708
+ console.log(chalk.gray(" Open VSCode/Cursor and use @test-writer agent\n"));
709
+ console.log(" 3. MCP server configured:");
710
+ console.log(chalk.gray(" TestDriver tools available via MCP in .vscode/mcp.json\n"));
434
711
  console.log(
435
- " 2. For CI/CD, add TD_API_KEY to your GitHub repository secrets",
712
+ " 4. For CI/CD, add TD_API_KEY to your GitHub repository secrets",
436
713
  );
437
714
  console.log(
438
715
  chalk.gray(" Settings → Secrets → Actions → New repository secret\n"),
@@ -0,0 +1,382 @@
1
+ const { Command } = require("@oclif/core");
2
+ const { createCommandDefinitions } = require("../../../agent/interface.js");
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const os = require("os");
6
+ const chalk = require("chalk");
7
+ const { execSync } = require("child_process");
8
+ const readline = require("readline");
9
+
10
+ const PACKAGE_ROOT = path.resolve(__dirname, "..", "..", "..");
11
+ const CLAUDE_HOME = path.join(os.homedir(), ".claude");
12
+ const CLAUDE_MCP_FILE = path.join(os.homedir(), ".claude.json");
13
+ const CURSOR_MCP_FILE = path.join(os.homedir(), ".cursor", "mcp.json");
14
+
15
+ const MCP_SERVER_CONFIG = {
16
+ "testdriver-cloud": {
17
+ type: "sse",
18
+ url: "https://replayable-dev-ian-mac-m1-16.ngrok.io/api/v1/mcp",
19
+ headers: {
20
+ "x-api-key": "${TD_API_KEY}",
21
+ },
22
+ description:
23
+ "Query TestDriver test runs, test cases, and filters for your team using an API key.",
24
+ },
25
+ };
26
+
27
+ const CURSOR_MCP_SERVER_CONFIG = {
28
+ "testdriver-cloud": {
29
+ type: "sse",
30
+ url: "https://replayable-dev-ian-mac-m1-16.ngrok.io/api/v1/mcp",
31
+ headers: {
32
+ "x-api-key": "${TD_API_KEY}",
33
+ },
34
+ description:
35
+ "Query TestDriver test runs, test cases, and filters for your team using an API key.",
36
+ },
37
+ };
38
+
39
+ class SetupCommand extends Command {
40
+ async run() {
41
+ await this.parse(SetupCommand);
42
+
43
+ console.log(chalk.cyan("\nSetting up TestDriver for Claude Code...\n"));
44
+
45
+ const sourceSkills = path.join(PACKAGE_ROOT, "ai", "skills");
46
+ const sourceAgents = path.join(PACKAGE_ROOT, "ai", "agents");
47
+
48
+ this.installSkills(sourceSkills, path.join(CLAUDE_HOME, "skills"));
49
+ this.installAgents(sourceAgents, path.join(CLAUDE_HOME, "agents"));
50
+ this.installMcp();
51
+ this.installCursorMcp();
52
+ await this.promptForApiKey();
53
+
54
+ console.log(chalk.green("\nSetup complete!\n"));
55
+ this.printNextSteps();
56
+ process.exit(0);
57
+ }
58
+
59
+ /**
60
+ * Recursively copy a directory's contents
61
+ */
62
+ copyDirSync(src, dest) {
63
+ fs.mkdirSync(dest, { recursive: true });
64
+
65
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
66
+ const srcPath = path.join(src, entry.name);
67
+ const destPath = path.join(dest, entry.name);
68
+
69
+ if (entry.isDirectory()) {
70
+ this.copyDirSync(srcPath, destPath);
71
+ } else {
72
+ fs.copyFileSync(srcPath, destPath);
73
+ }
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Install skills to ~/.claude/skills
79
+ */
80
+ installSkills(source, dest) {
81
+ if (!fs.existsSync(source)) {
82
+ console.log(
83
+ chalk.yellow(" Skills source not found, skipping: " + source),
84
+ );
85
+ return;
86
+ }
87
+
88
+ const skills = fs
89
+ .readdirSync(source, { withFileTypes: true })
90
+ .filter((d) => d.isDirectory());
91
+
92
+ for (const skill of skills) {
93
+ const srcDir = path.join(source, skill.name);
94
+ const destDir = path.join(dest, skill.name);
95
+ this.copyDirSync(srcDir, destDir);
96
+ }
97
+
98
+ console.log(chalk.green(` Installed ${skills.length} skills to ${dest}`));
99
+ }
100
+
101
+ /**
102
+ * Install agents to ~/.claude/agents
103
+ */
104
+ installAgents(source, dest) {
105
+ if (!fs.existsSync(source)) {
106
+ console.log(
107
+ chalk.yellow(" Agents source not found, skipping: " + source),
108
+ );
109
+ return;
110
+ }
111
+
112
+ fs.mkdirSync(dest, { recursive: true });
113
+
114
+ const agents = fs.readdirSync(source).filter((f) => f.endsWith(".md"));
115
+
116
+ for (const agent of agents) {
117
+ fs.copyFileSync(path.join(source, agent), path.join(dest, agent));
118
+ }
119
+
120
+ console.log(
121
+ chalk.green(` Installed ${agents.length} agent(s) to ${dest}`),
122
+ );
123
+ }
124
+
125
+ /**
126
+ * Add testdriver MCP server to ~/.claude.json
127
+ */
128
+ installMcp() {
129
+ let config = {};
130
+
131
+ if (fs.existsSync(CLAUDE_MCP_FILE)) {
132
+ try {
133
+ config = JSON.parse(fs.readFileSync(CLAUDE_MCP_FILE, "utf8"));
134
+ } catch {
135
+ // If the file is malformed, start fresh but warn
136
+ console.log(
137
+ chalk.yellow(
138
+ " Warning: existing ~/.claude.json was not valid JSON, overwriting",
139
+ ),
140
+ );
141
+ }
142
+ }
143
+
144
+ if (!config.mcpServers) {
145
+ config.mcpServers = {};
146
+ }
147
+
148
+ const alreadyConfigured = config.mcpServers["testdriver-cloud"];
149
+
150
+ Object.assign(config.mcpServers, MCP_SERVER_CONFIG);
151
+ fs.writeFileSync(CLAUDE_MCP_FILE, JSON.stringify(config, null, 2) + "\n");
152
+
153
+ if (alreadyConfigured) {
154
+ console.log(
155
+ chalk.green(` Updated testdriver MCP server in ${CLAUDE_MCP_FILE}`),
156
+ );
157
+ } else {
158
+ console.log(
159
+ chalk.green(` Added testdriver MCP server to ${CLAUDE_MCP_FILE}`),
160
+ );
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Add testdriver MCP server to ~/.cursor/mcp.json
166
+ */
167
+ installCursorMcp() {
168
+ const cursorDir = path.dirname(CURSOR_MCP_FILE);
169
+ fs.mkdirSync(cursorDir, { recursive: true });
170
+
171
+ let config = {};
172
+
173
+ if (fs.existsSync(CURSOR_MCP_FILE)) {
174
+ try {
175
+ config = JSON.parse(fs.readFileSync(CURSOR_MCP_FILE, "utf8"));
176
+ } catch {
177
+ console.log(
178
+ chalk.yellow(
179
+ " Warning: existing ~/.cursor/mcp.json was not valid JSON, overwriting",
180
+ ),
181
+ );
182
+ }
183
+ }
184
+
185
+ if (!config.mcpServers) {
186
+ config.mcpServers = {};
187
+ }
188
+
189
+ const alreadyConfigured = config.mcpServers["testdriver-cloud"];
190
+
191
+ Object.assign(config.mcpServers, CURSOR_MCP_SERVER_CONFIG);
192
+ fs.writeFileSync(CURSOR_MCP_FILE, JSON.stringify(config, null, 2) + "\n");
193
+
194
+ if (alreadyConfigured) {
195
+ console.log(
196
+ chalk.green(` Updated testdriver MCP server in ${CURSOR_MCP_FILE}`),
197
+ );
198
+ } else {
199
+ console.log(
200
+ chalk.green(` Added testdriver MCP server to ${CURSOR_MCP_FILE}`),
201
+ );
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Prompt user for API key and save globally to shell profile
207
+ */
208
+ async promptForApiKey() {
209
+ // Check if TD_API_KEY is already set in the environment
210
+ if (process.env.TD_API_KEY) {
211
+ console.log(
212
+ chalk.gray("\n API key already set in environment, skipping...\n"),
213
+ );
214
+ return;
215
+ }
216
+
217
+ console.log(chalk.cyan("\n Setting up your TestDriver API key...\n"));
218
+ console.log(
219
+ chalk.gray(" Get your API key from: https://console.testdriver.ai/team"),
220
+ );
221
+
222
+ const shouldOpen = await this.askYesNo(
223
+ " Open API keys page in browser? (Y/n): ",
224
+ );
225
+ if (shouldOpen) {
226
+ try {
227
+ const open = (await import("open")).default;
228
+ await open("https://console.testdriver.ai/team");
229
+ console.log(chalk.gray(" Opening browser...\n"));
230
+ } catch (error) {
231
+ console.log(
232
+ chalk.yellow(" Could not open browser automatically\n"),
233
+ );
234
+ }
235
+ }
236
+
237
+ const apiKey = await this.promptHidden(
238
+ " Enter your API key (input will be hidden): ",
239
+ );
240
+
241
+ if (apiKey && apiKey.trim()) {
242
+ this.addToShellProfile("TD_API_KEY", apiKey.trim());
243
+ process.env.TD_API_KEY = apiKey.trim();
244
+ } else {
245
+ console.log(
246
+ chalk.yellow(
247
+ "\n No API key entered. You can set it later:\n",
248
+ ),
249
+ );
250
+ console.log(chalk.gray(' export TD_API_KEY="your_api_key"\n'));
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Prompt for hidden input (like password)
256
+ */
257
+ async promptHidden(question) {
258
+ return new Promise((resolve) => {
259
+ process.stdout.write(question);
260
+
261
+ const stdin = process.stdin;
262
+ const wasRaw = stdin.isRaw;
263
+ stdin.setRawMode(true);
264
+ stdin.resume();
265
+ stdin.setEncoding("utf8");
266
+
267
+ let input = "";
268
+
269
+ const onData = (char) => {
270
+ if (char === "\u0003") {
271
+ stdin.setRawMode(wasRaw);
272
+ process.exit();
273
+ }
274
+ if (char === "\r" || char === "\n") {
275
+ stdin.setRawMode(wasRaw);
276
+ stdin.removeListener("data", onData);
277
+ stdin.pause();
278
+ console.log("");
279
+ resolve(input);
280
+ return;
281
+ }
282
+ if (char === "\u007F" || char === "\b") {
283
+ input = input.slice(0, -1);
284
+ return;
285
+ }
286
+ input += char;
287
+ };
288
+
289
+ stdin.on("data", onData);
290
+ });
291
+ }
292
+
293
+ /**
294
+ * Ask a yes/no question
295
+ */
296
+ async askYesNo(question) {
297
+ return new Promise((resolve) => {
298
+ const rl = readline.createInterface({
299
+ input: process.stdin,
300
+ output: process.stdout,
301
+ });
302
+
303
+ rl.question(question, (answer) => {
304
+ rl.close();
305
+ const normalized = answer.toLowerCase().trim();
306
+ resolve(
307
+ normalized === "" || normalized === "y" || normalized === "yes",
308
+ );
309
+ });
310
+ });
311
+ }
312
+
313
+ /**
314
+ * Add an environment variable export to the user's shell profile
315
+ */
316
+ addToShellProfile(key, value) {
317
+ if (process.platform === "win32") {
318
+ try {
319
+ execSync(`setx ${key} "${value}"`, { stdio: "ignore" });
320
+ console.log(
321
+ chalk.green(`\n Set ${key} as user environment variable\n`),
322
+ );
323
+ } catch (error) {
324
+ console.log(
325
+ chalk.yellow(`\n Could not set ${key} via setx. You can set it manually:\n`),
326
+ );
327
+ console.log(chalk.gray(` setx ${key} "your_api_key"\n`));
328
+ }
329
+ return;
330
+ }
331
+
332
+ const shell = process.env.SHELL || "/bin/bash";
333
+ const home = os.homedir();
334
+ let profilePath;
335
+
336
+ if (shell.includes("zsh")) {
337
+ profilePath = path.join(home, ".zshrc");
338
+ } else {
339
+ profilePath = path.join(home, ".bashrc");
340
+ }
341
+
342
+ const exportLine = `export ${key}="${value}"`;
343
+
344
+ if (fs.existsSync(profilePath)) {
345
+ const content = fs.readFileSync(profilePath, "utf8");
346
+ if (content.includes(`export ${key}=`)) {
347
+ const updated = content.replace(
348
+ new RegExp(`^export ${key}=.*$`, "m"),
349
+ exportLine,
350
+ );
351
+ fs.writeFileSync(profilePath, updated);
352
+ console.log(
353
+ chalk.green(`\n Updated ${key} in ${profilePath}\n`),
354
+ );
355
+ return;
356
+ }
357
+ }
358
+
359
+ fs.appendFileSync(profilePath, `\n${exportLine}\n`);
360
+ console.log(
361
+ chalk.green(`\n Added ${key} to ${profilePath}\n`),
362
+ );
363
+ }
364
+
365
+ printNextSteps() {
366
+ console.log(chalk.cyan("Next steps:\n"));
367
+ console.log(" 1. Restart Claude Code to pick up the new MCP server\n");
368
+ }
369
+ }
370
+
371
+ // Get command definition from interface.js
372
+ const tempAgent = { workingDir: process.cwd() };
373
+ const definitions = createCommandDefinitions(tempAgent);
374
+ const commandDef = definitions["setup"];
375
+
376
+ SetupCommand.description =
377
+ commandDef?.description ||
378
+ "Set up TestDriver skills, agents, and MCP for Claude Code";
379
+ SetupCommand.args = commandDef?.args || {};
380
+ SetupCommand.flags = commandDef?.flags || {};
381
+
382
+ module.exports = SetupCommand;