@pennyfarthing/core 10.1.0 → 10.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (407) hide show
  1. package/README.md +13 -18
  2. package/package.json +3 -1
  3. package/packages/core/dist/cli/commands/doctor-file-layout.test.js.map +1 -1
  4. package/packages/core/dist/cli/commands/doctor-legacy.test.js +24 -0
  5. package/packages/core/dist/cli/commands/doctor-legacy.test.js.map +1 -1
  6. package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
  7. package/packages/core/dist/cli/commands/doctor.js +101 -15
  8. package/packages/core/dist/cli/commands/doctor.js.map +1 -1
  9. package/packages/core/dist/cli/commands/e2e-fresh-install.test.js +1 -1
  10. package/packages/core/dist/cli/commands/e2e-fresh-install.test.js.map +1 -1
  11. package/packages/core/dist/cli/commands/e2e-upgrade.test.js +1 -1
  12. package/packages/core/dist/cli/commands/e2e-upgrade.test.js.map +1 -1
  13. package/packages/core/dist/cli/commands/hooks-consolidation.test.js +2 -2
  14. package/packages/core/dist/cli/commands/hooks-consolidation.test.js.map +1 -1
  15. package/packages/core/dist/cli/commands/init-consolidation.test.js.map +1 -1
  16. package/packages/core/dist/cli/commands/uninstall.d.ts.map +1 -1
  17. package/packages/core/dist/cli/commands/uninstall.js +24 -13
  18. package/packages/core/dist/cli/commands/uninstall.js.map +1 -1
  19. package/packages/core/dist/cli/commands/update-consolidation.test.js +0 -10
  20. package/packages/core/dist/cli/commands/update-consolidation.test.js.map +1 -1
  21. package/packages/core/dist/cli/commands/update.js.map +1 -1
  22. package/packages/core/dist/cli/ocean-profiles.test.js.map +1 -1
  23. package/packages/core/dist/cli/theme-maker.test.js +64 -115
  24. package/packages/core/dist/cli/theme-maker.test.js.map +1 -1
  25. package/packages/core/dist/index.d.ts +1 -1
  26. package/packages/core/dist/index.d.ts.map +1 -1
  27. package/packages/core/dist/index.js +2 -2
  28. package/packages/core/dist/index.js.map +1 -1
  29. package/packages/core/dist/plugins/plugin-discovery.d.ts +116 -0
  30. package/packages/core/dist/plugins/plugin-discovery.d.ts.map +1 -0
  31. package/packages/core/dist/plugins/plugin-discovery.js +165 -0
  32. package/packages/core/dist/plugins/plugin-discovery.js.map +1 -0
  33. package/packages/core/dist/plugins/plugin-discovery.test.d.ts +22 -0
  34. package/packages/core/dist/plugins/plugin-discovery.test.d.ts.map +1 -0
  35. package/packages/core/dist/plugins/plugin-discovery.test.js +498 -0
  36. package/packages/core/dist/plugins/plugin-discovery.test.js.map +1 -0
  37. package/packages/core/dist/scripts/generate-spider-report.js.map +1 -1
  38. package/packages/core/dist/workflow/context-watch.d.ts +80 -0
  39. package/packages/core/dist/workflow/context-watch.d.ts.map +1 -0
  40. package/packages/core/dist/workflow/context-watch.js +235 -0
  41. package/packages/core/dist/workflow/context-watch.js.map +1 -0
  42. package/packages/core/dist/workflow/context-watch.test.d.ts +1 -0
  43. package/packages/core/dist/workflow/context-watch.test.d.ts.map +1 -0
  44. package/packages/core/dist/workflow/context-watch.test.js +746 -0
  45. package/packages/core/dist/workflow/context-watch.test.js.map +1 -0
  46. package/packages/core/dist/workflow/file-watch.d.ts +82 -0
  47. package/packages/core/dist/workflow/file-watch.d.ts.map +1 -0
  48. package/packages/core/dist/workflow/file-watch.js +198 -0
  49. package/packages/core/dist/workflow/file-watch.js.map +1 -0
  50. package/packages/core/dist/workflow/file-watch.test.d.ts +21 -0
  51. package/packages/core/dist/workflow/file-watch.test.d.ts.map +1 -0
  52. package/packages/core/dist/workflow/file-watch.test.js +469 -0
  53. package/packages/core/dist/workflow/file-watch.test.js.map +1 -0
  54. package/packages/core/dist/workflow/observation-writer.d.ts +79 -0
  55. package/packages/core/dist/workflow/observation-writer.d.ts.map +1 -0
  56. package/packages/core/dist/workflow/observation-writer.js +97 -0
  57. package/packages/core/dist/workflow/observation-writer.js.map +1 -0
  58. package/packages/core/dist/workflow/observation-writer.test.d.ts +18 -0
  59. package/packages/core/dist/workflow/observation-writer.test.d.ts.map +1 -0
  60. package/packages/core/dist/workflow/observation-writer.test.js +424 -0
  61. package/packages/core/dist/workflow/observation-writer.test.js.map +1 -0
  62. package/packages/core/dist/workflow/story-workflow-routing.test.js +4 -2
  63. package/packages/core/dist/workflow/story-workflow-routing.test.js.map +1 -1
  64. package/packages/core/dist/workflow/tandem-lifecycle.d.ts +117 -0
  65. package/packages/core/dist/workflow/tandem-lifecycle.d.ts.map +1 -0
  66. package/packages/core/dist/workflow/tandem-lifecycle.js +186 -0
  67. package/packages/core/dist/workflow/tandem-lifecycle.js.map +1 -0
  68. package/packages/core/dist/workflow/tandem-lifecycle.test.d.ts +16 -0
  69. package/packages/core/dist/workflow/tandem-lifecycle.test.d.ts.map +1 -0
  70. package/packages/core/dist/workflow/tandem-lifecycle.test.js +531 -0
  71. package/packages/core/dist/workflow/tandem-lifecycle.test.js.map +1 -0
  72. package/packages/core/dist/workflow/tool-watch.d.ts +68 -0
  73. package/packages/core/dist/workflow/tool-watch.d.ts.map +1 -0
  74. package/packages/core/dist/workflow/tool-watch.js +166 -0
  75. package/packages/core/dist/workflow/tool-watch.js.map +1 -0
  76. package/packages/core/dist/workflow/tool-watch.test.d.ts +18 -0
  77. package/packages/core/dist/workflow/tool-watch.test.d.ts.map +1 -0
  78. package/packages/core/dist/workflow/tool-watch.test.js +718 -0
  79. package/packages/core/dist/workflow/tool-watch.test.js.map +1 -0
  80. package/packages/core/dist/workflow/workflow-migration.test.js +8 -4
  81. package/packages/core/dist/workflow/workflow-migration.test.js.map +1 -1
  82. package/packages/core/dist/workflow/workflow-schema.d.ts +7 -0
  83. package/packages/core/dist/workflow/workflow-schema.d.ts.map +1 -1
  84. package/packages/core/dist/workflow/workflow-schema.js +44 -0
  85. package/packages/core/dist/workflow/workflow-schema.js.map +1 -1
  86. package/packages/core/dist/workflow/workflow-schema.test.d.ts.map +1 -1
  87. package/packages/core/dist/workflow/workflow-schema.test.js +192 -0
  88. package/packages/core/dist/workflow/workflow-schema.test.js.map +1 -1
  89. package/pennyfarthing-dist/agents/handoff.md +18 -3
  90. package/pennyfarthing-dist/agents/sm-finish.md +1 -1
  91. package/pennyfarthing-dist/agents/sm-handoff.md +27 -4
  92. package/pennyfarthing-dist/agents/sm.md +11 -5
  93. package/pennyfarthing-dist/agents/tandem-backseat.md +119 -0
  94. package/pennyfarthing-dist/commands/setup.md +4 -0
  95. package/pennyfarthing-dist/guides/agent-behavior.md +62 -6
  96. package/pennyfarthing-dist/guides/bikelane.md +3 -2
  97. package/pennyfarthing-dist/guides/scale-levels.md +4 -6
  98. package/pennyfarthing-dist/guides/tandem-protocol.md +158 -0
  99. package/pennyfarthing-dist/personas/themes/discworld.yaml +1 -1
  100. package/pennyfarthing-dist/personas/themes/fifth-element.yaml +295 -0
  101. package/pennyfarthing-dist/scripts/README.md +1 -1
  102. package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +131 -54
  103. package/pennyfarthing-dist/scripts/hooks/post-merge.sh +20 -10
  104. package/pennyfarthing-dist/scripts/misc/statusline.sh +50 -8
  105. package/pennyfarthing-dist/scripts/workflow/README.md +2 -2
  106. package/pennyfarthing-dist/scripts/workflow/finish-story.sh +10 -189
  107. package/pennyfarthing-dist/skills/skill-registry.schema.json +8 -0
  108. package/pennyfarthing-dist/skills/skill-registry.yaml +1 -1
  109. package/pennyfarthing-dist/skills/sprint/skill.md +25 -2
  110. package/pennyfarthing-dist/skills/workflow/skill.md +24 -1
  111. package/pennyfarthing-dist/workflows/architecture/workflow.yaml +65 -0
  112. package/pennyfarthing-dist/workflows/bdd-tandem.yaml +70 -0
  113. package/pennyfarthing-dist/workflows/tdd-tandem.yaml +61 -0
  114. package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  115. package/pennyfarthing_scripts/__pycache__/bellmode_hook.cpython-314.pyc +0 -0
  116. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  117. package/pennyfarthing_scripts/__pycache__/config.cpython-314.pyc +0 -0
  118. package/pennyfarthing_scripts/__pycache__/hooks.cpython-314.pyc +0 -0
  119. package/pennyfarthing_scripts/__pycache__/jira_bidirectional_sync.cpython-314.pyc +0 -0
  120. package/pennyfarthing_scripts/__pycache__/jira_epic_creation.cpython-314.pyc +0 -0
  121. package/pennyfarthing_scripts/__pycache__/jira_sync.cpython-314.pyc +0 -0
  122. package/pennyfarthing_scripts/__pycache__/jira_sync_story.cpython-314.pyc +0 -0
  123. package/pennyfarthing_scripts/__pycache__/output.cpython-314.pyc +0 -0
  124. package/pennyfarthing_scripts/__pycache__/patch_mode.cpython-314.pyc +0 -0
  125. package/pennyfarthing_scripts/__pycache__/schema_validation_hook.cpython-314.pyc +0 -0
  126. package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
  127. package/pennyfarthing_scripts/bellmode_hook.py +202 -47
  128. package/pennyfarthing_scripts/brownfield/__init__.py +6 -6
  129. package/pennyfarthing_scripts/brownfield/__main__.py +1 -0
  130. package/pennyfarthing_scripts/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
  131. package/pennyfarthing_scripts/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
  132. package/pennyfarthing_scripts/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
  133. package/pennyfarthing_scripts/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
  134. package/pennyfarthing_scripts/brownfield/cli.py +0 -1
  135. package/pennyfarthing_scripts/brownfield/discover.py +1 -2
  136. package/pennyfarthing_scripts/cli.py +11 -6
  137. package/pennyfarthing_scripts/codemarkers/__init__.py +5 -1
  138. package/pennyfarthing_scripts/codemarkers/__pycache__/__init__.cpython-314.pyc +0 -0
  139. package/pennyfarthing_scripts/codemarkers/__pycache__/__main__.cpython-314.pyc +0 -0
  140. package/pennyfarthing_scripts/codemarkers/__pycache__/analyze.cpython-314.pyc +0 -0
  141. package/pennyfarthing_scripts/codemarkers/__pycache__/cli.cpython-314.pyc +0 -0
  142. package/pennyfarthing_scripts/codemarkers/__pycache__/formatters.cpython-314.pyc +0 -0
  143. package/pennyfarthing_scripts/codemarkers/__pycache__/models.cpython-314.pyc +0 -0
  144. package/pennyfarthing_scripts/codemarkers/analyze.py +177 -2
  145. package/pennyfarthing_scripts/codemarkers/cli.py +50 -0
  146. package/pennyfarthing_scripts/codemarkers/formatters.py +0 -1
  147. package/pennyfarthing_scripts/codemarkers/models.py +15 -0
  148. package/pennyfarthing_scripts/common/__init__.py +8 -9
  149. package/pennyfarthing_scripts/common/__pycache__/__init__.cpython-314.pyc +0 -0
  150. package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
  151. package/pennyfarthing_scripts/common/__pycache__/output.cpython-314.pyc +0 -0
  152. package/pennyfarthing_scripts/common/__pycache__/themes.cpython-314.pyc +0 -0
  153. package/pennyfarthing_scripts/common/config.py +1 -1
  154. package/pennyfarthing_scripts/complexity/__init__.py +1 -1
  155. package/pennyfarthing_scripts/complexity/__pycache__/__init__.cpython-314.pyc +0 -0
  156. package/pennyfarthing_scripts/complexity/__pycache__/__main__.cpython-314.pyc +0 -0
  157. package/pennyfarthing_scripts/complexity/__pycache__/analyze.cpython-314.pyc +0 -0
  158. package/pennyfarthing_scripts/complexity/__pycache__/cli.cpython-314.pyc +0 -0
  159. package/pennyfarthing_scripts/complexity/__pycache__/formatters.cpython-314.pyc +0 -0
  160. package/pennyfarthing_scripts/complexity/__pycache__/models.cpython-314.pyc +0 -0
  161. package/pennyfarthing_scripts/complexity/analyze.py +1 -1
  162. package/pennyfarthing_scripts/complexity/cli.py +5 -1
  163. package/pennyfarthing_scripts/complexity/formatters.py +1 -1
  164. package/pennyfarthing_scripts/context.py +14 -15
  165. package/pennyfarthing_scripts/deadcode/__pycache__/__init__.cpython-314.pyc +0 -0
  166. package/pennyfarthing_scripts/deadcode/__pycache__/__main__.cpython-314.pyc +0 -0
  167. package/pennyfarthing_scripts/deadcode/__pycache__/analyze.cpython-314.pyc +0 -0
  168. package/pennyfarthing_scripts/deadcode/__pycache__/cli.cpython-314.pyc +0 -0
  169. package/pennyfarthing_scripts/deadcode/__pycache__/formatters.cpython-314.pyc +0 -0
  170. package/pennyfarthing_scripts/deadcode/__pycache__/models.cpython-314.pyc +0 -0
  171. package/pennyfarthing_scripts/deadcode/analyze.py +3 -4
  172. package/pennyfarthing_scripts/deadcode/cli.py +2 -2
  173. package/pennyfarthing_scripts/dependencies/__init__.py +2 -2
  174. package/pennyfarthing_scripts/dependencies/__pycache__/__init__.cpython-314.pyc +0 -0
  175. package/pennyfarthing_scripts/dependencies/__pycache__/__main__.cpython-314.pyc +0 -0
  176. package/pennyfarthing_scripts/dependencies/__pycache__/analyze.cpython-314.pyc +0 -0
  177. package/pennyfarthing_scripts/dependencies/__pycache__/cli.cpython-314.pyc +0 -0
  178. package/pennyfarthing_scripts/dependencies/__pycache__/formatters.cpython-314.pyc +0 -0
  179. package/pennyfarthing_scripts/dependencies/__pycache__/models.cpython-314.pyc +0 -0
  180. package/pennyfarthing_scripts/dependencies/analyze.py +1 -1
  181. package/pennyfarthing_scripts/dependencies/cli.py +8 -4
  182. package/pennyfarthing_scripts/dependencies/formatters.py +1 -1
  183. package/pennyfarthing_scripts/git/__init__.py +5 -5
  184. package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
  185. package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
  186. package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
  187. package/pennyfarthing_scripts/git/create_branches.py +3 -2
  188. package/pennyfarthing_scripts/git/status_all.py +1 -1
  189. package/pennyfarthing_scripts/healthscore/__init__.py +2 -2
  190. package/pennyfarthing_scripts/healthscore/__main__.py +8 -0
  191. package/pennyfarthing_scripts/healthscore/__pycache__/__init__.cpython-314.pyc +0 -0
  192. package/pennyfarthing_scripts/healthscore/__pycache__/__main__.cpython-314.pyc +0 -0
  193. package/pennyfarthing_scripts/healthscore/__pycache__/analyze.cpython-314.pyc +0 -0
  194. package/pennyfarthing_scripts/healthscore/__pycache__/cli.cpython-314.pyc +0 -0
  195. package/pennyfarthing_scripts/healthscore/__pycache__/formatters.cpython-314.pyc +0 -0
  196. package/pennyfarthing_scripts/healthscore/__pycache__/models.cpython-314.pyc +0 -0
  197. package/pennyfarthing_scripts/healthscore/analyze.py +451 -21
  198. package/pennyfarthing_scripts/healthscore/cli.py +5 -1
  199. package/pennyfarthing_scripts/healthscore/models.py +0 -1
  200. package/pennyfarthing_scripts/hooks.py +8 -11
  201. package/pennyfarthing_scripts/hotspots/__init__.py +6 -6
  202. package/pennyfarthing_scripts/hotspots/__pycache__/__init__.cpython-314.pyc +0 -0
  203. package/pennyfarthing_scripts/hotspots/__pycache__/__main__.cpython-314.pyc +0 -0
  204. package/pennyfarthing_scripts/hotspots/__pycache__/analyze.cpython-314.pyc +0 -0
  205. package/pennyfarthing_scripts/hotspots/__pycache__/cli.cpython-314.pyc +0 -0
  206. package/pennyfarthing_scripts/hotspots/__pycache__/formatters.cpython-314.pyc +0 -0
  207. package/pennyfarthing_scripts/hotspots/__pycache__/models.cpython-314.pyc +0 -0
  208. package/pennyfarthing_scripts/hotspots/analyze.py +128 -14
  209. package/pennyfarthing_scripts/hotspots/cli.py +2 -2
  210. package/pennyfarthing_scripts/hotspots/models.py +0 -1
  211. package/pennyfarthing_scripts/jira/__init__.py +15 -17
  212. package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-314.pyc +0 -0
  213. package/pennyfarthing_scripts/jira/__pycache__/__main__.cpython-314.pyc +0 -0
  214. package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
  215. package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
  216. package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
  217. package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
  218. package/pennyfarthing_scripts/jira/__pycache__/create.cpython-314.pyc +0 -0
  219. package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-314.pyc +0 -0
  220. package/pennyfarthing_scripts/jira/__pycache__/operations.cpython-314.pyc +0 -0
  221. package/pennyfarthing_scripts/jira/__pycache__/reconcile.cpython-314.pyc +0 -0
  222. package/pennyfarthing_scripts/jira/__pycache__/story.cpython-314.pyc +0 -0
  223. package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-314.pyc +0 -0
  224. package/pennyfarthing_scripts/jira/bidirectional.py +2 -3
  225. package/pennyfarthing_scripts/jira/claim.py +21 -0
  226. package/pennyfarthing_scripts/jira/cli.py +2 -2
  227. package/pennyfarthing_scripts/jira/client.py +4 -4
  228. package/pennyfarthing_scripts/jira/create.py +45 -1
  229. package/pennyfarthing_scripts/jira/epic.py +3 -2
  230. package/pennyfarthing_scripts/jira/reconcile.py +0 -1
  231. package/pennyfarthing_scripts/jira/story.py +2 -0
  232. package/pennyfarthing_scripts/jira/sync.py +1 -1
  233. package/pennyfarthing_scripts/migration/__pycache__/__init__.cpython-314.pyc +0 -0
  234. package/pennyfarthing_scripts/migration/__pycache__/session.cpython-314.pyc +0 -0
  235. package/pennyfarthing_scripts/migration/__pycache__/skill.cpython-314.pyc +0 -0
  236. package/pennyfarthing_scripts/migration/__pycache__/step.cpython-314.pyc +0 -0
  237. package/pennyfarthing_scripts/migration/__pycache__/validate.cpython-314.pyc +0 -0
  238. package/pennyfarthing_scripts/migration/skill.py +0 -1
  239. package/pennyfarthing_scripts/migration/step.py +0 -1
  240. package/pennyfarthing_scripts/migration/validate.py +8 -5
  241. package/pennyfarthing_scripts/patch_mode.py +2 -2
  242. package/pennyfarthing_scripts/preflight/__init__.py +1 -1
  243. package/pennyfarthing_scripts/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
  244. package/pennyfarthing_scripts/preflight/__pycache__/__main__.cpython-314.pyc +0 -0
  245. package/pennyfarthing_scripts/preflight/__pycache__/cli.cpython-314.pyc +0 -0
  246. package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
  247. package/pennyfarthing_scripts/preflight/finish.py +0 -1
  248. package/pennyfarthing_scripts/pretooluse_hook.py +6 -7
  249. package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
  250. package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
  251. package/pennyfarthing_scripts/prime/__pycache__/loader.cpython-314.pyc +0 -0
  252. package/pennyfarthing_scripts/prime/__pycache__/models.cpython-314.pyc +0 -0
  253. package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
  254. package/pennyfarthing_scripts/prime/__pycache__/session.cpython-314.pyc +0 -0
  255. package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
  256. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  257. package/pennyfarthing_scripts/prime/cli.py +5 -1
  258. package/pennyfarthing_scripts/prime/loader.py +2 -3
  259. package/pennyfarthing_scripts/prime/persona.py +2 -1
  260. package/pennyfarthing_scripts/prime/tiers.py +4 -4
  261. package/pennyfarthing_scripts/schema_validation_hook.py +2 -3
  262. package/pennyfarthing_scripts/sprint/__init__.py +10 -12
  263. package/pennyfarthing_scripts/sprint/__main__.py +2 -2
  264. package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
  265. package/pennyfarthing_scripts/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
  266. package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
  267. package/pennyfarthing_scripts/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
  268. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  269. package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
  270. package/pennyfarthing_scripts/sprint/__pycache__/import_epic.cpython-314.pyc +0 -0
  271. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  272. package/pennyfarthing_scripts/sprint/__pycache__/status.cpython-314.pyc +0 -0
  273. package/pennyfarthing_scripts/sprint/__pycache__/story_add.cpython-314.pyc +0 -0
  274. package/pennyfarthing_scripts/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
  275. package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
  276. package/pennyfarthing_scripts/sprint/__pycache__/validate_cmd.cpython-314.pyc +0 -0
  277. package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
  278. package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-314.pyc +0 -0
  279. package/pennyfarthing_scripts/sprint/__pycache__/yaml_io.cpython-314.pyc +0 -0
  280. package/pennyfarthing_scripts/sprint/archive.py +0 -1
  281. package/pennyfarthing_scripts/sprint/archive_epic.py +1 -4
  282. package/pennyfarthing_scripts/sprint/cli.py +34 -28
  283. package/pennyfarthing_scripts/sprint/epic_add.py +8 -1
  284. package/pennyfarthing_scripts/sprint/import_epic.py +42 -18
  285. package/pennyfarthing_scripts/sprint/loader.py +6 -0
  286. package/pennyfarthing_scripts/sprint/status.py +1 -2
  287. package/pennyfarthing_scripts/sprint/story_add.py +2 -2
  288. package/pennyfarthing_scripts/sprint/story_finish.py +3 -5
  289. package/pennyfarthing_scripts/sprint/story_update.py +11 -3
  290. package/pennyfarthing_scripts/sprint/validate_cmd.py +0 -1
  291. package/pennyfarthing_scripts/sprint/validator.py +120 -6
  292. package/pennyfarthing_scripts/sprint/work.py +1 -4
  293. package/pennyfarthing_scripts/sprint/yaml_io.py +10 -2
  294. package/pennyfarthing_scripts/story/__init__.py +14 -16
  295. package/pennyfarthing_scripts/story/__pycache__/__init__.cpython-314.pyc +0 -0
  296. package/pennyfarthing_scripts/story/__pycache__/__main__.cpython-314.pyc +0 -0
  297. package/pennyfarthing_scripts/story/__pycache__/cli.cpython-314.pyc +0 -0
  298. package/pennyfarthing_scripts/story/__pycache__/create.cpython-314.pyc +0 -0
  299. package/pennyfarthing_scripts/story/__pycache__/size.cpython-314.pyc +0 -0
  300. package/pennyfarthing_scripts/story/__pycache__/template.cpython-314.pyc +0 -0
  301. package/pennyfarthing_scripts/story/size.py +0 -1
  302. package/pennyfarthing_scripts/story/template.py +0 -1
  303. package/pennyfarthing_scripts/swebench.py +1 -2
  304. package/pennyfarthing_scripts/tests/__pycache__/__init__.cpython-314.pyc +0 -0
  305. package/pennyfarthing_scripts/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
  306. package/pennyfarthing_scripts/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
  307. package/pennyfarthing_scripts/tests/__pycache__/test_cli_modules.cpython-314-pytest-9.0.2.pyc +0 -0
  308. package/pennyfarthing_scripts/tests/__pycache__/test_codemarkers.cpython-314-pytest-9.0.2.pyc +0 -0
  309. package/pennyfarthing_scripts/tests/__pycache__/test_common.cpython-314-pytest-9.0.2.pyc +0 -0
  310. package/pennyfarthing_scripts/tests/__pycache__/test_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
  311. package/pennyfarthing_scripts/tests/__pycache__/test_healthscore.cpython-314-pytest-9.0.2.pyc +0 -0
  312. package/pennyfarthing_scripts/tests/__pycache__/test_jira_package.cpython-314-pytest-9.0.2.pyc +0 -0
  313. package/pennyfarthing_scripts/tests/__pycache__/test_package_structure.cpython-314-pytest-9.0.2.pyc +0 -0
  314. package/pennyfarthing_scripts/tests/__pycache__/test_patch_mode.cpython-314-pytest-9.0.2.pyc +0 -0
  315. package/pennyfarthing_scripts/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
  316. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc +0 -0
  317. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
  318. package/pennyfarthing_scripts/tests/__pycache__/test_story_add.cpython-314-pytest-9.0.2.pyc +0 -0
  319. package/pennyfarthing_scripts/tests/__pycache__/test_story_package.cpython-314-pytest-9.0.2.pyc +0 -0
  320. package/pennyfarthing_scripts/tests/__pycache__/test_story_update.cpython-314-pytest-9.0.2.pyc +0 -0
  321. package/pennyfarthing_scripts/tests/__pycache__/test_tiers.cpython-314-pytest-9.0.2.pyc +0 -0
  322. package/pennyfarthing_scripts/tests/__pycache__/test_token_counting.cpython-314-pytest-9.0.2.pyc +0 -0
  323. package/pennyfarthing_scripts/tests/__pycache__/test_validate_cmd.cpython-314-pytest-9.0.2.pyc +0 -0
  324. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
  325. package/pennyfarthing_scripts/tests/__pycache__/test_yaml_io.cpython-314-pytest-9.0.2.pyc +0 -0
  326. package/pennyfarthing_scripts/tests/conftest.py +1 -2
  327. package/pennyfarthing_scripts/tests/test_brownfield.py +10 -13
  328. package/pennyfarthing_scripts/tests/test_cli_modules.py +0 -4
  329. package/pennyfarthing_scripts/tests/test_codemarkers.py +13 -8
  330. package/pennyfarthing_scripts/tests/test_common.py +9 -4
  331. package/pennyfarthing_scripts/tests/test_epic_shard_validation.py +699 -0
  332. package/pennyfarthing_scripts/tests/test_git_utils.py +10 -13
  333. package/pennyfarthing_scripts/tests/test_healthscore.py +17 -25
  334. package/pennyfarthing_scripts/tests/test_jira_package.py +0 -3
  335. package/pennyfarthing_scripts/tests/test_package_structure.py +3 -16
  336. package/pennyfarthing_scripts/tests/test_patch_mode.py +7 -11
  337. package/pennyfarthing_scripts/tests/test_prime.py +39 -21
  338. package/pennyfarthing_scripts/tests/test_sprint_package.py +3 -8
  339. package/pennyfarthing_scripts/tests/test_sprint_validator.py +53 -5
  340. package/pennyfarthing_scripts/tests/test_story_add.py +3 -7
  341. package/pennyfarthing_scripts/tests/test_story_package.py +0 -3
  342. package/pennyfarthing_scripts/tests/test_story_update.py +5 -10
  343. package/pennyfarthing_scripts/tests/test_tiers.py +18 -17
  344. package/pennyfarthing_scripts/tests/test_token_counting.py +19 -13
  345. package/pennyfarthing_scripts/tests/test_validate_cmd.py +2 -7
  346. package/pennyfarthing_scripts/tests/test_workflow_check.py +0 -2
  347. package/pennyfarthing_scripts/tests/test_yaml_io.py +0 -3
  348. package/pennyfarthing_scripts/theme/__pycache__/__init__.cpython-314.pyc +0 -0
  349. package/pennyfarthing_scripts/theme/__pycache__/cli.cpython-314.pyc +0 -0
  350. package/pennyfarthing_scripts/theme/cli.py +3 -2
  351. package/pennyfarthing_scripts/validate/__init__.py +21 -0
  352. package/pennyfarthing_scripts/validate/__pycache__/__init__.cpython-314.pyc +0 -0
  353. package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +0 -0
  354. package/pennyfarthing_scripts/validate/adapters/__init__.py +0 -0
  355. package/pennyfarthing_scripts/validate/adapters/__pycache__/__init__.cpython-314.pyc +0 -0
  356. package/pennyfarthing_scripts/validate/adapters/__pycache__/agent.cpython-314.pyc +0 -0
  357. package/pennyfarthing_scripts/validate/adapters/__pycache__/schema.cpython-314.pyc +0 -0
  358. package/pennyfarthing_scripts/validate/adapters/__pycache__/skill_command.cpython-314.pyc +0 -0
  359. package/pennyfarthing_scripts/validate/adapters/__pycache__/sprint.cpython-314.pyc +0 -0
  360. package/pennyfarthing_scripts/validate/adapters/__pycache__/workflow.cpython-314.pyc +0 -0
  361. package/pennyfarthing_scripts/validate/adapters/agent.py +239 -0
  362. package/pennyfarthing_scripts/validate/adapters/schema.py +30 -0
  363. package/pennyfarthing_scripts/validate/adapters/skill_command.py +292 -0
  364. package/pennyfarthing_scripts/validate/adapters/sprint.py +69 -0
  365. package/pennyfarthing_scripts/validate/adapters/workflow.py +320 -0
  366. package/pennyfarthing_scripts/validate/cli.py +141 -0
  367. package/pennyfarthing_scripts/welcome_hook.py +2 -3
  368. package/pennyfarthing_scripts/workflow.py +3 -3
  369. package/scripts/README.md +3 -15
  370. package/pennyfarthing-dist/commands/benchmark-control.md +0 -69
  371. package/pennyfarthing-dist/commands/benchmark.md +0 -485
  372. package/pennyfarthing-dist/commands/job-fair.md +0 -102
  373. package/pennyfarthing-dist/commands/solo.md +0 -447
  374. package/pennyfarthing-dist/guides/benchmarks.md +0 -62
  375. package/pennyfarthing-dist/scripts/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
  376. package/pennyfarthing-dist/scripts/test/ensure-swebench-data.sh +0 -59
  377. package/pennyfarthing-dist/scripts/test/ground-truth-judge.py +0 -220
  378. package/pennyfarthing-dist/scripts/test/swebench-judge.py +0 -374
  379. package/pennyfarthing-dist/scripts/test/test-cache.sh +0 -165
  380. package/pennyfarthing-dist/scripts/test/test-setup.sh +0 -337
  381. package/pennyfarthing-dist/scripts/theme/compute-theme-tiers.sh +0 -13
  382. package/pennyfarthing-dist/scripts/theme/compute_theme_tiers.py +0 -402
  383. package/pennyfarthing-dist/scripts/theme/update-theme-tiers.sh +0 -97
  384. package/pennyfarthing-dist/skills/finalize-run/SKILL.md +0 -261
  385. package/pennyfarthing-dist/skills/judge/SKILL.md +0 -644
  386. package/pennyfarthing-dist/skills/persona-benchmark/SKILL.md +0 -187
  387. package/pennyfarthing-dist/workflows/dev-story/checklist.md +0 -80
  388. package/pennyfarthing-dist/workflows/dev-story/instructions.xml +0 -410
  389. package/pennyfarthing-dist/workflows/dev-story/workflow.yaml +0 -50
  390. package/pennyfarthing-dist/workflows/quick-spec/steps/step-01-understand.md +0 -201
  391. package/pennyfarthing-dist/workflows/quick-spec/steps/step-02-investigate.md +0 -156
  392. package/pennyfarthing-dist/workflows/quick-spec/steps/step-03-generate.md +0 -140
  393. package/pennyfarthing-dist/workflows/quick-spec/steps/step-04-review.md +0 -203
  394. package/pennyfarthing-dist/workflows/quick-spec/tech-spec-template.md +0 -74
  395. package/pennyfarthing-dist/workflows/quick-spec/workflow.yaml +0 -27
  396. package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  397. package/pennyfarthing_scripts/__pycache__/jira.cpython-314.pyc +0 -0
  398. package/pennyfarthing_scripts/__pycache__/pretooluse_hook.cpython-314.pyc +0 -0
  399. package/pennyfarthing_scripts/__pycache__/sprint.cpython-314.pyc +0 -0
  400. package/pennyfarthing_scripts/__pycache__/workflow.cpython-311.pyc +0 -0
  401. package/pennyfarthing_scripts/jira/__pycache__/compat.cpython-314.pyc +0 -0
  402. package/pennyfarthing_scripts/jira/__pycache__/mappings.cpython-314.pyc +0 -0
  403. package/pennyfarthing_scripts/jira/__pycache__/models.cpython-314.pyc +0 -0
  404. package/pennyfarthing_scripts/migration/__pycache__/__main__.cpython-314.pyc +0 -0
  405. package/pennyfarthing_scripts/migration/__pycache__/cli.cpython-314.pyc +0 -0
  406. package/pennyfarthing_scripts/prime/__pycache__/__main__.cpython-314.pyc +0 -0
  407. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_cli.cpython-314-pytest-9.0.2.pyc +0 -0
