@pennyfarthing/core 11.1.1 → 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 (487) hide show
  1. package/README.md +8 -8
  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/otlp-receiver.d.ts +16 -11
  41. package/packages/core/dist/server/otlp-receiver.d.ts.map +1 -1
  42. package/packages/core/dist/server/otlp-receiver.js +185 -24
  43. package/packages/core/dist/server/otlp-receiver.js.map +1 -1
  44. package/packages/core/dist/server/otlp-receiver.test.d.ts +21 -0
  45. package/packages/core/dist/server/otlp-receiver.test.d.ts.map +1 -0
  46. package/packages/core/dist/server/otlp-receiver.test.js +446 -0
  47. package/packages/core/dist/server/otlp-receiver.test.js.map +1 -0
  48. package/packages/core/dist/server/server.d.ts +0 -3
  49. package/packages/core/dist/server/server.d.ts.map +1 -1
  50. package/packages/core/dist/server/server.js +3 -37
  51. package/packages/core/dist/server/server.js.map +1 -1
  52. package/packages/core/dist/server/server.test.d.ts +1 -1
  53. package/packages/core/dist/server/server.test.js +12 -23
  54. package/packages/core/dist/server/server.test.js.map +1 -1
  55. package/packages/core/dist/shared/capabilities.d.ts +88 -0
  56. package/packages/core/dist/shared/capabilities.d.ts.map +1 -0
  57. package/packages/core/dist/shared/capabilities.js +133 -0
  58. package/packages/core/dist/shared/capabilities.js.map +1 -0
  59. package/packages/core/dist/shared/capabilities.test.d.ts +2 -0
  60. package/packages/core/dist/shared/capabilities.test.d.ts.map +1 -0
  61. package/packages/core/dist/shared/capabilities.test.js +217 -0
  62. package/packages/core/dist/shared/capabilities.test.js.map +1 -0
  63. package/packages/core/dist/shared/portrait-resolver.d.ts +9 -0
  64. package/packages/core/dist/shared/portrait-resolver.d.ts.map +1 -1
  65. package/packages/core/dist/shared/portrait-resolver.js +27 -0
  66. package/packages/core/dist/shared/portrait-resolver.js.map +1 -1
  67. package/packages/core/dist/shared/portrait-resolver.test.js +47 -1
  68. package/packages/core/dist/shared/portrait-resolver.test.js.map +1 -1
  69. package/packages/core/dist/shared/spawn-prompt.d.ts +47 -0
  70. package/packages/core/dist/shared/spawn-prompt.d.ts.map +1 -0
  71. package/packages/core/dist/shared/spawn-prompt.js +82 -0
  72. package/packages/core/dist/shared/spawn-prompt.js.map +1 -0
  73. package/packages/core/dist/shared/spawn-prompt.test.d.ts +2 -0
  74. package/packages/core/dist/shared/spawn-prompt.test.d.ts.map +1 -0
  75. package/packages/core/dist/shared/spawn-prompt.test.js +251 -0
  76. package/packages/core/dist/shared/spawn-prompt.test.js.map +1 -0
  77. package/packages/core/dist/shared/tandem-portrait-inventory.test.d.ts +13 -0
  78. package/packages/core/dist/shared/tandem-portrait-inventory.test.d.ts.map +1 -0
  79. package/packages/core/dist/shared/tandem-portrait-inventory.test.js +126 -0
  80. package/packages/core/dist/shared/tandem-portrait-inventory.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/workflow-schema.d.ts +32 -0
  86. package/packages/core/dist/workflow/workflow-schema.d.ts.map +1 -1
  87. package/packages/core/dist/workflow/workflow-schema.js +120 -0
  88. package/packages/core/dist/workflow/workflow-schema.js.map +1 -1
  89. package/packages/core/dist/workflow/workflow-schema.test.d.ts.map +1 -1
  90. package/packages/core/dist/workflow/workflow-schema.test.js +570 -1
  91. package/packages/core/dist/workflow/workflow-schema.test.js.map +1 -1
  92. package/pennyfarthing-dist/agents/dev.md +7 -11
  93. package/pennyfarthing-dist/agents/reviewer.md +9 -3
  94. package/pennyfarthing-dist/agents/sm-finish.md +18 -1
  95. package/pennyfarthing-dist/agents/sm-setup.md +1 -1
  96. package/pennyfarthing-dist/agents/sm.md +2 -2
  97. package/pennyfarthing-dist/agents/tea.md +1 -1
  98. package/pennyfarthing-dist/agents/testing-runner.md +2 -1
  99. package/pennyfarthing-dist/commands/pf-chore.md +2 -2
  100. package/pennyfarthing-dist/commands/pf-git.md +4 -2
  101. package/pennyfarthing-dist/commands/pf-standalone.md +7 -2
  102. package/pennyfarthing-dist/gates/approval.md +63 -0
  103. package/pennyfarthing-dist/gates/confidence-sm.md +71 -0
  104. package/pennyfarthing-dist/gates/context-ok.md +56 -0
  105. package/pennyfarthing-dist/gates/evaluations/confidence-sm.md +54 -0
  106. package/pennyfarthing-dist/gates/quality-pass.md +67 -0
  107. package/pennyfarthing-dist/gates/tests-fail.md +84 -0
  108. package/pennyfarthing-dist/gates/tests-pass.md +79 -0
  109. package/pennyfarthing-dist/guides/agent-behavior.md +23 -19
  110. package/pennyfarthing-dist/guides/agent-tag-taxonomy.md +1 -1
  111. package/pennyfarthing-dist/guides/bell-mode.md +1 -1
  112. package/pennyfarthing-dist/guides/bikerack.md +3 -3
  113. package/pennyfarthing-dist/guides/hooks.md +29 -29
  114. package/pennyfarthing-dist/guides/reflector.md +1 -1
  115. package/pennyfarthing-dist/guides/tandem-protocol.md +3 -3
  116. package/pennyfarthing-dist/guides/worktree-mode.md +3 -3
  117. package/pennyfarthing-dist/guides/xml-tags.md +2 -2
  118. package/pennyfarthing-dist/scripts/README.md +1 -1
  119. package/pennyfarthing-dist/scripts/core/check-context.sh +3 -1
  120. package/pennyfarthing-dist/scripts/core/phase-check-start.sh +5 -87
  121. package/pennyfarthing-dist/scripts/git/README.md +24 -14
  122. package/pennyfarthing-dist/scripts/git/create-feature-branches.sh +5 -266
  123. package/pennyfarthing-dist/scripts/git/git-status-all.sh +5 -151
  124. package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +6 -144
  125. package/pennyfarthing-dist/scripts/git/worktree-manager.sh +5 -496
  126. package/pennyfarthing-dist/scripts/hooks/README.md +6 -6
  127. package/pennyfarthing-dist/scripts/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
  128. package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +4 -183
  129. package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +4 -95
  130. package/pennyfarthing-dist/scripts/hooks/context-warning.sh +4 -65
  131. package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +3 -31
  132. package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +9 -11
  133. package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +27 -33
  134. package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +4 -71
  135. package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +3 -19
  136. package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +4 -30
  137. package/pennyfarthing-dist/scripts/hooks/session-start.sh +3 -32
  138. package/pennyfarthing-dist/scripts/hooks/session-stop.sh +4 -65
  139. package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +4 -78
  140. package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +4 -93
  141. package/pennyfarthing-dist/scripts/misc/README.md +1 -1
  142. package/pennyfarthing-dist/scripts/misc/statusline.sh +4 -301
  143. package/pennyfarthing-dist/scripts/portraits/generate-tandem-portraits.sh +76 -0
  144. package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +4 -221
  145. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +5 -13
  146. package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +4 -123
  147. package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +4 -33
  148. package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +4 -156
  149. package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +4 -131
  150. package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +4 -249
  151. package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +4 -160
  152. package/pennyfarthing-dist/skills/pf-bc/usage.md +1 -1
  153. package/pennyfarthing-dist/skills/pf-jira/examples.md +5 -2
  154. package/pennyfarthing-dist/skills/pf-workflow/examples.md +27 -16
  155. package/pennyfarthing-dist/skills/pf-workflow/skill.md +9 -12
  156. package/pennyfarthing-dist/skills/pf-workflow/usage.md +33 -8
  157. package/pennyfarthing-dist/templates/settings.local.json.template +19 -10
  158. package/pennyfarthing-dist/workflows/bdd-tandem.yaml +18 -6
  159. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-01-analyze.md +1 -1
  160. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-04-verify.md +1 -1
  161. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +1 -1
  162. package/pennyfarthing-dist/workflows/review-tandem.yaml +65 -0
  163. package/pennyfarthing-dist/workflows/tdd-tandem.yaml +16 -8
  164. package/pennyfarthing-dist/workflows/tdd.yaml +11 -2
  165. package/pennyfarthing_scripts/CLAUDE.md +45 -14
  166. package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  167. package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  168. package/pennyfarthing_scripts/__pycache__/bellmode_hook.cpython-314.pyc +0 -0
  169. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  170. package/pennyfarthing_scripts/__pycache__/config.cpython-314.pyc +0 -0
  171. package/pennyfarthing_scripts/__pycache__/context.cpython-314.pyc +0 -0
  172. package/pennyfarthing_scripts/__pycache__/hooks.cpython-314.pyc +0 -0
  173. package/pennyfarthing_scripts/__pycache__/jira.cpython-314.pyc +0 -0
  174. package/pennyfarthing_scripts/__pycache__/jira_bidirectional_sync.cpython-314.pyc +0 -0
  175. package/pennyfarthing_scripts/__pycache__/jira_epic_creation.cpython-314.pyc +0 -0
  176. package/pennyfarthing_scripts/__pycache__/jira_sync.cpython-314.pyc +0 -0
  177. package/pennyfarthing_scripts/__pycache__/jira_sync_story.cpython-314.pyc +0 -0
  178. package/pennyfarthing_scripts/__pycache__/output.cpython-314.pyc +0 -0
  179. package/pennyfarthing_scripts/__pycache__/patch_mode.cpython-314.pyc +0 -0
  180. package/pennyfarthing_scripts/__pycache__/pretooluse_hook.cpython-314.pyc +0 -0
  181. package/pennyfarthing_scripts/__pycache__/schema_validation_hook.cpython-314.pyc +0 -0
  182. package/pennyfarthing_scripts/__pycache__/session_start_hook.cpython-314.pyc +0 -0
  183. package/pennyfarthing_scripts/__pycache__/sprint.cpython-314.pyc +0 -0
  184. package/pennyfarthing_scripts/__pycache__/workflow.cpython-311.pyc +0 -0
  185. package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
  186. package/pennyfarthing_scripts/bc/__pycache__/__init__.cpython-314.pyc +0 -0
  187. package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-314.pyc +0 -0
  188. package/pennyfarthing_scripts/bc/__pycache__/focus.cpython-314.pyc +0 -0
  189. package/pennyfarthing_scripts/bc/cli.py +3 -5
  190. package/pennyfarthing_scripts/bellmode_hook.py +12 -296
  191. package/pennyfarthing_scripts/bikerack/__pycache__/__init__.cpython-314.pyc +0 -0
  192. package/pennyfarthing_scripts/bikerack/__pycache__/__main__.cpython-314.pyc +0 -0
  193. package/pennyfarthing_scripts/bikerack/__pycache__/audit_log_panel.cpython-314.pyc +0 -0
  194. package/pennyfarthing_scripts/bikerack/__pycache__/background_panel.cpython-314.pyc +0 -0
  195. package/pennyfarthing_scripts/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
  196. package/pennyfarthing_scripts/bikerack/__pycache__/changed_panel.cpython-314.pyc +0 -0
  197. package/pennyfarthing_scripts/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
  198. package/pennyfarthing_scripts/bikerack/__pycache__/context_meter_footer.cpython-314.pyc +0 -0
  199. package/pennyfarthing_scripts/bikerack/__pycache__/debug_panel.cpython-314.pyc +0 -0
  200. package/pennyfarthing_scripts/bikerack/__pycache__/diffs_panel.cpython-314.pyc +0 -0
  201. package/pennyfarthing_scripts/bikerack/__pycache__/events.cpython-314.pyc +0 -0
  202. package/pennyfarthing_scripts/bikerack/__pycache__/git_panel.cpython-314.pyc +0 -0
  203. package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
  204. package/pennyfarthing_scripts/bikerack/__pycache__/portrait_resolver.cpython-314.pyc +0 -0
  205. package/pennyfarthing_scripts/bikerack/__pycache__/progress_panel.cpython-314.pyc +0 -0
  206. package/pennyfarthing_scripts/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
  207. package/pennyfarthing_scripts/bikerack/__pycache__/story_detail_data.cpython-314.pyc +0 -0
  208. package/pennyfarthing_scripts/bikerack/__pycache__/story_detail_screen.cpython-314.pyc +0 -0
  209. package/pennyfarthing_scripts/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
  210. package/pennyfarthing_scripts/bikerack/__pycache__/ws_client.cpython-314.pyc +0 -0
  211. package/pennyfarthing_scripts/bikerack/audit_log_panel.py +119 -0
  212. package/pennyfarthing_scripts/bikerack/background_panel.py +86 -5
  213. package/pennyfarthing_scripts/bikerack/base_panel.py +87 -2
  214. package/pennyfarthing_scripts/bikerack/changed_panel.py +125 -29
  215. package/pennyfarthing_scripts/bikerack/context_meter_footer.py +88 -0
  216. package/pennyfarthing_scripts/bikerack/debug_panel.py +32 -2
  217. package/pennyfarthing_scripts/bikerack/diffs_panel.py +104 -17
  218. package/pennyfarthing_scripts/bikerack/events.py +28 -0
  219. package/pennyfarthing_scripts/bikerack/git_panel.py +103 -33
  220. package/pennyfarthing_scripts/bikerack/launcher.py +15 -15
  221. package/pennyfarthing_scripts/bikerack/portrait_resolver.py +139 -0
  222. package/pennyfarthing_scripts/bikerack/progress_panel.py +315 -0
  223. package/pennyfarthing_scripts/bikerack/sprint_panel.py +395 -32
  224. package/pennyfarthing_scripts/bikerack/story_detail_data.py +244 -0
  225. package/pennyfarthing_scripts/bikerack/story_detail_screen.py +176 -0
  226. package/pennyfarthing_scripts/bikerack/tui.py +575 -37
  227. package/pennyfarthing_scripts/bikerack/ws_client.py +2 -2
  228. package/pennyfarthing_scripts/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
  229. package/pennyfarthing_scripts/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
  230. package/pennyfarthing_scripts/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
  231. package/pennyfarthing_scripts/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
  232. package/pennyfarthing_scripts/cli.py +42 -65
  233. package/pennyfarthing_scripts/codemarkers/__pycache__/__init__.cpython-314.pyc +0 -0
  234. package/pennyfarthing_scripts/codemarkers/__pycache__/__main__.cpython-314.pyc +0 -0
  235. package/pennyfarthing_scripts/codemarkers/__pycache__/analyze.cpython-314.pyc +0 -0
  236. package/pennyfarthing_scripts/codemarkers/__pycache__/cli.cpython-314.pyc +0 -0
  237. package/pennyfarthing_scripts/codemarkers/__pycache__/formatters.cpython-314.pyc +0 -0
  238. package/pennyfarthing_scripts/codemarkers/__pycache__/models.cpython-314.pyc +0 -0
  239. package/pennyfarthing_scripts/common/__pycache__/__init__.cpython-314.pyc +0 -0
  240. package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
  241. package/pennyfarthing_scripts/common/__pycache__/output.cpython-314.pyc +0 -0
  242. package/pennyfarthing_scripts/common/__pycache__/themes.cpython-314.pyc +0 -0
  243. package/pennyfarthing_scripts/common/pr_config.py +38 -0
  244. package/pennyfarthing_scripts/complexity/__pycache__/__init__.cpython-314.pyc +0 -0
  245. package/pennyfarthing_scripts/complexity/__pycache__/__main__.cpython-314.pyc +0 -0
  246. package/pennyfarthing_scripts/complexity/__pycache__/analyze.cpython-314.pyc +0 -0
  247. package/pennyfarthing_scripts/complexity/__pycache__/cli.cpython-314.pyc +0 -0
  248. package/pennyfarthing_scripts/complexity/__pycache__/formatters.cpython-314.pyc +0 -0
  249. package/pennyfarthing_scripts/complexity/__pycache__/models.cpython-314.pyc +0 -0
  250. package/pennyfarthing_scripts/consultation/__init__.py +1 -0
  251. package/pennyfarthing_scripts/consultation/__pycache__/__init__.cpython-314.pyc +0 -0
  252. package/pennyfarthing_scripts/consultation/__pycache__/cli.cpython-314.pyc +0 -0
  253. package/pennyfarthing_scripts/consultation/__pycache__/dialogue_manager.cpython-314.pyc +0 -0
  254. package/pennyfarthing_scripts/consultation/cli.py +149 -0
  255. package/pennyfarthing_scripts/consultation/dialogue_manager.py +417 -0
  256. package/pennyfarthing_scripts/context.py +3 -3
  257. package/pennyfarthing_scripts/deadcode/__pycache__/__init__.cpython-314.pyc +0 -0
  258. package/pennyfarthing_scripts/deadcode/__pycache__/__main__.cpython-314.pyc +0 -0
  259. package/pennyfarthing_scripts/deadcode/__pycache__/analyze.cpython-314.pyc +0 -0
  260. package/pennyfarthing_scripts/deadcode/__pycache__/cli.cpython-314.pyc +0 -0
  261. package/pennyfarthing_scripts/deadcode/__pycache__/formatters.cpython-314.pyc +0 -0
  262. package/pennyfarthing_scripts/deadcode/__pycache__/models.cpython-314.pyc +0 -0
  263. package/pennyfarthing_scripts/dependencies/__pycache__/__init__.cpython-314.pyc +0 -0
  264. package/pennyfarthing_scripts/dependencies/__pycache__/__main__.cpython-314.pyc +0 -0
  265. package/pennyfarthing_scripts/dependencies/__pycache__/analyze.cpython-314.pyc +0 -0
  266. package/pennyfarthing_scripts/dependencies/__pycache__/cli.cpython-314.pyc +0 -0
  267. package/pennyfarthing_scripts/dependencies/__pycache__/formatters.cpython-314.pyc +0 -0
  268. package/pennyfarthing_scripts/dependencies/__pycache__/models.cpython-314.pyc +0 -0
  269. package/pennyfarthing_scripts/epic/__pycache__/__init__.cpython-314.pyc +0 -0
  270. package/pennyfarthing_scripts/epic/__pycache__/cli.cpython-314.pyc +0 -0
  271. package/pennyfarthing_scripts/git/__init__.py +12 -1
  272. package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
  273. package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
  274. package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
  275. package/pennyfarthing_scripts/git/create_branches.py +3 -4
  276. package/pennyfarthing_scripts/git/hooks_installer.py +152 -0
  277. package/pennyfarthing_scripts/git/repos.py +196 -0
  278. package/pennyfarthing_scripts/git/status_all.py +27 -11
  279. package/pennyfarthing_scripts/git/worktree.py +302 -0
  280. package/pennyfarthing_scripts/git_group/__pycache__/__init__.cpython-314.pyc +0 -0
  281. package/pennyfarthing_scripts/git_group/__pycache__/cli.cpython-314.pyc +0 -0
  282. package/pennyfarthing_scripts/git_group/cli.py +143 -40
  283. package/pennyfarthing_scripts/handoff/__pycache__/__init__.cpython-314.pyc +0 -0
  284. package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-314.pyc +0 -0
  285. package/pennyfarthing_scripts/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
  286. package/pennyfarthing_scripts/handoff/__pycache__/gate_file.cpython-314.pyc +0 -0
  287. package/pennyfarthing_scripts/handoff/__pycache__/gate_runner.cpython-314.pyc +0 -0
  288. package/pennyfarthing_scripts/handoff/__pycache__/marker.cpython-314.pyc +0 -0
  289. package/pennyfarthing_scripts/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
  290. package/pennyfarthing_scripts/handoff/cli.py +33 -1
  291. package/pennyfarthing_scripts/handoff/complete_phase.py +40 -0
  292. package/pennyfarthing_scripts/handoff/marker.py +15 -15
  293. package/pennyfarthing_scripts/handoff/phase_check.py +96 -0
  294. package/pennyfarthing_scripts/handoff/resolve_gate.py +18 -15
  295. package/pennyfarthing_scripts/healthscore/__pycache__/__init__.cpython-314.pyc +0 -0
  296. package/pennyfarthing_scripts/healthscore/__pycache__/__main__.cpython-314.pyc +0 -0
  297. package/pennyfarthing_scripts/healthscore/__pycache__/analyze.cpython-314.pyc +0 -0
  298. package/pennyfarthing_scripts/healthscore/__pycache__/cli.cpython-314.pyc +0 -0
  299. package/pennyfarthing_scripts/healthscore/__pycache__/formatters.cpython-314.pyc +0 -0
  300. package/pennyfarthing_scripts/healthscore/__pycache__/models.cpython-314.pyc +0 -0
  301. package/pennyfarthing_scripts/hooks/__init__.py +437 -0
  302. package/pennyfarthing_scripts/hooks/__pycache__/__init__.cpython-314.pyc +0 -0
  303. package/pennyfarthing_scripts/hooks/__pycache__/bell_mode.cpython-314.pyc +0 -0
  304. package/pennyfarthing_scripts/hooks/__pycache__/cli.cpython-314.pyc +0 -0
  305. package/pennyfarthing_scripts/hooks/__pycache__/context_breaker.cpython-314.pyc +0 -0
  306. package/pennyfarthing_scripts/hooks/__pycache__/context_warning.cpython-314.pyc +0 -0
  307. package/pennyfarthing_scripts/hooks/__pycache__/cyclist_pretooluse.cpython-314.pyc +0 -0
  308. package/pennyfarthing_scripts/hooks/__pycache__/pre_edit_check.cpython-314.pyc +0 -0
  309. package/pennyfarthing_scripts/hooks/__pycache__/reflector_check.cpython-314.pyc +0 -0
  310. package/pennyfarthing_scripts/hooks/__pycache__/schema_validation.cpython-314.pyc +0 -0
  311. package/pennyfarthing_scripts/hooks/__pycache__/session_start.cpython-314.pyc +0 -0
  312. package/pennyfarthing_scripts/hooks/__pycache__/session_stop.cpython-314.pyc +0 -0
  313. package/pennyfarthing_scripts/hooks/__pycache__/sprint_yaml_validation.cpython-314.pyc +0 -0
  314. package/pennyfarthing_scripts/hooks/__pycache__/statusline.cpython-314.pyc +0 -0
  315. package/pennyfarthing_scripts/hooks/bell_mode.py +215 -0
  316. package/pennyfarthing_scripts/hooks/cli.py +96 -0
  317. package/pennyfarthing_scripts/hooks/context_breaker.py +104 -0
  318. package/pennyfarthing_scripts/hooks/context_warning.py +66 -0
  319. package/pennyfarthing_scripts/hooks/cyclist_pretooluse.py +129 -0
  320. package/pennyfarthing_scripts/hooks/pre_edit_check.py +78 -0
  321. package/pennyfarthing_scripts/hooks/reflector_check.py +271 -0
  322. package/pennyfarthing_scripts/hooks/schema_validation.py +203 -0
  323. package/pennyfarthing_scripts/hooks/session_start.py +296 -0
  324. package/pennyfarthing_scripts/hooks/session_stop.py +111 -0
  325. package/pennyfarthing_scripts/hooks/sprint_yaml_validation.py +97 -0
  326. package/pennyfarthing_scripts/hooks/statusline.py +420 -0
  327. package/pennyfarthing_scripts/hooks.py +27 -446
  328. package/pennyfarthing_scripts/hotspots/__pycache__/__init__.cpython-314.pyc +0 -0
  329. package/pennyfarthing_scripts/hotspots/__pycache__/__main__.cpython-314.pyc +0 -0
  330. package/pennyfarthing_scripts/hotspots/__pycache__/analyze.cpython-314.pyc +0 -0
  331. package/pennyfarthing_scripts/hotspots/__pycache__/cli.cpython-314.pyc +0 -0
  332. package/pennyfarthing_scripts/hotspots/__pycache__/formatters.cpython-314.pyc +0 -0
  333. package/pennyfarthing_scripts/hotspots/__pycache__/models.cpython-314.pyc +0 -0
  334. package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-314.pyc +0 -0
  335. package/pennyfarthing_scripts/jira/__pycache__/__main__.cpython-314.pyc +0 -0
  336. package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
  337. package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
  338. package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
  339. package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
  340. package/pennyfarthing_scripts/jira/__pycache__/compat.cpython-314.pyc +0 -0
  341. package/pennyfarthing_scripts/jira/__pycache__/create.cpython-314.pyc +0 -0
  342. package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-314.pyc +0 -0
  343. package/pennyfarthing_scripts/jira/__pycache__/mappings.cpython-314.pyc +0 -0
  344. package/pennyfarthing_scripts/jira/__pycache__/models.cpython-314.pyc +0 -0
  345. package/pennyfarthing_scripts/jira/__pycache__/operations.cpython-314.pyc +0 -0
  346. package/pennyfarthing_scripts/jira/__pycache__/reconcile.cpython-314.pyc +0 -0
  347. package/pennyfarthing_scripts/jira/__pycache__/story.cpython-314.pyc +0 -0
  348. package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-314.pyc +0 -0
  349. package/pennyfarthing_scripts/launch/__pycache__/__init__.cpython-314.pyc +0 -0
  350. package/pennyfarthing_scripts/launch/__pycache__/cli.cpython-314.pyc +0 -0
  351. package/pennyfarthing_scripts/migration/__pycache__/__init__.cpython-314.pyc +0 -0
  352. package/pennyfarthing_scripts/migration/__pycache__/__main__.cpython-314.pyc +0 -0
  353. package/pennyfarthing_scripts/migration/__pycache__/cli.cpython-314.pyc +0 -0
  354. package/pennyfarthing_scripts/migration/__pycache__/session.cpython-314.pyc +0 -0
  355. package/pennyfarthing_scripts/migration/__pycache__/skill.cpython-314.pyc +0 -0
  356. package/pennyfarthing_scripts/migration/__pycache__/step.cpython-314.pyc +0 -0
  357. package/pennyfarthing_scripts/migration/__pycache__/validate.cpython-314.pyc +0 -0
  358. package/pennyfarthing_scripts/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
  359. package/pennyfarthing_scripts/preflight/__pycache__/__main__.cpython-314.pyc +0 -0
  360. package/pennyfarthing_scripts/preflight/__pycache__/cli.cpython-314.pyc +0 -0
  361. package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
  362. package/pennyfarthing_scripts/pretooluse_hook.py +3 -185
  363. package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
  364. package/pennyfarthing_scripts/prime/__pycache__/__main__.cpython-314.pyc +0 -0
  365. package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
  366. package/pennyfarthing_scripts/prime/__pycache__/loader.cpython-314.pyc +0 -0
  367. package/pennyfarthing_scripts/prime/__pycache__/models.cpython-314.pyc +0 -0
  368. package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
  369. package/pennyfarthing_scripts/prime/__pycache__/session.cpython-314.pyc +0 -0
  370. package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
  371. package/pennyfarthing_scripts/prime/__pycache__/version_sentinel.cpython-314.pyc +0 -0
  372. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  373. package/pennyfarthing_scripts/prime/heatmap.py +655 -0
  374. package/pennyfarthing_scripts/prime/workflow.py +2 -1
  375. package/pennyfarthing_scripts/schema_validation_hook.py +3 -298
  376. package/pennyfarthing_scripts/session/__pycache__/__init__.cpython-314.pyc +0 -0
  377. package/pennyfarthing_scripts/session/__pycache__/cli.cpython-314.pyc +0 -0
  378. package/pennyfarthing_scripts/session_start_hook.py +4 -186
  379. package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
  380. package/pennyfarthing_scripts/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
  381. package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
  382. package/pennyfarthing_scripts/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
  383. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  384. package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
  385. package/pennyfarthing_scripts/sprint/__pycache__/epic_update.cpython-314.pyc +0 -0
  386. package/pennyfarthing_scripts/sprint/__pycache__/import_epic.cpython-314.pyc +0 -0
  387. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  388. package/pennyfarthing_scripts/sprint/__pycache__/status.cpython-314.pyc +0 -0
  389. package/pennyfarthing_scripts/sprint/__pycache__/story_add.cpython-314.pyc +0 -0
  390. package/pennyfarthing_scripts/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
  391. package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
  392. package/pennyfarthing_scripts/sprint/__pycache__/validate_cmd.cpython-314.pyc +0 -0
  393. package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
  394. package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-314.pyc +0 -0
  395. package/pennyfarthing_scripts/sprint/__pycache__/yaml_io.cpython-314.pyc +0 -0
  396. package/pennyfarthing_scripts/sprint/loader.py +15 -1
  397. package/pennyfarthing_scripts/sprint/story_update.py +19 -0
  398. package/pennyfarthing_scripts/story/__pycache__/__init__.cpython-314.pyc +0 -0
  399. package/pennyfarthing_scripts/story/__pycache__/__main__.cpython-314.pyc +0 -0
  400. package/pennyfarthing_scripts/story/__pycache__/cli.cpython-314.pyc +0 -0
  401. package/pennyfarthing_scripts/story/__pycache__/create.cpython-314.pyc +0 -0
  402. package/pennyfarthing_scripts/story/__pycache__/size.cpython-314.pyc +0 -0
  403. package/pennyfarthing_scripts/story/__pycache__/template.cpython-314.pyc +0 -0
  404. package/pennyfarthing_scripts/tests/__pycache__/__init__.cpython-314.pyc +0 -0
  405. package/pennyfarthing_scripts/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
  406. package/pennyfarthing_scripts/tests/__pycache__/test_108_1_gate_migration.cpython-314-pytest-9.0.2.pyc +0 -0
  407. package/pennyfarthing_scripts/tests/__pycache__/test_archive_epic.cpython-314-pytest-9.0.2.pyc +0 -0
  408. package/pennyfarthing_scripts/tests/__pycache__/test_bc.cpython-314-pytest-9.0.2.pyc +0 -0
  409. package/pennyfarthing_scripts/tests/__pycache__/test_bikerack.cpython-314-pytest-9.0.2.pyc +0 -0
  410. package/pennyfarthing_scripts/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
  411. package/pennyfarthing_scripts/tests/__pycache__/test_cli_modules.cpython-314-pytest-9.0.2.pyc +0 -0
  412. package/pennyfarthing_scripts/tests/__pycache__/test_cli_normalization.cpython-314-pytest-9.0.2.pyc +0 -0
  413. package/pennyfarthing_scripts/tests/__pycache__/test_codemarkers.cpython-314-pytest-9.0.2.pyc +0 -0
  414. package/pennyfarthing_scripts/tests/__pycache__/test_common.cpython-314-pytest-9.0.2.pyc +0 -0
  415. package/pennyfarthing_scripts/tests/__pycache__/test_confidence_sm_evaluation.cpython-314-pytest-9.0.2.pyc +0 -0
  416. package/pennyfarthing_scripts/tests/__pycache__/test_confidence_sm_gate.cpython-314-pytest-9.0.2.pyc +0 -0
  417. package/pennyfarthing_scripts/tests/__pycache__/test_dialogue_manager.cpython-314-pytest-9.0.2.pyc +0 -0
  418. package/pennyfarthing_scripts/tests/__pycache__/test_epic_shard_validation.cpython-314-pytest-9.0.2.pyc +0 -0
  419. package/pennyfarthing_scripts/tests/__pycache__/test_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
  420. package/pennyfarthing_scripts/tests/__pycache__/test_handoff_cli.cpython-314-pytest-9.0.2.pyc +0 -0
  421. package/pennyfarthing_scripts/tests/__pycache__/test_handoff_e2e.cpython-314-pytest-9.0.2.pyc +0 -0
  422. package/pennyfarthing_scripts/tests/__pycache__/test_healthscore.cpython-314-pytest-9.0.2.pyc +0 -0
  423. package/pennyfarthing_scripts/tests/__pycache__/test_jira_package.cpython-314-pytest-9.0.2.pyc +0 -0
  424. package/pennyfarthing_scripts/tests/__pycache__/test_package_structure.cpython-314-pytest-9.0.2.pyc +0 -0
  425. package/pennyfarthing_scripts/tests/__pycache__/test_patch_mode.cpython-314-pytest-9.0.2.pyc +0 -0
  426. package/pennyfarthing_scripts/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
  427. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc +0 -0
  428. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_panel.cpython-314-pytest-9.0.2.pyc +0 -0
  429. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
  430. package/pennyfarthing_scripts/tests/__pycache__/test_story_add.cpython-314-pytest-9.0.2.pyc +0 -0
  431. package/pennyfarthing_scripts/tests/__pycache__/test_story_package.cpython-314-pytest-9.0.2.pyc +0 -0
  432. package/pennyfarthing_scripts/tests/__pycache__/test_story_update.cpython-314-pytest-9.0.2.pyc +0 -0
  433. package/pennyfarthing_scripts/tests/__pycache__/test_tiers.cpython-314-pytest-9.0.2.pyc +0 -0
  434. package/pennyfarthing_scripts/tests/__pycache__/test_token_counting.cpython-314-pytest-9.0.2.pyc +0 -0
  435. package/pennyfarthing_scripts/tests/__pycache__/test_topology_loader.cpython-314-pytest-9.0.2.pyc +0 -0
  436. package/pennyfarthing_scripts/tests/__pycache__/test_tui_focus.cpython-314-pytest-9.0.2.pyc +0 -0
  437. package/pennyfarthing_scripts/tests/__pycache__/test_tui_panel_persistence.cpython-314-pytest-9.0.2.pyc +0 -0
  438. package/pennyfarthing_scripts/tests/__pycache__/test_validate_cmd.cpython-314-pytest-9.0.2.pyc +0 -0
  439. package/pennyfarthing_scripts/tests/__pycache__/test_version_sentinel.cpython-314-pytest-9.0.2.pyc +0 -0
  440. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
  441. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_cli.cpython-314-pytest-9.0.2.pyc +0 -0
  442. package/pennyfarthing_scripts/tests/__pycache__/test_yaml_io.cpython-314-pytest-9.0.2.pyc +0 -0
  443. package/pennyfarthing_scripts/tests/test_bikerack.py +51 -51
  444. package/pennyfarthing_scripts/tests/test_dialogue_manager.py +811 -0
  445. package/pennyfarthing_scripts/tests/test_handoff_cli.py +16 -11
  446. package/pennyfarthing_scripts/tests/test_sprint_panel.py +344 -265
  447. package/pennyfarthing_scripts/tests/test_workflow_check.py +2 -3
  448. package/pennyfarthing_scripts/theme/__pycache__/__init__.cpython-314.pyc +0 -0
  449. package/pennyfarthing_scripts/theme/__pycache__/cli.cpython-314.pyc +0 -0
  450. package/pennyfarthing_scripts/validate/__pycache__/__init__.cpython-314.pyc +0 -0
  451. package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +0 -0
  452. package/pennyfarthing_scripts/validate/adapters/__pycache__/__init__.cpython-314.pyc +0 -0
  453. package/pennyfarthing_scripts/validate/adapters/__pycache__/agent.cpython-314.pyc +0 -0
  454. package/pennyfarthing_scripts/validate/adapters/__pycache__/schema.cpython-314.pyc +0 -0
  455. package/pennyfarthing_scripts/validate/adapters/__pycache__/skill_command.cpython-314.pyc +0 -0
  456. package/pennyfarthing_scripts/validate/adapters/__pycache__/sprint.cpython-314.pyc +0 -0
  457. package/pennyfarthing_scripts/validate/adapters/__pycache__/tandem_awareness.cpython-314.pyc +0 -0
  458. package/pennyfarthing_scripts/validate/adapters/__pycache__/workflow.cpython-314.pyc +0 -0
  459. package/pennyfarthing_scripts/validate/adapters/tandem_awareness.py +254 -0
  460. package/pennyfarthing_scripts/validate/adapters/workflow.py +19 -0
  461. package/pennyfarthing_scripts/validate/cli.py +17 -5
  462. package/pennyfarthing_scripts/welcome_hook.py +3 -149
  463. package/pennyfarthing_scripts/workflow/__init__.py +40 -0
  464. package/pennyfarthing_scripts/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
  465. package/pennyfarthing_scripts/workflow/__pycache__/cli.cpython-314.pyc +0 -0
  466. package/pennyfarthing_scripts/workflow/__pycache__/helpers.cpython-314.pyc +0 -0
  467. package/pennyfarthing_scripts/workflow/__pycache__/scale.cpython-314.pyc +0 -0
  468. package/pennyfarthing_scripts/workflow/__pycache__/state.cpython-314.pyc +0 -0
  469. package/pennyfarthing_scripts/workflow/cli.py +1100 -0
  470. package/pennyfarthing_scripts/workflow/helpers.py +241 -0
  471. package/pennyfarthing_scripts/{workflow.py → workflow/scale.py} +0 -104
  472. package/pennyfarthing_scripts/workflow/state.py +112 -0
  473. package/pennyfarthing_scripts/workflow/team_lifecycle.py +257 -0
  474. package/packages/core/dist/scripts/theme-detail.test.d.ts +0 -10
  475. package/packages/core/dist/scripts/theme-detail.test.js +0 -199
  476. package/pennyfarthing-dist/skills/pf-workflow/scripts/list-workflows.sh +0 -91
  477. package/pennyfarthing-dist/skills/pf-workflow/scripts/resume-workflow.sh +0 -163
  478. package/pennyfarthing-dist/skills/pf-workflow/scripts/show-workflow.sh +0 -138
  479. package/pennyfarthing-dist/skills/pf-workflow/scripts/start-workflow.sh +0 -273
  480. package/pennyfarthing-dist/skills/pf-workflow/scripts/workflow-status.sh +0 -167
  481. package/pennyfarthing_scripts/gate/__pycache__/__init__.cpython-314.pyc +0 -0
  482. package/pennyfarthing_scripts/gate/__pycache__/cli.cpython-314.pyc +0 -0
  483. package/pennyfarthing_scripts/gate/__pycache__/validate.cpython-314.pyc +0 -0
  484. package/pennyfarthing_scripts/tests/__pycache__/test_108_2_remove_handoff_fallback.cpython-314-pytest-9.0.2.pyc +0 -0
  485. package/pennyfarthing_scripts/tests/__pycache__/test_gate_file_resolution.cpython-314-pytest-9.0.2.pyc +0 -0
  486. package/pennyfarthing_scripts/tests/__pycache__/test_gate_runner.cpython-314-pytest-9.0.2.pyc +0 -0
  487. package/pennyfarthing_scripts/tests/__pycache__/test_resolve_gate_file_field.cpython-314-pytest-9.0.2.pyc +0 -0
