@pennyfarthing/core 11.2.0 → 11.2.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 (403) hide show
  1. package/README.md +1 -1
  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 +381 -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 +11 -0
  16. package/packages/core/dist/cli/utils/settings.d.ts.map +1 -1
  17. package/packages/core/dist/cli/utils/settings.js +65 -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/tandem-metrics.d.ts +91 -0
  22. package/packages/core/dist/consultation/tandem-metrics.d.ts.map +1 -0
  23. package/packages/core/dist/consultation/tandem-metrics.js +131 -0
  24. package/packages/core/dist/consultation/tandem-metrics.js.map +1 -0
  25. package/packages/core/dist/consultation/tandem-metrics.test.d.ts +18 -0
  26. package/packages/core/dist/consultation/tandem-metrics.test.d.ts.map +1 -0
  27. package/packages/core/dist/consultation/tandem-metrics.test.js +457 -0
  28. package/packages/core/dist/consultation/tandem-metrics.test.js.map +1 -0
  29. package/packages/core/dist/public/js/react/react.js +14 -14
  30. package/packages/core/dist/scripts/benchmark-integration.d.ts +182 -0
  31. package/packages/core/dist/scripts/benchmark-integration.d.ts.map +1 -0
  32. package/packages/core/dist/scripts/benchmark-integration.js +691 -0
  33. package/packages/core/dist/scripts/benchmark-integration.js.map +1 -0
  34. package/packages/core/dist/scripts/job-fair-aggregator.d.ts +150 -0
  35. package/packages/core/dist/scripts/job-fair-aggregator.d.ts.map +1 -0
  36. package/packages/core/dist/scripts/job-fair-aggregator.js +547 -0
  37. package/packages/core/dist/scripts/job-fair-aggregator.js.map +1 -0
  38. package/packages/core/dist/server/api/agent-load.js +1 -1
  39. package/packages/core/dist/server/api/agent-load.js.map +1 -1
  40. package/packages/core/dist/server/server.d.ts +0 -3
  41. package/packages/core/dist/server/server.d.ts.map +1 -1
  42. package/packages/core/dist/server/server.js +3 -37
  43. package/packages/core/dist/server/server.js.map +1 -1
  44. package/packages/core/dist/server/server.test.d.ts +1 -1
  45. package/packages/core/dist/server/server.test.js +12 -23
  46. package/packages/core/dist/server/server.test.js.map +1 -1
  47. package/packages/core/dist/shared/capabilities.d.ts +88 -0
  48. package/packages/core/dist/shared/capabilities.d.ts.map +1 -0
  49. package/packages/core/dist/shared/capabilities.js +133 -0
  50. package/packages/core/dist/shared/capabilities.js.map +1 -0
  51. package/packages/core/dist/shared/capabilities.test.d.ts +2 -0
  52. package/packages/core/dist/shared/capabilities.test.d.ts.map +1 -0
  53. package/packages/core/dist/shared/capabilities.test.js +217 -0
  54. package/packages/core/dist/shared/capabilities.test.js.map +1 -0
  55. package/packages/core/dist/shared/spawn-prompt.d.ts +47 -0
  56. package/packages/core/dist/shared/spawn-prompt.d.ts.map +1 -0
  57. package/packages/core/dist/shared/spawn-prompt.js +82 -0
  58. package/packages/core/dist/shared/spawn-prompt.js.map +1 -0
  59. package/packages/core/dist/shared/spawn-prompt.test.d.ts +2 -0
  60. package/packages/core/dist/shared/spawn-prompt.test.d.ts.map +1 -0
  61. package/packages/core/dist/shared/spawn-prompt.test.js +251 -0
  62. package/packages/core/dist/shared/spawn-prompt.test.js.map +1 -0
  63. package/packages/core/dist/workflow/tandem-workflow-templates.test.d.ts +18 -0
  64. package/packages/core/dist/workflow/tandem-workflow-templates.test.d.ts.map +1 -0
  65. package/packages/core/dist/workflow/tandem-workflow-templates.test.js +434 -0
  66. package/packages/core/dist/workflow/tandem-workflow-templates.test.js.map +1 -0
  67. package/packages/core/dist/workflow/workflow-schema.d.ts +32 -0
  68. package/packages/core/dist/workflow/workflow-schema.d.ts.map +1 -1
  69. package/packages/core/dist/workflow/workflow-schema.js +120 -0
  70. package/packages/core/dist/workflow/workflow-schema.js.map +1 -1
  71. package/packages/core/dist/workflow/workflow-schema.test.d.ts.map +1 -1
  72. package/packages/core/dist/workflow/workflow-schema.test.js +570 -1
  73. package/packages/core/dist/workflow/workflow-schema.test.js.map +1 -1
  74. package/pennyfarthing-dist/agents/dev.md +6 -10
  75. package/pennyfarthing-dist/agents/reviewer.md +8 -2
  76. package/pennyfarthing-dist/agents/sm-finish.md +18 -1
  77. package/pennyfarthing-dist/commands/pf-git.md +4 -2
  78. package/pennyfarthing-dist/gates/approval.md +63 -0
  79. package/pennyfarthing-dist/gates/confidence-sm.md +71 -0
  80. package/pennyfarthing-dist/gates/context-ok.md +56 -0
  81. package/pennyfarthing-dist/gates/evaluations/confidence-sm.md +54 -0
  82. package/pennyfarthing-dist/gates/quality-pass.md +67 -0
  83. package/pennyfarthing-dist/gates/tests-fail.md +84 -0
  84. package/pennyfarthing-dist/gates/tests-pass.md +79 -0
  85. package/pennyfarthing-dist/guides/agent-behavior.md +23 -19
  86. package/pennyfarthing-dist/guides/bell-mode.md +1 -1
  87. package/pennyfarthing-dist/guides/hooks.md +28 -28
  88. package/pennyfarthing-dist/guides/reflector.md +1 -1
  89. package/pennyfarthing-dist/guides/tandem-protocol.md +3 -3
  90. package/pennyfarthing-dist/scripts/core/check-context.sh +2 -0
  91. package/pennyfarthing-dist/scripts/core/phase-check-start.sh +5 -87
  92. package/pennyfarthing-dist/scripts/hooks/README.md +5 -5
  93. package/pennyfarthing-dist/scripts/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
  94. package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +4 -183
  95. package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +4 -95
  96. package/pennyfarthing-dist/scripts/hooks/context-warning.sh +4 -65
  97. package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +3 -31
  98. package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +27 -33
  99. package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +4 -71
  100. package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +3 -19
  101. package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +4 -30
  102. package/pennyfarthing-dist/scripts/hooks/session-start.sh +3 -32
  103. package/pennyfarthing-dist/scripts/hooks/session-stop.sh +4 -65
  104. package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +4 -78
  105. package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +4 -93
  106. package/pennyfarthing-dist/scripts/misc/README.md +1 -1
  107. package/pennyfarthing-dist/scripts/misc/statusline.sh +4 -301
  108. package/pennyfarthing-dist/templates/settings.local.json.template +19 -10
  109. package/pennyfarthing-dist/workflows/tdd.yaml +11 -2
  110. package/pennyfarthing_scripts/CLAUDE.md +19 -10
  111. package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  112. package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  113. package/pennyfarthing_scripts/__pycache__/bellmode_hook.cpython-314.pyc +0 -0
  114. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  115. package/pennyfarthing_scripts/__pycache__/config.cpython-314.pyc +0 -0
  116. package/pennyfarthing_scripts/__pycache__/context.cpython-314.pyc +0 -0
  117. package/pennyfarthing_scripts/__pycache__/hooks.cpython-314.pyc +0 -0
  118. package/pennyfarthing_scripts/__pycache__/jira.cpython-314.pyc +0 -0
  119. package/pennyfarthing_scripts/__pycache__/jira_bidirectional_sync.cpython-314.pyc +0 -0
  120. package/pennyfarthing_scripts/__pycache__/jira_epic_creation.cpython-314.pyc +0 -0
  121. package/pennyfarthing_scripts/__pycache__/jira_sync.cpython-314.pyc +0 -0
  122. package/pennyfarthing_scripts/__pycache__/jira_sync_story.cpython-314.pyc +0 -0
  123. package/pennyfarthing_scripts/__pycache__/output.cpython-314.pyc +0 -0
  124. package/pennyfarthing_scripts/__pycache__/patch_mode.cpython-314.pyc +0 -0
  125. package/pennyfarthing_scripts/__pycache__/pretooluse_hook.cpython-314.pyc +0 -0
  126. package/pennyfarthing_scripts/__pycache__/schema_validation_hook.cpython-314.pyc +0 -0
  127. package/pennyfarthing_scripts/__pycache__/session_start_hook.cpython-314.pyc +0 -0
  128. package/pennyfarthing_scripts/__pycache__/sprint.cpython-314.pyc +0 -0
  129. package/pennyfarthing_scripts/__pycache__/workflow.cpython-311.pyc +0 -0
  130. package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
  131. package/pennyfarthing_scripts/bc/__pycache__/__init__.cpython-314.pyc +0 -0
  132. package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-314.pyc +0 -0
  133. package/pennyfarthing_scripts/bc/__pycache__/focus.cpython-314.pyc +0 -0
  134. package/pennyfarthing_scripts/bellmode_hook.py +12 -296
  135. package/pennyfarthing_scripts/bikerack/__pycache__/__init__.cpython-314.pyc +0 -0
  136. package/pennyfarthing_scripts/bikerack/__pycache__/__main__.cpython-314.pyc +0 -0
  137. package/pennyfarthing_scripts/bikerack/__pycache__/audit_log_panel.cpython-314.pyc +0 -0
  138. package/pennyfarthing_scripts/bikerack/__pycache__/background_panel.cpython-314.pyc +0 -0
  139. package/pennyfarthing_scripts/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
  140. package/pennyfarthing_scripts/bikerack/__pycache__/changed_panel.cpython-314.pyc +0 -0
  141. package/pennyfarthing_scripts/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
  142. package/pennyfarthing_scripts/bikerack/__pycache__/context_meter_footer.cpython-314.pyc +0 -0
  143. package/pennyfarthing_scripts/bikerack/__pycache__/debug_panel.cpython-314.pyc +0 -0
  144. package/pennyfarthing_scripts/bikerack/__pycache__/diffs_panel.cpython-314.pyc +0 -0
  145. package/pennyfarthing_scripts/bikerack/__pycache__/events.cpython-314.pyc +0 -0
  146. package/pennyfarthing_scripts/bikerack/__pycache__/git_panel.cpython-314.pyc +0 -0
  147. package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
  148. package/pennyfarthing_scripts/bikerack/__pycache__/portrait_resolver.cpython-314.pyc +0 -0
  149. package/pennyfarthing_scripts/bikerack/__pycache__/progress_panel.cpython-314.pyc +0 -0
  150. package/pennyfarthing_scripts/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
  151. package/pennyfarthing_scripts/bikerack/__pycache__/story_detail_data.cpython-314.pyc +0 -0
  152. package/pennyfarthing_scripts/bikerack/__pycache__/story_detail_screen.cpython-314.pyc +0 -0
  153. package/pennyfarthing_scripts/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
  154. package/pennyfarthing_scripts/bikerack/__pycache__/ws_client.cpython-314.pyc +0 -0
  155. package/pennyfarthing_scripts/bikerack/audit_log_panel.py +119 -0
  156. package/pennyfarthing_scripts/bikerack/base_panel.py +27 -4
  157. package/pennyfarthing_scripts/bikerack/changed_panel.py +96 -4
  158. package/pennyfarthing_scripts/bikerack/context_meter_footer.py +88 -0
  159. package/pennyfarthing_scripts/bikerack/debug_panel.py +1 -1
  160. package/pennyfarthing_scripts/bikerack/diffs_panel.py +30 -0
  161. package/pennyfarthing_scripts/bikerack/events.py +28 -0
  162. package/pennyfarthing_scripts/bikerack/portrait_resolver.py +139 -0
  163. package/pennyfarthing_scripts/bikerack/sprint_panel.py +373 -142
  164. package/pennyfarthing_scripts/bikerack/story_detail_data.py +244 -0
  165. package/pennyfarthing_scripts/bikerack/story_detail_screen.py +176 -0
  166. package/pennyfarthing_scripts/bikerack/tui.py +293 -61
  167. package/pennyfarthing_scripts/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
  168. package/pennyfarthing_scripts/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
  169. package/pennyfarthing_scripts/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
  170. package/pennyfarthing_scripts/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
  171. package/pennyfarthing_scripts/cli.py +5 -0
  172. package/pennyfarthing_scripts/codemarkers/__pycache__/__init__.cpython-314.pyc +0 -0
  173. package/pennyfarthing_scripts/codemarkers/__pycache__/__main__.cpython-314.pyc +0 -0
  174. package/pennyfarthing_scripts/codemarkers/__pycache__/analyze.cpython-314.pyc +0 -0
  175. package/pennyfarthing_scripts/codemarkers/__pycache__/cli.cpython-314.pyc +0 -0
  176. package/pennyfarthing_scripts/codemarkers/__pycache__/formatters.cpython-314.pyc +0 -0
  177. package/pennyfarthing_scripts/codemarkers/__pycache__/models.cpython-314.pyc +0 -0
  178. package/pennyfarthing_scripts/common/__pycache__/__init__.cpython-314.pyc +0 -0
  179. package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
  180. package/pennyfarthing_scripts/common/__pycache__/output.cpython-314.pyc +0 -0
  181. package/pennyfarthing_scripts/common/__pycache__/themes.cpython-314.pyc +0 -0
  182. package/pennyfarthing_scripts/common/pr_config.py +38 -0
  183. package/pennyfarthing_scripts/complexity/__pycache__/__init__.cpython-314.pyc +0 -0
  184. package/pennyfarthing_scripts/complexity/__pycache__/__main__.cpython-314.pyc +0 -0
  185. package/pennyfarthing_scripts/complexity/__pycache__/analyze.cpython-314.pyc +0 -0
  186. package/pennyfarthing_scripts/complexity/__pycache__/cli.cpython-314.pyc +0 -0
  187. package/pennyfarthing_scripts/complexity/__pycache__/formatters.cpython-314.pyc +0 -0
  188. package/pennyfarthing_scripts/complexity/__pycache__/models.cpython-314.pyc +0 -0
  189. package/pennyfarthing_scripts/consultation/__pycache__/__init__.cpython-314.pyc +0 -0
  190. package/pennyfarthing_scripts/consultation/__pycache__/cli.cpython-314.pyc +0 -0
  191. package/pennyfarthing_scripts/consultation/__pycache__/dialogue_manager.cpython-314.pyc +0 -0
  192. package/pennyfarthing_scripts/deadcode/__pycache__/__init__.cpython-314.pyc +0 -0
  193. package/pennyfarthing_scripts/deadcode/__pycache__/__main__.cpython-314.pyc +0 -0
  194. package/pennyfarthing_scripts/deadcode/__pycache__/analyze.cpython-314.pyc +0 -0
  195. package/pennyfarthing_scripts/deadcode/__pycache__/cli.cpython-314.pyc +0 -0
  196. package/pennyfarthing_scripts/deadcode/__pycache__/formatters.cpython-314.pyc +0 -0
  197. package/pennyfarthing_scripts/deadcode/__pycache__/models.cpython-314.pyc +0 -0
  198. package/pennyfarthing_scripts/dependencies/__pycache__/__init__.cpython-314.pyc +0 -0
  199. package/pennyfarthing_scripts/dependencies/__pycache__/__main__.cpython-314.pyc +0 -0
  200. package/pennyfarthing_scripts/dependencies/__pycache__/analyze.cpython-314.pyc +0 -0
  201. package/pennyfarthing_scripts/dependencies/__pycache__/cli.cpython-314.pyc +0 -0
  202. package/pennyfarthing_scripts/dependencies/__pycache__/formatters.cpython-314.pyc +0 -0
  203. package/pennyfarthing_scripts/dependencies/__pycache__/models.cpython-314.pyc +0 -0
  204. package/pennyfarthing_scripts/epic/__pycache__/__init__.cpython-314.pyc +0 -0
  205. package/pennyfarthing_scripts/epic/__pycache__/cli.cpython-314.pyc +0 -0
  206. package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
  207. package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
  208. package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
  209. package/pennyfarthing_scripts/git_group/__pycache__/__init__.cpython-314.pyc +0 -0
  210. package/pennyfarthing_scripts/git_group/__pycache__/cli.cpython-314.pyc +0 -0
  211. package/pennyfarthing_scripts/handoff/__pycache__/__init__.cpython-314.pyc +0 -0
  212. package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-314.pyc +0 -0
  213. package/pennyfarthing_scripts/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
  214. package/pennyfarthing_scripts/handoff/__pycache__/gate_file.cpython-314.pyc +0 -0
  215. package/pennyfarthing_scripts/handoff/__pycache__/gate_runner.cpython-314.pyc +0 -0
  216. package/pennyfarthing_scripts/handoff/__pycache__/marker.cpython-314.pyc +0 -0
  217. package/pennyfarthing_scripts/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
  218. package/pennyfarthing_scripts/handoff/cli.py +33 -1
  219. package/pennyfarthing_scripts/handoff/complete_phase.py +28 -0
  220. package/pennyfarthing_scripts/handoff/marker.py +15 -15
  221. package/pennyfarthing_scripts/handoff/phase_check.py +96 -0
  222. package/pennyfarthing_scripts/handoff/resolve_gate.py +13 -1
  223. package/pennyfarthing_scripts/healthscore/__pycache__/__init__.cpython-314.pyc +0 -0
  224. package/pennyfarthing_scripts/healthscore/__pycache__/__main__.cpython-314.pyc +0 -0
  225. package/pennyfarthing_scripts/healthscore/__pycache__/analyze.cpython-314.pyc +0 -0
  226. package/pennyfarthing_scripts/healthscore/__pycache__/cli.cpython-314.pyc +0 -0
  227. package/pennyfarthing_scripts/healthscore/__pycache__/formatters.cpython-314.pyc +0 -0
  228. package/pennyfarthing_scripts/healthscore/__pycache__/models.cpython-314.pyc +0 -0
  229. package/pennyfarthing_scripts/hooks/__init__.py +437 -0
  230. package/pennyfarthing_scripts/hooks/__pycache__/__init__.cpython-314.pyc +0 -0
  231. package/pennyfarthing_scripts/hooks/__pycache__/bell_mode.cpython-314.pyc +0 -0
  232. package/pennyfarthing_scripts/hooks/__pycache__/cli.cpython-314.pyc +0 -0
  233. package/pennyfarthing_scripts/hooks/__pycache__/context_breaker.cpython-314.pyc +0 -0
  234. package/pennyfarthing_scripts/hooks/__pycache__/context_warning.cpython-314.pyc +0 -0
  235. package/pennyfarthing_scripts/hooks/__pycache__/cyclist_pretooluse.cpython-314.pyc +0 -0
  236. package/pennyfarthing_scripts/hooks/__pycache__/pre_edit_check.cpython-314.pyc +0 -0
  237. package/pennyfarthing_scripts/hooks/__pycache__/reflector_check.cpython-314.pyc +0 -0
  238. package/pennyfarthing_scripts/hooks/__pycache__/schema_validation.cpython-314.pyc +0 -0
  239. package/pennyfarthing_scripts/hooks/__pycache__/session_start.cpython-314.pyc +0 -0
  240. package/pennyfarthing_scripts/hooks/__pycache__/session_stop.cpython-314.pyc +0 -0
  241. package/pennyfarthing_scripts/hooks/__pycache__/sprint_yaml_validation.cpython-314.pyc +0 -0
  242. package/pennyfarthing_scripts/hooks/__pycache__/statusline.cpython-314.pyc +0 -0
  243. package/pennyfarthing_scripts/hooks/bell_mode.py +215 -0
  244. package/pennyfarthing_scripts/hooks/cli.py +96 -0
  245. package/pennyfarthing_scripts/hooks/context_breaker.py +104 -0
  246. package/pennyfarthing_scripts/hooks/context_warning.py +66 -0
  247. package/pennyfarthing_scripts/hooks/cyclist_pretooluse.py +129 -0
  248. package/pennyfarthing_scripts/hooks/pre_edit_check.py +78 -0
  249. package/pennyfarthing_scripts/hooks/reflector_check.py +271 -0
  250. package/pennyfarthing_scripts/hooks/schema_validation.py +203 -0
  251. package/pennyfarthing_scripts/hooks/session_start.py +296 -0
  252. package/pennyfarthing_scripts/hooks/session_stop.py +111 -0
  253. package/pennyfarthing_scripts/hooks/sprint_yaml_validation.py +97 -0
  254. package/pennyfarthing_scripts/hooks/statusline.py +420 -0
  255. package/pennyfarthing_scripts/hooks.py +27 -432
  256. package/pennyfarthing_scripts/hotspots/__pycache__/__init__.cpython-314.pyc +0 -0
  257. package/pennyfarthing_scripts/hotspots/__pycache__/__main__.cpython-314.pyc +0 -0
  258. package/pennyfarthing_scripts/hotspots/__pycache__/analyze.cpython-314.pyc +0 -0
  259. package/pennyfarthing_scripts/hotspots/__pycache__/cli.cpython-314.pyc +0 -0
  260. package/pennyfarthing_scripts/hotspots/__pycache__/formatters.cpython-314.pyc +0 -0
  261. package/pennyfarthing_scripts/hotspots/__pycache__/models.cpython-314.pyc +0 -0
  262. package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-314.pyc +0 -0
  263. package/pennyfarthing_scripts/jira/__pycache__/__main__.cpython-314.pyc +0 -0
  264. package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
  265. package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
  266. package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
  267. package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
  268. package/pennyfarthing_scripts/jira/__pycache__/compat.cpython-314.pyc +0 -0
  269. package/pennyfarthing_scripts/jira/__pycache__/create.cpython-314.pyc +0 -0
  270. package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-314.pyc +0 -0
  271. package/pennyfarthing_scripts/jira/__pycache__/mappings.cpython-314.pyc +0 -0
  272. package/pennyfarthing_scripts/jira/__pycache__/models.cpython-314.pyc +0 -0
  273. package/pennyfarthing_scripts/jira/__pycache__/operations.cpython-314.pyc +0 -0
  274. package/pennyfarthing_scripts/jira/__pycache__/reconcile.cpython-314.pyc +0 -0
  275. package/pennyfarthing_scripts/jira/__pycache__/story.cpython-314.pyc +0 -0
  276. package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-314.pyc +0 -0
  277. package/pennyfarthing_scripts/launch/__pycache__/__init__.cpython-314.pyc +0 -0
  278. package/pennyfarthing_scripts/launch/__pycache__/cli.cpython-314.pyc +0 -0
  279. package/pennyfarthing_scripts/migration/__pycache__/__init__.cpython-314.pyc +0 -0
  280. package/pennyfarthing_scripts/migration/__pycache__/__main__.cpython-314.pyc +0 -0
  281. package/pennyfarthing_scripts/migration/__pycache__/cli.cpython-314.pyc +0 -0
  282. package/pennyfarthing_scripts/migration/__pycache__/session.cpython-314.pyc +0 -0
  283. package/pennyfarthing_scripts/migration/__pycache__/skill.cpython-314.pyc +0 -0
  284. package/pennyfarthing_scripts/migration/__pycache__/step.cpython-314.pyc +0 -0
  285. package/pennyfarthing_scripts/migration/__pycache__/validate.cpython-314.pyc +0 -0
  286. package/pennyfarthing_scripts/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
  287. package/pennyfarthing_scripts/preflight/__pycache__/__main__.cpython-314.pyc +0 -0
  288. package/pennyfarthing_scripts/preflight/__pycache__/cli.cpython-314.pyc +0 -0
  289. package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
  290. package/pennyfarthing_scripts/pretooluse_hook.py +3 -185
  291. package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
  292. package/pennyfarthing_scripts/prime/__pycache__/__main__.cpython-314.pyc +0 -0
  293. package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
  294. package/pennyfarthing_scripts/prime/__pycache__/loader.cpython-314.pyc +0 -0
  295. package/pennyfarthing_scripts/prime/__pycache__/models.cpython-314.pyc +0 -0
  296. package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
  297. package/pennyfarthing_scripts/prime/__pycache__/session.cpython-314.pyc +0 -0
  298. package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
  299. package/pennyfarthing_scripts/prime/__pycache__/version_sentinel.cpython-314.pyc +0 -0
  300. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  301. package/pennyfarthing_scripts/prime/workflow.py +2 -1
  302. package/pennyfarthing_scripts/schema_validation_hook.py +3 -298
  303. package/pennyfarthing_scripts/session/__pycache__/__init__.cpython-314.pyc +0 -0
  304. package/pennyfarthing_scripts/session/__pycache__/cli.cpython-314.pyc +0 -0
  305. package/pennyfarthing_scripts/session_start_hook.py +4 -186
  306. package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
  307. package/pennyfarthing_scripts/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
  308. package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
  309. package/pennyfarthing_scripts/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
  310. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  311. package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
  312. package/pennyfarthing_scripts/sprint/__pycache__/epic_update.cpython-314.pyc +0 -0
  313. package/pennyfarthing_scripts/sprint/__pycache__/import_epic.cpython-314.pyc +0 -0
  314. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  315. package/pennyfarthing_scripts/sprint/__pycache__/status.cpython-314.pyc +0 -0
  316. package/pennyfarthing_scripts/sprint/__pycache__/story_add.cpython-314.pyc +0 -0
  317. package/pennyfarthing_scripts/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
  318. package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
  319. package/pennyfarthing_scripts/sprint/__pycache__/validate_cmd.cpython-314.pyc +0 -0
  320. package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
  321. package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-314.pyc +0 -0
  322. package/pennyfarthing_scripts/sprint/__pycache__/yaml_io.cpython-314.pyc +0 -0
  323. package/pennyfarthing_scripts/sprint/story_update.py +19 -0
  324. package/pennyfarthing_scripts/story/__pycache__/__init__.cpython-314.pyc +0 -0
  325. package/pennyfarthing_scripts/story/__pycache__/__main__.cpython-314.pyc +0 -0
  326. package/pennyfarthing_scripts/story/__pycache__/cli.cpython-314.pyc +0 -0
  327. package/pennyfarthing_scripts/story/__pycache__/create.cpython-314.pyc +0 -0
  328. package/pennyfarthing_scripts/story/__pycache__/size.cpython-314.pyc +0 -0
  329. package/pennyfarthing_scripts/story/__pycache__/template.cpython-314.pyc +0 -0
  330. package/pennyfarthing_scripts/tests/__pycache__/__init__.cpython-314.pyc +0 -0
  331. package/pennyfarthing_scripts/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
  332. package/pennyfarthing_scripts/tests/__pycache__/test_108_1_gate_migration.cpython-314-pytest-9.0.2.pyc +0 -0
  333. package/pennyfarthing_scripts/tests/__pycache__/test_archive_epic.cpython-314-pytest-9.0.2.pyc +0 -0
  334. package/pennyfarthing_scripts/tests/__pycache__/test_bc.cpython-314-pytest-9.0.2.pyc +0 -0
  335. package/pennyfarthing_scripts/tests/__pycache__/test_bikerack.cpython-314-pytest-9.0.2.pyc +0 -0
  336. package/pennyfarthing_scripts/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
  337. package/pennyfarthing_scripts/tests/__pycache__/test_cli_modules.cpython-314-pytest-9.0.2.pyc +0 -0
  338. package/pennyfarthing_scripts/tests/__pycache__/test_cli_normalization.cpython-314-pytest-9.0.2.pyc +0 -0
  339. package/pennyfarthing_scripts/tests/__pycache__/test_codemarkers.cpython-314-pytest-9.0.2.pyc +0 -0
  340. package/pennyfarthing_scripts/tests/__pycache__/test_common.cpython-314-pytest-9.0.2.pyc +0 -0
  341. package/pennyfarthing_scripts/tests/__pycache__/test_confidence_sm_evaluation.cpython-314-pytest-9.0.2.pyc +0 -0
  342. package/pennyfarthing_scripts/tests/__pycache__/test_confidence_sm_gate.cpython-314-pytest-9.0.2.pyc +0 -0
  343. package/pennyfarthing_scripts/tests/__pycache__/test_dialogue_manager.cpython-314-pytest-9.0.2.pyc +0 -0
  344. package/pennyfarthing_scripts/tests/__pycache__/test_epic_shard_validation.cpython-314-pytest-9.0.2.pyc +0 -0
  345. package/pennyfarthing_scripts/tests/__pycache__/test_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
  346. package/pennyfarthing_scripts/tests/__pycache__/test_handoff_cli.cpython-314-pytest-9.0.2.pyc +0 -0
  347. package/pennyfarthing_scripts/tests/__pycache__/test_handoff_e2e.cpython-314-pytest-9.0.2.pyc +0 -0
  348. package/pennyfarthing_scripts/tests/__pycache__/test_healthscore.cpython-314-pytest-9.0.2.pyc +0 -0
  349. package/pennyfarthing_scripts/tests/__pycache__/test_jira_package.cpython-314-pytest-9.0.2.pyc +0 -0
  350. package/pennyfarthing_scripts/tests/__pycache__/test_package_structure.cpython-314-pytest-9.0.2.pyc +0 -0
  351. package/pennyfarthing_scripts/tests/__pycache__/test_patch_mode.cpython-314-pytest-9.0.2.pyc +0 -0
  352. package/pennyfarthing_scripts/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
  353. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc +0 -0
  354. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_panel.cpython-314-pytest-9.0.2.pyc +0 -0
  355. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
  356. package/pennyfarthing_scripts/tests/__pycache__/test_story_add.cpython-314-pytest-9.0.2.pyc +0 -0
  357. package/pennyfarthing_scripts/tests/__pycache__/test_story_package.cpython-314-pytest-9.0.2.pyc +0 -0
  358. package/pennyfarthing_scripts/tests/__pycache__/test_story_update.cpython-314-pytest-9.0.2.pyc +0 -0
  359. package/pennyfarthing_scripts/tests/__pycache__/test_tiers.cpython-314-pytest-9.0.2.pyc +0 -0
  360. package/pennyfarthing_scripts/tests/__pycache__/test_token_counting.cpython-314-pytest-9.0.2.pyc +0 -0
  361. package/pennyfarthing_scripts/tests/__pycache__/test_topology_loader.cpython-314-pytest-9.0.2.pyc +0 -0
  362. package/pennyfarthing_scripts/tests/__pycache__/test_tui_focus.cpython-314-pytest-9.0.2.pyc +0 -0
  363. package/pennyfarthing_scripts/tests/__pycache__/test_tui_panel_persistence.cpython-314-pytest-9.0.2.pyc +0 -0
  364. package/pennyfarthing_scripts/tests/__pycache__/test_validate_cmd.cpython-314-pytest-9.0.2.pyc +0 -0
  365. package/pennyfarthing_scripts/tests/__pycache__/test_version_sentinel.cpython-314-pytest-9.0.2.pyc +0 -0
  366. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
  367. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_cli.cpython-314-pytest-9.0.2.pyc +0 -0
  368. package/pennyfarthing_scripts/tests/__pycache__/test_yaml_io.cpython-314-pytest-9.0.2.pyc +0 -0
  369. package/pennyfarthing_scripts/tests/test_sprint_panel.py +344 -265
  370. package/pennyfarthing_scripts/theme/__pycache__/__init__.cpython-314.pyc +0 -0
  371. package/pennyfarthing_scripts/theme/__pycache__/cli.cpython-314.pyc +0 -0
  372. package/pennyfarthing_scripts/validate/__pycache__/__init__.cpython-314.pyc +0 -0
  373. package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +0 -0
  374. package/pennyfarthing_scripts/validate/adapters/__pycache__/__init__.cpython-314.pyc +0 -0
  375. package/pennyfarthing_scripts/validate/adapters/__pycache__/agent.cpython-314.pyc +0 -0
  376. package/pennyfarthing_scripts/validate/adapters/__pycache__/schema.cpython-314.pyc +0 -0
  377. package/pennyfarthing_scripts/validate/adapters/__pycache__/skill_command.cpython-314.pyc +0 -0
  378. package/pennyfarthing_scripts/validate/adapters/__pycache__/sprint.cpython-314.pyc +0 -0
  379. package/pennyfarthing_scripts/validate/adapters/__pycache__/tandem_awareness.cpython-314.pyc +0 -0
  380. package/pennyfarthing_scripts/validate/adapters/__pycache__/workflow.cpython-314.pyc +0 -0
  381. package/pennyfarthing_scripts/validate/adapters/workflow.py +19 -0
  382. package/pennyfarthing_scripts/welcome_hook.py +3 -149
  383. package/pennyfarthing_scripts/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
  384. package/pennyfarthing_scripts/workflow/__pycache__/cli.cpython-314.pyc +0 -0
  385. package/pennyfarthing_scripts/workflow/__pycache__/helpers.cpython-314.pyc +0 -0
  386. package/pennyfarthing_scripts/workflow/__pycache__/scale.cpython-314.pyc +0 -0
  387. package/pennyfarthing_scripts/workflow/__pycache__/state.cpython-314.pyc +0 -0
  388. package/pennyfarthing_scripts/workflow/cli.py +7 -6
  389. package/pennyfarthing_scripts/workflow/team_lifecycle.py +257 -0
  390. package/packages/core/dist/scripts/theme-detail.test.d.ts +0 -10
  391. package/packages/core/dist/scripts/theme-detail.test.js +0 -199
  392. package/pennyfarthing_scripts/bikerack/__pycache__/portrait.cpython-314.pyc +0 -0
  393. package/pennyfarthing_scripts/gate/__pycache__/__init__.cpython-314.pyc +0 -0
  394. package/pennyfarthing_scripts/gate/__pycache__/cli.cpython-314.pyc +0 -0
  395. package/pennyfarthing_scripts/gate/__pycache__/validate.cpython-314.pyc +0 -0
  396. package/pennyfarthing_scripts/git/__pycache__/hooks_installer.cpython-314.pyc +0 -0
  397. package/pennyfarthing_scripts/git/__pycache__/repos.cpython-314.pyc +0 -0
  398. package/pennyfarthing_scripts/git/__pycache__/worktree.cpython-314.pyc +0 -0
  399. package/pennyfarthing_scripts/prime/__pycache__/heatmap.cpython-314.pyc +0 -0
  400. package/pennyfarthing_scripts/tests/__pycache__/test_108_2_remove_handoff_fallback.cpython-314-pytest-9.0.2.pyc +0 -0
  401. package/pennyfarthing_scripts/tests/__pycache__/test_gate_file_resolution.cpython-314-pytest-9.0.2.pyc +0 -0
  402. package/pennyfarthing_scripts/tests/__pycache__/test_gate_runner.cpython-314-pytest-9.0.2.pyc +0 -0
  403. package/pennyfarthing_scripts/tests/__pycache__/test_resolve_gate_file_field.cpython-314-pytest-9.0.2.pyc +0 -0