@@ -293,7 +293,7 @@ pf sprint story add <epic-id> "<title>" <points> [options]
293
293
  | `points` | Yes | Story points (1, 2, 3, 5, 8) |
294
294
  | `--type` | No | Story type: feature, bug, chore, refactor (default: feature) |
295
295
  | `--priority` | No | Priority: P0, P1, P2, P3 (default: P1) |
296
- | `--workflow` | No | Workflow: tdd, trivial, bdd (default: tdd) |
296
+ | `--workflow` | No | Workflow: tdd, tdd-tandem, trivial, bdd, bdd-tandem (default: tdd) |
297
297
  | `--jira` | No | Jira issue key |
298
298
  </args>
299
299
 
@@ -399,6 +399,28 @@ pf sprint story claim <story-id>
399
399
 
400
400
  ---
401
401
 
402
+ ### `/sprint story assign <story-id> <assignee>`
403
+
404
+ Assign a Jira story to a user. Shortcut for `/jira assign`.
405
+
406
+ <run>
407
+ pf jira assign <story-id> <assignee>
408
+ </run>
409
+
410
+ <args>
411
+ | Arg | Required | Description |
412
+ |-----|----------|-------------|
413
+ | `story-id` | Yes | Jira key (e.g., `MSSCI-14552`) |
414
+ | `assignee` | Yes | Email or GitHub username (e.g., `keith.avery@1898andco.io`, `slabgorb`) |
415
+ </args>
416
+
417
+ <example>
418
+ pf jira assign MSSCI-14552 keith.avery@1898andco.io
419
+ pf jira assign MSSCI-14552 slabgorb
420
+ </example>
421
+
422
+ ---
423
+
402
424
  ### `/sprint epic add <epic-id> <title> [options]`
