bmad-studio 0.1.0 → 1.0.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 (1077) hide show
  1. package/README.md +6 -0
  2. package/package.json +27 -2
  3. package/packages/client/dist/assets/index-CWL4J-eZ.css +1 -0
  4. package/packages/client/dist/assets/index-DBqsFqD5.js +535 -0
  5. package/packages/client/dist/assets/inter-tight-cyrillic-400-normal-DPRou3KO.woff +0 -0
  6. package/packages/client/dist/assets/inter-tight-cyrillic-400-normal-G7BuwYWK.woff2 +0 -0
  7. package/packages/client/dist/assets/inter-tight-cyrillic-700-normal-DDZiLR3d.woff +0 -0
  8. package/packages/client/dist/assets/inter-tight-cyrillic-700-normal-DtWWjp29.woff2 +0 -0
  9. package/packages/client/dist/assets/inter-tight-cyrillic-800-normal-CC_RRmyd.woff2 +0 -0
  10. package/packages/client/dist/assets/inter-tight-cyrillic-800-normal-ChWbk4mt.woff +0 -0
  11. package/packages/client/dist/assets/inter-tight-cyrillic-ext-400-normal-CKO3cWyd.woff2 +0 -0
  12. package/packages/client/dist/assets/inter-tight-cyrillic-ext-400-normal-DiHHcP5k.woff +0 -0
  13. package/packages/client/dist/assets/inter-tight-cyrillic-ext-700-normal-BFcoPt71.woff +0 -0
  14. package/packages/client/dist/assets/inter-tight-cyrillic-ext-700-normal-D_7NT8eF.woff2 +0 -0
  15. package/packages/client/dist/assets/inter-tight-cyrillic-ext-800-normal-CzCOBUhR.woff2 +0 -0
  16. package/packages/client/dist/assets/inter-tight-cyrillic-ext-800-normal-WowoGRZX.woff +0 -0
  17. package/packages/client/dist/assets/inter-tight-greek-400-normal-BLLSFQTx.woff +0 -0
  18. package/packages/client/dist/assets/inter-tight-greek-400-normal-Br_GQm5W.woff2 +0 -0
  19. package/packages/client/dist/assets/inter-tight-greek-700-normal-DU8XoIeX.woff +0 -0
  20. package/packages/client/dist/assets/inter-tight-greek-700-normal-RgALSHS4.woff2 +0 -0
  21. package/packages/client/dist/assets/inter-tight-greek-800-normal-CxHmUlv4.woff +0 -0
  22. package/packages/client/dist/assets/inter-tight-greek-800-normal-X9eW6L67.woff2 +0 -0
  23. package/packages/client/dist/assets/inter-tight-greek-ext-400-normal-C3GMUg_S.woff2 +0 -0
  24. package/packages/client/dist/assets/inter-tight-greek-ext-400-normal-DMqR5RoS.woff +0 -0
  25. package/packages/client/dist/assets/inter-tight-greek-ext-700-normal-D0RkHJEE.woff2 +0 -0
  26. package/packages/client/dist/assets/inter-tight-greek-ext-700-normal-mW3dmpoD.woff +0 -0
  27. package/packages/client/dist/assets/inter-tight-greek-ext-800-normal-DNWjXg9N.woff2 +0 -0
  28. package/packages/client/dist/assets/inter-tight-greek-ext-800-normal-DfP-QoiQ.woff +0 -0
  29. package/packages/client/dist/assets/inter-tight-latin-400-normal-BLrFJfvD.woff +0 -0
  30. package/packages/client/dist/assets/inter-tight-latin-400-normal-iW8qmuJY.woff2 +0 -0
  31. package/packages/client/dist/assets/inter-tight-latin-700-normal-BZKd_v_8.woff2 +0 -0
  32. package/packages/client/dist/assets/inter-tight-latin-700-normal-DvYAVZQd.woff +0 -0
  33. package/packages/client/dist/assets/inter-tight-latin-800-normal-CRGCHRPv.woff2 +0 -0
  34. package/packages/client/dist/assets/inter-tight-latin-800-normal-D2te1T7i.woff +0 -0
  35. package/packages/client/dist/assets/inter-tight-latin-ext-400-normal-BQQeeQrv.woff +0 -0
  36. package/packages/client/dist/assets/inter-tight-latin-ext-400-normal-DN7wyBvd.woff2 +0 -0
  37. package/packages/client/dist/assets/inter-tight-latin-ext-700-normal-BpKPOkj3.woff2 +0 -0
  38. package/packages/client/dist/assets/inter-tight-latin-ext-700-normal-CrlzIQ10.woff +0 -0
  39. package/packages/client/dist/assets/inter-tight-latin-ext-800-normal-A39zhqaD.woff +0 -0
  40. package/packages/client/dist/assets/inter-tight-latin-ext-800-normal-DfKN99cy.woff2 +0 -0
  41. package/packages/client/dist/assets/inter-tight-vietnamese-400-normal-CqzIqjuX.woff2 +0 -0
  42. package/packages/client/dist/assets/inter-tight-vietnamese-400-normal-kSDs3Vd1.woff +0 -0
  43. package/packages/client/dist/assets/inter-tight-vietnamese-700-normal-Cn_DV52A.woff2 +0 -0
  44. package/packages/client/dist/assets/inter-tight-vietnamese-700-normal-DwHBAXeT.woff +0 -0
  45. package/packages/client/dist/assets/inter-tight-vietnamese-800-normal-CbYrLkxo.woff +0 -0
  46. package/packages/client/dist/assets/inter-tight-vietnamese-800-normal-OJoDhpMd.woff2 +0 -0
  47. package/packages/client/dist/index.html +13 -0
  48. package/packages/server/dist/app.d.ts +12 -0
  49. package/packages/server/dist/app.d.ts.map +1 -0
  50. package/packages/server/dist/app.js +94 -0
  51. package/packages/server/dist/app.js.map +1 -0
  52. package/packages/server/dist/app.test.d.ts +2 -0
  53. package/packages/server/dist/app.test.d.ts.map +1 -0
  54. package/packages/server/dist/app.test.js +72 -0
  55. package/packages/server/dist/app.test.js.map +1 -0
  56. package/packages/server/dist/core/errors.d.ts +29 -0
  57. package/packages/server/dist/core/errors.d.ts.map +1 -0
  58. package/packages/server/dist/core/errors.js +49 -0
  59. package/packages/server/dist/core/errors.js.map +1 -0
  60. package/packages/server/dist/core/errors.test.d.ts +2 -0
  61. package/packages/server/dist/core/errors.test.d.ts.map +1 -0
  62. package/packages/server/dist/core/errors.test.js +37 -0
  63. package/packages/server/dist/core/errors.test.js.map +1 -0
  64. package/packages/server/dist/core/file-store.d.ts +40 -0
  65. package/packages/server/dist/core/file-store.d.ts.map +1 -0
  66. package/packages/server/dist/core/file-store.js +160 -0
  67. package/packages/server/dist/core/file-store.js.map +1 -0
  68. package/packages/server/dist/core/file-store.test.d.ts +2 -0
  69. package/packages/server/dist/core/file-store.test.d.ts.map +1 -0
  70. package/packages/server/dist/core/file-store.test.js +75 -0
  71. package/packages/server/dist/core/file-store.test.js.map +1 -0
  72. package/packages/server/dist/core/ide-skill-generator.d.ts +58 -0
  73. package/packages/server/dist/core/ide-skill-generator.d.ts.map +1 -0
  74. package/packages/server/dist/core/ide-skill-generator.js +270 -0
  75. package/packages/server/dist/core/ide-skill-generator.js.map +1 -0
  76. package/packages/server/dist/core/ide-skill-generator.test.d.ts +2 -0
  77. package/packages/server/dist/core/ide-skill-generator.test.d.ts.map +1 -0
  78. package/packages/server/dist/core/ide-skill-generator.test.js +257 -0
  79. package/packages/server/dist/core/ide-skill-generator.test.js.map +1 -0
  80. package/packages/server/dist/core/module-installer.d.ts +165 -0
  81. package/packages/server/dist/core/module-installer.d.ts.map +1 -0
  82. package/packages/server/dist/core/module-installer.js +445 -0
  83. package/packages/server/dist/core/module-installer.js.map +1 -0
  84. package/packages/server/dist/core/module-installer.test.d.ts +2 -0
  85. package/packages/server/dist/core/module-installer.test.d.ts.map +1 -0
  86. package/packages/server/dist/core/module-installer.test.js +509 -0
  87. package/packages/server/dist/core/module-installer.test.js.map +1 -0
  88. package/packages/server/dist/core/module-registry.d.ts +5 -0
  89. package/packages/server/dist/core/module-registry.d.ts.map +1 -0
  90. package/packages/server/dist/core/module-registry.js +109 -0
  91. package/packages/server/dist/core/module-registry.js.map +1 -0
  92. package/packages/server/dist/core/module-registry.test.d.ts +2 -0
  93. package/packages/server/dist/core/module-registry.test.d.ts.map +1 -0
  94. package/packages/server/dist/core/module-registry.test.js +280 -0
  95. package/packages/server/dist/core/module-registry.test.js.map +1 -0
  96. package/packages/server/dist/core/project-detector.d.ts +14 -0
  97. package/packages/server/dist/core/project-detector.d.ts.map +1 -0
  98. package/packages/server/dist/core/project-detector.js +67 -0
  99. package/packages/server/dist/core/project-detector.js.map +1 -0
  100. package/packages/server/dist/core/project-detector.test.d.ts +2 -0
  101. package/packages/server/dist/core/project-detector.test.d.ts.map +1 -0
  102. package/packages/server/dist/core/project-detector.test.js +73 -0
  103. package/packages/server/dist/core/project-detector.test.js.map +1 -0
  104. package/packages/server/dist/core/websocket.d.ts +16 -0
  105. package/packages/server/dist/core/websocket.d.ts.map +1 -0
  106. package/packages/server/dist/core/websocket.js +30 -0
  107. package/packages/server/dist/core/websocket.js.map +1 -0
  108. package/packages/server/dist/core/websocket.test.d.ts +2 -0
  109. package/packages/server/dist/core/websocket.test.d.ts.map +1 -0
  110. package/packages/server/dist/core/websocket.test.js +64 -0
  111. package/packages/server/dist/core/websocket.test.js.map +1 -0
  112. package/packages/server/dist/core/write-service.d.ts +32 -0
  113. package/packages/server/dist/core/write-service.d.ts.map +1 -0
  114. package/packages/server/dist/core/write-service.js +173 -0
  115. package/packages/server/dist/core/write-service.js.map +1 -0
  116. package/packages/server/dist/core/write-service.test.d.ts +2 -0
  117. package/packages/server/dist/core/write-service.test.d.ts.map +1 -0
  118. package/packages/server/dist/core/write-service.test.js +156 -0
  119. package/packages/server/dist/core/write-service.test.js.map +1 -0
  120. package/packages/server/dist/index.d.ts +2 -0
  121. package/packages/server/dist/index.d.ts.map +1 -0
  122. package/packages/server/dist/index.js +133 -0
  123. package/packages/server/dist/index.js.map +1 -0
  124. package/packages/server/dist/parsers/agent-parser.d.ts +4 -0
  125. package/packages/server/dist/parsers/agent-parser.d.ts.map +1 -0
  126. package/packages/server/dist/parsers/agent-parser.js +88 -0
  127. package/packages/server/dist/parsers/agent-parser.js.map +1 -0
  128. package/packages/server/dist/parsers/agent-parser.test.d.ts +2 -0
  129. package/packages/server/dist/parsers/agent-parser.test.d.ts.map +1 -0
  130. package/packages/server/dist/parsers/agent-parser.test.js +66 -0
  131. package/packages/server/dist/parsers/agent-parser.test.js.map +1 -0
  132. package/packages/server/dist/parsers/config-parser.d.ts +14 -0
  133. package/packages/server/dist/parsers/config-parser.d.ts.map +1 -0
  134. package/packages/server/dist/parsers/config-parser.js +39 -0
  135. package/packages/server/dist/parsers/config-parser.js.map +1 -0
  136. package/packages/server/dist/parsers/config-parser.test.d.ts +2 -0
  137. package/packages/server/dist/parsers/config-parser.test.d.ts.map +1 -0
  138. package/packages/server/dist/parsers/config-parser.test.js +56 -0
  139. package/packages/server/dist/parsers/config-parser.test.js.map +1 -0
  140. package/packages/server/dist/parsers/csv-parser.d.ts +4 -0
  141. package/packages/server/dist/parsers/csv-parser.d.ts.map +1 -0
  142. package/packages/server/dist/parsers/csv-parser.js +50 -0
  143. package/packages/server/dist/parsers/csv-parser.js.map +1 -0
  144. package/packages/server/dist/parsers/csv-parser.test.d.ts +2 -0
  145. package/packages/server/dist/parsers/csv-parser.test.d.ts.map +1 -0
  146. package/packages/server/dist/parsers/csv-parser.test.js +44 -0
  147. package/packages/server/dist/parsers/csv-parser.test.js.map +1 -0
  148. package/packages/server/dist/parsers/ide-config-parser.d.ts +9 -0
  149. package/packages/server/dist/parsers/ide-config-parser.d.ts.map +1 -0
  150. package/packages/server/dist/parsers/ide-config-parser.js +28 -0
  151. package/packages/server/dist/parsers/ide-config-parser.js.map +1 -0
  152. package/packages/server/dist/parsers/ide-config-parser.test.d.ts +2 -0
  153. package/packages/server/dist/parsers/ide-config-parser.test.d.ts.map +1 -0
  154. package/packages/server/dist/parsers/ide-config-parser.test.js +32 -0
  155. package/packages/server/dist/parsers/ide-config-parser.test.js.map +1 -0
  156. package/packages/server/dist/parsers/index-builder.d.ts +20 -0
  157. package/packages/server/dist/parsers/index-builder.d.ts.map +1 -0
  158. package/packages/server/dist/parsers/index-builder.js +155 -0
  159. package/packages/server/dist/parsers/index-builder.js.map +1 -0
  160. package/packages/server/dist/parsers/index-builder.test.d.ts +2 -0
  161. package/packages/server/dist/parsers/index-builder.test.d.ts.map +1 -0
  162. package/packages/server/dist/parsers/index-builder.test.js +57 -0
  163. package/packages/server/dist/parsers/index-builder.test.js.map +1 -0
  164. package/packages/server/dist/parsers/module-yaml-parser.d.ts +16 -0
  165. package/packages/server/dist/parsers/module-yaml-parser.d.ts.map +1 -0
  166. package/packages/server/dist/parsers/module-yaml-parser.js +62 -0
  167. package/packages/server/dist/parsers/module-yaml-parser.js.map +1 -0
  168. package/packages/server/dist/parsers/module-yaml-parser.test.d.ts +2 -0
  169. package/packages/server/dist/parsers/module-yaml-parser.test.d.ts.map +1 -0
  170. package/packages/server/dist/parsers/module-yaml-parser.test.js +156 -0
  171. package/packages/server/dist/parsers/module-yaml-parser.test.js.map +1 -0
  172. package/packages/server/dist/parsers/package-parser.d.ts +4 -0
  173. package/packages/server/dist/parsers/package-parser.d.ts.map +1 -0
  174. package/packages/server/dist/parsers/package-parser.js +29 -0
  175. package/packages/server/dist/parsers/package-parser.js.map +1 -0
  176. package/packages/server/dist/parsers/package-parser.test.d.ts +2 -0
  177. package/packages/server/dist/parsers/package-parser.test.d.ts.map +1 -0
  178. package/packages/server/dist/parsers/package-parser.test.js +34 -0
  179. package/packages/server/dist/parsers/package-parser.test.js.map +1 -0
  180. package/packages/server/dist/parsers/skill-parser.d.ts +4 -0
  181. package/packages/server/dist/parsers/skill-parser.d.ts.map +1 -0
  182. package/packages/server/dist/parsers/skill-parser.js +58 -0
  183. package/packages/server/dist/parsers/skill-parser.js.map +1 -0
  184. package/packages/server/dist/parsers/skill-parser.test.d.ts +2 -0
  185. package/packages/server/dist/parsers/skill-parser.test.d.ts.map +1 -0
  186. package/packages/server/dist/parsers/skill-parser.test.js +49 -0
  187. package/packages/server/dist/parsers/skill-parser.test.js.map +1 -0
  188. package/packages/server/dist/parsers/team-parser.d.ts +4 -0
  189. package/packages/server/dist/parsers/team-parser.d.ts.map +1 -0
  190. package/packages/server/dist/parsers/team-parser.js +108 -0
  191. package/packages/server/dist/parsers/team-parser.js.map +1 -0
  192. package/packages/server/dist/parsers/team-parser.test.d.ts +2 -0
  193. package/packages/server/dist/parsers/team-parser.test.d.ts.map +1 -0
  194. package/packages/server/dist/parsers/team-parser.test.js +142 -0
  195. package/packages/server/dist/parsers/team-parser.test.js.map +1 -0
  196. package/packages/server/dist/parsers/workflow-parser.d.ts +4 -0
  197. package/packages/server/dist/parsers/workflow-parser.d.ts.map +1 -0
  198. package/packages/server/dist/parsers/workflow-parser.js +266 -0
  199. package/packages/server/dist/parsers/workflow-parser.js.map +1 -0
  200. package/packages/server/dist/parsers/workflow-parser.test.d.ts +2 -0
  201. package/packages/server/dist/parsers/workflow-parser.test.d.ts.map +1 -0
  202. package/packages/server/dist/parsers/workflow-parser.test.js +318 -0
  203. package/packages/server/dist/parsers/workflow-parser.test.js.map +1 -0
  204. package/packages/server/dist/plugins/agents-plugin.d.ts +3 -0
  205. package/packages/server/dist/plugins/agents-plugin.d.ts.map +1 -0
  206. package/packages/server/dist/plugins/agents-plugin.js +129 -0
  207. package/packages/server/dist/plugins/agents-plugin.js.map +1 -0
  208. package/packages/server/dist/plugins/agents-plugin.test.d.ts +2 -0
  209. package/packages/server/dist/plugins/agents-plugin.test.d.ts.map +1 -0
  210. package/packages/server/dist/plugins/agents-plugin.test.js +94 -0
  211. package/packages/server/dist/plugins/agents-plugin.test.js.map +1 -0
  212. package/packages/server/dist/plugins/commands-plugin.d.ts +16 -0
  213. package/packages/server/dist/plugins/commands-plugin.d.ts.map +1 -0
  214. package/packages/server/dist/plugins/commands-plugin.js +38 -0
  215. package/packages/server/dist/plugins/commands-plugin.js.map +1 -0
  216. package/packages/server/dist/plugins/datasources-plugin.d.ts +3 -0
  217. package/packages/server/dist/plugins/datasources-plugin.d.ts.map +1 -0
  218. package/packages/server/dist/plugins/datasources-plugin.js +211 -0
  219. package/packages/server/dist/plugins/datasources-plugin.js.map +1 -0
  220. package/packages/server/dist/plugins/files-plugin.d.ts +3 -0
  221. package/packages/server/dist/plugins/files-plugin.d.ts.map +1 -0
  222. package/packages/server/dist/plugins/files-plugin.js +77 -0
  223. package/packages/server/dist/plugins/files-plugin.js.map +1 -0
  224. package/packages/server/dist/plugins/files-plugin.test.d.ts +2 -0
  225. package/packages/server/dist/plugins/files-plugin.test.d.ts.map +1 -0
  226. package/packages/server/dist/plugins/files-plugin.test.js +107 -0
  227. package/packages/server/dist/plugins/files-plugin.test.js.map +1 -0
  228. package/packages/server/dist/plugins/modules-plugin.d.ts +3 -0
  229. package/packages/server/dist/plugins/modules-plugin.d.ts.map +1 -0
  230. package/packages/server/dist/plugins/modules-plugin.js +1313 -0
  231. package/packages/server/dist/plugins/modules-plugin.js.map +1 -0
  232. package/packages/server/dist/plugins/modules-plugin.test.d.ts +2 -0
  233. package/packages/server/dist/plugins/modules-plugin.test.d.ts.map +1 -0
  234. package/packages/server/dist/plugins/modules-plugin.test.js +2124 -0
  235. package/packages/server/dist/plugins/modules-plugin.test.js.map +1 -0
  236. package/packages/server/dist/plugins/outputs-plugin.d.ts +3 -0
  237. package/packages/server/dist/plugins/outputs-plugin.d.ts.map +1 -0
  238. package/packages/server/dist/plugins/outputs-plugin.js +75 -0
  239. package/packages/server/dist/plugins/outputs-plugin.js.map +1 -0
  240. package/packages/server/dist/plugins/outputs-plugin.test.d.ts +2 -0
  241. package/packages/server/dist/plugins/outputs-plugin.test.d.ts.map +1 -0
  242. package/packages/server/dist/plugins/outputs-plugin.test.js +79 -0
  243. package/packages/server/dist/plugins/outputs-plugin.test.js.map +1 -0
  244. package/packages/server/dist/plugins/overview-plugin.d.ts +3 -0
  245. package/packages/server/dist/plugins/overview-plugin.d.ts.map +1 -0
  246. package/packages/server/dist/plugins/overview-plugin.js +70 -0
  247. package/packages/server/dist/plugins/overview-plugin.js.map +1 -0
  248. package/packages/server/dist/plugins/search-plugin.d.ts +3 -0
  249. package/packages/server/dist/plugins/search-plugin.d.ts.map +1 -0
  250. package/packages/server/dist/plugins/search-plugin.js +46 -0
  251. package/packages/server/dist/plugins/search-plugin.js.map +1 -0
  252. package/packages/server/dist/plugins/settings-plugin.d.ts +3 -0
  253. package/packages/server/dist/plugins/settings-plugin.d.ts.map +1 -0
  254. package/packages/server/dist/plugins/settings-plugin.js +61 -0
  255. package/packages/server/dist/plugins/settings-plugin.js.map +1 -0
  256. package/packages/server/dist/plugins/settings-plugin.test.d.ts +2 -0
  257. package/packages/server/dist/plugins/settings-plugin.test.d.ts.map +1 -0
  258. package/packages/server/dist/plugins/settings-plugin.test.js +144 -0
  259. package/packages/server/dist/plugins/settings-plugin.test.js.map +1 -0
  260. package/packages/server/dist/plugins/skills-plugin.d.ts +3 -0
  261. package/packages/server/dist/plugins/skills-plugin.d.ts.map +1 -0
  262. package/packages/server/dist/plugins/skills-plugin.js +72 -0
  263. package/packages/server/dist/plugins/skills-plugin.js.map +1 -0
  264. package/packages/server/dist/plugins/teams-plugin.d.ts +3 -0
  265. package/packages/server/dist/plugins/teams-plugin.d.ts.map +1 -0
  266. package/packages/server/dist/plugins/teams-plugin.js +162 -0
  267. package/packages/server/dist/plugins/teams-plugin.js.map +1 -0
  268. package/packages/server/dist/plugins/teams-plugin.test.d.ts +2 -0
  269. package/packages/server/dist/plugins/teams-plugin.test.d.ts.map +1 -0
  270. package/packages/server/dist/plugins/teams-plugin.test.js +171 -0
  271. package/packages/server/dist/plugins/teams-plugin.test.js.map +1 -0
  272. package/packages/server/dist/plugins/validation-plugin.d.ts +3 -0
  273. package/packages/server/dist/plugins/validation-plugin.d.ts.map +1 -0
  274. package/packages/server/dist/plugins/validation-plugin.js +102 -0
  275. package/packages/server/dist/plugins/validation-plugin.js.map +1 -0
  276. package/packages/server/dist/plugins/validation-plugin.test.d.ts +2 -0
  277. package/packages/server/dist/plugins/validation-plugin.test.d.ts.map +1 -0
  278. package/packages/server/dist/plugins/validation-plugin.test.js +161 -0
  279. package/packages/server/dist/plugins/validation-plugin.test.js.map +1 -0
  280. package/packages/server/dist/plugins/workflows-plugin.d.ts +3 -0
  281. package/packages/server/dist/plugins/workflows-plugin.d.ts.map +1 -0
  282. package/packages/server/dist/plugins/workflows-plugin.js +227 -0
  283. package/packages/server/dist/plugins/workflows-plugin.js.map +1 -0
  284. package/packages/server/dist/plugins/workflows-plugin.test.d.ts +2 -0
  285. package/packages/server/dist/plugins/workflows-plugin.test.d.ts.map +1 -0
  286. package/packages/server/dist/plugins/workflows-plugin.test.js +115 -0
  287. package/packages/server/dist/plugins/workflows-plugin.test.js.map +1 -0
  288. package/packages/server/dist/static.d.ts +3 -0
  289. package/packages/server/dist/static.d.ts.map +1 -0
  290. package/packages/server/dist/static.js +23 -0
  291. package/packages/server/dist/static.js.map +1 -0
  292. package/packages/shared/src/agents.ts +4 -0
  293. package/packages/shared/src/config.ts +26 -0
  294. package/packages/shared/src/connections.ts +11 -8
  295. package/packages/shared/src/index.ts +13 -0
  296. package/packages/shared/src/modules.ts +42 -0
  297. package/packages/shared/src/registry.ts +26 -0
  298. package/packages/shared/src/types.test.ts +41 -4
  299. package/.prettierrc +0 -8
  300. package/CONTRIBUTING.md +0 -109
  301. package/_bmad/_config/agent-manifest.csv +0 -11
  302. package/_bmad/_config/bmad-help.csv +0 -57
  303. package/_bmad/_config/files-manifest.csv +0 -646
  304. package/_bmad/_config/ides/claude-code.yaml +0 -5
  305. package/_bmad/_config/manifest.yaml +0 -42
  306. package/_bmad/_config/skill-manifest.csv +0 -48
  307. package/_bmad/_config/task-manifest.csv +0 -1
  308. package/_bmad/_config/tool-manifest.csv +0 -1
  309. package/_bmad/_config/workflow-manifest.csv +0 -1
  310. package/_bmad/bmb/config.yaml +0 -13
  311. package/_bmad/bmb/module-help.csv +0 -7
  312. package/_bmad/bmb/skills/bmad-agent-builder/SKILL.md +0 -65
  313. package/_bmad/bmb/skills/bmad-agent-builder/assets/SKILL-template.md +0 -97
  314. package/_bmad/bmb/skills/bmad-agent-builder/assets/autonomous-wake.md +0 -37
  315. package/_bmad/bmb/skills/bmad-agent-builder/assets/init-template.md +0 -47
  316. package/_bmad/bmb/skills/bmad-agent-builder/assets/memory-system.md +0 -129
  317. package/_bmad/bmb/skills/bmad-agent-builder/assets/quality-report-template.md +0 -282
  318. package/_bmad/bmb/skills/bmad-agent-builder/assets/save-memory.md +0 -29
  319. package/_bmad/bmb/skills/bmad-agent-builder/bmad-manifest.json +0 -24
  320. package/_bmad/bmb/skills/bmad-agent-builder/bmad-skill-manifest.yaml +0 -1
  321. package/_bmad/bmb/skills/bmad-agent-builder/build-process.md +0 -199
  322. package/_bmad/bmb/skills/bmad-agent-builder/quality-optimizer.md +0 -208
  323. package/_bmad/bmb/skills/bmad-agent-builder/quality-scan-agent-cohesion.md +0 -272
  324. package/_bmad/bmb/skills/bmad-agent-builder/quality-scan-enhancement-opportunities.md +0 -277
  325. package/_bmad/bmb/skills/bmad-agent-builder/quality-scan-execution-efficiency.md +0 -181
  326. package/_bmad/bmb/skills/bmad-agent-builder/quality-scan-prompt-craft.md +0 -245
  327. package/_bmad/bmb/skills/bmad-agent-builder/quality-scan-script-opportunities.md +0 -262
  328. package/_bmad/bmb/skills/bmad-agent-builder/quality-scan-structure.md +0 -183
  329. package/_bmad/bmb/skills/bmad-agent-builder/references/metadata-reference.md +0 -126
  330. package/_bmad/bmb/skills/bmad-agent-builder/references/quality-dimensions.md +0 -46
  331. package/_bmad/bmb/skills/bmad-agent-builder/references/script-opportunities-reference.md +0 -385
  332. package/_bmad/bmb/skills/bmad-agent-builder/references/skill-best-practices.md +0 -218
  333. package/_bmad/bmb/skills/bmad-agent-builder/references/standard-fields.md +0 -103
  334. package/_bmad/bmb/skills/bmad-agent-builder/references/template-substitution-rules.md +0 -72
  335. package/_bmad/bmb/skills/bmad-agent-builder/references/universal-scan-schema.md +0 -267
  336. package/_bmad/bmb/skills/bmad-agent-builder/report-quality-scan-creator.md +0 -138
  337. package/_bmad/bmb/skills/bmad-agent-builder/scripts/bmad-manifest-schema.json +0 -103
  338. package/_bmad/bmb/skills/bmad-agent-builder/scripts/generate-html-report.py +0 -1002
  339. package/_bmad/bmb/skills/bmad-agent-builder/scripts/manifest.py +0 -420
  340. package/_bmad/bmb/skills/bmad-agent-builder/scripts/prepass-execution-deps.py +0 -368
  341. package/_bmad/bmb/skills/bmad-agent-builder/scripts/prepass-prompt-metrics.py +0 -476
  342. package/_bmad/bmb/skills/bmad-agent-builder/scripts/prepass-structure-capabilities.py +0 -636
  343. package/_bmad/bmb/skills/bmad-agent-builder/scripts/scan-path-standards.py +0 -253
  344. package/_bmad/bmb/skills/bmad-agent-builder/scripts/scan-scripts.py +0 -745
  345. package/_bmad/bmb/skills/bmad-workflow-builder/SKILL.md +0 -65
  346. package/_bmad/bmb/skills/bmad-workflow-builder/assets/SKILL-template.md +0 -117
  347. package/_bmad/bmb/skills/bmad-workflow-builder/assets/quality-report-template.md +0 -260
  348. package/_bmad/bmb/skills/bmad-workflow-builder/bmad-manifest.json +0 -23
  349. package/_bmad/bmb/skills/bmad-workflow-builder/bmad-skill-manifest.yaml +0 -1
  350. package/_bmad/bmb/skills/bmad-workflow-builder/build-process.md +0 -208
  351. package/_bmad/bmb/skills/bmad-workflow-builder/quality-optimizer.md +0 -209
  352. package/_bmad/bmb/skills/bmad-workflow-builder/quality-scan-enhancement-opportunities.md +0 -273
  353. package/_bmad/bmb/skills/bmad-workflow-builder/quality-scan-execution-efficiency.md +0 -322
  354. package/_bmad/bmb/skills/bmad-workflow-builder/quality-scan-prompt-craft.md +0 -328
  355. package/_bmad/bmb/skills/bmad-workflow-builder/quality-scan-script-opportunities.md +0 -261
  356. package/_bmad/bmb/skills/bmad-workflow-builder/quality-scan-skill-cohesion.md +0 -340
  357. package/_bmad/bmb/skills/bmad-workflow-builder/quality-scan-workflow-integrity.md +0 -280
  358. package/_bmad/bmb/skills/bmad-workflow-builder/references/classification-reference.md +0 -61
  359. package/_bmad/bmb/skills/bmad-workflow-builder/references/complex-workflow-patterns.md +0 -523
  360. package/_bmad/bmb/skills/bmad-workflow-builder/references/metadata-reference.md +0 -126
  361. package/_bmad/bmb/skills/bmad-workflow-builder/references/quality-dimensions.md +0 -45
  362. package/_bmad/bmb/skills/bmad-workflow-builder/references/script-opportunities-reference.md +0 -354
  363. package/_bmad/bmb/skills/bmad-workflow-builder/references/skill-best-practices.md +0 -218
  364. package/_bmad/bmb/skills/bmad-workflow-builder/references/standard-fields.md +0 -121
  365. package/_bmad/bmb/skills/bmad-workflow-builder/references/template-substitution-rules.md +0 -85
  366. package/_bmad/bmb/skills/bmad-workflow-builder/references/universal-scan-schema.md +0 -267
  367. package/_bmad/bmb/skills/bmad-workflow-builder/report-quality-scan-creator.md +0 -134
  368. package/_bmad/bmb/skills/bmad-workflow-builder/scripts/bmad-manifest-schema.json +0 -103
  369. package/_bmad/bmb/skills/bmad-workflow-builder/scripts/generate-html-report.py +0 -1002
  370. package/_bmad/bmb/skills/bmad-workflow-builder/scripts/manifest.py +0 -420
  371. package/_bmad/bmb/skills/bmad-workflow-builder/scripts/prepass-execution-deps.py +0 -313
  372. package/_bmad/bmb/skills/bmad-workflow-builder/scripts/prepass-prompt-metrics.py +0 -285
  373. package/_bmad/bmb/skills/bmad-workflow-builder/scripts/prepass-workflow-integrity.py +0 -485
  374. package/_bmad/bmb/skills/bmad-workflow-builder/scripts/scan-path-standards.py +0 -213
  375. package/_bmad/bmb/skills/bmad-workflow-builder/scripts/scan-scripts.py +0 -745
  376. package/_bmad/bmm/agents/analyst.md +0 -69
  377. package/_bmad/bmm/agents/architect.md +0 -59
  378. package/_bmad/bmm/agents/bmad-skill-manifest.yaml +0 -39
  379. package/_bmad/bmm/agents/dev.md +0 -66
  380. package/_bmad/bmm/agents/pm.md +0 -63
  381. package/_bmad/bmm/agents/qa.md +0 -89
  382. package/_bmad/bmm/agents/quick-flow-solo-dev.md +0 -61
  383. package/_bmad/bmm/agents/sm.md +0 -67
  384. package/_bmad/bmm/agents/tech-writer/bmad-skill-manifest.yaml +0 -3
  385. package/_bmad/bmm/agents/tech-writer/tech-writer.md +0 -67
  386. package/_bmad/bmm/agents/ux-designer.md +0 -58
  387. package/_bmad/bmm/config.yaml +0 -16
  388. package/_bmad/bmm/data/project-context-template.md +0 -26
  389. package/_bmad/bmm/module-help.csv +0 -32
  390. package/_bmad/bmm/teams/default-party.csv +0 -20
  391. package/_bmad/bmm/teams/team-fullstack.yaml +0 -12
  392. package/_bmad/bmm/workflows/1-analysis/bmad-create-product-brief/SKILL.md +0 -6
  393. package/_bmad/bmm/workflows/1-analysis/bmad-create-product-brief/bmad-skill-manifest.yaml +0 -1
  394. package/_bmad/bmm/workflows/1-analysis/bmad-create-product-brief/product-brief.template.md +0 -10
  395. package/_bmad/bmm/workflows/1-analysis/bmad-create-product-brief/steps/step-01-init.md +0 -170
  396. package/_bmad/bmm/workflows/1-analysis/bmad-create-product-brief/steps/step-01b-continue.md +0 -158
  397. package/_bmad/bmm/workflows/1-analysis/bmad-create-product-brief/steps/step-02-vision.md +0 -193
  398. package/_bmad/bmm/workflows/1-analysis/bmad-create-product-brief/steps/step-03-users.md +0 -196
  399. package/_bmad/bmm/workflows/1-analysis/bmad-create-product-brief/steps/step-04-metrics.md +0 -199
  400. package/_bmad/bmm/workflows/1-analysis/bmad-create-product-brief/steps/step-05-scope.md +0 -213
  401. package/_bmad/bmm/workflows/1-analysis/bmad-create-product-brief/steps/step-06-complete.md +0 -159
  402. package/_bmad/bmm/workflows/1-analysis/bmad-create-product-brief/workflow.md +0 -55
  403. package/_bmad/bmm/workflows/1-analysis/bmad-product-brief-preview/SKILL.md +0 -88
  404. package/_bmad/bmm/workflows/1-analysis/bmad-product-brief-preview/agents/artifact-analyzer.md +0 -60
  405. package/_bmad/bmm/workflows/1-analysis/bmad-product-brief-preview/agents/opportunity-reviewer.md +0 -44
  406. package/_bmad/bmm/workflows/1-analysis/bmad-product-brief-preview/agents/skeptic-reviewer.md +0 -44
  407. package/_bmad/bmm/workflows/1-analysis/bmad-product-brief-preview/agents/web-researcher.md +0 -49
  408. package/_bmad/bmm/workflows/1-analysis/bmad-product-brief-preview/bmad-manifest.json +0 -17
  409. package/_bmad/bmm/workflows/1-analysis/bmad-product-brief-preview/bmad-skill-manifest.yaml +0 -1
  410. package/_bmad/bmm/workflows/1-analysis/bmad-product-brief-preview/prompts/contextual-discovery.md +0 -57
  411. package/_bmad/bmm/workflows/1-analysis/bmad-product-brief-preview/prompts/draft-and-review.md +0 -86
  412. package/_bmad/bmm/workflows/1-analysis/bmad-product-brief-preview/prompts/finalize.md +0 -75
  413. package/_bmad/bmm/workflows/1-analysis/bmad-product-brief-preview/prompts/guided-elicitation.md +0 -70
  414. package/_bmad/bmm/workflows/1-analysis/bmad-product-brief-preview/resources/brief-template.md +0 -60
  415. package/_bmad/bmm/workflows/1-analysis/research/bmad-domain-research/SKILL.md +0 -6
  416. package/_bmad/bmm/workflows/1-analysis/research/bmad-domain-research/bmad-skill-manifest.yaml +0 -1
  417. package/_bmad/bmm/workflows/1-analysis/research/bmad-domain-research/domain-steps/step-01-init.md +0 -137
  418. package/_bmad/bmm/workflows/1-analysis/research/bmad-domain-research/domain-steps/step-02-domain-analysis.md +0 -229
  419. package/_bmad/bmm/workflows/1-analysis/research/bmad-domain-research/domain-steps/step-03-competitive-landscape.md +0 -238
  420. package/_bmad/bmm/workflows/1-analysis/research/bmad-domain-research/domain-steps/step-04-regulatory-focus.md +0 -206
  421. package/_bmad/bmm/workflows/1-analysis/research/bmad-domain-research/domain-steps/step-05-technical-trends.md +0 -234
  422. package/_bmad/bmm/workflows/1-analysis/research/bmad-domain-research/domain-steps/step-06-research-synthesis.md +0 -444
  423. package/_bmad/bmm/workflows/1-analysis/research/bmad-domain-research/research.template.md +0 -29
  424. package/_bmad/bmm/workflows/1-analysis/research/bmad-domain-research/workflow.md +0 -49
  425. package/_bmad/bmm/workflows/1-analysis/research/bmad-market-research/SKILL.md +0 -6
  426. package/_bmad/bmm/workflows/1-analysis/research/bmad-market-research/bmad-skill-manifest.yaml +0 -1
  427. package/_bmad/bmm/workflows/1-analysis/research/bmad-market-research/research.template.md +0 -29
  428. package/_bmad/bmm/workflows/1-analysis/research/bmad-market-research/steps/step-01-init.md +0 -184
  429. package/_bmad/bmm/workflows/1-analysis/research/bmad-market-research/steps/step-02-customer-behavior.md +0 -239
  430. package/_bmad/bmm/workflows/1-analysis/research/bmad-market-research/steps/step-03-customer-pain-points.md +0 -251
  431. package/_bmad/bmm/workflows/1-analysis/research/bmad-market-research/steps/step-04-customer-decisions.md +0 -261
  432. package/_bmad/bmm/workflows/1-analysis/research/bmad-market-research/steps/step-05-competitive-analysis.md +0 -173
  433. package/_bmad/bmm/workflows/1-analysis/research/bmad-market-research/steps/step-06-research-completion.md +0 -478
  434. package/_bmad/bmm/workflows/1-analysis/research/bmad-market-research/workflow.md +0 -49
  435. package/_bmad/bmm/workflows/1-analysis/research/bmad-technical-research/SKILL.md +0 -6
  436. package/_bmad/bmm/workflows/1-analysis/research/bmad-technical-research/bmad-skill-manifest.yaml +0 -1
  437. package/_bmad/bmm/workflows/1-analysis/research/bmad-technical-research/research.template.md +0 -29
  438. package/_bmad/bmm/workflows/1-analysis/research/bmad-technical-research/technical-steps/step-01-init.md +0 -137
  439. package/_bmad/bmm/workflows/1-analysis/research/bmad-technical-research/technical-steps/step-02-technical-overview.md +0 -239
  440. package/_bmad/bmm/workflows/1-analysis/research/bmad-technical-research/technical-steps/step-03-integration-patterns.md +0 -248
  441. package/_bmad/bmm/workflows/1-analysis/research/bmad-technical-research/technical-steps/step-04-architectural-patterns.md +0 -202
  442. package/_bmad/bmm/workflows/1-analysis/research/bmad-technical-research/technical-steps/step-05-implementation-research.md +0 -233
  443. package/_bmad/bmm/workflows/1-analysis/research/bmad-technical-research/technical-steps/step-06-research-synthesis.md +0 -487
  444. package/_bmad/bmm/workflows/1-analysis/research/bmad-technical-research/workflow.md +0 -50
  445. package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-01-init.md +0 -182
  446. package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +0 -237
  447. package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +0 -249
  448. package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +0 -259
  449. package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +0 -177
  450. package/_bmad/bmm/workflows/1-analysis/research/market-steps/step-06-research-completion.md +0 -476
  451. package/_bmad/bmm/workflows/1-analysis/research/research.template.md +0 -29
  452. package/_bmad/bmm/workflows/2-plan-workflows/bmad-create-ux-design/SKILL.md +0 -6
  453. package/_bmad/bmm/workflows/2-plan-workflows/bmad-create-ux-design/bmad-skill-manifest.yaml +0 -1
  454. package/_bmad/bmm/workflows/2-plan-workflows/bmad-create-ux-design/steps/step-01-init.md +0 -135
  455. package/_bmad/bmm/workflows/2-plan-workflows/bmad-create-ux-design/steps/step-01b-continue.md +0 -127
  456. package/_bmad/bmm/workflows/2-plan-workflows/bmad-create-ux-design/steps/step-02-discovery.md +0 -190
  457. package/_bmad/bmm/workflows/2-plan-workflows/bmad-create-ux-design/steps/step-03-core-experience.md +0 -217
  458. package/_bmad/bmm/workflows/2-plan-workflows/bmad-create-ux-design/steps/step-04-emotional-response.md +0 -220
  459. package/_bmad/bmm/workflows/2-plan-workflows/bmad-create-ux-design/steps/step-05-inspiration.md +0 -235
  460. package/_bmad/bmm/workflows/2-plan-workflows/bmad-create-ux-design/steps/step-06-design-system.md +0 -253
  461. package/_bmad/bmm/workflows/2-plan-workflows/bmad-create-ux-design/steps/step-07-defining-experience.md +0 -255
  462. package/_bmad/bmm/workflows/2-plan-workflows/bmad-create-ux-design/steps/step-08-visual-foundation.md +0 -225
  463. package/_bmad/bmm/workflows/2-plan-workflows/bmad-create-ux-design/steps/step-09-design-directions.md +0 -225
  464. package/_bmad/bmm/workflows/2-plan-workflows/bmad-create-ux-design/steps/step-10-user-journeys.md +0 -242
  465. package/_bmad/bmm/workflows/2-plan-workflows/bmad-create-ux-design/steps/step-11-component-strategy.md +0 -249
  466. package/_bmad/bmm/workflows/2-plan-workflows/bmad-create-ux-design/steps/step-12-ux-patterns.md +0 -238
  467. package/_bmad/bmm/workflows/2-plan-workflows/bmad-create-ux-design/steps/step-13-responsive-accessibility.md +0 -265
  468. package/_bmad/bmm/workflows/2-plan-workflows/bmad-create-ux-design/steps/step-14-complete.md +0 -171
  469. package/_bmad/bmm/workflows/2-plan-workflows/bmad-create-ux-design/ux-design-template.md +0 -13
  470. package/_bmad/bmm/workflows/2-plan-workflows/bmad-create-ux-design/workflow.md +0 -36
  471. package/_bmad/bmm/workflows/2-plan-workflows/bmad-edit-prd/SKILL.md +0 -6
  472. package/_bmad/bmm/workflows/2-plan-workflows/bmad-edit-prd/bmad-skill-manifest.yaml +0 -1
  473. package/_bmad/bmm/workflows/2-plan-workflows/bmad-edit-prd/steps-e/step-e-01-discovery.md +0 -242
  474. package/_bmad/bmm/workflows/2-plan-workflows/bmad-edit-prd/steps-e/step-e-01b-legacy-conversion.md +0 -204
  475. package/_bmad/bmm/workflows/2-plan-workflows/bmad-edit-prd/steps-e/step-e-02-review.md +0 -245
  476. package/_bmad/bmm/workflows/2-plan-workflows/bmad-edit-prd/steps-e/step-e-03-edit.md +0 -250
  477. package/_bmad/bmm/workflows/2-plan-workflows/bmad-edit-prd/steps-e/step-e-04-complete.md +0 -165
  478. package/_bmad/bmm/workflows/2-plan-workflows/bmad-edit-prd/workflow.md +0 -63
  479. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/SKILL.md +0 -6
  480. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/bmad-skill-manifest.yaml +0 -1
  481. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/data/domain-complexity.csv +0 -15
  482. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/data/prd-purpose.md +0 -197
  483. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/data/project-types.csv +0 -11
  484. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/steps-v/step-v-01-discovery.md +0 -221
  485. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/steps-v/step-v-02-format-detection.md +0 -188
  486. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/steps-v/step-v-02b-parity-check.md +0 -206
  487. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/steps-v/step-v-03-density-validation.md +0 -171
  488. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/steps-v/step-v-04-brief-coverage-validation.md +0 -211
  489. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/steps-v/step-v-05-measurability-validation.md +0 -225
  490. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/steps-v/step-v-06-traceability-validation.md +0 -214
  491. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/steps-v/step-v-07-implementation-leakage-validation.md +0 -202
  492. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/steps-v/step-v-08-domain-compliance-validation.md +0 -240
  493. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/steps-v/step-v-09-project-type-validation.md +0 -260
  494. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/steps-v/step-v-10-smart-validation.md +0 -206
  495. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/steps-v/step-v-11-holistic-quality-validation.md +0 -261
  496. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/steps-v/step-v-12-completeness-validation.md +0 -239
  497. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/steps-v/step-v-13-report-complete.md +0 -229
  498. package/_bmad/bmm/workflows/2-plan-workflows/bmad-validate-prd/workflow.md +0 -62
  499. package/_bmad/bmm/workflows/2-plan-workflows/create-prd/data/domain-complexity.csv +0 -15
  500. package/_bmad/bmm/workflows/2-plan-workflows/create-prd/data/prd-purpose.md +0 -197
  501. package/_bmad/bmm/workflows/2-plan-workflows/create-prd/data/project-types.csv +0 -11
  502. package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-01-discovery.md +0 -224
  503. package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02-format-detection.md +0 -191
  504. package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02b-parity-check.md +0 -209
  505. package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-03-density-validation.md +0 -174
  506. package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-04-brief-coverage-validation.md +0 -214
  507. package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-05-measurability-validation.md +0 -228
  508. package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-06-traceability-validation.md +0 -217
  509. package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-07-implementation-leakage-validation.md +0 -205
  510. package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-08-domain-compliance-validation.md +0 -243
  511. package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-09-project-type-validation.md +0 -263
  512. package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-10-smart-validation.md +0 -209
  513. package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-11-holistic-quality-validation.md +0 -264
  514. package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-12-completeness-validation.md +0 -242
  515. package/_bmad/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-13-report-complete.md +0 -232
  516. package/_bmad/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md +0 -65
  517. package/_bmad/bmm/workflows/3-solutioning/bmad-check-implementation-readiness/SKILL.md +0 -6
  518. package/_bmad/bmm/workflows/3-solutioning/bmad-check-implementation-readiness/bmad-skill-manifest.yaml +0 -1
  519. package/_bmad/bmm/workflows/3-solutioning/bmad-check-implementation-readiness/steps/step-01-document-discovery.md +0 -179
  520. package/_bmad/bmm/workflows/3-solutioning/bmad-check-implementation-readiness/steps/step-02-prd-analysis.md +0 -168
  521. package/_bmad/bmm/workflows/3-solutioning/bmad-check-implementation-readiness/steps/step-03-epic-coverage-validation.md +0 -169
  522. package/_bmad/bmm/workflows/3-solutioning/bmad-check-implementation-readiness/steps/step-04-ux-alignment.md +0 -129
  523. package/_bmad/bmm/workflows/3-solutioning/bmad-check-implementation-readiness/steps/step-05-epic-quality-review.md +0 -241
  524. package/_bmad/bmm/workflows/3-solutioning/bmad-check-implementation-readiness/steps/step-06-final-assessment.md +0 -126
  525. package/_bmad/bmm/workflows/3-solutioning/bmad-check-implementation-readiness/templates/readiness-report-template.md +0 -4
  526. package/_bmad/bmm/workflows/3-solutioning/bmad-check-implementation-readiness/workflow.md +0 -49
  527. package/_bmad/bmm/workflows/3-solutioning/bmad-create-architecture/SKILL.md +0 -6
  528. package/_bmad/bmm/workflows/3-solutioning/bmad-create-architecture/architecture-decision-template.md +0 -12
  529. package/_bmad/bmm/workflows/3-solutioning/bmad-create-architecture/bmad-skill-manifest.yaml +0 -1
  530. package/_bmad/bmm/workflows/3-solutioning/bmad-create-architecture/data/domain-complexity.csv +0 -13
  531. package/_bmad/bmm/workflows/3-solutioning/bmad-create-architecture/data/project-types.csv +0 -7
  532. package/_bmad/bmm/workflows/3-solutioning/bmad-create-architecture/steps/step-01-init.md +0 -153
  533. package/_bmad/bmm/workflows/3-solutioning/bmad-create-architecture/steps/step-01b-continue.md +0 -173
  534. package/_bmad/bmm/workflows/3-solutioning/bmad-create-architecture/steps/step-02-context.md +0 -224
  535. package/_bmad/bmm/workflows/3-solutioning/bmad-create-architecture/steps/step-03-starter.md +0 -329
  536. package/_bmad/bmm/workflows/3-solutioning/bmad-create-architecture/steps/step-04-decisions.md +0 -318
  537. package/_bmad/bmm/workflows/3-solutioning/bmad-create-architecture/steps/step-05-patterns.md +0 -359
  538. package/_bmad/bmm/workflows/3-solutioning/bmad-create-architecture/steps/step-06-structure.md +0 -379
  539. package/_bmad/bmm/workflows/3-solutioning/bmad-create-architecture/steps/step-07-validation.md +0 -359
  540. package/_bmad/bmm/workflows/3-solutioning/bmad-create-architecture/steps/step-08-complete.md +0 -76
  541. package/_bmad/bmm/workflows/3-solutioning/bmad-create-architecture/workflow.md +0 -38
  542. package/_bmad/bmm/workflows/3-solutioning/bmad-create-epics-and-stories/SKILL.md +0 -6
  543. package/_bmad/bmm/workflows/3-solutioning/bmad-create-epics-and-stories/bmad-skill-manifest.yaml +0 -1
  544. package/_bmad/bmm/workflows/3-solutioning/bmad-create-epics-and-stories/steps/step-01-validate-prerequisites.md +0 -255
  545. package/_bmad/bmm/workflows/3-solutioning/bmad-create-epics-and-stories/steps/step-02-design-epics.md +0 -212
  546. package/_bmad/bmm/workflows/3-solutioning/bmad-create-epics-and-stories/steps/step-03-create-stories.md +0 -255
  547. package/_bmad/bmm/workflows/3-solutioning/bmad-create-epics-and-stories/steps/step-04-final-validation.md +0 -131
  548. package/_bmad/bmm/workflows/3-solutioning/bmad-create-epics-and-stories/templates/epics-template.md +0 -61
  549. package/_bmad/bmm/workflows/3-solutioning/bmad-create-epics-and-stories/workflow.md +0 -53
  550. package/_bmad/bmm/workflows/4-implementation/bmad-code-review/SKILL.md +0 -6
  551. package/_bmad/bmm/workflows/4-implementation/bmad-code-review/bmad-skill-manifest.yaml +0 -1
  552. package/_bmad/bmm/workflows/4-implementation/bmad-code-review/steps/step-01-gather-context.md +0 -61
  553. package/_bmad/bmm/workflows/4-implementation/bmad-code-review/steps/step-02-review.md +0 -41
  554. package/_bmad/bmm/workflows/4-implementation/bmad-code-review/steps/step-03-triage.md +0 -50
  555. package/_bmad/bmm/workflows/4-implementation/bmad-code-review/steps/step-04-present.md +0 -38
  556. package/_bmad/bmm/workflows/4-implementation/bmad-code-review/workflow.md +0 -54
  557. package/_bmad/bmm/workflows/4-implementation/bmad-correct-course/SKILL.md +0 -6
  558. package/_bmad/bmm/workflows/4-implementation/bmad-correct-course/bmad-skill-manifest.yaml +0 -1
  559. package/_bmad/bmm/workflows/4-implementation/bmad-correct-course/checklist.md +0 -288
  560. package/_bmad/bmm/workflows/4-implementation/bmad-correct-course/workflow.md +0 -267
  561. package/_bmad/bmm/workflows/4-implementation/bmad-create-story/SKILL.md +0 -6
  562. package/_bmad/bmm/workflows/4-implementation/bmad-create-story/bmad-skill-manifest.yaml +0 -1
  563. package/_bmad/bmm/workflows/4-implementation/bmad-create-story/checklist.md +0 -357
  564. package/_bmad/bmm/workflows/4-implementation/bmad-create-story/discover-inputs.md +0 -88
  565. package/_bmad/bmm/workflows/4-implementation/bmad-create-story/template.md +0 -49
  566. package/_bmad/bmm/workflows/4-implementation/bmad-create-story/workflow.md +0 -380
  567. package/_bmad/bmm/workflows/4-implementation/bmad-dev-story/SKILL.md +0 -6
  568. package/_bmad/bmm/workflows/4-implementation/bmad-dev-story/bmad-skill-manifest.yaml +0 -1
  569. package/_bmad/bmm/workflows/4-implementation/bmad-dev-story/checklist.md +0 -80
  570. package/_bmad/bmm/workflows/4-implementation/bmad-dev-story/workflow.md +0 -450
  571. package/_bmad/bmm/workflows/4-implementation/bmad-retrospective/SKILL.md +0 -6
  572. package/_bmad/bmm/workflows/4-implementation/bmad-retrospective/bmad-skill-manifest.yaml +0 -1
  573. package/_bmad/bmm/workflows/4-implementation/bmad-retrospective/workflow.md +0 -1479
  574. package/_bmad/bmm/workflows/4-implementation/bmad-sprint-planning/SKILL.md +0 -6
  575. package/_bmad/bmm/workflows/4-implementation/bmad-sprint-planning/bmad-skill-manifest.yaml +0 -1
  576. package/_bmad/bmm/workflows/4-implementation/bmad-sprint-planning/checklist.md +0 -33
  577. package/_bmad/bmm/workflows/4-implementation/bmad-sprint-planning/sprint-status-template.yaml +0 -56
  578. package/_bmad/bmm/workflows/4-implementation/bmad-sprint-planning/workflow.md +0 -263
  579. package/_bmad/bmm/workflows/4-implementation/bmad-sprint-status/SKILL.md +0 -6
  580. package/_bmad/bmm/workflows/4-implementation/bmad-sprint-status/bmad-skill-manifest.yaml +0 -1
  581. package/_bmad/bmm/workflows/4-implementation/bmad-sprint-status/workflow.md +0 -261
  582. package/_bmad/bmm/workflows/bmad-document-project/SKILL.md +0 -6
  583. package/_bmad/bmm/workflows/bmad-document-project/bmad-skill-manifest.yaml +0 -1
  584. package/_bmad/bmm/workflows/bmad-document-project/checklist.md +0 -245
  585. package/_bmad/bmm/workflows/bmad-document-project/documentation-requirements.csv +0 -12
  586. package/_bmad/bmm/workflows/bmad-document-project/instructions.md +0 -128
  587. package/_bmad/bmm/workflows/bmad-document-project/templates/deep-dive-template.md +0 -345
  588. package/_bmad/bmm/workflows/bmad-document-project/templates/index-template.md +0 -169
  589. package/_bmad/bmm/workflows/bmad-document-project/templates/project-overview-template.md +0 -103
  590. package/_bmad/bmm/workflows/bmad-document-project/templates/project-scan-report-schema.json +0 -160
  591. package/_bmad/bmm/workflows/bmad-document-project/templates/source-tree-template.md +0 -135
  592. package/_bmad/bmm/workflows/bmad-document-project/workflow.md +0 -27
  593. package/_bmad/bmm/workflows/bmad-document-project/workflows/deep-dive-instructions.md +0 -299
  594. package/_bmad/bmm/workflows/bmad-document-project/workflows/deep-dive-workflow.md +0 -34
  595. package/_bmad/bmm/workflows/bmad-document-project/workflows/full-scan-instructions.md +0 -1107
  596. package/_bmad/bmm/workflows/bmad-document-project/workflows/full-scan-workflow.md +0 -34
  597. package/_bmad/bmm/workflows/bmad-generate-project-context/SKILL.md +0 -6
  598. package/_bmad/bmm/workflows/bmad-generate-project-context/bmad-skill-manifest.yaml +0 -1
  599. package/_bmad/bmm/workflows/bmad-generate-project-context/project-context-template.md +0 -21
  600. package/_bmad/bmm/workflows/bmad-generate-project-context/steps/step-01-discover.md +0 -186
  601. package/_bmad/bmm/workflows/bmad-generate-project-context/steps/step-02-generate.md +0 -321
  602. package/_bmad/bmm/workflows/bmad-generate-project-context/steps/step-03-complete.md +0 -278
  603. package/_bmad/bmm/workflows/bmad-generate-project-context/workflow.md +0 -43
  604. package/_bmad/bmm/workflows/bmad-qa-generate-e2e-tests/SKILL.md +0 -6
  605. package/_bmad/bmm/workflows/bmad-qa-generate-e2e-tests/bmad-skill-manifest.yaml +0 -1
  606. package/_bmad/bmm/workflows/bmad-qa-generate-e2e-tests/checklist.md +0 -33
  607. package/_bmad/bmm/workflows/bmad-qa-generate-e2e-tests/workflow.md +0 -136
  608. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev/SKILL.md +0 -6
  609. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev/bmad-skill-manifest.yaml +0 -1
  610. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev/steps/step-01-mode-detection.md +0 -169
  611. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev/steps/step-02-context-gathering.md +0 -114
  612. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev/steps/step-03-execute.md +0 -107
  613. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev/steps/step-04-self-check.md +0 -107
  614. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev/steps/step-05-adversarial-review.md +0 -94
  615. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev/steps/step-06-resolve-findings.md +0 -144
  616. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev/workflow.md +0 -38
  617. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/SKILL.md +0 -6
  618. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/bmad-skill-manifest.yaml +0 -1
  619. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/steps/step-01-clarify-and-route.md +0 -51
  620. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/steps/step-02-plan.md +0 -35
  621. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/steps/step-03-implement.md +0 -33
  622. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/steps/step-04-review.md +0 -50
  623. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/steps/step-05-present.md +0 -17
  624. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/tech-spec-template.md +0 -90
  625. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-dev-new-preview/workflow.md +0 -79
  626. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-spec/SKILL.md +0 -6
  627. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-spec/bmad-skill-manifest.yaml +0 -1
  628. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-spec/steps/step-01-understand.md +0 -185
  629. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-spec/steps/step-02-investigate.md +0 -140
  630. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-spec/steps/step-03-generate.md +0 -123
  631. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-spec/steps/step-04-review.md +0 -195
  632. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-spec/tech-spec-template.md +0 -74
  633. package/_bmad/bmm/workflows/bmad-quick-flow/bmad-quick-spec/workflow.md +0 -73
  634. package/_bmad/core/config.yaml +0 -9
  635. package/_bmad/core/module-help.csv +0 -11
  636. package/_bmad/core/skills/bmad-advanced-elicitation/SKILL.md +0 -6
  637. package/_bmad/core/skills/bmad-advanced-elicitation/bmad-skill-manifest.yaml +0 -1
  638. package/_bmad/core/skills/bmad-advanced-elicitation/methods.csv +0 -51
  639. package/_bmad/core/skills/bmad-advanced-elicitation/workflow.md +0 -135
  640. package/_bmad/core/skills/bmad-brainstorming/SKILL.md +0 -6
  641. package/_bmad/core/skills/bmad-brainstorming/bmad-skill-manifest.yaml +0 -1
  642. package/_bmad/core/skills/bmad-brainstorming/brain-methods.csv +0 -62
  643. package/_bmad/core/skills/bmad-brainstorming/steps/step-01-session-setup.md +0 -214
  644. package/_bmad/core/skills/bmad-brainstorming/steps/step-01b-continue.md +0 -124
  645. package/_bmad/core/skills/bmad-brainstorming/steps/step-02a-user-selected.md +0 -229
  646. package/_bmad/core/skills/bmad-brainstorming/steps/step-02b-ai-recommended.md +0 -239
  647. package/_bmad/core/skills/bmad-brainstorming/steps/step-02c-random-selection.md +0 -211
  648. package/_bmad/core/skills/bmad-brainstorming/steps/step-02d-progressive-flow.md +0 -266
  649. package/_bmad/core/skills/bmad-brainstorming/steps/step-03-technique-execution.md +0 -401
  650. package/_bmad/core/skills/bmad-brainstorming/steps/step-04-idea-organization.md +0 -305
  651. package/_bmad/core/skills/bmad-brainstorming/template.md +0 -15
  652. package/_bmad/core/skills/bmad-brainstorming/workflow.md +0 -53
  653. package/_bmad/core/skills/bmad-distillator/SKILL.md +0 -178
  654. package/_bmad/core/skills/bmad-distillator/agents/distillate-compressor.md +0 -116
  655. package/_bmad/core/skills/bmad-distillator/agents/round-trip-reconstructor.md +0 -68
  656. package/_bmad/core/skills/bmad-distillator/bmad-skill-manifest.yaml +0 -15
  657. package/_bmad/core/skills/bmad-distillator/resources/compression-rules.md +0 -51
  658. package/_bmad/core/skills/bmad-distillator/resources/distillate-format-reference.md +0 -227
  659. package/_bmad/core/skills/bmad-distillator/resources/splitting-strategy.md +0 -78
  660. package/_bmad/core/skills/bmad-distillator/scripts/analyze_sources.py +0 -300
  661. package/_bmad/core/skills/bmad-distillator/scripts/tests/test_analyze_sources.py +0 -204
  662. package/_bmad/core/skills/bmad-editorial-review-prose/SKILL.md +0 -6
  663. package/_bmad/core/skills/bmad-editorial-review-prose/bmad-skill-manifest.yaml +0 -1
  664. package/_bmad/core/skills/bmad-editorial-review-prose/workflow.md +0 -81
  665. package/_bmad/core/skills/bmad-editorial-review-structure/SKILL.md +0 -6
  666. package/_bmad/core/skills/bmad-editorial-review-structure/bmad-skill-manifest.yaml +0 -1
  667. package/_bmad/core/skills/bmad-editorial-review-structure/workflow.md +0 -174
  668. package/_bmad/core/skills/bmad-help/SKILL.md +0 -6
  669. package/_bmad/core/skills/bmad-help/bmad-skill-manifest.yaml +0 -1
  670. package/_bmad/core/skills/bmad-help/workflow.md +0 -88
  671. package/_bmad/core/skills/bmad-index-docs/SKILL.md +0 -6
  672. package/_bmad/core/skills/bmad-index-docs/bmad-skill-manifest.yaml +0 -1
  673. package/_bmad/core/skills/bmad-index-docs/workflow.md +0 -61
  674. package/_bmad/core/skills/bmad-party-mode/SKILL.md +0 -6
  675. package/_bmad/core/skills/bmad-party-mode/bmad-skill-manifest.yaml +0 -1
  676. package/_bmad/core/skills/bmad-party-mode/steps/step-01-agent-loading.md +0 -138
  677. package/_bmad/core/skills/bmad-party-mode/steps/step-02-discussion-orchestration.md +0 -187
  678. package/_bmad/core/skills/bmad-party-mode/steps/step-03-graceful-exit.md +0 -167
  679. package/_bmad/core/skills/bmad-party-mode/workflow.md +0 -190
  680. package/_bmad/core/skills/bmad-review-adversarial-general/SKILL.md +0 -6
  681. package/_bmad/core/skills/bmad-review-adversarial-general/bmad-skill-manifest.yaml +0 -1
  682. package/_bmad/core/skills/bmad-review-adversarial-general/workflow.md +0 -32
  683. package/_bmad/core/skills/bmad-review-edge-case-hunter/SKILL.md +0 -6
  684. package/_bmad/core/skills/bmad-review-edge-case-hunter/bmad-skill-manifest.yaml +0 -1
  685. package/_bmad/core/skills/bmad-review-edge-case-hunter/workflow.md +0 -62
  686. package/_bmad/core/skills/bmad-shard-doc/SKILL.md +0 -6
  687. package/_bmad/core/skills/bmad-shard-doc/bmad-skill-manifest.yaml +0 -1
  688. package/_bmad/core/skills/bmad-shard-doc/workflow.md +0 -100
  689. package/_bmad/core/tasks/bmad-create-prd/SKILL.md +0 -6
  690. package/_bmad/core/tasks/bmad-create-prd/bmad-skill-manifest.yaml +0 -1
  691. package/_bmad/core/tasks/bmad-create-prd/data/domain-complexity.csv +0 -15
  692. package/_bmad/core/tasks/bmad-create-prd/data/prd-purpose.md +0 -197
  693. package/_bmad/core/tasks/bmad-create-prd/data/project-types.csv +0 -11
  694. package/_bmad/core/tasks/bmad-create-prd/steps-c/step-01-init.md +0 -178
  695. package/_bmad/core/tasks/bmad-create-prd/steps-c/step-01b-continue.md +0 -161
  696. package/_bmad/core/tasks/bmad-create-prd/steps-c/step-02-discovery.md +0 -208
  697. package/_bmad/core/tasks/bmad-create-prd/steps-c/step-02b-vision.md +0 -142
  698. package/_bmad/core/tasks/bmad-create-prd/steps-c/step-02c-executive-summary.md +0 -158
  699. package/_bmad/core/tasks/bmad-create-prd/steps-c/step-03-success.md +0 -214
  700. package/_bmad/core/tasks/bmad-create-prd/steps-c/step-04-journeys.md +0 -201
  701. package/_bmad/core/tasks/bmad-create-prd/steps-c/step-05-domain.md +0 -194
  702. package/_bmad/core/tasks/bmad-create-prd/steps-c/step-06-innovation.md +0 -211
  703. package/_bmad/core/tasks/bmad-create-prd/steps-c/step-07-project-type.md +0 -222
  704. package/_bmad/core/tasks/bmad-create-prd/steps-c/step-08-scoping.md +0 -216
  705. package/_bmad/core/tasks/bmad-create-prd/steps-c/step-09-functional.md +0 -219
  706. package/_bmad/core/tasks/bmad-create-prd/steps-c/step-10-nonfunctional.md +0 -230
  707. package/_bmad/core/tasks/bmad-create-prd/steps-c/step-11-polish.md +0 -221
  708. package/_bmad/core/tasks/bmad-create-prd/steps-c/step-12-complete.md +0 -115
  709. package/_bmad/core/tasks/bmad-create-prd/templates/prd-template.md +0 -10
  710. package/_bmad/core/tasks/bmad-create-prd/workflow.md +0 -62
  711. package/_bmad/tea/agents/tea.md +0 -63
  712. package/_bmad/tea/config.yaml +0 -25
  713. package/_bmad/tea/module-help.csv +0 -10
  714. package/_bmad/tea/teams/default-party.csv +0 -2
  715. package/_bmad/tea/testarch/knowledge/adr-quality-readiness-checklist.md +0 -377
  716. package/_bmad/tea/testarch/knowledge/api-request.md +0 -563
  717. package/_bmad/tea/testarch/knowledge/api-testing-patterns.md +0 -915
  718. package/_bmad/tea/testarch/knowledge/auth-session.md +0 -548
  719. package/_bmad/tea/testarch/knowledge/burn-in.md +0 -273
  720. package/_bmad/tea/testarch/knowledge/ci-burn-in.md +0 -717
  721. package/_bmad/tea/testarch/knowledge/component-tdd.md +0 -486
  722. package/_bmad/tea/testarch/knowledge/contract-testing.md +0 -1050
  723. package/_bmad/tea/testarch/knowledge/data-factories.md +0 -500
  724. package/_bmad/tea/testarch/knowledge/email-auth.md +0 -721
  725. package/_bmad/tea/testarch/knowledge/error-handling.md +0 -725
  726. package/_bmad/tea/testarch/knowledge/feature-flags.md +0 -750
  727. package/_bmad/tea/testarch/knowledge/file-utils.md +0 -456
  728. package/_bmad/tea/testarch/knowledge/fixture-architecture.md +0 -401
  729. package/_bmad/tea/testarch/knowledge/fixtures-composition.md +0 -382
  730. package/_bmad/tea/testarch/knowledge/intercept-network-call.md +0 -426
  731. package/_bmad/tea/testarch/knowledge/log.md +0 -426
  732. package/_bmad/tea/testarch/knowledge/network-error-monitor.md +0 -401
  733. package/_bmad/tea/testarch/knowledge/network-first.md +0 -486
  734. package/_bmad/tea/testarch/knowledge/network-recorder.md +0 -527
  735. package/_bmad/tea/testarch/knowledge/nfr-criteria.md +0 -670
  736. package/_bmad/tea/testarch/knowledge/overview.md +0 -286
  737. package/_bmad/tea/testarch/knowledge/pact-consumer-di.md +0 -310
  738. package/_bmad/tea/testarch/knowledge/pact-consumer-framework-setup.md +0 -635
  739. package/_bmad/tea/testarch/knowledge/pact-mcp.md +0 -204
  740. package/_bmad/tea/testarch/knowledge/pactjs-utils-consumer-helpers.md +0 -270
  741. package/_bmad/tea/testarch/knowledge/pactjs-utils-overview.md +0 -216
  742. package/_bmad/tea/testarch/knowledge/pactjs-utils-provider-verifier.md +0 -315
  743. package/_bmad/tea/testarch/knowledge/pactjs-utils-request-filter.md +0 -224
  744. package/_bmad/tea/testarch/knowledge/playwright-cli.md +0 -165
  745. package/_bmad/tea/testarch/knowledge/playwright-config.md +0 -730
  746. package/_bmad/tea/testarch/knowledge/probability-impact.md +0 -601
  747. package/_bmad/tea/testarch/knowledge/recurse.md +0 -421
  748. package/_bmad/tea/testarch/knowledge/risk-governance.md +0 -615
  749. package/_bmad/tea/testarch/knowledge/selective-testing.md +0 -732
  750. package/_bmad/tea/testarch/knowledge/selector-resilience.md +0 -527
  751. package/_bmad/tea/testarch/knowledge/test-healing-patterns.md +0 -644
  752. package/_bmad/tea/testarch/knowledge/test-levels-framework.md +0 -473
  753. package/_bmad/tea/testarch/knowledge/test-priorities-matrix.md +0 -373
  754. package/_bmad/tea/testarch/knowledge/test-quality.md +0 -664
  755. package/_bmad/tea/testarch/knowledge/timing-debugging.md +0 -372
  756. package/_bmad/tea/testarch/knowledge/visual-debugging.md +0 -524
  757. package/_bmad/tea/testarch/tea-index.csv +0 -43
  758. package/_bmad/tea/workflows/testarch/README.md +0 -74
  759. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/SKILL.md +0 -6
  760. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/bmad-skill-manifest.yaml +0 -1
  761. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/checklist.md +0 -197
  762. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/data/curriculum.yaml +0 -129
  763. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/data/quiz-questions.yaml +0 -206
  764. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/data/role-paths.yaml +0 -136
  765. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/data/session-content-map.yaml +0 -207
  766. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/data/tea-resources-index.yaml +0 -359
  767. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/instructions.md +0 -130
  768. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/steps-c/step-01-init.md +0 -235
  769. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/steps-c/step-01b-continue.md +0 -147
  770. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/steps-c/step-02-assess.md +0 -258
  771. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/steps-c/step-03-session-menu.md +0 -219
  772. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/steps-c/step-04-session-01.md +0 -460
  773. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/steps-c/step-04-session-02.md +0 -465
  774. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/steps-c/step-04-session-03.md +0 -301
  775. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/steps-c/step-04-session-04.md +0 -234
  776. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/steps-c/step-04-session-05.md +0 -234
  777. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/steps-c/step-04-session-06.md +0 -209
  778. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/steps-c/step-04-session-07.md +0 -212
  779. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/steps-c/step-05-completion.md +0 -339
  780. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/steps-e/step-e-01-assess-workflow.md +0 -141
  781. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/steps-e/step-e-02-apply-edits.md +0 -122
  782. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/steps-v/step-v-01-validate.md +0 -263
  783. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/templates/certificate-template.md +0 -86
  784. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/templates/progress-template.yaml +0 -95
  785. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/templates/session-notes-template.md +0 -83
  786. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/workflow-plan-teach-me-testing.md +0 -950
  787. package/_bmad/tea/workflows/testarch/bmad-teach-me-testing/workflow.md +0 -90
  788. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/SKILL.md +0 -6
  789. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/atdd-checklist-template.md +0 -371
  790. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/bmad-skill-manifest.yaml +0 -1
  791. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/checklist.md +0 -374
  792. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/instructions.md +0 -45
  793. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/steps-c/step-01-preflight-and-context.md +0 -226
  794. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/steps-c/step-01b-resume.md +0 -96
  795. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/steps-c/step-02-generation-mode.md +0 -125
  796. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/steps-c/step-03-test-strategy.md +0 -110
  797. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/steps-c/step-04-generate-tests.md +0 -334
  798. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/steps-c/step-04a-subagent-api-failing.md +0 -286
  799. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/steps-c/step-04b-subagent-e2e-failing.md +0 -244
  800. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/steps-c/step-04c-aggregate.md +0 -370
  801. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/steps-c/step-05-validate-and-complete.md +0 -106
  802. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/steps-e/step-01-assess.md +0 -65
  803. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/steps-e/step-02-apply-edit.md +0 -60
  804. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/steps-v/step-01-validate.md +0 -67
  805. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/validation-report-20260127-095021.md +0 -73
  806. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/validation-report-20260127-102401.md +0 -116
  807. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/workflow-plan.md +0 -21
  808. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/workflow.md +0 -41
  809. package/_bmad/tea/workflows/testarch/bmad-testarch-atdd/workflow.yaml +0 -46
  810. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/SKILL.md +0 -6
  811. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/bmad-skill-manifest.yaml +0 -1
  812. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/checklist.md +0 -611
  813. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/instructions.md +0 -50
  814. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/steps-c/step-01-preflight-and-context.md +0 -237
  815. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/steps-c/step-01b-resume.md +0 -94
  816. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/steps-c/step-02-identify-targets.md +0 -169
  817. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/steps-c/step-03-generate-tests.md +0 -394
  818. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/steps-c/step-03a-subagent-api.md +0 -263
  819. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/steps-c/step-03b-subagent-backend.md +0 -246
  820. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/steps-c/step-03b-subagent-e2e.md +0 -213
  821. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/steps-c/step-03c-aggregate.md +0 -393
  822. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/steps-c/step-04-validate-and-summarize.md +0 -106
  823. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/steps-e/step-01-assess.md +0 -65
  824. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/steps-e/step-02-apply-edit.md +0 -60
  825. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/steps-v/step-01-validate.md +0 -67
  826. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/validation-report-20260127-095021.md +0 -72
  827. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/validation-report-20260127-102401.md +0 -114
  828. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/workflow-plan.md +0 -20
  829. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/workflow.md +0 -41
  830. package/_bmad/tea/workflows/testarch/bmad-testarch-automate/workflow.yaml +0 -53
  831. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/SKILL.md +0 -6
  832. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/azure-pipelines-template.yaml +0 -155
  833. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/bmad-skill-manifest.yaml +0 -1
  834. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/checklist.md +0 -289
  835. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/github-actions-template.yaml +0 -328
  836. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/gitlab-ci-template.yaml +0 -158
  837. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/harness-pipeline-template.yaml +0 -159
  838. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/instructions.md +0 -45
  839. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/jenkins-pipeline-template.groovy +0 -129
  840. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/steps-c/step-01-preflight.md +0 -158
  841. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/steps-c/step-01b-resume.md +0 -110
  842. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/steps-c/step-02-generate-pipeline.md +0 -279
  843. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/steps-c/step-03-configure-quality-gates.md +0 -135
  844. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/steps-c/step-04-validate-and-summary.md +0 -92
  845. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/steps-e/step-01-assess.md +0 -65
  846. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/steps-e/step-02-apply-edit.md +0 -60
  847. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/steps-v/step-01-validate.md +0 -81
  848. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/validation-report-20260127-095021.md +0 -72
  849. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/validation-report-20260127-102401.md +0 -114
  850. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/workflow-plan.md +0 -20
  851. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/workflow.md +0 -41
  852. package/_bmad/tea/workflows/testarch/bmad-testarch-ci/workflow.yaml +0 -48
  853. package/_bmad/tea/workflows/testarch/bmad-testarch-framework/SKILL.md +0 -6
  854. package/_bmad/tea/workflows/testarch/bmad-testarch-framework/bmad-skill-manifest.yaml +0 -1
  855. package/_bmad/tea/workflows/testarch/bmad-testarch-framework/checklist.md +0 -345
  856. package/_bmad/tea/workflows/testarch/bmad-testarch-framework/instructions.md +0 -45
  857. package/_bmad/tea/workflows/testarch/bmad-testarch-framework/steps-c/step-01-preflight.md +0 -132
  858. package/_bmad/tea/workflows/testarch/bmad-testarch-framework/steps-c/step-01b-resume.md +0 -116
  859. package/_bmad/tea/workflows/testarch/bmad-testarch-framework/steps-c/step-02-select-framework.md +0 -117
  860. package/_bmad/tea/workflows/testarch/bmad-testarch-framework/steps-c/step-03-scaffold-framework.md +0 -323
  861. package/_bmad/tea/workflows/testarch/bmad-testarch-framework/steps-c/step-04-docs-and-scripts.md +0 -105
  862. package/_bmad/tea/workflows/testarch/bmad-testarch-framework/steps-c/step-05-validate-and-summary.md +0 -93
  863. package/_bmad/tea/workflows/testarch/bmad-testarch-framework/steps-e/step-01-assess.md +0 -65
  864. package/_bmad/tea/workflows/testarch/bmad-testarch-framework/steps-e/step-02-apply-edit.md +0 -60
  865. package/_bmad/tea/workflows/testarch/bmad-testarch-framework/steps-v/step-01-validate.md +0 -67
  866. package/_bmad/tea/workflows/testarch/bmad-testarch-framework/validation-report-20260127-095021.md +0 -73
  867. package/_bmad/tea/workflows/testarch/bmad-testarch-framework/validation-report-20260127-102401.md +0 -116
  868. package/_bmad/tea/workflows/testarch/bmad-testarch-framework/workflow-plan.md +0 -22
  869. package/_bmad/tea/workflows/testarch/bmad-testarch-framework/workflow.md +0 -41
  870. package/_bmad/tea/workflows/testarch/bmad-testarch-framework/workflow.yaml +0 -48
  871. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/SKILL.md +0 -6
  872. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/bmad-skill-manifest.yaml +0 -1
  873. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/checklist.md +0 -407
  874. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/instructions.md +0 -43
  875. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/nfr-report-template.md +0 -470
  876. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/steps-c/step-01-load-context.md +0 -138
  877. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/steps-c/step-01b-resume.md +0 -106
  878. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/steps-c/step-02-define-thresholds.md +0 -107
  879. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/steps-c/step-03-gather-evidence.md +0 -108
  880. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/steps-c/step-04-evaluate-and-score.md +0 -254
  881. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/steps-c/step-04a-subagent-security.md +0 -138
  882. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/steps-c/step-04b-subagent-performance.md +0 -84
  883. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/steps-c/step-04c-subagent-reliability.md +0 -85
  884. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/steps-c/step-04d-subagent-scalability.md +0 -88
  885. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/steps-c/step-04e-aggregate-nfr.md +0 -264
  886. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/steps-c/step-05-generate-report.md +0 -108
  887. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/steps-e/step-01-assess.md +0 -65
  888. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/steps-e/step-02-apply-edit.md +0 -60
  889. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/steps-v/step-01-validate.md +0 -67
  890. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/validation-report-20260127-095021.md +0 -73
  891. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/validation-report-20260127-102401.md +0 -116
  892. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/workflow-plan.md +0 -19
  893. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/workflow.md +0 -41
  894. package/_bmad/tea/workflows/testarch/bmad-testarch-nfr/workflow.yaml +0 -48
  895. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/SKILL.md +0 -6
  896. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/bmad-skill-manifest.yaml +0 -1
  897. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/checklist.md +0 -464
  898. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/instructions.md +0 -105
  899. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/steps-c/step-01-detect-mode.md +0 -134
  900. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/steps-c/step-01b-resume.md +0 -102
  901. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/steps-c/step-02-load-context.md +0 -242
  902. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/steps-c/step-03-risk-and-testability.md +0 -110
  903. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/steps-c/step-04-coverage-plan.md +0 -123
  904. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/steps-c/step-05-generate-output.md +0 -222
  905. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/steps-e/step-01-assess.md +0 -65
  906. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/steps-e/step-02-apply-edit.md +0 -60
  907. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/steps-v/step-01-validate.md +0 -67
  908. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/test-design-architecture-template.md +0 -230
  909. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/test-design-handoff-template.md +0 -70
  910. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/test-design-qa-template.md +0 -396
  911. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/test-design-template.md +0 -344
  912. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/validation-report-20260127-095021.md +0 -73
  913. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/validation-report-20260127-102401.md +0 -116
  914. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/workflow-plan.md +0 -22
  915. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/workflow.md +0 -41
  916. package/_bmad/tea/workflows/testarch/bmad-testarch-test-design/workflow.yaml +0 -77
  917. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/SKILL.md +0 -6
  918. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/bmad-skill-manifest.yaml +0 -1
  919. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/checklist.md +0 -475
  920. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/instructions.md +0 -45
  921. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/steps-c/step-01-load-context.md +0 -197
  922. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/steps-c/step-01b-resume.md +0 -104
  923. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/steps-c/step-02-discover-tests.md +0 -113
  924. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/steps-c/step-03-quality-evaluation.md +0 -274
  925. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/steps-c/step-03a-subagent-determinism.md +0 -214
  926. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/steps-c/step-03b-subagent-isolation.md +0 -125
  927. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/steps-c/step-03c-subagent-maintainability.md +0 -102
  928. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/steps-c/step-03e-subagent-performance.md +0 -117
  929. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/steps-c/step-03f-aggregate-scores.md +0 -277
  930. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/steps-c/step-04-generate-report.md +0 -111
  931. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/steps-e/step-01-assess.md +0 -65
  932. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/steps-e/step-02-apply-edit.md +0 -60
  933. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/steps-v/step-01-validate.md +0 -67
  934. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/test-review-template.md +0 -387
  935. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/validation-report-20260127-095021.md +0 -72
  936. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/validation-report-20260127-102401.md +0 -114
  937. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/workflow-plan.md +0 -18
  938. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/workflow.md +0 -41
  939. package/_bmad/tea/workflows/testarch/bmad-testarch-test-review/workflow.yaml +0 -48
  940. package/_bmad/tea/workflows/testarch/bmad-testarch-trace/SKILL.md +0 -6
  941. package/_bmad/tea/workflows/testarch/bmad-testarch-trace/bmad-skill-manifest.yaml +0 -1
  942. package/_bmad/tea/workflows/testarch/bmad-testarch-trace/checklist.md +0 -647
  943. package/_bmad/tea/workflows/testarch/bmad-testarch-trace/instructions.md +0 -43
  944. package/_bmad/tea/workflows/testarch/bmad-testarch-trace/steps-c/step-01-load-context.md +0 -105
  945. package/_bmad/tea/workflows/testarch/bmad-testarch-trace/steps-c/step-01b-resume.md +0 -102
  946. package/_bmad/tea/workflows/testarch/bmad-testarch-trace/steps-c/step-02-discover-tests.md +0 -112
  947. package/_bmad/tea/workflows/testarch/bmad-testarch-trace/steps-c/step-03-map-criteria.md +0 -97
  948. package/_bmad/tea/workflows/testarch/bmad-testarch-trace/steps-c/step-04-analyze-gaps.md +0 -421
  949. package/_bmad/tea/workflows/testarch/bmad-testarch-trace/steps-c/step-05-gate-decision.md +0 -266
  950. package/_bmad/tea/workflows/testarch/bmad-testarch-trace/steps-e/step-01-assess.md +0 -65
  951. package/_bmad/tea/workflows/testarch/bmad-testarch-trace/steps-e/step-02-apply-edit.md +0 -60
  952. package/_bmad/tea/workflows/testarch/bmad-testarch-trace/steps-v/step-01-validate.md +0 -67
  953. package/_bmad/tea/workflows/testarch/bmad-testarch-trace/trace-template.md +0 -708
  954. package/_bmad/tea/workflows/testarch/bmad-testarch-trace/validation-report-20260127-095021.md +0 -73
  955. package/_bmad/tea/workflows/testarch/bmad-testarch-trace/validation-report-20260127-102401.md +0 -116
  956. package/_bmad/tea/workflows/testarch/bmad-testarch-trace/workflow-plan.md +0 -21
  957. package/_bmad/tea/workflows/testarch/bmad-testarch-trace/workflow.md +0 -41
  958. package/_bmad/tea/workflows/testarch/bmad-testarch-trace/workflow.yaml +0 -56
  959. package/eslint.config.js +0 -38
  960. package/packages/client/index.html +0 -12
  961. package/packages/client/package.json +0 -46
  962. package/packages/client/src/app.tsx +0 -57
  963. package/packages/client/src/features/about/AboutPage.tsx +0 -144
  964. package/packages/client/src/features/agents/AgentCard.tsx +0 -52
  965. package/packages/client/src/features/agents/AgentDetail.tsx +0 -338
  966. package/packages/client/src/features/agents/AgentOverrideEditor.tsx +0 -127
  967. package/packages/client/src/features/agents/AgentsPage.tsx +0 -114
  968. package/packages/client/src/features/agents/EditAgentDialog.tsx +0 -260
  969. package/packages/client/src/features/agents/SkillAssignment.tsx +0 -153
  970. package/packages/client/src/features/agents/use-agent-detail.ts +0 -17
  971. package/packages/client/src/features/agents/use-agent-mutations.ts +0 -47
  972. package/packages/client/src/features/agents/use-agents.ts +0 -17
  973. package/packages/client/src/features/connections/ConnectionsPage.tsx +0 -165
  974. package/packages/client/src/features/files/FilesPage.tsx +0 -177
  975. package/packages/client/src/features/outputs/OutputsPage.tsx +0 -190
  976. package/packages/client/src/features/overview/OverviewPage.tsx +0 -282
  977. package/packages/client/src/features/packages/EditModuleDialog.tsx +0 -118
  978. package/packages/client/src/features/packages/PackagesPage.tsx +0 -982
  979. package/packages/client/src/features/settings/SettingsPage.tsx +0 -141
  980. package/packages/client/src/features/settings/use-settings.ts +0 -34
  981. package/packages/client/src/features/skills/CreateSkillDialog.tsx +0 -328
  982. package/packages/client/src/features/skills/SkillDetailSlideOver.tsx +0 -179
  983. package/packages/client/src/features/skills/SkillsPage.tsx +0 -142
  984. package/packages/client/src/features/skills/use-skills.ts +0 -27
  985. package/packages/client/src/features/teams/CreateTeamDialog.tsx +0 -239
  986. package/packages/client/src/features/teams/TeamDetailPanel.tsx +0 -549
  987. package/packages/client/src/features/teams/TeamsPage.tsx +0 -196
  988. package/packages/client/src/features/teams/use-teams.ts +0 -39
  989. package/packages/client/src/features/workflows/CreateWorkflowDialog.tsx +0 -395
  990. package/packages/client/src/features/workflows/EditWorkflowDialog.tsx +0 -131
  991. package/packages/client/src/features/workflows/WorkflowDetailPanel.tsx +0 -503
  992. package/packages/client/src/features/workflows/WorkflowGraph.tsx +0 -108
  993. package/packages/client/src/features/workflows/WorkflowsPage.tsx +0 -277
  994. package/packages/client/src/features/workflows/use-workflows.ts +0 -27
  995. package/packages/client/src/features/workspace/WorkspacePage.tsx +0 -304
  996. package/packages/client/src/globals.css +0 -184
  997. package/packages/client/src/hooks/use-detail-param.ts +0 -31
  998. package/packages/client/src/hooks/use-file-save.ts +0 -22
  999. package/packages/client/src/hooks/use-websocket.ts +0 -38
  1000. package/packages/client/src/layout/AppShell.tsx +0 -20
  1001. package/packages/client/src/layout/Breadcrumbs.tsx +0 -52
  1002. package/packages/client/src/layout/NotificationProvider.tsx +0 -95
  1003. package/packages/client/src/layout/Sidebar.tsx +0 -176
  1004. package/packages/client/src/lib/theme.ts +0 -30
  1005. package/packages/client/src/lib/websocket-client.ts +0 -97
  1006. package/packages/client/src/main.tsx +0 -14
  1007. package/packages/client/src/shared/CommandPalette.tsx +0 -151
  1008. package/packages/client/src/shared/CsvViewer.tsx +0 -120
  1009. package/packages/client/src/shared/EmptyState.tsx +0 -20
  1010. package/packages/client/src/shared/EntityPageHeader.tsx +0 -95
  1011. package/packages/client/src/shared/FilepathLink.tsx +0 -22
  1012. package/packages/client/src/shared/Skeleton.tsx +0 -31
  1013. package/packages/client/src/shared/SlideOver.tsx +0 -63
  1014. package/packages/client/src/shared/VocabularyHelper.tsx +0 -48
  1015. package/packages/client/src/shared/diff-viewer/DiffViewer.tsx +0 -93
  1016. package/packages/client/src/shared/markdown-editor/CodeMirrorEditor.tsx +0 -199
  1017. package/packages/client/src/shared/markdown-editor/MarkdownEditor.tsx +0 -95
  1018. package/packages/client/src/stores/ui-store.ts +0 -13
  1019. package/packages/client/tsconfig.json +0 -13
  1020. package/packages/client/vite.config.ts +0 -20
  1021. package/packages/server/package.json +0 -30
  1022. package/packages/server/src/app.test.ts +0 -89
  1023. package/packages/server/src/app.ts +0 -101
  1024. package/packages/server/src/core/errors.test.ts +0 -49
  1025. package/packages/server/src/core/errors.ts +0 -62
  1026. package/packages/server/src/core/file-store.test.ts +0 -104
  1027. package/packages/server/src/core/file-store.ts +0 -209
  1028. package/packages/server/src/core/project-detector.test.ts +0 -94
  1029. package/packages/server/src/core/project-detector.ts +0 -97
  1030. package/packages/server/src/core/websocket.test.ts +0 -80
  1031. package/packages/server/src/core/websocket.ts +0 -47
  1032. package/packages/server/src/core/write-service.test.ts +0 -87
  1033. package/packages/server/src/core/write-service.ts +0 -75
  1034. package/packages/server/src/index.ts +0 -97
  1035. package/packages/server/src/parsers/agent-parser.test.ts +0 -71
  1036. package/packages/server/src/parsers/agent-parser.ts +0 -83
  1037. package/packages/server/src/parsers/config-parser.test.ts +0 -65
  1038. package/packages/server/src/parsers/config-parser.ts +0 -51
  1039. package/packages/server/src/parsers/csv-parser.test.ts +0 -53
  1040. package/packages/server/src/parsers/csv-parser.ts +0 -55
  1041. package/packages/server/src/parsers/ide-config-parser.test.ts +0 -35
  1042. package/packages/server/src/parsers/ide-config-parser.ts +0 -37
  1043. package/packages/server/src/parsers/index-builder.test.ts +0 -78
  1044. package/packages/server/src/parsers/index-builder.ts +0 -195
  1045. package/packages/server/src/parsers/package-parser.test.ts +0 -36
  1046. package/packages/server/src/parsers/package-parser.ts +0 -34
  1047. package/packages/server/src/parsers/skill-parser.test.ts +0 -51
  1048. package/packages/server/src/parsers/skill-parser.ts +0 -26
  1049. package/packages/server/src/parsers/team-parser.test.ts +0 -186
  1050. package/packages/server/src/parsers/team-parser.ts +0 -128
  1051. package/packages/server/src/parsers/workflow-parser.test.ts +0 -415
  1052. package/packages/server/src/parsers/workflow-parser.ts +0 -311
  1053. package/packages/server/src/plugins/agents-plugin.test.ts +0 -111
  1054. package/packages/server/src/plugins/agents-plugin.ts +0 -164
  1055. package/packages/server/src/plugins/files-plugin.test.ts +0 -121
  1056. package/packages/server/src/plugins/files-plugin.ts +0 -93
  1057. package/packages/server/src/plugins/modules-plugin.test.ts +0 -264
  1058. package/packages/server/src/plugins/modules-plugin.ts +0 -386
  1059. package/packages/server/src/plugins/outputs-plugin.test.ts +0 -89
  1060. package/packages/server/src/plugins/outputs-plugin.ts +0 -93
  1061. package/packages/server/src/plugins/overview-plugin.ts +0 -73
  1062. package/packages/server/src/plugins/search-plugin.ts +0 -63
  1063. package/packages/server/src/plugins/settings-plugin.test.ts +0 -82
  1064. package/packages/server/src/plugins/settings-plugin.ts +0 -46
  1065. package/packages/server/src/plugins/skills-plugin.ts +0 -87
  1066. package/packages/server/src/plugins/teams-plugin.test.ts +0 -157
  1067. package/packages/server/src/plugins/teams-plugin.ts +0 -198
  1068. package/packages/server/src/plugins/validation-plugin.test.ts +0 -229
  1069. package/packages/server/src/plugins/validation-plugin.ts +0 -116
  1070. package/packages/server/src/plugins/workflows-plugin.test.ts +0 -145
  1071. package/packages/server/src/plugins/workflows-plugin.ts +0 -270
  1072. package/packages/server/src/static.ts +0 -29
  1073. package/packages/server/tsconfig.json +0 -8
  1074. package/packages/shared/package.json +0 -20
  1075. package/packages/shared/tsconfig.json +0 -8
  1076. package/tsconfig.base.json +0 -19
  1077. package/vitest.config.ts +0 -7
