@avi770/testteam 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (325) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/LICENSE +21 -0
  3. package/README.md +167 -0
  4. package/agents/01-analyst.ts +100 -0
  5. package/agents/02-seed-architect.ts +59 -0
  6. package/agents/03-test-generator.ts +191 -0
  7. package/agents/04-unit-runner.ts +160 -0
  8. package/agents/05-browser-crawler.ts +790 -0
  9. package/agents/06-api-exerciser.ts +311 -0
  10. package/agents/07-security-scout.ts +188 -0
  11. package/agents/08-a11y-guardian.ts +212 -0
  12. package/agents/09-healer.ts +228 -0
  13. package/agents/10-reporter.ts +266 -0
  14. package/agents/11-fixer.ts +253 -0
  15. package/agents/12-ux-inspector.ts +444 -0
  16. package/agents/13-performance-profiler.ts +271 -0
  17. package/agents/14-data-integrity-auditor.ts +417 -0
  18. package/agents/15-regression-sentinel.ts +307 -0
  19. package/agents/16-chaos-agent.ts +228 -0
  20. package/agents/17-documentation-validator.ts +266 -0
  21. package/agents/18-integration-watchdog.ts +178 -0
  22. package/agents/19-tenant-isolation-auditor.ts +199 -0
  23. package/agents/20-workflow-completion-tester.ts +203 -0
  24. package/agents/21-state-session-tester.ts +262 -0
  25. package/agents/22-email-notification-verifier.ts +244 -0
  26. package/agents/23-migration-tester.ts +80 -0
  27. package/agents/__tests__/01-analyst.test.ts +188 -0
  28. package/agents/__tests__/02-seed-architect.test.ts +152 -0
  29. package/agents/__tests__/03-test-generator-full.test.ts +321 -0
  30. package/agents/__tests__/03-test-generator.test.ts +318 -0
  31. package/agents/__tests__/04-unit-runner.test.ts +320 -0
  32. package/agents/__tests__/05-browser-crawler-beta.test.ts +492 -0
  33. package/agents/__tests__/05-browser-crawler-release.test.ts +412 -0
  34. package/agents/__tests__/05-browser-crawler-uat.test.ts +578 -0
  35. package/agents/__tests__/05-browser-crawler.test.ts +518 -0
  36. package/agents/__tests__/06-api-exerciser.test.ts +619 -0
  37. package/agents/__tests__/07-security-scout.test.ts +382 -0
  38. package/agents/__tests__/08-a11y-guardian.test.ts +530 -0
  39. package/agents/__tests__/09-healer.test.ts +384 -0
  40. package/agents/__tests__/10-reporter.test.ts +366 -0
  41. package/agents/__tests__/11-fixer.test.ts +406 -0
  42. package/agents/__tests__/12-ux-inspector-extended.test.ts +465 -0
  43. package/agents/__tests__/12-ux-inspector.test.ts +443 -0
  44. package/agents/__tests__/13-performance-profiler.test.ts +411 -0
  45. package/agents/__tests__/14-data-integrity-auditor-extended.test.ts +573 -0
  46. package/agents/__tests__/14-data-integrity-auditor.test.ts +407 -0
  47. package/agents/__tests__/15-regression-sentinel.test.ts +657 -0
  48. package/agents/__tests__/16-chaos-agent.test.ts +427 -0
  49. package/agents/__tests__/17-documentation-validator.test.ts +402 -0
  50. package/agents/__tests__/18-integration-watchdog.test.ts +263 -0
  51. package/agents/__tests__/19-tenant-isolation-auditor.test.ts +400 -0
  52. package/agents/__tests__/20-workflow-completion-tester.test.ts +586 -0
  53. package/agents/__tests__/21-state-session-tester.test.ts +374 -0
  54. package/agents/__tests__/22-email-notification-verifier.test.ts +441 -0
  55. package/agents/__tests__/23-migration-tester.test.ts +145 -0
  56. package/agents/__tests__/base-agent.test.ts +188 -0
  57. package/agents/__tests__/registry.test.ts +218 -0
  58. package/agents/base-agent.ts +77 -0
  59. package/agents/registry.ts +136 -0
  60. package/baselines/api-schemas/.gitkeep +0 -0
  61. package/baselines/performance/.gitkeep +0 -0
  62. package/baselines/screenshots/.gitkeep +0 -0
  63. package/bin/testteam.js +10 -0
  64. package/core/__tests__/ci-output.test.ts +430 -0
  65. package/core/__tests__/cli.test.ts +387 -0
  66. package/core/__tests__/config.test.ts +78 -0
  67. package/core/__tests__/cost-tracker.test.ts +158 -0
  68. package/core/__tests__/evidence.test.ts +265 -0
  69. package/core/__tests__/fix-loop.test.ts +210 -0
  70. package/core/__tests__/health-check.test.ts +44 -0
  71. package/core/__tests__/init.test.ts +609 -0
  72. package/core/__tests__/integration.test.ts +204 -0
  73. package/core/__tests__/license-gen.test.ts +227 -0
  74. package/core/__tests__/license.test.ts +326 -0
  75. package/core/__tests__/multi-browser.test.ts +278 -0
  76. package/core/__tests__/orchestrator.test.ts +519 -0
  77. package/core/__tests__/phase-gate.test.ts +43 -0
  78. package/core/__tests__/report-html.test.ts +398 -0
  79. package/core/__tests__/report-upload.test.ts +325 -0
  80. package/core/__tests__/run-counter.test.ts +234 -0
  81. package/core/ci-output.ts +240 -0
  82. package/core/cli.ts +232 -0
  83. package/core/config.ts +178 -0
  84. package/core/cost-tracker.ts +59 -0
  85. package/core/evidence.ts +132 -0
  86. package/core/fix-loop.ts +85 -0
  87. package/core/health-check.ts +54 -0
  88. package/core/init.ts +546 -0
  89. package/core/license-gen.ts +212 -0
  90. package/core/license.ts +211 -0
  91. package/core/messages.ts +67 -0
  92. package/core/multi-browser.ts +136 -0
  93. package/core/orchestrator.ts +354 -0
  94. package/core/phase-gate.ts +55 -0
  95. package/core/report-html.ts +657 -0
  96. package/core/report-upload.ts +188 -0
  97. package/core/run-counter.ts +175 -0
  98. package/core/types.ts +57 -0
  99. package/dist/agents/01-analyst.d.ts +11 -0
  100. package/dist/agents/01-analyst.d.ts.map +1 -0
  101. package/dist/agents/01-analyst.js +75 -0
  102. package/dist/agents/01-analyst.js.map +1 -0
  103. package/dist/agents/02-seed-architect.d.ts +11 -0
  104. package/dist/agents/02-seed-architect.d.ts.map +1 -0
  105. package/dist/agents/02-seed-architect.js +51 -0
  106. package/dist/agents/02-seed-architect.js.map +1 -0
  107. package/dist/agents/03-test-generator.d.ts +9 -0
  108. package/dist/agents/03-test-generator.d.ts.map +1 -0
  109. package/dist/agents/03-test-generator.js +167 -0
  110. package/dist/agents/03-test-generator.js.map +1 -0
  111. package/dist/agents/04-unit-runner.d.ts +9 -0
  112. package/dist/agents/04-unit-runner.d.ts.map +1 -0
  113. package/dist/agents/04-unit-runner.js +113 -0
  114. package/dist/agents/04-unit-runner.js.map +1 -0
  115. package/dist/agents/05-browser-crawler.d.ts +30 -0
  116. package/dist/agents/05-browser-crawler.d.ts.map +1 -0
  117. package/dist/agents/05-browser-crawler.js +685 -0
  118. package/dist/agents/05-browser-crawler.js.map +1 -0
  119. package/dist/agents/06-api-exerciser.d.ts +23 -0
  120. package/dist/agents/06-api-exerciser.d.ts.map +1 -0
  121. package/dist/agents/06-api-exerciser.js +253 -0
  122. package/dist/agents/06-api-exerciser.js.map +1 -0
  123. package/dist/agents/07-security-scout.d.ts +11 -0
  124. package/dist/agents/07-security-scout.d.ts.map +1 -0
  125. package/dist/agents/07-security-scout.js +142 -0
  126. package/dist/agents/07-security-scout.js.map +1 -0
  127. package/dist/agents/08-a11y-guardian.d.ts +13 -0
  128. package/dist/agents/08-a11y-guardian.d.ts.map +1 -0
  129. package/dist/agents/08-a11y-guardian.js +176 -0
  130. package/dist/agents/08-a11y-guardian.js.map +1 -0
  131. package/dist/agents/09-healer.d.ts +33 -0
  132. package/dist/agents/09-healer.d.ts.map +1 -0
  133. package/dist/agents/09-healer.js +167 -0
  134. package/dist/agents/09-healer.js.map +1 -0
  135. package/dist/agents/10-reporter.d.ts +26 -0
  136. package/dist/agents/10-reporter.d.ts.map +1 -0
  137. package/dist/agents/10-reporter.js +215 -0
  138. package/dist/agents/10-reporter.js.map +1 -0
  139. package/dist/agents/11-fixer.d.ts +26 -0
  140. package/dist/agents/11-fixer.d.ts.map +1 -0
  141. package/dist/agents/11-fixer.js +195 -0
  142. package/dist/agents/11-fixer.js.map +1 -0
  143. package/dist/agents/12-ux-inspector.d.ts +15 -0
  144. package/dist/agents/12-ux-inspector.d.ts.map +1 -0
  145. package/dist/agents/12-ux-inspector.js +364 -0
  146. package/dist/agents/12-ux-inspector.js.map +1 -0
  147. package/dist/agents/13-performance-profiler.d.ts +13 -0
  148. package/dist/agents/13-performance-profiler.d.ts.map +1 -0
  149. package/dist/agents/13-performance-profiler.js +216 -0
  150. package/dist/agents/13-performance-profiler.js.map +1 -0
  151. package/dist/agents/14-data-integrity-auditor.d.ts +12 -0
  152. package/dist/agents/14-data-integrity-auditor.d.ts.map +1 -0
  153. package/dist/agents/14-data-integrity-auditor.js +356 -0
  154. package/dist/agents/14-data-integrity-auditor.js.map +1 -0
  155. package/dist/agents/15-regression-sentinel.d.ts +25 -0
  156. package/dist/agents/15-regression-sentinel.d.ts.map +1 -0
  157. package/dist/agents/15-regression-sentinel.js +251 -0
  158. package/dist/agents/15-regression-sentinel.js.map +1 -0
  159. package/dist/agents/16-chaos-agent.d.ts +9 -0
  160. package/dist/agents/16-chaos-agent.d.ts.map +1 -0
  161. package/dist/agents/16-chaos-agent.js +207 -0
  162. package/dist/agents/16-chaos-agent.js.map +1 -0
  163. package/dist/agents/17-documentation-validator.d.ts +31 -0
  164. package/dist/agents/17-documentation-validator.d.ts.map +1 -0
  165. package/dist/agents/17-documentation-validator.js +246 -0
  166. package/dist/agents/17-documentation-validator.js.map +1 -0
  167. package/dist/agents/18-integration-watchdog.d.ts +10 -0
  168. package/dist/agents/18-integration-watchdog.d.ts.map +1 -0
  169. package/dist/agents/18-integration-watchdog.js +138 -0
  170. package/dist/agents/18-integration-watchdog.js.map +1 -0
  171. package/dist/agents/19-tenant-isolation-auditor.d.ts +9 -0
  172. package/dist/agents/19-tenant-isolation-auditor.d.ts.map +1 -0
  173. package/dist/agents/19-tenant-isolation-auditor.js +166 -0
  174. package/dist/agents/19-tenant-isolation-auditor.js.map +1 -0
  175. package/dist/agents/20-workflow-completion-tester.d.ts +12 -0
  176. package/dist/agents/20-workflow-completion-tester.d.ts.map +1 -0
  177. package/dist/agents/20-workflow-completion-tester.js +159 -0
  178. package/dist/agents/20-workflow-completion-tester.js.map +1 -0
  179. package/dist/agents/21-state-session-tester.d.ts +10 -0
  180. package/dist/agents/21-state-session-tester.d.ts.map +1 -0
  181. package/dist/agents/21-state-session-tester.js +233 -0
  182. package/dist/agents/21-state-session-tester.js.map +1 -0
  183. package/dist/agents/22-email-notification-verifier.d.ts +11 -0
  184. package/dist/agents/22-email-notification-verifier.d.ts.map +1 -0
  185. package/dist/agents/22-email-notification-verifier.js +199 -0
  186. package/dist/agents/22-email-notification-verifier.js.map +1 -0
  187. package/dist/agents/23-migration-tester.d.ts +10 -0
  188. package/dist/agents/23-migration-tester.d.ts.map +1 -0
  189. package/dist/agents/23-migration-tester.js +74 -0
  190. package/dist/agents/23-migration-tester.js.map +1 -0
  191. package/dist/agents/base-agent.d.ts +19 -0
  192. package/dist/agents/base-agent.d.ts.map +1 -0
  193. package/dist/agents/base-agent.js +67 -0
  194. package/dist/agents/base-agent.js.map +1 -0
  195. package/dist/agents/registry.d.ts +29 -0
  196. package/dist/agents/registry.d.ts.map +1 -0
  197. package/dist/agents/registry.js +117 -0
  198. package/dist/agents/registry.js.map +1 -0
  199. package/dist/core/ci-output.d.ts +35 -0
  200. package/dist/core/ci-output.d.ts.map +1 -0
  201. package/dist/core/ci-output.js +193 -0
  202. package/dist/core/ci-output.js.map +1 -0
  203. package/dist/core/cli.d.ts +11 -0
  204. package/dist/core/cli.d.ts.map +1 -0
  205. package/dist/core/cli.js +197 -0
  206. package/dist/core/cli.js.map +1 -0
  207. package/dist/core/config.d.ts +111 -0
  208. package/dist/core/config.d.ts.map +1 -0
  209. package/dist/core/config.js +42 -0
  210. package/dist/core/config.js.map +1 -0
  211. package/dist/core/cost-tracker.d.ts +22 -0
  212. package/dist/core/cost-tracker.d.ts.map +1 -0
  213. package/dist/core/cost-tracker.js +41 -0
  214. package/dist/core/cost-tracker.js.map +1 -0
  215. package/dist/core/evidence.d.ts +28 -0
  216. package/dist/core/evidence.d.ts.map +1 -0
  217. package/dist/core/evidence.js +95 -0
  218. package/dist/core/evidence.js.map +1 -0
  219. package/dist/core/fix-loop.d.ts +29 -0
  220. package/dist/core/fix-loop.d.ts.map +1 -0
  221. package/dist/core/fix-loop.js +70 -0
  222. package/dist/core/fix-loop.js.map +1 -0
  223. package/dist/core/health-check.d.ts +21 -0
  224. package/dist/core/health-check.d.ts.map +1 -0
  225. package/dist/core/health-check.js +26 -0
  226. package/dist/core/health-check.js.map +1 -0
  227. package/dist/core/init.d.ts +2 -0
  228. package/dist/core/init.d.ts.map +1 -0
  229. package/dist/core/init.js +435 -0
  230. package/dist/core/init.js.map +1 -0
  231. package/dist/core/license-gen.d.ts +12 -0
  232. package/dist/core/license-gen.d.ts.map +1 -0
  233. package/dist/core/license-gen.js +169 -0
  234. package/dist/core/license-gen.js.map +1 -0
  235. package/dist/core/license.d.ts +33 -0
  236. package/dist/core/license.d.ts.map +1 -0
  237. package/dist/core/license.js +170 -0
  238. package/dist/core/license.js.map +1 -0
  239. package/dist/core/messages.d.ts +10 -0
  240. package/dist/core/messages.d.ts.map +1 -0
  241. package/dist/core/messages.js +47 -0
  242. package/dist/core/messages.js.map +1 -0
  243. package/dist/core/multi-browser.d.ts +36 -0
  244. package/dist/core/multi-browser.d.ts.map +1 -0
  245. package/dist/core/multi-browser.js +88 -0
  246. package/dist/core/multi-browser.js.map +1 -0
  247. package/dist/core/orchestrator.d.ts +48 -0
  248. package/dist/core/orchestrator.d.ts.map +1 -0
  249. package/dist/core/orchestrator.js +291 -0
  250. package/dist/core/orchestrator.js.map +1 -0
  251. package/dist/core/phase-gate.d.ts +4 -0
  252. package/dist/core/phase-gate.d.ts.map +1 -0
  253. package/dist/core/phase-gate.js +39 -0
  254. package/dist/core/phase-gate.js.map +1 -0
  255. package/dist/core/report-html.d.ts +9 -0
  256. package/dist/core/report-html.d.ts.map +1 -0
  257. package/dist/core/report-html.js +617 -0
  258. package/dist/core/report-html.js.map +1 -0
  259. package/dist/core/report-upload.d.ts +16 -0
  260. package/dist/core/report-upload.d.ts.map +1 -0
  261. package/dist/core/report-upload.js +124 -0
  262. package/dist/core/report-upload.js.map +1 -0
  263. package/dist/core/run-counter.d.ts +40 -0
  264. package/dist/core/run-counter.d.ts.map +1 -0
  265. package/dist/core/run-counter.js +120 -0
  266. package/dist/core/run-counter.js.map +1 -0
  267. package/dist/core/types.d.ts +53 -0
  268. package/dist/core/types.d.ts.map +1 -0
  269. package/dist/core/types.js +2 -0
  270. package/dist/core/types.js.map +1 -0
  271. package/dist/helpers/api-client.d.ts +30 -0
  272. package/dist/helpers/api-client.d.ts.map +1 -0
  273. package/dist/helpers/api-client.js +77 -0
  274. package/dist/helpers/api-client.js.map +1 -0
  275. package/dist/helpers/element-discovery.d.ts +18 -0
  276. package/dist/helpers/element-discovery.d.ts.map +1 -0
  277. package/dist/helpers/element-discovery.js +82 -0
  278. package/dist/helpers/element-discovery.js.map +1 -0
  279. package/dist/helpers/env-resolver.d.ts +29 -0
  280. package/dist/helpers/env-resolver.d.ts.map +1 -0
  281. package/dist/helpers/env-resolver.js +51 -0
  282. package/dist/helpers/env-resolver.js.map +1 -0
  283. package/dist/helpers/form-filler.d.ts +13 -0
  284. package/dist/helpers/form-filler.d.ts.map +1 -0
  285. package/dist/helpers/form-filler.js +98 -0
  286. package/dist/helpers/form-filler.js.map +1 -0
  287. package/dist/helpers/modal-handler.d.ts +16 -0
  288. package/dist/helpers/modal-handler.d.ts.map +1 -0
  289. package/dist/helpers/modal-handler.js +95 -0
  290. package/dist/helpers/modal-handler.js.map +1 -0
  291. package/dist/helpers/navigation.d.ts +37 -0
  292. package/dist/helpers/navigation.d.ts.map +1 -0
  293. package/dist/helpers/navigation.js +83 -0
  294. package/dist/helpers/navigation.js.map +1 -0
  295. package/dist/helpers/quality-gate.d.ts +17 -0
  296. package/dist/helpers/quality-gate.d.ts.map +1 -0
  297. package/dist/helpers/quality-gate.js +144 -0
  298. package/dist/helpers/quality-gate.js.map +1 -0
  299. package/dist/helpers/screenshot.d.ts +24 -0
  300. package/dist/helpers/screenshot.d.ts.map +1 -0
  301. package/dist/helpers/screenshot.js +76 -0
  302. package/dist/helpers/screenshot.js.map +1 -0
  303. package/dist/helpers/seed-validator.d.ts +15 -0
  304. package/dist/helpers/seed-validator.d.ts.map +1 -0
  305. package/dist/helpers/seed-validator.js +53 -0
  306. package/dist/helpers/seed-validator.js.map +1 -0
  307. package/helpers/__tests__/api-client.test.ts +199 -0
  308. package/helpers/__tests__/element-discovery.test.ts +202 -0
  309. package/helpers/__tests__/form-filler-extended.test.ts +212 -0
  310. package/helpers/__tests__/form-filler.test.ts +99 -0
  311. package/helpers/__tests__/modal-handler.test.ts +152 -0
  312. package/helpers/__tests__/navigation.test.ts +214 -0
  313. package/helpers/__tests__/quality-gate.test.ts +117 -0
  314. package/helpers/__tests__/screenshot.test.ts +139 -0
  315. package/helpers/__tests__/seed-validator.test.ts +114 -0
  316. package/helpers/api-client.ts +111 -0
  317. package/helpers/element-discovery.ts +105 -0
  318. package/helpers/env-resolver.ts +69 -0
  319. package/helpers/form-filler.ts +126 -0
  320. package/helpers/modal-handler.ts +108 -0
  321. package/helpers/navigation.ts +100 -0
  322. package/helpers/quality-gate.ts +180 -0
  323. package/helpers/screenshot.ts +111 -0
  324. package/helpers/seed-validator.ts +70 -0
  325. package/package.json +88 -0