403
425
 
404
426
  Add a new epic to the current sprint.
@@ -696,7 +718,7 @@ All gates must pass before handoff:
696
718
  | Sprint name | `"TO Sprint YYWW"` (e.g., "TO Sprint 2604") |
697
719
  | Story IDs | Jira keys `MSSCI-XXXXX` |
698
720
  | Status | `backlog`, `ready`, `in_progress`, `done` |
699
- | Workflow | `tdd`, `trivial`, `agent-docs`, `bdd` |
721
+ | Workflow | `tdd`, `tdd-tandem`, `trivial`, `agent-docs`, `bdd`, `bdd-tandem` |
700
722
  | Priority | `P0`, `P1`, `P2`, `P3` |
701
723
  | in_sprint | `true`, `false` - Jira sprint membership |
702
724
 
@@ -761,6 +783,7 @@ For Jira integration, see `/jira` skill prerequisites.
761
783
  | `/sprint story template` | `pf sprint story template` |
762
784
  | `/sprint story finish ID` | `pf sprint story finish ID` |
763
785
  | `/sprint story claim ID` | `pf sprint story claim ID` |
786
+ | `/sprint story assign ID USER` | `pf jira assign ID USER` |
764
787
  | `/sprint epic show ID` | `pf sprint epic show ID` |
