@pennyfarthing/core 10.2.0 → 10.3.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 (268) hide show
  1. package/README.md +11 -8
  2. package/package.json +1 -1
  3. package/packages/core/dist/cli/commands/e2e-fresh-install.test.js +1 -1
  4. package/packages/core/dist/cli/commands/e2e-fresh-install.test.js.map +1 -1
  5. package/packages/core/dist/cli/commands/e2e-upgrade.test.js +1 -1
  6. package/packages/core/dist/cli/commands/e2e-upgrade.test.js.map +1 -1
  7. package/packages/core/dist/cli/commands/theme.js +1 -1
  8. package/packages/core/dist/cli/commands/theme.js.map +1 -1
  9. package/packages/core/dist/cli/utils/themes.d.ts.map +1 -1
  10. package/packages/core/dist/cli/utils/themes.js +3 -2
  11. package/packages/core/dist/cli/utils/themes.js.map +1 -1
  12. package/packages/core/dist/scripts/add-ocean-profiles.js +1 -1
  13. package/packages/core/dist/scripts/add-ocean-profiles.js.map +1 -1
  14. package/packages/core/dist/scripts/generate-all-spiders.js +2 -0
  15. package/packages/core/dist/scripts/generate-all-spiders.js.map +1 -1
  16. package/packages/core/dist/scripts/generate-report.d.ts.map +1 -1
  17. package/packages/core/dist/scripts/generate-report.js +2 -0
  18. package/packages/core/dist/scripts/generate-report.js.map +1 -1
  19. package/packages/core/dist/scripts/generate-spider.d.ts.map +1 -1
  20. package/packages/core/dist/scripts/generate-spider.js +2 -0
  21. package/packages/core/dist/scripts/generate-spider.js.map +1 -1
  22. package/packages/core/dist/scripts/validate-ocean-profiles.js +1 -1
  23. package/packages/core/dist/scripts/validate-ocean-profiles.js.map +1 -1
  24. package/packages/core/dist/workflow/file-watch.test.js.map +1 -1
  25. package/packages/core/dist/workflow/output-path-normalizer.d.ts +47 -0
  26. package/packages/core/dist/workflow/output-path-normalizer.d.ts.map +1 -0
  27. package/packages/core/dist/workflow/output-path-normalizer.js +79 -0
  28. package/packages/core/dist/workflow/output-path-normalizer.js.map +1 -0
  29. package/packages/core/dist/workflow/output-path-normalizer.test.d.ts +16 -0
  30. package/packages/core/dist/workflow/output-path-normalizer.test.d.ts.map +1 -0
  31. package/packages/core/dist/workflow/output-path-normalizer.test.js +157 -0
  32. package/packages/core/dist/workflow/output-path-normalizer.test.js.map +1 -0
  33. package/packages/core/dist/workflow/tool-watch.test.js +1 -2
  34. package/packages/core/dist/workflow/tool-watch.test.js.map +1 -1
  35. package/packages/core/dist/workflow/variable-resolver.js +1 -1
  36. package/packages/core/dist/workflow/variable-resolver.js.map +1 -1
  37. package/pennyfarthing-dist/agents/README.md +3 -1
  38. package/pennyfarthing-dist/agents/ba.md +165 -0
  39. package/pennyfarthing-dist/commands/ba.md +17 -0
  40. package/pennyfarthing-dist/guides/workflow-schema.md +1 -1
  41. package/pennyfarthing-dist/personas/themes/a-team.yaml +30 -0
  42. package/pennyfarthing-dist/personas/themes/alice-in-wonderland.yaml +30 -0
  43. package/pennyfarthing-dist/personas/themes/battlestar-galactica.yaml +30 -0
  44. package/pennyfarthing-dist/personas/themes/blade-runner.yaml +30 -0
  45. package/pennyfarthing-dist/personas/themes/catch-22.yaml +30 -0
  46. package/pennyfarthing-dist/personas/themes/control.yaml +30 -0
  47. package/pennyfarthing-dist/personas/themes/cowboy-bebop.yaml +31 -0
  48. package/pennyfarthing-dist/personas/themes/discworld.yaml +31 -0
  49. package/pennyfarthing-dist/personas/themes/doctor-who.yaml +31 -0
  50. package/pennyfarthing-dist/personas/themes/dune.yaml +32 -0
  51. package/pennyfarthing-dist/personas/themes/fifth-element.yaml +32 -0
  52. package/pennyfarthing-dist/personas/themes/firefly.yaml +31 -0
  53. package/pennyfarthing-dist/personas/themes/game-of-thrones.yaml +30 -0
  54. package/pennyfarthing-dist/personas/themes/harry-potter.yaml +30 -0
  55. package/pennyfarthing-dist/personas/themes/hitchhikers-guide.yaml +30 -0
  56. package/pennyfarthing-dist/personas/themes/lord-of-the-rings.yaml +30 -0
  57. package/pennyfarthing-dist/personas/themes/mad-max.yaml +30 -0
  58. package/pennyfarthing-dist/personas/themes/mash.yaml +33 -0
  59. package/pennyfarthing-dist/personas/themes/princess-bride.yaml +34 -0
  60. package/pennyfarthing-dist/personas/themes/sandman.yaml +33 -0
  61. package/pennyfarthing-dist/personas/themes/star-trek-tng.yaml +34 -0
  62. package/pennyfarthing-dist/personas/themes/star-wars.yaml +33 -0
  63. package/pennyfarthing-dist/personas/themes/the-expanse.yaml +30 -0
  64. package/pennyfarthing-dist/personas/themes/the-matrix.yaml +30 -0
  65. package/pennyfarthing-dist/personas/themes/watchmen.yaml +30 -0
  66. package/pennyfarthing-dist/personas/themes/west-wing.yaml +30 -0
  67. package/pennyfarthing-dist/personas/themes/x-files.yaml +30 -0
  68. package/pennyfarthing-dist/scripts/core/agent-session.sh +1 -1
  69. package/pennyfarthing-dist/scripts/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
  70. package/pennyfarthing-dist/scripts/portraits/generate-portraits.py +2 -2
  71. package/pennyfarthing-dist/scripts/validation/validate-agent-schema.sh +1 -0
  72. package/pennyfarthing-dist/skills/theme/skill.md +1 -1
  73. package/pennyfarthing-dist/workflows/architecture/workflow.yaml +2 -2
  74. package/pennyfarthing-dist/workflows/architecture.yaml +2 -2
  75. package/pennyfarthing-dist/workflows/epics-and-stories/workflow.yaml +2 -2
  76. package/pennyfarthing-dist/workflows/implementation-readiness/workflow.yaml +2 -2
  77. package/pennyfarthing-dist/workflows/prd/workflow.yaml +2 -2
  78. package/pennyfarthing-dist/workflows/product-brief/workflow.yaml +2 -2
  79. package/pennyfarthing-dist/workflows/project-context/workflow.yaml +2 -2
  80. package/pennyfarthing-dist/workflows/quick-dev/workflow.yaml +2 -2
  81. package/pennyfarthing-dist/workflows/research/workflow.yaml +2 -2
  82. package/pennyfarthing-dist/workflows/retrospective/workflow.yaml +1 -1
  83. package/pennyfarthing-dist/workflows/sprint-planning/workflow.yaml +3 -3
  84. package/pennyfarthing-dist/workflows/ux-design/workflow.yaml +2 -2
  85. package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  86. package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  87. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  88. package/pennyfarthing_scripts/__pycache__/config.cpython-314.pyc +0 -0
  89. package/pennyfarthing_scripts/__pycache__/hooks.cpython-314.pyc +0 -0
  90. package/pennyfarthing_scripts/__pycache__/jira.cpython-314.pyc +0 -0
  91. package/pennyfarthing_scripts/__pycache__/jira_bidirectional_sync.cpython-314.pyc +0 -0
  92. package/pennyfarthing_scripts/__pycache__/jira_epic_creation.cpython-314.pyc +0 -0
  93. package/pennyfarthing_scripts/__pycache__/jira_sync.cpython-314.pyc +0 -0
  94. package/pennyfarthing_scripts/__pycache__/jira_sync_story.cpython-314.pyc +0 -0
  95. package/pennyfarthing_scripts/__pycache__/output.cpython-314.pyc +0 -0
  96. package/pennyfarthing_scripts/__pycache__/patch_mode.cpython-314.pyc +0 -0
  97. package/pennyfarthing_scripts/__pycache__/pretooluse_hook.cpython-314.pyc +0 -0
  98. package/pennyfarthing_scripts/__pycache__/schema_validation_hook.cpython-314.pyc +0 -0
  99. package/pennyfarthing_scripts/__pycache__/sprint.cpython-314.pyc +0 -0
  100. package/pennyfarthing_scripts/__pycache__/workflow.cpython-311.pyc +0 -0
  101. package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
  102. package/pennyfarthing_scripts/bellmode_hook.py +1 -1
  103. package/pennyfarthing_scripts/bikerack/__init__.py +36 -0
  104. package/pennyfarthing_scripts/bikerack/__main__.py +5 -0
  105. package/pennyfarthing_scripts/bikerack/__pycache__/__init__.cpython-314.pyc +0 -0
  106. package/pennyfarthing_scripts/bikerack/__pycache__/__main__.cpython-314.pyc +0 -0
  107. package/pennyfarthing_scripts/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
  108. package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
  109. package/pennyfarthing_scripts/bikerack/cli.py +148 -0
  110. package/pennyfarthing_scripts/bikerack/launcher.py +181 -0
  111. package/pennyfarthing_scripts/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
  112. package/pennyfarthing_scripts/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
  113. package/pennyfarthing_scripts/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
  114. package/pennyfarthing_scripts/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
  115. package/pennyfarthing_scripts/cli.py +5 -0
  116. package/pennyfarthing_scripts/codemarkers/__pycache__/__init__.cpython-314.pyc +0 -0
  117. package/pennyfarthing_scripts/codemarkers/__pycache__/__main__.cpython-314.pyc +0 -0
  118. package/pennyfarthing_scripts/codemarkers/__pycache__/analyze.cpython-314.pyc +0 -0
  119. package/pennyfarthing_scripts/codemarkers/__pycache__/cli.cpython-314.pyc +0 -0
  120. package/pennyfarthing_scripts/codemarkers/__pycache__/formatters.cpython-314.pyc +0 -0
  121. package/pennyfarthing_scripts/codemarkers/__pycache__/models.cpython-314.pyc +0 -0
  122. package/pennyfarthing_scripts/common/__pycache__/__init__.cpython-314.pyc +0 -0
  123. package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
  124. package/pennyfarthing_scripts/common/__pycache__/output.cpython-314.pyc +0 -0
  125. package/pennyfarthing_scripts/common/__pycache__/themes.cpython-314.pyc +0 -0
  126. package/pennyfarthing_scripts/complexity/__pycache__/__init__.cpython-314.pyc +0 -0
  127. package/pennyfarthing_scripts/complexity/__pycache__/__main__.cpython-314.pyc +0 -0
  128. package/pennyfarthing_scripts/complexity/__pycache__/analyze.cpython-314.pyc +0 -0
  129. package/pennyfarthing_scripts/complexity/__pycache__/cli.cpython-314.pyc +0 -0
  130. package/pennyfarthing_scripts/complexity/__pycache__/formatters.cpython-314.pyc +0 -0
  131. package/pennyfarthing_scripts/complexity/__pycache__/models.cpython-314.pyc +0 -0
  132. package/pennyfarthing_scripts/deadcode/__pycache__/__init__.cpython-314.pyc +0 -0
  133. package/pennyfarthing_scripts/deadcode/__pycache__/__main__.cpython-314.pyc +0 -0
  134. package/pennyfarthing_scripts/deadcode/__pycache__/analyze.cpython-314.pyc +0 -0
  135. package/pennyfarthing_scripts/deadcode/__pycache__/cli.cpython-314.pyc +0 -0
  136. package/pennyfarthing_scripts/deadcode/__pycache__/formatters.cpython-314.pyc +0 -0
  137. package/pennyfarthing_scripts/deadcode/__pycache__/models.cpython-314.pyc +0 -0
  138. package/pennyfarthing_scripts/dependencies/__pycache__/__init__.cpython-314.pyc +0 -0
  139. package/pennyfarthing_scripts/dependencies/__pycache__/__main__.cpython-314.pyc +0 -0
  140. package/pennyfarthing_scripts/dependencies/__pycache__/analyze.cpython-314.pyc +0 -0
  141. package/pennyfarthing_scripts/dependencies/__pycache__/cli.cpython-314.pyc +0 -0
  142. package/pennyfarthing_scripts/dependencies/__pycache__/formatters.cpython-314.pyc +0 -0
  143. package/pennyfarthing_scripts/dependencies/__pycache__/models.cpython-314.pyc +0 -0
  144. package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
  145. package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
  146. package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
  147. package/pennyfarthing_scripts/healthscore/__pycache__/__init__.cpython-314.pyc +0 -0
  148. package/pennyfarthing_scripts/healthscore/__pycache__/__main__.cpython-314.pyc +0 -0
  149. package/pennyfarthing_scripts/healthscore/__pycache__/analyze.cpython-314.pyc +0 -0
  150. package/pennyfarthing_scripts/healthscore/__pycache__/cli.cpython-314.pyc +0 -0
  151. package/pennyfarthing_scripts/healthscore/__pycache__/formatters.cpython-314.pyc +0 -0
  152. package/pennyfarthing_scripts/healthscore/__pycache__/models.cpython-314.pyc +0 -0
  153. package/pennyfarthing_scripts/healthscore/analyze.py +2 -1
  154. package/pennyfarthing_scripts/hotspots/__pycache__/__init__.cpython-314.pyc +0 -0
  155. package/pennyfarthing_scripts/hotspots/__pycache__/__main__.cpython-314.pyc +0 -0
  156. package/pennyfarthing_scripts/hotspots/__pycache__/analyze.cpython-314.pyc +0 -0
  157. package/pennyfarthing_scripts/hotspots/__pycache__/cli.cpython-314.pyc +0 -0
  158. package/pennyfarthing_scripts/hotspots/__pycache__/formatters.cpython-314.pyc +0 -0
  159. package/pennyfarthing_scripts/hotspots/__pycache__/models.cpython-314.pyc +0 -0
  160. package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-314.pyc +0 -0
  161. package/pennyfarthing_scripts/jira/__pycache__/__main__.cpython-314.pyc +0 -0
  162. package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
  163. package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
  164. package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
  165. package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
  166. package/pennyfarthing_scripts/jira/__pycache__/compat.cpython-314.pyc +0 -0
  167. package/pennyfarthing_scripts/jira/__pycache__/create.cpython-314.pyc +0 -0
  168. package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-314.pyc +0 -0
  169. package/pennyfarthing_scripts/jira/__pycache__/mappings.cpython-314.pyc +0 -0
  170. package/pennyfarthing_scripts/jira/__pycache__/models.cpython-314.pyc +0 -0
  171. package/pennyfarthing_scripts/jira/__pycache__/operations.cpython-314.pyc +0 -0
  172. package/pennyfarthing_scripts/jira/__pycache__/reconcile.cpython-314.pyc +0 -0
  173. package/pennyfarthing_scripts/jira/__pycache__/story.cpython-314.pyc +0 -0
  174. package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-314.pyc +0 -0
  175. package/pennyfarthing_scripts/migration/__pycache__/__init__.cpython-314.pyc +0 -0
  176. package/pennyfarthing_scripts/migration/__pycache__/__main__.cpython-314.pyc +0 -0
  177. package/pennyfarthing_scripts/migration/__pycache__/cli.cpython-314.pyc +0 -0
  178. package/pennyfarthing_scripts/migration/__pycache__/session.cpython-314.pyc +0 -0
  179. package/pennyfarthing_scripts/migration/__pycache__/skill.cpython-314.pyc +0 -0
  180. package/pennyfarthing_scripts/migration/__pycache__/step.cpython-314.pyc +0 -0
  181. package/pennyfarthing_scripts/migration/__pycache__/validate.cpython-314.pyc +0 -0
  182. package/pennyfarthing_scripts/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
  183. package/pennyfarthing_scripts/preflight/__pycache__/__main__.cpython-314.pyc +0 -0
  184. package/pennyfarthing_scripts/preflight/__pycache__/cli.cpython-314.pyc +0 -0
  185. package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
  186. package/pennyfarthing_scripts/prime/__init__.py +2 -0
  187. package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
  188. package/pennyfarthing_scripts/prime/__pycache__/__main__.cpython-314.pyc +0 -0
  189. package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
  190. package/pennyfarthing_scripts/prime/__pycache__/loader.cpython-314.pyc +0 -0
  191. package/pennyfarthing_scripts/prime/__pycache__/models.cpython-314.pyc +0 -0
  192. package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
  193. package/pennyfarthing_scripts/prime/__pycache__/session.cpython-314.pyc +0 -0
  194. package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
  195. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  196. package/pennyfarthing_scripts/prime/cli.py +13 -0
  197. package/pennyfarthing_scripts/prime/loader.py +70 -0
  198. package/pennyfarthing_scripts/prime/persona.py +2 -1
  199. package/pennyfarthing_scripts/prime/tiers.py +13 -0
  200. package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
  201. package/pennyfarthing_scripts/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
  202. package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
  203. package/pennyfarthing_scripts/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
  204. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  205. package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
  206. package/pennyfarthing_scripts/sprint/__pycache__/import_epic.cpython-314.pyc +0 -0
  207. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  208. package/pennyfarthing_scripts/sprint/__pycache__/status.cpython-314.pyc +0 -0
  209. package/pennyfarthing_scripts/sprint/__pycache__/story_add.cpython-314.pyc +0 -0
  210. package/pennyfarthing_scripts/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
  211. package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
  212. package/pennyfarthing_scripts/sprint/__pycache__/validate_cmd.cpython-314.pyc +0 -0
  213. package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
  214. package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-314.pyc +0 -0
  215. package/pennyfarthing_scripts/sprint/__pycache__/yaml_io.cpython-314.pyc +0 -0
  216. package/pennyfarthing_scripts/story/__pycache__/__init__.cpython-314.pyc +0 -0
  217. package/pennyfarthing_scripts/story/__pycache__/__main__.cpython-314.pyc +0 -0
  218. package/pennyfarthing_scripts/story/__pycache__/cli.cpython-314.pyc +0 -0
  219. package/pennyfarthing_scripts/story/__pycache__/create.cpython-314.pyc +0 -0
  220. package/pennyfarthing_scripts/story/__pycache__/size.cpython-314.pyc +0 -0
  221. package/pennyfarthing_scripts/story/__pycache__/template.cpython-314.pyc +0 -0
  222. package/pennyfarthing_scripts/tests/__pycache__/__init__.cpython-314.pyc +0 -0
  223. package/pennyfarthing_scripts/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
  224. package/pennyfarthing_scripts/tests/__pycache__/test_bikerack.cpython-314-pytest-9.0.2.pyc +0 -0
  225. package/pennyfarthing_scripts/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
  226. package/pennyfarthing_scripts/tests/__pycache__/test_cli_modules.cpython-314-pytest-9.0.2.pyc +0 -0
  227. package/pennyfarthing_scripts/tests/__pycache__/test_codemarkers.cpython-314-pytest-9.0.2.pyc +0 -0
  228. package/pennyfarthing_scripts/tests/__pycache__/test_common.cpython-314-pytest-9.0.2.pyc +0 -0
  229. package/pennyfarthing_scripts/tests/__pycache__/test_epic_shard_validation.cpython-314-pytest-9.0.2.pyc +0 -0
  230. package/pennyfarthing_scripts/tests/__pycache__/test_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
  231. package/pennyfarthing_scripts/tests/__pycache__/test_healthscore.cpython-314-pytest-9.0.2.pyc +0 -0
  232. package/pennyfarthing_scripts/tests/__pycache__/test_jira_package.cpython-314-pytest-9.0.2.pyc +0 -0
  233. package/pennyfarthing_scripts/tests/__pycache__/test_package_structure.cpython-314-pytest-9.0.2.pyc +0 -0
  234. package/pennyfarthing_scripts/tests/__pycache__/test_patch_mode.cpython-314-pytest-9.0.2.pyc +0 -0
  235. package/pennyfarthing_scripts/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
  236. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc +0 -0
  237. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
  238. package/pennyfarthing_scripts/tests/__pycache__/test_story_add.cpython-314-pytest-9.0.2.pyc +0 -0
  239. package/pennyfarthing_scripts/tests/__pycache__/test_story_package.cpython-314-pytest-9.0.2.pyc +0 -0
  240. package/pennyfarthing_scripts/tests/__pycache__/test_story_update.cpython-314-pytest-9.0.2.pyc +0 -0
  241. package/pennyfarthing_scripts/tests/__pycache__/test_tiers.cpython-314-pytest-9.0.2.pyc +0 -0
  242. package/pennyfarthing_scripts/tests/__pycache__/test_token_counting.cpython-314-pytest-9.0.2.pyc +0 -0
  243. package/pennyfarthing_scripts/tests/__pycache__/test_validate_cmd.cpython-314-pytest-9.0.2.pyc +0 -0
  244. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
  245. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_cli.cpython-314-pytest-9.0.2.pyc +0 -0
  246. package/pennyfarthing_scripts/tests/__pycache__/test_yaml_io.cpython-314-pytest-9.0.2.pyc +0 -0
  247. package/pennyfarthing_scripts/tests/test_bikerack.py +785 -0
  248. package/pennyfarthing_scripts/tests/test_topology_loader.py +620 -0
  249. package/pennyfarthing_scripts/theme/__pycache__/__init__.cpython-314.pyc +0 -0
  250. package/pennyfarthing_scripts/theme/__pycache__/cli.cpython-314.pyc +0 -0
  251. package/pennyfarthing_scripts/validate/__pycache__/__init__.cpython-314.pyc +0 -0
  252. package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +0 -0
  253. package/pennyfarthing_scripts/validate/adapters/__pycache__/__init__.cpython-314.pyc +0 -0
  254. package/pennyfarthing_scripts/validate/adapters/__pycache__/agent.cpython-314.pyc +0 -0
  255. package/pennyfarthing_scripts/validate/adapters/__pycache__/schema.cpython-314.pyc +0 -0
  256. package/pennyfarthing_scripts/validate/adapters/__pycache__/skill_command.cpython-314.pyc +0 -0
  257. package/pennyfarthing_scripts/validate/adapters/__pycache__/sprint.cpython-314.pyc +0 -0
  258. package/pennyfarthing_scripts/validate/adapters/__pycache__/workflow.cpython-314.pyc +0 -0
  259. package/pennyfarthing_scripts/validate/adapters/skill_command.py +0 -1
  260. package/packages/core/dist/workflow/context-watch.d.ts +0 -80
  261. package/packages/core/dist/workflow/context-watch.d.ts.map +0 -1
  262. package/packages/core/dist/workflow/context-watch.js +0 -235
  263. package/packages/core/dist/workflow/context-watch.js.map +0 -1
  264. package/packages/core/dist/workflow/context-watch.test.d.ts +0 -1
  265. package/packages/core/dist/workflow/context-watch.test.d.ts.map +0 -1
  266. package/packages/core/dist/workflow/context-watch.test.js +0 -746
  267. package/packages/core/dist/workflow/context-watch.test.js.map +0 -1
  268. package/pennyfarthing_scripts/__pycache__/bellmode_hook.cpython-314.pyc +0 -0
