@pennyfarthing/core 11.2.1 → 11.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (405) hide show
  1. package/README.md +102 -42
  2. package/package.json +1 -1
  3. package/packages/core/dist/cli/commands/doctor-legacy.test.js +2 -2
  4. package/packages/core/dist/cli/commands/doctor-legacy.test.js.map +1 -1
  5. package/packages/core/dist/cli/commands/doctor.d.ts +55 -0
  6. package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
  7. package/packages/core/dist/cli/commands/doctor.js +324 -50
  8. package/packages/core/dist/cli/commands/doctor.js.map +1 -1
  9. package/packages/core/dist/cli/commands/init.d.ts +12 -0
  10. package/packages/core/dist/cli/commands/init.d.ts.map +1 -1
  11. package/packages/core/dist/cli/commands/init.js +45 -0
  12. package/packages/core/dist/cli/commands/init.js.map +1 -1
  13. package/packages/core/dist/cli/commands/pyproject-install.test.d.ts +19 -0
  14. package/packages/core/dist/cli/commands/pyproject-install.test.d.ts.map +1 -0
  15. package/packages/core/dist/cli/commands/pyproject-install.test.js +261 -0
  16. package/packages/core/dist/cli/commands/pyproject-install.test.js.map +1 -0
  17. package/packages/core/dist/cli/commands/stale-artifacts-cleanup.test.d.ts +17 -0
  18. package/packages/core/dist/cli/commands/stale-artifacts-cleanup.test.d.ts.map +1 -0
  19. package/packages/core/dist/cli/commands/stale-artifacts-cleanup.test.js +470 -0
  20. package/packages/core/dist/cli/commands/stale-artifacts-cleanup.test.js.map +1 -0
  21. package/packages/core/dist/cli/commands/update-consolidation.test.js +14 -6
  22. package/packages/core/dist/cli/commands/update-consolidation.test.js.map +1 -1
  23. package/packages/core/dist/cli/commands/update.d.ts.map +1 -1
  24. package/packages/core/dist/cli/commands/update.js +31 -2
  25. package/packages/core/dist/cli/commands/update.js.map +1 -1
  26. package/packages/core/dist/cli/index.js +2 -0
  27. package/packages/core/dist/cli/index.js.map +1 -1
  28. package/packages/core/dist/cli/utils/python.d.ts.map +1 -1
  29. package/packages/core/dist/cli/utils/python.js +11 -0
  30. package/packages/core/dist/cli/utils/python.js.map +1 -1
  31. package/packages/core/dist/cli/utils/settings-hook-migration.test.d.ts +17 -0
  32. package/packages/core/dist/cli/utils/settings-hook-migration.test.d.ts.map +1 -0
  33. package/packages/core/dist/cli/utils/settings-hook-migration.test.js +382 -0
  34. package/packages/core/dist/cli/utils/settings-hook-migration.test.js.map +1 -0
  35. package/packages/core/dist/cli/utils/settings.d.ts +0 -4
  36. package/packages/core/dist/cli/utils/settings.d.ts.map +1 -1
  37. package/packages/core/dist/cli/utils/settings.js +45 -27
  38. package/packages/core/dist/cli/utils/settings.js.map +1 -1
  39. package/packages/core/dist/cli/utils/stale-artifacts.d.ts +59 -0
  40. package/packages/core/dist/cli/utils/stale-artifacts.d.ts.map +1 -0
  41. package/packages/core/dist/cli/utils/stale-artifacts.js +163 -0
  42. package/packages/core/dist/cli/utils/stale-artifacts.js.map +1 -0
  43. package/packages/core/dist/consultation/dialogue-manager.d.ts +1 -1
  44. package/packages/core/dist/consultation/dialogue-manager.d.ts.map +1 -1
  45. package/packages/core/dist/consultation/dialogue-manager.js +1 -1
  46. package/packages/core/dist/consultation/dialogue-manager.js.map +1 -1
  47. package/packages/core/dist/consultation/dialogue-manager.test.js.map +1 -1
  48. package/packages/core/dist/consultation/tandem-metrics.test.js.map +1 -1
  49. package/packages/core/dist/public/css/react.css +1 -1
  50. package/packages/core/dist/public/js/react/react.js +9 -9
  51. package/packages/core/dist/server/api/git.d.ts.map +1 -1
  52. package/packages/core/dist/server/api/git.js +0 -1
  53. package/packages/core/dist/server/api/git.js.map +1 -1
  54. package/packages/core/dist/server/api/index.d.ts +2 -0
  55. package/packages/core/dist/server/api/index.d.ts.map +1 -1
  56. package/packages/core/dist/server/api/index.js +2 -0
  57. package/packages/core/dist/server/api/index.js.map +1 -1
  58. package/packages/core/dist/server/api/project-info.d.ts +11 -0
  59. package/packages/core/dist/server/api/project-info.d.ts.map +1 -0
  60. package/packages/core/dist/server/api/project-info.js +18 -0
  61. package/packages/core/dist/server/api/project-info.js.map +1 -0
  62. package/packages/core/dist/server/otlp-receiver.d.ts.map +1 -1
  63. package/packages/core/dist/server/otlp-receiver.js +18 -1
  64. package/packages/core/dist/server/otlp-receiver.js.map +1 -1
  65. package/packages/core/dist/server/otlp-receiver.test.js +1 -1
  66. package/packages/core/dist/server/otlp-receiver.test.js.map +1 -1
  67. package/packages/core/dist/server/server.d.ts.map +1 -1
  68. package/packages/core/dist/server/server.js +3 -2
  69. package/packages/core/dist/server/server.js.map +1 -1
  70. package/packages/core/dist/server/server.test.d.ts +1 -1
  71. package/packages/core/dist/server/server.test.js +8 -8
  72. package/packages/core/dist/server/settings.d.ts +1 -0
  73. package/packages/core/dist/server/settings.d.ts.map +1 -1
  74. package/packages/core/dist/server/settings.js +18 -0
  75. package/packages/core/dist/server/settings.js.map +1 -1
  76. package/packages/core/dist/workflow/tandem-workflow-templates.test.js +7 -5
  77. package/packages/core/dist/workflow/tandem-workflow-templates.test.js.map +1 -1
  78. package/packages/core/dist/workflow/workflow-migration.test.js +6 -5
  79. package/packages/core/dist/workflow/workflow-migration.test.js.map +1 -1
  80. package/packages/core/dist/workflow/workflow-team-templates.test.d.ts +17 -0
  81. package/packages/core/dist/workflow/workflow-team-templates.test.d.ts.map +1 -0
  82. package/packages/core/dist/workflow/workflow-team-templates.test.js +275 -0
  83. package/packages/core/dist/workflow/workflow-team-templates.test.js.map +1 -0
  84. package/pennyfarthing-dist/agents/dev.md +19 -4
  85. package/pennyfarthing-dist/agents/devops.md +2 -10
  86. package/pennyfarthing-dist/agents/reviewer-preflight.md +4 -5
  87. package/pennyfarthing-dist/agents/reviewer.md +17 -4
  88. package/pennyfarthing-dist/agents/sm-finish.md +1 -1
  89. package/pennyfarthing-dist/agents/sm-setup.md +7 -7
  90. package/pennyfarthing-dist/agents/sm.md +16 -29
  91. package/pennyfarthing-dist/agents/tea.md +2 -2
  92. package/pennyfarthing-dist/agents/testing-runner.md +1 -1
  93. package/pennyfarthing-dist/commands/pf-architect.md +1 -1
  94. package/pennyfarthing-dist/commands/pf-ba.md +1 -1
  95. package/pennyfarthing-dist/commands/pf-chore.md +2 -2
  96. package/pennyfarthing-dist/commands/pf-dev.md +1 -1
  97. package/pennyfarthing-dist/commands/pf-devops.md +1 -1
  98. package/pennyfarthing-dist/commands/pf-epic.md +6 -6
  99. package/pennyfarthing-dist/commands/pf-git.md +10 -10
  100. package/pennyfarthing-dist/commands/pf-health-check.md +31 -12
  101. package/pennyfarthing-dist/commands/pf-help.md +12 -12
  102. package/pennyfarthing-dist/commands/pf-orchestrator.md +1 -1
  103. package/pennyfarthing-dist/commands/pf-pm.md +1 -1
  104. package/pennyfarthing-dist/commands/pf-prime.md +8 -8
  105. package/pennyfarthing-dist/commands/pf-reviewer.md +1 -1
  106. package/pennyfarthing-dist/commands/pf-session.md +7 -7
  107. package/pennyfarthing-dist/commands/pf-sm.md +1 -1
  108. package/pennyfarthing-dist/commands/pf-sprint.md +7 -7
  109. package/pennyfarthing-dist/commands/pf-tea.md +1 -1
  110. package/pennyfarthing-dist/commands/pf-tech-writer.md +1 -1
  111. package/pennyfarthing-dist/commands/pf-theme.md +9 -9
  112. package/pennyfarthing-dist/commands/pf-ux-designer.md +1 -1
  113. package/pennyfarthing-dist/commands/pf-work.md +1 -1
  114. package/pennyfarthing-dist/gates/{confidence-sm.md → confidence.md} +16 -17
  115. package/pennyfarthing-dist/gates/dev-exit.md +75 -0
  116. package/pennyfarthing-dist/gates/merge-ready.md +49 -0
  117. package/pennyfarthing-dist/gates/release-ready.md +95 -0
  118. package/pennyfarthing-dist/gates/reviewer-preflight-check.md +90 -0
  119. package/pennyfarthing-dist/gates/sm-setup-exit.md +82 -0
  120. package/pennyfarthing-dist/guides/agent-behavior.md +129 -20
  121. package/pennyfarthing-dist/guides/agent-coordination.md +10 -10
  122. package/pennyfarthing-dist/guides/agent-tag-taxonomy.md +6 -6
  123. package/pennyfarthing-dist/guides/agent-template-tactical.md +1 -1
  124. package/pennyfarthing-dist/guides/bell-mode.md +1 -1
  125. package/pennyfarthing-dist/guides/bikerack.md +10 -10
  126. package/pennyfarthing-dist/guides/brownfield-tools.md +24 -24
  127. package/pennyfarthing-dist/guides/command-tag-taxonomy.md +1 -1
  128. package/pennyfarthing-dist/guides/gate-schema.md +2 -2
  129. package/pennyfarthing-dist/guides/gates.md +10 -5
  130. package/pennyfarthing-dist/guides/handoff-cli.md +8 -8
  131. package/pennyfarthing-dist/guides/hooks.md +27 -27
  132. package/pennyfarthing-dist/guides/prime.md +2 -2
  133. package/pennyfarthing-dist/guides/reflector.md +1 -1
  134. package/pennyfarthing-dist/guides/skill-schema.md +6 -6
  135. package/pennyfarthing-dist/guides/tandem-protocol.md +3 -3
  136. package/pennyfarthing-dist/guides/workflow-schema.md +1 -1
  137. package/pennyfarthing-dist/guides/worktree-mode.md +3 -3
  138. package/pennyfarthing-dist/guides/xml-tags.md +8 -8
  139. package/pennyfarthing-dist/scripts/README.md +4 -4
  140. package/pennyfarthing-dist/scripts/core/agent-session.sh +2 -5
  141. package/pennyfarthing-dist/scripts/core/check-context.sh +1 -1
  142. package/pennyfarthing-dist/scripts/core/pf.sh +5 -0
  143. package/pennyfarthing-dist/scripts/core/phase-check-start.sh +2 -5
  144. package/pennyfarthing-dist/scripts/core/prime.sh +2 -25
  145. package/pennyfarthing-dist/scripts/git/README.md +14 -14
  146. package/pennyfarthing-dist/scripts/git/create-feature-branches.sh +2 -3
  147. package/pennyfarthing-dist/scripts/git/git-status-all.sh +2 -3
  148. package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +2 -3
  149. package/pennyfarthing-dist/scripts/git/worktree-manager.sh +2 -4
  150. package/pennyfarthing-dist/scripts/hooks/README.md +6 -6
  151. package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +3 -3
  152. package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +3 -3
  153. package/pennyfarthing-dist/scripts/hooks/context-warning.sh +3 -3
  154. package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +3 -3
  155. package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +5 -4
  156. package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +2 -1
  157. package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +3 -3
  158. package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +3 -3
  159. package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +3 -3
  160. package/pennyfarthing-dist/scripts/hooks/session-start.sh +3 -3
  161. package/pennyfarthing-dist/scripts/hooks/session-stop.sh +3 -3
  162. package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +3 -3
  163. package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +3 -4
  164. package/pennyfarthing-dist/scripts/lib/env.sh +34 -0
  165. package/pennyfarthing-dist/scripts/lib/find-root.sh +5 -0
  166. package/pennyfarthing-dist/scripts/lib/run-pf.sh +43 -0
  167. package/pennyfarthing-dist/scripts/misc/README.md +1 -1
  168. package/pennyfarthing-dist/scripts/misc/statusline.sh +3 -3
  169. package/pennyfarthing-dist/scripts/sprint/README.md +21 -21
  170. package/pennyfarthing-dist/scripts/workflow/README.md +2 -2
  171. package/pennyfarthing-dist/scripts/workflow/finish-story.sh +2 -16
  172. package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +3 -3
  173. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +3 -3
  174. package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +3 -3
  175. package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +3 -3
  176. package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +3 -3
  177. package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +3 -3
  178. package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +3 -3
  179. package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +3 -3
  180. package/pennyfarthing-dist/skills/pf-bc/examples.md +23 -23
  181. package/pennyfarthing-dist/skills/pf-bc/skill.md +17 -17
  182. package/pennyfarthing-dist/skills/pf-bc/usage.md +8 -8
  183. package/pennyfarthing-dist/skills/pf-jira/SKILL.md +15 -15
  184. package/pennyfarthing-dist/skills/pf-jira/examples.md +48 -48
  185. package/pennyfarthing-dist/skills/pf-jira/usage.md +15 -15
  186. package/pennyfarthing-dist/skills/pf-settings/skill.md +42 -0
  187. package/pennyfarthing-dist/skills/pf-sprint/examples.md +80 -80
  188. package/pennyfarthing-dist/skills/pf-sprint/skill.md +35 -35
  189. package/pennyfarthing-dist/skills/pf-sprint/usage.md +30 -30
  190. package/pennyfarthing-dist/skills/pf-theme/examples.md +15 -15
  191. package/pennyfarthing-dist/skills/pf-theme/skill.md +6 -6
  192. package/pennyfarthing-dist/skills/pf-theme/usage.md +5 -5
  193. package/pennyfarthing-dist/skills/pf-workflow/examples.md +27 -27
  194. package/pennyfarthing-dist/skills/pf-workflow/skill.md +11 -11
  195. package/pennyfarthing-dist/skills/pf-workflow/usage.md +11 -11
  196. package/pennyfarthing-dist/skills/skill-registry.yaml +34 -19
  197. package/pennyfarthing-dist/templates/pyproject.toml +27 -0
  198. package/pennyfarthing-dist/templates/settings.local.json.template +11 -11
  199. package/pennyfarthing-dist/workflows/bdd-tandem.yaml +7 -3
  200. package/pennyfarthing-dist/workflows/bdd-team.yaml +89 -0
  201. package/pennyfarthing-dist/workflows/bdd.yaml +7 -3
  202. package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-05-import-to-future.md +1 -1
  203. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-01-analyze.md +1 -1
  204. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-04-verify.md +1 -1
  205. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +1 -1
  206. package/pennyfarthing-dist/workflows/installation-check/steps/step-01-foundation.md +77 -0
  207. package/pennyfarthing-dist/workflows/installation-check/steps/step-02-commands.md +82 -0
  208. package/pennyfarthing-dist/workflows/installation-check/steps/step-03-hooks.md +121 -0
  209. package/pennyfarthing-dist/workflows/installation-check/steps/step-04-scripts.md +83 -0
  210. package/pennyfarthing-dist/workflows/installation-check/steps/step-05-layout.md +81 -0
  211. package/pennyfarthing-dist/workflows/installation-check/steps/step-06-legacy.md +94 -0
  212. package/pennyfarthing-dist/workflows/installation-check/steps/step-07-tools.md +80 -0
  213. package/pennyfarthing-dist/workflows/installation-check/steps/step-08-summary.md +99 -0
  214. package/pennyfarthing-dist/workflows/installation-check/workflow.yaml +47 -0
  215. package/pennyfarthing-dist/workflows/project-setup/steps/step-01-discover.md +47 -0
  216. package/pennyfarthing-dist/workflows/tdd-tandem.yaml +7 -3
  217. package/pennyfarthing-dist/workflows/tdd-team.yaml +80 -0
  218. package/pennyfarthing-dist/workflows/tdd.yaml +7 -3
  219. package/pennyfarthing-dist/workflows/trivial.yaml +7 -3
  220. package/pennyfarthing_scripts/__init__.py +1 -1
  221. package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  222. package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  223. package/pennyfarthing_scripts/__pycache__/cli.cpython-311.pyc +0 -0
  224. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  225. package/pennyfarthing_scripts/__pycache__/context.cpython-311.pyc +0 -0
  226. package/pennyfarthing_scripts/__pycache__/context.cpython-314.pyc +0 -0
  227. package/pennyfarthing_scripts/bc/__pycache__/__init__.cpython-311.pyc +0 -0
  228. package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-311.pyc +0 -0
  229. package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-314.pyc +0 -0
  230. package/pennyfarthing_scripts/bc/__pycache__/focus.cpython-311.pyc +0 -0
  231. package/pennyfarthing_scripts/bc/__pycache__/focus.cpython-314.pyc +0 -0
  232. package/pennyfarthing_scripts/bc/__pycache__/split.cpython-314.pyc +0 -0
  233. package/pennyfarthing_scripts/bc/cli.py +23 -2
  234. package/pennyfarthing_scripts/bc/focus.py +1 -0
  235. package/pennyfarthing_scripts/bc/split.py +52 -0
  236. package/pennyfarthing_scripts/bellmode_hook.py +2 -5
  237. package/pennyfarthing_scripts/bikerack/__pycache__/__init__.cpython-311.pyc +0 -0
  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__/cli.cpython-311.pyc +0 -0
  243. package/pennyfarthing_scripts/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
  244. package/pennyfarthing_scripts/bikerack/__pycache__/context_meter_footer.cpython-314.pyc +0 -0
  245. package/pennyfarthing_scripts/bikerack/__pycache__/debug_panel.cpython-314.pyc +0 -0
  246. package/pennyfarthing_scripts/bikerack/__pycache__/diffs_panel.cpython-314.pyc +0 -0
  247. package/pennyfarthing_scripts/bikerack/__pycache__/events.cpython-314.pyc +0 -0
  248. package/pennyfarthing_scripts/bikerack/__pycache__/git_panel.cpython-314.pyc +0 -0
  249. package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-311.pyc +0 -0
  250. package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
  251. package/pennyfarthing_scripts/bikerack/__pycache__/portrait_resolver.cpython-314.pyc +0 -0
  252. package/pennyfarthing_scripts/bikerack/__pycache__/progress_panel.cpython-314.pyc +0 -0
  253. package/pennyfarthing_scripts/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
  254. package/pennyfarthing_scripts/bikerack/__pycache__/story_detail_data.cpython-314.pyc +0 -0
  255. package/pennyfarthing_scripts/bikerack/__pycache__/story_detail_screen.cpython-314.pyc +0 -0
  256. package/pennyfarthing_scripts/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
  257. package/pennyfarthing_scripts/bikerack/__pycache__/ws_client.cpython-314.pyc +0 -0
  258. package/pennyfarthing_scripts/bikerack/audit_log_panel.py +48 -6
  259. package/pennyfarthing_scripts/bikerack/context_meter_footer.py +53 -3
  260. package/pennyfarthing_scripts/bikerack/launcher.py +6 -6
  261. package/pennyfarthing_scripts/bikerack/progress_panel.py +0 -1
  262. package/pennyfarthing_scripts/bikerack/sprint_panel.py +1 -1
  263. package/pennyfarthing_scripts/bikerack/story_detail_data.py +4 -1
  264. package/pennyfarthing_scripts/bikerack/story_detail_screen.py +2 -1
  265. package/pennyfarthing_scripts/bikerack/tui.py +214 -10
  266. package/pennyfarthing_scripts/bikerack/ws_client.py +2 -2
  267. package/pennyfarthing_scripts/cli.py +5 -0
  268. package/pennyfarthing_scripts/common/__pycache__/__init__.cpython-311.pyc +0 -0
  269. package/pennyfarthing_scripts/common/__pycache__/config.cpython-311.pyc +0 -0
  270. package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
  271. package/pennyfarthing_scripts/common/__pycache__/output.cpython-311.pyc +0 -0
  272. package/pennyfarthing_scripts/common/__pycache__/pr_config.cpython-314.pyc +0 -0
  273. package/pennyfarthing_scripts/common/config.py +29 -2
  274. package/pennyfarthing_scripts/consultation/__pycache__/__init__.cpython-311.pyc +0 -0
  275. package/pennyfarthing_scripts/consultation/__pycache__/__init__.cpython-314.pyc +0 -0
  276. package/pennyfarthing_scripts/consultation/__pycache__/cli.cpython-311.pyc +0 -0
  277. package/pennyfarthing_scripts/consultation/__pycache__/cli.cpython-314.pyc +0 -0
  278. package/pennyfarthing_scripts/consultation/cli.py +3 -3
  279. package/pennyfarthing_scripts/context.py +3 -3
  280. package/pennyfarthing_scripts/deadcode/__pycache__/__init__.cpython-311.pyc +0 -0
  281. package/pennyfarthing_scripts/deadcode/__pycache__/cli.cpython-311.pyc +0 -0
  282. package/pennyfarthing_scripts/epic/__pycache__/__init__.cpython-311.pyc +0 -0
  283. package/pennyfarthing_scripts/epic/__pycache__/cli.cpython-311.pyc +0 -0
  284. package/pennyfarthing_scripts/git/hooks_installer.py +2 -3
  285. package/pennyfarthing_scripts/git/status_all.py +1 -1
  286. package/pennyfarthing_scripts/git/worktree.py +2 -2
  287. package/pennyfarthing_scripts/git_group/__pycache__/__init__.cpython-311.pyc +0 -0
  288. package/pennyfarthing_scripts/git_group/__pycache__/cli.cpython-311.pyc +0 -0
  289. package/pennyfarthing_scripts/git_group/__pycache__/cli.cpython-314.pyc +0 -0
  290. package/pennyfarthing_scripts/handoff/__pycache__/__init__.cpython-311.pyc +0 -0
  291. package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-311.pyc +0 -0
  292. package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-314.pyc +0 -0
  293. package/pennyfarthing_scripts/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
  294. package/pennyfarthing_scripts/handoff/__pycache__/marker.cpython-314.pyc +0 -0
  295. package/pennyfarthing_scripts/handoff/__pycache__/phase_check.cpython-314.pyc +0 -0
  296. package/pennyfarthing_scripts/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
  297. package/pennyfarthing_scripts/healthscore/__pycache__/__init__.cpython-311.pyc +0 -0
  298. package/pennyfarthing_scripts/healthscore/__pycache__/analyze.cpython-311.pyc +0 -0
  299. package/pennyfarthing_scripts/healthscore/__pycache__/cli.cpython-311.pyc +0 -0
  300. package/pennyfarthing_scripts/healthscore/__pycache__/models.cpython-311.pyc +0 -0
  301. package/pennyfarthing_scripts/hooks/__init__.py +8 -3
  302. package/pennyfarthing_scripts/hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  303. package/pennyfarthing_scripts/hooks/__pycache__/__init__.cpython-314.pyc +0 -0
  304. package/pennyfarthing_scripts/hooks/__pycache__/bell_mode.cpython-311.pyc +0 -0
  305. package/pennyfarthing_scripts/hooks/__pycache__/bell_mode.cpython-314.pyc +0 -0
  306. package/pennyfarthing_scripts/hooks/__pycache__/cli.cpython-311.pyc +0 -0
  307. package/pennyfarthing_scripts/hooks/__pycache__/cli.cpython-314.pyc +0 -0
  308. package/pennyfarthing_scripts/hooks/__pycache__/context_breaker.cpython-311.pyc +0 -0
  309. package/pennyfarthing_scripts/hooks/__pycache__/context_breaker.cpython-314.pyc +0 -0
  310. package/pennyfarthing_scripts/hooks/__pycache__/context_warning.cpython-311.pyc +0 -0
  311. package/pennyfarthing_scripts/hooks/__pycache__/context_warning.cpython-314.pyc +0 -0
  312. package/pennyfarthing_scripts/hooks/__pycache__/cyclist_pretooluse.cpython-311.pyc +0 -0
  313. package/pennyfarthing_scripts/hooks/__pycache__/cyclist_pretooluse.cpython-314.pyc +0 -0
  314. package/pennyfarthing_scripts/hooks/__pycache__/pre_edit_check.cpython-311.pyc +0 -0
  315. package/pennyfarthing_scripts/hooks/__pycache__/pre_edit_check.cpython-314.pyc +0 -0
  316. package/pennyfarthing_scripts/hooks/__pycache__/reflector_check.cpython-311.pyc +0 -0
  317. package/pennyfarthing_scripts/hooks/__pycache__/reflector_check.cpython-314.pyc +0 -0
  318. package/pennyfarthing_scripts/hooks/__pycache__/schema_validation.cpython-311.pyc +0 -0
  319. package/pennyfarthing_scripts/hooks/__pycache__/schema_validation.cpython-314.pyc +0 -0
  320. package/pennyfarthing_scripts/hooks/__pycache__/session_start.cpython-311.pyc +0 -0
  321. package/pennyfarthing_scripts/hooks/__pycache__/session_start.cpython-314.pyc +0 -0
  322. package/pennyfarthing_scripts/hooks/__pycache__/session_stop.cpython-314.pyc +0 -0
  323. package/pennyfarthing_scripts/hooks/__pycache__/sprint_yaml_validation.cpython-311.pyc +0 -0
  324. package/pennyfarthing_scripts/hooks/__pycache__/sprint_yaml_validation.cpython-314.pyc +0 -0
  325. package/pennyfarthing_scripts/hooks/__pycache__/statusline.cpython-311.pyc +0 -0
  326. package/pennyfarthing_scripts/hooks/__pycache__/statusline.cpython-314.pyc +0 -0
  327. package/pennyfarthing_scripts/hooks/bell_mode.py +0 -1
  328. package/pennyfarthing_scripts/hooks/pre_edit_check.py +0 -1
  329. package/pennyfarthing_scripts/hooks/reflector_check.py +1 -2
  330. package/pennyfarthing_scripts/hooks/schema_validation.py +0 -1
  331. package/pennyfarthing_scripts/hooks/session_start.py +6 -8
  332. package/pennyfarthing_scripts/hooks/statusline.py +10 -1
  333. package/pennyfarthing_scripts/hotspots/__pycache__/__init__.cpython-311.pyc +0 -0
  334. package/pennyfarthing_scripts/hotspots/__pycache__/analyze.cpython-311.pyc +0 -0
  335. package/pennyfarthing_scripts/hotspots/__pycache__/cli.cpython-311.pyc +0 -0
  336. package/pennyfarthing_scripts/hotspots/__pycache__/models.cpython-311.pyc +0 -0
  337. package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-311.pyc +0 -0
  338. package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-311.pyc +0 -0
  339. package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-311.pyc +0 -0
  340. package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-311.pyc +0 -0
  341. package/pennyfarthing_scripts/jira/__pycache__/client.cpython-311.pyc +0 -0
  342. package/pennyfarthing_scripts/jira/__pycache__/create.cpython-311.pyc +0 -0
  343. package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-311.pyc +0 -0
  344. package/pennyfarthing_scripts/jira/__pycache__/operations.cpython-311.pyc +0 -0
  345. package/pennyfarthing_scripts/jira/__pycache__/reconcile.cpython-311.pyc +0 -0
  346. package/pennyfarthing_scripts/jira/__pycache__/story.cpython-311.pyc +0 -0
  347. package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-311.pyc +0 -0
  348. package/pennyfarthing_scripts/launch/__pycache__/__init__.cpython-311.pyc +0 -0
  349. package/pennyfarthing_scripts/launch/__pycache__/cli.cpython-311.pyc +0 -0
  350. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  351. package/pennyfarthing_scripts/prime/heatmap.py +3 -15
  352. package/pennyfarthing_scripts/session/__pycache__/__init__.cpython-311.pyc +0 -0
  353. package/pennyfarthing_scripts/session/__pycache__/cli.cpython-311.pyc +0 -0
  354. package/pennyfarthing_scripts/settings/__init__.py +0 -0
  355. package/pennyfarthing_scripts/settings/__pycache__/__init__.cpython-314.pyc +0 -0
  356. package/pennyfarthing_scripts/settings/__pycache__/cli.cpython-314.pyc +0 -0
  357. package/pennyfarthing_scripts/settings/__pycache__/settings.cpython-314.pyc +0 -0
  358. package/pennyfarthing_scripts/settings/cli.py +55 -0
  359. package/pennyfarthing_scripts/settings/settings.py +98 -0
  360. package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-311.pyc +0 -0
  361. package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-311.pyc +0 -0
  362. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-311.pyc +0 -0
  363. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  364. package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-311.pyc +0 -0
  365. package/pennyfarthing_scripts/sprint/__pycache__/epic_update.cpython-311.pyc +0 -0
  366. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-311.pyc +0 -0
  367. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  368. package/pennyfarthing_scripts/sprint/__pycache__/status.cpython-311.pyc +0 -0
  369. package/pennyfarthing_scripts/sprint/__pycache__/story_add.cpython-311.pyc +0 -0
  370. package/pennyfarthing_scripts/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
  371. package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-311.pyc +0 -0
  372. package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
  373. package/pennyfarthing_scripts/sprint/__pycache__/validate_cmd.cpython-311.pyc +0 -0
  374. package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-311.pyc +0 -0
  375. package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-311.pyc +0 -0
  376. package/pennyfarthing_scripts/sprint/__pycache__/yaml_io.cpython-311.pyc +0 -0
  377. package/pennyfarthing_scripts/sprint/cli.py +121 -0
  378. package/pennyfarthing_scripts/sprint/loader.py +154 -3
  379. package/pennyfarthing_scripts/sprint/story_update.py +7 -0
  380. package/pennyfarthing_scripts/tests/__pycache__/test_bikerack.cpython-314-pytest-9.0.2.pyc +0 -0
  381. package/pennyfarthing_scripts/tests/__pycache__/test_tui_focus.cpython-314-pytest-9.0.2.pyc +0 -0
  382. package/pennyfarthing_scripts/tests/__pycache__/test_tui_panel_persistence.cpython-314-pytest-9.0.2.pyc +0 -0
  383. package/pennyfarthing_scripts/tests/test_bikerack.py +26 -26
  384. package/pennyfarthing_scripts/tests/test_confidence_sm_gate.py +17 -16
  385. package/pennyfarthing_scripts/tests/test_dialogue_manager.py +0 -1
  386. package/pennyfarthing_scripts/tests/test_resolve_gate_file_field.py +45 -47
  387. package/pennyfarthing_scripts/tests/test_workflow_list_team.py +143 -0
  388. package/pennyfarthing_scripts/theme/__pycache__/__init__.cpython-311.pyc +0 -0
  389. package/pennyfarthing_scripts/theme/__pycache__/cli.cpython-311.pyc +0 -0
  390. package/pennyfarthing_scripts/validate/__pycache__/__init__.cpython-311.pyc +0 -0
  391. package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-311.pyc +0 -0
  392. package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +0 -0
  393. package/pennyfarthing_scripts/validate/adapters/team_mode.py +323 -0
  394. package/pennyfarthing_scripts/workflow/__pycache__/__init__.cpython-311.pyc +0 -0
  395. package/pennyfarthing_scripts/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
  396. package/pennyfarthing_scripts/workflow/__pycache__/cli.cpython-311.pyc +0 -0
  397. package/pennyfarthing_scripts/workflow/__pycache__/cli.cpython-314.pyc +0 -0
  398. package/pennyfarthing_scripts/workflow/__pycache__/helpers.cpython-314.pyc +0 -0
  399. package/pennyfarthing_scripts/workflow/__pycache__/scale.cpython-311.pyc +0 -0
  400. package/pennyfarthing_scripts/workflow/__pycache__/scale.cpython-314.pyc +0 -0
  401. package/pennyfarthing_scripts/workflow/__pycache__/state.cpython-311.pyc +0 -0
  402. package/pennyfarthing_scripts/workflow/__pycache__/state.cpython-314.pyc +0 -0
  403. package/pennyfarthing_scripts/workflow/cli.py +15 -14
  404. package/pennyfarthing_scripts/workflow/state.py +0 -1
  405. package/pennyfarthing_scripts/workflow/team_lifecycle.py +3 -4