765
788
  | `/sprint epic field ID FIELD` | `pf sprint epic field ID FIELD` |
766
789
  | `/sprint epic add ...` | `pf sprint epic add ...` |
@@ -163,6 +163,18 @@ SM → TEA → Dev → Reviewer → SM
163
163
  - Dev implements to make tests pass
164
164
  - **Triggers:** `types: [feature, enhancement]`, `points.min: 3`
165
165
 
166
+ ### TDD-Tandem (TDD with full tandem chain)
167
+
168
+ ```
169
+ setup → red (+Architect) → green (+TEA) → review (+PM) → finish
170
+ SM → TEA (+Architect) → Dev (+TEA) → Reviewer (+PM) → SM
171
+ ```
172
+
173
+ - Full tandem chain: every phase has a backseat observer
174
+ - Architect watches test design, TEA watches implementation, PM watches review
175
+ - For high-stakes features needing cross-role visibility
176
+ - **Triggers:** `tags: [tandem]`, `points.min: 3`
177
+
166
178
  ### Trivial (for 1-2 point fixes)
167
179
 
168
180
  ```
@@ -196,6 +208,17 @@ SM → UX-Designer → TEA → Dev → Reviewer → SM
196
208
  - For UI components and user-facing features
197
209
  - **Triggers:** `types: [ui, ux, behavior]`, `tags: [bdd, ux-first]`
198
210
 
211
+ ### BDD-Tandem (BDD with full tandem chain)
212
+
213
+ ```
214
+ setup → design (+Architect) → red → green (+UX-Designer) → review (+PM) → finish
215
+ SM → UX-Designer (+Architect) → TEA → Dev (+UX-Designer) → Reviewer (+PM) → SM
216
+ ```
217
+
218
+ - Full tandem chain: Architect watches design, UX-Designer watches implementation, PM watches review
219
+ - Everyone shares their cake — cross-role visibility at every phase
220
+ - **Triggers:** `tags: [bdd-tandem, tandem, ux-first]`, `points.min: 3`
221
+
199
222
  ### Architecture (stepped workflow)
200
223
 
201
224
  ```