@@ -0,0 +1,2124 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { execSync } from 'node:child_process';
3
+ import fs from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import FormData from 'form-data';
7
+ import yaml from 'js-yaml';
8
+ import { createApp, MAX_MODULE_UPLOAD_BYTES } from '../app.js';
9
+ function makeManifest(modules) {
10
+ return {
11
+ installation: {
12
+ version: '6.2.0',
13
+ installDate: '2026-01-01T00:00:00.000Z',
14
+ lastUpdated: '2026-01-01T00:00:00.000Z',
15
+ },
16
+ modules: modules.map((m) => ({
17
+ name: m.name,
18
+ version: '1.0.0',
19
+ installDate: '2026-01-01T00:00:00.000Z',
20
+ lastUpdated: '2026-01-01T00:00:00.000Z',
21
+ source: m.source,
22
+ npmPackage: null,
23
+ repoUrl: null,
24
+ })),
25
+ };
26
+ }
27
+ describe('modules-plugin', () => {
28
+ let tmpDir;
29
+ beforeEach(() => {
30
+ // TD-20 — realpathSync resolves the macOS /var → /private/var symlink so path
31
+ // comparisons against tmpDir are stable. New tests in Story 15.2 onwards depend on this.
32
+ tmpDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'modules-plugin-test-')));
33
+ const configDir = path.join(tmpDir, '_bmad', '_config');
34
+ const moduleDir = path.join(tmpDir, '_bmad', 'test-mod');
35
+ fs.mkdirSync(configDir, { recursive: true });
36
+ fs.mkdirSync(path.join(moduleDir, 'agents'), { recursive: true });
37
+ fs.mkdirSync(path.join(moduleDir, 'skills'), { recursive: true });
38
+ fs.mkdirSync(path.join(moduleDir, 'workflows'), { recursive: true });
39
+ fs.writeFileSync(path.join(moduleDir, 'config.yaml'), 'project_name: test-mod\n');
40
+ fs.writeFileSync(path.join(configDir, 'manifest.yaml'), yaml.dump(makeManifest([{ name: 'test-mod', source: 'custom' }])));
41
+ fs.mkdirSync(path.join(tmpDir, '.bmad-studio'), { recursive: true });
42
+ });
43
+ afterEach(() => {
44
+ fs.rmSync(tmpDir, { recursive: true, force: true });
45
+ });
46
+ function createTestApp() {
47
+ return createApp({
48
+ logger: false,
49
+ serveStatic: false,
50
+ project: {
51
+ projectRoot: tmpDir,
52
+ bmadVersion: '6.2.0',
53
+ versionSupported: true,
54
+ modules: [{ name: 'test-mod', version: '1.0.0', source: 'custom' }],
55
+ ideDirectories: [],
56
+ },
57
+ });
58
+ }
59
+ // --- Story 12.1: Add entities ---
60
+ it('POST /api/modules/:name/entities creates a skill file', async () => {
61
+ const app = await createTestApp();
62
+ const resp = await app.inject({
63
+ method: 'POST',
64
+ url: '/api/modules/test-mod/entities',
65
+ payload: { type: 'skill', name: 'my-skill' },
66
+ });
67
+ expect(resp.statusCode).toBe(201);
68
+ const body = JSON.parse(resp.body);
69
+ expect(body.ok).toBe(true);
70
+ expect(body.type).toBe('skill');
71
+ const skillPath = path.join(tmpDir, '_bmad', 'test-mod', 'skills', 'my-skill', 'SKILL.md');
72
+ expect(fs.existsSync(skillPath)).toBe(true);
73
+ const content = fs.readFileSync(skillPath, 'utf-8');
74
+ expect(content).toContain('name: my-skill');
75
+ await app.close();
76
+ });
77
+ it('POST /api/modules/:name/entities creates a workflow directory', async () => {
78
+ const app = await createTestApp();
79
+ const resp = await app.inject({
80
+ method: 'POST',
81
+ url: '/api/modules/test-mod/entities',
82
+ payload: { type: 'workflow', name: 'my-workflow' },
83
+ });
84
+ expect(resp.statusCode).toBe(201);
85
+ const wfPath = path.join(tmpDir, '_bmad', 'test-mod', 'workflows', 'my-workflow', 'workflow.md');
86
+ expect(fs.existsSync(wfPath)).toBe(true);
87
+ const content = fs.readFileSync(wfPath, 'utf-8');
88
+ expect(content).toContain('name: my-workflow');
89
+ await app.close();
90
+ });
91
+ it('POST /api/modules/:name/entities creates an agent file', async () => {
92
+ const app = await createTestApp();
93
+ const resp = await app.inject({
94
+ method: 'POST',
95
+ url: '/api/modules/test-mod/entities',
96
+ payload: { type: 'agent', name: 'my-agent' },
97
+ });
98
+ expect(resp.statusCode).toBe(201);
99
+ const agentPath = path.join(tmpDir, '_bmad', 'test-mod', 'agents', 'my-agent.md');
100
+ expect(fs.existsSync(agentPath)).toBe(true);
101
+ const content = fs.readFileSync(agentPath, 'utf-8');
102
+ expect(content).toContain('name: my-agent');
103
+ await app.close();
104
+ });
105
+ it('POST /api/modules/:name/entities rejects invalid type', async () => {
106
+ const app = await createTestApp();
107
+ const resp = await app.inject({
108
+ method: 'POST',
109
+ url: '/api/modules/test-mod/entities',
110
+ payload: { type: 'invalid', name: 'test' },
111
+ });
112
+ expect(resp.statusCode).toBe(422);
113
+ await app.close();
114
+ });
115
+ it('POST /api/modules/:name/entities rejects missing name', async () => {
116
+ const app = await createTestApp();
117
+ const resp = await app.inject({
118
+ method: 'POST',
119
+ url: '/api/modules/test-mod/entities',
120
+ payload: { type: 'skill' },
121
+ });
122
+ expect(resp.statusCode).toBe(422);
123
+ await app.close();
124
+ });
125
+ it('POST /api/modules/:name/entities returns 404 for nonexistent module', async () => {
126
+ const app = await createTestApp();
127
+ const resp = await app.inject({
128
+ method: 'POST',
129
+ url: '/api/modules/no-such-mod/entities',
130
+ payload: { type: 'skill', name: 'test' },
131
+ });
132
+ expect(resp.statusCode).toBe(404);
133
+ await app.close();
134
+ });
135
+ it('POST /api/modules/:name/entities rejects duplicate skill', async () => {
136
+ const app = await createTestApp();
137
+ // Create first
138
+ await app.inject({
139
+ method: 'POST',
140
+ url: '/api/modules/test-mod/entities',
141
+ payload: { type: 'skill', name: 'dup-skill' },
142
+ });
143
+ // Create duplicate
144
+ const resp = await app.inject({
145
+ method: 'POST',
146
+ url: '/api/modules/test-mod/entities',
147
+ payload: { type: 'skill', name: 'dup-skill' },
148
+ });
149
+ expect(resp.statusCode).toBe(409);
150
+ await app.close();
151
+ });
152
+ // --- Story 12.2: Upload entities with content ---
153
+ it('POST /api/modules/:name/entities accepts custom content', async () => {
154
+ const app = await createTestApp();
155
+ const customContent = '---\nname: uploaded-skill\ncategory: testing\n---\n\n# Uploaded Skill\n\nCustom content here.\n';
156
+ const resp = await app.inject({
157
+ method: 'POST',
158
+ url: '/api/modules/test-mod/entities',
159
+ payload: { type: 'skill', name: 'uploaded-skill', content: customContent },
160
+ });
161
+ expect(resp.statusCode).toBe(201);
162
+ const skillPath = path.join(tmpDir, '_bmad', 'test-mod', 'skills', 'uploaded-skill', 'SKILL.md');
163
+ const content = fs.readFileSync(skillPath, 'utf-8');
164
+ expect(content).toBe(customContent);
165
+ await app.close();
166
+ });
167
+ it('POST /api/modules/:name/entities uploads agent with content', async () => {
168
+ const app = await createTestApp();
169
+ const customContent = '---\nname: uploaded-agent\ntitle: Test Agent\n---\n\n# Uploaded Agent\n';
170
+ const resp = await app.inject({
171
+ method: 'POST',
172
+ url: '/api/modules/test-mod/entities',
173
+ payload: { type: 'agent', name: 'uploaded-agent', content: customContent },
174
+ });
175
+ expect(resp.statusCode).toBe(201);
176
+ const agentPath = path.join(tmpDir, '_bmad', 'test-mod', 'agents', 'uploaded-agent.md');
177
+ const content = fs.readFileSync(agentPath, 'utf-8');
178
+ expect(content).toBe(customContent);
179
+ await app.close();
180
+ });
181
+ // --- Story 12.3: Export module manifest ---
182
+ it('POST /api/modules/:name/export returns manifest', async () => {
183
+ const app = await createTestApp();
184
+ const resp = await app.inject({
185
+ method: 'POST',
186
+ url: '/api/modules/test-mod/export',
187
+ });
188
+ expect(resp.statusCode).toBe(200);
189
+ const body = JSON.parse(resp.body);
190
+ expect(body.module).toBe('test-mod');
191
+ expect(body.version).toBe('1.0.0');
192
+ expect(body.exportDate).toBeTruthy();
193
+ expect(body.entities).toBeTruthy();
194
+ expect(body.entities.agents).toHaveProperty('count');
195
+ expect(body.entities.agents).toHaveProperty('names');
196
+ expect(body.entities.skills).toHaveProperty('count');
197
+ expect(body.entities.workflows).toHaveProperty('count');
198
+ expect(typeof body.totalEntities).toBe('number');
199
+ expect(body.note).toContain('future enhancement');
200
+ await app.close();
201
+ });
202
+ it('POST /api/modules/:name/export returns 404 for nonexistent module', async () => {
203
+ const app = await createTestApp();
204
+ const resp = await app.inject({
205
+ method: 'POST',
206
+ url: '/api/modules/no-such-mod/export',
207
+ });
208
+ expect(resp.statusCode).toBe(404);
209
+ await app.close();
210
+ });
211
+ it('POST /api/modules/:name/export includes entities after adding them', async () => {
212
+ const app = await createTestApp();
213
+ // Add a skill
214
+ await app.inject({
215
+ method: 'POST',
216
+ url: '/api/modules/test-mod/entities',
217
+ payload: { type: 'skill', name: 'export-test-skill' },
218
+ });
219
+ // Add a workflow
220
+ await app.inject({
221
+ method: 'POST',
222
+ url: '/api/modules/test-mod/entities',
223
+ payload: { type: 'workflow', name: 'export-test-wf' },
224
+ });
225
+ const resp = await app.inject({
226
+ method: 'POST',
227
+ url: '/api/modules/test-mod/export',
228
+ });
229
+ expect(resp.statusCode).toBe(200);
230
+ const body = JSON.parse(resp.body);
231
+ expect(body.entities.skills.count).toBeGreaterThanOrEqual(1);
232
+ expect(body.entities.skills.names).toContain('export-test-skill');
233
+ expect(body.totalEntities).toBeGreaterThanOrEqual(2);
234
+ await app.close();
235
+ });
236
+ });
237
+ // ─────────────────────────────────────────────────────────────────────────────
238
+ // Story 15.2 — Polymorphic install endpoint + local source type
239
+ // ─────────────────────────────────────────────────────────────────────────────
240
+ describe('modules-plugin — Story 15.2 polymorphic install', () => {
241
+ let tmpDir;
242
+ let sourceParent;
243
+ beforeEach(() => {
244
+ // TD-20 — realpathSync for stable path comparisons.
245
+ tmpDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'modules-plugin-15-2-')));
246
+ sourceParent = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'modules-plugin-15-2-src-')));
247
+ const configDir = path.join(tmpDir, '_bmad', '_config');
248
+ fs.mkdirSync(configDir, { recursive: true });
249
+ fs.writeFileSync(path.join(configDir, 'manifest.yaml'), yaml.dump(makeManifest([])));
250
+ fs.mkdirSync(path.join(tmpDir, '.bmad-studio'), { recursive: true });
251
+ });
252
+ afterEach(() => {
253
+ fs.rmSync(tmpDir, { recursive: true, force: true });
254
+ fs.rmSync(sourceParent, { recursive: true, force: true });
255
+ });
256
+ function createTestApp() {
257
+ return createApp({
258
+ logger: false,
259
+ serveStatic: false,
260
+ project: {
261
+ projectRoot: tmpDir,
262
+ bmadVersion: '6.2.0',
263
+ versionSupported: true,
264
+ modules: [],
265
+ ideDirectories: [],
266
+ },
267
+ });
268
+ }
269
+ /**
270
+ * Build a fixture source module under sourceParent.
271
+ * @param dirName the directory name (becomes the basename)
272
+ * @param opts.code if provided, writes a module.yaml with this code
273
+ * @param opts.binary if true, also writes assets/icon.png with valid PNG header bytes
274
+ * (deliberately invalid utf-8 — exposes any accidental decode)
275
+ */
276
+ function createSourceModule(dirName, opts = {}) {
277
+ const sourceDir = path.join(sourceParent, dirName);
278
+ fs.mkdirSync(path.join(sourceDir, 'agents'), { recursive: true });
279
+ fs.writeFileSync(path.join(sourceDir, 'agents', 'architect.md'), '---\nname: architect\ntitle: Architect\n---\n\n# Architect\n');
280
+ if (opts.code) {
281
+ fs.writeFileSync(path.join(sourceDir, 'module.yaml'), `code: ${opts.code}\nname: "Test Module"\nversion: "1.0.0"\n`);
282
+ }
283
+ if (opts.binary) {
284
+ fs.mkdirSync(path.join(sourceDir, 'assets'), { recursive: true });
285
+ // PNG signature 0x89 0x50 0x4E 0x47 — leading 0x89 is invalid UTF-8.
286
+ // If anyone tries to decode this as utf-8, the round-trip is lossy and
287
+ // the buffer comparison in AC-15.2.2a fails — exactly the regression
288
+ // guard we want.
289
+ fs.writeFileSync(path.join(sourceDir, 'assets', 'icon.png'), Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]));
290
+ }
291
+ return sourceDir;
292
+ }
293
+ // ─── AC-15.2.1 — legacy { packageName } body still routes to npm branch ───
294
+ it('AC-15.2.1: legacy { packageName } body routes to the npm branch', async () => {
295
+ // We can't easily mock execSync inside the handler from here without
296
+ // restructuring the plugin, so instead we send a packageName for a package
297
+ // that definitely doesn't exist and assert we get the npm-branch error path
298
+ // (a 400 from the npm pack failure), not the "Either source or packageName"
299
+ // error from the discrimination step. This proves the legacy shape was
300
+ // accepted and routed.
301
+ const app = await createTestApp();
302
+ const resp = await app.inject({
303
+ method: 'POST',
304
+ url: '/api/modules/install',
305
+ payload: { packageName: 'this-package-definitely-does-not-exist-1234567890' },
306
+ });
307
+ // npm branch errors now use ValidationError → 422 (consistent with other branches)
308
+ expect(resp.statusCode).toBe(422);
309
+ const body = JSON.parse(resp.body);
310
+ // Error message comes from npm pack failure — proves we routed past discrimination
311
+ // into the npm branch (not the "Either source or packageName" guard).
312
+ expect(body.error).not.toContain('Either `source` or `packageName`');
313
+ await app.close();
314
+ });
315
+ // ─── AC-15.2.2 + AC-15.2.2a — local install copies text + binary correctly ───
316
+ it('AC-15.2.2 + 2a: local install copies text and binary files byte-identically', async () => {
317
+ const sourceDir = createSourceModule('bin-test', { code: 'bin-test', binary: true });
318
+ const originalPng = fs.readFileSync(path.join(sourceDir, 'assets', 'icon.png'));
319
+ const app = await createTestApp();
320
+ const resp = await app.inject({
321
+ method: 'POST',
322
+ url: '/api/modules/install',
323
+ payload: { source: { type: 'local', value: sourceDir } },
324
+ });
325
+ expect(resp.statusCode).toBe(200);
326
+ const body = JSON.parse(resp.body);
327
+ expect(body.ok).toBe(true);
328
+ expect(body.modules).toEqual(['bin-test']);
329
+ // text count: agents/architect.md + module.yaml = 2; binary count: assets/icon.png = 1
330
+ expect(body.filesCopied).toEqual({ text: 2, binary: 1 });
331
+ // Markdown file landed correctly
332
+ const installedMd = fs.readFileSync(path.join(tmpDir, '_bmad', 'bin-test', 'agents', 'architect.md'), 'utf-8');
333
+ expect(installedMd).toContain('# Architect');
334
+ // PNG file is byte-identical (the regression guard against accidental utf-8 decode)
335
+ const installedPng = fs.readFileSync(path.join(tmpDir, '_bmad', 'bin-test', 'assets', 'icon.png'));
336
+ expect(Buffer.compare(installedPng, originalPng)).toBe(0);
337
+ await app.close();
338
+ });
339
+ // ─── AC-15.2.3 — relative path resolves against projectRoot ───
340
+ it('AC-15.2.3: relative local path resolves against projectRoot', async () => {
341
+ // Place the source inside the project root
342
+ const sourceInsideProject = path.join(tmpDir, 'scratch', 'my-mod');
343
+ fs.mkdirSync(path.join(sourceInsideProject, 'agents'), { recursive: true });
344
+ fs.writeFileSync(path.join(sourceInsideProject, 'agents', 'a.md'), '# a\n');
345
+ fs.writeFileSync(path.join(sourceInsideProject, 'module.yaml'), 'code: rel-mod\n');
346
+ const app = await createTestApp();
347
+ const resp = await app.inject({
348
+ method: 'POST',
349
+ url: '/api/modules/install',
350
+ payload: { source: { type: 'local', value: 'scratch/my-mod' } },
351
+ });
352
+ expect(resp.statusCode).toBe(200);
353
+ expect(fs.existsSync(path.join(tmpDir, '_bmad', 'rel-mod', 'agents', 'a.md'))).toBe(true);
354
+ await app.close();
355
+ });
356
+ // ─── AC-15.2.4 — non-existent or non-module path is rejected ───
357
+ it('AC-15.2.4 (a): non-existent local path returns 422 (ValidationError)', async () => {
358
+ const app = await createTestApp();
359
+ const resp = await app.inject({
360
+ method: 'POST',
361
+ url: '/api/modules/install',
362
+ payload: { source: { type: 'local', value: '/does/not/exist/anywhere/12345' } },
363
+ });
364
+ expect(resp.statusCode).toBe(422);
365
+ expect(JSON.parse(resp.body).error.message).toContain('does not look like a BMAD module');
366
+ await app.close();
367
+ });
368
+ it('AC-15.2.4 (b): existing path with no entity dirs and no module.yaml returns 422', async () => {
369
+ const sourceDir = path.join(sourceParent, 'just-readme');
370
+ fs.mkdirSync(sourceDir, { recursive: true });
371
+ fs.writeFileSync(path.join(sourceDir, 'README.md'), '# Not a module\n');
372
+ const app = await createTestApp();
373
+ const resp = await app.inject({
374
+ method: 'POST',
375
+ url: '/api/modules/install',
376
+ payload: { source: { type: 'local', value: sourceDir } },
377
+ });
378
+ expect(resp.statusCode).toBe(422);
379
+ expect(JSON.parse(resp.body).error.message).toContain('does not look like a BMAD module');
380
+ await app.close();
381
+ });
382
+ // ─── AC-15.2.5 — module.yaml.code overrides directory basename ───
383
+ it('AC-15.2.5: module.yaml.code overrides the directory basename', async () => {
384
+ const sourceDir = createSourceModule('source-name-different', { code: 'dept-aem' });
385
+ const app = await createTestApp();
386
+ const resp = await app.inject({
387
+ method: 'POST',
388
+ url: '/api/modules/install',
389
+ payload: { source: { type: 'local', value: sourceDir } },
390
+ });
391
+ expect(resp.statusCode).toBe(200);
392
+ const body = JSON.parse(resp.body);
393
+ expect(body.modules).toEqual(['dept-aem']);
394
+ // Destination directory uses the code, not the source basename
395
+ expect(fs.existsSync(path.join(tmpDir, '_bmad', 'dept-aem'))).toBe(true);
396
+ expect(fs.existsSync(path.join(tmpDir, '_bmad', 'source-name-different'))).toBe(false);
397
+ // Manifest entry name matches the code
398
+ const manifest = yaml.load(fs.readFileSync(path.join(tmpDir, '_bmad', '_config', 'manifest.yaml'), 'utf-8'));
399
+ expect(manifest.modules.find((m) => m.name === 'dept-aem')).toBeDefined();
400
+ expect(manifest.modules.find((m) => m.name === 'source-name-different')).toBeUndefined();
401
+ await app.close();
402
+ });
403
+ // ─── AC-15.2.6 / Story 17.3 — second install of same module returns 200 (clean-slate) ───
404
+ it('AC-15.2.6: re-install of an existing module returns 200 (clean-slate)', async () => {
405
+ const sourceDir = createSourceModule('reinstall-test', { code: 'reinstall-test' });
406
+ const app = await createTestApp();
407
+ const first = await app.inject({
408
+ method: 'POST',
409
+ url: '/api/modules/install',
410
+ payload: { source: { type: 'local', value: sourceDir } },
411
+ });
412
+ expect(first.statusCode).toBe(200);
413
+ const second = await app.inject({
414
+ method: 'POST',
415
+ url: '/api/modules/install',
416
+ payload: { source: { type: 'local', value: sourceDir } },
417
+ });
418
+ expect(second.statusCode).toBe(200);
419
+ expect(JSON.parse(second.body).ok).toBe(true);
420
+ await app.close();
421
+ });
422
+ // ─── AC-15.2.7 — text writes go through WriteService snapshots; binaries do not ───
423
+ it('AC-15.2.7: text writes snapshot to .bmad-studio/history/, binaries do not', async () => {
424
+ // Pre-populate the destination with a file that the install will overwrite,
425
+ // so the WriteService has a previous version to snapshot.
426
+ const destModuleDir = path.join(tmpDir, '_bmad', 'snap-test');
427
+ fs.mkdirSync(path.join(destModuleDir, 'agents'), { recursive: true });
428
+ fs.writeFileSync(path.join(destModuleDir, 'agents', 'architect.md'), '# Old version\n');
429
+ // The install will 409 because the dest already exists, so instead we test
430
+ // the snapshot behavior by manually invoking copyDirThroughWriteService through
431
+ // a different module (touching a fresh dest). Use the create-module endpoint
432
+ // which routes manifest writes through WriteService — that's enough to verify
433
+ // the snapshot mechanism is wired up.
434
+ const app = await createTestApp();
435
+ // Create a module — this writes config.yaml directly (fs.writeFileSync, not WriteService)
436
+ // and updates the manifest through writeManifestThroughWriteService.
437
+ await app.inject({
438
+ method: 'POST',
439
+ url: '/api/modules',
440
+ payload: { name: 'snap-test-2', version: '1.0.0' },
441
+ });
442
+ // The first manifest update happens during the test setup (beforeEach writes manifest.yaml).
443
+ // The create-module call above modifies it through WriteService — so a snapshot of
444
+ // the original (empty modules) manifest should now exist.
445
+ const historyDir = path.join(tmpDir, '.bmad-studio', 'history');
446
+ expect(fs.existsSync(historyDir)).toBe(true);
447
+ const snapshots = fs.readdirSync(historyDir);
448
+ expect(snapshots.some((f) => f.endsWith('manifest.yaml'))).toBe(true);
449
+ // No snapshot files should be PNGs (binaries don't snapshot)
450
+ expect(snapshots.some((f) => f.endsWith('.png'))).toBe(false);
451
+ await app.close();
452
+ });
453
+ // ─── AC-15.2.8 — old helper functions are gone from modules-plugin.ts ───
454
+ it('AC-15.2.8: modules-plugin.ts no longer contains the old helper functions', () => {
455
+ const pluginSource = fs.readFileSync(path.join(process.cwd(), 'packages/server/src/plugins/modules-plugin.ts'), 'utf-8');
456
+ expect(pluginSource).not.toMatch(/function readManifest\(/);
457
+ expect(pluginSource).not.toMatch(/function writeManifest\(/);
458
+ expect(pluginSource).not.toMatch(/function copyDirRecursive\(/);
459
+ });
460
+ // ─── AC-15.2.9 — missing manifest.yaml is a hard 422 ───
461
+ it('AC-15.2.9: missing manifest.yaml returns 422 with installer instructions', async () => {
462
+ // Delete the manifest the beforeEach created
463
+ fs.unlinkSync(path.join(tmpDir, '_bmad', '_config', 'manifest.yaml'));
464
+ const sourceDir = createSourceModule('orphan-mod', { code: 'orphan-mod' });
465
+ const app = await createTestApp();
466
+ const resp = await app.inject({
467
+ method: 'POST',
468
+ url: '/api/modules/install',
469
+ payload: { source: { type: 'local', value: sourceDir } },
470
+ });
471
+ expect(resp.statusCode).toBe(422);
472
+ const body = JSON.parse(resp.body);
473
+ expect(body.error.message).toBe('Cannot install module: missing _bmad/_config/manifest.yaml. Run `npx bmad-method install` to initialise the project first.');
474
+ await app.close();
475
+ });
476
+ // ─── AC-15.2.10 — both source and packageName: source wins ───
477
+ it('AC-15.2.10: when both source and packageName are present, source wins', async () => {
478
+ const sourceDir = createSourceModule('precedence-test', { code: 'precedence-test' });
479
+ const app = await createTestApp();
480
+ const resp = await app.inject({
481
+ method: 'POST',
482
+ url: '/api/modules/install',
483
+ payload: {
484
+ source: { type: 'local', value: sourceDir },
485
+ packageName: 'this-would-fail-if-it-was-used',
486
+ },
487
+ });
488
+ // If `packageName` had won, the npm branch would 400. If `source` won, the local install succeeds.
489
+ expect(resp.statusCode).toBe(200);
490
+ expect(JSON.parse(resp.body).modules).toEqual(['precedence-test']);
491
+ await app.close();
492
+ });
493
+ // ─── AC-15.2.11 — variables field accepted but not consumed (forward compatibility) ───
494
+ it('AC-15.2.11: variables field is accepted in the body without affecting install', async () => {
495
+ const sourceDir = createSourceModule('vars-test', { code: 'vars-test' });
496
+ const app = await createTestApp();
497
+ const resp = await app.inject({
498
+ method: 'POST',
499
+ url: '/api/modules/install',
500
+ payload: {
501
+ source: { type: 'local', value: sourceDir },
502
+ variables: { project_name: 'AcmeProject', region: 'us-east-1' },
503
+ },
504
+ });
505
+ expect(resp.statusCode).toBe(200);
506
+ expect(JSON.parse(resp.body).modules).toEqual(['vars-test']);
507
+ // The variables aren't consumed yet (Story 15.5), so the file content is unchanged
508
+ const installed = fs.readFileSync(path.join(tmpDir, '_bmad', 'vars-test', 'agents', 'architect.md'), 'utf-8');
509
+ expect(installed).not.toContain('AcmeProject');
510
+ await app.close();
511
+ });
512
+ // (Story 15.2 had a github 501 placeholder test here. Story 15.3 implemented
513
+ // the github branch — see the dedicated 'modules-plugin — Story 15.3 github install'
514
+ // describe block below for the real github tests with mocked fetch.)
515
+ // ─── neither source nor packageName: clean error ───
516
+ it('returns 422 when neither source nor packageName is provided', async () => {
517
+ const app = await createTestApp();
518
+ const resp = await app.inject({
519
+ method: 'POST',
520
+ url: '/api/modules/install',
521
+ payload: {},
522
+ });
523
+ expect(resp.statusCode).toBe(422);
524
+ expect(JSON.parse(resp.body).error.message).toContain('Either `source` or `packageName` is required');
525
+ await app.close();
526
+ });
527
+ });
528
+ // ─────────────────────────────────────────────────────────────────────────────
529
+ // Story 15.3 — Polymorphic install: github source type
530
+ // ─────────────────────────────────────────────────────────────────────────────
531
+ /**
532
+ * Build a fixture tarball that mimics GitHub's tarball format. GitHub tarballs
533
+ * always extract to a single wrapper directory like {owner}-{repo}-{shortsha}/.
534
+ *
535
+ * @param wrapperName the wrapper directory name (e.g. 'owner-repo-abc1234')
536
+ * @param contentBuilder callback that populates the wrapper dir with files
537
+ */
538
+ function buildFixtureTarball(wrapperName, contentBuilder) {
539
+ const stagingRoot = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'bmad-tarball-fixture-')));
540
+ try {
541
+ const wrapperDir = path.join(stagingRoot, wrapperName);
542
+ fs.mkdirSync(wrapperDir, { recursive: true });
543
+ contentBuilder(wrapperDir);
544
+ const tarballPath = path.join(stagingRoot, 'fixture.tar.gz');
545
+ execSync(`tar -czf "${tarballPath}" -C "${stagingRoot}" "${wrapperName}"`, { stdio: 'pipe' });
546
+ return fs.readFileSync(tarballPath);
547
+ }
548
+ finally {
549
+ fs.rmSync(stagingRoot, { recursive: true, force: true });
550
+ }
551
+ }
552
+ // Build the two fixture tarballs once at module load (cheap, ~1ms each).
553
+ const VALID_MODULE_TARBALL = buildFixtureTarball('owner-repo-abc1234', (wrapperDir) => {
554
+ fs.mkdirSync(path.join(wrapperDir, 'agents'), { recursive: true });
555
+ fs.writeFileSync(path.join(wrapperDir, 'agents', 'test.md'), '---\nname: test\ntitle: Test Agent\n---\n# Test\n');
556
+ fs.writeFileSync(path.join(wrapperDir, 'module.yaml'), 'code: gh-test\nname: "GitHub Test Module"\nversion: "1.0.0"\n');
557
+ });
558
+ // A second fixture where the only content is a docs subdir with no entity dirs and no module.yaml —
559
+ // used to verify isPlausibleModuleDir rejects non-module subpaths and that cleanup still happens.
560
+ const DOCS_ONLY_TARBALL = buildFixtureTarball('owner-repo-abc1234', (wrapperDir) => {
561
+ fs.mkdirSync(path.join(wrapperDir, 'docs'), { recursive: true });
562
+ fs.writeFileSync(path.join(wrapperDir, 'docs', 'README.md'), '# Just docs\n');
563
+ });
564
+ // A third fixture with a valid subpath module — used to verify subpath navigation works.
565
+ const SUBPATH_MODULE_TARBALL = buildFixtureTarball('owner-repo-abc1234', (wrapperDir) => {
566
+ fs.mkdirSync(path.join(wrapperDir, 'modules', 'inner', 'agents'), { recursive: true });
567
+ fs.writeFileSync(path.join(wrapperDir, 'modules', 'inner', 'agents', 'a.md'), '---\nname: a\n---\n# A\n');
568
+ fs.writeFileSync(path.join(wrapperDir, 'modules', 'inner', 'module.yaml'), 'code: inner-mod\nversion: "1.0.0"\n');
569
+ });
570
+ /** Make a Response that mocks the github tarball API. */
571
+ function mockTarballResponse(bytes) {
572
+ return new Response(bytes, {
573
+ status: 200,
574
+ statusText: 'OK',
575
+ headers: { 'Content-Type': 'application/gzip' },
576
+ });
577
+ }
578
+ function mockNotFoundResponse() {
579
+ return new Response('Not Found', { status: 404, statusText: 'Not Found' });
580
+ }
581
+ function mockUnauthorizedResponse() {
582
+ return new Response('Unauthorized', { status: 401, statusText: 'Unauthorized' });
583
+ }
584
+ describe('modules-plugin — Story 15.3 github install', () => {
585
+ let tmpDir;
586
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
587
+ let fetchSpy;
588
+ beforeEach(() => {
589
+ tmpDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'modules-plugin-15-3-')));
590
+ const configDir = path.join(tmpDir, '_bmad', '_config');
591
+ fs.mkdirSync(configDir, { recursive: true });
592
+ fs.writeFileSync(path.join(configDir, 'manifest.yaml'), yaml.dump(makeManifest([])));
593
+ fs.mkdirSync(path.join(tmpDir, '.bmad-studio'), { recursive: true });
594
+ fetchSpy = vi.spyOn(globalThis, 'fetch');
595
+ });
596
+ afterEach(() => {
597
+ fs.rmSync(tmpDir, { recursive: true, force: true });
598
+ vi.restoreAllMocks();
599
+ });
600
+ function createTestApp() {
601
+ return createApp({
602
+ logger: false,
603
+ serveStatic: false,
604
+ project: {
605
+ projectRoot: tmpDir,
606
+ bmadVersion: '6.2.0',
607
+ versionSupported: true,
608
+ modules: [],
609
+ ideDirectories: [],
610
+ },
611
+ });
612
+ }
613
+ // ─── AC-15.3.1, AC-15.3.5 (no fallback needed), AC-15.3.7, AC-15.3.9 ─────────
614
+ it('AC-15.3.1: bare owner/repo installs from main branch', async () => {
615
+ fetchSpy.mockImplementationOnce(async () => mockTarballResponse(VALID_MODULE_TARBALL));
616
+ const app = await createTestApp();
617
+ const resp = await app.inject({
618
+ method: 'POST',
619
+ url: '/api/modules/install',
620
+ payload: { source: { type: 'github', value: 'owner/repo' } },
621
+ });
622
+ expect(resp.statusCode).toBe(200);
623
+ const body = JSON.parse(resp.body);
624
+ expect(body.ok).toBe(true);
625
+ expect(body.modules).toEqual(['gh-test']);
626
+ expect(body.source).toEqual({ type: 'github', value: 'owner/repo', branch: 'main' });
627
+ // The fetch URL should target main
628
+ expect(fetchSpy).toHaveBeenCalledTimes(1);
629
+ expect(fetchSpy.mock.calls[0][0]).toContain('/repos/owner/repo/tarball/main');
630
+ // Module landed correctly
631
+ expect(fs.existsSync(path.join(tmpDir, '_bmad', 'gh-test', 'agents', 'test.md'))).toBe(true);
632
+ // AC-15.3.7 — manifest entry shape
633
+ const manifest = yaml.load(fs.readFileSync(path.join(tmpDir, '_bmad', '_config', 'manifest.yaml'), 'utf-8'));
634
+ const entry = manifest.modules.find((m) => m.name === 'gh-test');
635
+ expect(entry).toBeDefined();
636
+ expect(entry.source).toBe('github');
637
+ expect(entry.repoUrl).toBe('https://github.com/owner/repo');
638
+ expect(entry.npmPackage).toBeNull();
639
+ await app.close();
640
+ });
641
+ // ─── AC-15.3.2 — owner/repo/subpath@branch ───
642
+ it('AC-15.3.2: owner/repo/subpath@dev fetches the dev branch and navigates the subpath', async () => {
643
+ fetchSpy.mockImplementationOnce(async () => mockTarballResponse(SUBPATH_MODULE_TARBALL));
644
+ const app = await createTestApp();
645
+ const resp = await app.inject({
646
+ method: 'POST',
647
+ url: '/api/modules/install',
648
+ payload: { source: { type: 'github', value: 'owner/repo/modules/inner@dev' } },
649
+ });
650
+ expect(resp.statusCode).toBe(200);
651
+ const body = JSON.parse(resp.body);
652
+ expect(body.modules).toEqual(['inner-mod']);
653
+ expect(body.source.branch).toBe('dev');
654
+ expect(fetchSpy.mock.calls[0][0]).toContain('/tarball/dev');
655
+ expect(fs.existsSync(path.join(tmpDir, '_bmad', 'inner-mod', 'agents', 'a.md'))).toBe(true);
656
+ await app.close();
657
+ });
658
+ // ─── AC-15.3.3 — full URL with /tree/ form ───
659
+ it('AC-15.3.3: full URL with /tree/branch/subpath parses correctly', async () => {
660
+ fetchSpy.mockImplementationOnce(async () => mockTarballResponse(SUBPATH_MODULE_TARBALL));
661
+ const app = await createTestApp();
662
+ const resp = await app.inject({
663
+ method: 'POST',
664
+ url: '/api/modules/install',
665
+ payload: {
666
+ source: {
667
+ type: 'github',
668
+ value: 'https://github.com/owner/repo/tree/main/modules/inner',
669
+ },
670
+ },
671
+ });
672
+ expect(resp.statusCode).toBe(200);
673
+ expect(fetchSpy.mock.calls[0][0]).toContain('/repos/owner/repo/tarball/main');
674
+ expect(JSON.parse(resp.body).source.branch).toBe('main');
675
+ await app.close();
676
+ });
677
+ // ─── AC-15.3.4 — 401 returns the friendly token-error message ───
678
+ it('AC-15.3.4: 401 from GitHub returns 422 with the GITHUB_TOKEN guidance', async () => {
679
+ // Make sure no token is set so the 401 path is reached cleanly
680
+ const savedToken = process.env.GITHUB_TOKEN;
681
+ const savedBmadToken = process.env.BMAD_GITHUB_TOKEN;
682
+ delete process.env.GITHUB_TOKEN;
683
+ delete process.env.BMAD_GITHUB_TOKEN;
684
+ try {
685
+ fetchSpy.mockImplementationOnce(async () => mockUnauthorizedResponse());
686
+ const app = await createTestApp();
687
+ const resp = await app.inject({
688
+ method: 'POST',
689
+ url: '/api/modules/install',
690
+ payload: { source: { type: 'github', value: 'private/repo' } },
691
+ });
692
+ expect(resp.statusCode).toBe(422);
693
+ expect(JSON.parse(resp.body).error.message).toBe('Cannot access private/repo. If this is a private repository, set GITHUB_TOKEN in your environment before starting BMAD Studio.');
694
+ await app.close();
695
+ }
696
+ finally {
697
+ if (savedToken !== undefined)
698
+ process.env.GITHUB_TOKEN = savedToken;
699
+ if (savedBmadToken !== undefined)
700
+ process.env.BMAD_GITHUB_TOKEN = savedBmadToken;
701
+ }
702
+ });
703
+ // ─── AC-15.3.5 — main 404 → master 200 fallback ───
704
+ it('AC-15.3.5: main 404 falls back to master', async () => {
705
+ fetchSpy
706
+ .mockImplementationOnce(async () => mockNotFoundResponse())
707
+ .mockImplementationOnce(async () => mockTarballResponse(VALID_MODULE_TARBALL));
708
+ const app = await createTestApp();
709
+ const resp = await app.inject({
710
+ method: 'POST',
711
+ url: '/api/modules/install',
712
+ payload: { source: { type: 'github', value: 'owner/repo' } },
713
+ });
714
+ expect(resp.statusCode).toBe(200);
715
+ expect(JSON.parse(resp.body).source.branch).toBe('master');
716
+ expect(fetchSpy).toHaveBeenCalledTimes(2);
717
+ expect(fetchSpy.mock.calls[0][0]).toContain('/tarball/main');
718
+ expect(fetchSpy.mock.calls[1][0]).toContain('/tarball/master');
719
+ await app.close();
720
+ });
721
+ // ─── AC-15.3.6 — both branches 404 → 422 with branch names in error ───
722
+ it('AC-15.3.6: both main and master 404 returns 422 mentioning the failed branch', async () => {
723
+ fetchSpy
724
+ .mockImplementationOnce(async () => mockNotFoundResponse())
725
+ .mockImplementationOnce(async () => mockNotFoundResponse());
726
+ const app = await createTestApp();
727
+ const resp = await app.inject({
728
+ method: 'POST',
729
+ url: '/api/modules/install',
730
+ payload: { source: { type: 'github', value: 'owner/repo' } },
731
+ });
732
+ expect(resp.statusCode).toBe(422);
733
+ const errMsg = JSON.parse(resp.body).error.message;
734
+ // The lastError reflects the last attempted branch (master)
735
+ expect(errMsg).toMatch(/master/);
736
+ expect(errMsg).toMatch(/not found/);
737
+ await app.close();
738
+ });
739
+ // ─── AC-15.3.8 — temp dirs are cleaned up on failure ───
740
+ it('AC-15.3.8: failed install cleans up temp directories', async () => {
741
+ fetchSpy
742
+ .mockImplementationOnce(async () => mockNotFoundResponse())
743
+ .mockImplementationOnce(async () => mockNotFoundResponse());
744
+ const before = fs
745
+ .readdirSync(os.tmpdir())
746
+ .filter((n) => n.startsWith('bmad-github-')).length;
747
+ const app = await createTestApp();
748
+ const resp = await app.inject({
749
+ method: 'POST',
750
+ url: '/api/modules/install',
751
+ payload: { source: { type: 'github', value: 'owner/repo' } },
752
+ });
753
+ expect(resp.statusCode).toBe(422);
754
+ const after = fs
755
+ .readdirSync(os.tmpdir())
756
+ .filter((n) => n.startsWith('bmad-github-')).length;
757
+ // The failing install should not LEAK a new dir. Other tests in the suite may
758
+ // have dropped their dirs concurrently, so use <= rather than ===.
759
+ expect(after).toBeLessThanOrEqual(before);
760
+ await app.close();
761
+ });
762
+ // ─── AC-15.3.9 — explicit branch is reflected in the response ───
763
+ it('AC-15.3.9: explicit branch is reflected in response.source.branch', async () => {
764
+ fetchSpy.mockImplementationOnce(async () => mockTarballResponse(VALID_MODULE_TARBALL));
765
+ const app = await createTestApp();
766
+ const resp = await app.inject({
767
+ method: 'POST',
768
+ url: '/api/modules/install',
769
+ payload: { source: { type: 'github', value: 'owner/repo@develop' } },
770
+ });
771
+ expect(resp.statusCode).toBe(200);
772
+ expect(JSON.parse(resp.body).source.branch).toBe('develop');
773
+ expect(fetchSpy.mock.calls[0][0]).toContain('/tarball/develop');
774
+ await app.close();
775
+ });
776
+ // ─── AC-15.3.10 — manifest existence guard runs BEFORE the network call ───
777
+ it('AC-15.3.10: missing manifest.yaml prevents the github fetch from being made', async () => {
778
+ fs.unlinkSync(path.join(tmpDir, '_bmad', '_config', 'manifest.yaml'));
779
+ const app = await createTestApp();
780
+ const resp = await app.inject({
781
+ method: 'POST',
782
+ url: '/api/modules/install',
783
+ payload: { source: { type: 'github', value: 'owner/repo' } },
784
+ });
785
+ expect(resp.statusCode).toBe(422);
786
+ expect(JSON.parse(resp.body).error.message).toBe('Cannot install module: missing _bmad/_config/manifest.yaml. Run `npx bmad-method install` to initialise the project first.');
787
+ // CRITICAL — fetch was NOT called. The guard runs before the download.
788
+ expect(fetchSpy).not.toHaveBeenCalled();
789
+ await app.close();
790
+ });
791
+ // ─── AC-15.3.11 — non-module subpath returns 422 and cleans up ───
792
+ it('AC-15.3.11: non-module subpath returns 422 and cleans up the temp dir', async () => {
793
+ fetchSpy.mockImplementationOnce(async () => mockTarballResponse(DOCS_ONLY_TARBALL));
794
+ const before = fs
795
+ .readdirSync(os.tmpdir())
796
+ .filter((n) => n.startsWith('bmad-github-')).length;
797
+ const app = await createTestApp();
798
+ const resp = await app.inject({
799
+ method: 'POST',
800
+ url: '/api/modules/install',
801
+ payload: { source: { type: 'github', value: 'owner/repo/docs' } },
802
+ });
803
+ expect(resp.statusCode).toBe(422);
804
+ expect(JSON.parse(resp.body).error.message).toContain('does not look like a BMAD module');
805
+ const after = fs
806
+ .readdirSync(os.tmpdir())
807
+ .filter((n) => n.startsWith('bmad-github-')).length;
808
+ expect(after).toBeLessThanOrEqual(before);
809
+ await app.close();
810
+ });
811
+ // ─── GITHUB_TOKEN sent in the Authorization header ───
812
+ it('GITHUB_TOKEN env var is forwarded as Authorization: Bearer header', async () => {
813
+ const savedToken = process.env.GITHUB_TOKEN;
814
+ process.env.GITHUB_TOKEN = 'test-token-xyz';
815
+ try {
816
+ fetchSpy.mockImplementationOnce(async () => mockTarballResponse(VALID_MODULE_TARBALL));
817
+ const app = await createTestApp();
818
+ const resp = await app.inject({
819
+ method: 'POST',
820
+ url: '/api/modules/install',
821
+ payload: { source: { type: 'github', value: 'owner/repo' } },
822
+ });
823
+ expect(resp.statusCode).toBe(200);
824
+ const fetchCallArgs = fetchSpy.mock.calls[0];
825
+ const init = fetchCallArgs[1];
826
+ const headers = init?.headers;
827
+ expect(headers).toBeDefined();
828
+ expect(headers['Authorization']).toBe('Bearer test-token-xyz');
829
+ expect(headers['User-Agent']).toBe('bmad-studio');
830
+ await app.close();
831
+ }
832
+ finally {
833
+ if (savedToken === undefined) {
834
+ delete process.env.GITHUB_TOKEN;
835
+ }
836
+ else {
837
+ process.env.GITHUB_TOKEN = savedToken;
838
+ }
839
+ }
840
+ });
841
+ // ─── BMAD_GITHUB_TOKEN fallback when GITHUB_TOKEN is not set ───
842
+ it('BMAD_GITHUB_TOKEN is used when GITHUB_TOKEN is not set', async () => {
843
+ const savedGh = process.env.GITHUB_TOKEN;
844
+ const savedBmad = process.env.BMAD_GITHUB_TOKEN;
845
+ delete process.env.GITHUB_TOKEN;
846
+ process.env.BMAD_GITHUB_TOKEN = 'bmad-test-token';
847
+ try {
848
+ fetchSpy.mockImplementationOnce(async () => mockTarballResponse(VALID_MODULE_TARBALL));
849
+ const app = await createTestApp();
850
+ const resp = await app.inject({
851
+ method: 'POST',
852
+ url: '/api/modules/install',
853
+ payload: { source: { type: 'github', value: 'owner/repo' } },
854
+ });
855
+ expect(resp.statusCode).toBe(200);
856
+ const headers = fetchSpy.mock.calls[0][1].headers;
857
+ expect(headers['Authorization']).toBe('Bearer bmad-test-token');
858
+ await app.close();
859
+ }
860
+ finally {
861
+ if (savedGh !== undefined)
862
+ process.env.GITHUB_TOKEN = savedGh;
863
+ if (savedBmad === undefined) {
864
+ delete process.env.BMAD_GITHUB_TOKEN;
865
+ }
866
+ else {
867
+ process.env.BMAD_GITHUB_TOKEN = savedBmad;
868
+ }
869
+ }
870
+ });
871
+ // ─── Invalid GitHub source string returns 422 ───
872
+ it('invalid github source string returns 422', async () => {
873
+ const app = await createTestApp();
874
+ const resp = await app.inject({
875
+ method: 'POST',
876
+ url: '/api/modules/install',
877
+ payload: { source: { type: 'github', value: 'just-one-segment' } },
878
+ });
879
+ expect(resp.statusCode).toBe(422);
880
+ expect(JSON.parse(resp.body).error.message).toContain('Invalid GitHub source');
881
+ expect(fetchSpy).not.toHaveBeenCalled();
882
+ await app.close();
883
+ });
884
+ // ─── Story 17.3 — Re-install of an existing github module returns 200 (clean-slate) ───
885
+ it('Story 17.3: re-install of an existing github module returns 200 (clean-slate)', async () => {
886
+ fetchSpy
887
+ .mockImplementationOnce(async () => mockTarballResponse(VALID_MODULE_TARBALL))
888
+ .mockImplementationOnce(async () => mockTarballResponse(VALID_MODULE_TARBALL));
889
+ const app = await createTestApp();
890
+ const first = await app.inject({
891
+ method: 'POST',
892
+ url: '/api/modules/install',
893
+ payload: { source: { type: 'github', value: 'owner/repo' } },
894
+ });
895
+ expect(first.statusCode).toBe(200);
896
+ const second = await app.inject({
897
+ method: 'POST',
898
+ url: '/api/modules/install',
899
+ payload: { source: { type: 'github', value: 'owner/repo' } },
900
+ });
901
+ expect(second.statusCode).toBe(200);
902
+ expect(JSON.parse(second.body).ok).toBe(true);
903
+ await app.close();
904
+ });
905
+ });
906
+ // ─────────────────────────────────────────────────────────────────────────────
907
+ // Story 15.4 — Polymorphic install: zip source type
908
+ // ─────────────────────────────────────────────────────────────────────────────
909
+ /**
910
+ * Build a zip in memory using adm-zip directly. Same approach as the unit tests
911
+ * in module-installer.test.ts — we use adm-zip to construct the fixture, then
912
+ * extractZipUpload (which also uses adm-zip) to consume it.
913
+ */
914
+ async function buildFixtureZip(contents) {
915
+ const AdmZip = (await import('adm-zip')).default;
916
+ const zip = new AdmZip();
917
+ for (const { entryName, data } of contents) {
918
+ const buf = typeof data === 'string' ? Buffer.from(data) : data;
919
+ zip.addFile(entryName, buf);
920
+ }
921
+ return zip.toBuffer();
922
+ }
923
+ /** Make a multipart payload for app.inject from a zip buffer. */
924
+ function makeMultipartPayload(zipBytes, filename = 'module.zip') {
925
+ const form = new FormData();
926
+ form.append('file', zipBytes, { filename, contentType: 'application/zip' });
927
+ return { payload: form.getBuffer(), headers: form.getHeaders() };
928
+ }
929
+ describe('modules-plugin — Story 15.4 zip upload', () => {
930
+ let tmpDir;
931
+ // Three fixtures are built in beforeAll-style at file scope below by reusing buildFixtureZip
932
+ // inside each test (cheap — ~1ms per build).
933
+ beforeEach(() => {
934
+ tmpDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'modules-plugin-15-4-')));
935
+ const configDir = path.join(tmpDir, '_bmad', '_config');
936
+ fs.mkdirSync(configDir, { recursive: true });
937
+ fs.writeFileSync(path.join(configDir, 'manifest.yaml'), yaml.dump(makeManifest([])));
938
+ fs.mkdirSync(path.join(tmpDir, '.bmad-studio'), { recursive: true });
939
+ });
940
+ afterEach(() => {
941
+ fs.rmSync(tmpDir, { recursive: true, force: true });
942
+ });
943
+ function createTestApp() {
944
+ return createApp({
945
+ logger: false,
946
+ serveStatic: false,
947
+ project: {
948
+ projectRoot: tmpDir,
949
+ bmadVersion: '6.2.0',
950
+ versionSupported: true,
951
+ modules: [],
952
+ ideDirectories: [],
953
+ },
954
+ });
955
+ }
956
+ // Helper to build a "valid" fixture zip (no wrapper dir, has agents/ + module.yaml)
957
+ async function makeValidZip() {
958
+ return buildFixtureZip([
959
+ { entryName: 'agents/test.md', data: '---\nname: test\ntitle: Test Agent\n---\n# Test\n' },
960
+ { entryName: 'module.yaml', data: 'code: zip-test\nname: "Zip Test Module"\nversion: "1.0.0"\n' },
961
+ ]);
962
+ }
963
+ // Helper to build a "wrapper dir" fixture zip
964
+ async function makeWrapperZip() {
965
+ return buildFixtureZip([
966
+ { entryName: 'my-module/agents/test.md', data: '---\nname: test\n---\n# Test\n' },
967
+ { entryName: 'my-module/module.yaml', data: 'code: wrapped-test\nversion: "1.0.0"\n' },
968
+ ]);
969
+ }
970
+ // Helper to build a zip with no module-shaped contents
971
+ async function makeNoModuleZip() {
972
+ return buildFixtureZip([
973
+ { entryName: 'README.md', data: '# Just a README\n' },
974
+ { entryName: 'LICENSE', data: 'MIT\n' },
975
+ ]);
976
+ }
977
+ // ─── AC-15.4.2 ───
978
+ it('AC-15.4.2: valid zip upload installs the module', async () => {
979
+ const zipBytes = await makeValidZip();
980
+ const { payload, headers } = makeMultipartPayload(zipBytes);
981
+ const app = await createTestApp();
982
+ const resp = await app.inject({
983
+ method: 'POST',
984
+ url: '/api/modules/install/upload',
985
+ payload,
986
+ headers,
987
+ });
988
+ expect(resp.statusCode).toBe(200);
989
+ const body = JSON.parse(resp.body);
990
+ expect(body.ok).toBe(true);
991
+ expect(body.modules).toEqual(['zip-test']);
992
+ expect(body.source).toEqual({ type: 'zip' });
993
+ expect(fs.existsSync(path.join(tmpDir, '_bmad', 'zip-test', 'agents', 'test.md'))).toBe(true);
994
+ const manifest = yaml.load(fs.readFileSync(path.join(tmpDir, '_bmad', '_config', 'manifest.yaml'), 'utf-8'));
995
+ const entry = manifest.modules.find((m) => m.name === 'zip-test');
996
+ expect(entry).toBeDefined();
997
+ expect(entry.source).toBe('zip');
998
+ expect(entry.npmPackage).toBeNull();
999
+ expect(entry.repoUrl).toBeNull();
1000
+ await app.close();
1001
+ });
1002
+ // ─── AC-15.4.3 (wrapper dir integration) ───
1003
+ it('AC-15.4.3: zip with a single wrapper dir is unwrapped during install', async () => {
1004
+ const zipBytes = await makeWrapperZip();
1005
+ const { payload, headers } = makeMultipartPayload(zipBytes);
1006
+ const app = await createTestApp();
1007
+ const resp = await app.inject({
1008
+ method: 'POST',
1009
+ url: '/api/modules/install/upload',
1010
+ payload,
1011
+ headers,
1012
+ });
1013
+ expect(resp.statusCode).toBe(200);
1014
+ expect(JSON.parse(resp.body).modules).toEqual(['wrapped-test']);
1015
+ expect(fs.existsSync(path.join(tmpDir, '_bmad', 'wrapped-test', 'agents', 'test.md'))).toBe(true);
1016
+ await app.close();
1017
+ });
1018
+ // ─── AC-15.4.4 ───
1019
+ it('AC-15.4.4: zip with no module-shaped contents returns 422', async () => {
1020
+ const zipBytes = await makeNoModuleZip();
1021
+ const { payload, headers } = makeMultipartPayload(zipBytes);
1022
+ const app = await createTestApp();
1023
+ const resp = await app.inject({
1024
+ method: 'POST',
1025
+ url: '/api/modules/install/upload',
1026
+ payload,
1027
+ headers,
1028
+ });
1029
+ expect(resp.statusCode).toBe(422);
1030
+ expect(JSON.parse(resp.body).error.message).toContain('does not look like a BMAD module');
1031
+ await app.close();
1032
+ });
1033
+ // ─── AC-15.4.6 (cleanup on failure) ───
1034
+ it('AC-15.4.6: failed upload cleans up tmp directories', async () => {
1035
+ const zipBytes = await makeNoModuleZip();
1036
+ const { payload, headers } = makeMultipartPayload(zipBytes);
1037
+ const before = fs.readdirSync(os.tmpdir()).filter((n) => n.startsWith('bmad-zip-')).length;
1038
+ const app = await createTestApp();
1039
+ const resp = await app.inject({
1040
+ method: 'POST',
1041
+ url: '/api/modules/install/upload',
1042
+ payload,
1043
+ headers,
1044
+ });
1045
+ expect(resp.statusCode).toBe(422);
1046
+ const after = fs.readdirSync(os.tmpdir()).filter((n) => n.startsWith('bmad-zip-')).length;
1047
+ expect(after).toBeLessThanOrEqual(before);
1048
+ await app.close();
1049
+ });
1050
+ // ─── AC-15.4.9 (manifest guard runs before zip extraction) ───
1051
+ it('AC-15.4.9: missing manifest.yaml prevents zip extraction', async () => {
1052
+ fs.unlinkSync(path.join(tmpDir, '_bmad', '_config', 'manifest.yaml'));
1053
+ const zipBytes = await makeValidZip();
1054
+ const { payload, headers } = makeMultipartPayload(zipBytes);
1055
+ const before = fs.readdirSync(os.tmpdir()).filter((n) => n.startsWith('bmad-zip-')).length;
1056
+ const app = await createTestApp();
1057
+ const resp = await app.inject({
1058
+ method: 'POST',
1059
+ url: '/api/modules/install/upload',
1060
+ payload,
1061
+ headers,
1062
+ });
1063
+ expect(resp.statusCode).toBe(422);
1064
+ expect(JSON.parse(resp.body).error.message).toBe('Cannot install module: missing _bmad/_config/manifest.yaml. Run `npx bmad-method install` to initialise the project first.');
1065
+ // No new bmad-zip-* dirs created — extraction never happened
1066
+ const after = fs.readdirSync(os.tmpdir()).filter((n) => n.startsWith('bmad-zip-')).length;
1067
+ expect(after).toBeLessThanOrEqual(before);
1068
+ await app.close();
1069
+ });
1070
+ // ─── AC-15.4.10 / Story 17.3 — re-install returns 200 (clean-slate) ───
1071
+ it('AC-15.4.10: re-install of an existing zip module returns 200 (clean-slate)', async () => {
1072
+ const zipBytes = await makeValidZip();
1073
+ const { payload: p1, headers: h1 } = makeMultipartPayload(zipBytes);
1074
+ const app = await createTestApp();
1075
+ const first = await app.inject({
1076
+ method: 'POST',
1077
+ url: '/api/modules/install/upload',
1078
+ payload: p1,
1079
+ headers: h1,
1080
+ });
1081
+ expect(first.statusCode).toBe(200);
1082
+ const { payload: p2, headers: h2 } = makeMultipartPayload(zipBytes);
1083
+ const second = await app.inject({
1084
+ method: 'POST',
1085
+ url: '/api/modules/install/upload',
1086
+ payload: p2,
1087
+ headers: h2,
1088
+ });
1089
+ expect(second.statusCode).toBe(200);
1090
+ expect(JSON.parse(second.body).ok).toBe(true);
1091
+ await app.close();
1092
+ });
1093
+ // ─── AC-15.4.11 (multipart to JSON endpoint is rejected cleanly) ───
1094
+ it('AC-15.4.11: multipart request to /api/modules/install (JSON route) is rejected by the body guard', async () => {
1095
+ const zipBytes = await makeValidZip();
1096
+ const { payload, headers } = makeMultipartPayload(zipBytes);
1097
+ const app = await createTestApp();
1098
+ const resp = await app.inject({
1099
+ method: 'POST',
1100
+ url: '/api/modules/install', // NOT /upload — the wrong endpoint
1101
+ payload,
1102
+ headers,
1103
+ });
1104
+ // The body guard at the top of the JSON handler catches the missing/non-object body
1105
+ // (multipart bodies don't deserialize as JSON) and throws a clean ValidationError.
1106
+ expect(resp.statusCode).toBe(422);
1107
+ expect(JSON.parse(resp.body).error.message).toContain('For zip uploads, POST to /api/modules/install/upload instead');
1108
+ // Verify no module was installed via this misuse
1109
+ expect(fs.existsSync(path.join(tmpDir, '_bmad', 'zip-test'))).toBe(false);
1110
+ await app.close();
1111
+ });
1112
+ // ─── AC-15.4.12 (missing file field returns 422) ───
1113
+ it('AC-15.4.12: multipart with no file field returns 422', async () => {
1114
+ const form = new FormData();
1115
+ form.append('other', 'not-a-file');
1116
+ const payload = form.getBuffer();
1117
+ const headers = form.getHeaders();
1118
+ const app = await createTestApp();
1119
+ const resp = await app.inject({
1120
+ method: 'POST',
1121
+ url: '/api/modules/install/upload',
1122
+ payload,
1123
+ headers,
1124
+ });
1125
+ expect(resp.statusCode).toBe(422);
1126
+ expect(JSON.parse(resp.body).error.message).toBe('No zip file uploaded');
1127
+ await app.close();
1128
+ });
1129
+ // ─── AC-15.4.7 (zip-slip integration test) ───
1130
+ it('AC-15.4.7: zip-slip attack is rejected at the upload endpoint', async () => {
1131
+ // Build a malicious zip (same trick as the unit test — mutate entryName after addFile)
1132
+ const AdmZip = (await import('adm-zip')).default;
1133
+ const evilZip = new AdmZip();
1134
+ evilZip.addFile('escape.txt', Buffer.from('PWNED'));
1135
+ evilZip.addFile('agents/test.md', Buffer.from('# Test\n'));
1136
+ const escapeEntry = evilZip.getEntries().find((e) => e.entryName === 'escape.txt');
1137
+ if (!escapeEntry)
1138
+ throw new Error('test setup failed');
1139
+ escapeEntry.entryName = '../escape.txt';
1140
+ const maliciousZipBytes = evilZip.toBuffer();
1141
+ const { payload, headers } = makeMultipartPayload(maliciousZipBytes, 'malicious.zip');
1142
+ const app = await createTestApp();
1143
+ const resp = await app.inject({
1144
+ method: 'POST',
1145
+ url: '/api/modules/install/upload',
1146
+ payload,
1147
+ headers,
1148
+ });
1149
+ expect(resp.statusCode).toBe(422);
1150
+ expect(JSON.parse(resp.body).error.message).toContain('attempts to write outside');
1151
+ // Verify no escape.txt landed in the project root or _bmad/
1152
+ expect(fs.existsSync(path.join(tmpDir, 'escape.txt'))).toBe(false);
1153
+ expect(fs.existsSync(path.join(tmpDir, '_bmad', 'escape.txt'))).toBe(false);
1154
+ await app.close();
1155
+ });
1156
+ // ─── AC-15.4.1 / AC-15.4.8 — size cap constant assertion ───
1157
+ // The actual 50 MB cap is enforced by @fastify/multipart, which is well-tested upstream.
1158
+ // We assert the constant value here as a regression guard against accidental edits.
1159
+ it('AC-15.4.1/8: MAX_MODULE_UPLOAD_BYTES is 50 MB', () => {
1160
+ expect(MAX_MODULE_UPLOAD_BYTES).toBe(50 * 1024 * 1024);
1161
+ });
1162
+ // Manual end-to-end size-cap test — building a 51 MB Buffer in vitest is wasteful
1163
+ // (~200ms + 51 MB RAM per test run) and the multipart plugin's behavior is upstream.
1164
+ it.skip('manual: a 51 MB upload is rejected by @fastify/multipart', async () => {
1165
+ // To run this manually, unskip and uncomment:
1166
+ // const oversize = Buffer.alloc(51 * 1024 * 1024)
1167
+ // const { payload, headers } = makeMultipartPayload(oversize, 'big.zip')
1168
+ // const app = await createTestApp()
1169
+ // const resp = await app.inject({ method: 'POST', url: '/api/modules/install/upload', payload, headers })
1170
+ // expect(resp.statusCode).toBeGreaterThanOrEqual(400)
1171
+ // await app.close()
1172
+ });
1173
+ });
1174
+ // ─────────────────────────────────────────────────────────────────────────────
1175
+ // Story 15.5 — Variable substitution pass (integration tests via the install endpoint)
1176
+ // ─────────────────────────────────────────────────────────────────────────────
1177
+ describe('modules-plugin — Story 15.5 variable substitution', () => {
1178
+ let tmpDir;
1179
+ let sourceParent;
1180
+ beforeEach(() => {
1181
+ tmpDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'modules-plugin-15-5-')));
1182
+ sourceParent = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'modules-plugin-15-5-src-')));
1183
+ const configDir = path.join(tmpDir, '_bmad', '_config');
1184
+ fs.mkdirSync(configDir, { recursive: true });
1185
+ fs.writeFileSync(path.join(configDir, 'manifest.yaml'), yaml.dump(makeManifest([])));
1186
+ fs.mkdirSync(path.join(tmpDir, '.bmad-studio'), { recursive: true });
1187
+ });
1188
+ afterEach(() => {
1189
+ fs.rmSync(tmpDir, { recursive: true, force: true });
1190
+ fs.rmSync(sourceParent, { recursive: true, force: true });
1191
+ });
1192
+ function createTestApp() {
1193
+ return createApp({
1194
+ logger: false,
1195
+ serveStatic: false,
1196
+ project: {
1197
+ projectRoot: tmpDir,
1198
+ bmadVersion: '6.2.0',
1199
+ versionSupported: true,
1200
+ modules: [],
1201
+ ideDirectories: [],
1202
+ },
1203
+ });
1204
+ }
1205
+ function makeSourceModule(code, files) {
1206
+ const sourceDir = path.join(sourceParent, code);
1207
+ fs.mkdirSync(sourceDir, { recursive: true });
1208
+ fs.writeFileSync(path.join(sourceDir, 'module.yaml'), `code: ${code}\nversion: "1.0.0"\n`);
1209
+ fs.mkdirSync(path.join(sourceDir, 'agents'), { recursive: true });
1210
+ for (const [relPath, content] of Object.entries(files)) {
1211
+ const full = path.join(sourceDir, relPath);
1212
+ fs.mkdirSync(path.dirname(full), { recursive: true });
1213
+ fs.writeFileSync(full, content);
1214
+ }
1215
+ return sourceDir;
1216
+ }
1217
+ // ─── AC-15.5.1 ───
1218
+ it('AC-15.5.1: substitutes {{var}} in installed file from local source', async () => {
1219
+ const sourceDir = makeSourceModule('sub-test-1', {
1220
+ 'agents/greeting.md': 'Hello from {{project_name}}!\n',
1221
+ });
1222
+ const app = await createTestApp();
1223
+ const resp = await app.inject({
1224
+ method: 'POST',
1225
+ url: '/api/modules/install',
1226
+ payload: {
1227
+ source: { type: 'local', value: sourceDir },
1228
+ variables: { project_name: 'AcmeProject' },
1229
+ },
1230
+ });
1231
+ expect(resp.statusCode).toBe(200);
1232
+ const installed = fs.readFileSync(path.join(tmpDir, '_bmad', 'sub-test-1', 'agents', 'greeting.md'), 'utf-8');
1233
+ expect(installed).toBe('Hello from AcmeProject!\n');
1234
+ await app.close();
1235
+ });
1236
+ // ─── AC-15.5.2 + 15.5.3 (combined — both static placeholders in one file) ───
1237
+ it('AC-15.5.2/3: substitutes {project-root} and {module-code} in installed files', async () => {
1238
+ const sourceDir = makeSourceModule('sub-test-2', {
1239
+ 'agents/info.yaml': 'project_root: "{project-root}"\nmodule_code: "{module-code}"\n',
1240
+ });
1241
+ const app = await createTestApp();
1242
+ const resp = await app.inject({
1243
+ method: 'POST',
1244
+ url: '/api/modules/install',
1245
+ payload: { source: { type: 'local', value: sourceDir } },
1246
+ });
1247
+ expect(resp.statusCode).toBe(200);
1248
+ const installed = fs.readFileSync(path.join(tmpDir, '_bmad', 'sub-test-2', 'agents', 'info.yaml'), 'utf-8');
1249
+ expect(installed).toBe(`project_root: "${tmpDir}"\nmodule_code: "sub-test-2"\n`);
1250
+ await app.close();
1251
+ });
1252
+ // ─── AC-15.5.5 — no spurious snapshots for files with no placeholders ───
1253
+ it('AC-15.5.5: a file with no placeholders is not re-written', async () => {
1254
+ const sourceDir = makeSourceModule('sub-test-noop', {
1255
+ 'agents/plain.md': '# Plain agent\nNo placeholders here.\n',
1256
+ });
1257
+ const historyDir = path.join(tmpDir, '.bmad-studio', 'history');
1258
+ const before = fs.existsSync(historyDir) ? fs.readdirSync(historyDir).length : 0;
1259
+ const app = await createTestApp();
1260
+ const resp = await app.inject({
1261
+ method: 'POST',
1262
+ url: '/api/modules/install',
1263
+ payload: { source: { type: 'local', value: sourceDir } },
1264
+ });
1265
+ expect(resp.statusCode).toBe(200);
1266
+ // The file should be byte-identical to the source (no substitution)
1267
+ const installed = fs.readFileSync(path.join(tmpDir, '_bmad', 'sub-test-noop', 'agents', 'plain.md'), 'utf-8');
1268
+ expect(installed).toBe('# Plain agent\nNo placeholders here.\n');
1269
+ // Snapshots: any new history entries should be from the install (manifest write
1270
+ // is the only one expected — copy of new files has snapshotPath: null).
1271
+ // The plain.md file should NOT have a snapshot since it wasn't re-written by substitution.
1272
+ const after = fs.existsSync(historyDir) ? fs.readdirSync(historyDir) : [];
1273
+ const plainSnapshots = after.filter((n) => n.endsWith('plain.md'));
1274
+ expect(plainSnapshots).toEqual([]);
1275
+ void before; // satisfy noUnusedLocals
1276
+ await app.close();
1277
+ });
1278
+ // ─── AC-15.5.8 — invalid variables fail fast ───
1279
+ it('AC-15.5.8: invalid variable values return 422 BEFORE any files are copied', async () => {
1280
+ const sourceDir = makeSourceModule('sub-test-bad', {
1281
+ 'agents/test.md': 'placeholder {{name}}\n',
1282
+ });
1283
+ const app = await createTestApp();
1284
+ const resp = await app.inject({
1285
+ method: 'POST',
1286
+ url: '/api/modules/install',
1287
+ payload: {
1288
+ source: { type: 'local', value: sourceDir },
1289
+ variables: { name: '# bad value' },
1290
+ },
1291
+ });
1292
+ expect(resp.statusCode).toBe(422);
1293
+ expect(JSON.parse(resp.body).error.message).toContain('"name"');
1294
+ expect(JSON.parse(resp.body).error.message).toContain('# bad value');
1295
+ // Critical: NO files should have been copied — the validation runs BEFORE source-type branching.
1296
+ expect(fs.existsSync(path.join(tmpDir, '_bmad', 'sub-test-bad'))).toBe(false);
1297
+ await app.close();
1298
+ });
1299
+ // ─── AC-15.5.9 — values with allowed chars work ───
1300
+ it('AC-15.5.9: variable values with allowed chars are substituted verbatim', async () => {
1301
+ const sourceDir = makeSourceModule('sub-test-allowed', {
1302
+ 'agents/info.md': 'project: {{project}}\n',
1303
+ });
1304
+ const app = await createTestApp();
1305
+ const resp = await app.inject({
1306
+ method: 'POST',
1307
+ url: '/api/modules/install',
1308
+ payload: {
1309
+ source: { type: 'local', value: sourceDir },
1310
+ variables: { project: 'my-project_v1.0/aem' },
1311
+ },
1312
+ });
1313
+ expect(resp.statusCode).toBe(200);
1314
+ const installed = fs.readFileSync(path.join(tmpDir, '_bmad', 'sub-test-allowed', 'agents', 'info.md'), 'utf-8');
1315
+ expect(installed).toBe('project: my-project_v1.0/aem\n');
1316
+ await app.close();
1317
+ });
1318
+ // ─── AC-15.6.8 — install response includes skillsGenerated counts when ides are configured ───
1319
+ it('AC-15.6.8: install response includes skillsGenerated when manifest.ides is set', async () => {
1320
+ // Override the beforeEach manifest to include ides
1321
+ fs.writeFileSync(path.join(tmpDir, '_bmad', '_config', 'manifest.yaml'), yaml.dump({
1322
+ installation: {
1323
+ version: '6.2.0',
1324
+ installDate: '2026-01-01T00:00:00.000Z',
1325
+ lastUpdated: '2026-01-01T00:00:00.000Z',
1326
+ },
1327
+ modules: [],
1328
+ ides: ['claude-code', 'antigravity'],
1329
+ }));
1330
+ // Build a fixture with one agent and one workflow (workflow uses **Goal:** format)
1331
+ const sourceDir = path.join(sourceParent, 'gen-test');
1332
+ fs.mkdirSync(path.join(sourceDir, 'agents'), { recursive: true });
1333
+ fs.mkdirSync(path.join(sourceDir, 'workflows', 'do-thing'), { recursive: true });
1334
+ fs.writeFileSync(path.join(sourceDir, 'module.yaml'), 'code: gen-test\nversion: "1.0.0"\n');
1335
+ fs.writeFileSync(path.join(sourceDir, 'agents', 'helper.md'), '<agent id="helper" name="helper" title="Helper" capabilities="help">\n</agent>\n');
1336
+ fs.writeFileSync(path.join(sourceDir, 'workflows', 'do-thing', 'workflow.md'), '---\nname: do-thing\n---\n# Do Thing\n\n**Goal:** Does the thing\n');
1337
+ const app = await createTestApp();
1338
+ const resp = await app.inject({
1339
+ method: 'POST',
1340
+ url: '/api/modules/install',
1341
+ payload: { source: { type: 'local', value: sourceDir } },
1342
+ });
1343
+ expect(resp.statusCode).toBe(200);
1344
+ const body = JSON.parse(resp.body);
1345
+ // Per-IDE counts: 1 agent + 1 workflow = 2 launchers per IDE
1346
+ expect(body.skillsGenerated).toEqual({
1347
+ 'claude-code': 2,
1348
+ antigravity: 2,
1349
+ });
1350
+ // Verify the launchers exist on disk
1351
+ expect(fs.existsSync(path.join(tmpDir, '.claude/skills/bmad-agent-gen-test-helper/SKILL.md'))).toBe(true);
1352
+ expect(fs.existsSync(path.join(tmpDir, '.antigravity/skills/bmad-agent-gen-test-helper/SKILL.md'))).toBe(true);
1353
+ expect(fs.existsSync(path.join(tmpDir, '.claude/skills/bmad-gen-test-do-thing/SKILL.md'))).toBe(true);
1354
+ expect(fs.existsSync(path.join(tmpDir, '.antigravity/skills/bmad-gen-test-do-thing/SKILL.md'))).toBe(true);
1355
+ await app.close();
1356
+ });
1357
+ // ─── AC-15.5.4 — binary files are NOT substituted ───
1358
+ it('AC-15.5.4: binary files in the module are left unchanged', async () => {
1359
+ const sourceDir = makeSourceModule('sub-test-bin', {
1360
+ 'agents/test.md': 'just text\n',
1361
+ });
1362
+ // Add a binary file with PNG header bytes (deliberately invalid utf-8)
1363
+ fs.mkdirSync(path.join(sourceDir, 'assets'), { recursive: true });
1364
+ const originalPng = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
1365
+ fs.writeFileSync(path.join(sourceDir, 'assets', 'logo.png'), originalPng);
1366
+ const app = await createTestApp();
1367
+ const resp = await app.inject({
1368
+ method: 'POST',
1369
+ url: '/api/modules/install',
1370
+ payload: { source: { type: 'local', value: sourceDir } },
1371
+ });
1372
+ expect(resp.statusCode).toBe(200);
1373
+ const installedPng = fs.readFileSync(path.join(tmpDir, '_bmad', 'sub-test-bin', 'assets', 'logo.png'));
1374
+ expect(Buffer.compare(installedPng, originalPng)).toBe(0);
1375
+ await app.close();
1376
+ });
1377
+ });
1378
+ // ─────────────────────────────────────────────────────────────────────────────
1379
+ // Story 15.7 — Remove flow (preview endpoint + rich DELETE)
1380
+ // ─────────────────────────────────────────────────────────────────────────────
1381
+ describe('modules-plugin — Story 15.7 remove flow', () => {
1382
+ let tmpDir;
1383
+ beforeEach(() => {
1384
+ tmpDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'modules-plugin-15-7-')));
1385
+ const configDir = path.join(tmpDir, '_bmad', '_config');
1386
+ fs.mkdirSync(configDir, { recursive: true });
1387
+ fs.mkdirSync(path.join(tmpDir, '.bmad-studio'), { recursive: true });
1388
+ });
1389
+ afterEach(() => {
1390
+ fs.rmSync(tmpDir, { recursive: true, force: true });
1391
+ });
1392
+ function writeManifest(modules, ides = []) {
1393
+ fs.writeFileSync(path.join(tmpDir, '_bmad', '_config', 'manifest.yaml'), yaml.dump({
1394
+ installation: {
1395
+ version: '6.2.0',
1396
+ installDate: '2026-01-01T00:00:00.000Z',
1397
+ lastUpdated: '2026-01-01T00:00:00.000Z',
1398
+ },
1399
+ modules: modules.map((m) => ({
1400
+ name: m.name,
1401
+ version: m.version ?? '1.0.0',
1402
+ installDate: '2026-01-01T00:00:00.000Z',
1403
+ lastUpdated: '2026-01-01T00:00:00.000Z',
1404
+ source: m.source,
1405
+ npmPackage: null,
1406
+ repoUrl: null,
1407
+ })),
1408
+ ides,
1409
+ }));
1410
+ }
1411
+ function createTestApp() {
1412
+ return createApp({
1413
+ logger: false,
1414
+ serveStatic: false,
1415
+ project: {
1416
+ projectRoot: tmpDir,
1417
+ bmadVersion: '6.2.0',
1418
+ versionSupported: true,
1419
+ modules: [],
1420
+ ideDirectories: [],
1421
+ },
1422
+ });
1423
+ }
1424
+ // Helper: write a simple module on disk (no module.yaml → TD-14 fallback)
1425
+ function seedModule(code, source = 'custom') {
1426
+ const moduleDir = path.join(tmpDir, '_bmad', code);
1427
+ fs.mkdirSync(path.join(moduleDir, 'agents'), { recursive: true });
1428
+ fs.writeFileSync(path.join(moduleDir, 'agents', 'a.md'), `<agent id="${code}-a" name="a" title="${code} agent" capabilities="work"></agent>\n`);
1429
+ fs.writeFileSync(path.join(moduleDir, 'config.yaml'), `project_name: ${code}\n`);
1430
+ writeManifest([{ name: code, source }]);
1431
+ }
1432
+ // Helper: write a module WITH a module.yaml declaring preserved directories
1433
+ function seedModuleWithYaml(code, directories) {
1434
+ const moduleDir = path.join(tmpDir, '_bmad', code);
1435
+ fs.mkdirSync(path.join(moduleDir, 'agents'), { recursive: true });
1436
+ fs.writeFileSync(path.join(moduleDir, 'agents', 'a.md'), '# A\n');
1437
+ fs.writeFileSync(path.join(moduleDir, 'module.yaml'), `code: ${code}\nversion: "1.0.0"\ndirectories:\n${directories.map((d) => ` - "${d}"`).join('\n')}\n`);
1438
+ writeManifest([{ name: code, source: 'custom' }]);
1439
+ // Create the preserved directories with sentinel files
1440
+ for (const dir of directories) {
1441
+ const resolved = path.isAbsolute(dir) ? dir : path.join(tmpDir, dir);
1442
+ fs.mkdirSync(resolved, { recursive: true });
1443
+ fs.writeFileSync(path.join(resolved, 'user-work.txt'), 'important user content\n');
1444
+ }
1445
+ }
1446
+ // ─── AC-15.7.4 — preview response shape ───
1447
+ it('AC-15.7.4: GET /remove-preview returns all required fields', async () => {
1448
+ seedModule('preview-test');
1449
+ const app = await createTestApp();
1450
+ const resp = await app.inject({
1451
+ method: 'GET',
1452
+ url: '/api/modules/preview-test/remove-preview',
1453
+ });
1454
+ expect(resp.statusCode).toBe(200);
1455
+ const body = JSON.parse(resp.body);
1456
+ expect(body.module).toEqual({ name: 'preview-test', version: '1.0.0', source: 'custom' });
1457
+ expect(body.moduleFiles).toBeDefined();
1458
+ expect(body.moduleFiles.count).toBeGreaterThan(0);
1459
+ expect(body.moduleFiles.totalBytes).toBeGreaterThan(0);
1460
+ expect(body.ideSkills).toBeDefined();
1461
+ expect(body.manifestEntries).toEqual({ 'manifest.yaml': true });
1462
+ expect(body.preservedDirectories).toEqual([]);
1463
+ expect(body.moduleYamlPresent).toBe(false);
1464
+ expect(body.crossReferences).toEqual([]);
1465
+ expect(body.crossReferenceScopeNotice).toContain('Cross-reference scanning covers');
1466
+ expect(body.recoverableFrom).toBe('.bmad-studio/history/');
1467
+ expect(body.removalBlocked).toBeNull();
1468
+ expect(body.externalInstallerWarning).toBeNull();
1469
+ await app.close();
1470
+ });
1471
+ // ─── AC-15.7.5 — built-in removalBlocked ───
1472
+ it('AC-15.7.5: built-in module sets removalBlocked to a non-empty string', async () => {
1473
+ seedModule('core', 'built-in');
1474
+ const app = await createTestApp();
1475
+ const resp = await app.inject({
1476
+ method: 'GET',
1477
+ url: '/api/modules/core/remove-preview',
1478
+ });
1479
+ expect(resp.statusCode).toBe(200);
1480
+ const body = JSON.parse(resp.body);
1481
+ expect(body.removalBlocked).toContain('cannot be removed');
1482
+ await app.close();
1483
+ });
1484
+ // ─── AC-15.7.6 — external installer warning ───
1485
+ it('AC-15.7.6: external module sets externalInstallerWarning', async () => {
1486
+ seedModule('bmb', 'external');
1487
+ const app = await createTestApp();
1488
+ const resp = await app.inject({
1489
+ method: 'GET',
1490
+ url: '/api/modules/bmb/remove-preview',
1491
+ });
1492
+ expect(resp.statusCode).toBe(200);
1493
+ const body = JSON.parse(resp.body);
1494
+ expect(body.externalInstallerWarning).toContain('BMAD installer');
1495
+ await app.close();
1496
+ });
1497
+ // ─── AC-15.7.7 — preservedDirectories ───
1498
+ it('AC-15.7.7: preservedDirectories lists declared output dirs that exist', async () => {
1499
+ seedModuleWithYaml('preserve-test', ['_bmad-output/preserve-artifacts']);
1500
+ const app = await createTestApp();
1501
+ const resp = await app.inject({
1502
+ method: 'GET',
1503
+ url: '/api/modules/preserve-test/remove-preview',
1504
+ });
1505
+ expect(resp.statusCode).toBe(200);
1506
+ const body = JSON.parse(resp.body);
1507
+ expect(body.moduleYamlPresent).toBe(true);
1508
+ expect(body.preservedDirectories).toHaveLength(1);
1509
+ expect(body.preservedDirectories[0].path).toBe(path.join(tmpDir, '_bmad-output/preserve-artifacts'));
1510
+ expect(body.preservedDirectories[0].declared).toBe(true);
1511
+ await app.close();
1512
+ });
1513
+ // ─── AC-15.7.8a — cross-reference scope ───
1514
+ it('AC-15.7.8a: cross-references detect teams referencing target-module agents', async () => {
1515
+ // Build two modules: target has an agent, other has a team referencing it
1516
+ const targetDir = path.join(tmpDir, '_bmad', 'target-mod', 'agents');
1517
+ fs.mkdirSync(targetDir, { recursive: true });
1518
+ fs.writeFileSync(path.join(targetDir, 'architect.md'), '<agent id="architect" name="architect" title="Architect" capabilities="design"></agent>\n');
1519
+ const otherDir = path.join(tmpDir, '_bmad', 'other-mod', 'teams');
1520
+ fs.mkdirSync(otherDir, { recursive: true });
1521
+ fs.writeFileSync(path.join(otherDir, 'design-team.yaml'), 'bundle:\n name: Design Team\n icon: 🎨\n description: refs target\nagents:\n - architect\n');
1522
+ writeManifest([
1523
+ { name: 'target-mod', source: 'custom' },
1524
+ { name: 'other-mod', source: 'custom' },
1525
+ ]);
1526
+ const app = await createTestApp();
1527
+ const resp = await app.inject({
1528
+ method: 'GET',
1529
+ url: '/api/modules/target-mod/remove-preview',
1530
+ });
1531
+ expect(resp.statusCode).toBe(200);
1532
+ const body = JSON.parse(resp.body);
1533
+ expect(body.crossReferences.length).toBeGreaterThanOrEqual(1);
1534
+ const otherRef = body.crossReferences.find((r) => r.ownerModule === 'other-mod');
1535
+ expect(otherRef).toBeDefined();
1536
+ expect(otherRef.reason).toContain('team');
1537
+ expect(otherRef.reason).toContain('design-team');
1538
+ await app.close();
1539
+ });
1540
+ // ─── AC-15.7.8c — scope notice field ───
1541
+ it('AC-15.7.8c: response includes crossReferenceScopeNotice field', async () => {
1542
+ seedModule('scope-notice-test');
1543
+ const app = await createTestApp();
1544
+ const resp = await app.inject({
1545
+ method: 'GET',
1546
+ url: '/api/modules/scope-notice-test/remove-preview',
1547
+ });
1548
+ expect(resp.statusCode).toBe(200);
1549
+ const body = JSON.parse(resp.body);
1550
+ expect(body.crossReferenceScopeNotice).toBe('Cross-reference scanning covers teams and workflow steps. References from agent menus or skill lists are not detected — review the affected modules manually after removal.');
1551
+ await app.close();
1552
+ });
1553
+ // ─── Preview: IDE skills listing ───
1554
+ it('preview lists prefix-matched IDE skill directories', async () => {
1555
+ seedModule('ide-skills-test');
1556
+ // Write manifest with ides array
1557
+ writeManifest([{ name: 'ide-skills-test', source: 'custom' }], ['claude-code', 'antigravity']);
1558
+ // Manually create some IDE skill dirs mimicking a prior install
1559
+ fs.mkdirSync(path.join(tmpDir, '.claude/skills/bmad-agent-ide-skills-test-a'), { recursive: true });
1560
+ fs.writeFileSync(path.join(tmpDir, '.claude/skills/bmad-agent-ide-skills-test-a/SKILL.md'), 'skill');
1561
+ fs.mkdirSync(path.join(tmpDir, '.claude/skills/bmad-other-mod-foo'), { recursive: true });
1562
+ const app = await createTestApp();
1563
+ const resp = await app.inject({
1564
+ method: 'GET',
1565
+ url: '/api/modules/ide-skills-test/remove-preview',
1566
+ });
1567
+ expect(resp.statusCode).toBe(200);
1568
+ const body = JSON.parse(resp.body);
1569
+ expect(body.ideSkills['claude-code']).toEqual(['bmad-agent-ide-skills-test-a']);
1570
+ // The other-mod skill is NOT listed (prefix mismatch)
1571
+ expect(body.ideSkills['claude-code']).not.toContain('bmad-other-mod-foo');
1572
+ expect(body.ideSkills.antigravity).toEqual([]);
1573
+ await app.close();
1574
+ });
1575
+ // ─── DELETE: rich summary response ───
1576
+ it('AC-15.7.14: DELETE returns rich summary with removed counts and preservedDirectories', async () => {
1577
+ seedModuleWithYaml('delete-test', ['_bmad-output/delete-artifacts']);
1578
+ const app = await createTestApp();
1579
+ const resp = await app.inject({
1580
+ method: 'DELETE',
1581
+ url: '/api/modules/delete-test',
1582
+ });
1583
+ expect(resp.statusCode).toBe(200);
1584
+ const body = JSON.parse(resp.body);
1585
+ expect(body.ok).toBe(true);
1586
+ expect(body.name).toBe('delete-test');
1587
+ expect(body.removed.filesRemoved).toBeGreaterThan(0);
1588
+ expect(body.removed.skills).toEqual({});
1589
+ expect(body.preservedDirectories).toHaveLength(1);
1590
+ expect(body.recoverableFrom).toBe('.bmad-studio/history/');
1591
+ // Module dir gone
1592
+ expect(fs.existsSync(path.join(tmpDir, '_bmad', 'delete-test'))).toBe(false);
1593
+ // Preserved directory AND its sentinel file are still on disk
1594
+ const preserved = path.join(tmpDir, '_bmad-output/delete-artifacts');
1595
+ expect(fs.existsSync(preserved)).toBe(true);
1596
+ expect(fs.readFileSync(path.join(preserved, 'user-work.txt'), 'utf-8')).toBe('important user content\n');
1597
+ await app.close();
1598
+ });
1599
+ // ─── AC-15.7.10 — snapshots recoverable ───
1600
+ it('AC-15.7.10: files from the module dir are snapshotted to history/ on delete', async () => {
1601
+ seedModule('snapshot-test');
1602
+ const app = await createTestApp();
1603
+ const resp = await app.inject({
1604
+ method: 'DELETE',
1605
+ url: '/api/modules/snapshot-test',
1606
+ });
1607
+ expect(resp.statusCode).toBe(200);
1608
+ // Every text file from the module dir should have a snapshot in history/
1609
+ const historyDir = path.join(tmpDir, '.bmad-studio/history');
1610
+ const history = fs.readdirSync(historyDir);
1611
+ // We wrote agents/a.md + config.yaml — both should have snapshots
1612
+ expect(history.some((n) => n.endsWith('a.md'))).toBe(true);
1613
+ expect(history.some((n) => n.endsWith('config.yaml'))).toBe(true);
1614
+ await app.close();
1615
+ });
1616
+ // ─── AC-15.7.11 ───
1617
+ it('AC-15.7.11: DELETE on built-in module returns 422', async () => {
1618
+ seedModule('core', 'built-in');
1619
+ const app = await createTestApp();
1620
+ const resp = await app.inject({
1621
+ method: 'DELETE',
1622
+ url: '/api/modules/core',
1623
+ });
1624
+ expect(resp.statusCode).toBe(422);
1625
+ expect(JSON.parse(resp.body).error.message).toContain('Cannot remove built-in module');
1626
+ await app.close();
1627
+ });
1628
+ // ─── AC-15.7.12 ───
1629
+ it('AC-15.7.12: DELETE on non-existent module returns 404', async () => {
1630
+ writeManifest([]);
1631
+ const app = await createTestApp();
1632
+ const resp = await app.inject({
1633
+ method: 'DELETE',
1634
+ url: '/api/modules/ghost',
1635
+ });
1636
+ expect(resp.statusCode).toBe(404);
1637
+ await app.close();
1638
+ });
1639
+ // ─── AC-15.7.9 — preserved directories are NOT deleted ───
1640
+ it('AC-15.7.9: preserved directories survive the delete', async () => {
1641
+ seedModuleWithYaml('preserve-delete-test', [
1642
+ '_bmad-output/artifact-a',
1643
+ '_bmad-output/artifact-b',
1644
+ ]);
1645
+ const app = await createTestApp();
1646
+ await app.inject({ method: 'DELETE', url: '/api/modules/preserve-delete-test' });
1647
+ // Module gone
1648
+ expect(fs.existsSync(path.join(tmpDir, '_bmad', 'preserve-delete-test'))).toBe(false);
1649
+ // Preserved dirs + their content still there
1650
+ expect(fs.existsSync(path.join(tmpDir, '_bmad-output/artifact-a'))).toBe(true);
1651
+ expect(fs.existsSync(path.join(tmpDir, '_bmad-output/artifact-b'))).toBe(true);
1652
+ expect(fs.readFileSync(path.join(tmpDir, '_bmad-output/artifact-a/user-work.txt'), 'utf-8')).toBe('important user content\n');
1653
+ await app.close();
1654
+ });
1655
+ // ─── Remove preview: no-module-yaml case ───
1656
+ it('preview for a module without module.yaml has empty preservedDirectories', async () => {
1657
+ seedModule('no-yaml-test');
1658
+ const app = await createTestApp();
1659
+ const resp = await app.inject({
1660
+ method: 'GET',
1661
+ url: '/api/modules/no-yaml-test/remove-preview',
1662
+ });
1663
+ expect(resp.statusCode).toBe(200);
1664
+ const body = JSON.parse(resp.body);
1665
+ expect(body.moduleYamlPresent).toBe(false);
1666
+ expect(body.preservedDirectories).toEqual([]);
1667
+ await app.close();
1668
+ });
1669
+ });
1670
+ // ─────────────────────────────────────────────────────────────────────────────
1671
+ // Story 15.8 — Regenerate IDE skills endpoint
1672
+ // All tests run against a TEMP project — the Q9 manual smoke test against the
1673
+ // real `dept-aem` in the developer's working tree is documented separately
1674
+ // and intentionally NOT automated (it would dirty the tree).
1675
+ // ─────────────────────────────────────────────────────────────────────────────
1676
+ describe('modules-plugin — Story 15.8 regenerate IDE skills', () => {
1677
+ let tmpDir;
1678
+ beforeEach(() => {
1679
+ tmpDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'modules-plugin-15-8-')));
1680
+ const configDir = path.join(tmpDir, '_bmad', '_config');
1681
+ fs.mkdirSync(configDir, { recursive: true });
1682
+ fs.mkdirSync(path.join(tmpDir, '.bmad-studio'), { recursive: true });
1683
+ });
1684
+ afterEach(() => {
1685
+ fs.rmSync(tmpDir, { recursive: true, force: true });
1686
+ });
1687
+ function writeManifestWithIdes(modules, ides) {
1688
+ fs.writeFileSync(path.join(tmpDir, '_bmad', '_config', 'manifest.yaml'), yaml.dump({
1689
+ installation: {
1690
+ version: '6.2.0',
1691
+ installDate: '2026-01-01T00:00:00.000Z',
1692
+ lastUpdated: '2026-01-01T00:00:00.000Z',
1693
+ },
1694
+ modules: modules.map((m) => ({
1695
+ name: m.name,
1696
+ version: '1.0.0',
1697
+ installDate: '2026-01-01T00:00:00.000Z',
1698
+ lastUpdated: '2026-01-01T00:00:00.000Z',
1699
+ source: m.source,
1700
+ npmPackage: null,
1701
+ repoUrl: null,
1702
+ })),
1703
+ ides,
1704
+ }));
1705
+ }
1706
+ function createTestApp() {
1707
+ return createApp({
1708
+ logger: false,
1709
+ serveStatic: false,
1710
+ project: {
1711
+ projectRoot: tmpDir,
1712
+ bmadVersion: '6.2.0',
1713
+ versionSupported: true,
1714
+ modules: [],
1715
+ ideDirectories: [],
1716
+ },
1717
+ });
1718
+ }
1719
+ // Helper: create an installed module fixture on disk with agents + workflow
1720
+ function seedInstalledModule(code) {
1721
+ const moduleDir = path.join(tmpDir, '_bmad', code);
1722
+ fs.mkdirSync(path.join(moduleDir, 'agents'), { recursive: true });
1723
+ fs.mkdirSync(path.join(moduleDir, 'workflows', 'do-thing'), { recursive: true });
1724
+ fs.writeFileSync(path.join(moduleDir, 'agents', 'helper.md'), '<agent id="helper" name="helper" title="Helper" capabilities="help"></agent>\n');
1725
+ fs.writeFileSync(path.join(moduleDir, 'workflows', 'do-thing', 'workflow.md'), '---\nname: do-thing\n---\n# Do Thing\n\n**Goal:** Does the thing\n');
1726
+ }
1727
+ // ─── AC-15.8.1 ───
1728
+ it('AC-15.8.1: regenerates IDE skills for an installed module with no prior launchers', async () => {
1729
+ seedInstalledModule('regen-test');
1730
+ writeManifestWithIdes([{ name: 'regen-test', source: 'custom' }], ['claude-code']);
1731
+ // Verify no launchers exist beforehand
1732
+ expect(fs.existsSync(path.join(tmpDir, '.claude/skills'))).toBe(false);
1733
+ const app = await createTestApp();
1734
+ const resp = await app.inject({
1735
+ method: 'POST',
1736
+ url: '/api/modules/regen-test/regenerate-skills',
1737
+ });
1738
+ expect(resp.statusCode).toBe(200);
1739
+ const body = JSON.parse(resp.body);
1740
+ expect(body.ok).toBe(true);
1741
+ // 1 agent + 1 workflow = 2 launchers
1742
+ expect(body.regenerated).toEqual({ 'claude-code': 2 });
1743
+ // Verify files on disk
1744
+ expect(fs.existsSync(path.join(tmpDir, '.claude/skills/bmad-agent-regen-test-helper/SKILL.md'))).toBe(true);
1745
+ expect(fs.existsSync(path.join(tmpDir, '.claude/skills/bmad-regen-test-do-thing/SKILL.md'))).toBe(true);
1746
+ await app.close();
1747
+ });
1748
+ // ─── AC-15.8.2 ───
1749
+ it('AC-15.8.2: adding antigravity to manifest.ides and re-calling regenerate adds antigravity skills', async () => {
1750
+ seedInstalledModule('regen-multi');
1751
+ writeManifestWithIdes([{ name: 'regen-multi', source: 'custom' }], ['claude-code']);
1752
+ const app = await createTestApp();
1753
+ // First call with only claude-code
1754
+ const r1 = await app.inject({
1755
+ method: 'POST',
1756
+ url: '/api/modules/regen-multi/regenerate-skills',
1757
+ });
1758
+ expect(r1.statusCode).toBe(200);
1759
+ expect(JSON.parse(r1.body).regenerated).toEqual({ 'claude-code': 2 });
1760
+ expect(fs.existsSync(path.join(tmpDir, '.antigravity'))).toBe(false);
1761
+ // Update manifest to add antigravity
1762
+ writeManifestWithIdes([{ name: 'regen-multi', source: 'custom' }], ['claude-code', 'antigravity']);
1763
+ // Second call picks up antigravity
1764
+ const r2 = await app.inject({
1765
+ method: 'POST',
1766
+ url: '/api/modules/regen-multi/regenerate-skills',
1767
+ });
1768
+ expect(r2.statusCode).toBe(200);
1769
+ expect(JSON.parse(r2.body).regenerated).toEqual({
1770
+ 'claude-code': 2,
1771
+ antigravity: 2,
1772
+ });
1773
+ expect(fs.existsSync(path.join(tmpDir, '.antigravity/skills/bmad-agent-regen-multi-helper/SKILL.md'))).toBe(true);
1774
+ await app.close();
1775
+ });
1776
+ // ─── AC-15.8.3 ───
1777
+ it('AC-15.8.3: regenerate re-reads source files so manual edits are picked up', async () => {
1778
+ seedInstalledModule('regen-edit');
1779
+ writeManifestWithIdes([{ name: 'regen-edit', source: 'custom' }], ['claude-code']);
1780
+ const app = await createTestApp();
1781
+ // First call — original helper title
1782
+ await app.inject({
1783
+ method: 'POST',
1784
+ url: '/api/modules/regen-edit/regenerate-skills',
1785
+ });
1786
+ const firstContent = fs.readFileSync(path.join(tmpDir, '.claude/skills/bmad-agent-regen-edit-helper/SKILL.md'), 'utf-8');
1787
+ expect(firstContent).toContain('description: "Helper"');
1788
+ // Manually edit the agent file to change the title
1789
+ fs.writeFileSync(path.join(tmpDir, '_bmad', 'regen-edit', 'agents', 'helper.md'), '<agent id="helper" name="helper" title="Brilliant Helper" capabilities="help"></agent>\n');
1790
+ // Second call — new title should flow through
1791
+ await app.inject({
1792
+ method: 'POST',
1793
+ url: '/api/modules/regen-edit/regenerate-skills',
1794
+ });
1795
+ const secondContent = fs.readFileSync(path.join(tmpDir, '.claude/skills/bmad-agent-regen-edit-helper/SKILL.md'), 'utf-8');
1796
+ expect(secondContent).toContain('description: "Brilliant Helper"');
1797
+ expect(secondContent).not.toContain('description: "Helper"');
1798
+ await app.close();
1799
+ });
1800
+ // ─── AC-15.8.4 ───
1801
+ it('AC-15.8.4: regenerate on non-existent module returns 404', async () => {
1802
+ writeManifestWithIdes([], ['claude-code']);
1803
+ const app = await createTestApp();
1804
+ const resp = await app.inject({
1805
+ method: 'POST',
1806
+ url: '/api/modules/ghost-mod/regenerate-skills',
1807
+ });
1808
+ expect(resp.statusCode).toBe(404);
1809
+ expect(JSON.parse(resp.body).error.message).toContain('not installed');
1810
+ await app.close();
1811
+ });
1812
+ // ─── Idempotent regenerate (calling twice produces byte-identical files) ───
1813
+ it('regenerate is idempotent: calling twice produces byte-identical files', async () => {
1814
+ seedInstalledModule('regen-idem');
1815
+ writeManifestWithIdes([{ name: 'regen-idem', source: 'custom' }], ['claude-code']);
1816
+ const app = await createTestApp();
1817
+ await app.inject({
1818
+ method: 'POST',
1819
+ url: '/api/modules/regen-idem/regenerate-skills',
1820
+ });
1821
+ const skillPath = path.join(tmpDir, '.claude/skills/bmad-agent-regen-idem-helper/SKILL.md');
1822
+ const firstContent = fs.readFileSync(skillPath, 'utf-8');
1823
+ await app.inject({
1824
+ method: 'POST',
1825
+ url: '/api/modules/regen-idem/regenerate-skills',
1826
+ });
1827
+ const secondContent = fs.readFileSync(skillPath, 'utf-8');
1828
+ expect(secondContent).toBe(firstContent);
1829
+ await app.close();
1830
+ });
1831
+ // ─── Regenerate with no IDE configured is a no-op with empty counts ───
1832
+ it('regenerate with empty manifest.ides returns { regenerated: {} }', async () => {
1833
+ seedInstalledModule('regen-no-ide');
1834
+ writeManifestWithIdes([{ name: 'regen-no-ide', source: 'custom' }], []);
1835
+ const app = await createTestApp();
1836
+ const resp = await app.inject({
1837
+ method: 'POST',
1838
+ url: '/api/modules/regen-no-ide/regenerate-skills',
1839
+ });
1840
+ expect(resp.statusCode).toBe(200);
1841
+ expect(JSON.parse(resp.body)).toEqual({ ok: true, regenerated: {} });
1842
+ expect(fs.existsSync(path.join(tmpDir, '.claude'))).toBe(false);
1843
+ await app.close();
1844
+ });
1845
+ });
1846
+ describe('modules-plugin — Story 15.9 preview-source endpoint', () => {
1847
+ let tmpDir;
1848
+ beforeEach(() => {
1849
+ tmpDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'modules-plugin-15-9-')));
1850
+ const configDir = path.join(tmpDir, '_bmad', '_config');
1851
+ fs.mkdirSync(configDir, { recursive: true });
1852
+ fs.mkdirSync(path.join(tmpDir, '.bmad-studio'), { recursive: true });
1853
+ fs.writeFileSync(path.join(configDir, 'manifest.yaml'), yaml.dump({
1854
+ installation: {
1855
+ version: '6.2.0',
1856
+ installDate: '2026-01-01T00:00:00.000Z',
1857
+ lastUpdated: '2026-01-01T00:00:00.000Z',
1858
+ },
1859
+ modules: [],
1860
+ ides: [],
1861
+ }));
1862
+ });
1863
+ afterEach(() => {
1864
+ fs.rmSync(tmpDir, { recursive: true, force: true });
1865
+ });
1866
+ function createTestApp() {
1867
+ return createApp({
1868
+ logger: false,
1869
+ serveStatic: false,
1870
+ project: {
1871
+ projectRoot: tmpDir,
1872
+ bmadVersion: '6.2.0',
1873
+ versionSupported: true,
1874
+ modules: [],
1875
+ ideDirectories: [],
1876
+ },
1877
+ });
1878
+ }
1879
+ function seedLocalModule(code, opts = {}) {
1880
+ const moduleDir = path.join(tmpDir, 'src-modules', code);
1881
+ fs.mkdirSync(path.join(moduleDir, 'agents'), { recursive: true });
1882
+ fs.mkdirSync(path.join(moduleDir, 'workflows'), { recursive: true });
1883
+ fs.writeFileSync(path.join(moduleDir, 'module.yaml'), yaml.dump({
1884
+ code,
1885
+ name: `${code} Module`,
1886
+ version: '2.0.0',
1887
+ description: 'A test module',
1888
+ ...(opts.variables ? { variables: opts.variables } : {}),
1889
+ }));
1890
+ for (let i = 0; i < (opts.agentCount ?? 1); i++) {
1891
+ fs.writeFileSync(path.join(moduleDir, 'agents', `agent-${i}.md`), `<agent id="agent-${i}" name="agent-${i}" title="Agent ${i}"></agent>\n`);
1892
+ }
1893
+ for (let i = 0; i < (opts.workflowCount ?? 0); i++) {
1894
+ fs.mkdirSync(path.join(moduleDir, 'workflows', `wf-${i}`), { recursive: true });
1895
+ fs.writeFileSync(path.join(moduleDir, 'workflows', `wf-${i}`, 'workflow.md'), `# Workflow ${i}\n`);
1896
+ }
1897
+ return moduleDir;
1898
+ }
1899
+ // ─── AC-15.9.11: local source returns structured preview without modifying _bmad/ ───
1900
+ it('AC-15.9.11: returns preview for local source and does not modify _bmad/', async () => {
1901
+ const srcDir = seedLocalModule('preview-local', { agentCount: 2, workflowCount: 1 });
1902
+ const app = await createTestApp();
1903
+ const resp = await app.inject({
1904
+ method: 'POST',
1905
+ url: '/api/modules/preview-source',
1906
+ payload: { source: { type: 'local', value: srcDir } },
1907
+ });
1908
+ expect(resp.statusCode).toBe(200);
1909
+ const body = JSON.parse(resp.body);
1910
+ expect(body.ok).toBe(true);
1911
+ expect(body.moduleYaml.code).toBe('preview-local');
1912
+ expect(body.moduleYaml.version).toBe('2.0.0');
1913
+ expect(body.counts.agents).toBe(2);
1914
+ expect(body.counts.workflows).toBe(1);
1915
+ expect(body.counts.tasks).toBe(0);
1916
+ expect(body.willReplace).toBe(false);
1917
+ // AC-15.9.11 — read-only: _bmad/ must not have been touched
1918
+ expect(fs.existsSync(path.join(tmpDir, '_bmad', 'preview-local'))).toBe(false);
1919
+ await app.close();
1920
+ });
1921
+ // ─── willReplace=true when module dir already exists ───
1922
+ it('willReplace is true when the module is already installed', async () => {
1923
+ const srcDir = seedLocalModule('already-installed');
1924
+ // Pre-create the dest dir to simulate an existing installation
1925
+ fs.mkdirSync(path.join(tmpDir, '_bmad', 'already-installed', 'agents'), { recursive: true });
1926
+ const app = await createTestApp();
1927
+ const resp = await app.inject({
1928
+ method: 'POST',
1929
+ url: '/api/modules/preview-source',
1930
+ payload: { source: { type: 'local', value: srcDir } },
1931
+ });
1932
+ expect(resp.statusCode).toBe(200);
1933
+ expect(JSON.parse(resp.body).willReplace).toBe(true);
1934
+ await app.close();
1935
+ });
1936
+ // ─── 422 for unsupported zip type ───
1937
+ it('returns 422 for source type "zip"', async () => {
1938
+ const app = await createTestApp();
1939
+ const resp = await app.inject({
1940
+ method: 'POST',
1941
+ url: '/api/modules/preview-source',
1942
+ payload: { source: { type: 'zip', value: 'some.zip' } },
1943
+ });
1944
+ expect(resp.statusCode).toBe(422);
1945
+ expect(JSON.parse(resp.body).error.message).toContain('zip');
1946
+ await app.close();
1947
+ });
1948
+ // ─── 422 for invalid local path ───
1949
+ it('returns 422 when local path is not a plausible module dir', async () => {
1950
+ const emptyDir = path.join(tmpDir, 'empty-dir');
1951
+ fs.mkdirSync(emptyDir, { recursive: true });
1952
+ const app = await createTestApp();
1953
+ const resp = await app.inject({
1954
+ method: 'POST',
1955
+ url: '/api/modules/preview-source',
1956
+ payload: { source: { type: 'local', value: emptyDir } },
1957
+ });
1958
+ expect(resp.statusCode).toBe(422);
1959
+ await app.close();
1960
+ });
1961
+ // ─── variables are returned in moduleYaml ───
1962
+ it('returns moduleYaml.variables when the module.yaml declares them', async () => {
1963
+ const srcDir = seedLocalModule('vars-mod', {
1964
+ variables: { output_folder: { prompt: 'Output folder', default: 'output/default' } },
1965
+ });
1966
+ const app = await createTestApp();
1967
+ const resp = await app.inject({
1968
+ method: 'POST',
1969
+ url: '/api/modules/preview-source',
1970
+ payload: { source: { type: 'local', value: srcDir } },
1971
+ });
1972
+ expect(resp.statusCode).toBe(200);
1973
+ const body = JSON.parse(resp.body);
1974
+ expect(body.moduleYaml.variables).toBeDefined();
1975
+ expect(body.moduleYaml.variables.output_folder.default).toBe('output/default');
1976
+ await app.close();
1977
+ });
1978
+ });
1979
+ // ─────────────────────────────────────────────────────────────────────────────
1980
+ // Story 17.3 — Clean-slate reinstall (local + zip sources)
1981
+ // ─────────────────────────────────────────────────────────────────────────────
1982
+ describe('modules-plugin — Story 17.3 clean-slate reinstall', () => {
1983
+ let tmpDir;
1984
+ let srcDir;
1985
+ beforeEach(() => {
1986
+ tmpDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'modules-17-3-')));
1987
+ srcDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'modules-17-3-src-')));
1988
+ const configDir = path.join(tmpDir, '_bmad', '_config');
1989
+ fs.mkdirSync(configDir, { recursive: true });
1990
+ fs.writeFileSync(path.join(configDir, 'manifest.yaml'), yaml.dump(makeManifest([])));
1991
+ fs.mkdirSync(path.join(tmpDir, '.bmad-studio'), { recursive: true });
1992
+ });
1993
+ afterEach(() => {
1994
+ fs.rmSync(tmpDir, { recursive: true, force: true });
1995
+ fs.rmSync(srcDir, { recursive: true, force: true });
1996
+ });
1997
+ function createTestApp() {
1998
+ return createApp({
1999
+ logger: false,
2000
+ serveStatic: false,
2001
+ project: {
2002
+ projectRoot: tmpDir,
2003
+ bmadVersion: '6.2.0',
2004
+ versionSupported: true,
2005
+ modules: [],
2006
+ ideDirectories: [],
2007
+ },
2008
+ });
2009
+ }
2010
+ function makeLocalModule(code, agentFile) {
2011
+ const dir = path.join(srcDir, code);
2012
+ fs.mkdirSync(path.join(dir, 'agents'), { recursive: true });
2013
+ fs.writeFileSync(path.join(dir, 'agents', agentFile), `---\nname: ${agentFile.replace('.md', '')}\ntitle: Test\n---\n# Test\n`);
2014
+ fs.writeFileSync(dir + '/module.yaml', `code: ${code}\nname: "${code}"\nversion: "1.0.0"\n`);
2015
+ return dir;
2016
+ }
2017
+ // AC-17.3.1: local source reinstall returns 200 (not 409)
2018
+ it('AC-17.3.1: local source reinstall returns 200', async () => {
2019
+ const modDir = makeLocalModule('replace-me', 'v1-agent.md');
2020
+ const app = await createTestApp();
2021
+ const first = await app.inject({
2022
+ method: 'POST',
2023
+ url: '/api/modules/install',
2024
+ payload: { source: { type: 'local', value: modDir } },
2025
+ });
2026
+ expect(first.statusCode).toBe(200);
2027
+ const second = await app.inject({
2028
+ method: 'POST',
2029
+ url: '/api/modules/install',
2030
+ payload: { source: { type: 'local', value: modDir } },
2031
+ });
2032
+ expect(second.statusCode).toBe(200);
2033
+ expect(JSON.parse(second.body).ok).toBe(true);
2034
+ await app.close();
2035
+ });
2036
+ // AC-17.3.2: reinstall replaces old files with new files
2037
+ it('AC-17.3.2: reinstall replaces old agent with updated agent', async () => {
2038
+ const v1Dir = makeLocalModule('swap-mod', 'old-agent.md');
2039
+ const app = await createTestApp();
2040
+ await app.inject({
2041
+ method: 'POST',
2042
+ url: '/api/modules/install',
2043
+ payload: { source: { type: 'local', value: v1Dir } },
2044
+ });
2045
+ // Produce a v2 source with a different agent file
2046
+ const v2Dir = path.join(srcDir, 'swap-mod-v2');
2047
+ fs.mkdirSync(path.join(v2Dir, 'agents'), { recursive: true });
2048
+ fs.writeFileSync(path.join(v2Dir, 'agents', 'new-agent.md'), '---\nname: new-agent\ntitle: New\n---\n# New\n');
2049
+ fs.writeFileSync(v2Dir + '/module.yaml', 'code: swap-mod\nname: "Swap Mod"\nversion: "2.0.0"\n');
2050
+ const resp = await app.inject({
2051
+ method: 'POST',
2052
+ url: '/api/modules/install',
2053
+ payload: { source: { type: 'local', value: v2Dir } },
2054
+ });
2055
+ expect(resp.statusCode).toBe(200);
2056
+ const destBase = path.join(tmpDir, '_bmad', 'swap-mod', 'agents');
2057
+ expect(fs.existsSync(path.join(destBase, 'new-agent.md'))).toBe(true);
2058
+ expect(fs.existsSync(path.join(destBase, 'old-agent.md'))).toBe(false);
2059
+ await app.close();
2060
+ });
2061
+ // AC-17.3.3: zip upload reinstall returns 200 (not 409)
2062
+ it('AC-17.3.3: zip upload reinstall returns 200', async () => {
2063
+ const zipBytes = await buildFixtureZip([
2064
+ { entryName: 'agents/zip-agent.md', data: '---\nname: zip-agent\ntitle: Zip\n---\n# Zip\n' },
2065
+ { entryName: 'module.yaml', data: 'code: zip-reinstall\nname: "Zip Reinstall"\nversion: "1.0.0"\n' },
2066
+ ]);
2067
+ const { payload: p1, headers: h1 } = makeMultipartPayload(zipBytes);
2068
+ const app = await createTestApp();
2069
+ const first = await app.inject({
2070
+ method: 'POST',
2071
+ url: '/api/modules/install/upload',
2072
+ payload: p1,
2073
+ headers: h1,
2074
+ });
2075
+ expect(first.statusCode).toBe(200);
2076
+ const { payload: p2, headers: h2 } = makeMultipartPayload(zipBytes);
2077
+ const second = await app.inject({
2078
+ method: 'POST',
2079
+ url: '/api/modules/install/upload',
2080
+ payload: p2,
2081
+ headers: h2,
2082
+ });
2083
+ expect(second.statusCode).toBe(200);
2084
+ expect(JSON.parse(second.body).ok).toBe(true);
2085
+ await app.close();
2086
+ });
2087
+ // AC-17.3.4: reinstalling a built-in module is rejected with 422
2088
+ it('AC-17.3.4: reinstalling a built-in module returns 422', async () => {
2089
+ // Manually write a manifest with a built-in entry + create the module dir
2090
+ const configDir = path.join(tmpDir, '_bmad', '_config');
2091
+ const builtInManifest = {
2092
+ installation: {
2093
+ version: '6.2.0',
2094
+ installDate: '2026-01-01T00:00:00.000Z',
2095
+ lastUpdated: '2026-01-01T00:00:00.000Z',
2096
+ },
2097
+ modules: [
2098
+ {
2099
+ name: 'builtin-mod',
2100
+ version: '1.0.0',
2101
+ installDate: '2026-01-01T00:00:00.000Z',
2102
+ lastUpdated: '2026-01-01T00:00:00.000Z',
2103
+ source: 'built-in',
2104
+ npmPackage: null,
2105
+ repoUrl: null,
2106
+ },
2107
+ ],
2108
+ };
2109
+ fs.writeFileSync(path.join(configDir, 'manifest.yaml'), yaml.dump(builtInManifest));
2110
+ const builtInDir = path.join(tmpDir, '_bmad', 'builtin-mod', 'agents');
2111
+ fs.mkdirSync(builtInDir, { recursive: true });
2112
+ const srcMod = makeLocalModule('builtin-mod', 'agent.md');
2113
+ const app = await createTestApp();
2114
+ const resp = await app.inject({
2115
+ method: 'POST',
2116
+ url: '/api/modules/install',
2117
+ payload: { source: { type: 'local', value: srcMod } },
2118
+ });
2119
+ expect(resp.statusCode).toBe(422);
2120
+ expect(JSON.parse(resp.body).error.message).toContain('built-in');
2121
+ await app.close();
2122
+ });
2123
+ });
2124
+ //# sourceMappingURL=modules-plugin.test.js.map