@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
@@ -14,13 +14,10 @@ Usage:
14
14
  from __future__ import annotations
15
15
 
16
16
  import json
17
- import math
18
17
  import re
19
18
  import subprocess
20
19
  import sys
21
20
  from dataclasses import dataclass, field
22
- from pathlib import Path
23
-
24
21
 
25
22
  # ── Section Categories ──────────────────────────────────────────────
26
23
 
@@ -204,7 +201,6 @@ def parse_sections(raw_output: str) -> list[Section]:
204
201
  current_section_lines: list[str] = []
205
202
  in_agent_def = False
206
203
  in_behavior_guide = False
207
- in_sidecar = False
208
204
 
209
205
  def _flush_section(end_line: int) -> None:
210
206
  """Emit the current section."""
@@ -245,42 +241,36 @@ def parse_sections(raw_output: str) -> list[Section]:
245
241
  current_section_name = "Workflow State"
246
242
  in_agent_def = False
247
243
  in_behavior_guide = False
248
- in_sidecar = False
249
244
  elif header_text.startswith("Agent Definition"):
250
245
  current_component = "agent_definition"
251
246
  current_section_category = "identity"
252
247
  current_section_name = "Agent Definition"
253
248
  in_agent_def = True
254
249
  in_behavior_guide = False
255
- in_sidecar = False
256
250
  elif header_text.startswith("Persona:"):
257
251
  current_component = "persona"
258
252
  current_section_category = "persona"
259
253
  current_section_name = header_text
260
254
  in_agent_def = False
261
255
  in_behavior_guide = False
262
- in_sidecar = False
263
256
  elif header_text.startswith("Agent Behavior Guide"):
264
257
  current_component = "behavior_guide"
265
258
  current_section_category = "shared"
266
259
  current_section_name = "BG: Preamble"
267
260
  in_agent_def = False
268
261
  in_behavior_guide = True
269
- in_sidecar = False
270
262
  elif header_text.startswith("Sprint Context"):
271
263
  current_component = "sprint_context"
272
264
  current_section_category = "shared"
273
265
  current_section_name = "Sprint Context"
274
266
  in_agent_def = False
275
267
  in_behavior_guide = False
276
- in_sidecar = False
277
268
  elif header_text.startswith("Repos Topology"):
278
269
  current_component = "repos_topology"
279
270
  current_section_category = "shared"
280
271
  current_section_name = "Repos Topology"
281
272
  in_agent_def = False
282
273
  in_behavior_guide = False
283
- in_sidecar = False
284
274
  elif header_text.startswith("Agent Sidecar:"):
285
275
  current_component = "sidecars"
286
276
  current_section_category = "learned"
@@ -288,14 +278,12 @@ def parse_sections(raw_output: str) -> list[Section]:
288
278
  current_section_name = f"Sidecar: {sidecar_file.replace('.md', '').title()}"
289
279
  in_agent_def = False
290
280
  in_behavior_guide = False
291
- in_sidecar = True
292
281
  elif header_text.startswith("Active Session:"):
293
282
  current_component = "session"
294
283
  current_section_category = "routing"
295
284
  current_section_name = "Active Session"
296
285
  in_agent_def = False
297
286
  in_behavior_guide = False
298
- in_sidecar = False
299
287
 
300
288
  current_section_start = i
301
289
  current_section_lines = [line]
@@ -347,7 +335,7 @@ def _pretty_tag(tag: str) -> str:
347
335
  def capture_agent_output(agent_name: str) -> str:
348
336
  """Run pf agent start and capture raw output."""