@@ -341,5 +364,5 @@ workflow:
341
364
  For comprehensive documentation on creating stepped workflows, see:
342
365
 
343
366
  - **[guides/bikelane.md](../../guides/bikelane.md)** - Full BikeLane user guide
344
- - **[docs/adr/0013-bmad-workflow-import.md](../../docs/adr/0013-bmad-workflow-import.md)** - Technical ADR
367
+ - **ADR-0013: Stepped Workflow Support** - Technical ADR (see `docs/adr/0013-bmad-workflow-import.md` in framework repo)
345
368
  - **[pennyfarthing-dist/workflows/architecture.yaml](../../workflows/architecture.yaml)** - Example implementation
@@ -0,0 +1,65 @@
1
+ # Architecture Workflow - Migrated from BMAD format
2
+ # Collaborative architectural decision-making with progressive disclosure
3
+ #
4
+ # Flow: Initialize → [Continue?] → Context → Patterns → Components → Interfaces → Risks → Document
5
+ # Use for architectural decisions, system design, and technical planning
6
+ #
7
+ # BMAD Enhancements:
8
+ # - Continuation handler (step-01b) for resuming workflows
9
+ # - A/P/C menus: Advanced Elicitation, Party Mode, Continue
10
+ # - Web search integration for current technology versions
11
+ # - AI agent consistency focus in component and risk steps
12
+ # - Pre-documentation validation checks
13
+
14
+ workflow:
15
+ name: architecture
16
+ description: Collaborative architectural decision facilitation for AI-agent consistency. Produces a decision-focused architecture document optimized for preventing agent conflicts through step-by-step discovery with A/P/C collaboration menus.
17
+ version: "2.0.0"
18
+ type: stepped
19
+
20
+ # Step configuration
21
+ steps:
22
+ path: ./steps/
23
+ pattern: step-{nn}-*.md
24
+ # Note: step-01b-continue.md is loaded conditionally by step-01 when existing workflow detected
25
+
26
+ # Variables available in step files
27
+ variables:
28
+ project_root: .
29
+ planning_artifacts: ./artifacts
30
+ output_file: artifacts/architecture.md
31
+ input_required:
32
+ - prd
33
+
34
+ # User approval gates - pause for confirmation at key decision points
35
+ # Steps with A/P/C menus naturally pause; these are the formal gates
36
+ gates:
37
+ after_steps: [2, 4, 6]
38
+ gate_marker: "<!-- GATE -->"
39
+
40
+ # Collaboration menus available in steps
41
+ collaboration:
42
+ menus:
43
+ - key: A
44
+ name: Advanced Elicitation
45
+ description: Use discovery protocols to develop deeper insights
46
+ - key: P
47
+ name: Party Mode
48
+ description: Bring multiple perspectives to analyze from different angles
49
+ - key: C
50
+ name: Continue
51
+ description: Save content and proceed to next step
52
+ - key: R
53
+ name: Revise
54
+ description: Need to gather more information or make changes
55
+
56
+ # Output template
57
+ template: ./templates/architecture-decision.md
58
+
59
+ # Agent assignment
60
+ agent: architect
61
+
62
+ # Triggers - when to suggest this workflow
63
+ triggers:
64
+ types: [architecture, design, adr, system-design]
65
+ tags: [architecture, stepped]
@@ -0,0 +1,70 @@
1
+ # BDD Tandem Workflow - BDD with Full Tandem Chain
2
+ # Extends standard BDD with tandem observers:
3
+ # - Architect backseats UX-Designer during design
4
+ # - UX-Designer backseats Dev during implementation
5
+ # - PM backseats Reviewer during review
6
+ #
7
+ # Flow: SM → UX-Designer (+Architect) → TEA → Dev (+UX-Designer) → Reviewer (+PM) → SM
8
+
9
+ workflow:
10
+ name: bdd-tandem
11
+ description: BDD with full tandem chain across design, implementation, and review
12
+ version: "1.0.0"
13
+
14
+ phases:
15
+ - name: setup
16
+ agent: sm
17
+ output: [session_file, branches, story_context]
18
+
19
+ - name: design
20
+ agent: ux-designer
21
+ input: [session_file, story_context]
22
+ output: [design_spec, user_flows, wireframes, behavior_scenarios]
23
+ gate:
24
+ type: design_review
25
+ condition: UX spec defines user flows and acceptance behaviors
26
+ tandem:
27
+ partner: architect
28
+ scope: file-watch
29
+
30
+ - name: red
31
+ agent: tea
32
+ input: [design_spec, behavior_scenarios, story_context]
33
+ output: [failing_tests]
34
+ gate:
35
+ type: tests_fail
36
+ condition: Behavior scenarios have test coverage
37
+
38
+ - name: green
39
+ agent: dev
40
+ input: [failing_tests, design_spec, story_context]
41
+ output: [implementation, passing_tests]
42
+ gate:
43
+ type: tests_pass
44
+ condition: All tests passing, UX spec implemented
45
+ tandem:
46
+ partner: ux-designer
47
+ scope: file-watch
48
+
49
+ - name: review
50
+ agent: reviewer
51
+ input: [implementation, passing_tests, design_spec]
52
+ output: [approval]
53
+ gate:
54
+ type: approval
55
+ condition: Code review approved, UX requirements met
56
+ tandem:
57
+ partner: pm
58
+ scope: file-watch
59
+
60
+ - name: finish
61
+ agent: sm
62
+ input: [approval]
63
+ output: [archived_session, story_summary]
64
+
65
+ triggers:
66
+ types: [ui, ux, behavior, component, settings]
67
+ tags: [bdd-tandem, tandem, ux-first]
68
+ points:
69
+ min: 3
70
+ default: false
@@ -0,0 +1,61 @@
1
+ # TDD Tandem Workflow - TDD with Full Tandem Chain
2
+ # Extends standard TDD with tandem observers on every phase:
3
+ # - Architect backseats TEA during red phase
4
+ # - TEA backseats Dev during green phase
5
+ # - PM backseats Reviewer during review phase
6
+ #
7
+ # Flow: SM → TEA (+Architect) → Dev (+TEA) → Reviewer (+PM) → SM
8
+
9
+ workflow:
10
+ name: tdd-tandem
11
+ description: TDD with full tandem chain across all phases
12
+ version: "2.0.0"
13
+
14
+ phases:
15
+ - name: setup
16
+ agent: sm
17
+ output: [session_file, branches, story_context]
18
+
19
+ - name: red
20
+ agent: tea
21
+ input: [session_file, story_context]
22
+ output: [failing_tests]
23
+ gate:
24
+ type: tests_fail
25
+ condition: All acceptance criteria have test coverage
26
+ tandem:
27
+ partner: architect
28
+ scope: file-watch
29
+
30
+ - name: green
31
+ agent: dev
32
+ input: [failing_tests, story_context]
33
+ output: [implementation, passing_tests]
34
+ gate:
35
+ type: tests_pass
36
+ condition: All tests passing, no skipped tests
37
+ tandem:
38
+ partner: tea
39
+ scope: file-watch
40
+
41
+ - name: review
42
+ agent: reviewer
43
+ input: [implementation, passing_tests]
44
+ output: [approval]
45
+ gate:
46
+ type: approval
47
+ condition: Code review approved, no blocking issues
48
+ tandem:
49
+ partner: pm
50
+ scope: file-watch
51
+
52
+ - name: finish
53
+ agent: sm
54
+ input: [approval]
55
+ output: [archived_session, story_summary]
56
+
57
+ triggers:
58
+ tags: [tandem]
59
+ points:
60
+ min: 3
61
+ default: false
@@ -1,30 +1,23 @@
1
1
  #!/usr/bin/env python3
