@pennyfarthing/core 7.0.2 → 7.4.1

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 (628) hide show
  1. package/LICENSE +14 -0
  2. package/README.md +2 -2
  3. package/package.json +11 -10
  4. package/packages/core/dist/cli/commands/cyclist.d.ts +2 -2
  5. package/packages/core/dist/cli/commands/cyclist.d.ts.map +1 -1
  6. package/packages/core/dist/cli/commands/cyclist.js +8 -9
  7. package/packages/core/dist/cli/commands/cyclist.js.map +1 -1
  8. package/packages/core/dist/cli/commands/cyclist.test.js +6 -4
  9. package/packages/core/dist/cli/commands/cyclist.test.js.map +1 -1
  10. package/packages/core/dist/cli/commands/init.js +4 -3
  11. package/packages/core/dist/cli/commands/init.js.map +1 -1
  12. package/packages/core/dist/cli/commands/update.d.ts.map +1 -1
  13. package/packages/core/dist/cli/commands/update.js +15 -139
  14. package/packages/core/dist/cli/commands/update.js.map +1 -1
  15. package/packages/core/dist/cli/cyclist-migration.test.js +4 -3
  16. package/packages/core/dist/cli/cyclist-migration.test.js.map +1 -1
  17. package/packages/core/dist/cli/utils/constants.d.ts +7 -1
  18. package/packages/core/dist/cli/utils/constants.d.ts.map +1 -1
  19. package/packages/core/dist/cli/utils/constants.js +4 -2
  20. package/packages/core/dist/cli/utils/constants.js.map +1 -1
  21. package/packages/core/dist/cli/utils/node-modules.d.ts.map +1 -1
  22. package/packages/core/dist/cli/utils/node-modules.js +2 -1
  23. package/packages/core/dist/cli/utils/node-modules.js.map +1 -1
  24. package/packages/core/dist/cli/utils/themes.d.ts +1 -4
  25. package/packages/core/dist/cli/utils/themes.d.ts.map +1 -1
  26. package/packages/core/dist/cli/utils/themes.js +2 -22
  27. package/packages/core/dist/cli/utils/themes.js.map +1 -1
  28. package/packages/core/dist/cli/utils/themes.test.d.ts +3 -3
  29. package/packages/core/dist/cli/utils/themes.test.js +16 -13
  30. package/packages/core/dist/cli/utils/themes.test.js.map +1 -1
  31. package/packages/core/dist/cli/workspace.test.js +11 -9
  32. package/packages/core/dist/cli/workspace.test.js.map +1 -1
  33. package/packages/core/dist/index.d.ts +1 -0
  34. package/packages/core/dist/index.d.ts.map +1 -1
  35. package/packages/core/dist/index.js +10 -0
  36. package/packages/core/dist/index.js.map +1 -1
  37. package/packages/core/dist/jira/jira-epic-creation.d.ts +109 -0
  38. package/packages/core/dist/jira/jira-epic-creation.d.ts.map +1 -0
  39. package/packages/core/dist/jira/jira-epic-creation.js +253 -0
  40. package/packages/core/dist/jira/jira-epic-creation.js.map +1 -0
  41. package/packages/core/dist/jira/jira-epic-creation.test.d.ts +16 -0
  42. package/packages/core/dist/jira/jira-epic-creation.test.d.ts.map +1 -0
  43. package/packages/core/dist/jira/jira-epic-creation.test.js +387 -0
  44. package/packages/core/dist/jira/jira-epic-creation.test.js.map +1 -0
  45. package/packages/core/dist/jira/jira-sprint-sync.d.ts +247 -0
  46. package/packages/core/dist/jira/jira-sprint-sync.d.ts.map +1 -0
  47. package/packages/core/dist/jira/jira-sprint-sync.js +670 -0
  48. package/packages/core/dist/jira/jira-sprint-sync.js.map +1 -0
  49. package/packages/core/dist/jira/jira-sprint-sync.test.d.ts +16 -0
  50. package/packages/core/dist/jira/jira-sprint-sync.test.d.ts.map +1 -0
  51. package/packages/core/dist/jira/jira-sprint-sync.test.js +845 -0
  52. package/packages/core/dist/jira/jira-sprint-sync.test.js.map +1 -0
  53. package/packages/core/dist/scripts/generate-spider.d.ts +11 -1
  54. package/packages/core/dist/scripts/generate-spider.d.ts.map +1 -1
  55. package/packages/core/dist/scripts/generate-spider.js +24 -1
  56. package/packages/core/dist/scripts/generate-spider.js.map +1 -1
  57. package/packages/core/dist/scripts/generate-spider.test.js +6 -4
  58. package/packages/core/dist/scripts/generate-spider.test.js.map +1 -1
  59. package/packages/core/dist/workflow/gate-handler.d.ts +94 -0
  60. package/packages/core/dist/workflow/gate-handler.d.ts.map +1 -0
  61. package/packages/core/dist/workflow/gate-handler.js +189 -0
  62. package/packages/core/dist/workflow/gate-handler.js.map +1 -0
  63. package/packages/core/dist/workflow/gate-handler.test.d.ts +14 -0
  64. package/packages/core/dist/workflow/gate-handler.test.d.ts.map +1 -0
  65. package/packages/core/dist/workflow/gate-handler.test.js +543 -0
  66. package/packages/core/dist/workflow/gate-handler.test.js.map +1 -0
  67. package/packages/core/dist/workflow/generic-handoff.d.ts +46 -0
  68. package/packages/core/dist/workflow/generic-handoff.d.ts.map +1 -1
  69. package/packages/core/dist/workflow/generic-handoff.js +53 -0
  70. package/packages/core/dist/workflow/generic-handoff.js.map +1 -1
  71. package/packages/core/dist/workflow/generic-handoff.test.js +2 -2
  72. package/packages/core/dist/workflow/generic-handoff.test.js.map +1 -1
  73. package/packages/core/dist/workflow/handoff.d.ts +281 -0
  74. package/packages/core/dist/workflow/handoff.d.ts.map +1 -0
  75. package/packages/core/dist/workflow/handoff.js +411 -0
  76. package/packages/core/dist/workflow/handoff.js.map +1 -0
  77. package/packages/core/dist/workflow/handoff.test.d.ts +21 -0
  78. package/packages/core/dist/workflow/handoff.test.d.ts.map +1 -0
  79. package/packages/core/dist/workflow/handoff.test.js +499 -0
  80. package/packages/core/dist/workflow/handoff.test.js.map +1 -0
  81. package/packages/core/dist/workflow/index.d.ts +16 -0
  82. package/packages/core/dist/workflow/index.d.ts.map +1 -0
  83. package/packages/core/dist/workflow/index.js +24 -0
  84. package/packages/core/dist/workflow/index.js.map +1 -0
  85. package/packages/core/dist/workflow/session-state.d.ts +92 -0
  86. package/packages/core/dist/workflow/session-state.d.ts.map +1 -0
  87. package/packages/core/dist/workflow/session-state.js +198 -0
  88. package/packages/core/dist/workflow/session-state.js.map +1 -0
  89. package/packages/core/dist/workflow/session-state.test.d.ts +8 -0
  90. package/packages/core/dist/workflow/session-state.test.d.ts.map +1 -0
  91. package/packages/core/dist/workflow/session-state.test.js +551 -0
  92. package/packages/core/dist/workflow/session-state.test.js.map +1 -0
  93. package/packages/core/dist/workflow/sm-subagents.test.d.ts +2 -2
  94. package/packages/core/dist/workflow/sm-subagents.test.js +6 -6
  95. package/packages/core/dist/workflow/sm-subagents.test.js.map +1 -1
  96. package/packages/core/dist/workflow/step-parser.d.ts +45 -0
  97. package/packages/core/dist/workflow/step-parser.d.ts.map +1 -0
  98. package/packages/core/dist/workflow/step-parser.js +147 -0
  99. package/packages/core/dist/workflow/step-parser.js.map +1 -0
  100. package/packages/core/dist/workflow/step-parser.test.d.ts +14 -0
  101. package/packages/core/dist/workflow/step-parser.test.d.ts.map +1 -0
  102. package/packages/core/dist/workflow/step-parser.test.js +470 -0
  103. package/packages/core/dist/workflow/step-parser.test.js.map +1 -0
  104. package/packages/core/dist/workflow/trimodal.d.ts +86 -0
  105. package/packages/core/dist/workflow/trimodal.d.ts.map +1 -0
  106. package/packages/core/dist/workflow/trimodal.js +118 -0
  107. package/packages/core/dist/workflow/trimodal.js.map +1 -0
  108. package/packages/core/dist/workflow/trimodal.test.d.ts +11 -0
  109. package/packages/core/dist/workflow/trimodal.test.d.ts.map +1 -0
  110. package/packages/core/dist/workflow/trimodal.test.js +395 -0
  111. package/packages/core/dist/workflow/trimodal.test.js.map +1 -0
  112. package/packages/core/dist/workflow/variable-resolver.d.ts +67 -0
  113. package/packages/core/dist/workflow/variable-resolver.d.ts.map +1 -0
  114. package/packages/core/dist/workflow/variable-resolver.js +156 -0
  115. package/packages/core/dist/workflow/variable-resolver.js.map +1 -0
  116. package/packages/core/dist/workflow/variable-resolver.test.d.ts +14 -0
  117. package/packages/core/dist/workflow/variable-resolver.test.d.ts.map +1 -0
  118. package/packages/core/dist/workflow/variable-resolver.test.js +400 -0
  119. package/packages/core/dist/workflow/variable-resolver.test.js.map +1 -0
  120. package/packages/core/dist/workflow/workflow-executor.d.ts +163 -0
  121. package/packages/core/dist/workflow/workflow-executor.d.ts.map +1 -0
  122. package/packages/core/dist/workflow/workflow-executor.js +197 -0
  123. package/packages/core/dist/workflow/workflow-executor.js.map +1 -0
  124. package/packages/core/dist/workflow/workflow-executor.test.d.ts +8 -0
  125. package/packages/core/dist/workflow/workflow-executor.test.d.ts.map +1 -0
  126. package/packages/core/dist/workflow/workflow-executor.test.js +444 -0
  127. package/packages/core/dist/workflow/workflow-executor.test.js.map +1 -0
  128. package/packages/core/dist/workflow/workflow-loader.test.js +5 -5
  129. package/packages/core/dist/workflow/workflow-loader.test.js.map +1 -1
  130. package/packages/core/dist/workflow/workflow-migration.test.js +8 -9
  131. package/packages/core/dist/workflow/workflow-migration.test.js.map +1 -1
  132. package/packages/core/dist/workflow/workflow-permissions.d.ts +55 -0
  133. package/packages/core/dist/workflow/workflow-permissions.d.ts.map +1 -0
  134. package/packages/core/dist/workflow/workflow-permissions.js +64 -0
  135. package/packages/core/dist/workflow/workflow-permissions.js.map +1 -0
  136. package/packages/core/dist/workflow/workflow-permissions.test.d.ts +15 -0
  137. package/packages/core/dist/workflow/workflow-permissions.test.d.ts.map +1 -0
  138. package/packages/core/dist/workflow/workflow-permissions.test.js +301 -0
  139. package/packages/core/dist/workflow/workflow-permissions.test.js.map +1 -0
  140. package/packages/core/dist/workflow/workflow-schema.d.ts +61 -2
  141. package/packages/core/dist/workflow/workflow-schema.d.ts.map +1 -1
  142. package/packages/core/dist/workflow/workflow-schema.js +293 -69
  143. package/packages/core/dist/workflow/workflow-schema.js.map +1 -1
  144. package/packages/core/dist/workflow/workflow-schema.test.js +6 -6
  145. package/packages/core/dist/workflow/workflow-schema.test.js.map +1 -1
  146. package/packages/core/dist/workflow/workflow-stepped-schema.test.d.ts +18 -0
  147. package/packages/core/dist/workflow/workflow-stepped-schema.test.d.ts.map +1 -0
  148. package/packages/core/dist/workflow/workflow-stepped-schema.test.js +608 -0
  149. package/packages/core/dist/workflow/workflow-stepped-schema.test.js.map +1 -0
  150. package/pennyfarthing-dist/agents/README.md +63 -134
  151. package/pennyfarthing-dist/agents/architect.md +18 -10
  152. package/pennyfarthing-dist/agents/dev.md +47 -32
  153. package/pennyfarthing-dist/agents/devops.md +18 -9
  154. package/pennyfarthing-dist/agents/handoff.md +289 -0
  155. package/pennyfarthing-dist/agents/orchestrator.md +44 -22
  156. package/pennyfarthing-dist/agents/pm.md +13 -8
  157. package/pennyfarthing-dist/agents/reviewer-preflight.md +42 -128
  158. package/pennyfarthing-dist/agents/reviewer.md +104 -57
  159. package/pennyfarthing-dist/agents/sm-file-summary.md +24 -75
  160. package/pennyfarthing-dist/agents/sm-finish.md +61 -0
  161. package/pennyfarthing-dist/agents/sm-handoff.md +102 -54
  162. package/pennyfarthing-dist/agents/sm-setup.md +174 -0
  163. package/pennyfarthing-dist/agents/sm.md +223 -100
  164. package/pennyfarthing-dist/agents/tea.md +42 -34
  165. package/pennyfarthing-dist/agents/tech-writer.md +74 -6
  166. package/pennyfarthing-dist/agents/testing-runner.md +78 -360
  167. package/pennyfarthing-dist/agents/ux-designer.md +80 -7
  168. package/pennyfarthing-dist/agents/workflow-status-check.md +35 -299
  169. package/pennyfarthing-dist/commands/architect.md +2 -2
  170. package/pennyfarthing-dist/commands/close-epic.md +5 -2
  171. package/pennyfarthing-dist/commands/create-branches-from-story.md +7 -23
  172. package/pennyfarthing-dist/commands/dev.md +2 -2
  173. package/pennyfarthing-dist/commands/devops.md +3 -3
  174. package/pennyfarthing-dist/commands/git-cleanup.md +5 -5
  175. package/pennyfarthing-dist/commands/health-check.md +1 -1
  176. package/pennyfarthing-dist/commands/list-themes.md +7 -3
  177. package/pennyfarthing-dist/commands/orchestrator.md +2 -2
  178. package/pennyfarthing-dist/commands/parallel-work.md +4 -4
  179. package/pennyfarthing-dist/commands/pm.md +2 -2
  180. package/pennyfarthing-dist/commands/prime.md +7 -7
  181. package/pennyfarthing-dist/commands/release.md +1 -1
  182. package/pennyfarthing-dist/commands/repo-status.md +2 -2
  183. package/pennyfarthing-dist/commands/retro.md +2 -2
  184. package/pennyfarthing-dist/commands/reviewer.md +2 -2
  185. package/pennyfarthing-dist/commands/set-theme.md +5 -1
  186. package/pennyfarthing-dist/commands/sm.md +2 -2
  187. package/pennyfarthing-dist/commands/start-epic.md +26 -14
  188. package/pennyfarthing-dist/commands/sync-epic-to-jira.md +8 -8
  189. package/pennyfarthing-dist/commands/sync-work-with-sprint.md +1 -4
  190. package/pennyfarthing-dist/commands/tea.md +2 -2
  191. package/pennyfarthing-dist/commands/tech-writer.md +2 -2
  192. package/pennyfarthing-dist/commands/ux-designer.md +3 -3
  193. package/pennyfarthing-dist/commands/work.md +15 -4
  194. package/pennyfarthing-dist/commands/workflow.md +21 -0
  195. package/pennyfarthing-dist/guides/AGENT-COORDINATION.md +15 -15
  196. package/pennyfarthing-dist/guides/PROMPT-PATTERNS.md +1 -1
  197. package/pennyfarthing-dist/guides/SESSION-ARTIFACTS.md +4 -4
  198. package/pennyfarthing-dist/guides/agent-behavior.md +238 -0
  199. package/pennyfarthing-dist/guides/agent-template-strategic.md +2 -2
  200. package/pennyfarthing-dist/guides/agent-template-tactical.md +4 -4
  201. package/pennyfarthing-dist/guides/patterns/approval-gates-pattern.md +1 -1
  202. package/pennyfarthing-dist/guides/patterns/helper-delegation-pattern.md +17 -17
  203. package/pennyfarthing-dist/guides/patterns/tdd-flow-pattern.md +8 -8
  204. package/pennyfarthing-dist/guides/workflow-schema.md +62 -0
  205. package/pennyfarthing-dist/guides/worktree-mode.md +5 -5
  206. package/pennyfarthing-dist/personas/themes/1984.yaml +1 -1
  207. package/pennyfarthing-dist/personas/themes/a-team.yaml +1 -1
  208. package/pennyfarthing-dist/personas/themes/agatha-christie.yaml +1 -1
  209. package/pennyfarthing-dist/personas/themes/alice-in-wonderland.yaml +1 -1
  210. package/pennyfarthing-dist/personas/themes/all-stars.yaml +1 -1
  211. package/pennyfarthing-dist/personas/themes/ancient-philosophers.yaml +1 -1
  212. package/pennyfarthing-dist/personas/themes/ancient-strategists.yaml +1 -1
  213. package/pennyfarthing-dist/personas/themes/arcane.yaml +1 -1
  214. package/pennyfarthing-dist/personas/themes/arthurian-mythos.yaml +1 -1
  215. package/pennyfarthing-dist/personas/themes/avatar-the-last-airbender.yaml +1 -1
  216. package/pennyfarthing-dist/personas/themes/babylon-5.yaml +1 -1
  217. package/pennyfarthing-dist/personas/themes/battlestar-galactica.yaml +1 -1
  218. package/pennyfarthing-dist/personas/themes/better-call-saul.yaml +1 -1
  219. package/pennyfarthing-dist/personas/themes/big-lebowski.yaml +2 -2
  220. package/pennyfarthing-dist/personas/themes/black-sails.yaml +1 -1
  221. package/pennyfarthing-dist/personas/themes/blade-runner.yaml +1 -1
  222. package/pennyfarthing-dist/personas/themes/bobiverse.yaml +1 -1
  223. package/pennyfarthing-dist/personas/themes/breaking-bad.yaml +1 -1
  224. package/pennyfarthing-dist/personas/themes/catch-22.yaml +1 -1
  225. package/pennyfarthing-dist/personas/themes/classical-composers.yaml +1 -1
  226. package/pennyfarthing-dist/personas/themes/control.yaml +1 -1
  227. package/pennyfarthing-dist/personas/themes/count-of-monte-cristo.yaml +1 -1
  228. package/pennyfarthing-dist/personas/themes/cowboy-bebop.yaml +1 -1
  229. package/pennyfarthing-dist/personas/themes/deadwood.yaml +2 -2
  230. package/pennyfarthing-dist/personas/themes/discworld.yaml +2 -2
  231. package/pennyfarthing-dist/personas/themes/doctor-who.yaml +1 -1
  232. package/pennyfarthing-dist/personas/themes/enlightenment-thinkers.yaml +1 -1
  233. package/pennyfarthing-dist/personas/themes/expeditionary-force.yaml +1 -1
  234. package/pennyfarthing-dist/personas/themes/firefly.yaml +1 -1
  235. package/pennyfarthing-dist/personas/themes/foundation.yaml +1 -1
  236. package/pennyfarthing-dist/personas/themes/futurama.yaml +1 -1
  237. package/pennyfarthing-dist/personas/themes/game-of-thrones.yaml +2 -2
  238. package/pennyfarthing-dist/personas/themes/gilligans-island.yaml +1 -1
  239. package/pennyfarthing-dist/personas/themes/gothic-literature.yaml +1 -1
  240. package/pennyfarthing-dist/personas/themes/great-gatsby.yaml +1 -1
  241. package/pennyfarthing-dist/personas/themes/greek-mythology.yaml +1 -1
  242. package/pennyfarthing-dist/personas/themes/harry-potter.yaml +2 -2
  243. package/pennyfarthing-dist/personas/themes/his-dark-materials.yaml +1 -1
  244. package/pennyfarthing-dist/personas/themes/historical-figures.yaml +1 -1
  245. package/pennyfarthing-dist/personas/themes/hitchhikers-guide.yaml +1 -1
  246. package/pennyfarthing-dist/personas/themes/house-md.yaml +1 -1
  247. package/pennyfarthing-dist/personas/themes/imperial-radch.yaml +1 -1
  248. package/pennyfarthing-dist/personas/themes/jane-austen.yaml +1 -1
  249. package/pennyfarthing-dist/personas/themes/jazz-legends.yaml +1 -1
  250. package/pennyfarthing-dist/personas/themes/justified.yaml +1 -1
  251. package/pennyfarthing-dist/personas/themes/legion-of-doom.yaml +1 -1
  252. package/pennyfarthing-dist/personas/themes/les-miserables.yaml +1 -1
  253. package/pennyfarthing-dist/personas/themes/lord-of-the-rings.yaml +1 -1
  254. package/pennyfarthing-dist/personas/themes/lovecraft-mythos.yaml +1 -1
  255. package/pennyfarthing-dist/personas/themes/mad-men.yaml +1 -1
  256. package/pennyfarthing-dist/personas/themes/marvel-mcu.yaml +1 -1
  257. package/pennyfarthing-dist/personas/themes/mash.yaml +69 -66
  258. package/pennyfarthing-dist/personas/themes/mass-effect.yaml +1 -1
  259. package/pennyfarthing-dist/personas/themes/military-commanders.yaml +1 -1
  260. package/pennyfarthing-dist/personas/themes/moby-dick.yaml +1 -1
  261. package/pennyfarthing-dist/personas/themes/monty-python.yaml +1 -1
  262. package/pennyfarthing-dist/personas/themes/neuromancer.yaml +1 -1
  263. package/pennyfarthing-dist/personas/themes/norse-mythology.yaml +1 -1
  264. package/pennyfarthing-dist/personas/themes/peaky-blinders.yaml +1 -1
  265. package/pennyfarthing-dist/personas/themes/princess-bride.yaml +1 -1
  266. package/pennyfarthing-dist/personas/themes/rome.yaml +1 -1
  267. package/pennyfarthing-dist/personas/themes/russian-masters.yaml +1 -1
  268. package/pennyfarthing-dist/personas/themes/sandman.yaml +1 -1
  269. package/pennyfarthing-dist/personas/themes/shakespeare.yaml +1 -1
  270. package/pennyfarthing-dist/personas/themes/sherlock-holmes.yaml +1 -1
  271. package/pennyfarthing-dist/personas/themes/snow-crash.yaml +1 -1
  272. package/pennyfarthing-dist/personas/themes/software-pioneers.yaml +1 -1
  273. package/pennyfarthing-dist/personas/themes/star-trek-tng.yaml +1 -1
  274. package/pennyfarthing-dist/personas/themes/star-trek-tos.yaml +1 -1
  275. package/pennyfarthing-dist/personas/themes/star-wars.yaml +1 -1
  276. package/pennyfarthing-dist/personas/themes/superfriends.yaml +1 -1
  277. package/pennyfarthing-dist/personas/themes/the-americans.yaml +1 -1
  278. package/pennyfarthing-dist/personas/themes/the-crown.yaml +1 -1
  279. package/pennyfarthing-dist/personas/themes/the-expanse.yaml +1 -1
  280. package/pennyfarthing-dist/personas/themes/the-good-place.yaml +1 -1
  281. package/pennyfarthing-dist/personas/themes/the-office.yaml +1 -1
  282. package/pennyfarthing-dist/personas/themes/the-simpsons.yaml +1 -1
  283. package/pennyfarthing-dist/personas/themes/the-sopranos.yaml +1 -1
  284. package/pennyfarthing-dist/personas/themes/the-wire.yaml +1 -1
  285. package/pennyfarthing-dist/personas/themes/the-witcher.yaml +1 -1
  286. package/pennyfarthing-dist/personas/themes/vorkosigan-saga.yaml +1 -1
  287. package/pennyfarthing-dist/personas/themes/west-wing.yaml +1 -1
  288. package/pennyfarthing-dist/personas/themes/wwii-leaders.yaml +1 -1
  289. package/pennyfarthing-dist/scripts/README.md +68 -0
  290. package/pennyfarthing-dist/scripts/core/README.md +26 -0
  291. package/pennyfarthing-dist/scripts/{agent-session.sh → core/agent-session.sh} +34 -23
  292. package/pennyfarthing-dist/scripts/{check-context.sh → core/check-context.sh} +93 -37
  293. package/pennyfarthing-dist/scripts/core/handoff-marker.sh +90 -0
  294. package/pennyfarthing-dist/scripts/core/prime.sh +136 -0
  295. package/pennyfarthing-dist/scripts/core/run.sh +75 -0
  296. package/pennyfarthing-dist/scripts/cyclist/is-cyclist.sh +21 -0
  297. package/pennyfarthing-dist/scripts/git/README.md +25 -0
  298. package/pennyfarthing-dist/scripts/{utils → git}/create-feature-branches.sh +11 -15
  299. package/pennyfarthing-dist/scripts/{utils → git}/git-status-all.sh +1 -1
  300. package/pennyfarthing-dist/scripts/{install-git-hooks.sh → git/install-git-hooks.sh} +0 -0
  301. package/pennyfarthing-dist/scripts/{release.sh → git/release.sh} +0 -0
  302. package/pennyfarthing-dist/scripts/{worktree-manager.sh → git/worktree-manager.sh} +0 -4
  303. package/pennyfarthing-dist/scripts/hooks/README.md +32 -0
  304. package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +3 -4
  305. package/pennyfarthing-dist/scripts/hooks/context-warning.sh +1 -2
  306. package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +0 -0
  307. package/pennyfarthing-dist/scripts/hooks/post-merge.sh +1 -1
  308. package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +0 -0
  309. package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +0 -0
  310. package/pennyfarthing-dist/scripts/hooks/pre-push.sh +0 -0
  311. package/pennyfarthing-dist/scripts/hooks/session-start.sh +2 -3
  312. package/pennyfarthing-dist/scripts/hooks/session-stop.sh +2 -3
  313. package/pennyfarthing-dist/scripts/jira/README.md +33 -0
  314. package/pennyfarthing-dist/scripts/jira/create-jira-epic.sh +101 -0
  315. package/pennyfarthing-dist/scripts/jira/create-jira-story.sh +97 -0
  316. package/pennyfarthing-dist/scripts/jira/jira-bidirectional-sync.mjs +327 -0
  317. package/pennyfarthing-dist/scripts/jira/jira-bidirectional-sync.test.mjs +503 -0
  318. package/pennyfarthing-dist/scripts/{utils → jira}/jira-claim-story.sh +8 -6
  319. package/pennyfarthing-dist/scripts/{utils/jira → jira}/jira-lib.mjs +10 -10
  320. package/pennyfarthing-dist/scripts/{utils → jira}/jira-lib.sh +18 -17
  321. package/pennyfarthing-dist/scripts/jira/jira-reconcile.sh +266 -0
  322. package/pennyfarthing-dist/scripts/{utils/jira → jira}/jira-sync-story.mjs +14 -14
  323. package/pennyfarthing-dist/scripts/{utils → jira}/jira-sync-story.sh +0 -0
  324. package/pennyfarthing-dist/scripts/{utils/jira → jira}/jira-sync.mjs +4 -4
  325. package/pennyfarthing-dist/scripts/{utils → jira}/jira-sync.sh +0 -0
  326. package/pennyfarthing-dist/scripts/jira/sync-epic-jira.sh +104 -0
  327. package/pennyfarthing-dist/scripts/{utils → jira}/sync-epic-to-jira.sh +1 -1
  328. package/pennyfarthing-dist/scripts/lib/README.md +29 -0
  329. package/pennyfarthing-dist/scripts/{utils → lib}/background-tasks.sh +1 -1
  330. package/pennyfarthing-dist/scripts/{utils → lib}/checkpoint.sh +0 -0
  331. package/pennyfarthing-dist/scripts/{utils → lib}/common.sh +1 -1
  332. package/pennyfarthing-dist/scripts/{utils → lib}/file-lock.sh +0 -0
  333. package/pennyfarthing-dist/scripts/{utils → lib}/find-root.sh +7 -5
  334. package/pennyfarthing-dist/scripts/{utils → lib}/logging.sh +0 -0
  335. package/pennyfarthing-dist/scripts/{utils → lib}/retry.sh +0 -0
  336. package/pennyfarthing-dist/scripts/misc/README.md +44 -0
  337. package/pennyfarthing-dist/scripts/{add-short-names.mjs → misc/add-short-names.mjs} +0 -0
  338. package/pennyfarthing-dist/scripts/misc/backlog.sh +91 -0
  339. package/pennyfarthing-dist/scripts/{utils → misc}/check-status.sh +4 -8
  340. package/pennyfarthing-dist/scripts/{deploy.sh → misc/deploy.sh} +0 -0
  341. package/pennyfarthing-dist/scripts/{doctor-dogfood.sh → misc/doctor-dogfood.sh} +2 -5
  342. package/pennyfarthing-dist/scripts/{utils → misc}/find-related-work.sh +0 -0
  343. package/pennyfarthing-dist/scripts/{utils → misc}/generate-skill-docs.sh +0 -0
  344. package/pennyfarthing-dist/scripts/{utils → misc}/log-skill-usage.sh +0 -0
  345. package/pennyfarthing-dist/scripts/misc/migrate-bmad-workflow.mjs +474 -0
  346. package/pennyfarthing-dist/scripts/misc/migrate-bmad-workflow.sh +9 -0
  347. package/pennyfarthing-dist/scripts/{utils → misc}/repo-scan.sh +0 -0
  348. package/pennyfarthing-dist/scripts/{repo-utils.sh → misc/repo-utils.sh} +2 -2
  349. package/pennyfarthing-dist/scripts/{run-ci.sh → misc/run-ci.sh} +0 -0
  350. package/pennyfarthing-dist/scripts/{utils → misc}/run-timestamp.sh +0 -0
  351. package/pennyfarthing-dist/scripts/{utils → misc}/session-cleanup.sh +0 -0
  352. package/pennyfarthing-dist/scripts/{utils → misc}/skill-usage-report.sh +0 -0
  353. package/pennyfarthing-dist/scripts/{statusline.sh → misc/statusline.sh} +2 -7
  354. package/pennyfarthing-dist/scripts/{uninstall.sh → misc/uninstall.sh} +4 -5
  355. package/pennyfarthing-dist/scripts/{utils → misc}/validate-subagent-frontmatter.sh +3 -3
  356. package/pennyfarthing-dist/scripts/sprint/README.md +29 -0
  357. package/pennyfarthing-dist/scripts/sprint/archive-story.sh +135 -0
  358. package/pennyfarthing-dist/scripts/sprint/available-stories.sh +97 -0
  359. package/pennyfarthing-dist/scripts/sprint/check-story.sh +164 -0
  360. package/pennyfarthing-dist/scripts/sprint/get-epic-field.sh +58 -0
  361. package/pennyfarthing-dist/scripts/sprint/get-story-field.sh +69 -0
  362. package/pennyfarthing-dist/scripts/sprint/list-future.sh +151 -0
  363. package/pennyfarthing-dist/scripts/sprint/new-sprint.sh +116 -0
  364. package/pennyfarthing-dist/scripts/sprint/promote-epic.sh +164 -0
  365. package/pennyfarthing-dist/scripts/{utils → sprint}/sprint-common.sh +135 -0
  366. package/pennyfarthing-dist/scripts/sprint/sprint-info.sh +39 -0
  367. package/pennyfarthing-dist/scripts/{utils → sprint}/sprint-metrics.sh +0 -0
  368. package/pennyfarthing-dist/scripts/sprint/sprint-status.sh +134 -0
  369. package/pennyfarthing-dist/scripts/story/README.md +23 -0
  370. package/pennyfarthing-dist/scripts/story/create-story.sh +159 -0
  371. package/pennyfarthing-dist/scripts/story/size-story.sh +198 -0
  372. package/pennyfarthing-dist/scripts/story/story-template.sh +162 -0
  373. package/pennyfarthing-dist/scripts/test/README.md +23 -0
  374. package/pennyfarthing-dist/scripts/{utils → test}/swebench-judge.py +0 -0
  375. package/pennyfarthing-dist/scripts/test/test-cache.sh +165 -0
  376. package/pennyfarthing-dist/scripts/{utils → test}/test-setup.sh +5 -5
  377. package/pennyfarthing-dist/scripts/tests/check.test.sh +0 -0
  378. package/pennyfarthing-dist/scripts/tests/dev-story-workflow-import.test.sh +515 -0
  379. package/pennyfarthing-dist/scripts/tests/epics-and-stories-workflow-import.test.sh +599 -0
  380. package/pennyfarthing-dist/scripts/tests/handoff-phase-update.test.sh +332 -0
  381. package/pennyfarthing-dist/scripts/tests/implementation-readiness-workflow-import.test.sh +573 -0
  382. package/pennyfarthing-dist/scripts/tests/migrate-bmad-workflow.test.sh +859 -0
  383. package/pennyfarthing-dist/scripts/tests/prd-workflow-import.test.sh +662 -0
  384. package/pennyfarthing-dist/scripts/tests/project-context-workflow-import.test.sh +589 -0
  385. package/pennyfarthing-dist/scripts/tests/test-character-voice.sh +7 -9
  386. package/pennyfarthing-dist/scripts/tests/test-drift-detection.sh +18 -18
  387. package/pennyfarthing-dist/scripts/tests/test-post-merge-hook.sh +10 -10
  388. package/pennyfarthing-dist/scripts/tests/test-session-checkpoint.sh +1 -1
  389. package/pennyfarthing-dist/scripts/tests/test-solo-command.sh +0 -0
  390. package/pennyfarthing-dist/scripts/tests/ux-design-workflow-import.test.sh +647 -0
  391. package/pennyfarthing-dist/scripts/theme/README.md +22 -0
  392. package/pennyfarthing-dist/scripts/theme/compute-theme-tiers.sh +203 -0
  393. package/pennyfarthing-dist/scripts/theme/list-themes.sh +73 -0
  394. package/pennyfarthing-dist/scripts/theme/update-theme-tiers.sh +97 -0
  395. package/pennyfarthing-dist/scripts/workflow/README.md +28 -0
  396. package/pennyfarthing-dist/scripts/{check.sh → workflow/check.sh} +0 -0
  397. package/pennyfarthing-dist/scripts/workflow/finish-story.sh +159 -0
  398. package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +228 -0
  399. package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +91 -0
  400. package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +163 -0
  401. package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +138 -0
  402. package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +256 -0
  403. package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +167 -0
  404. package/pennyfarthing-dist/skills/agentic-patterns/SKILL.md +6 -0
  405. package/pennyfarthing-dist/skills/changelog/SKILL.md +2 -2
  406. package/pennyfarthing-dist/skills/code-review/SKILL.md +3 -3
  407. package/pennyfarthing-dist/skills/context-engineering/SKILL.md +6 -0
  408. package/pennyfarthing-dist/skills/cyclist/SKILL.md +56 -139
  409. package/pennyfarthing-dist/skills/dev-patterns/SKILL.md +21 -5
  410. package/pennyfarthing-dist/skills/finalize-run/SKILL.md +1 -1
  411. package/pennyfarthing-dist/skills/jira/SKILL.md +381 -178
  412. package/pennyfarthing-dist/skills/judge/SKILL.md +76 -21
  413. package/pennyfarthing-dist/skills/just/SKILL.md +345 -102
  414. package/pennyfarthing-dist/skills/otel/skill.md +1 -0
  415. package/pennyfarthing-dist/skills/permissions/skill.md +1 -1
  416. package/pennyfarthing-dist/skills/persona-benchmark/SKILL.md +5 -0
  417. package/pennyfarthing-dist/skills/skill-registry.schema.json +5 -0
  418. package/pennyfarthing-dist/skills/skill-registry.yaml +22 -0
  419. package/pennyfarthing-dist/skills/sprint/scripts/archive-story.sh +101 -0
  420. package/pennyfarthing-dist/skills/sprint/scripts/available-stories.sh +97 -0
  421. package/pennyfarthing-dist/skills/sprint/scripts/check-story.sh +164 -0
  422. package/pennyfarthing-dist/skills/sprint/scripts/create-jira-epic.sh +101 -0
  423. package/pennyfarthing-dist/skills/sprint/scripts/new-sprint.sh +116 -0
  424. package/pennyfarthing-dist/skills/sprint/scripts/promote-epic.sh +164 -0
  425. package/pennyfarthing-dist/skills/sprint/scripts/sprint-info.sh +39 -0
  426. package/pennyfarthing-dist/skills/sprint/scripts/sprint-status.sh +147 -0
  427. package/pennyfarthing-dist/skills/sprint/scripts/sync-epic-jira.sh +104 -0
  428. package/pennyfarthing-dist/skills/sprint/skill.md +465 -0
  429. package/pennyfarthing-dist/skills/story/scripts/create-story.sh +159 -0
  430. package/pennyfarthing-dist/skills/story/scripts/size-story.sh +198 -0
  431. package/pennyfarthing-dist/skills/story/scripts/story-template.sh +162 -0
  432. package/pennyfarthing-dist/skills/story/skill.md +219 -0
  433. package/pennyfarthing-dist/skills/systematic-debugging/SKILL.md +390 -0
  434. package/pennyfarthing-dist/skills/theme/skill.md +1 -1
  435. package/pennyfarthing-dist/skills/workflow/scripts/list-workflows.sh +91 -0
  436. package/pennyfarthing-dist/skills/workflow/scripts/resume-workflow.sh +163 -0
  437. package/pennyfarthing-dist/skills/workflow/scripts/show-workflow.sh +138 -0
  438. package/pennyfarthing-dist/skills/workflow/scripts/start-workflow.sh +273 -0
  439. package/pennyfarthing-dist/skills/workflow/scripts/workflow-status.sh +167 -0
  440. package/pennyfarthing-dist/skills/workflow/skill.md +337 -0
  441. package/pennyfarthing-dist/templates/settings.local.json.template +1 -1
  442. package/pennyfarthing-dist/workflows/agent-docs.yaml +1 -1
  443. package/pennyfarthing-dist/workflows/architecture/steps/step-01-initialize.md +101 -0
  444. package/pennyfarthing-dist/workflows/architecture/steps/step-01b-continue.md +93 -0
  445. package/pennyfarthing-dist/workflows/architecture/steps/step-02-context.md +115 -0
  446. package/pennyfarthing-dist/workflows/architecture/steps/step-03-patterns.md +133 -0
  447. package/pennyfarthing-dist/workflows/architecture/steps/step-04-components.md +138 -0
  448. package/pennyfarthing-dist/workflows/architecture/steps/step-05-interfaces.md +133 -0
  449. package/pennyfarthing-dist/workflows/architecture/steps/step-06-risks.md +142 -0
  450. package/pennyfarthing-dist/workflows/architecture/steps/step-07-document.md +160 -0
  451. package/pennyfarthing-dist/workflows/architecture/templates/architecture-decision.md +102 -0
  452. package/pennyfarthing-dist/workflows/architecture.yaml +65 -0
  453. package/pennyfarthing-dist/workflows/bdd.yaml +3 -1
  454. package/pennyfarthing-dist/workflows/brainstorming/brain-methods.csv +62 -0
  455. package/pennyfarthing-dist/workflows/brainstorming/checklist.md +44 -0
  456. package/pennyfarthing-dist/workflows/brainstorming/instructions.md +736 -0
  457. package/pennyfarthing-dist/workflows/brainstorming/workflow.yaml +49 -0
  458. package/pennyfarthing-dist/workflows/code-review/checklist.md +23 -0
  459. package/pennyfarthing-dist/workflows/code-review/instructions.md +234 -0
  460. package/pennyfarthing-dist/workflows/code-review/workflow.yaml +51 -0
  461. package/pennyfarthing-dist/workflows/dev-story/checklist.md +80 -0
  462. package/pennyfarthing-dist/workflows/dev-story/instructions.xml +410 -0
  463. package/pennyfarthing-dist/workflows/dev-story/workflow.yaml +50 -0
  464. package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-01-validate-prerequisites.md +256 -0
  465. package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-02-design-epics.md +233 -0
  466. package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-03-create-stories.md +272 -0
  467. package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-04-final-validation.md +145 -0
  468. package/pennyfarthing-dist/workflows/epics-and-stories/templates/epics-template.md +57 -0
  469. package/pennyfarthing-dist/workflows/epics-and-stories/workflow.yaml +27 -0
  470. package/pennyfarthing-dist/workflows/implementation-readiness/steps/step-01-document-discovery.md +190 -0
  471. package/pennyfarthing-dist/workflows/implementation-readiness/steps/step-02-prd-analysis.md +178 -0
  472. package/pennyfarthing-dist/workflows/implementation-readiness/steps/step-03-epic-coverage-validation.md +179 -0
  473. package/pennyfarthing-dist/workflows/implementation-readiness/steps/step-04-ux-alignment.md +139 -0
  474. package/pennyfarthing-dist/workflows/implementation-readiness/steps/step-05-epic-quality-review.md +252 -0
  475. package/pennyfarthing-dist/workflows/implementation-readiness/steps/step-06-final-assessment.md +133 -0
  476. package/pennyfarthing-dist/workflows/implementation-readiness/templates/readiness-report-template.md +4 -0
  477. package/pennyfarthing-dist/workflows/implementation-readiness/workflow.yaml +40 -0
  478. package/pennyfarthing-dist/workflows/prd/data/domain-complexity.csv +13 -0
  479. package/pennyfarthing-dist/workflows/prd/data/prd-purpose.md +197 -0
  480. package/pennyfarthing-dist/workflows/prd/data/project-types.csv +11 -0
  481. package/pennyfarthing-dist/workflows/prd/steps-c/step-01-init.md +191 -0
  482. package/pennyfarthing-dist/workflows/prd/steps-c/step-01b-continue.md +153 -0
  483. package/pennyfarthing-dist/workflows/prd/steps-c/step-02-discovery.md +224 -0
  484. package/pennyfarthing-dist/workflows/prd/steps-c/step-03-success.md +226 -0
  485. package/pennyfarthing-dist/workflows/prd/steps-c/step-04-journeys.md +213 -0
  486. package/pennyfarthing-dist/workflows/prd/steps-c/step-05-domain.md +207 -0
  487. package/pennyfarthing-dist/workflows/prd/steps-c/step-06-innovation.md +226 -0
  488. package/pennyfarthing-dist/workflows/prd/steps-c/step-07-project-type.md +237 -0
  489. package/pennyfarthing-dist/workflows/prd/steps-c/step-08-scoping.md +228 -0
  490. package/pennyfarthing-dist/workflows/prd/steps-c/step-09-functional.md +231 -0
  491. package/pennyfarthing-dist/workflows/prd/steps-c/step-10-nonfunctional.md +242 -0
  492. package/pennyfarthing-dist/workflows/prd/steps-c/step-11-polish.md +217 -0
  493. package/pennyfarthing-dist/workflows/prd/steps-c/step-12-complete.md +180 -0
  494. package/pennyfarthing-dist/workflows/prd/steps-e/step-e-01-discovery.md +247 -0
  495. package/pennyfarthing-dist/workflows/prd/steps-e/step-e-01b-legacy-conversion.md +208 -0
  496. package/pennyfarthing-dist/workflows/prd/steps-e/step-e-02-review.md +249 -0
  497. package/pennyfarthing-dist/workflows/prd/steps-e/step-e-03-edit.md +253 -0
  498. package/pennyfarthing-dist/workflows/prd/steps-e/step-e-04-complete.md +168 -0
  499. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-01-discovery.md +218 -0
  500. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-02-format-detection.md +191 -0
  501. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-02b-parity-check.md +209 -0
  502. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-03-density-validation.md +174 -0
  503. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-04-brief-coverage-validation.md +214 -0
  504. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-05-measurability-validation.md +228 -0
  505. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-06-traceability-validation.md +217 -0
  506. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-07-implementation-leakage-validation.md +205 -0
  507. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-08-domain-compliance-validation.md +243 -0
  508. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-09-project-type-validation.md +263 -0
  509. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-10-smart-validation.md +209 -0
  510. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-11-holistic-quality-validation.md +264 -0
  511. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-12-completeness-validation.md +242 -0
  512. package/pennyfarthing-dist/workflows/prd/steps-v/step-v-13-report-complete.md +232 -0
  513. package/pennyfarthing-dist/workflows/prd/templates/prd-template.md +10 -0
  514. package/pennyfarthing-dist/workflows/prd/workflow.yaml +42 -0
  515. package/pennyfarthing-dist/workflows/product-brief/steps/step-01-init.md +177 -0
  516. package/pennyfarthing-dist/workflows/product-brief/steps/step-01b-continue.md +161 -0
  517. package/pennyfarthing-dist/workflows/product-brief/steps/step-02-vision.md +199 -0
  518. package/pennyfarthing-dist/workflows/product-brief/steps/step-03-users.md +202 -0
  519. package/pennyfarthing-dist/workflows/product-brief/steps/step-04-metrics.md +205 -0
  520. package/pennyfarthing-dist/workflows/product-brief/steps/step-05-scope.md +219 -0
  521. package/pennyfarthing-dist/workflows/product-brief/steps/step-06-complete.md +194 -0
  522. package/pennyfarthing-dist/workflows/product-brief/templates/product-brief.template.md +10 -0
  523. package/pennyfarthing-dist/workflows/product-brief/workflow.yaml +31 -0
  524. package/pennyfarthing-dist/workflows/project-context/project-context-template.md +21 -0
  525. package/pennyfarthing-dist/workflows/project-context/steps/step-01-discover.md +184 -0
  526. package/pennyfarthing-dist/workflows/project-context/steps/step-02-generate.md +318 -0
  527. package/pennyfarthing-dist/workflows/project-context/steps/step-03-complete.md +278 -0
  528. package/pennyfarthing-dist/workflows/project-context/workflow.yaml +27 -0
  529. package/pennyfarthing-dist/workflows/quick-dev/steps/step-01-mode-detection.md +156 -0
  530. package/pennyfarthing-dist/workflows/quick-dev/steps/step-02-context-gathering.md +120 -0
  531. package/pennyfarthing-dist/workflows/quick-dev/steps/step-03-execute.md +113 -0
  532. package/pennyfarthing-dist/workflows/quick-dev/steps/step-04-self-check.md +113 -0
  533. package/pennyfarthing-dist/workflows/quick-dev/steps/step-05-adversarial-review.md +106 -0
  534. package/pennyfarthing-dist/workflows/quick-dev/steps/step-06-resolve-findings.md +140 -0
  535. package/pennyfarthing-dist/workflows/quick-dev/workflow.yaml +27 -0
  536. package/pennyfarthing-dist/workflows/quick-spec/steps/step-01-understand.md +189 -0
  537. package/pennyfarthing-dist/workflows/quick-spec/steps/step-02-investigate.md +144 -0
  538. package/pennyfarthing-dist/workflows/quick-spec/steps/step-03-generate.md +128 -0
  539. package/pennyfarthing-dist/workflows/quick-spec/steps/step-04-review.md +191 -0
  540. package/pennyfarthing-dist/workflows/quick-spec/tech-spec-template.md +74 -0
  541. package/pennyfarthing-dist/workflows/quick-spec/workflow.yaml +27 -0
  542. package/pennyfarthing-dist/workflows/research/steps-domain/step-01-init.md +137 -0
  543. package/pennyfarthing-dist/workflows/research/steps-domain/step-02-domain-analysis.md +229 -0
  544. package/pennyfarthing-dist/workflows/research/steps-domain/step-03-competitive-landscape.md +238 -0
  545. package/pennyfarthing-dist/workflows/research/steps-domain/step-04-regulatory-focus.md +206 -0
  546. package/pennyfarthing-dist/workflows/research/steps-domain/step-05-technical-trends.md +234 -0
  547. package/pennyfarthing-dist/workflows/research/steps-domain/step-06-research-synthesis.md +443 -0
  548. package/pennyfarthing-dist/workflows/research/steps-market/step-01-init.md +182 -0
  549. package/pennyfarthing-dist/workflows/research/steps-market/step-02-customer-behavior.md +237 -0
  550. package/pennyfarthing-dist/workflows/research/steps-market/step-02-customer-insights.md +200 -0
  551. package/pennyfarthing-dist/workflows/research/steps-market/step-03-customer-pain-points.md +249 -0
  552. package/pennyfarthing-dist/workflows/research/steps-market/step-04-customer-decisions.md +259 -0
  553. package/pennyfarthing-dist/workflows/research/steps-market/step-05-competitive-analysis.md +177 -0
  554. package/pennyfarthing-dist/workflows/research/steps-market/step-06-research-completion.md +475 -0
  555. package/pennyfarthing-dist/workflows/research/steps-technical/step-01-init.md +137 -0
  556. package/pennyfarthing-dist/workflows/research/steps-technical/step-02-technical-overview.md +239 -0
  557. package/pennyfarthing-dist/workflows/research/steps-technical/step-03-integration-patterns.md +248 -0
  558. package/pennyfarthing-dist/workflows/research/steps-technical/step-04-architectural-patterns.md +202 -0
  559. package/pennyfarthing-dist/workflows/research/steps-technical/step-05-implementation-research.md +239 -0
  560. package/pennyfarthing-dist/workflows/research/steps-technical/step-06-research-synthesis.md +486 -0
  561. package/pennyfarthing-dist/workflows/research/templates/research.template.md +29 -0
  562. package/pennyfarthing-dist/workflows/research/workflow.yaml +45 -0
  563. package/pennyfarthing-dist/workflows/retrospective/checklist.md +31 -0
  564. package/pennyfarthing-dist/workflows/retrospective/instructions.md +1443 -0
  565. package/pennyfarthing-dist/workflows/retrospective/workflow.yaml +50 -0
  566. package/pennyfarthing-dist/workflows/sprint-planning/checklist.md +33 -0
  567. package/pennyfarthing-dist/workflows/sprint-planning/sprint-status-template.yaml +55 -0
  568. package/pennyfarthing-dist/workflows/sprint-planning/steps/step-01-parse-epic-files.md +54 -0
  569. package/pennyfarthing-dist/workflows/sprint-planning/steps/step-02-build-sprint-status.md +44 -0
  570. package/pennyfarthing-dist/workflows/sprint-planning/steps/step-03-status-detection.md +64 -0
  571. package/pennyfarthing-dist/workflows/sprint-planning/steps/step-04-generate-status-file.md +73 -0
  572. package/pennyfarthing-dist/workflows/sprint-planning/steps/step-05-validate-and-report.md +56 -0
  573. package/pennyfarthing-dist/workflows/sprint-planning/workflow.yaml +34 -0
  574. package/pennyfarthing-dist/workflows/trivial.yaml +1 -1
  575. package/pennyfarthing-dist/workflows/ux-design/steps/step-01-init.md +135 -0
  576. package/pennyfarthing-dist/workflows/ux-design/steps/step-01b-continue.md +127 -0
  577. package/pennyfarthing-dist/workflows/ux-design/steps/step-02-discovery.md +190 -0
  578. package/pennyfarthing-dist/workflows/ux-design/steps/step-03-core-experience.md +216 -0
  579. package/pennyfarthing-dist/workflows/ux-design/steps/step-04-emotional-response.md +219 -0
  580. package/pennyfarthing-dist/workflows/ux-design/steps/step-05-inspiration.md +234 -0
  581. package/pennyfarthing-dist/workflows/ux-design/steps/step-06-design-system.md +252 -0
  582. package/pennyfarthing-dist/workflows/ux-design/steps/step-07-defining-experience.md +254 -0
  583. package/pennyfarthing-dist/workflows/ux-design/steps/step-08-visual-foundation.md +224 -0
  584. package/pennyfarthing-dist/workflows/ux-design/steps/step-09-design-directions.md +224 -0
  585. package/pennyfarthing-dist/workflows/ux-design/steps/step-10-user-journeys.md +241 -0
  586. package/pennyfarthing-dist/workflows/ux-design/steps/step-11-component-strategy.md +248 -0
  587. package/pennyfarthing-dist/workflows/ux-design/steps/step-12-ux-patterns.md +237 -0
  588. package/pennyfarthing-dist/workflows/ux-design/steps/step-13-responsive-accessibility.md +264 -0
  589. package/pennyfarthing-dist/workflows/ux-design/steps/step-14-complete.md +228 -0
  590. package/pennyfarthing-dist/workflows/ux-design/ux-design-template.md +13 -0
  591. package/pennyfarthing-dist/workflows/ux-design/workflow.yaml +41 -0
  592. package/packages/core/dist/scripts/generate-all-faces.d.ts +0 -10
  593. package/packages/core/dist/scripts/generate-all-faces.d.ts.map +0 -1
  594. package/packages/core/dist/scripts/generate-all-faces.js +0 -256
  595. package/packages/core/dist/scripts/generate-all-faces.js.map +0 -1
  596. package/packages/core/dist/scripts/generate-all-faces.test.d.ts +0 -17
  597. package/packages/core/dist/scripts/generate-all-faces.test.d.ts.map +0 -1
  598. package/packages/core/dist/scripts/generate-all-faces.test.js +0 -372
  599. package/packages/core/dist/scripts/generate-all-faces.test.js.map +0 -1
  600. package/packages/core/dist/scripts/generate-ascii-face.d.ts +0 -52
  601. package/packages/core/dist/scripts/generate-ascii-face.d.ts.map +0 -1
  602. package/packages/core/dist/scripts/generate-ascii-face.js +0 -155
  603. package/packages/core/dist/scripts/generate-ascii-face.js.map +0 -1
  604. package/packages/core/dist/scripts/generate-face.d.ts +0 -52
  605. package/packages/core/dist/scripts/generate-face.d.ts.map +0 -1
  606. package/packages/core/dist/scripts/generate-face.js +0 -199
  607. package/packages/core/dist/scripts/generate-face.js.map +0 -1
  608. package/packages/core/dist/scripts/generate-face.test.d.ts +0 -13
  609. package/packages/core/dist/scripts/generate-face.test.d.ts.map +0 -1
  610. package/packages/core/dist/scripts/generate-face.test.js +0 -301
  611. package/packages/core/dist/scripts/generate-face.test.js.map +0 -1
  612. package/pennyfarthing-dist/agents/generic-handoff.md +0 -454
  613. package/pennyfarthing-dist/agents/generic-sm-finish.md +0 -261
  614. package/pennyfarthing-dist/agents/generic-sm-setup.md +0 -214
  615. package/pennyfarthing-dist/commands/new-work.md +0 -127
  616. package/pennyfarthing-dist/guides/AGENT-SCOPES.md +0 -201
  617. package/pennyfarthing-dist/guides/persona-system.md +0 -294
  618. package/pennyfarthing-dist/guides/shared-agent-behavior.md +0 -388
  619. package/pennyfarthing-dist/guides/shared-context.md +0 -147
  620. package/pennyfarthing-dist/guides/strategic-agent-behavior.md +0 -348
  621. package/pennyfarthing-dist/guides/tactical-agent-behavior.md +0 -1041
  622. package/pennyfarthing-dist/scripts/prime.sh +0 -161
  623. package/pennyfarthing-dist/scripts/run.sh +0 -65
  624. package/pennyfarthing-dist/skills/sprint-context/SKILL.md +0 -120
  625. package/pennyfarthing-dist/skills/story-management/SKILL.md +0 -208
  626. package/pennyfarthing-dist/skills/workflow/SKILL.md +0 -160
  627. /package/pennyfarthing-dist/commands/{brainstorm.md → brainstorming.md} +0 -0
  628. /package/pennyfarthing-dist/scripts/{utils → test}/ground-truth-judge.py +0 -0