@@ -0,0 +1,148 @@
1
+ """BikeRack CLI — Click-based CLI for bikerack operations.
2
+
3
+ Usage:
4
+ pf bikerack [COMMAND]
5
+
6
+ Commands:
7
+ start Start BikeRack mode (default)
8
+ stop Stop running BikeRack instance
9
+ status Show running state
10
+ """
11
+
12
+ import os
13
+ import sys
14
+ from pathlib import Path
15
+
16
+ import click
17
+
18
+
19
+ @click.group(invoke_without_command=True)
20
+ @click.pass_context
21
+ def bikerack(ctx):
22
+ """BikeRack Mode — Decoupled WheelHub dashboard launcher.
23
+
24
+ \b
25
+ Commands:
26
+ start - Start WheelHub + Claude CLI (default)
27
+ stop - Stop running BikeRack instance
28
+ status - Show running state (PID, port, uptime)
29
+ """
30
+ if ctx.invoked_subcommand is None:
31
+ ctx.invoke(start)
32
+
33
+
34
+ @bikerack.command()
35
+ @click.option(
36
+ "--project-dir",
37
+ type=click.Path(exists=True, file_okay=False, resolve_path=True),
38
+ default=None,
39
+ help="Project directory (where .pennyfarthing/ lives). Falls back to CYCLIST_PROJECT_DIR env var, then cwd.",
40
+ )
41
+ def start(project_dir):
42
+ """Start BikeRack mode.
43
+
44
+ Starts WheelHub in background, waits for readiness,
45
+ sets OTEL env vars, and execs Claude CLI.
46
+ """
47
+ from pennyfarthing_scripts.bikerack.launcher import (
48
+ build_otel_env,
49
+ exec_claude,
50
+ is_already_running,
51
+ poll_for_port_file,
52
+ register_cleanup,
53
+ start_wheelhub,
54
+ write_pid_file,
55
+ )
56
+
57
+ if project_dir:
58
+ project_dir = Path(project_dir)
59
+ elif os.environ.get("CYCLIST_PROJECT_DIR"):
60
+ project_dir = Path(os.environ["CYCLIST_PROJECT_DIR"])
61
+ else:
62
+ project_dir = Path.cwd()
63
+
64
+ running, pid, port = is_already_running(project_dir)
65
+ if running:
66
+ click.echo(
67
+ f"Error: BikeRack is already running (PID {pid}, port {port})",
68
+ err=True,
69
+ )
70
+ click.echo("Use 'pf bikerack stop' to stop it.", err=True)
71
+ sys.exit(2)
72
+
73
+ click.echo("Starting BikeRack mode...")
74
+ try:
75
+ proc = start_wheelhub(project_dir)
76
+ write_pid_file(project_dir, proc.pid)
77
+
78
+ port = poll_for_port_file(project_dir)
79
+ click.echo(f"WheelHub listening on http://localhost:{port}")
80
+
81
+ otel_env = build_otel_env(port)
82
+ click.echo("Setting OTEL environment variables...")
83
+
84
+ register_cleanup(project_dir, proc.pid)
85
+
86
+ click.echo(f"Dashboard: http://localhost:{port}/bikerack")
87
+ click.echo("Starting Claude CLI...")
88
+ exec_claude(otel_env, project_dir)
89
+ except TimeoutError as e:
90
+ click.echo(f"Error: {e}", err=True)
91
+ sys.exit(1)
92
+ except Exception as e:
93
+ click.echo(f"Error: {e}", err=True)
94
+ sys.exit(1)
95
+
96
+
97
+ @bikerack.command()
98
+ @click.option(
99
+ "--project-dir",
100
+ type=click.Path(exists=True, file_okay=False, resolve_path=True),
101
+ default=None,
102
+ help="Project directory. Falls back to CYCLIST_PROJECT_DIR env var, then cwd.",
103
+ )
104
+ def stop(project_dir):
105
+ """Stop running BikeRack instance."""
106
+ from pennyfarthing_scripts.bikerack.launcher import stop_bikerack
107
+
108
+ if project_dir:
109
+ project_dir = Path(project_dir)
110
+ elif os.environ.get("CYCLIST_PROJECT_DIR"):
111
+ project_dir = Path(os.environ["CYCLIST_PROJECT_DIR"])
112
+ else:
113
+ project_dir = Path.cwd()
114
+ result = stop_bikerack(project_dir)
115
+
116
+ if result["success"]:
117
+ click.echo(result["message"])
118
+ else:
119
+ click.echo(result["message"], err=True)
120
+ sys.exit(1)
121
+
122
+
123
+ @bikerack.command()
124
+ @click.option(
125
+ "--project-dir",
126
+ type=click.Path(exists=True, file_okay=False, resolve_path=True),
127
+ default=None,
128
+ help="Project directory. Falls back to CYCLIST_PROJECT_DIR env var, then cwd.",
129
+ )
130
+ def status(project_dir):
131
+ """Show BikeRack running state."""
132
+ from pennyfarthing_scripts.bikerack.launcher import get_status
133
+
134
+ if project_dir:
135
+ project_dir = Path(project_dir)
136
+ elif os.environ.get("CYCLIST_PROJECT_DIR"):
137
+ project_dir = Path(os.environ["CYCLIST_PROJECT_DIR"])
138
+ else:
139
+ project_dir = Path.cwd()
140
+ result = get_status(project_dir)
141
+
142
+ if result["running"]:
143
+ click.echo("BikeRack is running")
144
+ click.echo(f" PID: {result['pid']}")
145
+ click.echo(f" Port: {result['port']}")
146
+ click.echo(f" Dashboard: {result['dashboard']}")
147
+ else:
148
+ click.echo("BikeRack is not running")
@@ -0,0 +1,181 @@
1
+ """BikeRack launcher — core lifecycle functions.
2
+
3
+ Story 101-5: BikeRack launcher CLI (pf bikerack start/stop/status)
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import atexit
9
+ import os
10
+ import signal
11
+ import subprocess
12
+ import time
13
+ from pathlib import Path
14
+ from typing import NoReturn
15
+
16
+
17
+ def is_process_alive(pid: int) -> bool:
18
+ """Check if a process with given PID is alive."""
19
+ try:
20
+ os.kill(pid, 0)
21
+ return True
22
+ except (ProcessLookupError, PermissionError, OSError):
23
+ return False
24
+
25
+
26
+ def cleanup_files(project_dir: Path) -> None:
27
+ """Clean up .bikerack-port and .bikerack-pid files."""
28
+ for name in (".bikerack-port", ".bikerack-pid"):
29
+ try:
30
+ (project_dir / name).unlink()
31
+ except FileNotFoundError:
32
+ pass
33
+
34
+
35
+ def read_port_file(project_dir: Path) -> int | None:
36
+ """Read port from .bikerack-port file. Returns None if not found."""
37
+ try:
38
+ return int((project_dir / ".bikerack-port").read_text().strip())
39
+ except (FileNotFoundError, ValueError):
40
+ return None
41
+
42
+
43
+ def read_pid_file(project_dir: Path) -> int | None:
44
+ """Read PID from .bikerack-pid file. Returns None if not found."""
45
+ try:
46
+ return int((project_dir / ".bikerack-pid").read_text().strip())
47
+ except (FileNotFoundError, ValueError):
48
+ return None
49
+
50
+
51
+ def write_pid_file(project_dir: Path, pid: int) -> None:
52
+ """Write .bikerack-pid file."""
53
+ (project_dir / ".bikerack-pid").write_text(str(pid))
54
+
55
+
56
+ def build_otel_env(port: int) -> dict[str, str]:
57
+ """Build the 5 OTEL environment variables from discovered port (Rule 5)."""
58
+ return {
59
+ "CLAUDE_CODE_ENABLE_TELEMETRY": "1",
60
+ "OTEL_LOGS_EXPORTER": "otlp",
61
+ "OTEL_METRICS_EXPORTER": "otlp",
62
+ "OTEL_EXPORTER_OTLP_PROTOCOL": "http/json",
63
+ "OTEL_EXPORTER_OTLP_ENDPOINT": f"http://localhost:{port}",
64
+ }
65
+
66
+
67
+ def _find_framework_dir() -> Path:
68
+ """Locate the pennyfarthing framework root from this package's location."""
69
+ # pennyfarthing_scripts/bikerack/launcher.py -> pennyfarthing/
70
+ return Path(__file__).resolve().parent.parent.parent
71
+
72
+
73
+ def start_wheelhub(project_dir: Path) -> subprocess.Popen:
74
+ """Start WheelHub server in background with IS_BIKERACK=1."""
75
+ framework_dir = _find_framework_dir()
76
+ bikerack_entry = framework_dir / "packages" / "cyclist" / "dist" / "bikerack.js"
77
+
78
+ env = os.environ.copy()
79
+ env["IS_BIKERACK"] = "1"
80
+ env["CYCLIST_PROJECT_DIR"] = str(project_dir)
81
+
82
+ return subprocess.Popen(
83
+ ["node", str(bikerack_entry)],
84
+ env=env,
85
+ cwd=str(project_dir),
86
+ stdout=subprocess.DEVNULL,
87
+ stderr=subprocess.DEVNULL,
88
+ )
89
+
90
+
91
+ def poll_for_port_file(
92
+ project_dir: Path, timeout: float = 5.0, interval: float = 0.1
93
+ ) -> int:
94
+ """Poll for .bikerack-port file, return port number."""
95
+ port_file = project_dir / ".bikerack-port"
96
+ deadline = time.monotonic() + timeout
97
+
98
+ while True:
99
+ if port_file.exists():
100
+ return int(port_file.read_text().strip())
101
+ if time.monotonic() >= deadline:
102
+ raise TimeoutError(
103
+ f"Timed out waiting for {port_file} after {timeout}s"
104
+ )
105
+ time.sleep(interval)
106
+
107
+
108
+ def register_cleanup(project_dir: Path, pid: int) -> None:
109
+ """Register atexit handler to kill WheelHub and clean up files (Rule 8)."""
110
+
111
+ def _cleanup(project_dir: Path, pid: int) -> None:
112
+ try:
113
+ os.kill(pid, signal.SIGTERM)
114
+ except (ProcessLookupError, OSError):
115
+ pass
116
+ cleanup_files(project_dir)
117
+
118
+ atexit.register(_cleanup, project_dir, pid)
119
+
120
+
121
+ def exec_claude(otel_env: dict[str, str], project_dir: Path | None = None) -> NoReturn:
122
+ """Replace current process with Claude CLI using os.execvpe (CE-4)."""
123
+ env = os.environ.copy()
124
+ env.update(otel_env)
125
+ if project_dir:
126
+ os.chdir(project_dir)
127
+ os.execvpe("claude", ["claude"], env)
128
+
129
+
130
+ def is_already_running(project_dir: Path) -> tuple[bool, int | None, int | None]:
131
+ """Check if BikeRack is already running.
132
+
133
+ Returns (is_running, pid_or_none, port_or_none).
134
+ Cleans up stale files if PID is dead.
135
+ """
136
+ pid = read_pid_file(project_dir)
137
+ port = read_port_file(project_dir)
138
+
139
+ if pid is None or port is None:
140
+ return (False, None, None)
141
+
142
+ if is_process_alive(pid):
143
+ return (True, pid, port)
144
+
145
+ cleanup_files(project_dir)
146
+ return (False, None, None)
147
+
148
+
149
+ def stop_bikerack(project_dir: Path) -> dict:
150
+ """Stop running BikeRack instance. Returns {success, pid, message}."""
151
+ pid = read_pid_file(project_dir)
152
+
153
+ if pid is None:
154
+ return {"success": False, "message": "BikeRack is not running"}
155
+
156
+ if not is_process_alive(pid):
157
+ cleanup_files(project_dir)
158
+ return {"success": False, "message": "BikeRack is not running (stale PID)"}
159
+
160
+ os.kill(pid, signal.SIGTERM)
161
+ cleanup_files(project_dir)
162
+ return {"success": True, "pid": pid, "message": f"Stopped BikeRack (PID {pid})"}
163
+
164
+
165
+ def get_status(project_dir: Path) -> dict:
166
+ """Get BikeRack running status. Returns {running, pid, port, dashboard}."""
167
+ pid = read_pid_file(project_dir)
168
+ port = read_port_file(project_dir)
169
+
170
+ if pid is None or port is None:
171
+ return {"running": False}
172
+
173
+ if not is_process_alive(pid):
174
+ return {"running": False}
175
+
176
+ return {
177
+ "running": True,
178
+ "pid": pid,
179
+ "port": port,
180
+ "dashboard": f"http://localhost:{port}/bikerack",
181
+ }
@@ -65,6 +65,11 @@ from pennyfarthing_scripts.validate.cli import validate # noqa: E402
65
65
 