package/core/init.ts ADDED
@@ -0,0 +1,546 @@
1
+ import { createInterface, Interface } from 'node:readline';
2
+ import { readFileSync, mkdirSync, writeFileSync, existsSync } from 'node:fs';
3
+ import { basename, resolve, join } from 'node:path';
4
+
5
+ // ── Types ──────────────────────────────────────────────────────────────────
6
+
7
+ type Framework = 'react' | 'next' | 'vue' | 'angular' | 'nestjs' | 'express' | 'generic';
8
+ type TestRunner = 'vitest' | 'jest' | 'none';
9
+ type AuthMethod = 'form-login' | 'api-token' | 'none';
10
+
11
+ interface InitAnswers {
12
+ projectName: string;
13
+ framework: Framework;
14
+ testRunner: TestRunner;
15
+ alphaUrl: string;
16
+ betaUrl: string;
17
+ uatUrl: string;
18
+ authMethod: AuthMethod;
19
+ multiTenant: boolean;
20
+ }
21
+
22
+ // ── Cancellation sentinel ────────────────────────────────────────────────
23
+
24
+ class CancelledError extends Error {
25
+ constructor() {
26
+ super('Setup cancelled.');
27
+ this.name = 'CancelledError';
28
+ }
29
+ }
30
+
31
+ // ── Prompt helper ──────────────────────────────────────────────────────────
32
+
33
+ function prompt(
34
+ rl: Interface,
35
+ question: string,
36
+ defaultValue: string,
37
+ ): Promise<string> {
38
+ const suffix = defaultValue ? ` (${defaultValue})` : '';
39
+ return new Promise<string>((resolve, reject) => {
40
+ rl.question(`${question}${suffix}: `, (answer: string) => {
41
+ resolve(answer.trim() || defaultValue);
42
+ });
43
+ rl.once('close', () => reject(new CancelledError()));
44
+ });
45
+ }
46
+
47
+ function promptChoice<T extends string>(
48
+ rl: Interface,
49
+ question: string,
50
+ choices: readonly T[],
51
+ defaultValue: T,
52
+ ): Promise<T> {
53
+ const choiceStr = choices
54
+ .map((c) => (c === defaultValue ? `[${c}]` : c))
55
+ .join(' / ');
56
+ return new Promise<T>((resolve, reject) => {
57
+ rl.question(`${question} ${choiceStr}: `, (answer: string) => {
58
+ const trimmed = answer.trim().toLowerCase();
59
+ if (trimmed === '') {
60
+ resolve(defaultValue);
61
+ return;
62
+ }
63
+ const match = choices.find((c) => c === trimmed);
64
+ if (match) {
65
+ resolve(match);
66
+ return;
67
+ }
68
+ // Partial match — first choice that starts with the input
69
+ const partial = choices.find((c) => c.startsWith(trimmed));
70
+ resolve(partial ?? defaultValue);
71
+ });
72
+ rl.once('close', () => reject(new CancelledError()));
73
+ });
74
+ }
75
+
76
+ function promptYesNo(
77
+ rl: Interface,
78
+ question: string,
79
+ defaultValue: boolean,
80
+ ): Promise<boolean> {
81
+ const hint = defaultValue ? '(Y/n)' : '(y/N)';
82
+ return new Promise<boolean>((resolve, reject) => {
83
+ rl.question(`${question} ${hint}: `, (answer: string) => {
84
+ const trimmed = answer.trim().toLowerCase();
85
+ if (trimmed === '') {
86
+ resolve(defaultValue);
87
+ return;
88
+ }
89
+ resolve(trimmed === 'y' || trimmed === 'yes');
90
+ });
91
+ rl.once('close', () => reject(new CancelledError()));
92
+ });
93
+ }
94
+
95
+ // ── Validation helpers ───────────────────────────────────────────────────
96
+
97
+ function isValidUrl(value: string): boolean {
98
+ return /^https?:\/\/.+/.test(value);
99
+ }
100
+
101
+ // ── Detection helpers ──────────────────────────────────────────────────────
102
+
103
+ interface PackageJson {
104
+ name?: string;
105
+ dependencies?: Record<string, string>;
106
+ devDependencies?: Record<string, string>;
107
+ }
108
+
109
+ function readTargetPackageJson(cwd: string): PackageJson | null {
110
+ const pkgPath = join(cwd, 'package.json');
111
+ if (!existsSync(pkgPath)) {
112
+ return null;
113
+ }
114
+ try {
115
+ const raw = readFileSync(pkgPath, 'utf-8');
116
+ return JSON.parse(raw) as PackageJson;
117
+ } catch {
118
+ return null;
119
+ }
120
+ }
121
+
122
+ function detectFramework(pkg: PackageJson | null): Framework {
123
+ if (!pkg) return 'generic';
124
+ const all: Record<string, string> = {
125
+ ...pkg.dependencies,
126
+ ...pkg.devDependencies,
127
+ };
128
+ if ('next' in all) return 'next';
129
+ if ('@nestjs/core' in all) return 'nestjs';
130
+ if ('@angular/core' in all) return 'angular';
131
+ if ('vue' in all) return 'vue';
132
+ if ('express' in all) return 'express';
133
+ if ('react' in all) return 'react';
134
+ return 'generic';
135
+ }
136
+
137
+ function detectTestRunner(pkg: PackageJson | null): TestRunner {
138
+ if (!pkg) return 'none';
139
+ const all: Record<string, string> = {
140
+ ...pkg.dependencies,
141
+ ...pkg.devDependencies,
142
+ };
143
+ if ('vitest' in all) return 'vitest';
144
+ if ('jest' in all) return 'jest';
145
+ return 'none';
146
+ }
147
+
148
+ // ── Config generation ──────────────────────────────────────────────────────
149
+
150
+ function buildConfigSource(answers: InitAnswers): string {
151
+ const envBlock = buildEnvironmentsBlock(answers);
152
+ const authBlock = buildAuthBlock(answers);
153
+ const tenancyBlock = buildTenancyBlock(answers);
154
+
155
+ return `import type { TestTeamConfig } from './core/config';
156
+
157
+ const config: TestTeamConfig = {
158
+ schemaVersion: 1,
159
+ name: '${escapeString(answers.projectName)}',
160
+
161
+ ${authBlock},
162
+
163
+ modules: [
164
+ { id: 'home', route: '/', sidebarIcon: true },
165
+ // Add your app's modules here
166
+ ],
167
+
168
+ ${envBlock},
169
+
170
+ portals: [
171
+ { name: 'main', queryParam: null },
172
+ ],
173
+
174
+ breakpoints: [1920, 1366, 768, 375],
175
+ integrations: [],
176
+ workers: [],
177
+ workflows: [],
178
+
179
+ accuracy: {
180
+ enabled: false,
181
+ decimalPrecision: 2,
182
+ currencySymbol: 'USD',
183
+ timezone: 'UTC',
184
+ },
185
+
186
+ ${tenancyBlock},
187
+
188
+ costBudget: {
189
+ maxPerRun: 50,
190
+ maxPerAgent: 10,
191
+ alertThreshold: 0.8,
192
+ onExceeded: 'abort',
193
+ },
194
+
195
+ agentClassification: {
196
+ blocking: [1, 2, 3, 4, 5, 6, 7, 8, 11, 14, 19, 23],
197
+ advisory: [12, 13, 15, 16, 17, 20, 21, 22],
198
+ },
199
+
200
+ thresholds: {
201
+ unitCoverage: { lines: 80, functions: 80, branches: 70, statements: 80 },
202
+ lighthouse: { accessibility: 90, performance: 85, bestPractices: 85, seo: 80 },
203
+ apiResponseTime: 500,
204
+ bundleSizeLimit: 500,
205
+ maxConsoleErrors: 0,
206
+ },
207
+
208
+ disabledAgents: [],
209
+ };
210
+
211
+ export default config;
212
+ `;
213
+ }
214
+
215
+ function escapeString(value: string): string {
216
+ return value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
217
+ }
218
+
219
+ function buildEnvironmentsBlock(answers: InitAnswers): string {
220
+ const lines: string[] = [' environments: {'];
221
+ lines.push(` alpha: { baseUrl: '${escapeString(answers.alphaUrl)}', seed: true },`);
222
+ if (answers.betaUrl) {
223
+ lines.push(` beta: { baseUrl: '${escapeString(answers.betaUrl)}', seed: false },`);
224
+ }
225
+ if (answers.uatUrl) {
226
+ lines.push(` uat: { baseUrl: '${escapeString(answers.uatUrl)}', seed: false },`);
227
+ }
228
+ lines.push(' }');
229
+ return lines.join('\n');
230
+ }
231
+
232
+ function buildAuthBlock(answers: InitAnswers): string {
233
+ switch (answers.authMethod) {
234
+ case 'form-login':
235
+ return ` auth: {
236
+ loginUrl: '/login',
237
+ credentials: {
238
+ admin: {
239
+ email: process.env.TF_ADMIN_EMAIL ?? '',
240
+ password: process.env.TF_ADMIN_PASSWORD ?? '',
241
+ },
242
+ },
243
+ roles: [
244
+ { name: 'admin', email: process.env.TF_ROLE_ADMIN_EMAIL ?? '' },
245
+ ],
246
+ }`;
247
+
248
+ case 'api-token':
249
+ return ` auth: {
250
+ credentials: {
251
+ api: {
252
+ email: process.env.TF_API_EMAIL ?? '',
253
+ password: process.env.TF_API_TOKEN ?? '',
254
+ },
255
+ },
256
+ roles: [],
257
+ }`;
258
+
259
+ case 'none':
260
+ return ` auth: {
261
+ credentials: {},
262
+ roles: [],
263
+ }`;
264
+ }
265
+ }
266
+
267
+ function buildTenancyBlock(answers: InitAnswers): string {
268
+ if (answers.multiTenant) {
269
+ return ` tenancy: {
270
+ enabled: true,
271
+ isolationField: 'tenantId',
272
+ testFirms: ['firm-a', 'firm-b'],
273
+ }`;
274
+ }
275
+ return ` tenancy: {
276
+ enabled: false,
277
+ isolationField: 'tenantId',
278
+ testFirms: [],
279
+ }`;
280
+ }
281
+
282
+ // ── Baseline scaffolding ───────────────────────────────────────────────────
283
+
284
+ function scaffoldBaselines(cwd: string): void {
285
+ const baseDir = join(cwd, 'testteam');
286
+ const dirs = [
287
+ baseDir,
288
+ join(baseDir, 'baselines'),
289
+ join(baseDir, 'baselines', 'screenshots'),
290
+ join(baseDir, 'baselines', 'api-snapshots'),
291
+ join(baseDir, 'baselines', 'dom-snapshots'),
292
+ join(baseDir, 'reports'),
293
+ ];
294
+
295
+ for (const dir of dirs) {
296
+ mkdirSync(dir, { recursive: true });
297
+ }
298
+
299
+ // Create .gitkeep files so empty dirs are tracked
300
+ const gitkeeps = [
301
+ join(baseDir, 'baselines', 'screenshots', '.gitkeep'),
302
+ join(baseDir, 'baselines', 'api-snapshots', '.gitkeep'),
303
+ join(baseDir, 'baselines', 'dom-snapshots', '.gitkeep'),
304
+ join(baseDir, 'reports', '.gitkeep'),
305
+ ];
306
+
307
+ for (const filepath of gitkeeps) {
308
+ if (!existsSync(filepath)) {
309
+ writeFileSync(filepath, '', 'utf-8');
310
+ }
311
+ }
312
+
313
+ // Create a starter .gitignore for the testteam directory
314
+ const gitignorePath = join(baseDir, '.gitignore');
315
+ if (!existsSync(gitignorePath)) {
316
+ writeFileSync(
317
+ gitignorePath,
318
+ `# TestTeam runtime artifacts
319
+ reports/*.html
320
+ reports/*.json
321
+ !reports/.gitkeep
322
+ `,
323
+ 'utf-8',
324
+ );
325
+ }
326
+ }
327
+
328
+ // ── Summary display ─────────────────────────────────────────────────────
329
+
330
+ function printSummary(answers: InitAnswers): void {
331
+ console.log('\n ── Configuration Summary ──────────────────────');
332
+ console.log(` Project: ${answers.projectName}`);
333
+ console.log(` Framework: ${answers.framework}`);
334
+ console.log(` Test runner: ${answers.testRunner}`);
335
+ console.log(` Alpha URL: ${answers.alphaUrl}`);
336
+ if (answers.betaUrl) {
337
+ console.log(` Beta URL: ${answers.betaUrl}`);
338
+ }
339
+ if (answers.uatUrl) {
340
+ console.log(` UAT URL: ${answers.uatUrl}`);
341
+ }
342
+ console.log(` Auth method: ${answers.authMethod}`);
343
+ console.log(` Multi-tenant: ${answers.multiTenant ? 'yes' : 'no'}`);
344
+ console.log(' ────────────────────────────────────────────────\n');
345
+ }
346
+
347
+ // ── Main init wizard ───────────────────────────────────────────────────────
348
+
349
+ export async function runInitWizard(): Promise<number> {
350
+ const cwd = process.cwd();
351
+ const configTarget = join(cwd, 'testteam.config.ts');
352
+
353
+ console.log('\n TestTeam -- Init Wizard\n');
354
+
355
+ const rl = createInterface({
356
+ input: process.stdin,
357
+ output: process.stdout,
358
+ });
359
+
360
+ // ── SIGINT handling: gracefully exit on Ctrl+C ─────────────────────────
361
+ let cancelled = false;
362
+ const sigintHandler = (): void => {
363
+ cancelled = true;
364
+ console.log('\n\n Setup cancelled.');
365
+ rl.close();
366
+ process.exit(130);
367
+ };
368
+ process.on('SIGINT', sigintHandler);
369
+
370
+ const cleanup = (): void => {
371
+ process.removeListener('SIGINT', sigintHandler);
372
+ rl.close();
373
+ };
374
+
375
+ try {
376
+ // ── Existing config guard ──────────────────────────────────────────
377
+ if (existsSync(configTarget)) {
378
+ const overwrite = await promptYesNo(
379
+ rl,
380
+ 'Config already exists. Overwrite?',
381
+ false,
382
+ );
383
+ if (!overwrite) {
384
+ console.log(' Setup cancelled.');
385
+ cleanup();
386
+ return 1;
387
+ }
388
+ }
389
+
390
+ // ── Missing package.json warning ─────────────────────────────────
391
+ const pkg = readTargetPackageJson(cwd);
392
+ if (pkg === null) {
393
+ console.log(
394
+ ' Warning: No package.json found in current directory.\n' +
395
+ ' Framework and test runner detection will use defaults.\n',
396
+ );
397
+ }
398
+
399
+ const detectedFramework = detectFramework(pkg);
400
+ const detectedTestRunner = detectTestRunner(pkg);
401
+ const dirName = basename(resolve(cwd));
402
+
403
+ // ── Gather answers ───────────────────────────────────────────────
404
+ const projectName = await prompt(rl, 'Project name', pkg?.name ?? dirName);
405
+
406
+ const framework = await promptChoice<Framework>(
407
+ rl,
408
+ 'Framework?',
409
+ ['react', 'next', 'vue', 'angular', 'nestjs', 'express', 'generic'] as const,
410
+ detectedFramework,
411
+ );
412
+
413
+ const testRunner = await promptChoice<TestRunner>(
414
+ rl,
415
+ 'Test runner?',
416
+ ['vitest', 'jest', 'none'] as const,
417
+ detectedTestRunner,
418
+ );
419
+
420
+ // Alpha URL — required, with validation
421
+ let alphaUrl = '';
422
+ const defaultAlpha = 'http://localhost:3000';
423
+ while (!alphaUrl) {
424
+ const raw = await prompt(rl, 'Alpha environment URL', defaultAlpha);
425
+ if (isValidUrl(raw)) {
426
+ alphaUrl = raw;
427
+ } else {
428
+ console.log(' Invalid URL. Must start with http:// or https://');
429
+ }
430
+ }
431
+
432
+ // Beta URL — optional, validated when provided
433
+ let betaUrl = '';
434
+ const rawBeta = await prompt(rl, 'Beta/staging URL (leave blank to skip)', '');
435
+ if (rawBeta !== '') {
436
+ if (isValidUrl(rawBeta)) {
437
+ betaUrl = rawBeta;
438
+ } else {
439
+ console.log(' Invalid URL — skipping beta environment.');
440
+ }
441
+ }
442
+
443
+ // UAT URL — optional, validated when provided
444
+ let uatUrl = '';
445
+ const rawUat = await prompt(rl, 'UAT/production URL (leave blank to skip)', '');
446
+ if (rawUat !== '') {
447
+ if (isValidUrl(rawUat)) {
448
+ uatUrl = rawUat;
449
+ } else {
450
+ console.log(' Invalid URL — skipping UAT environment.');
451
+ }
452
+ }
453
+
454
+ const authMethod = await promptChoice<AuthMethod>(
455
+ rl,
456
+ 'Auth method?',
457
+ ['form-login', 'api-token', 'none'] as const,
458
+ 'form-login',
459
+ );
460
+
461
+ const multiTenant = await promptYesNo(rl, 'Multi-tenant?', false);
462
+
463
+ const answers: InitAnswers = {
464
+ projectName,
465
+ framework,
466
+ testRunner,
467
+ alphaUrl,
468
+ betaUrl,
469
+ uatUrl,
470
+ authMethod,
471
+ multiTenant,
472
+ };
473
+
474
+ // ── Confirmation prompt ──────────────────────────────────────────
475
+ printSummary(answers);
476
+
477
+ const confirmed = await promptYesNo(
478
+ rl,
479
+ 'Create config with these settings?',
480
+ true,
481
+ );
482
+
483
+ if (!confirmed) {
484
+ console.log(' Setup cancelled.');
485
+ cleanup();
486
+ return 1;
487
+ }
488
+
489
+ cleanup();
490
+
491
+ // ── Write config file ────────────────────────────────────────────
492
+ const configSource = buildConfigSource(answers);
493
+ writeFileSync(configTarget, configSource, 'utf-8');
494
+ console.log(`\n Created testteam.config.ts`);
495
+
496
+ // ── Scaffold baselines directory ─────────────────────────────────
497
+ scaffoldBaselines(cwd);
498
+ console.log(' Created testteam/ directory with starter baselines');
499
+
500
+ // ── Print next steps ─────────────────────────────────────────────
501
+ printNextSteps(answers);
502
+
503
+ return 0;
504
+ } catch (error) {
505
+ cleanup();
506
+
507
+ if (cancelled || error instanceof CancelledError) {
508
+ console.log('\n Setup cancelled.');
509
+ return 130;
510
+ }
511
+
512
+ console.error(
513
+ `Init failed: ${error instanceof Error ? error.message : String(error)}`,
514
+ );
515
+ return 1;
516
+ }
517
+ }
518
+
519
+ function printNextSteps(answers: InitAnswers): void {
520
+ console.log(`
521
+ Next steps
522
+ ----------
523
+
524
+ 1. Edit testteam.config.ts to add your app's modules and routes.
525
+
526
+ 2. Set environment variables for credentials:`);
527
+
528
+ if (answers.authMethod === 'form-login') {
529
+ console.log(` export TF_ADMIN_EMAIL="admin@example.com"
530
+ export TF_ADMIN_PASSWORD="your-password"`);
531
+ } else if (answers.authMethod === 'api-token') {
532
+ console.log(` export TF_API_EMAIL="api@example.com"
533
+ export TF_API_TOKEN="your-token"`);
534
+ }
535
+
536
+ console.log(`
537
+ 3. Run a dry-run to see the dispatch plan:
538
+ npx testteam --dry-run
539
+
540
+ 4. Run the alpha phase:
541
+ npx testteam --phase alpha
542
+
543
+ 5. Run all phases:
544
+ npx testteam --phase all
545
+ `);
546
+ }