@@ -7,11 +7,8 @@ This file will be removed in a future version.
7
7
  # Re-export public API from new location for existing tests/imports
8
8
  from pennyfarthing_scripts.hooks.bell_mode import ( # noqa: F401
9
9
  _check_tandem_files as check_tandem_files,
10
- _get_latest_observation as get_latest_observation,
11
- _get_tandem_mtime as get_tandem_mtime,
12
- _read_bell_queue as read_bell_queue,
13
- _read_tandem_observations as read_tandem_observations,
14
- _save_tandem_mtime as save_tandem_mtime,
10
+ )
11
+ from pennyfarthing_scripts.hooks.bell_mode import (
15
12
  main,
16
13
  )
17
14
 
@@ -8,7 +8,7 @@ Uses native Textual widgets (DataTable) instead of Rich renderables.
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
- from datetime import datetime, timezone
11
+ from datetime import UTC, datetime
12
12
  from typing import Any
13
13
 
14
14
  from rich.console import Console
@@ -19,6 +19,18 @@ from pennyfarthing_scripts.bikerack.base_panel import PANEL_ICONS, BasePanel
19
19
 
20
20
  MAX_INPUT_LENGTH = 80
21
21
 
22
+ # Tool name → Rich style (matches GUI CSS custom properties)
23
+ _TOOL_COLORS: dict[str, str] = {
24
+ "read": "blue",
25
+ "write": "dark_orange",
26
+ "bash": "green",
27
+ "glob": "medium_purple1",
28
+ "grep": "dark_cyan",
29
+ "edit": "yellow",
30
+ "task": "hot_pink",
31
+ "skill": "dim",
32
+ }
33
+
22
34
  # Shared fallback console for offline column/cell measurement