66
66
  cli.add_command(validate)
67
67
 
68
+ # Import and register bikerack group
69
+ from pennyfarthing_scripts.bikerack.cli import bikerack # noqa: E402
70
+
71
+ cli.add_command(bikerack)
72
+
68
73
 
69
74
  @cli.group()
70
75
  def agent():
@@ -490,7 +490,8 @@ async def _probe_agent_context_efficiency(target_path: Path) -> float | None:
490
490
  from pennyfarthing_scripts.prime.tiers import ContextTier, load_tier_components
491
491
 
492
492
  agents = ["sm", "tea", "dev", "reviewer", "architect",
493
- "pm", "tech-writer", "ux-designer", "devops", "orchestrator"]
493
+ "pm", "tech-writer", "ux-designer", "devops", "orchestrator",
494
+ "ba"]
494
495
 
495
496
  target_budget = 4000
496
497
  scores: list[float] = []
@@ -47,6 +47,7 @@ from pennyfarthing_scripts.prime.loader import (
47
47
  load_agent_definition,
48
48
  load_behavior_guide,
49
49
  load_domain_docs,
50
+ load_repos_topology,
50
51
  load_session_context,
51
52
  load_sidecars,
52
53
  load_sprint_context,
@@ -94,6 +95,7 @@ __all__ = [
94
95
  "load_session_context",
95
96
  "load_sidecars",
96
97
  "load_domain_docs",
98
+ "load_repos_topology",
97
99
  # Workflow detection
98
100
  "detect_workflow_state",
99
101
  "check_redirect",
@@ -102,6 +102,7 @@ def _component_header(name: str, agent_name: str | None) -> str:
102
102
  "persona_compressed": f"Persona: {agent_name} (compressed)",
103
103
  "behavior_guide": "Agent Behavior Guide",
104
104
  "sprint_context": "Sprint Context",
105
+ "repos_topology": "Repos Topology",
105
106
  "session_header": "Active Session",
106
107
  "session_assessment": "Session Assessment",
107
108
  "sidecars": f"Agent Sidecar: {agent_name}",
@@ -118,6 +119,7 @@ def _component_source(name: str, agent_name: str | None, root: Path) -> str | No
118
119
  "persona_compressed": None,
119
120
  "behavior_guide": ".pennyfarthing/guides/agent-behavior.md",
120
121
  "sprint_context": "sprint/current-sprint.yaml",
122
+ "repos_topology": ".pennyfarthing/repos.yaml",
121
123
  "session_header": None,
122
124
  "session_assessment": None,
123
125
  "sidecars": f".pennyfarthing/sidecars/{agent_name}/",
@@ -474,6 +476,17 @@ def prime(
474
476
  _print_header("Sprint Context", quiet)
475
477
  print(sprint_content)
476
478
 
479
+ # ==========================================================================
480
+ # PRIORITY 5.5: Repos Topology
481
+ # ==========================================================================
482
+ if not json_output:
483
+ from pennyfarthing_scripts.prime.loader import load_repos_topology
484
+
485
+ topology_content = load_repos_topology(root)
486
+ if topology_content:
487
+ _print_header("Repos Topology", quiet)
488
+ print(topology_content)
489
+
477
490
  # ==========================================================================
478
491
  # PRIORITY 6: Session context
479
492
  # ==========================================================================