349
337
  result = subprocess.run(
350
- ["pf", "agent", "start", agent_name],
338
+ [sys.executable, "-m", "pennyfarthing_scripts.cli", "agent", "start", agent_name],
351
339
  capture_output=True,
352
340
  text=True,
353
341
  timeout=30,
@@ -484,10 +472,10 @@ def render_summary(heatmaps: list[AgentHeatmap]) -> str:
484
472
  comp_labels = ["Route", "Ident", "Guard", "Proced", "Refer", "Perso", "Shared", "Learn"]
485
473
 
486
474
  # Find max per category across all agents
487
- cat_maxes: dict[str, int] = {c: 0 for c in components}
475
+ cat_maxes: dict[str, int] = dict.fromkeys(components, 0)
488
476
  agent_cats: dict[str, dict[str, int]] = {}
489
477
  for hm in heatmaps:
490
- agent_cats[hm.agent] = {c: 0 for c in components}
478
+ agent_cats[hm.agent] = dict.fromkeys(components, 0)
491
479
  for s in hm.sections:
492
480
  agent_cats[hm.agent][s.category] = agent_cats[hm.agent].get(s.category, 0) + s.tokens
493
481
  for c in components:
@@ -104,8 +104,9 @@ def parse_session_header(session_path: Path) -> dict[str, Any]:
104
104
 
105
105
  if key == "workflow":
106
106
  result["workflow"] = value.lower()
107
- elif key == "current phase":
107
+ elif key in ("current phase", "phase"):
108
108
  # Extract phase name, handling "(APPROVED)" suffix
109
+ # Matches both "**Current Phase:**" and "**Phase:**"
109
110
  phase_match = re.match(r"(\w+)(?:\s*\(([^)]+)\))?", value)
110
111
  if phase_match:
111
112
  result["phase"] = phase_match.group(1).lower()
@@ -1,305 +1,10 @@
1
- #!/usr/bin/env python3
2
1
  """
3
- XML Schema Validation Hook for PreToolUse.
2
+ Backward-compatibility shim schema validation hook moved to hooks/schema_validation.py.
4
3
 
5
- Validates session, skill, and workflow step files on Write/Edit operations.
6
- Blocks the tool call if validation fails, providing immediate feedback.
7
-
8
- Usage in settings.json:
9
- {
10
- "hooks": {
11
- "PreToolUse": [{
12
- "matcher": "Write|Edit",
13
- "hooks": [{
14
- "type": "command",
15
- "command": "python3 -m pennyfarthing_scripts.schema_validation_hook"
16
- }]
17
- }]
18
- }
19
- }
20
-
21
- Story: XML Schema Migration Tools
4
+ This file will be removed in a future version.
22
5
  """
23
6
 
24
- import re
25
- import sys
26
- from pathlib import Path
27
-
28
- # Add parent directory to path for imports
29
- sys.path.insert(0, str(Path(__file__).parent))
30
-
31
- from hooks import (
32
- HookResponse,
33
- output_hook_response,
34
- read_stdin_json,
35
- )
36
-
37
- # =============================================================================
38
- # File Type Detection
39
- # =============================================================================
40
-
41
-
42
- def is_session_file(file_path: str) -> bool:
43
- """Check if path is a session file."""
44
- return file_path.endswith("-session.md") and ".session/" in file_path
45
-
46
-
47
- def is_skill_file(file_path: str) -> bool:
48
- """Check if path is a skill SKILL.md file."""
49
- return file_path.endswith("/SKILL.md") and "/skills/" in file_path
50
-
51
-
52
- def is_step_file(file_path: str) -> bool:
53
- """Check if path is a workflow step file."""
54
- path = Path(file_path)
55
- return (
56
- path.name.startswith("step-")
57
- and path.name.endswith(".md")
58
- and "/workflows/" in file_path
59
- and "/steps/" in file_path
60
- )
61
-
62
-
63
- def get_file_type(file_path: str) -> str | None:
64
- """Detect file type from path."""
65
- if is_session_file(file_path):
66
- return "session"
67
- elif is_skill_file(file_path):
68
- return "skill"
69
- elif is_step_file(file_path):
70
- return "step"
71
- return None
72
-
73
-
74
- # =============================================================================
75
- # Content-Based Validation (inline to avoid import issues)
76
- # =============================================================================
77
-
78
- # Skill required tags
79
- SKILL_REQUIRED_TAGS = ["run", "output"]
80
-
81
- # Workflow step required tags
82
- STEP_REQUIRED_TAGS = ["purpose", "instructions", "output"]
83
-
84
- # Step meta required fields
85
- STEP_META_FIELDS = ["step", "workflow", "agent", "next"]
86
-
87
-
88
- def _has_tag(content: str, tag: str) -> bool:
89
- """Check if content contains a specific XML tag."""
90
- return f"<{tag}>" in content or f"<{tag} " in content
91
-
92
-
93
- def validate_session_content(content: str) -> list[str]:
94
- """Validate session file content.
95
-
96
- Args:
97
- content: File content to validate
98
-
99
- Returns:
100
- List of error messages (empty if valid)
101
- """
102
- errors = []
103
-
104
- # Check for XML format indicator
105
- if "<session" not in content:
106
- # Old markdown format - warn but don't block during migration
107
- return []
108
-
109
- # Validate <session> root with attributes
110
- if not re.search(r'<session\s+story="[^"]+"', content):
111
- errors.append("Missing story attribute on <session>")
112
-
113
- if not re.search(r'<session[^>]+workflow="[^"]+"', content):
114
- errors.append("Missing workflow attribute on <session>")
115
-
116
- # Validate <meta> section
117
- if not _has_tag(content, "meta"):
118
- errors.append("Missing <meta> section")
119
- else:
120
- if "<jira>" not in content:
121
- errors.append("Missing <jira> in <meta>")
122
- if "<started>" not in content:
123
- errors.append("Missing <started> in <meta>")
124
-
125
- # Validate <status> element
126
- if not _has_tag(content, "status"):
127
- errors.append("Missing <status> element")
128
- else:
129
- if 'phase="' not in content:
130
- errors.append("Missing phase attribute on <status>")
131
-
132
- return errors
133
-
134
-
135
- def validate_skill_content(content: str) -> list[str]:
136
- """Validate skill file content.
137
-
138
- Args:
139
- content: File content to validate
140
-
141
- Returns:
142
- List of error messages (empty if valid)
143
- """
144
- errors = []
145
-
146
- # Check YAML frontmatter
147
- if not content.startswith("---\n"):
148
- errors.append("Missing YAML frontmatter")
149
- else:
150
- parts = content.split("---", 2)
151
- if len(parts) >= 2:
152
- frontmatter = parts[1]
153
- if "name:" not in frontmatter:
154
- errors.append("Missing 'name' in frontmatter")
155
- if "description:" not in frontmatter:
156
- errors.append("Missing 'description' in frontmatter")
157
-
158
- # Check required tags
159
- for tag in SKILL_REQUIRED_TAGS:
160
- if not _has_tag(content, tag):
161
- errors.append(f"Missing <{tag}> tag (required)")
162
-
163
- return errors
164
-
165
-
166
- def validate_step_content(content: str) -> list[str]:
167
- """Validate workflow step file content.
168
-
169
- Args:
170
- content: File content to validate
171
-
172
- Returns:
173
- List of error messages (empty if valid)
174
- """
175
- errors = []
176
-
177
- # Check required tags
178
- for tag in STEP_REQUIRED_TAGS:
179
- if not _has_tag(content, tag):
180
- errors.append(f"Missing <{tag}> tag")
181
-
182
- # Check step-meta fields if tag exists
183
- if _has_tag(content, "step-meta"):
184
- meta_match = re.search(r"<step-meta>(.+?)</step-meta>", content, re.DOTALL)
185
- if meta_match:
186
- meta_content = meta_match.group(1)
187
- for field_name in STEP_META_FIELDS:
188
- if f"{field_name}:" not in meta_content:
189
- errors.append(f"Missing '{field_name}' in step-meta")
190
-
191
- return errors
192
-
193
-
194
- def validate_content(file_path: str, content: str) -> list[str]:
195
- """Validate content based on file type.
196
-
197
- Args:
198
- file_path: Path to file being written
199
- content: Content being written
200
-
201
- Returns:
202
- List of error messages (empty if valid)
203
- """
204
- file_type = get_file_type(file_path)
205
-
206
- if file_type == "session":
207
- return validate_session_content(content)
208
- elif file_type == "skill":
209
- return validate_skill_content(content)
210
- elif file_type == "step":
211
- return validate_step_content(content)
212
-
213
- return []
214
-
215
-
216
- # =============================================================================
217
- # Hook Entry Point
218
- # =============================================================================
219
-
220
-
221
- def main() -> None:
222
- """Main entry point for schema validation hook."""
223
- try:
224
- # Read tool data from Claude Code
225
- tool_data = read_stdin_json()
226
-
227
- tool_name = tool_data.get("tool_name", "")
228
- tool_input = tool_data.get("tool_input", {})
229
-
230
- # Only process Write and Edit tools
231
- if tool_name not in ("Write", "Edit"):
232
- output_hook_response(HookResponse(
233
- event_name="PreToolUse",
234
- decision="allow",
235
- reason="Not a Write/Edit operation",
236
- ))
237
- sys.exit(0)
238
-
239
- # Get file path
240
- file_path = tool_input.get("file_path", "")
241
- if not file_path:
242
- output_hook_response(HookResponse(
243
- event_name="PreToolUse",
244
- decision="allow",
245
- reason="No file path",
246
- ))
247
- sys.exit(0)
248
-
249
- # Check if this is a file we care about
250
- file_type = get_file_type(file_path)
251
- if not file_type:
252
- output_hook_response(HookResponse(
253
- event_name="PreToolUse",
254
- decision="allow",
255
- reason="Not a session/skill/step file",
256
- ))
257
- sys.exit(0)
258
-
259
- # Get content to validate
260
- # For Write: use 'content'
261
- # For Edit: we need to simulate the edit result
262
- if tool_name == "Write":
263
- content = tool_input.get("content", "")
264
- else:
265
- # Edit operation - we can't easily validate without reading the file
266
- # For now, allow Edits and validate on Write only
267
- output_hook_response(HookResponse(
268
- event_name="PreToolUse",
269
- decision="allow",
270
- reason="Edit operations validated post-hoc",
271
- ))
272
- sys.exit(0)
273
-
274
- # Validate content
275
- errors = validate_content(file_path, content)
276
-
277
- if errors:
278
- # Block the write
279
- error_msg = f"Schema validation failed for {file_type} file:\n"
280
- error_msg += "\n".join(f" - {e}" for e in errors)
281
- error_msg += f"\n\nFile: {file_path}"
282
-
283
- output_hook_response(HookResponse(
284
- event_name="PreToolUse",
285
- decision="deny",
286
- reason=error_msg,
287
- ))
288
- sys.exit(0)
289
-
290
- # Validation passed
291
- output_hook_response(HookResponse(
292
- event_name="PreToolUse",
293
- decision="allow",
294
- reason=f"Schema validation passed for {file_type} file",
295
- ))
296
- sys.exit(0)
297
-
298
- except Exception as e:
299
- # On error, allow to avoid blocking legitimate work
300
- print(f"[schema-validation-hook] Error: {e}", file=sys.stderr)
301
- sys.exit(0)
302
-
7
+ from pennyfarthing_scripts.hooks.schema_validation import main # noqa: F401
303
8
 
304
9
  if __name__ == "__main__":
305
10
  main()
@@ -1,192 +1,10 @@
1
- #!/usr/bin/env python3
2
- """Session start hook — initialize environment for Claude Code session.
3
-
4
- Handles:
5
- 1. Session directory setup and logging
6
- 2. Checkpoint validation (cross-session drift detection)
7
- 3. WheelHub auto-start (ensure BikeRack server is running)
8
- 4. OTEL auto-configuration via CLAUDE_ENV_FILE
9
-
10
- Called by Claude Code SessionStart hook via shell wrapper.
11
1
  """
2
+ Backward-compatibility shim — session start hook moved to hooks/session_start.py.
12
3
 
13
- from __future__ import annotations
14
-
15
- import json
16
- import os
17
- import sys
18
- from pathlib import Path
19
-
20
-
21
- def _read_input() -> dict:
22
- """Read JSON from stdin (hook protocol)."""
23
- raw = sys.stdin.read()
24
- try:
25
- return json.loads(raw)
26
- except (json.JSONDecodeError, ValueError):
27
- return {}
28
-
29
-
30
- def _setup_session_dir(project_dir: Path, session_id: str, source_type: str) -> None:
31
- """Ensure .session directory exists and log session start."""
32
- session_dir = project_dir / ".session"
33
- session_dir.mkdir(parents=True, exist_ok=True)
34
- (session_dir / "agents").mkdir(exist_ok=True)
35
-
36
- from datetime import UTC, datetime
37
-
38
- timestamp = datetime.now(UTC).isoformat()
39
- log_file = session_dir / "session-log.txt"
40
- with open(log_file, "a") as f:
41
- f.write(f"{timestamp} | Session {source_type}: {session_id}\n")
42
-
43
-
44
- def _validate_checkpoint(project_dir: Path) -> None:
45
- """Validate previous session checkpoint for cross-session drift detection."""
46
- try:
47
- import subprocess
48
-
49
- script_dir = Path(__file__).resolve().parent.parent
50
- checkpoint_lib = script_dir / "pennyfarthing-dist" / "scripts" / "lib" / "checkpoint.sh"
51
-
52
- if not checkpoint_lib.exists():
53
- return
54
-
55
- result = subprocess.run(
56
- ["bash", "-c", f'source "{checkpoint_lib}" && checkpoint_restore session_state'],
57
- capture_output=True,
58
- text=True,
59
- cwd=str(project_dir),
60
- timeout=5,
61
- )
62
-
63
- prev_state = result.stdout.strip()
64
- if not prev_state:
65
- return
66
-
67
- # Parse checkpoint data
68
- fields = {}
69
- for part in prev_state.split(";"):
70
- if "=" in part:
71
- k, v = part.split("=", 1)
72
- fields[k] = v
73
-
74
- prev_sha = fields.get("sha", "")
75
- if not prev_sha:
76
- return
77
-
78
- # Get current git SHA
79
- result = subprocess.run(
80
- ["git", "rev-parse", "--short", "HEAD"],
81
- capture_output=True,
82
- text=True,
83
- cwd=str(project_dir),
84
- timeout=5,
85
- )
86
- current_sha = result.stdout.strip()
87
- if not current_sha or current_sha == prev_sha:
88
- return
89
-
90
- # Log drift
91
- from datetime import UTC, datetime
92
-
93
- timestamp = datetime.now(UTC).isoformat()
94
- session_dir = project_dir / ".session"
95
-
96
- warning = f"CROSS_SESSION_DRIFT: Git changed (was: {prev_sha}, now: {current_sha})"
97
- if fields.get("story"):
98
- warning += f" | Story: {fields['story']}"
99
- if fields.get("agent"):
100
- warning += f" | Agent: {fields['agent']}"
101
-
102
- with open(session_dir / "session-log.txt", "a") as f:
103
- f.write(f"{timestamp} | {warning}\n")
104
-
105
- drift_entry = f"{timestamp} | prev_sha={prev_sha} | current_sha={current_sha}"
106
- for key in ("story", "agent", "phase"):
107
- if fields.get(key):
108
- drift_entry += f" | {key}={fields[key]}"
109
- with open(session_dir / "drift-log.txt", "a") as f:
110
- f.write(drift_entry + "\n")
111
-
112
- except Exception:
113
- pass
114
-
115
-
116
- def _ensure_wheelhub(project_dir: Path) -> int | None:
117
- """Auto-start WheelHub if not already running. Returns port or None."""
118
- from pennyfarthing_scripts.bikerack.launcher import (
119
- is_already_running,
120
- poll_for_port_file,
121
- start_wheelhub,
122
- write_pid_file,
123
- )
124
-
125
- # Skip if full Cyclist is running
126
- cyclist_port_file = project_dir / ".wheelhub-port"
127
- if cyclist_port_file.exists():
128
- try:
129
- return int(cyclist_port_file.read_text().strip())
130
- except (ValueError, OSError):
131
- return None
132
-
133
- # Check if BikeRack WheelHub is already running
134
- running, _pid, port = is_already_running(project_dir)
135
- if running:
136
- return port
137
-
138
- # Start WheelHub
139
- try:
140
- proc = start_wheelhub(project_dir)
141
- write_pid_file(project_dir, proc.pid)
142
- return poll_for_port_file(project_dir)
143
- except Exception:
144
- return None
145
-
146
-
147
- def _write_env_file(project_dir: Path, session_id: str, otel_port: int | None) -> None:
148
- """Write environment variables to CLAUDE_ENV_FILE."""
149
- env_file = os.environ.get("CLAUDE_ENV_FILE")
150
- if not env_file:
151
- return
152
-
153
- lines = [
154
- "# Pennyfarthing core environment",
155
- f'export PROJECT_ROOT="{project_dir}"',
156
- f'export SESSION_ID="{session_id}"',
157
- ]
158
-
159
- if otel_port is not None:
160
- lines.extend([
161
- "# OTEL auto-configuration for Cyclist/WheelHub",
162
- 'export OTEL_EXPORTER_OTLP_PROTOCOL="http/json"',
163
- f'export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:{otel_port}"',
164
- ])
165
-
166
- with open(env_file, "a") as f:
167
- f.write("\n".join(lines) + "\n")
168
-
169
-
170
- def main() -> None:
171
- """Entry point for SessionStart hook."""
172
- try:
173
- input_data = _read_input()
174
- session_id = input_data.get("session_id", "unknown")
175
- source_type = input_data.get("source", "unknown")
176
-
177
- project_dir = Path(os.environ.get("CLAUDE_PROJECT_DIR", os.getcwd()))
178
-
179
- _setup_session_dir(project_dir, session_id, source_type)
180
- _validate_checkpoint(project_dir)
181
-
182
- otel_port = _ensure_wheelhub(project_dir)
183
- _write_env_file(project_dir, session_id, otel_port)
184
-
185
- except Exception:
186
- pass
187
-
188
- sys.exit(0)
4
+ This file will be removed in a future version.
5
+ """
189
6
 
7
+ from pennyfarthing_scripts.hooks.session_start import main # noqa: F401
190
8
 
191
9
  if __name__ == "__main__":
192
10
  main()