@@ -1,437 +1,32 @@
1
1
  """
2
- Shared utilities for Pennyfarthing Claude Code hooks.
2
+ Backward-compatibility shim hooks utilities moved to hooks/ subpackage.
3
3
 
4
- Provides common functionality for all hooks:
5
- - Project root detection
6
- - Port file discovery
7
- - Settings loading (relay_mode, permission_mode)
8
- - Context state checking
9
- - HTTP communication with Cyclist
4
+ All exports are re-imported from pennyfarthing_scripts.hooks (the package).
5
+ Existing code that does `from hooks import ...` or
6
+ `from pennyfarthing_scripts.hooks import ...` continues to work.
10
7
 
11
- All hooks should import from this module for consistency.
12
-
13
- Story: MSSCI-12409 - Hook consistency and relay mode compatibility
8
+ This file will be removed in a future version.
14
9
  """
15
10
 
16
- import json
17
- import os
18
- import sys
19
- import urllib.error
20
- import urllib.request
21
- from dataclasses import dataclass
22
- from pathlib import Path
23
- from typing import Any
24
-
25
- import yaml
26
-
27
- # =============================================================================
28
- # Port File Constants
29
- # =============================================================================
30
-
31
- # WheelHub port file - central coordination server for all communication
32
- # Per ADR-0004: "the hub where all communication converges"
33
- CYCLIST_PORT_FILE = ".wheelhub-port"
34
-
35
- # Default port if file not found
36
- DEFAULT_CYCLIST_PORT = 7431
37
-
38
- # HTTP timeout for Cyclist communication
39
- HTTP_TIMEOUT_SECONDS = 120
40
-
41
-
42
- # =============================================================================
43
- # Project Root Detection
44
- # =============================================================================
45
-
46
-
47
- def find_project_root(start_dir: Path | None = None) -> Path | None:
48
- """Find the project root by looking for marker files.
49
-
50
- Searches for (in order):
51
- 1. .wheelhub-port (WheelHub is running)
52
- 2. .pennyfarthing directory
53
- 3. .claude directory
54
-
55
- Args:
56
- start_dir: Directory to start search from (defaults to cwd)
57
-
58
- Returns:
59
- Path to project root, or None if not found
60
- """
61
- current = Path(start_dir) if start_dir else Path.cwd()
62
- current = current.resolve()
63
-
64
- while current != current.parent:
65
- # Check for Cyclist port files first (indicates Cyclist is running)
66
- if (current / CYCLIST_PORT_FILE).exists():
67
- return current
68
- # Fall back to directory markers
69
- if (current / ".pennyfarthing").is_dir():
70
- return current
71
- if (current / ".claude").is_dir():
72
- return current
73
- current = current.parent
74
-
75
- return None
76
-
77
-
78
- # =============================================================================
79
- # Port File Reading
80
- # =============================================================================
81
-
82
-
83
- def read_port_file(file_name: str, project_root: Path | None = None) -> int | None:
84
- """Read a port number from a Cyclist port file.
85
-
86
- Args:
87
- file_name: Name of the port file (e.g. .wheelhub-port)
88
- project_root: Project root directory (auto-detected if not provided)
89
-
90
- Returns:
91
- Port number, or None if file not found or invalid
92
- """
93
- root = project_root or find_project_root()
94
- if not root:
95
- return None
96
-
97
- port_file = root / file_name
98
- if not port_file.exists():
99
- return None
100
-
101
- try:
102
- content = port_file.read_text().strip()
103
- port = int(content)
104
- if 0 < port < 65536:
105
- return port
106
- except (ValueError, OSError):
107
- pass
108
-
109
- return None
110
-
111
-
112
- def get_cyclist_port(project_root: Path | None = None) -> int:
113
- """Get the WheelHub server port.
114
-
115
- WheelHub is the central coordination server for all Cyclist communication,
116
- including hook requests, OTEL, REST APIs, and WebSocket.
117
-
118
- Args:
119
- project_root: Project root directory (auto-detected if not provided)
120
-
121
- Returns:
122
- Port number (default if file not found)
123
- """
124
- port = read_port_file(CYCLIST_PORT_FILE, project_root)
125
- if port:
126
- return port
127
-
128
- return DEFAULT_CYCLIST_PORT
129
-
130
-
131
- # =============================================================================
132
- # Settings Loading
133
- # =============================================================================
134
-
135
-
136
- @dataclass
137
- class CyclistSettings:
138
- """Cyclist workflow settings from config.local.yaml."""
139
-
140
- permission_mode: str = "manual" # plan, manual, accept
141
- relay_mode: bool = False
142
- bell_mode: bool = False
143
- theme: str | None = None
144
-
145
-
146
- def load_settings(project_root: Path | None = None) -> CyclistSettings:
147
- """Load Cyclist settings from .pennyfarthing/config.local.yaml.
148
-
149
- Handles legacy setting migrations:
150
- - permission_mode: 'turbo' -> 'accept' + relay_mode: True
151
- - handoff_mode: 'auto' -> relay_mode: True
152
- - auto_handoff: True -> relay_mode: True
153
-
154
- Args:
155
- project_root: Project root directory (auto-detected if not provided)
156
-
157
- Returns:
158
- CyclistSettings with current configuration
159
- """
160
- settings = CyclistSettings()
161
-
162
- root = project_root or find_project_root()
163
- if not root:
164
- return settings
165
-
166
- config_path = root / ".pennyfarthing" / "config.local.yaml"
167
- if not config_path.exists():
168
- return settings
169
-
170
- try:
171
- with open(config_path) as f:
172
- config = yaml.safe_load(f) or {}
173
- except (OSError, yaml.YAMLError):
174
- return settings
175
-
176
- # Extract theme
177
- settings.theme = config.get("theme")
178
-
179
- # Extract workflow settings
180
- workflow = config.get("workflow", {})
181
- if not isinstance(workflow, dict):
182
- return settings
183
-
184
- # Handle permission_mode
185
- mode = workflow.get("permission_mode", "manual")
186
- if mode == "turbo":
187
- # Migrate turbo -> accept + relay_mode
188
- settings.permission_mode = "accept"
189
- settings.relay_mode = True
190
- elif mode in ("plan", "manual", "accept"):
191
- settings.permission_mode = mode
192
- else:
193
- settings.permission_mode = "manual"
194
-
195
- # Handle explicit relay_mode (overrides migration)
196
- if "relay_mode" in workflow and isinstance(workflow["relay_mode"], bool):
197
- settings.relay_mode = workflow["relay_mode"]
198
- elif not settings.relay_mode:
199
- # Check legacy settings
200
- if workflow.get("handoff_mode") == "auto":
201
- settings.relay_mode = True
202
- elif workflow.get("auto_handoff") is True:
203
- settings.relay_mode = True
204
-
205
- # Handle bell_mode
206
- if "bell_mode" in workflow and isinstance(workflow["bell_mode"], bool):
207
- settings.bell_mode = workflow["bell_mode"]
208
-
209
- return settings
210
-
211
-
212
- def is_relay_mode_enabled(project_root: Path | None = None) -> bool:
213
- """Check if relay mode (auto-handoff) is enabled.
214
-
215
- Args:
216
- project_root: Project root directory (auto-detected if not provided)
217
-
218
- Returns:
219
- True if relay mode is enabled
220
- """
221
- return load_settings(project_root).relay_mode
222
-
223
-
224
- def is_bell_mode_enabled(project_root: Path | None = None) -> bool:
225
- """Check if bell mode is enabled.
226
-
227
- Args:
228
- project_root: Project root directory (auto-detected if not provided)
229
-
230
- Returns:
231
- True if bell mode is enabled
232
- """
233
- return load_settings(project_root).bell_mode
234
-
235
-
236
- # =============================================================================
237
- # Context State
238
- # =============================================================================
239
-
240
-
241
- @dataclass
242
- class ContextState:
243
- """Current context usage state."""
244
-
245
- used_tokens: int = 0
246
- max_tokens: int = 200000
247
- percentage: float = 0.0
248
- is_high: bool = False # > 60%
249
- is_critical: bool = False # > 80%
250
-
251
-
252
- def get_context_state(project_root: Path | None = None) -> ContextState:
253
- """Get current context usage from Cyclist API.
254
-
255
- Calls Cyclist's /api/context endpoint which runs check-context.sh.
256
-
257
- Args:
258
- project_root: Project root directory (auto-detected if not provided)
259
-
260
- Returns:
261
- ContextState with current usage (defaults if Cyclist not running)
262
- """
263
- state = ContextState()
264
-
265
- port = get_cyclist_port(project_root)
266
- url = f"http://127.0.0.1:{port}/api/context"
267
-
268
- try:
269
- with urllib.request.urlopen(url, timeout=5) as response:
270
- data = json.loads(response.read().decode())
271
- state.used_tokens = data.get("used_tokens", 0)
272
- state.max_tokens = data.get("max_tokens", 200000)
273
- if state.max_tokens > 0:
274
- state.percentage = (state.used_tokens / state.max_tokens) * 100
275
- state.is_high = state.percentage > 60
276
- state.is_critical = state.percentage > 80
277
- except (urllib.error.URLError, json.JSONDecodeError, OSError):
278
- # Cyclist not running or error - return defaults
279
- pass
280
-
281
- return state
282
-
283
-
284
- # =============================================================================
285
- # Cyclist HTTP Communication
286
- # =============================================================================
287
-
288
-
289
- def send_to_cyclist(
290
- endpoint: str,
291
- data: dict[str, Any],
292
- port: int | None = None,
293
- project_root: Path | None = None,
294
- timeout: int = HTTP_TIMEOUT_SECONDS,
295
- ) -> dict[str, Any] | None:
296
- """Send a POST request to WheelHub (Cyclist's central coordination server).
297
-
298
- All endpoints go through WheelHub per ADR-0004.
299
-
300
- Args:
301
- endpoint: API endpoint path (e.g., "/api/hook-request")
302
- data: JSON data to send
303
- port: Port to use (auto-detected if not provided)
304
- project_root: Project root for port discovery
305
- timeout: Request timeout in seconds
306
-
307
- Returns:
308
- Response JSON as dict, or None on error
309
- """
310
- if port is None:
311
- port = get_cyclist_port(project_root)
312
-
313
- url = f"http://127.0.0.1:{port}{endpoint}"
314
- json_data = json.dumps(data).encode("utf-8")
315
-
316
- request = urllib.request.Request(
317
- url,
318
- data=json_data,
319
- headers={"Content-Type": "application/json"},
320
- method="POST",
321
- )
322
-
323
- try:
324
- with urllib.request.urlopen(request, timeout=timeout) as response:
325
- return json.loads(response.read().decode())
326
- except urllib.error.URLError as e:
327
- # Connection refused means Cyclist isn't running
328
- if "Connection refused" in str(e):
329
- return None
330
- raise
331
- except (json.JSONDecodeError, OSError):
332
- return None
333
-
334
-
335
- # =============================================================================
336
- # Hook Response Formatting
337
- # =============================================================================
338
-
339
-
340
- @dataclass
341
- class HookResponse:
342
- """Standard hook response for Claude Code."""
343
-
344
- event_name: str
345
- decision: str | None = None # allow, deny, ask (for PreToolUse)
346
- reason: str | None = None
347
- updated_input: dict[str, Any] | None = None
348
- additional_context: str | None = None # For PostToolUse context injection
349
-
350
- def to_json(self) -> str:
351
- """Format as Claude Code hook JSON output."""
352
- output: dict[str, Any] = {
353
- "hookSpecificOutput": {
354
- "hookEventName": self.event_name,
355
- }
356
- }
357
-
358
- hook_output = output["hookSpecificOutput"]
359
-
360
- if self.decision:
361
- hook_output["permissionDecision"] = self.decision
362
- if self.reason:
363
- hook_output["permissionDecisionReason"] = self.reason
364
- if self.updated_input:
365
- hook_output["updatedInput"] = self.updated_input
366
- if self.additional_context:
367
- hook_output["additionalContext"] = self.additional_context
368
-
369
- return json.dumps(output)
370
-
371
-
372
- def output_hook_response(response: HookResponse) -> None:
373
- """Output hook response to stdout for Claude Code."""
374
- print(response.to_json())
375
-
376
-
377
- def read_stdin_json() -> dict[str, Any]:
378
- """Read JSON from stdin (hook input from Claude Code).
379
-
380
- Returns:
381
- Parsed JSON as dict
382
-
383
- Raises:
384
- ValueError: If input is not valid JSON
385
- """
386
- data = sys.stdin.read()
387
- try:
388
- return json.loads(data)
389
- except json.JSONDecodeError as e:
390
- raise ValueError(f"Invalid JSON input: {e}") from e
391
-
392
-
393
- # =============================================================================
394
- # Hook Execution Utilities
395
- # =============================================================================
396
-
397
-
398
- def is_cyclist_running(project_root: Path | None = None) -> bool:
399
- """Check if Cyclist server is running.
400
-
401
- Checks the CYCLIST environment variable set by ClaudeService when
402
- spawning Claude inside Cyclist. No file I/O, no HTTP, no signals —
403
- this runs on every tool invocation and must be instant.
404
-
405
- The project_root parameter is kept for backward compatibility but
406
- is no longer used.
407
-
408
- Returns:
409
- True if running inside a Cyclist-spawned Claude process
410
- """
411
- return os.environ.get("CYCLIST") == "1"
412
-
413
-
414
- def should_auto_approve(settings: CyclistSettings) -> bool:
415
- """Check if requests should be auto-approved based on settings.
416
-
417
- Auto-approve when permission_mode is 'accept' (formerly turbo).
418
-
419
- Args:
420
- settings: Current Cyclist settings
421
-
422
- Returns:
423
- True if auto-approval is enabled
424
- """
425
- return settings.permission_mode == "accept"
426
-
427
-
428
- def should_auto_handoff(settings: CyclistSettings) -> bool:
429
- """Check if handoffs should be automatic based on settings.
430
-
431
- Args:
432
- settings: Current Cyclist settings
433
-
434
- Returns:
435
- True if relay_mode is enabled
436
- """
437
- return settings.relay_mode
11
+ # Re-export everything from the hooks package
12
+ from pennyfarthing_scripts.hooks import ( # noqa: F401
13
+ CYCLIST_PORT_FILE,
14
+ DEFAULT_CYCLIST_PORT,
15
+ HTTP_TIMEOUT_SECONDS,
16
+ ContextState,
17
+ CyclistSettings,
18
+ HookResponse,
19
+ find_project_root,
20
+ get_context_state,
21
+ get_cyclist_port,
22
+ is_bell_mode_enabled,
23
+ is_cyclist_running,
24
+ is_relay_mode_enabled,
25
+ load_settings,
26
+ output_hook_response,
27
+ read_port_file,
28
+ read_stdin_json,
29
+ send_to_cyclist,
30
+ should_auto_approve,
31
+ should_auto_handoff,
32
+ )