@pennyfarthing/core 6.6.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 (599) hide show
  1. package/README.md +293 -0
  2. package/package.json +58 -0
  3. package/packages/core/bin/pennyfarthing.js +14 -0
  4. package/packages/core/dist/bmad/context-reader.d.ts +71 -0
  5. package/packages/core/dist/bmad/context-reader.d.ts.map +1 -0
  6. package/packages/core/dist/bmad/context-reader.js +369 -0
  7. package/packages/core/dist/bmad/context-reader.js.map +1 -0
  8. package/packages/core/dist/bmad/context-reader.test.d.ts +71 -0
  9. package/packages/core/dist/bmad/context-reader.test.d.ts.map +1 -0
  10. package/packages/core/dist/bmad/context-reader.test.js +878 -0
  11. package/packages/core/dist/bmad/context-reader.test.js.map +1 -0
  12. package/packages/core/dist/bmad/epics-parser.d.ts +61 -0
  13. package/packages/core/dist/bmad/epics-parser.d.ts.map +1 -0
  14. package/packages/core/dist/bmad/epics-parser.js +331 -0
  15. package/packages/core/dist/bmad/epics-parser.js.map +1 -0
  16. package/packages/core/dist/bmad/epics-parser.test.d.ts +7 -0
  17. package/packages/core/dist/bmad/epics-parser.test.d.ts.map +1 -0
  18. package/packages/core/dist/bmad/epics-parser.test.js +449 -0
  19. package/packages/core/dist/bmad/epics-parser.test.js.map +1 -0
  20. package/packages/core/dist/bmad/index.d.ts +11 -0
  21. package/packages/core/dist/bmad/index.d.ts.map +1 -0
  22. package/packages/core/dist/bmad/index.js +24 -0
  23. package/packages/core/dist/bmad/index.js.map +1 -0
  24. package/packages/core/dist/bmad/status-sync.d.ts +173 -0
  25. package/packages/core/dist/bmad/status-sync.d.ts.map +1 -0
  26. package/packages/core/dist/bmad/status-sync.js +463 -0
  27. package/packages/core/dist/bmad/status-sync.js.map +1 -0
  28. package/packages/core/dist/bmad/status-sync.test.d.ts +7 -0
  29. package/packages/core/dist/bmad/status-sync.test.d.ts.map +1 -0
  30. package/packages/core/dist/bmad/status-sync.test.js +702 -0
  31. package/packages/core/dist/bmad/status-sync.test.js.map +1 -0
  32. package/packages/core/dist/bmad/story-exporter.d.ts +55 -0
  33. package/packages/core/dist/bmad/story-exporter.d.ts.map +1 -0
  34. package/packages/core/dist/bmad/story-exporter.js +170 -0
  35. package/packages/core/dist/bmad/story-exporter.js.map +1 -0
  36. package/packages/core/dist/bmad/story-exporter.test.d.ts +51 -0
  37. package/packages/core/dist/bmad/story-exporter.test.d.ts.map +1 -0
  38. package/packages/core/dist/bmad/story-exporter.test.js +603 -0
  39. package/packages/core/dist/bmad/story-exporter.test.js.map +1 -0
  40. package/packages/core/dist/bmad/story-parser.d.ts +44 -0
  41. package/packages/core/dist/bmad/story-parser.d.ts.map +1 -0
  42. package/packages/core/dist/bmad/story-parser.js +307 -0
  43. package/packages/core/dist/bmad/story-parser.js.map +1 -0
  44. package/packages/core/dist/bmad/story-parser.test.d.ts +44 -0
  45. package/packages/core/dist/bmad/story-parser.test.d.ts.map +1 -0
  46. package/packages/core/dist/bmad/story-parser.test.js +693 -0
  47. package/packages/core/dist/bmad/story-parser.test.js.map +1 -0
  48. package/packages/core/dist/cli/commands/command.d.ts +28 -0
  49. package/packages/core/dist/cli/commands/command.d.ts.map +1 -0
  50. package/packages/core/dist/cli/commands/command.js +399 -0
  51. package/packages/core/dist/cli/commands/command.js.map +1 -0
  52. package/packages/core/dist/cli/commands/cyclist.d.ts +46 -0
  53. package/packages/core/dist/cli/commands/cyclist.d.ts.map +1 -0
  54. package/packages/core/dist/cli/commands/cyclist.js +191 -0
  55. package/packages/core/dist/cli/commands/cyclist.js.map +1 -0
  56. package/packages/core/dist/cli/commands/cyclist.test.d.ts +13 -0
  57. package/packages/core/dist/cli/commands/cyclist.test.d.ts.map +1 -0
  58. package/packages/core/dist/cli/commands/cyclist.test.js +243 -0
  59. package/packages/core/dist/cli/commands/cyclist.test.js.map +1 -0
  60. package/packages/core/dist/cli/commands/doctor.d.ts +9 -0
  61. package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -0
  62. package/packages/core/dist/cli/commands/doctor.js +652 -0
  63. package/packages/core/dist/cli/commands/doctor.js.map +1 -0
  64. package/packages/core/dist/cli/commands/init.d.ts +8 -0
  65. package/packages/core/dist/cli/commands/init.d.ts.map +1 -0
  66. package/packages/core/dist/cli/commands/init.js +524 -0
  67. package/packages/core/dist/cli/commands/init.js.map +1 -0
  68. package/packages/core/dist/cli/commands/skill.d.ts +28 -0
  69. package/packages/core/dist/cli/commands/skill.d.ts.map +1 -0
  70. package/packages/core/dist/cli/commands/skill.js +416 -0
  71. package/packages/core/dist/cli/commands/skill.js.map +1 -0
  72. package/packages/core/dist/cli/commands/theme.d.ts +21 -0
  73. package/packages/core/dist/cli/commands/theme.d.ts.map +1 -0
  74. package/packages/core/dist/cli/commands/theme.js +201 -0
  75. package/packages/core/dist/cli/commands/theme.js.map +1 -0
  76. package/packages/core/dist/cli/commands/uninstall.d.ts +8 -0
  77. package/packages/core/dist/cli/commands/uninstall.d.ts.map +1 -0
  78. package/packages/core/dist/cli/commands/uninstall.js +237 -0
  79. package/packages/core/dist/cli/commands/uninstall.js.map +1 -0
  80. package/packages/core/dist/cli/commands/update.d.ts +9 -0
  81. package/packages/core/dist/cli/commands/update.d.ts.map +1 -0
  82. package/packages/core/dist/cli/commands/update.js +545 -0
  83. package/packages/core/dist/cli/commands/update.js.map +1 -0
  84. package/packages/core/dist/cli/commands/version.d.ts +2 -0
  85. package/packages/core/dist/cli/commands/version.d.ts.map +1 -0
  86. package/packages/core/dist/cli/commands/version.js +28 -0
  87. package/packages/core/dist/cli/commands/version.js.map +1 -0
  88. package/packages/core/dist/cli/customization.test.d.ts +12 -0
  89. package/packages/core/dist/cli/customization.test.d.ts.map +1 -0
  90. package/packages/core/dist/cli/customization.test.js +84 -0
  91. package/packages/core/dist/cli/customization.test.js.map +1 -0
  92. package/packages/core/dist/cli/cyclist-migration.test.d.ts +16 -0
  93. package/packages/core/dist/cli/cyclist-migration.test.d.ts.map +1 -0
  94. package/packages/core/dist/cli/cyclist-migration.test.js +224 -0
  95. package/packages/core/dist/cli/cyclist-migration.test.js.map +1 -0
  96. package/packages/core/dist/cli/index.d.ts +3 -0
  97. package/packages/core/dist/cli/index.d.ts.map +1 -0
  98. package/packages/core/dist/cli/index.js +174 -0
  99. package/packages/core/dist/cli/index.js.map +1 -0
  100. package/packages/core/dist/cli/ocean-profiles.test.d.ts +13 -0
  101. package/packages/core/dist/cli/ocean-profiles.test.d.ts.map +1 -0
  102. package/packages/core/dist/cli/ocean-profiles.test.js +134 -0
  103. package/packages/core/dist/cli/ocean-profiles.test.js.map +1 -0
  104. package/packages/core/dist/cli/theme-maker.test.d.ts +11 -0
  105. package/packages/core/dist/cli/theme-maker.test.d.ts.map +1 -0
  106. package/packages/core/dist/cli/theme-maker.test.js +356 -0
  107. package/packages/core/dist/cli/theme-maker.test.js.map +1 -0
  108. package/packages/core/dist/cli/utils/constants.d.ts +60 -0
  109. package/packages/core/dist/cli/utils/constants.d.ts.map +1 -0
  110. package/packages/core/dist/cli/utils/constants.js +52 -0
  111. package/packages/core/dist/cli/utils/constants.js.map +1 -0
  112. package/packages/core/dist/cli/utils/files.d.ts +71 -0
  113. package/packages/core/dist/cli/utils/files.d.ts.map +1 -0
  114. package/packages/core/dist/cli/utils/files.js +162 -0
  115. package/packages/core/dist/cli/utils/files.js.map +1 -0
  116. package/packages/core/dist/cli/utils/logger.d.ts +26 -0
  117. package/packages/core/dist/cli/utils/logger.d.ts.map +1 -0
  118. package/packages/core/dist/cli/utils/logger.js +88 -0
  119. package/packages/core/dist/cli/utils/logger.js.map +1 -0
  120. package/packages/core/dist/cli/utils/manifest.d.ts +47 -0
  121. package/packages/core/dist/cli/utils/manifest.d.ts.map +1 -0
  122. package/packages/core/dist/cli/utils/manifest.js +90 -0
  123. package/packages/core/dist/cli/utils/manifest.js.map +1 -0
  124. package/packages/core/dist/cli/utils/node-modules.d.ts +6 -0
  125. package/packages/core/dist/cli/utils/node-modules.d.ts.map +1 -0
  126. package/packages/core/dist/cli/utils/node-modules.js +22 -0
  127. package/packages/core/dist/cli/utils/node-modules.js.map +1 -0
  128. package/packages/core/dist/cli/utils/prompts.d.ts +34 -0
  129. package/packages/core/dist/cli/utils/prompts.d.ts.map +1 -0
  130. package/packages/core/dist/cli/utils/prompts.js +93 -0
  131. package/packages/core/dist/cli/utils/prompts.js.map +1 -0
  132. package/packages/core/dist/cli/utils/symlinks.d.ts +29 -0
  133. package/packages/core/dist/cli/utils/symlinks.d.ts.map +1 -0
  134. package/packages/core/dist/cli/utils/symlinks.js +181 -0
  135. package/packages/core/dist/cli/utils/symlinks.js.map +1 -0
  136. package/packages/core/dist/cli/utils/themes.d.ts +104 -0
  137. package/packages/core/dist/cli/utils/themes.d.ts.map +1 -0
  138. package/packages/core/dist/cli/utils/themes.js +393 -0
  139. package/packages/core/dist/cli/utils/themes.js.map +1 -0
  140. package/packages/core/dist/cli/utils/themes.test.d.ts +12 -0
  141. package/packages/core/dist/cli/utils/themes.test.d.ts.map +1 -0
  142. package/packages/core/dist/cli/utils/themes.test.js +144 -0
  143. package/packages/core/dist/cli/utils/themes.test.js.map +1 -0
  144. package/packages/core/dist/cli/utils/version.d.ts +10 -0
  145. package/packages/core/dist/cli/utils/version.d.ts.map +1 -0
  146. package/packages/core/dist/cli/utils/version.js +63 -0
  147. package/packages/core/dist/cli/utils/version.js.map +1 -0
  148. package/packages/core/dist/cli/workspace.test.d.ts +8 -0
  149. package/packages/core/dist/cli/workspace.test.d.ts.map +1 -0
  150. package/packages/core/dist/cli/workspace.test.js +154 -0
  151. package/packages/core/dist/cli/workspace.test.js.map +1 -0
  152. package/packages/core/dist/index.d.ts +6 -0
  153. package/packages/core/dist/index.d.ts.map +1 -0
  154. package/packages/core/dist/index.js +10 -0
  155. package/packages/core/dist/index.js.map +1 -0
  156. package/packages/core/dist/permissions/index.d.ts +9 -0
  157. package/packages/core/dist/permissions/index.d.ts.map +1 -0
  158. package/packages/core/dist/permissions/index.js +13 -0
  159. package/packages/core/dist/permissions/index.js.map +1 -0
  160. package/packages/core/dist/permissions/permission-schema.d.ts +89 -0
  161. package/packages/core/dist/permissions/permission-schema.d.ts.map +1 -0
  162. package/packages/core/dist/permissions/permission-schema.js +120 -0
  163. package/packages/core/dist/permissions/permission-schema.js.map +1 -0
  164. package/packages/core/dist/permissions/permission-schema.test.d.ts +40 -0
  165. package/packages/core/dist/permissions/permission-schema.test.d.ts.map +1 -0
  166. package/packages/core/dist/permissions/permission-schema.test.js +367 -0
  167. package/packages/core/dist/permissions/permission-schema.test.js.map +1 -0
  168. package/packages/core/dist/scripts/add-ocean-profiles.d.ts +9 -0
  169. package/packages/core/dist/scripts/add-ocean-profiles.d.ts.map +1 -0
  170. package/packages/core/dist/scripts/add-ocean-profiles.js +695 -0
  171. package/packages/core/dist/scripts/add-ocean-profiles.js.map +1 -0
  172. package/packages/core/dist/scripts/benchmark-integration.d.ts +182 -0
  173. package/packages/core/dist/scripts/benchmark-integration.d.ts.map +1 -0
  174. package/packages/core/dist/scripts/benchmark-integration.js +691 -0
  175. package/packages/core/dist/scripts/benchmark-integration.js.map +1 -0
  176. package/packages/core/dist/scripts/benchmark-integration.test.d.ts +13 -0
  177. package/packages/core/dist/scripts/benchmark-integration.test.d.ts.map +1 -0
  178. package/packages/core/dist/scripts/benchmark-integration.test.js +680 -0
  179. package/packages/core/dist/scripts/benchmark-integration.test.js.map +1 -0
  180. package/packages/core/dist/scripts/debugging-scenarios.test.d.ts +18 -0
  181. package/packages/core/dist/scripts/debugging-scenarios.test.d.ts.map +1 -0
  182. package/packages/core/dist/scripts/debugging-scenarios.test.js +317 -0
  183. package/packages/core/dist/scripts/debugging-scenarios.test.js.map +1 -0
  184. package/packages/core/dist/scripts/generate-all-faces.d.ts +10 -0
  185. package/packages/core/dist/scripts/generate-all-faces.d.ts.map +1 -0
  186. package/packages/core/dist/scripts/generate-all-faces.js +256 -0
  187. package/packages/core/dist/scripts/generate-all-faces.js.map +1 -0
  188. package/packages/core/dist/scripts/generate-all-faces.test.d.ts +17 -0
  189. package/packages/core/dist/scripts/generate-all-faces.test.d.ts.map +1 -0
  190. package/packages/core/dist/scripts/generate-all-faces.test.js +372 -0
  191. package/packages/core/dist/scripts/generate-all-faces.test.js.map +1 -0
  192. package/packages/core/dist/scripts/generate-all-spiders.d.ts +10 -0
  193. package/packages/core/dist/scripts/generate-all-spiders.d.ts.map +1 -0
  194. package/packages/core/dist/scripts/generate-all-spiders.js +306 -0
  195. package/packages/core/dist/scripts/generate-all-spiders.js.map +1 -0
  196. package/packages/core/dist/scripts/generate-ascii-face.d.ts +52 -0
  197. package/packages/core/dist/scripts/generate-ascii-face.d.ts.map +1 -0
  198. package/packages/core/dist/scripts/generate-ascii-face.js +155 -0
  199. package/packages/core/dist/scripts/generate-ascii-face.js.map +1 -0
  200. package/packages/core/dist/scripts/generate-face.d.ts +52 -0
  201. package/packages/core/dist/scripts/generate-face.d.ts.map +1 -0
  202. package/packages/core/dist/scripts/generate-face.js +199 -0
  203. package/packages/core/dist/scripts/generate-face.js.map +1 -0
  204. package/packages/core/dist/scripts/generate-face.test.d.ts +13 -0
  205. package/packages/core/dist/scripts/generate-face.test.d.ts.map +1 -0
  206. package/packages/core/dist/scripts/generate-face.test.js +301 -0
  207. package/packages/core/dist/scripts/generate-face.test.js.map +1 -0
  208. package/packages/core/dist/scripts/generate-report.d.ts +65 -0
  209. package/packages/core/dist/scripts/generate-report.d.ts.map +1 -0
  210. package/packages/core/dist/scripts/generate-report.js +378 -0
  211. package/packages/core/dist/scripts/generate-report.js.map +1 -0
  212. package/packages/core/dist/scripts/generate-report.test.d.ts +13 -0
  213. package/packages/core/dist/scripts/generate-report.test.d.ts.map +1 -0
  214. package/packages/core/dist/scripts/generate-report.test.js +363 -0
  215. package/packages/core/dist/scripts/generate-report.test.js.map +1 -0
  216. package/packages/core/dist/scripts/generate-spider-report.d.ts +65 -0
  217. package/packages/core/dist/scripts/generate-spider-report.d.ts.map +1 -0
  218. package/packages/core/dist/scripts/generate-spider-report.js +366 -0
  219. package/packages/core/dist/scripts/generate-spider-report.js.map +1 -0
  220. package/packages/core/dist/scripts/generate-spider-report.test.d.ts +13 -0
  221. package/packages/core/dist/scripts/generate-spider-report.test.d.ts.map +1 -0
  222. package/packages/core/dist/scripts/generate-spider-report.test.js +367 -0
  223. package/packages/core/dist/scripts/generate-spider-report.test.js.map +1 -0
  224. package/packages/core/dist/scripts/generate-spider.d.ts +37 -0
  225. package/packages/core/dist/scripts/generate-spider.d.ts.map +1 -0
  226. package/packages/core/dist/scripts/generate-spider.js +315 -0
  227. package/packages/core/dist/scripts/generate-spider.js.map +1 -0
  228. package/packages/core/dist/scripts/generate-spider.test.d.ts +14 -0
  229. package/packages/core/dist/scripts/generate-spider.test.d.ts.map +1 -0
  230. package/packages/core/dist/scripts/generate-spider.test.js +269 -0
  231. package/packages/core/dist/scripts/generate-spider.test.js.map +1 -0
  232. package/packages/core/dist/scripts/job-fair-aggregator.d.ts +150 -0
  233. package/packages/core/dist/scripts/job-fair-aggregator.d.ts.map +1 -0
  234. package/packages/core/dist/scripts/job-fair-aggregator.js +547 -0
  235. package/packages/core/dist/scripts/job-fair-aggregator.js.map +1 -0
  236. package/packages/core/dist/scripts/job-fair-aggregator.test.d.ts +14 -0
  237. package/packages/core/dist/scripts/job-fair-aggregator.test.d.ts.map +1 -0
  238. package/packages/core/dist/scripts/job-fair-aggregator.test.js +616 -0
  239. package/packages/core/dist/scripts/job-fair-aggregator.test.js.map +1 -0
  240. package/packages/core/dist/scripts/run-ci.test.d.ts +20 -0
  241. package/packages/core/dist/scripts/run-ci.test.d.ts.map +1 -0
  242. package/packages/core/dist/scripts/run-ci.test.js +127 -0
  243. package/packages/core/dist/scripts/run-ci.test.js.map +1 -0
  244. package/packages/core/dist/scripts/theme-detail.test.d.ts +10 -0
  245. package/packages/core/dist/scripts/theme-detail.test.d.ts.map +1 -0
  246. package/packages/core/dist/scripts/theme-detail.test.js +199 -0
  247. package/packages/core/dist/scripts/theme-detail.test.js.map +1 -0
  248. package/packages/core/dist/scripts/validate-ocean-profiles.d.ts +9 -0
  249. package/packages/core/dist/scripts/validate-ocean-profiles.d.ts.map +1 -0
  250. package/packages/core/dist/scripts/validate-ocean-profiles.js +130 -0
  251. package/packages/core/dist/scripts/validate-ocean-profiles.js.map +1 -0
  252. package/packages/core/dist/workflow/generic-handoff.d.ts +235 -0
  253. package/packages/core/dist/workflow/generic-handoff.d.ts.map +1 -0
  254. package/packages/core/dist/workflow/generic-handoff.js +358 -0
  255. package/packages/core/dist/workflow/generic-handoff.js.map +1 -0
  256. package/packages/core/dist/workflow/generic-handoff.test.d.ts +21 -0
  257. package/packages/core/dist/workflow/generic-handoff.test.d.ts.map +1 -0
  258. package/packages/core/dist/workflow/generic-handoff.test.js +499 -0
  259. package/packages/core/dist/workflow/generic-handoff.test.js.map +1 -0
  260. package/packages/core/dist/workflow/generic-sm-finish.d.ts +89 -0
  261. package/packages/core/dist/workflow/generic-sm-finish.d.ts.map +1 -0
  262. package/packages/core/dist/workflow/generic-sm-finish.js +157 -0
  263. package/packages/core/dist/workflow/generic-sm-finish.js.map +1 -0
  264. package/packages/core/dist/workflow/generic-sm-setup.d.ts +138 -0
  265. package/packages/core/dist/workflow/generic-sm-setup.d.ts.map +1 -0
  266. package/packages/core/dist/workflow/generic-sm-setup.js +382 -0
  267. package/packages/core/dist/workflow/generic-sm-setup.js.map +1 -0
  268. package/packages/core/dist/workflow/sm-subagents.test.d.ts +23 -0
  269. package/packages/core/dist/workflow/sm-subagents.test.d.ts.map +1 -0
  270. package/packages/core/dist/workflow/sm-subagents.test.js +727 -0
  271. package/packages/core/dist/workflow/sm-subagents.test.js.map +1 -0
  272. package/packages/core/dist/workflow/story-workflow-routing.test.d.ts +17 -0
  273. package/packages/core/dist/workflow/story-workflow-routing.test.d.ts.map +1 -0
  274. package/packages/core/dist/workflow/story-workflow-routing.test.js +559 -0
  275. package/packages/core/dist/workflow/story-workflow-routing.test.js.map +1 -0
  276. package/packages/core/dist/workflow/test-cache.d.ts +131 -0
  277. package/packages/core/dist/workflow/test-cache.d.ts.map +1 -0
  278. package/packages/core/dist/workflow/test-cache.js +226 -0
  279. package/packages/core/dist/workflow/test-cache.js.map +1 -0
  280. package/packages/core/dist/workflow/test-cache.test.d.ts +17 -0
  281. package/packages/core/dist/workflow/test-cache.test.d.ts.map +1 -0
  282. package/packages/core/dist/workflow/test-cache.test.js +438 -0
  283. package/packages/core/dist/workflow/test-cache.test.js.map +1 -0
  284. package/packages/core/dist/workflow/workflow-loader.d.ts +76 -0
  285. package/packages/core/dist/workflow/workflow-loader.d.ts.map +1 -0
  286. package/packages/core/dist/workflow/workflow-loader.js +133 -0
  287. package/packages/core/dist/workflow/workflow-loader.js.map +1 -0
  288. package/packages/core/dist/workflow/workflow-loader.test.d.ts +15 -0
  289. package/packages/core/dist/workflow/workflow-loader.test.d.ts.map +1 -0
  290. package/packages/core/dist/workflow/workflow-loader.test.js +354 -0
  291. package/packages/core/dist/workflow/workflow-loader.test.js.map +1 -0
  292. package/packages/core/dist/workflow/workflow-migration.test.d.ts +17 -0
  293. package/packages/core/dist/workflow/workflow-migration.test.d.ts.map +1 -0
  294. package/packages/core/dist/workflow/workflow-migration.test.js +372 -0
  295. package/packages/core/dist/workflow/workflow-migration.test.js.map +1 -0
  296. package/packages/core/dist/workflow/workflow-router.d.ts +55 -0
  297. package/packages/core/dist/workflow/workflow-router.d.ts.map +1 -0
  298. package/packages/core/dist/workflow/workflow-router.js +245 -0
  299. package/packages/core/dist/workflow/workflow-router.js.map +1 -0
  300. package/packages/core/dist/workflow/workflow-router.test.d.ts +20 -0
  301. package/packages/core/dist/workflow/workflow-router.test.d.ts.map +1 -0
  302. package/packages/core/dist/workflow/workflow-router.test.js +607 -0
  303. package/packages/core/dist/workflow/workflow-router.test.js.map +1 -0
  304. package/packages/core/dist/workflow/workflow-schema.d.ts +98 -0
  305. package/packages/core/dist/workflow/workflow-schema.d.ts.map +1 -0
  306. package/packages/core/dist/workflow/workflow-schema.js +230 -0
  307. package/packages/core/dist/workflow/workflow-schema.js.map +1 -0
  308. package/packages/core/dist/workflow/workflow-schema.test.d.ts +45 -0
  309. package/packages/core/dist/workflow/workflow-schema.test.d.ts.map +1 -0
  310. package/packages/core/dist/workflow/workflow-schema.test.js +512 -0
  311. package/packages/core/dist/workflow/workflow-schema.test.js.map +1 -0
  312. package/pennyfarthing-dist/agents/README.md +397 -0
  313. package/pennyfarthing-dist/agents/architect.md +171 -0
  314. package/pennyfarthing-dist/agents/dev.md +225 -0
  315. package/pennyfarthing-dist/agents/devops.md +183 -0
  316. package/pennyfarthing-dist/agents/generic-handoff.md +451 -0
  317. package/pennyfarthing-dist/agents/generic-sm-finish.md +261 -0
  318. package/pennyfarthing-dist/agents/generic-sm-setup.md +214 -0
  319. package/pennyfarthing-dist/agents/orchestrator.md +316 -0
  320. package/pennyfarthing-dist/agents/pm.md +153 -0
  321. package/pennyfarthing-dist/agents/reviewer-preflight.md +224 -0
  322. package/pennyfarthing-dist/agents/reviewer.md +315 -0
  323. package/pennyfarthing-dist/agents/sm-file-summary.md +109 -0
  324. package/pennyfarthing-dist/agents/sm-handoff.md +97 -0
  325. package/pennyfarthing-dist/agents/sm.md +480 -0
  326. package/pennyfarthing-dist/agents/tea.md +191 -0
  327. package/pennyfarthing-dist/agents/tech-writer.md +148 -0
  328. package/pennyfarthing-dist/agents/testing-runner.md +420 -0
  329. package/pennyfarthing-dist/agents/ux-designer.md +158 -0
  330. package/pennyfarthing-dist/agents/workflow-status-check.md +332 -0
  331. package/pennyfarthing-dist/commands/architect.md +62 -0
  332. package/pennyfarthing-dist/commands/benchmark-control.md +69 -0
  333. package/pennyfarthing-dist/commands/benchmark.md +467 -0
  334. package/pennyfarthing-dist/commands/brainstorm.md +91 -0
  335. package/pennyfarthing-dist/commands/check.md +156 -0
  336. package/pennyfarthing-dist/commands/chore.md +178 -0
  337. package/pennyfarthing-dist/commands/close-epic.md +136 -0
  338. package/pennyfarthing-dist/commands/continue-session.md +184 -0
  339. package/pennyfarthing-dist/commands/create-branches-from-story.md +374 -0
  340. package/pennyfarthing-dist/commands/create-theme.md +29 -0
  341. package/pennyfarthing-dist/commands/dev.md +60 -0
  342. package/pennyfarthing-dist/commands/devops.md +59 -0
  343. package/pennyfarthing-dist/commands/git-cleanup.md +340 -0
  344. package/pennyfarthing-dist/commands/health-check.md +108 -0
  345. package/pennyfarthing-dist/commands/help.md +264 -0
  346. package/pennyfarthing-dist/commands/job-fair.md +102 -0
  347. package/pennyfarthing-dist/commands/list-themes.md +17 -0
  348. package/pennyfarthing-dist/commands/new-work.md +127 -0
  349. package/pennyfarthing-dist/commands/orchestrator.md +56 -0
  350. package/pennyfarthing-dist/commands/parallel-work.md +71 -0
  351. package/pennyfarthing-dist/commands/party-mode.md +67 -0
  352. package/pennyfarthing-dist/commands/permissions.md +193 -0
  353. package/pennyfarthing-dist/commands/pm.md +60 -0
  354. package/pennyfarthing-dist/commands/prime.md +140 -0
  355. package/pennyfarthing-dist/commands/release.md +58 -0
  356. package/pennyfarthing-dist/commands/repo-status.md +49 -0
  357. package/pennyfarthing-dist/commands/retro.md +200 -0
  358. package/pennyfarthing-dist/commands/reviewer.md +64 -0
  359. package/pennyfarthing-dist/commands/run-ci.md +116 -0
  360. package/pennyfarthing-dist/commands/set-theme.md +52 -0
  361. package/pennyfarthing-dist/commands/show-theme.md +21 -0
  362. package/pennyfarthing-dist/commands/sm.md +70 -0
  363. package/pennyfarthing-dist/commands/solo.md +411 -0
  364. package/pennyfarthing-dist/commands/sprint-planning.md +109 -0
  365. package/pennyfarthing-dist/commands/start-epic.md +156 -0
  366. package/pennyfarthing-dist/commands/sync-epic-to-jira.md +184 -0
  367. package/pennyfarthing-dist/commands/sync-work-with-sprint.md +376 -0
  368. package/pennyfarthing-dist/commands/tea.md +63 -0
  369. package/pennyfarthing-dist/commands/tech-writer.md +53 -0
  370. package/pennyfarthing-dist/commands/theme-maker.md +671 -0
  371. package/pennyfarthing-dist/commands/update-domain-docs.md +83 -0
  372. package/pennyfarthing-dist/commands/ux-designer.md +62 -0
  373. package/pennyfarthing-dist/commands/work.md +111 -0
  374. package/pennyfarthing-dist/guides/AGENT-COORDINATION.md +480 -0
  375. package/pennyfarthing-dist/guides/AGENT-SCOPES.md +201 -0
  376. package/pennyfarthing-dist/guides/HOOKS.md +230 -0
  377. package/pennyfarthing-dist/guides/PROMPT-PATTERNS.md +338 -0
  378. package/pennyfarthing-dist/guides/SESSION-ARTIFACTS.md +193 -0
  379. package/pennyfarthing-dist/guides/agent-template-strategic.md +148 -0
  380. package/pennyfarthing-dist/guides/agent-template-tactical.md +162 -0
  381. package/pennyfarthing-dist/guides/patterns/approval-gates-pattern.md +746 -0
  382. package/pennyfarthing-dist/guides/patterns/fan-out-fan-in-pattern.md +574 -0
  383. package/pennyfarthing-dist/guides/patterns/helper-delegation-pattern.md +488 -0
  384. package/pennyfarthing-dist/guides/patterns/tdd-flow-pattern.md +402 -0
  385. package/pennyfarthing-dist/guides/permission-protocol.md +188 -0
  386. package/pennyfarthing-dist/guides/persona-loading.md +46 -0
  387. package/pennyfarthing-dist/guides/persona-system.md +294 -0
  388. package/pennyfarthing-dist/guides/shared-agent-behavior.md +388 -0
  389. package/pennyfarthing-dist/guides/shared-context.md +147 -0
  390. package/pennyfarthing-dist/guides/strategic-agent-behavior.md +348 -0
  391. package/pennyfarthing-dist/guides/tactical-agent-behavior.md +1041 -0
  392. package/pennyfarthing-dist/guides/workflow-schema.md +195 -0
  393. package/pennyfarthing-dist/guides/worktree-mode.md +113 -0
  394. package/pennyfarthing-dist/output-styles/teaching.md +33 -0
  395. package/pennyfarthing-dist/output-styles/terse.md +20 -0
  396. package/pennyfarthing-dist/output-styles/verbose.md +28 -0
  397. package/pennyfarthing-dist/personas/themes/1984.yaml +312 -0
  398. package/pennyfarthing-dist/personas/themes/a-team.yaml +207 -0
  399. package/pennyfarthing-dist/personas/themes/agatha-christie.yaml +300 -0
  400. package/pennyfarthing-dist/personas/themes/alice-in-wonderland.yaml +330 -0
  401. package/pennyfarthing-dist/personas/themes/all-stars.yaml +332 -0
  402. package/pennyfarthing-dist/personas/themes/ancient-philosophers.yaml +320 -0
  403. package/pennyfarthing-dist/personas/themes/ancient-strategists.yaml +306 -0
  404. package/pennyfarthing-dist/personas/themes/arcane.yaml +288 -0
  405. package/pennyfarthing-dist/personas/themes/arthurian-mythos.yaml +331 -0
  406. package/pennyfarthing-dist/personas/themes/avatar-the-last-airbender.yaml +288 -0
  407. package/pennyfarthing-dist/personas/themes/babylon-5.yaml +288 -0
  408. package/pennyfarthing-dist/personas/themes/battlestar-galactica.yaml +288 -0
  409. package/pennyfarthing-dist/personas/themes/better-call-saul.yaml +288 -0
  410. package/pennyfarthing-dist/personas/themes/big-lebowski.yaml +300 -0
  411. package/pennyfarthing-dist/personas/themes/black-sails.yaml +300 -0
  412. package/pennyfarthing-dist/personas/themes/blade-runner.yaml +295 -0
  413. package/pennyfarthing-dist/personas/themes/bobiverse.yaml +288 -0
  414. package/pennyfarthing-dist/personas/themes/breaking-bad.yaml +327 -0
  415. package/pennyfarthing-dist/personas/themes/catch-22.yaml +316 -0
  416. package/pennyfarthing-dist/personas/themes/classical-composers.yaml +310 -0
  417. package/pennyfarthing-dist/personas/themes/control.yaml +197 -0
  418. package/pennyfarthing-dist/personas/themes/count-of-monte-cristo.yaml +320 -0
  419. package/pennyfarthing-dist/personas/themes/cowboy-bebop.yaml +323 -0
  420. package/pennyfarthing-dist/personas/themes/deadwood.yaml +300 -0
  421. package/pennyfarthing-dist/personas/themes/dickens.yaml +320 -0
  422. package/pennyfarthing-dist/personas/themes/discworld.yaml +332 -0
  423. package/pennyfarthing-dist/personas/themes/doctor-who.yaml +290 -0
  424. package/pennyfarthing-dist/personas/themes/don-quixote.yaml +320 -0
  425. package/pennyfarthing-dist/personas/themes/dune.yaml +307 -0
  426. package/pennyfarthing-dist/personas/themes/enlightenment-thinkers.yaml +320 -0
  427. package/pennyfarthing-dist/personas/themes/expeditionary-force.yaml +288 -0
  428. package/pennyfarthing-dist/personas/themes/fargo.yaml +330 -0
  429. package/pennyfarthing-dist/personas/themes/film-auteurs.yaml +312 -0
  430. package/pennyfarthing-dist/personas/themes/firefly.yaml +328 -0
  431. package/pennyfarthing-dist/personas/themes/foundation.yaml +290 -0
  432. package/pennyfarthing-dist/personas/themes/futurama.yaml +321 -0
  433. package/pennyfarthing-dist/personas/themes/game-of-thrones.yaml +290 -0
  434. package/pennyfarthing-dist/personas/themes/gilligans-island.yaml +243 -0
  435. package/pennyfarthing-dist/personas/themes/gothic-literature.yaml +308 -0
  436. package/pennyfarthing-dist/personas/themes/great-gatsby.yaml +308 -0
  437. package/pennyfarthing-dist/personas/themes/greek-mythology.yaml +330 -0
  438. package/pennyfarthing-dist/personas/themes/hannibal.yaml +300 -0
  439. package/pennyfarthing-dist/personas/themes/harry-potter.yaml +324 -0
  440. package/pennyfarthing-dist/personas/themes/his-dark-materials.yaml +291 -0
  441. package/pennyfarthing-dist/personas/themes/historical-figures.yaml +288 -0
  442. package/pennyfarthing-dist/personas/themes/hitchhikers-guide.yaml +331 -0
  443. package/pennyfarthing-dist/personas/themes/house-md.yaml +321 -0
  444. package/pennyfarthing-dist/personas/themes/imperial-radch.yaml +289 -0
  445. package/pennyfarthing-dist/personas/themes/inspector-morse.yaml +300 -0
  446. package/pennyfarthing-dist/personas/themes/jane-austen.yaml +287 -0
  447. package/pennyfarthing-dist/personas/themes/jazz-legends.yaml +320 -0
  448. package/pennyfarthing-dist/personas/themes/justified.yaml +300 -0
  449. package/pennyfarthing-dist/personas/themes/legion-of-doom.yaml +219 -0
  450. package/pennyfarthing-dist/personas/themes/les-miserables.yaml +299 -0
  451. package/pennyfarthing-dist/personas/themes/lord-of-the-rings.yaml +334 -0
  452. package/pennyfarthing-dist/personas/themes/lovecraft-mythos.yaml +334 -0
  453. package/pennyfarthing-dist/personas/themes/mad-max.yaml +355 -0
  454. package/pennyfarthing-dist/personas/themes/mad-men.yaml +289 -0
  455. package/pennyfarthing-dist/personas/themes/marvel-mcu.yaml +300 -0
  456. package/pennyfarthing-dist/personas/themes/mash.yaml +334 -0
  457. package/pennyfarthing-dist/personas/themes/mass-effect.yaml +289 -0
  458. package/pennyfarthing-dist/personas/themes/military-commanders.yaml +306 -0
  459. package/pennyfarthing-dist/personas/themes/moby-dick.yaml +320 -0
  460. package/pennyfarthing-dist/personas/themes/monty-python.yaml +303 -0
  461. package/pennyfarthing-dist/personas/themes/neuromancer.yaml +300 -0
  462. package/pennyfarthing-dist/personas/themes/norse-mythology.yaml +329 -0
  463. package/pennyfarthing-dist/personas/themes/parks-and-rec.yaml +242 -0
  464. package/pennyfarthing-dist/personas/themes/peaky-blinders.yaml +298 -0
  465. package/pennyfarthing-dist/personas/themes/princess-bride.yaml +220 -0
  466. package/pennyfarthing-dist/personas/themes/renaissance-masters.yaml +320 -0
  467. package/pennyfarthing-dist/personas/themes/rome.yaml +300 -0
  468. package/pennyfarthing-dist/personas/themes/russian-masters.yaml +318 -0
  469. package/pennyfarthing-dist/personas/themes/sandman.yaml +288 -0
  470. package/pennyfarthing-dist/personas/themes/scientific-revolutionaries.yaml +320 -0
  471. package/pennyfarthing-dist/personas/themes/shakespeare.yaml +301 -0
  472. package/pennyfarthing-dist/personas/themes/sherlock-holmes.yaml +289 -0
  473. package/pennyfarthing-dist/personas/themes/snow-crash.yaml +288 -0
  474. package/pennyfarthing-dist/personas/themes/software-pioneers.yaml +300 -0
  475. package/pennyfarthing-dist/personas/themes/star-trek-tng.yaml +230 -0
  476. package/pennyfarthing-dist/personas/themes/star-trek-tos.yaml +210 -0
  477. package/pennyfarthing-dist/personas/themes/star-wars.yaml +303 -0
  478. package/pennyfarthing-dist/personas/themes/succession.yaml +300 -0
  479. package/pennyfarthing-dist/personas/themes/superfriends.yaml +208 -0
  480. package/pennyfarthing-dist/personas/themes/ted-lasso.yaml +236 -0
  481. package/pennyfarthing-dist/personas/themes/the-americans.yaml +300 -0
  482. package/pennyfarthing-dist/personas/themes/the-crown.yaml +300 -0
  483. package/pennyfarthing-dist/personas/themes/the-expanse.yaml +213 -0
  484. package/pennyfarthing-dist/personas/themes/the-good-place.yaml +322 -0
  485. package/pennyfarthing-dist/personas/themes/the-matrix.yaml +353 -0
  486. package/pennyfarthing-dist/personas/themes/the-odyssey.yaml +300 -0
  487. package/pennyfarthing-dist/personas/themes/the-office.yaml +330 -0
  488. package/pennyfarthing-dist/personas/themes/the-simpsons.yaml +308 -0
  489. package/pennyfarthing-dist/personas/themes/the-sopranos.yaml +300 -0
  490. package/pennyfarthing-dist/personas/themes/the-wire.yaml +311 -0
  491. package/pennyfarthing-dist/personas/themes/the-witcher.yaml +300 -0
  492. package/pennyfarthing-dist/personas/themes/twin-peaks.yaml +302 -0
  493. package/pennyfarthing-dist/personas/themes/vorkosigan-saga.yaml +300 -0
  494. package/pennyfarthing-dist/personas/themes/watchmen.yaml +291 -0
  495. package/pennyfarthing-dist/personas/themes/west-wing.yaml +291 -0
  496. package/pennyfarthing-dist/personas/themes/world-explorers.yaml +320 -0
  497. package/pennyfarthing-dist/personas/themes/wwii-leaders.yaml +307 -0
  498. package/pennyfarthing-dist/personas/themes/x-files.yaml +302 -0
  499. package/pennyfarthing-dist/scripts/add-short-names.mjs +264 -0
  500. package/pennyfarthing-dist/scripts/agent-session.sh +367 -0
  501. package/pennyfarthing-dist/scripts/check-context.sh +187 -0
  502. package/pennyfarthing-dist/scripts/check.sh +497 -0
  503. package/pennyfarthing-dist/scripts/deploy.sh +284 -0
  504. package/pennyfarthing-dist/scripts/doctor-dogfood.sh +360 -0
  505. package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +61 -0
  506. package/pennyfarthing-dist/scripts/hooks/context-warning.sh +66 -0
  507. package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +35 -0
  508. package/pennyfarthing-dist/scripts/hooks/post-merge.sh +166 -0
  509. package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +50 -0
  510. package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +71 -0
  511. package/pennyfarthing-dist/scripts/hooks/pre-push.sh +54 -0
  512. package/pennyfarthing-dist/scripts/hooks/session-start.sh +98 -0
  513. package/pennyfarthing-dist/scripts/hooks/session-stop.sh +59 -0
  514. package/pennyfarthing-dist/scripts/install-git-hooks.sh +91 -0
  515. package/pennyfarthing-dist/scripts/prime.sh +161 -0
  516. package/pennyfarthing-dist/scripts/release.sh +198 -0
  517. package/pennyfarthing-dist/scripts/repo-utils.sh +778 -0
  518. package/pennyfarthing-dist/scripts/run-ci.sh +219 -0
  519. package/pennyfarthing-dist/scripts/run.sh +65 -0
  520. package/pennyfarthing-dist/scripts/statusline.sh +264 -0
  521. package/pennyfarthing-dist/scripts/tests/check.test.sh +582 -0
  522. package/pennyfarthing-dist/scripts/tests/test-character-voice.sh +107 -0
  523. package/pennyfarthing-dist/scripts/tests/test-drift-detection.sh +597 -0
  524. package/pennyfarthing-dist/scripts/tests/test-post-merge-hook.sh +514 -0
  525. package/pennyfarthing-dist/scripts/tests/test-session-checkpoint.sh +517 -0
  526. package/pennyfarthing-dist/scripts/tests/test-solo-command.sh +331 -0
  527. package/pennyfarthing-dist/scripts/uninstall.sh +271 -0
  528. package/pennyfarthing-dist/scripts/utils/background-tasks.sh +177 -0
  529. package/pennyfarthing-dist/scripts/utils/check-status.sh +251 -0
  530. package/pennyfarthing-dist/scripts/utils/checkpoint.sh +136 -0
  531. package/pennyfarthing-dist/scripts/utils/common.sh +157 -0
  532. package/pennyfarthing-dist/scripts/utils/create-feature-branches.sh +230 -0
  533. package/pennyfarthing-dist/scripts/utils/file-lock.sh +269 -0
  534. package/pennyfarthing-dist/scripts/utils/find-related-work.sh +231 -0
  535. package/pennyfarthing-dist/scripts/utils/find-root.sh +33 -0
  536. package/pennyfarthing-dist/scripts/utils/generate-skill-docs.sh +110 -0
  537. package/pennyfarthing-dist/scripts/utils/git-status-all.sh +127 -0
  538. package/pennyfarthing-dist/scripts/utils/ground-truth-judge.py +289 -0
  539. package/pennyfarthing-dist/scripts/utils/jira/jira-lib.mjs +443 -0
  540. package/pennyfarthing-dist/scripts/utils/jira/jira-sync-story.mjs +208 -0
  541. package/pennyfarthing-dist/scripts/utils/jira/jira-sync.mjs +198 -0
  542. package/pennyfarthing-dist/scripts/utils/jira-claim-story.sh +162 -0
  543. package/pennyfarthing-dist/scripts/utils/jira-lib.sh +463 -0
  544. package/pennyfarthing-dist/scripts/utils/jira-sync-story.sh +8 -0
  545. package/pennyfarthing-dist/scripts/utils/jira-sync.sh +8 -0
  546. package/pennyfarthing-dist/scripts/utils/log-skill-usage.sh +74 -0
  547. package/pennyfarthing-dist/scripts/utils/logging.sh +186 -0
  548. package/pennyfarthing-dist/scripts/utils/repo-scan.sh +141 -0
  549. package/pennyfarthing-dist/scripts/utils/retry.sh +76 -0
  550. package/pennyfarthing-dist/scripts/utils/run-timestamp.sh +7 -0
  551. package/pennyfarthing-dist/scripts/utils/session-cleanup.sh +319 -0
  552. package/pennyfarthing-dist/scripts/utils/skill-usage-report.sh +193 -0
  553. package/pennyfarthing-dist/scripts/utils/sprint-common.sh +286 -0
  554. package/pennyfarthing-dist/scripts/utils/sprint-metrics.sh +241 -0
  555. package/pennyfarthing-dist/scripts/utils/swebench-judge.py +400 -0
  556. package/pennyfarthing-dist/scripts/utils/sync-epic-to-jira.sh +16 -0
  557. package/pennyfarthing-dist/scripts/utils/test-setup.sh +337 -0
  558. package/pennyfarthing-dist/scripts/utils/validate-subagent-frontmatter.sh +160 -0
  559. package/pennyfarthing-dist/scripts/worktree-manager.sh +498 -0
  560. package/pennyfarthing-dist/skills/agentic-patterns/SKILL.md +236 -0
  561. package/pennyfarthing-dist/skills/changelog/SKILL.md +367 -0
  562. package/pennyfarthing-dist/skills/code-review/SKILL.md +168 -0
  563. package/pennyfarthing-dist/skills/context-engineering/SKILL.md +268 -0
  564. package/pennyfarthing-dist/skills/cyclist/SKILL.md +117 -0
  565. package/pennyfarthing-dist/skills/dev-patterns/SKILL.md +421 -0
  566. package/pennyfarthing-dist/skills/finalize-run/SKILL.md +258 -0
  567. package/pennyfarthing-dist/skills/jira/SKILL.md +281 -0
  568. package/pennyfarthing-dist/skills/judge/SKILL.md +524 -0
  569. package/pennyfarthing-dist/skills/just/SKILL.md +160 -0
  570. package/pennyfarthing-dist/skills/mermaid/SKILL.md +240 -0
  571. package/pennyfarthing-dist/skills/otel/skill.md +222 -0
  572. package/pennyfarthing-dist/skills/permissions/skill.md +172 -0
  573. package/pennyfarthing-dist/skills/persona-benchmark/SKILL.md +173 -0
  574. package/pennyfarthing-dist/skills/skill-registry.schema.json +102 -0
  575. package/pennyfarthing-dist/skills/skill-registry.yaml +335 -0
  576. package/pennyfarthing-dist/skills/sprint-context/SKILL.md +120 -0
  577. package/pennyfarthing-dist/skills/story-management/SKILL.md +208 -0
  578. package/pennyfarthing-dist/skills/testing/SKILL.md +99 -0
  579. package/pennyfarthing-dist/skills/testing/references/troubleshooting.md +124 -0
  580. package/pennyfarthing-dist/skills/theme/skill.md +129 -0
  581. package/pennyfarthing-dist/skills/theme-creation/SKILL.md +169 -0
  582. package/pennyfarthing-dist/skills/workflow/SKILL.md +160 -0
  583. package/pennyfarthing-dist/skills/yq/SKILL.md +264 -0
  584. package/pennyfarthing-dist/templates/LEADERBOARD.schema.yaml +187 -0
  585. package/pennyfarthing-dist/templates/LEADERBOARD.template.md +59 -0
  586. package/pennyfarthing-dist/templates/agent-scopes.yaml.template +276 -0
  587. package/pennyfarthing-dist/templates/pennyfarthing-settings.yaml.template +61 -0
  588. package/pennyfarthing-dist/templates/persona-config.yaml.template +22 -0
  589. package/pennyfarthing-dist/templates/preferences.yaml.template +15 -0
  590. package/pennyfarthing-dist/templates/settings.local.json.template +90 -0
  591. package/pennyfarthing-dist/templates/setup-env.sh.template +18 -0
  592. package/pennyfarthing-dist/templates/shared-context.md.template +70 -0
  593. package/pennyfarthing-dist/templates/sidecar/decisions.md.template +40 -0
  594. package/pennyfarthing-dist/templates/sidecar/gotchas.md.template +37 -0
  595. package/pennyfarthing-dist/templates/sidecar/patterns.md.template +34 -0
  596. package/pennyfarthing-dist/workflows/agent-docs.yaml +70 -0
  597. package/pennyfarthing-dist/workflows/bdd.yaml +58 -0
  598. package/pennyfarthing-dist/workflows/tdd.yaml +50 -0
  599. package/pennyfarthing-dist/workflows/trivial.yaml +40 -0