23
35
  _FALLBACK_CONSOLE = Console()
24
36
 
@@ -58,8 +70,9 @@ class AuditLogPanel(BasePanel):
58
70
  def handle_message(self, message: dict[str, Any] | None) -> None:
59
71
  """Handle incoming WebSocket messages for tool events.
60
72
 
61
- Overrides BasePanel.handle_message to manipulate DataTable directly
62
- instead of going through render_panel/post_message.
73
+ Mutates the internal DataTable then delegates to
74
+ super().handle_message() which calls render_panel()
75
+ post_message(DataReceived) for thread-safe Textual repaint.
63
76
  """
64
77
  if not self._mounted or message is None:
65
78
  return
@@ -81,6 +94,11 @@ class AuditLogPanel(BasePanel):
81
94
  span = message.get("span")
82
95
  if isinstance(span, dict):
83
96
  self._add_row(span)
97
+ else:
98
+ return
99
+
100
+ # Trigger repaint through BasePanel's message system
101
+ super().handle_message(message)
84
102
 
85
103
  def _add_row(self, span: dict[str, Any]) -> None:
86
104
  """Add a single tool event row to the DataTable."""
@@ -104,8 +122,32 @@ class AuditLogPanel(BasePanel):
104
122
  self._table.add_row(time_str, tool_name, input_text, result)