@@ -1,309 +1,25 @@
1
- #!/usr/bin/env python3
2
1
  """
3
- PostToolUse HookBell Mode + Tandem Injection (Python)
2
+ Backward-compatibility shimbell mode hook moved to hooks/bell_mode.py.
4
3
 
5
- Called by Claude Code after each tool execution. Handles two independent
6
- injection systems:
7
-
8
- 1. Bell queue (Cyclist only) — injects queued user messages when Cyclist
9
- is running and bell_mode is enabled. In CLI sessions this is a no-op.
10
- 2. Tandem observations (always active) — injects backseat agent observations
11
- when tandem observation files exist. No configuration required.
12
-
13
- Bell queue takes precedence: if a queued message exists, tandem is
14
- deferred to the next hook invocation.
15
-
16
- Story: MSSCI-12409 - Hook consistency and WheelHub consolidation
4
+ This file will be removed in a future version.
17
5
  """
18
6
 
19
- import json
20
- import re
21
- import sys
22
- from pathlib import Path
23
-
24
- # Add parent directory to path for imports
25
- sys.path.insert(0, str(Path(__file__).parent))
26
-
27
- from hooks import (
28
- CYCLIST_PORT_FILE,
29
- HookResponse,
30
- find_project_root,
31
- is_bell_mode_enabled,
32
- output_hook_response,
33
- read_port_file,
34
- send_to_cyclist,
7
+ # Re-export public API from new location for existing tests/imports
8
+ from pennyfarthing_scripts.hooks.bell_mode import ( # noqa: F401
9
+ _check_tandem_files as check_tandem_files,
10
+ _get_latest_observation as get_latest_observation,
11
+ _get_tandem_mtime as get_tandem_mtime,
12
+ _read_bell_queue as read_bell_queue,
13
+ _read_tandem_observations as read_tandem_observations,
14
+ _save_tandem_mtime as save_tandem_mtime,
15
+ main,
35
16
  )
36
17
 
37
18
 
38
- def read_bell_queue(project_root: Path) -> list[dict]:
39
- """Read the bell message queue.
40
-
41
- Args:
42
- project_root: Project root directory
43
-
44
- Returns:
45
- List of queued messages, or empty list if none
46
- """
47
- queue_path = project_root / ".pennyfarthing" / "bell-queue.json"
48
- if not queue_path.exists():
49
- return []
50
-
51
- try:
52
- with open(queue_path) as f:
53
- queue = json.load(f)
54
- if isinstance(queue, list):
55
- return queue
56
- except (json.JSONDecodeError, OSError):
57
- pass
58
-
59
- return []
60
-
61
-
62
- def dequeue_message(project_root: Path) -> None:
63
- """Remove the first message from the queue.
64
-
65
- Args:
66
- project_root: Project root directory
67
- """
68
- queue_path = project_root / ".pennyfarthing" / "bell-queue.json"
69
- if not queue_path.exists():
70
- return
71
-
72
- try:
73
- with open(queue_path) as f:
74
- queue = json.load(f)
75
-
76
- if isinstance(queue, list) and len(queue) > 0:
77
- queue = queue[1:] # Remove first item
78
- with open(queue_path, "w") as f:
79
- json.dump(queue, f)
80
- except (json.JSONDecodeError, OSError):
81
- pass
82
-
83
-
84
- def notify_cyclist(project_root: Path, message_text: str) -> None:
85
- """Notify Cyclist browser that a queued message was consumed.
86
-
87
- Args:
88
- project_root: Project root directory
89
- message_text: The message text that was consumed
90
- """
91
- try:
92
- send_to_cyclist(
93
- endpoint="/api/bell-consumed",
94
- data={"text": message_text},
95
- project_root=project_root,
96
- timeout=5,
97
- )
98
- except Exception:
99
- # Ignore errors - don't block hook
100
- pass
101
-
102
-
103
- # =============================================================================
104
- # Tandem Observation Injection (Story 95-7 / MSSCI-14672)
105
- # =============================================================================
106
- # Stubs for tandem observation injection. Dev will implement.
107
-
108
-
109
- def read_tandem_observations(project_root: Path) -> list[Path]:
110
- """Find tandem observation files in .session/ directory.
111
-
112
- Looks for files matching .session/*-tandem-*.md pattern.
113
-
114
- Args:
115
- project_root: Project root directory
116
-
117
- Returns:
118
- List of Path objects for tandem observation files
119
- """
120
- session_dir = project_root / ".session"
121
- if not session_dir.is_dir():
122
- return []
123
- return sorted(session_dir.glob("*-tandem-*.md"))
124
-
125
-
126
- def get_latest_observation(file_content: str) -> dict | None:
127
- """Extract the latest observation entry from a tandem observation file.
128
-
129
- Parses the markdown format to extract the most recent ## [HH:MM] Observation
130
- block, including the observer persona name from the file header.
131
-
132
- Args:
133
- file_content: Full text content of the tandem observation file
134
-
135
- Returns:
136
- Dict with 'persona' and 'text' keys, or None if no observations found
137
- """
138
- # Extract persona from header: **Observer:** agent (Persona Name)
139
- persona_match = re.search(r"\*\*Observer:\*\*\s*\w+\s*\(([^)]+)\)", file_content)
140
- persona = persona_match.group(1) if persona_match else "Unknown"
141
-
142
- # Split on observation headers: ## [HH:MM] Observation
143
- entries = re.split(r"## \[\d{1,2}:\d{2}\] Observation\n", file_content)
144
- if len(entries) < 2:
145
- return None
146
-
147
- # Last entry is the most recent observation
148
- last_entry = entries[-1].strip()
149
- # Remove trailing --- separator
150
- last_entry = re.sub(r"\n---\s*$", "", last_entry).strip()
151
- # Remove the **Trigger:** line
152
- lines = last_entry.split("\n")
153
- text_lines = [line for line in lines if not line.startswith("**Trigger:**")]
154
- text = "\n".join(text_lines).strip()
155
-
156
- if not text:
157
- return None
158
-
159
- return {"persona": persona, "text": text}
160
-
161
-
162
19
  def format_tandem_message(persona_name: str, observation_text: str) -> str:
163
- """Format a tandem observation as a bell mode injection message.
164
-
165
- Args:
166
- persona_name: The backseat agent's persona name
167
- observation_text: The observation summary text
168
-
169
- Returns:
170
- Formatted string: [Tandem] {persona_name}: {observation_text}
171
- """
20
+ """Format a tandem observation as a bell mode injection message."""
172
21
  return f"[Tandem] {persona_name}: {observation_text}"
173
22
 
174
23
 
175
- def get_tandem_mtime(project_root: Path, agent: str) -> float:
176
- """Read the last-checked mtime for a tandem agent's observation file.
177
-
178
- Args:
179
- project_root: Project root directory
180
- agent: Agent name (e.g. 'reviewer', 'tea')
181
-
182
- Returns:
183
- Last-checked mtime as float, or 0.0 if no sidecar exists
184
- """
185
- sidecar = project_root / ".session" / f".tandem-mtime-{agent}"
186
- if not sidecar.exists():
187
- return 0.0
188
- try:
189
- return float(sidecar.read_text().strip())
190
- except (ValueError, OSError):
191
- return 0.0
192
-
193
-
194
- def save_tandem_mtime(project_root: Path, agent: str, mtime: float) -> None:
195
- """Save the mtime for a tandem agent's observation file.
196
-
197
- Writes to .session/.tandem-mtime-{agent} sidecar file.
198
-
199
- Args:
200
- project_root: Project root directory
201
- agent: Agent name (e.g. 'reviewer', 'tea')
202
- mtime: The mtime value to save
203
- """
204
- sidecar = project_root / ".session" / f".tandem-mtime-{agent}"
205
- try:
206
- sidecar.write_text(str(mtime))
207
- except OSError:
208
- pass
209
-
210
-
211
- def check_tandem_files(project_root: Path) -> list[dict]:
212
- """Check for new tandem observations and return injection messages.
213
-
214
- Main entry point for tandem injection in the PostToolUse hook.
215
- Always active — no configuration required. The presence of tandem
216
- observation files in .session/ is the only signal needed.
217
-
218
- Args:
219
- project_root: Project root directory
220
-
221
- Returns:
222
- List of dicts with 'agent', 'message' keys for each new observation
223
- """
224
- tandem_files = read_tandem_observations(project_root)
225
- if not tandem_files:
226
- return []
227
-
228
- results = []
229
- for obs_file in tandem_files:
230
- # Extract agent name from filename: *-tandem-{agent}.md
231
- agent_match = re.search(r"-tandem-(\w+)\.md$", obs_file.name)
232
- if not agent_match:
233
- continue
234
- agent = agent_match.group(1)
235
-
236
- # Compare mtime
237
- try:
238
- file_mtime = obs_file.stat().st_mtime
239
- except OSError:
240
- continue
241
- saved_mtime = get_tandem_mtime(project_root, agent)
242
- if file_mtime == saved_mtime:
243
- continue
244
-
245
- # Read and parse
246
- try:
247
- content = obs_file.read_text()
248
- except OSError:
249
- continue
250
- obs = get_latest_observation(content)
251
- if not obs:
252
- # Update mtime even for unparseable files to avoid re-checking
253
- save_tandem_mtime(project_root, agent, file_mtime)
254
- continue
255
-
256
- message = format_tandem_message(obs["persona"], obs["text"])
257
- results.append({"agent": agent, "message": message})
258
-
259
- # Update mtime sidecar
260
- save_tandem_mtime(project_root, agent, file_mtime)
261
-
262
- return results
263
-
264
-
265
- def main() -> None:
266
- """Main entry point for PostToolUse hook."""
267
- try:
268
- # Read and discard stdin (required by hook protocol)
269
- sys.stdin.read()
270
-
271
- # Find project root
272
- project_root = find_project_root()
273
- if not project_root:
274
- sys.exit(0)
275
-
276
- # --- Bell queue (Cyclist only, requires bell_mode: true) ---
277
- # Takes precedence over tandem when active.
278
- is_cyclist = read_port_file(CYCLIST_PORT_FILE, project_root) is not None
279
- if is_cyclist and is_bell_mode_enabled(project_root):
280
- queue = read_bell_queue(project_root)
281
- if queue:
282
- first_message = queue[0]
283
- message_text = first_message.get("text", "")
284
- if message_text:
285
- output_hook_response(HookResponse(
286
- event_name="PostToolUse",
287
- additional_context=f"User feedback: {message_text}",
288
- ))
289
- dequeue_message(project_root)
290
- notify_cyclist(project_root, message_text)
291
- sys.exit(0)
292
-
293
- # --- Tandem observations (always active, no config required) ---
294
- tandem_results = check_tandem_files(project_root)
295
- if tandem_results:
296
- output_hook_response(HookResponse(
297
- event_name="PostToolUse",
298
- additional_context=tandem_results[0]["message"],
299
- ))
300
-
301
- sys.exit(0)
302
-
303
- except Exception as e:
304
- print(f"[bellmode-hook] Error: {e}", file=sys.stderr)
305
- sys.exit(0)
306
-
307
-
308
24
  if __name__ == "__main__":
309
25
  main()
@@ -0,0 +1,119 @@
1
+ """AuditLogPanel — Real-time tool event audit log for BikeRack TUI.
2
+
3
+ Story 110-8: Subscribes to /ws/spans, displays tool events in a native
4
+ Textual DataTable with timestamp, tool name, input excerpt, and result.
5
+
6
+ Uses native Textual widgets (DataTable) instead of Rich renderables.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from datetime import datetime, timezone
12
+ from typing import Any
13
+
14
+ from rich.console import Console
15
+ from textual._context import NoActiveAppError
16
+ from textual.widgets import DataTable
17
+
18
+ from pennyfarthing_scripts.bikerack.base_panel import PANEL_ICONS, BasePanel
19
+
20
+ MAX_INPUT_LENGTH = 80
21
+
22
+ # Shared fallback console for offline column/cell measurement
23
+ _FALLBACK_CONSOLE = Console()
24
+
25
+
26
+ class _OfflineDataTable(DataTable):
27
+ """DataTable subclass that works without an active Textual app.
28
+
29
+ Textual's DataTable.add_columns/add_row need self.app.console for
30
+ column width measurement. This subclass provides a fallback Rich Console
31
+ so the table can be constructed and populated in unit tests.
32
+ """
33
+
34
+ @property
35
+ def app(self):
36
+ try:
37
+ return super().app
38
+ except NoActiveAppError:
39
+ return type("_Stub", (), {"console": _FALLBACK_CONSOLE})()
40
+
41
+
42
+ class AuditLogPanel(BasePanel):
43
+ """Audit log panel — real-time tool event display via DataTable.
44
+
45
+ Subscribes to the 'spans' WebSocket channel and renders tool events
46
+ in a native Textual DataTable with columns: Time, Tool, Input, Result.
47
+ """
48
+
49
+ channel: str = "spans"
50
+ panel_name: str = "Audit Log"
51
+ icon: str = PANEL_ICONS["audit-log"][0]
52
+
53
+ def __init__(self, client=None, **kwargs):
54
+ super().__init__(client=client, **kwargs)
55
+ self._table = _OfflineDataTable()
56
+ self._table.add_columns("Time", "Tool", "Input", "Result")
57
+
58
+ def handle_message(self, message: dict[str, Any] | None) -> None:
59
+ """Handle incoming WebSocket messages for tool events.
60
+
61
+ Overrides BasePanel.handle_message to manipulate DataTable directly
62
+ instead of going through render_panel/post_message.
63
+ """
64
+ if not self._mounted or message is None:
65
+ return
66
+ if not isinstance(message, dict):
67
+ return
68
+
69
+ msg_type = message.get("type")
70
+
71
+ if msg_type == "init":
72
+ spans = message.get("spans")
73
+ if not isinstance(spans, list):
74
+ return
75
+ self._table.clear()
76
+ for span in spans:
77
+ if isinstance(span, dict):
78
+ self._add_row(span)
79
+
80
+ elif msg_type == "span":
81
+ span = message.get("span")
82
+ if isinstance(span, dict):
83
+ self._add_row(span)
84
+
85
+ def _add_row(self, span: dict[str, Any]) -> None:
86
+ """Add a single tool event row to the DataTable."""
87
+ timestamp = span.get("timestamp")
88
+ tool_name = span.get("toolName", "")
89
+ input_text = span.get("input", "")
90
+ success = span.get("success")
91
+
92
+ time_str = _format_timestamp(timestamp)
93
+
94
+ if len(input_text) > MAX_INPUT_LENGTH:
95
+ input_text = input_text[: MAX_INPUT_LENGTH - 1] + "\u2026"
96
+
97
+ if success is True:
98
+ result = "\u2713"
99
+ elif success is False:
100
+ result = "\u2717"
101
+ else:
102
+ result = "\u2014"
103
+
104
+ self._table.add_row(time_str, tool_name, input_text, result)
105
+
106
+ def render_panel(self, payload: dict[str, Any]) -> Any:
107
+ """Not used — handle_message manages DataTable directly."""
108
+ return self._table
109
+
110
+
111
+ def _format_timestamp(ts: Any) -> str:
112
+ """Format millisecond timestamp to HH:MM:SS."""
113
+ if ts is None:
114
+ return "\u2014"
115
+ try:
116
+ dt = datetime.fromtimestamp(float(ts) / 1000, tz=timezone.utc)
117
+ return dt.strftime("%H:%M:%S")
118
+ except (ValueError, TypeError, OSError):
119
+ return "\u2014"
@@ -6,12 +6,13 @@ list with status indicators (running, completed, failed).
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
+ import time
9
10
  from typing import Any
10
11
 
11
12
  from rich.console import Group
12
13
  from rich.text import Text
13
14
 
14
- from pennyfarthing_scripts.bikerack.base_panel import PANEL_ICONS, BasePanel
15
+ from pennyfarthing_scripts.bikerack.base_panel import PANEL_ICONS, BasePanel, format_duration
15
16
 
16
17
 
17
18
  class BackgroundPanel(BasePanel):
@@ -25,12 +26,62 @@ class BackgroundPanel(BasePanel):
25
26
  panel_name: str = "Background"
26
27
  icon: str = PANEL_ICONS["background"][0]
27
28
 
29
+ def __init__(self, client=None, **kwargs):
30
+ super().__init__(client=client, **kwargs)
31
+ self._timer = None
32
+
33
+ def on_mount(self) -> None:
34
+ """Subscribe to channel and start elapsed timer."""
35
+ super().on_mount()
36
+ self._timer = self.set_interval(1, self._tick)
37
+
38
+ def on_unmount(self) -> None:
39
+ """Stop timer and cleanup."""
40
+ if self._timer is not None:
41
+ self._timer.stop()
42
+ super().on_unmount()
43
+
44
+ def _tick(self) -> None:
45
+ """Re-render every second to update elapsed times."""
46
+ if self._last_payload is not None:
47
+ rendered = self.render_panel(self._last_payload)
48
+ try:
49
+ self.update(rendered)
50
+ except Exception:
51
+ pass
52
+
28
53
  def render_panel(self, payload: dict[str, Any]) -> Any:
29
54
  """Render background task data from WebSocket payload."""
30
55
  tasks = payload.get("tasks", [])
31
56
  if not isinstance(tasks, list) or not tasks:
32
57
  return Text("No background tasks", style="dim italic")
33
58
 
59
+ running = 0
60
+ done = 0
61
+ failed = 0
62
+ for task in tasks:
63
+ if not isinstance(task, dict):
64
+ continue
65
+ if task.get("completedAt") is not None:
66
+ if task.get("success"):
67
+ done += 1
68
+ else:
69
+ failed += 1
70
+ else:
71
+ running += 1
72
+
73
+ # Summary header
74
+ summary = Text()
75
+ summary.append(f"{running} running", style="yellow")
76
+ summary.append(" ")
77
+ summary.append(f"{done} done", style="green")
78
+ if failed > 0:
79
+ summary.append(" ")
80
+ summary.append(f"{failed} failed", style="red")
81
+
82
+ separator = Text("─" * 30, style="dim")
83
+
84
+ # Task list
34
85
  parts: list[Any] = []
35
86
  for task in tasks:
36
87
  if not isinstance(task, dict):
@@ -40,7 +91,7 @@ class BackgroundPanel(BasePanel):
40
91
  if not parts:
41
92
  return Text("No background tasks", style="dim italic")
42
93
 
43
- return Group(*parts)
94
+ return Group(summary, separator, *parts)
44
95
 
45
96
 
46
97
  def _render_task(task: dict[str, Any]) -> Text:
@@ -48,6 +99,7 @@ def _render_task(task: dict[str, Any]) -> Text:
48
99
  description = task.get("description", "Unknown task")
49
100
  subagent_type = task.get("subagentType", "")
50
101
  completed_at = task.get("completedAt")
102
+ started_at = task.get("startedAt")
51
103
  success = task.get("success")
52
104
  result = task.get("result", "")
53
105
  error = task.get("error", "")
@@ -60,7 +112,12 @@ def _render_task(task: dict[str, Any]) -> Text:
60
112
  line.append(description)
61
113
  if subagent_type:
62
114
  line.append(f" [{subagent_type}]", style="dim")
63
- line.append(" done", style="green")
115
+ # Show duration if timestamps available
116
+ duration = _calc_duration(started_at, completed_at)
117
+ if duration:
118
+ line.append(f" — {duration}", style="green")
119
+ else:
120
+ line.append(" — done", style="green")
64
121
  if result:
65
122
  line.append(f" ({result})", style="dim green")
66
123
  else:
@@ -68,7 +125,11 @@ def _render_task(task: dict[str, Any]) -> Text:
68
125
  line.append(description)
69
126
  if subagent_type:
70
127
  line.append(f" [{subagent_type}]", style="dim")
71
- line.append(" failed", style="red")
128
+ duration = _calc_duration(started_at, completed_at)
129
+ if duration:
130
+ line.append(f" — {duration}", style="red")
131
+ else:
132
+ line.append(" — failed", style="red")
72
133
  if error:
73
134
  line.append(f"\n {error}", style="red")
74
135
  else:
@@ -76,6 +137,26 @@ def _render_task(task: dict[str, Any]) -> Text:
76
137
  line.append(description)
77
138
  if subagent_type:
78
139
  line.append(f" [{subagent_type}]", style="dim")
79
- line.append(" running", style="yellow")
140
+ # Live elapsed time for running tasks
141
+ if started_at:
142
+ try:
143
+ elapsed = time.time() - float(started_at) / 1000 # startedAt is typically ms
144
+ line.append(f" — {format_duration(elapsed)}", style="yellow")
145
+ except (ValueError, TypeError):
146
+ line.append(" — running", style="yellow")
147
+ else:
148
+ line.append(" — running", style="yellow")
80
149
 
81
150
  return line
151
+
152
+
153
+ def _calc_duration(started_at, completed_at) -> str:
154
+ """Calculate duration string from timestamps."""
155
+ if started_at is None or completed_at is None:
156
+ return ""
157
+ try:
158
+ start = float(started_at) / 1000 # ms to seconds
159
+ end = float(completed_at) / 1000
160
+ return format_duration(end - start)
161
+ except (ValueError, TypeError):
162
+ return ""