@@ -0,0 +1,845 @@
1
+ /**
2
+ * Tests for Story 47-2: Sync sprint numbers with Jira sprint IDs
3
+ *
4
+ * These tests define the contract for syncing Pennyfarthing sprint numbers
5
+ * with Jira sprint IDs, enabling bidirectional sprint tracking.
6
+ *
7
+ * Acceptance Criteria:
8
+ * 1. Sprint YAML references Jira sprint ID (e.g., 275)
9
+ * 2. Status check queries Jira sprint for membership
10
+ * 3. Sprint velocity pulls from Jira sprint metrics
11
+ * 4. Local sprint number matches Jira sprint
12
+ *
13
+ * Run with: npm test
14
+ */
15
+ import { describe, it, beforeEach, afterEach } from 'node:test';
16
+ import assert from 'node:assert';
17
+ import { mkdirSync, rmSync, existsSync, writeFileSync, readFileSync } from 'node:fs';
18
+ import { join, dirname } from 'node:path';
19
+ import { fileURLToPath } from 'node:url';
20
+ // Get directory for test fixtures
21
+ const __dirname = dirname(fileURLToPath(import.meta.url));
22
+ const TEST_DIR = join(__dirname, '__test_jira_sprint_sync__');
23
+ // Import the sprint sync functions (to be implemented)
24
+ import { getJiraSprintInfo, getSprintIssues, isStoryInJiraSprint, getSprintVelocityFromJira, validateSprintAlignment, addJiraSprintIdToYaml,
25
+ // Story 47-3 imports
26
+ getYamlStoryIds, findJiraOnlyStories, formatMissingStoriesReport, importMissingStoriesToYaml } from './jira-sprint-sync.js';
27
+ describe('Jira Sprint Sync (47-2)', () => {
28
+ beforeEach(() => {
29
+ if (existsSync(TEST_DIR)) {
30
+ rmSync(TEST_DIR, { recursive: true });
31
+ }
32
+ mkdirSync(TEST_DIR, { recursive: true });
33
+ });
34
+ afterEach(() => {
35
+ if (existsSync(TEST_DIR)) {
36
+ rmSync(TEST_DIR, { recursive: true });
37
+ }
38
+ });
39
+ // ============================================
40
+ // AC1: Sprint YAML references Jira sprint ID
41
+ // ============================================
42
+ describe('addJiraSprintIdToYaml() - AC1: Sprint YAML references Jira sprint ID', () => {
43
+ it('should add jira_sprint_id field to sprint section', async () => {
44
+ const sprintYaml = `sprint:
45
+ number: 11
46
+ goal: Complete Epic 31 workflow engine
47
+ planned_start: 2026-01-15
48
+ status: active
49
+ velocity_target: 22
50
+ `;
51
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
52
+ writeFileSync(sprintPath, sprintYaml);
53
+ const result = await addJiraSprintIdToYaml({
54
+ sprintPath,
55
+ jiraSprintId: 275
56
+ });
57
+ assert.strictEqual(result.success, true, 'Update should succeed');
58
+ const updatedContent = readFileSync(sprintPath, 'utf-8');
59
+ assert.ok(updatedContent.includes('jira_sprint_id: 275'), 'YAML should contain jira_sprint_id field');
60
+ });
61
+ it('should preserve existing sprint fields when adding jira_sprint_id', async () => {
62
+ const sprintYaml = `sprint:
63
+ number: 11
64
+ goal: Complete Epic 31 workflow engine
65
+ planned_start: 2026-01-15
66
+ status: active
67
+ velocity_target: 22
68
+ `;
69
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
70
+ writeFileSync(sprintPath, sprintYaml);
71
+ await addJiraSprintIdToYaml({
72
+ sprintPath,
73
+ jiraSprintId: 275
74
+ });
75
+ const updatedContent = readFileSync(sprintPath, 'utf-8');
76
+ assert.ok(updatedContent.includes('number: 11'), 'Number should be preserved');
77
+ assert.ok(updatedContent.includes('goal: Complete Epic 31'), 'Goal should be preserved');
78
+ assert.ok(updatedContent.includes('velocity_target: 22'), 'Velocity target should be preserved');
79
+ });
80
+ it('should update existing jira_sprint_id if already present', async () => {
81
+ const sprintYaml = `sprint:
82
+ number: 11
83
+ jira_sprint_id: 274
84
+ goal: Old sprint
85
+ status: active
86
+ `;
87
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
88
+ writeFileSync(sprintPath, sprintYaml);
89
+ const result = await addJiraSprintIdToYaml({
90
+ sprintPath,
91
+ jiraSprintId: 275,
92
+ force: true
93
+ });
94
+ assert.strictEqual(result.success, true);
95
+ const updatedContent = readFileSync(sprintPath, 'utf-8');
96
+ assert.ok(updatedContent.includes('jira_sprint_id: 275'), 'Should have updated jira_sprint_id');
97
+ assert.ok(!updatedContent.includes('jira_sprint_id: 274'), 'Should not have old jira_sprint_id');
98
+ });
99
+ it('should fail if jira_sprint_id exists and force is false', async () => {
100
+ const sprintYaml = `sprint:
101
+ number: 11
102
+ jira_sprint_id: 274
103
+ status: active
104
+ `;
105
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
106
+ writeFileSync(sprintPath, sprintYaml);
107
+ const result = await addJiraSprintIdToYaml({
108
+ sprintPath,
109
+ jiraSprintId: 275,
110
+ force: false
111
+ });
112
+ assert.strictEqual(result.success, false);
113
+ assert.ok(result.error?.includes('already has'), 'Should mention existing ID');
114
+ });
115
+ it('should handle file not found gracefully', async () => {
116
+ const result = await addJiraSprintIdToYaml({
117
+ sprintPath: join(TEST_DIR, 'nonexistent.yaml'),
118
+ jiraSprintId: 275
119
+ });
120
+ assert.strictEqual(result.success, false);
121
+ assert.ok(result.error?.includes('not found') || result.error?.includes('ENOENT'), 'Should report file not found');
122
+ });
123
+ });
124
+ // ============================================
125
+ // AC2: Status check queries Jira sprint for membership
126
+ // ============================================
127
+ describe('getJiraSprintInfo() - AC2: Query Jira sprint for membership', () => {
128
+ it('should return sprint details from Jira', async () => {
129
+ const result = await getJiraSprintInfo({
130
+ sprintId: 275,
131
+ _mockResponse: {
132
+ id: 275,
133
+ name: 'Sprint 11',
134
+ state: 'active',
135
+ startDate: '2026-01-15',
136
+ endDate: '2026-01-29'
137
+ }
138
+ });
139
+ assert.strictEqual(result.success, true);
140
+ assert.strictEqual(result.sprint?.id, 275);
141
+ assert.strictEqual(result.sprint?.name, 'Sprint 11');
142
+ assert.strictEqual(result.sprint?.state, 'active');
143
+ });
144
+ it('should return null for non-existent sprint', async () => {
145
+ const result = await getJiraSprintInfo({
146
+ sprintId: 99999,
147
+ _mockResponse: null
148
+ });
149
+ assert.strictEqual(result.success, false);
150
+ assert.ok(result.error?.includes('not found'), 'Should indicate sprint not found');
151
+ });
152
+ it('should handle Jira API errors', async () => {
153
+ const result = await getJiraSprintInfo({
154
+ sprintId: 275,
155
+ _mockError: 'Connection refused'
156
+ });
157
+ assert.strictEqual(result.success, false);
158
+ assert.ok(result.error, 'Should have error message');
159
+ });
160
+ });
161
+ describe('getSprintIssues() - AC2: List issues in Jira sprint', () => {
162
+ it('should return issues in the sprint', async () => {
163
+ const result = await getSprintIssues({
164
+ sprintId: 275,
165
+ _mockResponse: [
166
+ { key: 'MSSCI-11798', summary: 'Sprint sync story', status: 'In Progress' },
167
+ { key: 'MSSCI-11750', summary: 'Another story', status: 'Done' }
168
+ ]
169
+ });
170
+ assert.strictEqual(result.success, true);
171
+ assert.strictEqual(result.issues?.length, 2);
172
+ assert.strictEqual(result.issues?.[0].key, 'MSSCI-11798');
173
+ });
174
+ it('should filter by label when provided', async () => {
175
+ const result = await getSprintIssues({
176
+ sprintId: 275,
177
+ label: 'pennyfarthing',
178
+ _mockResponse: [
179
+ { key: 'MSSCI-11798', summary: 'Pennyfarthing story', status: 'In Progress', labels: ['pennyfarthing'] }
180
+ ]
181
+ });
182
+ assert.strictEqual(result.success, true);
183
+ assert.strictEqual(result.issues?.length, 1);
184
+ });
185
+ it('should return empty array for sprint with no issues', async () => {
186
+ const result = await getSprintIssues({
187
+ sprintId: 275,
188
+ _mockResponse: []
189
+ });
190
+ assert.strictEqual(result.success, true);
191
+ assert.strictEqual(result.issues?.length, 0);
192
+ });
193
+ });
194
+ describe('isStoryInJiraSprint() - AC2: Check sprint membership', () => {
195
+ it('should return true if story is in the sprint', async () => {
196
+ const result = await isStoryInJiraSprint({
197
+ jiraKey: 'MSSCI-11798',
198
+ sprintId: 275,
199
+ _mockResponse: true
200
+ });
201
+ assert.strictEqual(result.success, true);
202
+ assert.strictEqual(result.inSprint, true);
203
+ });
204
+ it('should return false if story is not in the sprint', async () => {
205
+ const result = await isStoryInJiraSprint({
206
+ jiraKey: 'MSSCI-11798',
207
+ sprintId: 274, // Different sprint
208
+ _mockResponse: false
209
+ });
210
+ assert.strictEqual(result.success, true);
211
+ assert.strictEqual(result.inSprint, false);
212
+ });
213
+ it('should handle story not found in Jira', async () => {
214
+ const result = await isStoryInJiraSprint({
215
+ jiraKey: 'MSSCI-99999',
216
+ sprintId: 275,
217
+ _mockError: 'Issue not found'
218
+ });
219
+ assert.strictEqual(result.success, false);
220
+ assert.ok(result.error?.includes('not found'));
221
+ });
222
+ });
223
+ // ============================================
224
+ // AC3: Sprint velocity pulls from Jira sprint metrics
225
+ // ============================================
226
+ describe('getSprintVelocityFromJira() - AC3: Sprint velocity from Jira', () => {
227
+ it('should return velocity metrics from Jira sprint', async () => {
228
+ const result = await getSprintVelocityFromJira({
229
+ sprintId: 275,
230
+ _mockResponse: {
231
+ totalPoints: 22,
232
+ completedPoints: 15,
233
+ remainingPoints: 7,
234
+ issueCount: 8,
235
+ completedCount: 5
236
+ }
237
+ });
238
+ assert.strictEqual(result.success, true);
239
+ assert.strictEqual(result.metrics?.totalPoints, 22);
240
+ assert.strictEqual(result.metrics?.completedPoints, 15);
241
+ assert.strictEqual(result.metrics?.remainingPoints, 7);
242
+ });
243
+ it('should calculate velocity percentage', async () => {
244
+ const result = await getSprintVelocityFromJira({
245
+ sprintId: 275,
246
+ _mockResponse: {
247
+ totalPoints: 20,
248
+ completedPoints: 10,
249
+ remainingPoints: 10
250
+ }
251
+ });
252
+ assert.strictEqual(result.success, true);
253
+ assert.strictEqual(result.metrics?.velocityPercentage, 50);
254
+ });
255
+ it('should handle sprint with no points', async () => {
256
+ const result = await getSprintVelocityFromJira({
257
+ sprintId: 275,
258
+ _mockResponse: {
259
+ totalPoints: 0,
260
+ completedPoints: 0,
261
+ remainingPoints: 0
262
+ }
263
+ });
264
+ assert.strictEqual(result.success, true);
265
+ assert.strictEqual(result.metrics?.totalPoints, 0);
266
+ // Should not divide by zero
267
+ assert.ok(result.metrics?.velocityPercentage === 0 || result.metrics?.velocityPercentage === null, 'Should handle zero total gracefully');
268
+ });
269
+ it('should return issue breakdown by status', async () => {
270
+ const result = await getSprintVelocityFromJira({
271
+ sprintId: 275,
272
+ _mockResponse: {
273
+ totalPoints: 22,
274
+ completedPoints: 15,
275
+ remainingPoints: 7,
276
+ byStatus: {
277
+ 'To Do': { count: 2, points: 3 },
278
+ 'In Progress': { count: 1, points: 4 },
279
+ 'Done': { count: 5, points: 15 }
280
+ }
281
+ }
282
+ });
283
+ assert.strictEqual(result.success, true);
284
+ assert.ok(result.metrics?.byStatus, 'Should have status breakdown');
285
+ assert.strictEqual(result.metrics?.byStatus?.Done?.points, 15);
286
+ });
287
+ });
288
+ // ============================================
289
+ // AC4: Local sprint name matches Jira sprint
290
+ // ============================================
291
+ describe('validateSprintAlignment() - AC4: Local sprint matches Jira', () => {
292
+ it('should return aligned when sprint names match', async () => {
293
+ const sprintYaml = `sprint:
294
+ name: "TO Sprint 2604"
295
+ jira_sprint_id: 275
296
+ jira_sprint_name: "TO Sprint 2604"
297
+ status: active
298
+ `;
299
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
300
+ writeFileSync(sprintPath, sprintYaml);
301
+ const result = await validateSprintAlignment({
302
+ sprintPath,
303
+ _mockJiraSprint: {
304
+ id: 275,
305
+ name: 'TO Sprint 2604',
306
+ state: 'active'
307
+ }
308
+ });
309
+ assert.strictEqual(result.success, true);
310
+ assert.strictEqual(result.aligned, true);
311
+ assert.strictEqual(result.localSprintName, 'TO Sprint 2604');
312
+ assert.strictEqual(result.jiraSprintId, 275);
313
+ });
314
+ it('should detect misalignment when sprint names differ', async () => {
315
+ const sprintYaml = `sprint:
316
+ name: "TO Sprint 2604"
317
+ jira_sprint_id: 275
318
+ jira_sprint_name: "TO Sprint 2604"
319
+ status: active
320
+ `;
321
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
322
+ writeFileSync(sprintPath, sprintYaml);
323
+ const result = await validateSprintAlignment({
324
+ sprintPath,
325
+ _mockJiraSprint: {
326
+ id: 275,
327
+ name: 'TO Sprint 2605', // Mismatch!
328
+ state: 'active'
329
+ }
330
+ });
331
+ assert.strictEqual(result.success, true);
332
+ assert.strictEqual(result.aligned, false);
333
+ assert.ok(result.warning?.includes('mismatch'), 'Should warn about mismatch');
334
+ });
335
+ it('should detect when Jira sprint is closed but local is active', async () => {
336
+ const sprintYaml = `sprint:
337
+ name: "TO Sprint 2604"
338
+ jira_sprint_id: 275
339
+ jira_sprint_name: "TO Sprint 2604"
340
+ status: active
341
+ `;
342
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
343
+ writeFileSync(sprintPath, sprintYaml);
344
+ const result = await validateSprintAlignment({
345
+ sprintPath,
346
+ _mockJiraSprint: {
347
+ id: 275,
348
+ name: 'TO Sprint 2604',
349
+ state: 'closed' // Jira sprint closed!
350
+ }
351
+ });
352
+ assert.strictEqual(result.success, true);
353
+ assert.strictEqual(result.aligned, false);
354
+ assert.ok(result.warning?.includes('closed'), 'Should warn about closed sprint');
355
+ });
356
+ it('should handle missing jira_sprint_id in YAML', async () => {
357
+ const sprintYaml = `sprint:
358
+ name: "TO Sprint 2604"
359
+ status: active
360
+ `;
361
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
362
+ writeFileSync(sprintPath, sprintYaml);
363
+ const result = await validateSprintAlignment({
364
+ sprintPath,
365
+ _mockJiraSprint: null
366
+ });
367
+ assert.strictEqual(result.success, false);
368
+ assert.ok(result.error?.includes('jira_sprint_id') || result.error?.includes('not configured'), 'Should report missing jira_sprint_id');
369
+ });
370
+ it('should use jira_sprint_name for comparison if present', async () => {
371
+ const sprintYaml = `sprint:
372
+ name: "My Local Name"
373
+ jira_sprint_id: 275
374
+ jira_sprint_name: "TO Sprint 2604"
375
+ status: active
376
+ `;
377
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
378
+ writeFileSync(sprintPath, sprintYaml);
379
+ const result = await validateSprintAlignment({
380
+ sprintPath,
381
+ _mockJiraSprint: {
382
+ id: 275,
383
+ name: 'TO Sprint 2604',
384
+ state: 'active'
385
+ }
386
+ });
387
+ assert.strictEqual(result.success, true);
388
+ assert.strictEqual(result.aligned, true);
389
+ assert.strictEqual(result.jiraSprintName, 'TO Sprint 2604');
390
+ });
391
+ });
392
+ // ============================================
393
+ // Integration tests
394
+ // ============================================
395
+ describe('Integration: Full sprint sync workflow', () => {
396
+ it('should sync sprint with Jira and update YAML', async () => {
397
+ // Start with no jira_sprint_id
398
+ const sprintYaml = `sprint:
399
+ name: "TO Sprint 2604"
400
+ goal: Test sprint
401
+ status: active
402
+ `;
403
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
404
+ writeFileSync(sprintPath, sprintYaml);
405
+ // Step 1: Add jira_sprint_id
406
+ const addResult = await addJiraSprintIdToYaml({
407
+ sprintPath,
408
+ jiraSprintId: 275
409
+ });
410
+ assert.strictEqual(addResult.success, true);
411
+ // Step 2: Validate alignment
412
+ const validateResult = await validateSprintAlignment({
413
+ sprintPath,
414
+ _mockJiraSprint: {
415
+ id: 275,
416
+ name: 'TO Sprint 2604',
417
+ state: 'active'
418
+ }
419
+ });
420
+ assert.strictEqual(validateResult.success, true);
421
+ assert.strictEqual(validateResult.aligned, true);
422
+ // Step 3: Get velocity
423
+ const velocityResult = await getSprintVelocityFromJira({
424
+ sprintId: 275,
425
+ _mockResponse: {
426
+ totalPoints: 22,
427
+ completedPoints: 10,
428
+ remainingPoints: 12
429
+ }
430
+ });
431
+ assert.strictEqual(velocityResult.success, true);
432
+ assert.strictEqual(velocityResult.metrics?.totalPoints, 22);
433
+ });
434
+ it('should warn when story is not in Jira sprint', async () => {
435
+ // This tests the SM setup flow integration
436
+ const membershipResult = await isStoryInJiraSprint({
437
+ jiraKey: 'MSSCI-11798',
438
+ sprintId: 275,
439
+ _mockResponse: false
440
+ });
441
+ assert.strictEqual(membershipResult.success, true);
442
+ assert.strictEqual(membershipResult.inSprint, false);
443
+ // In real implementation, this would trigger a warning in SM setup
444
+ });
445
+ });
446
+ });
447
+ // ============================================
448
+ // Story 47-3: Detect Jira-only stories missing from sprint YAML
449
+ // ============================================
450
+ //
451
+ // Acceptance Criteria:
452
+ // 1. Sync script queries Jira sprint for all pennyfarthing stories
453
+ // 2. Compares against sprint YAML story list
454
+ // 3. Reports stories in Jira but not in YAML
455
+ // 4. Optionally imports missing stories to YAML
456
+ describe('Jira-Only Story Detection (47-3)', () => {
457
+ beforeEach(() => {
458
+ if (existsSync(TEST_DIR)) {
459
+ rmSync(TEST_DIR, { recursive: true });
460
+ }
461
+ mkdirSync(TEST_DIR, { recursive: true });
462
+ });
463
+ afterEach(() => {
464
+ if (existsSync(TEST_DIR)) {
465
+ rmSync(TEST_DIR, { recursive: true });
466
+ }
467
+ });
468
+ // ============================================
469
+ // AC1 + AC2: Query Jira and compare against YAML
470
+ // ============================================
471
+ describe('getYamlStoryIds() - Extract story IDs from sprint YAML', () => {
472
+ it('should extract all story IDs from sprint YAML', async () => {
473
+ const sprintYaml = `sprint:
474
+ number: 11
475
+ status: active
476
+ epics:
477
+ - id: epic-47
478
+ title: Jira Sync
479
+ stories:
480
+ - id: "47-1"
481
+ title: Auto-create Jira epic
482
+ status: done
483
+ - id: "47-2"
484
+ title: Sync sprint numbers
485
+ status: done
486
+ - id: "47-3"
487
+ title: Detect Jira-only stories
488
+ status: in_progress
489
+ `;
490
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
491
+ writeFileSync(sprintPath, sprintYaml);
492
+ const result = await getYamlStoryIds({ sprintPath });
493
+ assert.strictEqual(result.success, true);
494
+ assert.ok(result.storyIds, 'Should return storyIds array');
495
+ assert.strictEqual(result.storyIds?.length, 3);
496
+ assert.ok(result.storyIds?.includes('47-1'));
497
+ assert.ok(result.storyIds?.includes('47-2'));
498
+ assert.ok(result.storyIds?.includes('47-3'));
499
+ });
500
+ it('should extract stories from multiple epics', async () => {
501
+ const sprintYaml = `sprint:
502
+ number: 11
503
+ status: active
504
+ epics:
505
+ - id: epic-31
506
+ title: Workflow Engine
507
+ stories:
508
+ - id: "31-1"
509
+ status: done
510
+ - id: "31-2"
511
+ status: done
512
+ - id: epic-47
513
+ title: Jira Sync
514
+ stories:
515
+ - id: "47-1"
516
+ status: done
517
+ `;
518
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
519
+ writeFileSync(sprintPath, sprintYaml);
520
+ const result = await getYamlStoryIds({ sprintPath });
521
+ assert.strictEqual(result.success, true);
522
+ assert.strictEqual(result.storyIds?.length, 3);
523
+ assert.ok(result.storyIds?.includes('31-1'));
524
+ assert.ok(result.storyIds?.includes('31-2'));
525
+ assert.ok(result.storyIds?.includes('47-1'));
526
+ });
527
+ it('should return empty array for YAML with no stories', async () => {
528
+ const sprintYaml = `sprint:
529
+ number: 11
530
+ status: active
531
+ epics: []
532
+ `;
533
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
534
+ writeFileSync(sprintPath, sprintYaml);
535
+ const result = await getYamlStoryIds({ sprintPath });
536
+ assert.strictEqual(result.success, true);
537
+ assert.strictEqual(result.storyIds?.length, 0);
538
+ });
539
+ it('should handle missing file gracefully', async () => {
540
+ const result = await getYamlStoryIds({
541
+ sprintPath: join(TEST_DIR, 'nonexistent.yaml')
542
+ });
543
+ assert.strictEqual(result.success, false);
544
+ assert.ok(result.error?.includes('not found') || result.error?.includes('ENOENT'));
545
+ });
546
+ });
547
+ describe('findJiraOnlyStories() - Compare Jira issues with YAML stories', () => {
548
+ it('should find stories in Jira but not in YAML', async () => {
549
+ const jiraIssues = [
550
+ { key: 'MSSCI-11797', summary: '47-1: Auto-create Jira epic', status: 'Done' },
551
+ { key: 'MSSCI-11798', summary: '47-2: Sync sprint numbers', status: 'Done' },
552
+ { key: 'MSSCI-11800', summary: '47-4: Bidirectional sync', status: 'To Do' } // Not in YAML!
553
+ ];
554
+ const yamlStoryIds = ['47-1', '47-2', '47-3'];
555
+ const result = await findJiraOnlyStories({
556
+ jiraIssues,
557
+ yamlStoryIds
558
+ });
559
+ assert.strictEqual(result.success, true);
560
+ assert.strictEqual(result.missingStories?.length, 1);
561
+ assert.strictEqual(result.missingStories?.[0].key, 'MSSCI-11800');
562
+ assert.strictEqual(result.missingStories?.[0].storyId, '47-4');
563
+ });
564
+ it('should return empty when all Jira stories are in YAML', async () => {
565
+ const jiraIssues = [
566
+ { key: 'MSSCI-11797', summary: '47-1: Auto-create Jira epic', status: 'Done' },
567
+ { key: 'MSSCI-11798', summary: '47-2: Sync sprint numbers', status: 'Done' }
568
+ ];
569
+ const yamlStoryIds = ['47-1', '47-2', '47-3'];
570
+ const result = await findJiraOnlyStories({
571
+ jiraIssues,
572
+ yamlStoryIds
573
+ });
574
+ assert.strictEqual(result.success, true);
575
+ assert.strictEqual(result.missingStories?.length, 0);
576
+ });
577
+ it('should extract story ID from Jira summary format', async () => {
578
+ // Jira summaries often have format "47-1: Title" or "Story 47-1: Title"
579
+ const jiraIssues = [
580
+ { key: 'MSSCI-100', summary: 'Story 31-5: New feature', status: 'In Progress' },
581
+ { key: 'MSSCI-101', summary: '31-6: Another feature', status: 'To Do' }
582
+ ];
583
+ const yamlStoryIds = ['31-1', '31-2'];
584
+ const result = await findJiraOnlyStories({
585
+ jiraIssues,
586
+ yamlStoryIds
587
+ });
588
+ assert.strictEqual(result.success, true);
589
+ assert.strictEqual(result.missingStories?.length, 2);
590
+ assert.strictEqual(result.missingStories?.[0].storyId, '31-5');
591
+ assert.strictEqual(result.missingStories?.[1].storyId, '31-6');
592
+ });
593
+ it('should filter by pennyfarthing label when provided', async () => {
594
+ const jiraIssues = [
595
+ { key: 'MSSCI-100', summary: '47-4: PF story', status: 'To Do', labels: ['pennyfarthing'] },
596
+ { key: 'MSSCI-101', summary: 'OTHER-1: Not PF', status: 'To Do', labels: ['other-project'] }
597
+ ];
598
+ const yamlStoryIds = ['47-1'];
599
+ const result = await findJiraOnlyStories({
600
+ jiraIssues,
601
+ yamlStoryIds,
602
+ filterLabel: 'pennyfarthing'
603
+ });
604
+ assert.strictEqual(result.success, true);
605
+ assert.strictEqual(result.missingStories?.length, 1);
606
+ assert.strictEqual(result.missingStories?.[0].key, 'MSSCI-100');
607
+ });
608
+ });
609
+ // ============================================
610
+ // AC3: Report stories in Jira but not in YAML
611
+ // ============================================
612
+ describe('formatMissingStoriesReport() - Human-readable report', () => {
613
+ it('should format missing stories as readable report', async () => {
614
+ const missingStories = [
615
+ { key: 'MSSCI-11800', summary: '47-4: Bidirectional sync', status: 'To Do', storyId: '47-4' },
616
+ { key: 'MSSCI-11801', summary: '47-5: Retrofit epics', status: 'To Do', storyId: '47-5' }
617
+ ];
618
+ const result = await formatMissingStoriesReport({ missingStories });
619
+ assert.strictEqual(result.success, true);
620
+ assert.ok(result.report, 'Should have report string');
621
+ assert.ok(result.report?.includes('MSSCI-11800'));
622
+ assert.ok(result.report?.includes('47-4'));
623
+ assert.ok(result.report?.includes('Bidirectional sync'));
624
+ assert.ok(result.report?.includes('2 stories')); // Count in header
625
+ });
626
+ it('should return "no missing stories" message when empty', async () => {
627
+ const result = await formatMissingStoriesReport({ missingStories: [] });
628
+ assert.strictEqual(result.success, true);
629
+ assert.ok(result.report?.toLowerCase().includes('no missing') ||
630
+ result.report?.toLowerCase().includes('all synced') ||
631
+ result.report?.includes('0 stories'));
632
+ });
633
+ it('should include Jira URL in report', async () => {
634
+ const missingStories = [
635
+ { key: 'MSSCI-11800', summary: '47-4: Test', status: 'To Do', storyId: '47-4' }
636
+ ];
637
+ const result = await formatMissingStoriesReport({
638
+ missingStories,
639
+ jiraBaseUrl: 'https://1898andco.atlassian.net'
640
+ });
641
+ assert.strictEqual(result.success, true);
642
+ assert.ok(result.report?.includes('https://1898andco.atlassian.net/browse/MSSCI-11800'));
643
+ });
644
+ });
645
+ // ============================================
646
+ // AC4: Optionally import missing stories to YAML
647
+ // ============================================
648
+ describe('importMissingStoriesToYaml() - Add missing stories to sprint YAML', () => {
649
+ it('should add missing story to existing epic in YAML', async () => {
650
+ const sprintYaml = `sprint:
651
+ number: 11
652
+ status: active
653
+ epics:
654
+ - id: epic-47
655
+ title: Jira Sync
656
+ stories:
657
+ - id: "47-1"
658
+ title: Auto-create Jira epic
659
+ status: done
660
+ `;
661
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
662
+ writeFileSync(sprintPath, sprintYaml);
663
+ const missingStories = [
664
+ { key: 'MSSCI-11800', summary: '47-4: Bidirectional sync', status: 'To Do', storyId: '47-4', points: 4 }
665
+ ];
666
+ const result = await importMissingStoriesToYaml({
667
+ sprintPath,
668
+ missingStories,
669
+ targetEpicId: 'epic-47'
670
+ });
671
+ assert.strictEqual(result.success, true);
672
+ assert.strictEqual(result.importedCount, 1);
673
+ // Verify the YAML was updated
674
+ const updatedContent = readFileSync(sprintPath, 'utf-8');
675
+ assert.ok(updatedContent.includes('47-4'));
676
+ assert.ok(updatedContent.includes('Bidirectional sync'));
677
+ });
678
+ it('should preserve existing story order and add new at end', async () => {
679
+ const sprintYaml = `sprint:
680
+ number: 11
681
+ epics:
682
+ - id: epic-47
683
+ stories:
684
+ - id: "47-1"
685
+ status: done
686
+ - id: "47-2"
687
+ status: done
688
+ `;
689
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
690
+ writeFileSync(sprintPath, sprintYaml);
691
+ const missingStories = [
692
+ { key: 'MSSCI-11800', summary: '47-4: New story', status: 'To Do', storyId: '47-4' }
693
+ ];
694
+ const result = await importMissingStoriesToYaml({
695
+ sprintPath,
696
+ missingStories,
697
+ targetEpicId: 'epic-47'
698
+ });
699
+ assert.strictEqual(result.success, true);
700
+ const updatedContent = readFileSync(sprintPath, 'utf-8');
701
+ const idx47_1 = updatedContent.indexOf('47-1');
702
+ const idx47_2 = updatedContent.indexOf('47-2');
703
+ const idx47_4 = updatedContent.indexOf('47-4');
704
+ assert.ok(idx47_1 < idx47_2, '47-1 should come before 47-2');
705
+ assert.ok(idx47_2 < idx47_4, '47-4 should come after 47-2');
706
+ });
707
+ it('should support dry-run mode that does not modify file', async () => {
708
+ const sprintYaml = `sprint:
709
+ number: 11
710
+ epics:
711
+ - id: epic-47
712
+ stories:
713
+ - id: "47-1"
714
+ status: done
715
+ `;
716
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
717
+ writeFileSync(sprintPath, sprintYaml);
718
+ const missingStories = [
719
+ { key: 'MSSCI-11800', summary: '47-4: New story', status: 'To Do', storyId: '47-4' }
720
+ ];
721
+ const result = await importMissingStoriesToYaml({
722
+ sprintPath,
723
+ missingStories,
724
+ targetEpicId: 'epic-47',
725
+ dryRun: true
726
+ });
727
+ assert.strictEqual(result.success, true);
728
+ assert.strictEqual(result.importedCount, 1);
729
+ assert.ok(result.wouldImport, 'Should indicate what would be imported');
730
+ // File should NOT be modified
731
+ const content = readFileSync(sprintPath, 'utf-8');
732
+ assert.ok(!content.includes('47-4'), 'File should not contain 47-4 in dry-run');
733
+ });
734
+ it('should fail if target epic does not exist', async () => {
735
+ const sprintYaml = `sprint:
736
+ number: 11
737
+ epics:
738
+ - id: epic-31
739
+ stories:
740
+ - id: "31-1"
741
+ status: done
742
+ `;
743
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
744
+ writeFileSync(sprintPath, sprintYaml);
745
+ const missingStories = [
746
+ { key: 'MSSCI-11800', summary: '47-4: New story', status: 'To Do', storyId: '47-4' }
747
+ ];
748
+ const result = await importMissingStoriesToYaml({
749
+ sprintPath,
750
+ missingStories,
751
+ targetEpicId: 'epic-47' // Does not exist!
752
+ });
753
+ assert.strictEqual(result.success, false);
754
+ assert.ok(result.error?.includes('epic-47') || result.error?.includes('not found'));
755
+ });
756
+ it('should map Jira status to YAML status', async () => {
757
+ const sprintYaml = `sprint:
758
+ number: 11
759
+ epics:
760
+ - id: epic-47
761
+ stories:
762
+ - id: "47-1"
763
+ status: done
764
+ `;
765
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
766
+ writeFileSync(sprintPath, sprintYaml);
767
+ const missingStories = [
768
+ { key: 'MSSCI-100', summary: '47-2: Story', status: 'In Progress', storyId: '47-2' },
769
+ { key: 'MSSCI-101', summary: '47-3: Story', status: 'Done', storyId: '47-3' },
770
+ { key: 'MSSCI-102', summary: '47-4: Story', status: 'To Do', storyId: '47-4' }
771
+ ];
772
+ const result = await importMissingStoriesToYaml({
773
+ sprintPath,
774
+ missingStories,
775
+ targetEpicId: 'epic-47'
776
+ });
777
+ assert.strictEqual(result.success, true);
778
+ const content = readFileSync(sprintPath, 'utf-8');
779
+ assert.ok(content.includes('status: in_progress') || content.includes('status: in-progress'));
780
+ assert.ok(content.includes('status: done'));
781
+ assert.ok(content.includes('status: backlog') || content.includes('status: todo'));
782
+ });
783
+ });
784
+ // ============================================
785
+ // Integration: Full detection flow
786
+ // ============================================
787
+ describe('Integration: Detect and report Jira-only stories', () => {
788
+ it('should detect stories in Jira sprint but missing from YAML', async () => {
789
+ // Setup: YAML with stories 47-1, 47-2, 47-3
790
+ const sprintYaml = `sprint:
791
+ number: 11
792
+ jira_sprint_id: 275
793
+ status: active
794
+ epics:
795
+ - id: epic-47
796
+ title: Jira Sync
797
+ stories:
798
+ - id: "47-1"
799
+ title: Auto-create Jira epic
800
+ jira: MSSCI-11797
801
+ status: done
802
+ - id: "47-2"
803
+ title: Sync sprint numbers
804
+ jira: MSSCI-11798
805
+ status: done
806
+ - id: "47-3"
807
+ title: Detect Jira-only stories
808
+ jira: MSSCI-11799
809
+ status: in_progress
810
+ `;
811
+ const sprintPath = join(TEST_DIR, 'current-sprint.yaml');
812
+ writeFileSync(sprintPath, sprintYaml);
813
+ // Step 1: Get YAML story IDs
814
+ const yamlResult = await getYamlStoryIds({ sprintPath });
815
+ assert.strictEqual(yamlResult.success, true);
816
+ assert.strictEqual(yamlResult.storyIds?.length, 3);
817
+ // Step 2: Mock Jira sprint issues (includes 47-4 and 47-5 not in YAML)
818
+ const jiraIssues = [
819
+ { key: 'MSSCI-11797', summary: '47-1: Auto-create Jira epic', status: 'Done', labels: ['pennyfarthing'] },
820
+ { key: 'MSSCI-11798', summary: '47-2: Sync sprint numbers', status: 'Done', labels: ['pennyfarthing'] },
821
+ { key: 'MSSCI-11799', summary: '47-3: Detect Jira-only stories', status: 'In Progress', labels: ['pennyfarthing'] },
822
+ { key: 'MSSCI-11800', summary: '47-4: Bidirectional sync', status: 'To Do', labels: ['pennyfarthing'] },
823
+ { key: 'MSSCI-11801', summary: '47-5: Retrofit epics', status: 'To Do', labels: ['pennyfarthing'] }
824
+ ];
825
+ // Step 3: Find Jira-only stories
826
+ const compareResult = await findJiraOnlyStories({
827
+ jiraIssues,
828
+ yamlStoryIds: yamlResult.storyIds,
829
+ filterLabel: 'pennyfarthing'
830
+ });
831
+ assert.strictEqual(compareResult.success, true);
832
+ assert.strictEqual(compareResult.missingStories?.length, 2);
833
+ // Step 4: Generate report
834
+ const reportResult = await formatMissingStoriesReport({
835
+ missingStories: compareResult.missingStories,
836
+ jiraBaseUrl: 'https://1898andco.atlassian.net'
837
+ });
838
+ assert.strictEqual(reportResult.success, true);
839
+ assert.ok(reportResult.report?.includes('47-4'));
840
+ assert.ok(reportResult.report?.includes('47-5'));
841
+ assert.ok(reportResult.report?.includes('2 stories'));
842
+ });
843
+ });
844
+ });
845
+ //# sourceMappingURL=jira-sprint-sync.test.js.map