105
123
 
106
124
  def render_panel(self, payload: dict[str, Any]) -> Any:
107
- """Not used handle_message manages DataTable directly."""
108
- return self._table
125
+ """Render current DataTable rows as a Rich Table renderable."""
126
+ from rich.table import Table as RichTable
127
+ from rich.text import Text
128
+
129
+ if self._table.row_count == 0:
130
+ return Text("No audit events yet", style="dim italic")
131
+
132
+ rich_table = RichTable(show_header=True, expand=True, box=None)
133
+ rich_table.add_column("Time", style="dim", no_wrap=True)
134
+ rich_table.add_column("Tool", no_wrap=True)
135
+ rich_table.add_column("Input")
136
+ rich_table.add_column("Result", justify="center", no_wrap=True)
137
+
138
+ for row_key in reversed(list(self._table.rows)):
139
+ row_data = self._table.get_row(row_key)
140
+ time_str, tool_name, input_text, result = (str(c) for c in row_data)
141
+ tool_style = _TOOL_COLORS.get(tool_name.lower(), "bold cyan")
142
+ result_style = "green" if result == "\u2713" else "red" if result == "\u2717" else "dim"
143
+ rich_table.add_row(
144
+ time_str,
145
+ Text(tool_name, style=f"bold {tool_style}"),
146
+ Text(input_text, style="dim") if input_text else Text(""),
147
+ Text(result, style=result_style),
148
+ )
149
+
150
+ return rich_table
109
151
 