@@ -0,0 +1,727 @@
1
+ /**
2
+ * Tests for Story 31-11: Consolidate SM Bookkeeping Subagents
3
+ *
4
+ * These tests define the contract for consolidating 6 SM subagents into 3:
5
+ *
6
+ * 1. generic-sm-setup - Combines sm-story-setup + sm-work-research
7
+ * - Mode: 'research' | 'setup'
8
+ * - Research: scan backlog, batch Jira query, recommend stories
9
+ * - Setup: claim Jira, create branches, write session file
10
+ *
11
+ * 2. generic-sm-finish - Combines sm-finish-bookkeeping + sm-finish-execution
12
+ * - Phase: 'preflight' | 'execute'
13
+ * - Preflight: PR check, lint fix, Jira status → JSON report
14
+ * - Execute: archive, Jira transition, cleanup → completion flags
15
+ *
16
+ * 3. generic-handoff with setup phase support
17
+ * - Add setup→red transition to generic-handoff
18
+ * - Gate type: manual (verifies context exists)
19
+ *
20
+ * Run with: npm test
21
+ */
22
+ import { describe, it, beforeEach, afterEach } from 'node:test';
23
+ import assert from 'node:assert';
24
+ import { mkdirSync, rmSync, existsSync, writeFileSync, readFileSync } from 'node:fs';
25
+ import { join, dirname } from 'node:path';
26
+ import { fileURLToPath } from 'node:url';
27
+ // Get directory for test fixtures
28
+ const __dirname = dirname(fileURLToPath(import.meta.url));
29
+ const TEST_DIR = join(__dirname, '__test_sm_subagents__');
30
+ // Import the generic-sm-setup module
31
+ import { researchBacklog, setupStory, checkEpicContext, createEpicContext } from './generic-sm-setup.js';
32
+ // SetupParams interface now includes checkEpicContext and contextDir fields
33
+ // Import the generic-sm-finish module
34
+ import { preflightCheck, executeFinish } from './generic-sm-finish.js';
35
+ // Import extended generic-handoff for setup phase
36
+ import { findCurrentPhase, getNextPhase, checkGate } from './generic-handoff.js';
37
+ // Test fixture: TDD workflow with setup phase
38
+ const TDD_WORKFLOW = {
39
+ name: 'tdd',
40
+ description: 'Test-driven development with code review',
41
+ version: '1.0.0',
42
+ phases: [
43
+ { name: 'setup', agent: 'sm', output: ['session_file', 'branches', 'story_context'] },
44
+ {
45
+ name: 'red',
46
+ agent: 'tea',
47
+ input: ['session_file', 'story_context'],
48
+ output: ['failing_tests'],
49
+ gate: { type: 'tests_fail', condition: 'All acceptance criteria have test coverage' }
50
+ },
51
+ {
52
+ name: 'green',
53
+ agent: 'dev',
54
+ input: ['failing_tests', 'story_context'],
55
+ output: ['implementation', 'passing_tests'],
56
+ gate: { type: 'tests_pass', condition: 'All tests passing, no skipped tests' }
57
+ },
58
+ {
59
+ name: 'review',
60
+ agent: 'reviewer',
61
+ input: ['implementation', 'passing_tests'],
62
+ output: ['approval'],
63
+ gate: { type: 'approval', condition: 'Code review approved, no blocking issues' }
64
+ },
65
+ {
66
+ name: 'finish',
67
+ agent: 'sm',
68
+ input: ['approval'],
69
+ output: ['archived_session', 'story_summary']
70
+ }
71
+ ],
72
+ triggers: { types: ['feature', 'enhancement'], points: { min: 3 }, default: true }
73
+ };
74
+ describe('Generic SM Setup (31-11)', () => {
75
+ beforeEach(() => {
76
+ if (existsSync(TEST_DIR)) {
77
+ rmSync(TEST_DIR, { recursive: true });
78
+ }
79
+ mkdirSync(TEST_DIR, { recursive: true });
80
+ });
81
+ afterEach(() => {
82
+ if (existsSync(TEST_DIR)) {
83
+ rmSync(TEST_DIR, { recursive: true });
84
+ }
85
+ });
86
+ describe('researchBacklog() - Research mode', () => {
87
+ it('should return available stories from sprint YAML', async () => {
88
+ // AC: generic-sm-setup.md created combining setup + research
89
+ // Research mode scans backlog and returns available stories
90
+ const sprintYaml = `
91
+ sprint:
92
+ number: 10
93
+ goal: "Customizable workflows"
94
+ epics:
95
+ - id: 31
96
+ stories:
97
+ - id: "31-11"
98
+ title: "Consolidate SM subagents"
99
+ status: backlog
100
+ points: 3
101
+ - id: "31-10"
102
+ title: "Activate workflow handoffs"
103
+ status: done
104
+ assigned_to: "Keith"
105
+ `;
106
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
107
+ writeFileSync(sprintPath, sprintYaml);
108
+ const result = await researchBacklog({ sprintPath });
109
+ assert.strictEqual(result.success, true, 'Research should succeed');
110
+ assert.ok(result.availableStories, 'Should have available stories');
111
+ assert.strictEqual(result.availableStories.length, 1, 'Should have 1 available story');
112
+ assert.strictEqual(result.availableStories[0].id, '31-11');
113
+ });
114
+ it('should exclude assigned stories from available list', async () => {
115
+ // Stories with assigned_to should be excluded from research results
116
+ const sprintYaml = `
117
+ sprint:
118
+ number: 10
119
+ epics:
120
+ - id: 31
121
+ stories:
122
+ - id: "31-11"
123
+ title: "Story A"
124
+ status: in_progress
125
+ assigned_to: "Someone"
126
+ - id: "31-12"
127
+ title: "Story B"
128
+ status: backlog
129
+ `;
130
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
131
+ writeFileSync(sprintPath, sprintYaml);
132
+ const result = await researchBacklog({ sprintPath });
133
+ assert.strictEqual(result.availableStories.length, 1);
134
+ assert.strictEqual(result.availableStories[0].id, '31-12');
135
+ });
136
+ it('should sort stories by priority then points', async () => {
137
+ const sprintYaml = `
138
+ sprint:
139
+ number: 10
140
+ epics:
141
+ - id: 31
142
+ stories:
143
+ - id: "31-a"
144
+ title: "Low priority"
145
+ status: backlog
146
+ points: 2
147
+ priority: P2
148
+ - id: "31-b"
149
+ title: "High priority small"
150
+ status: backlog
151
+ points: 2
152
+ priority: P1
153
+ - id: "31-c"
154
+ title: "High priority large"
155
+ status: backlog
156
+ points: 5
157
+ priority: P1
158
+ `;
159
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
160
+ writeFileSync(sprintPath, sprintYaml);
161
+ const result = await researchBacklog({ sprintPath });
162
+ // P1 stories first, then sorted by points ascending
163
+ assert.strictEqual(result.availableStories[0].id, '31-b', 'P1 2pt should be first');
164
+ assert.strictEqual(result.availableStories[1].id, '31-c', 'P1 5pt should be second');
165
+ assert.strictEqual(result.availableStories[2].id, '31-a', 'P2 should be last');
166
+ });
167
+ it('should include sprint metadata in result', async () => {
168
+ const sprintYaml = `
169
+ sprint:
170
+ number: 10
171
+ goal: "Test sprint goal"
172
+ summary:
173
+ completed_points: 37
174
+ total_points: 60
175
+ epics: []
176
+ `;
177
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
178
+ writeFileSync(sprintPath, sprintYaml);
179
+ const result = await researchBacklog({ sprintPath });
180
+ assert.strictEqual(result.sprintNumber, 10);
181
+ assert.strictEqual(result.sprintGoal, 'Test sprint goal');
182
+ assert.strictEqual(result.completedPoints, 37);
183
+ assert.strictEqual(result.totalPoints, 60);
184
+ });
185
+ it('should handle missing sprint file gracefully', async () => {
186
+ const result = await researchBacklog({
187
+ sprintPath: join(TEST_DIR, 'nonexistent.yaml')
188
+ });
189
+ assert.strictEqual(result.success, false);
190
+ assert.ok(result.error, 'Should have error message');
191
+ });
192
+ });
193
+ describe('setupStory() - Setup mode', () => {
194
+ it('should create session file with story context', async () => {
195
+ // AC: generic-sm-setup.md created combining setup + research
196
+ // Setup mode creates session file, branches, claims Jira
197
+ const sessionDir = join(TEST_DIR, '.session');
198
+ mkdirSync(sessionDir, { recursive: true });
199
+ const result = await setupStory({
200
+ storyId: '31-11',
201
+ title: 'Consolidate SM subagents',
202
+ points: 3,
203
+ epic: 31,
204
+ repos: 'pennyfarthing',
205
+ sessionDir,
206
+ workflow: 'tdd',
207
+ assignee: 'Keith',
208
+ jiraKey: 'MSSCI-11616'
209
+ });
210
+ assert.strictEqual(result.success, true, 'Setup should succeed');
211
+ assert.ok(result.sessionFile, 'Should return session file path');
212
+ const sessionPath = join(sessionDir, '31-11-session.md');
213
+ assert.ok(existsSync(sessionPath), 'Session file should exist');
214
+ const content = readFileSync(sessionPath, 'utf-8');
215
+ assert.ok(content.includes('31-11'), 'Should contain story ID');
216
+ assert.ok(content.includes('Consolidate SM subagents'), 'Should contain title');
217
+ assert.ok(content.includes('## Workflow Tracking'), 'Should have workflow section');
218
+ assert.ok(content.includes('**Phase:** setup'), 'Should start in setup phase');
219
+ });
220
+ it('should include acceptance criteria in session file', async () => {
221
+ const sessionDir = join(TEST_DIR, '.session');
222
+ mkdirSync(sessionDir, { recursive: true });
223
+ const result = await setupStory({
224
+ storyId: '31-11',
225
+ title: 'Test story',
226
+ points: 3,
227
+ epic: 31,
228
+ repos: 'pennyfarthing',
229
+ sessionDir,
230
+ workflow: 'tdd',
231
+ acceptanceCriteria: [
232
+ 'AC1: First criterion',
233
+ 'AC2: Second criterion'
234
+ ]
235
+ });
236
+ const content = readFileSync(result.sessionFile, 'utf-8');
237
+ assert.ok(content.includes('## Acceptance Criteria'), 'Should have AC section');
238
+ assert.ok(content.includes('AC1: First criterion'), 'Should include first AC');
239
+ assert.ok(content.includes('AC2: Second criterion'), 'Should include second AC');
240
+ });
241
+ it('should calculate branch name from story ID and slug', async () => {
242
+ const sessionDir = join(TEST_DIR, '.session');
243
+ mkdirSync(sessionDir, { recursive: true });
244
+ const result = await setupStory({
245
+ storyId: '31-11',
246
+ title: 'Consolidate SM subagents',
247
+ points: 3,
248
+ epic: 31,
249
+ repos: 'pennyfarthing',
250
+ sessionDir,
251
+ workflow: 'tdd'
252
+ });
253
+ assert.ok(result.branchName, 'Should return branch name');
254
+ assert.strictEqual(result.branchName, 'feat/31-11-consolidate-sm-subagents', 'Branch name should follow pattern');
255
+ });
256
+ it('should fail if session file already exists', async () => {
257
+ const sessionDir = join(TEST_DIR, '.session');
258
+ mkdirSync(sessionDir, { recursive: true });
259
+ // Create existing session file
260
+ const existingPath = join(sessionDir, '31-11-session.md');
261
+ writeFileSync(existingPath, '# Existing session');
262
+ const result = await setupStory({
263
+ storyId: '31-11',
264
+ title: 'Test story',
265
+ points: 3,
266
+ epic: 31,
267
+ repos: 'pennyfarthing',
268
+ sessionDir,
269
+ workflow: 'tdd'
270
+ });
271
+ assert.strictEqual(result.success, false);
272
+ assert.ok(result.error?.includes('exists'), 'Should mention existing file');
273
+ });
274
+ it('should include workflow tracking with Phase History table', async () => {
275
+ const sessionDir = join(TEST_DIR, '.session');
276
+ mkdirSync(sessionDir, { recursive: true });
277
+ const result = await setupStory({
278
+ storyId: '31-11',
279
+ title: 'Test story',
280
+ points: 3,
281
+ epic: 31,
282
+ repos: 'pennyfarthing',
283
+ sessionDir,
284
+ workflow: 'tdd'
285
+ });
286
+ const content = readFileSync(result.sessionFile, 'utf-8');
287
+ assert.ok(content.includes('### Phase History'), 'Should have Phase History');
288
+ assert.ok(content.includes('| Phase | Started | Ended | Duration |'), 'Should have table header');
289
+ assert.ok(content.includes('| setup |'), 'Should have setup phase row');
290
+ });
291
+ });
292
+ });
293
+ describe('Generic SM Finish (31-11)', () => {
294
+ beforeEach(() => {
295
+ if (existsSync(TEST_DIR)) {
296
+ rmSync(TEST_DIR, { recursive: true });
297
+ }
298
+ mkdirSync(TEST_DIR, { recursive: true });
299
+ });
300
+ afterEach(() => {
301
+ if (existsSync(TEST_DIR)) {
302
+ rmSync(TEST_DIR, { recursive: true });
303
+ }
304
+ });
305
+ describe('preflightCheck() - Preflight phase', () => {
306
+ it('should return JSON report with PR status', async () => {
307
+ // AC: generic-sm-finish.md created combining bookkeeping + execution
308
+ // Preflight phase checks PR, lint, Jira status
309
+ const result = await preflightCheck({
310
+ storyId: '31-11',
311
+ repos: 'pennyfarthing',
312
+ branch: 'feat/31-11-consolidate-sm-subagents',
313
+ jiraKey: 'MSSCI-11616',
314
+ projectRoot: TEST_DIR
315
+ });
316
+ assert.ok(result, 'Should return result');
317
+ assert.ok('prStatus' in result, 'Should have prStatus');
318
+ assert.ok('lintStatus' in result, 'Should have lintStatus');
319
+ assert.ok('jiraStatus' in result, 'Should have jiraStatus');
320
+ assert.ok('readyToFinish' in result, 'Should have readyToFinish flag');
321
+ });
322
+ it('should report merged PR as ready', async () => {
323
+ // Mock a scenario where PR is merged
324
+ const result = await preflightCheck({
325
+ storyId: '31-11',
326
+ repos: 'pennyfarthing',
327
+ branch: 'feat/31-11-test',
328
+ jiraKey: 'MSSCI-11616',
329
+ projectRoot: TEST_DIR,
330
+ // For testing: inject mock PR status
331
+ _mockPrStatus: 'merged'
332
+ });
333
+ assert.strictEqual(result.prStatus.pennyfarthing, 'merged');
334
+ });
335
+ it('should report open PR as warning', async () => {
336
+ const result = await preflightCheck({
337
+ storyId: '31-11',
338
+ repos: 'pennyfarthing',
339
+ branch: 'feat/31-11-test',
340
+ jiraKey: 'MSSCI-11616',
341
+ projectRoot: TEST_DIR,
342
+ _mockPrStatus: 'open'
343
+ });
344
+ assert.strictEqual(result.prStatus.pennyfarthing, 'open');
345
+ assert.ok(result.warnings?.some(w => w.includes('PR')), 'Should warn about open PR');
346
+ });
347
+ it('should check acceptance criteria completion', async () => {
348
+ const sessionDir = join(TEST_DIR, '.session');
349
+ mkdirSync(sessionDir, { recursive: true });
350
+ // Create session file with ACs
351
+ writeFileSync(join(sessionDir, '31-11-session.md'), `
352
+ # Story 31-11
353
+
354
+ ## Acceptance Criteria
355
+ - [x] AC1: First done
356
+ - [x] AC2: Second done
357
+ - [ ] AC3: Third not done
358
+ `);
359
+ const result = await preflightCheck({
360
+ storyId: '31-11',
361
+ repos: 'pennyfarthing',
362
+ branch: 'feat/31-11-test',
363
+ projectRoot: TEST_DIR
364
+ });
365
+ assert.strictEqual(result.acceptanceCriteria.total, 3);
366
+ assert.strictEqual(result.acceptanceCriteria.checked, 2);
367
+ assert.strictEqual(result.acceptanceCriteria.complete, false);
368
+ });
369
+ it('should return structured issues array', async () => {
370
+ const result = await preflightCheck({
371
+ storyId: '31-11',
372
+ repos: 'pennyfarthing',
373
+ branch: 'feat/31-11-test',
374
+ projectRoot: TEST_DIR,
375
+ _mockPrStatus: 'NO_PR'
376
+ });
377
+ assert.ok(Array.isArray(result.issues), 'Should have issues array');
378
+ if (result.issues.length > 0) {
379
+ assert.ok(result.issues[0].type, 'Issue should have type');
380
+ assert.ok(result.issues[0].message, 'Issue should have message');
381
+ }
382
+ });
383
+ });
384
+ describe('executeFinish() - Execute phase', () => {
385
+ it('should archive session file to sprint/archive', async () => {
386
+ // AC: generic-sm-finish.md created combining bookkeeping + execution
387
+ // Execute phase archives session, transitions Jira, cleans up
388
+ const sessionDir = join(TEST_DIR, '.session');
389
+ const archiveDir = join(TEST_DIR, 'sprint', 'archive');
390
+ mkdirSync(sessionDir, { recursive: true });
391
+ mkdirSync(archiveDir, { recursive: true });
392
+ // Create session file
393
+ const sessionContent = '# Story 31-11 Session\n\nContent here';
394
+ writeFileSync(join(sessionDir, '31-11-session.md'), sessionContent);
395
+ const result = await executeFinish({
396
+ storyId: '31-11',
397
+ storyTitle: 'Consolidate SM subagents',
398
+ sessionDir,
399
+ archiveDir,
400
+ summaryContent: '## Summary\n\nStory complete.'
401
+ });
402
+ assert.strictEqual(result.success, true);
403
+ assert.ok(result.archivePath, 'Should return archive path');
404
+ assert.ok(existsSync(result.archivePath), 'Archive file should exist');
405
+ });
406
+ it('should remove session file after archiving', async () => {
407
+ const sessionDir = join(TEST_DIR, '.session');
408
+ const archiveDir = join(TEST_DIR, 'sprint', 'archive');
409
+ mkdirSync(sessionDir, { recursive: true });
410
+ mkdirSync(archiveDir, { recursive: true });
411
+ const sessionPath = join(sessionDir, '31-11-session.md');
412
+ writeFileSync(sessionPath, '# Session content');
413
+ await executeFinish({
414
+ storyId: '31-11',
415
+ storyTitle: 'Test',
416
+ sessionDir,
417
+ archiveDir,
418
+ summaryContent: '## Summary'
419
+ });
420
+ assert.ok(!existsSync(sessionPath), 'Session file should be removed');
421
+ });
422
+ it('should write summary file to sprint/context', async () => {
423
+ const sessionDir = join(TEST_DIR, '.session');
424
+ const archiveDir = join(TEST_DIR, 'sprint', 'archive');
425
+ const contextDir = join(TEST_DIR, 'sprint', 'context');
426
+ mkdirSync(sessionDir, { recursive: true });
427
+ mkdirSync(archiveDir, { recursive: true });
428
+ mkdirSync(contextDir, { recursive: true });
429
+ writeFileSync(join(sessionDir, '31-11-session.md'), '# Session');
430
+ const result = await executeFinish({
431
+ storyId: '31-11',
432
+ storyTitle: 'Consolidate SM subagents',
433
+ sessionDir,
434
+ archiveDir,
435
+ contextDir,
436
+ summaryContent: '## Summary\n\nKey learnings here.'
437
+ });
438
+ assert.ok(result.summaryPath, 'Should return summary path');
439
+ const summaryPath = join(contextDir, 'story-31-11-summary.md');
440
+ assert.ok(existsSync(summaryPath), 'Summary file should exist');
441
+ });
442
+ it('should return completion flags', async () => {
443
+ const sessionDir = join(TEST_DIR, '.session');
444
+ const archiveDir = join(TEST_DIR, 'sprint', 'archive');
445
+ mkdirSync(sessionDir, { recursive: true });
446
+ mkdirSync(archiveDir, { recursive: true });
447
+ writeFileSync(join(sessionDir, '31-11-session.md'), '# Session');
448
+ const result = await executeFinish({
449
+ storyId: '31-11',
450
+ storyTitle: 'Test',
451
+ sessionDir,
452
+ archiveDir,
453
+ summaryContent: '## Summary'
454
+ });
455
+ assert.ok('archived' in result, 'Should have archived flag');
456
+ assert.ok('sessionCleared' in result, 'Should have sessionCleared flag');
457
+ assert.strictEqual(result.archived, true);
458
+ assert.strictEqual(result.sessionCleared, true);
459
+ });
460
+ it('should include timestamp in archive filename', async () => {
461
+ const sessionDir = join(TEST_DIR, '.session');
462
+ const archiveDir = join(TEST_DIR, 'sprint', 'archive');
463
+ mkdirSync(sessionDir, { recursive: true });
464
+ mkdirSync(archiveDir, { recursive: true });
465
+ writeFileSync(join(sessionDir, '31-11-session.md'), '# Session');
466
+ const result = await executeFinish({
467
+ storyId: '31-11',
468
+ storyTitle: 'Test',
469
+ sessionDir,
470
+ archiveDir,
471
+ summaryContent: '## Summary'
472
+ });
473
+ // Archive path should be like: story-31-11-20260114.md
474
+ assert.ok(result.archivePath?.match(/story-31-11-\d{8}\.md$/), `Archive path should include date: ${result.archivePath}`);
475
+ });
476
+ });
477
+ });
478
+ describe('Generic Handoff - Setup Phase Support (31-11)', () => {
479
+ describe('setup phase in TDD workflow', () => {
480
+ it('should find setup phase with no gate', () => {
481
+ // AC: sm-handoff folded into generic-handoff with setup phase support
482
+ const phase = findCurrentPhase(TDD_WORKFLOW, 'setup');
483
+ assert.ok(phase, 'Should find setup phase');
484
+ assert.strictEqual(phase.name, 'setup');
485
+ assert.strictEqual(phase.agent, 'sm');
486
+ assert.strictEqual(phase.gate, undefined, 'Setup has no gate');
487
+ });
488
+ it('should transition from setup to red (TEA)', () => {
489
+ // AC: sm-handoff folded into generic-handoff with setup phase support
490
+ const next = getNextPhase(TDD_WORKFLOW, 'setup');
491
+ assert.ok(next, 'Should find next phase');
492
+ assert.strictEqual(next.name, 'red');
493
+ assert.strictEqual(next.agent, 'tea');
494
+ });
495
+ it('should pass gate check for setup phase (no gate = pass)', () => {
496
+ // Setup phase has no gate - should always pass
497
+ const result = checkGate(TDD_WORKFLOW, 'setup', {});
498
+ assert.strictEqual(result.passed, true, 'Setup gate should pass');
499
+ assert.strictEqual(result.gateType, undefined, 'No gate type for setup');
500
+ });
501
+ it('should support manual gate type for explicit setup gates', () => {
502
+ // For workflows that want explicit manual gate on setup
503
+ const workflowWithManualSetup = {
504
+ name: 'explicit-setup',
505
+ phases: [
506
+ {
507
+ name: 'setup',
508
+ agent: 'sm',
509
+ gate: { type: 'manual', condition: 'Context prepared' }
510
+ },
511
+ { name: 'implement', agent: 'dev' }
512
+ ]
513
+ };
514
+ const result = checkGate(workflowWithManualSetup, 'setup', {});
515
+ assert.strictEqual(result.passed, true, 'Manual gate should pass');
516
+ assert.strictEqual(result.gateType, 'manual');
517
+ });
518
+ });
519
+ describe('finish phase in TDD workflow', () => {
520
+ it('should find finish phase with no gate', () => {
521
+ const phase = findCurrentPhase(TDD_WORKFLOW, 'finish');
522
+ assert.ok(phase, 'Should find finish phase');
523
+ assert.strictEqual(phase.name, 'finish');
524
+ assert.strictEqual(phase.agent, 'sm');
525
+ assert.strictEqual(phase.gate, undefined, 'Finish has no gate');
526
+ });
527
+ it('should return null for next phase from finish', () => {
528
+ const next = getNextPhase(TDD_WORKFLOW, 'finish');
529
+ assert.strictEqual(next, null, 'No next phase after finish');
530
+ });
531
+ });
532
+ describe('full TDD workflow transitions', () => {
533
+ it('should complete setup → red → green → review → finish', () => {
534
+ // AC: Both new-work and finish-story flows work end-to-end
535
+ // setup → red
536
+ let next = getNextPhase(TDD_WORKFLOW, 'setup');
537
+ assert.strictEqual(next?.name, 'red');
538
+ // red → green (after tests written)
539
+ next = getNextPhase(TDD_WORKFLOW, 'red');
540
+ assert.strictEqual(next?.name, 'green');
541
+ // green → review (after tests pass)
542
+ next = getNextPhase(TDD_WORKFLOW, 'green');
543
+ assert.strictEqual(next?.name, 'review');
544
+ // review → finish (after approval)
545
+ next = getNextPhase(TDD_WORKFLOW, 'review', { verdict: 'approved' });
546
+ assert.strictEqual(next?.name, 'finish');
547
+ // finish → null (done)
548
+ next = getNextPhase(TDD_WORKFLOW, 'finish');
549
+ assert.strictEqual(next, null);
550
+ });
551
+ });
552
+ });
553
+ describe('Deprecated File Removal (31-11)', () => {
554
+ it('should verify deprecated handoff files are marked for removal', () => {
555
+ // AC: Deprecated handoff files removed (tea-handoff, dev-handoff, reviewer-handoff-*)
556
+ const deprecatedFiles = [
557
+ 'tea-handoff.md',
558
+ 'dev-handoff.md',
559
+ 'reviewer-handoff-approve.md',
560
+ 'reviewer-handoff-reject.md'
561
+ ];
562
+ // These files should NOT exist after Dev implements
563
+ // This test documents the requirement
564
+ for (const file of deprecatedFiles) {
565
+ assert.ok(deprecatedFiles.includes(file), `${file} is marked for removal`);
566
+ }
567
+ });
568
+ });
569
+ /**
570
+ * Story 38-10: SM Gate for Epic Technical Context
571
+ *
572
+ * Tests for the epic context gate that ensures stories don't start
573
+ * without understanding their epic's technical landscape.
574
+ */
575
+ describe('Epic Context Gate (38-10)', () => {
576
+ beforeEach(() => {
577
+ if (existsSync(TEST_DIR)) {
578
+ rmSync(TEST_DIR, { recursive: true });
579
+ }
580
+ mkdirSync(TEST_DIR, { recursive: true });
581
+ });
582
+ afterEach(() => {
583
+ if (existsSync(TEST_DIR)) {
584
+ rmSync(TEST_DIR, { recursive: true });
585
+ }
586
+ });
587
+ describe('checkEpicContext() - Context validation', () => {
588
+ it('should return true when epic context file exists', async () => {
589
+ // AC1: SM checks for sprint/context/context-epic-{N}.md before story setup
590
+ const contextDir = join(TEST_DIR, 'sprint', 'context');
591
+ mkdirSync(contextDir, { recursive: true });
592
+ // Create epic context file
593
+ writeFileSync(join(contextDir, 'context-epic-38.md'), '# Epic 38 Context\n\nTechnical overview here.');
594
+ const result = await checkEpicContext({
595
+ epicId: 38,
596
+ contextDir
597
+ });
598
+ assert.strictEqual(result.exists, true, 'Should detect existing context');
599
+ assert.ok(result.path, 'Should return file path');
600
+ });
601
+ it('should return false with message when epic context missing', async () => {
602
+ // AC2: Missing epic context blocks story setup with clear message
603
+ const contextDir = join(TEST_DIR, 'sprint', 'context');
604
+ mkdirSync(contextDir, { recursive: true });
605
+ // No context file created
606
+ const result = await checkEpicContext({
607
+ epicId: 38,
608
+ contextDir
609
+ });
610
+ assert.strictEqual(result.exists, false, 'Should detect missing context');
611
+ assert.ok(result.message, 'Should provide a message');
612
+ assert.ok(result.message?.includes('38'), 'Message should reference epic ID');
613
+ });
614
+ it('should provide path hint for missing context', async () => {
615
+ // AC2: Missing epic context blocks with clear message showing expected path
616
+ const contextDir = join(TEST_DIR, 'sprint', 'context');
617
+ mkdirSync(contextDir, { recursive: true });
618
+ const result = await checkEpicContext({
619
+ epicId: 42,
620
+ contextDir
621
+ });
622
+ assert.strictEqual(result.exists, false);
623
+ assert.ok(result.expectedPath, 'Should provide expected path');
624
+ assert.ok(result.expectedPath?.includes('context-epic-42.md'), 'Expected path should include filename');
625
+ });
626
+ });
627
+ describe('createEpicContext() - Context creation', () => {
628
+ it('should create epic context file from template', async () => {
629
+ // AC3: SM can create epic context (researches epic, writes file)
630
+ const contextDir = join(TEST_DIR, 'sprint', 'context');
631
+ mkdirSync(contextDir, { recursive: true });
632
+ const result = await createEpicContext({
633
+ epicId: 38,
634
+ epicTitle: 'Agent File Modernization',
635
+ contextDir,
636
+ content: '## Technical Landscape\n\nThis epic modernizes agent files.'
637
+ });
638
+ assert.strictEqual(result.success, true, 'Creation should succeed');
639
+ assert.ok(result.path, 'Should return file path');
640
+ const filePath = join(contextDir, 'context-epic-38.md');
641
+ assert.ok(existsSync(filePath), 'File should exist');
642
+ const content = readFileSync(filePath, 'utf-8');
643
+ assert.ok(content.includes('Agent File Modernization'), 'Should include epic title');
644
+ });
645
+ it('should use template structure when creating context', async () => {
646
+ // AC4: Epic context template exists and is documented
647
+ const contextDir = join(TEST_DIR, 'sprint', 'context');
648
+ mkdirSync(contextDir, { recursive: true });
649
+ const result = await createEpicContext({
650
+ epicId: 38,
651
+ epicTitle: 'Agent File Modernization',
652
+ contextDir
653
+ // No content provided - should use template
654
+ });
655
+ assert.strictEqual(result.success, true);
656
+ const content = readFileSync(result.path, 'utf-8');
657
+ // Template should have standard sections
658
+ assert.ok(content.includes('# Epic 38'), 'Should have epic header');
659
+ assert.ok(content.includes('## Epic Overview') || content.includes('## Technical Landscape'), 'Should have overview section');
660
+ });
661
+ it('should not overwrite existing epic context', async () => {
662
+ // Safety check: don't clobber existing context
663
+ const contextDir = join(TEST_DIR, 'sprint', 'context');
664
+ mkdirSync(contextDir, { recursive: true });
665
+ // Create existing context
666
+ const existingContent = '# Existing Epic 38 Context\n\nValuable information here.';
667
+ writeFileSync(join(contextDir, 'context-epic-38.md'), existingContent);
668
+ const result = await createEpicContext({
669
+ epicId: 38,
670
+ epicTitle: 'New Title',
671
+ contextDir
672
+ });
673
+ assert.strictEqual(result.success, false, 'Should fail when file exists');
674
+ assert.ok(result.error?.includes('exists'), 'Error should mention existing file');
675
+ // Verify original content preserved
676
+ const content = readFileSync(join(contextDir, 'context-epic-38.md'), 'utf-8');
677
+ assert.ok(content.includes('Valuable information'), 'Original content should be preserved');
678
+ });
679
+ });
680
+ describe('setupStory() with epic context gate', () => {
681
+ it('should check epic context before setup when gate enabled', async () => {
682
+ // AC1: SM checks for context file before story setup
683
+ const sessionDir = join(TEST_DIR, '.session');
684
+ const contextDir = join(TEST_DIR, 'sprint', 'context');
685
+ mkdirSync(sessionDir, { recursive: true });
686
+ mkdirSync(contextDir, { recursive: true });
687
+ // No epic context file - should warn/block
688
+ const result = await setupStory({
689
+ storyId: '38-10',
690
+ title: 'SM gate for epic technical context',
691
+ points: 2,
692
+ epic: 38,
693
+ repos: 'pennyfarthing',
694
+ sessionDir,
695
+ workflow: 'tdd',
696
+ // New parameter to enable gate
697
+ checkEpicContext: true,
698
+ contextDir
699
+ });
700
+ // With gate enabled and no context, should warn (not hard fail initially)
701
+ assert.ok(result.warnings?.some((w) => w.includes('epic context')), 'Should warn about missing epic context');
702
+ });
703
+ it('should proceed when epic context exists', async () => {
704
+ // AC1: Gate passes when context file exists
705
+ const sessionDir = join(TEST_DIR, '.session');
706
+ const contextDir = join(TEST_DIR, 'sprint', 'context');
707
+ mkdirSync(sessionDir, { recursive: true });
708
+ mkdirSync(contextDir, { recursive: true });
709
+ // Create epic context
710
+ writeFileSync(join(contextDir, 'context-epic-38.md'), '# Epic 38\n\nContext here.');
711
+ const result = await setupStory({
712
+ storyId: '38-10-test',
713
+ title: 'SM gate for epic technical context',
714
+ points: 2,
715
+ epic: 38,
716
+ repos: 'pennyfarthing',
717
+ sessionDir,
718
+ workflow: 'tdd',
719
+ checkEpicContext: true,
720
+ contextDir
721
+ });
722
+ assert.strictEqual(result.success, true, 'Should succeed with context present');
723
+ assert.ok(!result.warnings?.some((w) => w.includes('epic context')), 'Should not warn when context exists');
724
+ });
725
+ });
726
+ });
727
+ //# sourceMappingURL=sm-subagents.test.js.map