@pennyfarthing/core 11.2.0 → 11.2.2

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 (367) hide show
  1. package/README.md +100 -40
  2. package/package.json +2 -1
  3. package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
  4. package/packages/core/dist/cli/commands/doctor.js +474 -66
  5. package/packages/core/dist/cli/commands/doctor.js.map +1 -1
  6. package/packages/core/dist/cli/commands/init.js +4 -4
  7. package/packages/core/dist/cli/commands/init.js.map +1 -1
  8. package/packages/core/dist/cli/commands/update.d.ts.map +1 -1
  9. package/packages/core/dist/cli/commands/update.js +4 -5
  10. package/packages/core/dist/cli/commands/update.js.map +1 -1
  11. package/packages/core/dist/cli/utils/constants.d.ts +3 -8
  12. package/packages/core/dist/cli/utils/constants.d.ts.map +1 -1
  13. package/packages/core/dist/cli/utils/constants.js +3 -4
  14. package/packages/core/dist/cli/utils/constants.js.map +1 -1
  15. package/packages/core/dist/cli/utils/settings.d.ts +7 -0
  16. package/packages/core/dist/cli/utils/settings.d.ts.map +1 -1
  17. package/packages/core/dist/cli/utils/settings.js +70 -29
  18. package/packages/core/dist/cli/utils/settings.js.map +1 -1
  19. package/packages/core/dist/cli/utils/symlinks.js +16 -16
  20. package/packages/core/dist/cli/utils/symlinks.js.map +1 -1
  21. package/packages/core/dist/consultation/dialogue-manager.d.ts +1 -1
  22. package/packages/core/dist/consultation/dialogue-manager.d.ts.map +1 -1
  23. package/packages/core/dist/consultation/dialogue-manager.js +1 -1
  24. package/packages/core/dist/consultation/dialogue-manager.js.map +1 -1
  25. package/packages/core/dist/consultation/dialogue-manager.test.js.map +1 -1
  26. package/packages/core/dist/consultation/tandem-metrics.d.ts +91 -0
  27. package/packages/core/dist/consultation/tandem-metrics.d.ts.map +1 -0
  28. package/packages/core/dist/consultation/tandem-metrics.js +131 -0
  29. package/packages/core/dist/consultation/tandem-metrics.js.map +1 -0
  30. package/packages/core/dist/consultation/tandem-metrics.test.d.ts +18 -0
  31. package/packages/core/dist/consultation/tandem-metrics.test.d.ts.map +1 -0
  32. package/packages/core/dist/consultation/tandem-metrics.test.js +457 -0
  33. package/packages/core/dist/consultation/tandem-metrics.test.js.map +1 -0
  34. package/packages/core/dist/public/css/react.css +1 -1
  35. package/packages/core/dist/public/js/react/react.js +14 -14
  36. package/packages/core/dist/server/api/agent-load.js +1 -1
  37. package/packages/core/dist/server/api/agent-load.js.map +1 -1
  38. package/packages/core/dist/server/api/git.d.ts.map +1 -1
  39. package/packages/core/dist/server/api/git.js +0 -1
  40. package/packages/core/dist/server/api/git.js.map +1 -1
  41. package/packages/core/dist/server/api/index.d.ts +2 -0
  42. package/packages/core/dist/server/api/index.d.ts.map +1 -1
  43. package/packages/core/dist/server/api/index.js +2 -0
  44. package/packages/core/dist/server/api/index.js.map +1 -1
  45. package/packages/core/dist/server/api/project-info.d.ts +11 -0
  46. package/packages/core/dist/server/api/project-info.d.ts.map +1 -0
  47. package/packages/core/dist/server/api/project-info.js +18 -0
  48. package/packages/core/dist/server/api/project-info.js.map +1 -0
  49. package/packages/core/dist/server/otlp-receiver.d.ts.map +1 -1
  50. package/packages/core/dist/server/otlp-receiver.js +18 -1
  51. package/packages/core/dist/server/otlp-receiver.js.map +1 -1
  52. package/packages/core/dist/server/otlp-receiver.test.js +1 -1
  53. package/packages/core/dist/server/otlp-receiver.test.js.map +1 -1
  54. package/packages/core/dist/server/server.d.ts +0 -3
  55. package/packages/core/dist/server/server.d.ts.map +1 -1
  56. package/packages/core/dist/server/server.js +5 -38
  57. package/packages/core/dist/server/server.js.map +1 -1
  58. package/packages/core/dist/server/server.test.d.ts +1 -1
  59. package/packages/core/dist/server/server.test.js +12 -23
  60. package/packages/core/dist/server/server.test.js.map +1 -1
  61. package/packages/core/dist/server/settings.d.ts +1 -0
  62. package/packages/core/dist/server/settings.d.ts.map +1 -1
  63. package/packages/core/dist/server/settings.js +13 -0
  64. package/packages/core/dist/server/settings.js.map +1 -1
  65. package/packages/core/dist/shared/capabilities.d.ts +88 -0
  66. package/packages/core/dist/shared/capabilities.d.ts.map +1 -0
  67. package/packages/core/dist/shared/capabilities.js +133 -0
  68. package/packages/core/dist/shared/capabilities.js.map +1 -0
  69. package/packages/core/dist/shared/capabilities.test.d.ts +2 -0
  70. package/packages/core/dist/shared/capabilities.test.d.ts.map +1 -0
  71. package/packages/core/dist/shared/capabilities.test.js +217 -0
  72. package/packages/core/dist/shared/capabilities.test.js.map +1 -0
  73. package/packages/core/dist/shared/spawn-prompt.d.ts +47 -0
  74. package/packages/core/dist/shared/spawn-prompt.d.ts.map +1 -0
  75. package/packages/core/dist/shared/spawn-prompt.js +82 -0
  76. package/packages/core/dist/shared/spawn-prompt.js.map +1 -0
  77. package/packages/core/dist/shared/spawn-prompt.test.d.ts +2 -0
  78. package/packages/core/dist/shared/spawn-prompt.test.d.ts.map +1 -0
  79. package/packages/core/dist/shared/spawn-prompt.test.js +251 -0
  80. package/packages/core/dist/shared/spawn-prompt.test.js.map +1 -0
  81. package/packages/core/dist/workflow/tandem-workflow-templates.test.d.ts +18 -0
  82. package/packages/core/dist/workflow/tandem-workflow-templates.test.d.ts.map +1 -0
  83. package/packages/core/dist/workflow/tandem-workflow-templates.test.js +434 -0
  84. package/packages/core/dist/workflow/tandem-workflow-templates.test.js.map +1 -0
  85. package/packages/core/dist/workflow/team-lifecycle.d.ts +169 -0
  86. package/packages/core/dist/workflow/team-lifecycle.d.ts.map +1 -0
  87. package/packages/core/dist/workflow/team-lifecycle.js +217 -0
  88. package/packages/core/dist/workflow/team-lifecycle.js.map +1 -0
  89. package/packages/core/dist/workflow/team-lifecycle.test.d.ts +20 -0
  90. package/packages/core/dist/workflow/team-lifecycle.test.d.ts.map +1 -0
  91. package/packages/core/dist/workflow/team-lifecycle.test.js +966 -0
  92. package/packages/core/dist/workflow/team-lifecycle.test.js.map +1 -0
  93. package/packages/core/dist/workflow/workflow-schema.d.ts +32 -0
  94. package/packages/core/dist/workflow/workflow-schema.d.ts.map +1 -1
  95. package/packages/core/dist/workflow/workflow-schema.js +120 -0
  96. package/packages/core/dist/workflow/workflow-schema.js.map +1 -1
  97. package/packages/core/dist/workflow/workflow-schema.test.d.ts.map +1 -1
  98. package/packages/core/dist/workflow/workflow-schema.test.js +570 -1
  99. package/packages/core/dist/workflow/workflow-schema.test.js.map +1 -1
  100. package/packages/core/dist/workflow/workflow-team-templates.test.d.ts +17 -0
  101. package/packages/core/dist/workflow/workflow-team-templates.test.d.ts.map +1 -0
  102. package/packages/core/dist/workflow/workflow-team-templates.test.js +275 -0
  103. package/packages/core/dist/workflow/workflow-team-templates.test.js.map +1 -0
  104. package/pennyfarthing-dist/agents/dev.md +21 -12
  105. package/pennyfarthing-dist/agents/reviewer.md +23 -4
  106. package/pennyfarthing-dist/agents/sm-finish.md +19 -2
  107. package/pennyfarthing-dist/agents/sm-setup.md +7 -7
  108. package/pennyfarthing-dist/agents/sm.md +12 -12
  109. package/pennyfarthing-dist/agents/tea.md +2 -2
  110. package/pennyfarthing-dist/agents/testing-runner.md +1 -1
  111. package/pennyfarthing-dist/commands/pf-architect.md +1 -1
  112. package/pennyfarthing-dist/commands/pf-ba.md +1 -1
  113. package/pennyfarthing-dist/commands/pf-chore.md +2 -2
  114. package/pennyfarthing-dist/commands/pf-dev.md +1 -1
  115. package/pennyfarthing-dist/commands/pf-devops.md +1 -1
  116. package/pennyfarthing-dist/commands/pf-epic.md +6 -6
  117. package/pennyfarthing-dist/commands/pf-git.md +12 -10
  118. package/pennyfarthing-dist/commands/pf-health-check.md +1 -1
  119. package/pennyfarthing-dist/commands/pf-help.md +12 -12
  120. package/pennyfarthing-dist/commands/pf-orchestrator.md +1 -1
  121. package/pennyfarthing-dist/commands/pf-pm.md +1 -1
  122. package/pennyfarthing-dist/commands/pf-prime.md +8 -8
  123. package/pennyfarthing-dist/commands/pf-reviewer.md +1 -1
  124. package/pennyfarthing-dist/commands/pf-session.md +7 -7
  125. package/pennyfarthing-dist/commands/pf-sm.md +1 -1
  126. package/pennyfarthing-dist/commands/pf-sprint.md +7 -7
  127. package/pennyfarthing-dist/commands/pf-tea.md +1 -1
  128. package/pennyfarthing-dist/commands/pf-tech-writer.md +1 -1
  129. package/pennyfarthing-dist/commands/pf-theme.md +9 -9
  130. package/pennyfarthing-dist/commands/pf-ux-designer.md +1 -1
  131. package/pennyfarthing-dist/commands/pf-work.md +1 -1
  132. package/pennyfarthing-dist/gates/approval.md +63 -0
  133. package/pennyfarthing-dist/gates/confidence-sm.md +71 -0
  134. package/pennyfarthing-dist/gates/context-ok.md +56 -0
  135. package/pennyfarthing-dist/gates/evaluations/confidence-sm.md +54 -0
  136. package/pennyfarthing-dist/gates/quality-pass.md +67 -0
  137. package/pennyfarthing-dist/gates/tests-fail.md +84 -0
  138. package/pennyfarthing-dist/gates/tests-pass.md +79 -0
  139. package/pennyfarthing-dist/guides/agent-behavior.md +84 -29
  140. package/pennyfarthing-dist/guides/agent-coordination.md +10 -10
  141. package/pennyfarthing-dist/guides/agent-tag-taxonomy.md +6 -6
  142. package/pennyfarthing-dist/guides/agent-template-tactical.md +1 -1
  143. package/pennyfarthing-dist/guides/bell-mode.md +1 -1
  144. package/pennyfarthing-dist/guides/bikerack.md +10 -10
  145. package/pennyfarthing-dist/guides/brownfield-tools.md +24 -24
  146. package/pennyfarthing-dist/guides/command-tag-taxonomy.md +1 -1
  147. package/pennyfarthing-dist/guides/gate-schema.md +2 -2
  148. package/pennyfarthing-dist/guides/gates.md +3 -3
  149. package/pennyfarthing-dist/guides/handoff-cli.md +8 -8
  150. package/pennyfarthing-dist/guides/hooks.md +29 -29
  151. package/pennyfarthing-dist/guides/prime.md +2 -2
  152. package/pennyfarthing-dist/guides/reflector.md +1 -1
  153. package/pennyfarthing-dist/guides/skill-schema.md +6 -6
  154. package/pennyfarthing-dist/guides/tandem-protocol.md +3 -3
  155. package/pennyfarthing-dist/guides/workflow-schema.md +1 -1
  156. package/pennyfarthing-dist/guides/worktree-mode.md +3 -3
  157. package/pennyfarthing-dist/guides/xml-tags.md +8 -8
  158. package/pennyfarthing-dist/scripts/README.md +4 -4
  159. package/pennyfarthing-dist/scripts/core/agent-session.sh +2 -5
  160. package/pennyfarthing-dist/scripts/core/check-context.sh +3 -1
  161. package/pennyfarthing-dist/scripts/core/pf.sh +5 -0
  162. package/pennyfarthing-dist/scripts/core/phase-check-start.sh +4 -89
  163. package/pennyfarthing-dist/scripts/core/prime.sh +2 -25
  164. package/pennyfarthing-dist/scripts/git/README.md +14 -14
  165. package/pennyfarthing-dist/scripts/git/create-feature-branches.sh +2 -3
  166. package/pennyfarthing-dist/scripts/git/git-status-all.sh +2 -3
  167. package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +2 -3
  168. package/pennyfarthing-dist/scripts/git/worktree-manager.sh +2 -4
  169. package/pennyfarthing-dist/scripts/hooks/README.md +6 -6
  170. package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +4 -183
  171. package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +4 -95
  172. package/pennyfarthing-dist/scripts/hooks/context-warning.sh +4 -65
  173. package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +3 -31
  174. package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +5 -4
  175. package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +29 -34
  176. package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +4 -71
  177. package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +3 -19
  178. package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +4 -30
  179. package/pennyfarthing-dist/scripts/hooks/session-start.sh +3 -32
  180. package/pennyfarthing-dist/scripts/hooks/session-stop.sh +4 -65
  181. package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +4 -78
  182. package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +3 -93
  183. package/pennyfarthing-dist/scripts/lib/env.sh +34 -0
  184. package/pennyfarthing-dist/scripts/lib/run-pf.sh +39 -0
  185. package/pennyfarthing-dist/scripts/misc/README.md +1 -1
  186. package/pennyfarthing-dist/scripts/misc/statusline.sh +4 -301
  187. package/pennyfarthing-dist/scripts/sprint/README.md +21 -21
  188. package/pennyfarthing-dist/scripts/workflow/README.md +2 -2
  189. package/pennyfarthing-dist/scripts/workflow/finish-story.sh +2 -16
  190. package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +3 -3
  191. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +3 -3
  192. package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +3 -3
  193. package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +3 -3
  194. package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +3 -3
  195. package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +3 -3
  196. package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +3 -3
  197. package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +3 -3
  198. package/pennyfarthing-dist/skills/pf-bc/examples.md +23 -23
  199. package/pennyfarthing-dist/skills/pf-bc/skill.md +17 -17
  200. package/pennyfarthing-dist/skills/pf-bc/usage.md +8 -8
  201. package/pennyfarthing-dist/skills/pf-jira/SKILL.md +15 -15
  202. package/pennyfarthing-dist/skills/pf-jira/examples.md +48 -48
  203. package/pennyfarthing-dist/skills/pf-jira/usage.md +15 -15
  204. package/pennyfarthing-dist/skills/pf-sprint/examples.md +80 -80
  205. package/pennyfarthing-dist/skills/pf-sprint/skill.md +35 -35
  206. package/pennyfarthing-dist/skills/pf-sprint/usage.md +30 -30
  207. package/pennyfarthing-dist/skills/pf-theme/examples.md +15 -15
  208. package/pennyfarthing-dist/skills/pf-theme/skill.md +6 -6
  209. package/pennyfarthing-dist/skills/pf-theme/usage.md +5 -5
  210. package/pennyfarthing-dist/skills/pf-workflow/examples.md +27 -27
  211. package/pennyfarthing-dist/skills/pf-workflow/skill.md +11 -11
  212. package/pennyfarthing-dist/skills/pf-workflow/usage.md +11 -11
  213. package/pennyfarthing-dist/skills/skill-registry.yaml +19 -19
  214. package/pennyfarthing-dist/templates/settings.local.json.template +19 -10
  215. package/pennyfarthing-dist/workflows/bdd-team.yaml +89 -0
  216. package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-05-import-to-future.md +1 -1
  217. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-01-analyze.md +1 -1
  218. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-04-verify.md +1 -1
  219. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +1 -1
  220. package/pennyfarthing-dist/workflows/project-setup/steps/step-01-discover.md +47 -0
  221. package/pennyfarthing-dist/workflows/tdd-team.yaml +80 -0
  222. package/pennyfarthing-dist/workflows/tdd.yaml +11 -2
  223. package/pennyfarthing_scripts/CLAUDE.md +19 -10
  224. package/pennyfarthing_scripts/__init__.py +1 -1
  225. package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  226. package/pennyfarthing_scripts/__pycache__/bellmode_hook.cpython-314.pyc +0 -0
  227. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  228. package/pennyfarthing_scripts/__pycache__/context.cpython-314.pyc +0 -0
  229. package/pennyfarthing_scripts/__pycache__/hooks.cpython-314.pyc +0 -0
  230. package/pennyfarthing_scripts/__pycache__/pretooluse_hook.cpython-314.pyc +0 -0
  231. package/pennyfarthing_scripts/__pycache__/schema_validation_hook.cpython-314.pyc +0 -0
  232. package/pennyfarthing_scripts/__pycache__/session_start_hook.cpython-314.pyc +0 -0
  233. package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-314.pyc +0 -0
  234. package/pennyfarthing_scripts/bc/__pycache__/focus.cpython-314.pyc +0 -0
  235. package/pennyfarthing_scripts/bc/__pycache__/split.cpython-314.pyc +0 -0
  236. package/pennyfarthing_scripts/bc/cli.py +2 -2
  237. package/pennyfarthing_scripts/bellmode_hook.py +9 -296
  238. package/pennyfarthing_scripts/bikerack/__pycache__/audit_log_panel.cpython-314.pyc +0 -0
  239. package/pennyfarthing_scripts/bikerack/__pycache__/background_panel.cpython-314.pyc +0 -0
  240. package/pennyfarthing_scripts/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
  241. package/pennyfarthing_scripts/bikerack/__pycache__/changed_panel.cpython-314.pyc +0 -0
  242. package/pennyfarthing_scripts/bikerack/__pycache__/context_meter_footer.cpython-314.pyc +0 -0
  243. package/pennyfarthing_scripts/bikerack/__pycache__/debug_panel.cpython-314.pyc +0 -0
  244. package/pennyfarthing_scripts/bikerack/__pycache__/diffs_panel.cpython-314.pyc +0 -0
  245. package/pennyfarthing_scripts/bikerack/__pycache__/events.cpython-314.pyc +0 -0
  246. package/pennyfarthing_scripts/bikerack/__pycache__/git_panel.cpython-314.pyc +0 -0
  247. package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
  248. package/pennyfarthing_scripts/bikerack/__pycache__/portrait_resolver.cpython-314.pyc +0 -0
  249. package/pennyfarthing_scripts/bikerack/__pycache__/progress_panel.cpython-314.pyc +0 -0
  250. package/pennyfarthing_scripts/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
  251. package/pennyfarthing_scripts/bikerack/__pycache__/story_detail_data.cpython-314.pyc +0 -0
  252. package/pennyfarthing_scripts/bikerack/__pycache__/story_detail_screen.cpython-314.pyc +0 -0
  253. package/pennyfarthing_scripts/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
  254. package/pennyfarthing_scripts/bikerack/__pycache__/ws_client.cpython-314.pyc +0 -0
  255. package/pennyfarthing_scripts/bikerack/audit_log_panel.py +161 -0
  256. package/pennyfarthing_scripts/bikerack/base_panel.py +27 -4
  257. package/pennyfarthing_scripts/bikerack/changed_panel.py +96 -4
  258. package/pennyfarthing_scripts/bikerack/context_meter_footer.py +88 -0
  259. package/pennyfarthing_scripts/bikerack/debug_panel.py +1 -1
  260. package/pennyfarthing_scripts/bikerack/diffs_panel.py +30 -0
  261. package/pennyfarthing_scripts/bikerack/events.py +28 -0
  262. package/pennyfarthing_scripts/bikerack/launcher.py +6 -6
  263. package/pennyfarthing_scripts/bikerack/portrait_resolver.py +139 -0
  264. package/pennyfarthing_scripts/bikerack/progress_panel.py +0 -1
  265. package/pennyfarthing_scripts/bikerack/sprint_panel.py +373 -142
  266. package/pennyfarthing_scripts/bikerack/story_detail_data.py +247 -0
  267. package/pennyfarthing_scripts/bikerack/story_detail_screen.py +177 -0
  268. package/pennyfarthing_scripts/bikerack/tui.py +304 -62
  269. package/pennyfarthing_scripts/bikerack/ws_client.py +2 -2
  270. package/pennyfarthing_scripts/cli.py +5 -0
  271. package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
  272. package/pennyfarthing_scripts/common/config.py +29 -2
  273. package/pennyfarthing_scripts/common/pr_config.py +38 -0
  274. package/pennyfarthing_scripts/consultation/__pycache__/__init__.cpython-314.pyc +0 -0
  275. package/pennyfarthing_scripts/consultation/__pycache__/cli.cpython-314.pyc +0 -0
  276. package/pennyfarthing_scripts/consultation/cli.py +3 -3
  277. package/pennyfarthing_scripts/context.py +3 -3
  278. package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
  279. package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
  280. package/pennyfarthing_scripts/git/__pycache__/repos.cpython-314.pyc +0 -0
  281. package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
  282. package/pennyfarthing_scripts/git/hooks_installer.py +2 -3
  283. package/pennyfarthing_scripts/git/status_all.py +1 -1
  284. package/pennyfarthing_scripts/git/worktree.py +2 -2
  285. package/pennyfarthing_scripts/git_group/__pycache__/cli.cpython-314.pyc +0 -0
  286. package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-314.pyc +0 -0
  287. package/pennyfarthing_scripts/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
  288. package/pennyfarthing_scripts/handoff/__pycache__/marker.cpython-314.pyc +0 -0
  289. package/pennyfarthing_scripts/handoff/__pycache__/phase_check.cpython-314.pyc +0 -0
  290. package/pennyfarthing_scripts/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
  291. package/pennyfarthing_scripts/handoff/cli.py +33 -1
  292. package/pennyfarthing_scripts/handoff/complete_phase.py +28 -0
  293. package/pennyfarthing_scripts/handoff/marker.py +15 -15
  294. package/pennyfarthing_scripts/handoff/phase_check.py +96 -0
  295. package/pennyfarthing_scripts/handoff/resolve_gate.py +13 -1
  296. package/pennyfarthing_scripts/hooks/__init__.py +442 -0
  297. package/pennyfarthing_scripts/hooks/__pycache__/__init__.cpython-314.pyc +0 -0
  298. package/pennyfarthing_scripts/hooks/__pycache__/bell_mode.cpython-314.pyc +0 -0
  299. package/pennyfarthing_scripts/hooks/__pycache__/cli.cpython-314.pyc +0 -0
  300. package/pennyfarthing_scripts/hooks/__pycache__/context_breaker.cpython-314.pyc +0 -0
  301. package/pennyfarthing_scripts/hooks/__pycache__/context_warning.cpython-314.pyc +0 -0
  302. package/pennyfarthing_scripts/hooks/__pycache__/cyclist_pretooluse.cpython-314.pyc +0 -0
  303. package/pennyfarthing_scripts/hooks/__pycache__/pre_edit_check.cpython-314.pyc +0 -0
  304. package/pennyfarthing_scripts/hooks/__pycache__/reflector_check.cpython-314.pyc +0 -0
  305. package/pennyfarthing_scripts/hooks/__pycache__/schema_validation.cpython-314.pyc +0 -0
  306. package/pennyfarthing_scripts/hooks/__pycache__/session_start.cpython-314.pyc +0 -0
  307. package/pennyfarthing_scripts/hooks/__pycache__/session_stop.cpython-314.pyc +0 -0
  308. package/pennyfarthing_scripts/hooks/__pycache__/sprint_yaml_validation.cpython-314.pyc +0 -0
  309. package/pennyfarthing_scripts/hooks/__pycache__/statusline.cpython-314.pyc +0 -0
  310. package/pennyfarthing_scripts/hooks/bell_mode.py +214 -0
  311. package/pennyfarthing_scripts/hooks/cli.py +96 -0
  312. package/pennyfarthing_scripts/hooks/context_breaker.py +104 -0
  313. package/pennyfarthing_scripts/hooks/context_warning.py +66 -0
  314. package/pennyfarthing_scripts/hooks/cyclist_pretooluse.py +129 -0
  315. package/pennyfarthing_scripts/hooks/pre_edit_check.py +77 -0
  316. package/pennyfarthing_scripts/hooks/reflector_check.py +270 -0
  317. package/pennyfarthing_scripts/hooks/schema_validation.py +202 -0
  318. package/pennyfarthing_scripts/hooks/session_start.py +294 -0
  319. package/pennyfarthing_scripts/hooks/session_stop.py +111 -0
  320. package/pennyfarthing_scripts/hooks/sprint_yaml_validation.py +97 -0
  321. package/pennyfarthing_scripts/hooks/statusline.py +429 -0
  322. package/pennyfarthing_scripts/hooks.py +27 -432
  323. package/pennyfarthing_scripts/pretooluse_hook.py +3 -185
  324. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  325. package/pennyfarthing_scripts/prime/heatmap.py +3 -15
  326. package/pennyfarthing_scripts/prime/workflow.py +2 -1
  327. package/pennyfarthing_scripts/schema_validation_hook.py +3 -298
  328. package/pennyfarthing_scripts/session_start_hook.py +4 -186
  329. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  330. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  331. package/pennyfarthing_scripts/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
  332. package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
  333. package/pennyfarthing_scripts/sprint/cli.py +121 -0
  334. package/pennyfarthing_scripts/sprint/loader.py +154 -3
  335. package/pennyfarthing_scripts/sprint/story_update.py +26 -0
  336. package/pennyfarthing_scripts/tests/__pycache__/test_bikerack.cpython-314-pytest-9.0.2.pyc +0 -0
  337. package/pennyfarthing_scripts/tests/__pycache__/test_handoff_cli.cpython-314-pytest-9.0.2.pyc +0 -0
  338. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_list_team.cpython-314-pytest-9.0.2.pyc +0 -0
  339. package/pennyfarthing_scripts/tests/test_bikerack.py +26 -26
  340. package/pennyfarthing_scripts/tests/test_dialogue_manager.py +0 -1
  341. package/pennyfarthing_scripts/tests/test_sprint_panel.py +344 -265
  342. package/pennyfarthing_scripts/tests/test_workflow_list_team.py +147 -0
  343. package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +0 -0
  344. package/pennyfarthing_scripts/validate/adapters/__pycache__/skill_command.cpython-314.pyc +0 -0
  345. package/pennyfarthing_scripts/validate/adapters/__pycache__/tandem_awareness.cpython-314.pyc +0 -0
  346. package/pennyfarthing_scripts/validate/adapters/__pycache__/team_mode.cpython-314.pyc +0 -0
  347. package/pennyfarthing_scripts/validate/adapters/__pycache__/workflow.cpython-314.pyc +0 -0
  348. package/pennyfarthing_scripts/validate/adapters/team_mode.py +323 -0
  349. package/pennyfarthing_scripts/validate/adapters/workflow.py +19 -0
  350. package/pennyfarthing_scripts/welcome_hook.py +3 -149
  351. package/pennyfarthing_scripts/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
  352. package/pennyfarthing_scripts/workflow/__pycache__/cli.cpython-314.pyc +0 -0
  353. package/pennyfarthing_scripts/workflow/__pycache__/helpers.cpython-314.pyc +0 -0
  354. package/pennyfarthing_scripts/workflow/__pycache__/scale.cpython-314.pyc +0 -0
  355. package/pennyfarthing_scripts/workflow/__pycache__/state.cpython-314.pyc +0 -0
  356. package/pennyfarthing_scripts/workflow/__pycache__/team_lifecycle.cpython-314.pyc +0 -0
  357. package/pennyfarthing_scripts/workflow/cli.py +22 -20
  358. package/pennyfarthing_scripts/workflow/state.py +0 -1
  359. package/pennyfarthing_scripts/workflow/team_lifecycle.py +256 -0
  360. package/packages/core/dist/cli/cyclist-migration.test.d.ts +0 -16
  361. package/packages/core/dist/cli/cyclist-migration.test.d.ts.map +0 -1
  362. package/packages/core/dist/cli/cyclist-migration.test.js +0 -229
  363. package/packages/core/dist/cli/cyclist-migration.test.js.map +0 -1
  364. package/packages/core/dist/scripts/theme-detail.test.d.ts +0 -10
  365. package/packages/core/dist/scripts/theme-detail.test.d.ts.map +0 -1
  366. package/packages/core/dist/scripts/theme-detail.test.js +0 -199
  367. package/packages/core/dist/scripts/theme-detail.test.js.map +0 -1