2
2
  """
3
- Bell Mode PostToolUse Hook (Python)
3
+ PostToolUse Hook — Bell Mode + Tandem Injection (Python)
4
4
 
5
- This hook is called by Claude Code after each tool execution.
6
- When bell mode is enabled and there are queued messages, it returns
7
- the first queued message as additionalContext to be injected into
8
- Claude's next API call.
5
+ Called by Claude Code after each tool execution. Handles two independent
6
+ injection systems:
9
7
 
10
- Configuration files:
11
- .pennyfarthing/config.local.yaml - workflow.bell_mode: true/false
12
- .pennyfarthing/bell-queue.json - [{"text": "...", "images": [...]}, ...]
8
+ 1. Bell queue (Cyclist only) — injects queued user messages when Cyclist
9
+ is running and bell_mode is enabled. In CLI sessions this is a no-op.
10
+ 2. Tandem observations (always active) injects backseat agent observations
11
+ when tandem observation files exist. No configuration required.
13
12
 
14
- Output format (when injecting):
15
- {
16
- "hookSpecificOutput": {
17
- "hookEventName": "PostToolUse",
18
- "additionalContext": "User feedback: <message>"
19
- }
20
- }
21
-
22
- Output when disabled or queue empty: (nothing - exit 0)
13
+ Bell queue takes precedence: if a queued message exists, tandem is
14
+ deferred to the next hook invocation.
23
15
 
24
16
  Story: MSSCI-12409 - Hook consistency and WheelHub consolidation
25
17
  """