110
152
 
111
153
  def _format_timestamp(ts: Any) -> str:
@@ -113,7 +155,7 @@ def _format_timestamp(ts: Any) -> str:
113
155
  if ts is None:
114
156
  return "\u2014"
115
157
  try:
116
- dt = datetime.fromtimestamp(float(ts) / 1000, tz=timezone.utc)
158
+ dt = datetime.fromtimestamp(float(ts) / 1000, tz=UTC)
117
159
  return dt.strftime("%H:%M:%S")
118
160
  except (ValueError, TypeError, OSError):
119
161
  return "\u2014"
@@ -4,11 +4,15 @@ Story 110-5: Context meter footer bar. Displays context window usage
4
4
  percentage with color-coded tier thresholds, always visible at the
5
5
  bottom of the layout.
6
6
 
7
+ Story 110-12: Periodic refresh timer, event-driven redraws, and
8
+ throttling for more frequent updates without performance degradation.
9
+
7
10
  Subscribes to /ws/context WebSocket channel.
8
11
  """
9
12
 
10
13
  from __future__ import annotations
11
14
 
15
+ import time
12
16
  from typing import Any
13
17
 
14
18
  from rich.text import Text
@@ -35,34 +39,80 @@ class ContextMeterFooter(Static):
35
39
  #: WebSocket channel this footer subscribes to
36
40
  channel: str = "context"
37
41
 
42
+ #: Seconds between periodic refresh redraws
43
+ refresh_interval: float = 5.0
44
+
45
+ #: Minimum seconds between redraws (throttle)
46
+ min_redraw_interval: float = 0.25
47
+
38
48
  def __init__(self, client: Any = None, **kwargs: Any) -> None:
39
49
  super().__init__(**kwargs)
40
50
  self._client = client
41
51
  self._context_data: dict[str, Any] | None = None
42
52
  self._mounted = False
53
+ self._refresh_timer: Any = None
54
+ self._last_redraw_time: float = 0.0
55
+ self.last_update_time: float = 0.0
56
+
57
+ @property
58
+ def is_stale(self) -> bool:
59
+ """True if no context data has been received."""
60
+ return self._context_data is None
43
61
 
44
62
  def on_mount(self) -> None:
45
- """Subscribe to context channel on mount."""
63
+ """Subscribe to context channel and start periodic refresh on mount."""
46
64
  self._mounted = True
47
65
  if self._client is not None:
48
66
  self._client.subscribe("context", self.handle_context_message)
67
+ self._client.on_state_change(self.on_connection_state_change)
68
+ try:
69
+ self._refresh_timer = self.set_interval(
70
+ self.refresh_interval, self.request_refresh
71
+ )
72
+ except RuntimeError:
73
+ # No running event loop (e.g. unit tests calling on_mount directly)
74
+ pass
49
75
 
50
76
  def on_unmount(self) -> None:
51
- """Mark as unmounted so further messages are ignored."""
77
+ """Stop timer and mark as unmounted so further messages are ignored."""
52
78
  self._mounted = False
79
+ if self._refresh_timer is not None:
80
+ self._refresh_timer.stop()
53
81
 
54
82
  def on_context_meter_footer_meter_update(self, event: MeterUpdate) -> None:
55
83
  """Process MeterUpdate in Textual message context — triggers repaint."""
56
84
  self.update(event.content)
57
85
 
86
+ def request_refresh(self) -> None:
87
+ """Redraw from cached context data. Safe to call at any time."""
88
+ if not self._mounted or self._context_data is None:
89
+ return
90
+ try:
91
+ rendered = self.render_meter(self._context_data)
92
+ self.post_message(self.MeterUpdate(rendered))
93
+ except Exception:
94
+ pass
95
+
96
+ def on_connection_state_change(self, state: Any) -> None:
97
+ """Redraw on WebSocket reconnection."""
98
+ self.request_refresh()
99
+
58
100
  def handle_context_message(self, msg: dict[str, Any] | None) -> None:
59
- """Process incoming /ws/context message."""
101
+ """Process incoming /ws/context message with throttling."""
60
102
  if not self._mounted or msg is None:
61
103
  return
62
104
  ctx = msg.get("context")
63
105
  if ctx is None:
64
106
  return
65
107
  self._context_data = ctx
108
+ self.last_update_time = time.monotonic()
109
+
110
+ # Throttle: skip redraw if too soon after last one
111
+ now = time.monotonic()
112
+ if now - self._last_redraw_time < self.min_redraw_interval:
113
+ return
114
+ self._last_redraw_time = now
115
+
66
116
  try:
67
117
  rendered = self.render_meter(ctx)
68
118
  self.post_message(self.MeterUpdate(rendered))
@@ -24,8 +24,8 @@ def is_process_alive(pid: int) -> bool:
24
24
 
25
25
 
26
26
  def cleanup_files(project_dir: Path) -> None:
27
- """Clean up .wheelhub-port, .wheelhub-pid, and .wheelhub-gui-pid files."""
28
- for name in (".wheelhub-port", ".wheelhub-pid", ".wheelhub-gui-pid"):
27
+ """Clean up .bikerack-port, .wheelhub-pid, and .wheelhub-gui-pid files."""
28
+ for name in (".bikerack-port", ".wheelhub-pid", ".wheelhub-gui-pid"):
29
29
  try:
30
30
  (project_dir / name).unlink()
31
31
  except FileNotFoundError:
@@ -33,9 +33,9 @@ def cleanup_files(project_dir: Path) -> None:
33
33
 
34
34
 
35
35
  def read_port_file(project_dir: Path) -> int | None:
36
- """Read port from .wheelhub-port file. Returns None if not found."""
36
+ """Read port from .bikerack-port file. Returns None if not found."""
37
37
  try:
38
- return int((project_dir / ".wheelhub-port").read_text().strip())
38
+ return int((project_dir / ".bikerack-port").read_text().strip())
39
39
  except (FileNotFoundError, ValueError):
40
40
  return None
41
41
 
@@ -104,8 +104,8 @@ def start_wheelhub(project_dir: Path) -> subprocess.Popen:
104
104
  def poll_for_port_file(
105
105
  project_dir: Path, timeout: float = 5.0, interval: float = 0.1
106
106
  ) -> int:
107
- """Poll for .wheelhub-port file, return port number."""
108
- port_file = project_dir / ".wheelhub-port"
107
+ """Poll for .bikerack-port file, return port number."""
108
+ port_file = project_dir / ".bikerack-port"
109
109
  deadline = time.monotonic() + timeout
110
110
 
111
111
  while True:
@@ -265,7 +265,6 @@ class ProgressPanel(BasePanel):
265
265
  line = Text()
266
266
  line.append("Git ", style="bold")
267
267
 
268
- repo_parts: list[str] = []
269
268
  for repo in repos:
270
269
  if not isinstance(repo, dict):
271
270
  continue
@@ -228,7 +228,7 @@ class SprintPanel(Widget):
228
228
  """Mark unmounted so WS callbacks are ignored."""
229
229
  self._mounted = False
230
230
 
231
- def focus(self, scroll_visible: bool = True) -> "SprintPanel":
231
+ def focus(self, scroll_visible: bool = True) -> SprintPanel:
232
232
  """Delegate focus to the tree widget."""
233
233
  try:
234
234
  tree = self.query_one("#sprint-tree", Tree)
@@ -107,9 +107,12 @@ def _parse_session_file(session_path: str) -> dict[str, Any]:
107
107
 
108
108
  def _get_sprint_story_status(story_id: str) -> str | None:
109
109
  """Try to get story status from pf sprint CLI."""
110
+ import sys
111
+
110
112
  try:
111
113
  out = subprocess.run(
112
- ["pf", "sprint", "story", "field", story_id, "status"],
114
+ [sys.executable, "-m", "pennyfarthing_scripts.cli",
115
+ "sprint", "story", "field", story_id, "status"],
113
116
  capture_output=True,
114
117
  text=True,
115
118
  timeout=10,
@@ -88,7 +88,8 @@ class StoryDetailScreen(Screen):
88
88
  name = _format_assignee(assignee)
89
89
  if name:
90
90
  meta_parts.append(name)
91
- header_text.append(f"\n{' \u00b7 '.join(meta_parts)}", style="dim")
91
+ separator = " \u00b7 "
92
+ header_text.append(f"\n{separator.join(meta_parts)}", style="dim")
92
93
  yield Static(header_text, id="dossier-header")
93
94
 
94
95
  # Context section (only if context files exist)