@@ -0,0 +1,247 @@
1
+ """Story detail data fetching for BikeRack TUI.
2
+
3
+ Story 110-2: Fetch story detail data (AC, session, workflow) via file read or API.
4
+ Returns enriched story data for StoryDetailScreen dossier layout.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import os
10
+ import re
11
+ import subprocess
12
+ from typing import Any
13
+
14
+
15
+ def _find_project_root() -> str | None:
16
+ """Walk up from CWD looking for .pennyfarthing/ directory."""
17
+ path = os.getcwd()
18
+ while True:
19
+ if os.path.isdir(os.path.join(path, ".pennyfarthing")):
20
+ return path
21
+ parent = os.path.dirname(path)
22
+ if parent == path:
23
+ return None
24
+ path = parent
25
+
26
+
27
+ def _find_session_file(story_id: str, project_root: str | None) -> str | None:
28
+ """Locate the session file for a story, trying given root then walking up."""
29
+ filename = f"{story_id}-session.md"
30
+ if project_root:
31
+ candidate = os.path.join(project_root, ".session", filename)
32
+ if os.path.isfile(candidate):
33
+ return candidate
34
+ # Walk up from CWD looking for .session/{story_id}-session.md
35
+ path = os.getcwd()
36
+ while True:
37
+ candidate = os.path.join(path, ".session", filename)
38
+ if os.path.isfile(candidate):
39
+ return candidate
40
+ parent = os.path.dirname(path)
41
+ if parent == path:
42
+ return None
43
+ path = parent
44
+
45
+
46
+ def _parse_session_file(session_path: str) -> dict[str, Any]:
47
+ """Parse a session markdown file for metadata fields."""
48
+ result: dict[str, Any] = {}
49
+ with open(session_path) as f:
50
+ content = f.read()
51
+
52
+ # Parse title from first heading: # Story X-Y: Title
53
+ title_match = re.search(r"^# Story [\w-]+:\s*(.+)", content, re.MULTILINE)
54
+ if title_match:
55
+ result["title"] = title_match.group(1).strip()
56
+
57
+ # Parse bold metadata lines: **Key:** value
58
+ for match in re.finditer(r"\*\*(\w[\w\s]*?):\*\*\s*(.*)", content):
59
+ key = match.group(1).strip().lower()
60
+ value = match.group(2).strip()
61
+ if key == "phase":
62
+ result["workflow_phase"] = value
63
+ elif key == "workflow":
64
+ result["workflow"] = value
65
+ elif key == "branch":
66
+ result["git_branch"] = value
67
+ elif key == "jira":
68
+ result["jiraKey"] = value
69
+ elif key == "points":
70
+ try:
71
+ result["points"] = int(value)
72
+ except ValueError:
73
+ result["points"] = value
74
+ elif key == "review verdict":
75
+ result["review_verdict"] = value
76
+ elif key == "review findings":
77
+ result["review_findings"] = value
78
+
79
+ # Parse ACs from ## Acceptance Criteria section
80
+ ac_match = re.search(
81
+ r"## Acceptance Criteria\n(.*?)(?=\n##|\Z)", content, re.DOTALL
82
+ )
83
+ if ac_match:
84
+ ac_text = ac_match.group(1)
85
+ acs: list[dict[str, Any]] = []
86
+ for line in ac_text.strip().split("\n"):
87
+ line = line.strip()
88
+ if not line:
89
+ continue
90
+ # Strip leading number. or - or *
91
+ text = re.sub(r"^[\d]+\.\s*", "", line).strip()
92
+ text = re.sub(r"^[-*]\s*", "", text).strip()
93
+ if text:
94
+ acs.append({"text": text, "done": False})
95
+ if acs:
96
+ result["acceptance_criteria"] = acs
97
+
98
+ # Parse session notes from ## Session Log section
99
+ log_match = re.search(
100
+ r"## Session Log\n(.*?)(?=\n##|\Z)", content, re.DOTALL
101
+ )
102
+ if log_match:
103
+ result["session_notes"] = log_match.group(1).strip()
104
+
105
+ return result
106
+
107
+
108
+ def _get_sprint_story_status(story_id: str) -> str | None:
109
+ """Try to get story status from pf sprint CLI."""
110
+ import sys
111
+
112
+ try:
113
+ out = subprocess.run(
114
+ [sys.executable, "-m", "pennyfarthing_scripts.cli",
115
+ "sprint", "story", "field", story_id, "status"],
116
+ capture_output=True,
117
+ text=True,
118
+ timeout=10,
119
+ )
120
+ if out.returncode == 0 and out.stdout.strip():
121
+ return out.stdout.strip()
122
+ except Exception:
123
+ pass
124
+ return None
125
+
126
+
127
+ def _get_pr_url(branch: str) -> str | None:
128
+ """Try to get PR URL for a branch via gh CLI."""
129
+ if not branch:
130
+ return None
131
+ try:
132
+ out = subprocess.run(
133
+ [
134
+ "gh",
135
+ "pr",
136
+ "list",
137
+ "--head",
138
+ branch,
139
+ "--json",
140
+ "url",
141
+ "--jq",
142
+ ".[0].url",
143
+ ],
144
+ capture_output=True,
145
+ text=True,
146
+ timeout=10,
147
+ )
148
+ if out.returncode == 0 and out.stdout.strip():
149
+ return out.stdout.strip()
150
+ except Exception:
151
+ pass
152
+ return None
153
+
154
+
155
+ def _check_context_files(story_id: str, project_root: str | None) -> dict[str, Any]:
156
+ """Check for epic and story context files.
157
+
158
+ Args:
159
+ story_id: Story identifier (e.g. "110-2").
160
+ project_root: Path to project root.
161
+
162
+ Returns:
163
+ Dict with has_epic_context, has_story_context, and their paths.
164
+ """
165
+ result: dict[str, Any] = {
166
+ "has_epic_context": False,
167
+ "has_story_context": False,
168
+ "epic_context_path": "",
169
+ "story_context_path": "",
170
+ }
171
+ if not project_root:
172
+ return result
173
+
174
+ # Extract epic number from story ID (e.g. "110-2" → "110")
175
+ parts = story_id.split("-")
176
+ if parts:
177
+ epic_num = parts[0]
178
+ epic_path = os.path.join(project_root, "sprint", "context", f"context-epic-{epic_num}.md")
179
+ if os.path.isfile(epic_path):
180
+ result["has_epic_context"] = True
181
+ result["epic_context_path"] = epic_path
182
+
183
+ story_path = os.path.join(project_root, "sprint", "context", f"context-story-{story_id}.md")
184
+ if os.path.isfile(story_path):
185
+ result["has_story_context"] = True
186
+ result["story_context_path"] = story_path
187
+
188
+ return result
189
+
190
+
191
+ def fetch_story_detail(
192
+ story_id: str,
193
+ project_root: str | None = None,
194
+ ) -> dict[str, Any]:
195
+ """Fetch detailed story data including ACs, session, workflow, and git info.
196
+
197
+ Args:
198
+ story_id: Story identifier (e.g. "110-2").
199
+ project_root: Path to project root (for file reads). Auto-detected if None.
200
+
201
+ Returns:
202
+ Dict with keys: id, title, points, status, jiraKey,
203
+ acceptance_criteria, workflow, workflow_phase,
204
+ git_branch, pr_url, session_notes.
205
+ Empty dict if story not found.
206
+ """
207
+ session_path = _find_session_file(story_id, project_root)
208
+ if not session_path:
209
+ return {}
210
+
211
+ result: dict[str, Any] = {"id": story_id}
212
+
213
+ # Parse session file (primary data source)
214
+ session_data = _parse_session_file(session_path)
215
+ result.update(session_data)
216
+
217
+ # Get status from sprint YAML if not in session
218
+ if "status" not in result:
219
+ status = _get_sprint_story_status(story_id)
220
+ if status:
221
+ result["status"] = status
222
+
223
+ # Get PR URL if we have a branch
224
+ if "pr_url" not in result:
225
+ pr_url = _get_pr_url(result.get("git_branch", ""))
226
+ result["pr_url"] = pr_url
227
+
228
+ # Check for context files
229
+ root = project_root or _find_project_root()
230
+ context_info = _check_context_files(story_id, root)
231
+ result.update(context_info)
232
+
233
+ # Ensure all required keys exist with defaults
234
+ result.setdefault("title", "")
235
+ result.setdefault("points", 0)
236
+ result.setdefault("status", "unknown")
237
+ result.setdefault("jiraKey", "")
238
+ result.setdefault("acceptance_criteria", [])
239
+ result.setdefault("workflow", "")
240
+ result.setdefault("workflow_phase", "")
241
+ result.setdefault("git_branch", "")
242
+ result.setdefault("pr_url", None)
243
+ result.setdefault("session_notes", "")
244
+ result.setdefault("review_findings", "")
245
+ result.setdefault("review_verdict", "")
246
+
247
+ return result
@@ -0,0 +1,177 @@
1
+ """StoryDetailScreen — Mission dossier detail view for BikeRack TUI.
2
+
3
+ Story 110-2: Story drill-through with dossier detail screen.
4
+ Pushed via Screen.push() from SprintPanel when user presses Enter on a story.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import webbrowser
10
+ from typing import Any
11
+
12
+ from rich.text import Text
13
+ from textual.binding import Binding
14
+ from textual.screen import Screen
15
+ from textual.widgets import Static
16
+
17
+
18
+ class StoryDetailScreen(Screen):
19
+ """Detail screen showing story dossier layout.
20
+
21
+ Displays AC checklist, workflow phase dots, git branch, PR link,
22
+ and session notes in a mission-dossier layout.
23
+ """
24
+
25
+ BINDINGS = [
26
+ Binding("escape", "pop_screen", "Back"),
27
+ Binding("enter", "open_pr_link", "Open PR"),
28
+ ]
29
+
30
+ def __init__(self, story_data: dict[str, Any] | None = None, **kwargs: Any) -> None:
31
+ super().__init__(**kwargs)
32
+ ws_data: dict[str, Any] = story_data or {}
33
+ self._story_data = self._enrich(ws_data)
34
+
35
+ @staticmethod
36
+ def _enrich(ws_data: dict[str, Any]) -> dict[str, Any]:
37
+ """Merge WS data with file-based enrichment. WS wins for non-null fields."""
38
+ story_id = ws_data.get("id", "")
39
+ if not story_id:
40
+ return ws_data
41
+
42
+ try:
43
+ from pennyfarthing_scripts.bikerack.story_detail_data import fetch_story_detail
44
+
45
+ enriched = fetch_story_detail(story_id)
46
+ except Exception:
47
+ return ws_data
48
+
49
+ if not enriched:
50
+ return ws_data
51
+
52
+ # Merge: enriched is base, WS data overwrites non-null values
53
+ merged = {**enriched}
54
+ for key, val in ws_data.items():
55
+ if val is not None and val != "" and val != []:
56
+ merged[key] = val
57
+ return merged
58
+
59
+ def compose(self):
60
+ """Compose the dossier layout with header, ACs, workflow, git info.
61
+
62
+ Absent sections are simply omitted (Tufte: absence is information).
63
+ """
64
+ data = self._story_data
65
+
66
+ # Header section
67
+ title = data.get("title", "Unknown Story")
68
+ points = data.get("points", "?")
69
+ status = data.get("status", "unknown")
70
+ jira_key = data.get("jiraKey", "")
71
+ assignee = data.get("assignee", "")
72
+ priority = data.get("priority", "")
73
+
74
+ header_text = Text()
75
+ if jira_key:
76
+ header_text.append(f"{jira_key}", style="bold cyan")
77
+ header_text.append(" ")
78
+ header_text.append(f"{title}", style="bold")
79
+ # Metadata line
80
+ meta_parts = [status]
81
+ if points and points != "?":
82
+ meta_parts.append(f"{points} pts")
83
+ if priority:
84
+ meta_parts.append(priority)
85
+ if assignee:
86
+ from pennyfarthing_scripts.bikerack.sprint_panel import _format_assignee
87
+
88
+ name = _format_assignee(assignee)
89
+ if name:
90
+ meta_parts.append(name)
91
+ separator = " \u00b7 "
92
+ header_text.append(f"\n{separator.join(meta_parts)}", style="dim")
93
+ yield Static(header_text, id="dossier-header")
94
+
95
+ # Context section (only if context files exist)
96
+ has_epic_ctx = data.get("has_epic_context", False)
97
+ has_story_ctx = data.get("has_story_context", False)
98
+ if has_epic_ctx or has_story_ctx:
99
+ ctx_text = Text()
100
+ ctx_text.append("Context\n", style="bold underline")
101
+ if has_epic_ctx:
102
+ ctx_text.append(" \u2713 Epic context\n", style="green")
103
+ else:
104
+ ctx_text.append(" \u25cb Epic context\n", style="dim")
105
+ if has_story_ctx:
106
+ ctx_text.append(" \u2713 Story context\n", style="green")
107
+ else:
108
+ ctx_text.append(" \u25cb Story context\n", style="dim")
109
+ yield Static(ctx_text, id="dossier-context")
110
+
111
+ # Acceptance Criteria section
112
+ acs = data.get("acceptance_criteria", [])
113
+ if acs:
114
+ ac_text = Text()
115
+ done_count = sum(1 for ac in acs if ac.get("done"))
116
+ ac_text.append("Acceptance Criteria", style="bold underline")
117
+ ac_text.append(f" {done_count}/{len(acs)}\n", style="dim")
118
+ for ac in acs:
119
+ check = "\u2713" if ac.get("done") else "\u25cb"
120
+ style = "green" if ac.get("done") else ""
121
+ ac_text.append(f" {check} {ac.get('text', '')}\n", style=style)
122
+ yield Static(ac_text, id="dossier-ac")
123
+
124
+ # Workflow section
125
+ workflow = data.get("workflow", "")
126
+ phase = data.get("workflow_phase", "")
127
+ if workflow:
128
+ wf_text = Text()
129
+ wf_text.append("Workflow\n", style="bold underline")
130
+ wf_text.append(f" {workflow}", style="cyan")
131
+ if phase:
132
+ wf_text.append(f" Phase: {phase}", style="yellow")
133
+ wf_text.append("\n")
134
+ yield Static(wf_text, id="dossier-workflow")
135
+
136
+ # Git info section
137
+ branch = data.get("git_branch", "")
138
+ pr_url = data.get("pr_url")
139
+ if branch or pr_url:
140
+ git_text = Text()
141
+ git_text.append("Git\n", style="bold underline")
142
+ if branch:
143
+ git_text.append(f" Branch: {branch}\n")
144
+ if pr_url:
145
+ git_text.append(f" PR: {pr_url}\n", style="cyan")
146
+ yield Static(git_text, id="dossier-git")
147
+
148
+ # Session notes section
149
+ notes = data.get("session_notes", "")
150
+ if notes:
151
+ notes_text = Text()
152
+ notes_text.append("Session Notes\n", style="bold underline")
153
+ notes_text.append(f" {notes}\n")
154
+ yield Static(notes_text, id="dossier-notes")
155
+
156
+ # Keybinding hint
157
+ hint = Text.from_markup(
158
+ "\n[dim][Escape] Back [Enter] Open PR[/dim]"
159
+ )
160
+ yield Static(hint, id="dossier-hint")
161
+
162
+ def action_pop_screen(self) -> None:
163
+ """Pop this screen off the stack."""
164
+ self.dismiss()
165
+
166
+ def get_pr_url(self) -> str | None:
167
+ """Get the PR URL from story data."""
168
+ url = self._story_data.get("pr_url")
169
+ return url if url else None
170
+
171
+ def action_open_pr_link(self) -> bool:
172
+ """Open PR link in browser via webbrowser.open()."""
173
+ url = self.get_pr_url()
174
+ if not url:
175
+ return False
176
+ webbrowser.open(url)
177
+ return True