26
18
 
27
19
  import json
20
+ import re
28
21
  import sys
29
22
  from pathlib import Path
30
23
 
@@ -32,12 +25,13 @@ from pathlib import Path
32
25
  sys.path.insert(0, str(Path(__file__).parent))
33
26
 
34
27
  from hooks import (
35
- find_project_root,
36
- get_cyclist_port,
37
- send_to_cyclist,
38
- output_hook_response,
28
+ CYCLIST_PORT_FILE,
39
29
  HookResponse,
30
+ find_project_root,
40
31
  is_bell_mode_enabled,
32
+ output_hook_response,
33
+ read_port_file,
34
+ send_to_cyclist,
41
35
  )
42
36
 
43
37
 
@@ -106,8 +100,170 @@ def notify_cyclist(project_root: Path, message_text: str) -> None:
106
100
  pass
107
101
 
108
102
 
103
+ # =============================================================================
104
+ # Tandem Observation Injection (Story 95-7 / MSSCI-14672)
105
+ # =============================================================================
106
+ # Stubs for tandem observation injection. Dev will implement.
107
+
108
+
109
+ def read_tandem_observations(project_root: Path) -> list[Path]:
110
+ """Find tandem observation files in .session/ directory.
111
+
112
+ Looks for files matching .session/*-tandem-*.md pattern.
113
+
114
+ Args:
115
+ project_root: Project root directory
116
+
117
+ Returns:
118
+ List of Path objects for tandem observation files
119
+ """
120
+ session_dir = project_root / ".session"
121
+ if not session_dir.is_dir():
122
+ return []
123
+ return sorted(session_dir.glob("*-tandem-*.md"))
124
+
125
+
126
+ def get_latest_observation(file_content: str) -> dict | None:
127
+ """Extract the latest observation entry from a tandem observation file.
128
+
129
+ Parses the markdown format to extract the most recent ## [HH:MM] Observation
130
+ block, including the observer persona name from the file header.
131
+
132
+ Args:
133
+ file_content: Full text content of the tandem observation file
134
+
135
+ Returns:
136
+ Dict with 'persona' and 'text' keys, or None if no observations found
137
+ """
138
+ # Extract persona from header: **Observer:** agent (Persona Name)
139
+ persona_match = re.search(r"\*\*Observer:\*\*\s*\w+\s*\(([^)]+)\)", file_content)
140
+ persona = persona_match.group(1) if persona_match else "Unknown"
141
+
142
+ # Split on observation headers: ## [HH:MM] Observation
143
+ entries = re.split(r"## \[\d{1,2}:\d{2}\] Observation\n", file_content)
144
+ if len(entries) < 2:
145
+ return None
146
+
147
+ # Last entry is the most recent observation
148
+ last_entry = entries[-1].strip()
149
+ # Remove trailing --- separator
150
+ last_entry = re.sub(r"\n---\s*$", "", last_entry).strip()
151
+ # Remove the **Trigger:** line
152
+ lines = last_entry.split("\n")
153
+ text_lines = [l for l in lines if not l.startswith("**Trigger:**")]
154
+ text = "\n".join(text_lines).strip()
155
+
156
+ if not text:
157
+ return None
158
+
159
+ return {"persona": persona, "text": text}
160
+
161
+
162
+ def format_tandem_message(persona_name: str, observation_text: str) -> str:
163
+ """Format a tandem observation as a bell mode injection message.
164
+
165
+ Args:
166
+ persona_name: The backseat agent's persona name
167
+ observation_text: The observation summary text
168
+
169
+ Returns:
170
+ Formatted string: [Tandem] {persona_name}: {observation_text}
171
+ """
172
+ return f"[Tandem] {persona_name}: {observation_text}"
173
+
174
+
175
+ def get_tandem_mtime(project_root: Path, agent: str) -> float:
176
+ """Read the last-checked mtime for a tandem agent's observation file.
177
+
178
+ Args:
179
+ project_root: Project root directory
180
+ agent: Agent name (e.g. 'reviewer', 'tea')
181
+
182
+ Returns:
183
+ Last-checked mtime as float, or 0.0 if no sidecar exists
184
+ """
185
+ sidecar = project_root / ".session" / f".tandem-mtime-{agent}"
186
+ if not sidecar.exists():
187
+ return 0.0
188
+ try:
189
+ return float(sidecar.read_text().strip())
190
+ except (ValueError, OSError):
191
+ return 0.0
192
+
193
+
194
+ def save_tandem_mtime(project_root: Path, agent: str, mtime: float) -> None:
195
+ """Save the mtime for a tandem agent's observation file.
196
+
197
+ Writes to .session/.tandem-mtime-{agent} sidecar file.
198
+
199
+ Args:
200
+ project_root: Project root directory
201
+ agent: Agent name (e.g. 'reviewer', 'tea')
202
+ mtime: The mtime value to save
203
+ """
204
+ sidecar = project_root / ".session" / f".tandem-mtime-{agent}"
205
+ try:
206
+ sidecar.write_text(str(mtime))
207
+ except OSError:
208
+ pass
209
+
210
+
211
+ def check_tandem_files(project_root: Path) -> list[dict]:
212
+ """Check for new tandem observations and return injection messages.
213
+
214
+ Main entry point for tandem injection in the PostToolUse hook.
215
+ Always active — no configuration required. The presence of tandem
216
+ observation files in .session/ is the only signal needed.
217
+
218
+ Args:
219
+ project_root: Project root directory
220
+
221
+ Returns:
222
+ List of dicts with 'agent', 'message' keys for each new observation
223
+ """
224
+ tandem_files = read_tandem_observations(project_root)
225
+ if not tandem_files:
226
+ return []
227
+
228
+ results = []
229
+ for obs_file in tandem_files:
230
+ # Extract agent name from filename: *-tandem-{agent}.md
231
+ agent_match = re.search(r"-tandem-(\w+)\.md$", obs_file.name)
232
+ if not agent_match:
233
+ continue
234
+ agent = agent_match.group(1)
235
+
236
+ # Compare mtime
237
+ try:
238
+ file_mtime = obs_file.stat().st_mtime
239
+ except OSError:
240
+ continue
241
+ saved_mtime = get_tandem_mtime(project_root, agent)
242
+ if file_mtime == saved_mtime:
243
+ continue
244
+
245
+ # Read and parse
246
+ try:
247
+ content = obs_file.read_text()
248
+ except OSError:
249
+ continue
250
+ obs = get_latest_observation(content)
251
+ if not obs:
252
+ # Update mtime even for unparseable files to avoid re-checking
253
+ save_tandem_mtime(project_root, agent, file_mtime)
254
+ continue
255
+
256
+ message = format_tandem_message(obs["persona"], obs["text"])
257
+ results.append({"agent": agent, "message": message})
258
+
259
+ # Update mtime sidecar
260
+ save_tandem_mtime(project_root, agent, file_mtime)
261
+
262
+ return results
263
+
264
+
109
265
  def main() -> None:
110
- """Main entry point for PostToolUse bell mode hook."""
266
+ """Main entry point for PostToolUse hook."""
111
267
  try:
112
268
  # Read and discard stdin (required by hook protocol)
113
269
  sys.stdin.read()
@@ -117,35 +273,34 @@ def main() -> None:
117
273
  if not project_root:
118
274
  sys.exit(0)
119
275
 
120
- # Check if bell mode is enabled
121
- if not is_bell_mode_enabled(project_root):
122
- sys.exit(0)
123
-
124
- # Read queue
125
- queue = read_bell_queue(project_root)
126
- if not queue:
127
- sys.exit(0)
128
-
129
- # Get first message
130
- first_message = queue[0]
131
- message_text = first_message.get("text", "")
132
- if not message_text:
133
- sys.exit(0)
134
-
135
- # Output hook response with additionalContext
136
- output_hook_response(HookResponse(
137
- event_name="PostToolUse",
138
- additional_context=f"User feedback: {message_text}",
139
- ))
140
-
141
- # Dequeue message and notify Cyclist (in background-ish - after output)
142
- dequeue_message(project_root)
143
- notify_cyclist(project_root, message_text)
276
+ # --- Bell queue (Cyclist only, requires bell_mode: true) ---
277
+ # Takes precedence over tandem when active.
278
+ is_cyclist = read_port_file(CYCLIST_PORT_FILE, project_root) is not None
279
+ if is_cyclist and is_bell_mode_enabled(project_root):
280
+ queue = read_bell_queue(project_root)
281
+ if queue:
282
+ first_message = queue[0]
283
+ message_text = first_message.get("text", "")
284
+ if message_text:
285
+ output_hook_response(HookResponse(
286
+ event_name="PostToolUse",
287
+ additional_context=f"User feedback: {message_text}",
288
+ ))
289
+ dequeue_message(project_root)
290
+ notify_cyclist(project_root, message_text)
291
+ sys.exit(0)
292
+
293
+ # --- Tandem observations (always active, no config required) ---
294
+ tandem_results = check_tandem_files(project_root)
295
+ if tandem_results:
296
+ output_hook_response(HookResponse(
297
+ event_name="PostToolUse",
298
+ additional_context=tandem_results[0]["message"],
299
+ ))
144
300
 
145
301
  sys.exit(0)
146
302
 
147
303
  except Exception as e:
148
- # On error, exit silently
149
304
  print(f"[bellmode-hook] Error: {e}", file=sys.stderr)
150
305
  sys.exit(0)
151
306
 
@@ -6,17 +6,17 @@ Analyzes existing codebases and generates AI-ready documentation.
6
6
 
7
7
  from pennyfarthing_scripts.brownfield.discover import (
8
8
  DepthLevel,
9
- ProjectType,
10
9
  DiscoveryResult,
10
+ ProjectType,
11
+ detect_architecture_patterns,
11
12
  detect_project_type,
12
13
  detect_tech_stack,
13
- scan_directory_structure,
14
- detect_architecture_patterns,
14
+ discover,
15
+ generate_ai_guidance_doc,
15
16
  generate_project_overview,
16
- generate_tech_stack_doc,
17
17
  generate_source_tree_doc,
18
- generate_ai_guidance_doc,
19
- discover,
18
+ generate_tech_stack_doc,
19
+ scan_directory_structure,
20
20
  )
21
21
 
22
22
  __all__ = [
@@ -1,6 +1,7 @@
1
1
  """Entry point for python -m pennyfarthing_scripts.brownfield."""
2
2
 
3
3
  import sys
4
+
4
5
  from pennyfarthing_scripts.brownfield.cli import cli
5
6
 
6
7
  if __name__ == "__main__":
@@ -12,7 +12,6 @@ import argparse
12
12
  import asyncio
13
13
  import sys
14
14
  from pathlib import Path
15
- from typing import Any
16
15
 
17
16
  from pennyfarthing_scripts.brownfield.discover import (
18
17
  DepthLevel,