@pennyfarthing/core 11.2.1 → 11.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (405) hide show
  1. package/README.md +102 -42
  2. package/package.json +1 -1
  3. package/packages/core/dist/cli/commands/doctor-legacy.test.js +2 -2
  4. package/packages/core/dist/cli/commands/doctor-legacy.test.js.map +1 -1
  5. package/packages/core/dist/cli/commands/doctor.d.ts +55 -0
  6. package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
  7. package/packages/core/dist/cli/commands/doctor.js +324 -50
  8. package/packages/core/dist/cli/commands/doctor.js.map +1 -1
  9. package/packages/core/dist/cli/commands/init.d.ts +12 -0
  10. package/packages/core/dist/cli/commands/init.d.ts.map +1 -1
  11. package/packages/core/dist/cli/commands/init.js +45 -0
  12. package/packages/core/dist/cli/commands/init.js.map +1 -1
  13. package/packages/core/dist/cli/commands/pyproject-install.test.d.ts +19 -0
  14. package/packages/core/dist/cli/commands/pyproject-install.test.d.ts.map +1 -0
  15. package/packages/core/dist/cli/commands/pyproject-install.test.js +261 -0
  16. package/packages/core/dist/cli/commands/pyproject-install.test.js.map +1 -0
  17. package/packages/core/dist/cli/commands/stale-artifacts-cleanup.test.d.ts +17 -0
  18. package/packages/core/dist/cli/commands/stale-artifacts-cleanup.test.d.ts.map +1 -0
  19. package/packages/core/dist/cli/commands/stale-artifacts-cleanup.test.js +470 -0
  20. package/packages/core/dist/cli/commands/stale-artifacts-cleanup.test.js.map +1 -0
  21. package/packages/core/dist/cli/commands/update-consolidation.test.js +14 -6
  22. package/packages/core/dist/cli/commands/update-consolidation.test.js.map +1 -1
  23. package/packages/core/dist/cli/commands/update.d.ts.map +1 -1
  24. package/packages/core/dist/cli/commands/update.js +31 -2
  25. package/packages/core/dist/cli/commands/update.js.map +1 -1
  26. package/packages/core/dist/cli/index.js +2 -0
  27. package/packages/core/dist/cli/index.js.map +1 -1
  28. package/packages/core/dist/cli/utils/python.d.ts.map +1 -1
  29. package/packages/core/dist/cli/utils/python.js +11 -0
  30. package/packages/core/dist/cli/utils/python.js.map +1 -1
  31. package/packages/core/dist/cli/utils/settings-hook-migration.test.d.ts +17 -0
  32. package/packages/core/dist/cli/utils/settings-hook-migration.test.d.ts.map +1 -0
  33. package/packages/core/dist/cli/utils/settings-hook-migration.test.js +382 -0
  34. package/packages/core/dist/cli/utils/settings-hook-migration.test.js.map +1 -0
  35. package/packages/core/dist/cli/utils/settings.d.ts +0 -4
  36. package/packages/core/dist/cli/utils/settings.d.ts.map +1 -1
  37. package/packages/core/dist/cli/utils/settings.js +45 -27
  38. package/packages/core/dist/cli/utils/settings.js.map +1 -1
  39. package/packages/core/dist/cli/utils/stale-artifacts.d.ts +59 -0
  40. package/packages/core/dist/cli/utils/stale-artifacts.d.ts.map +1 -0
  41. package/packages/core/dist/cli/utils/stale-artifacts.js +163 -0
  42. package/packages/core/dist/cli/utils/stale-artifacts.js.map +1 -0
  43. package/packages/core/dist/consultation/dialogue-manager.d.ts +1 -1
  44. package/packages/core/dist/consultation/dialogue-manager.d.ts.map +1 -1
  45. package/packages/core/dist/consultation/dialogue-manager.js +1 -1
  46. package/packages/core/dist/consultation/dialogue-manager.js.map +1 -1
  47. package/packages/core/dist/consultation/dialogue-manager.test.js.map +1 -1
  48. package/packages/core/dist/consultation/tandem-metrics.test.js.map +1 -1
  49. package/packages/core/dist/public/css/react.css +1 -1
  50. package/packages/core/dist/public/js/react/react.js +9 -9
  51. package/packages/core/dist/server/api/git.d.ts.map +1 -1
  52. package/packages/core/dist/server/api/git.js +0 -1
  53. package/packages/core/dist/server/api/git.js.map +1 -1
  54. package/packages/core/dist/server/api/index.d.ts +2 -0
  55. package/packages/core/dist/server/api/index.d.ts.map +1 -1
  56. package/packages/core/dist/server/api/index.js +2 -0
  57. package/packages/core/dist/server/api/index.js.map +1 -1
  58. package/packages/core/dist/server/api/project-info.d.ts +11 -0
  59. package/packages/core/dist/server/api/project-info.d.ts.map +1 -0
  60. package/packages/core/dist/server/api/project-info.js +18 -0
  61. package/packages/core/dist/server/api/project-info.js.map +1 -0
  62. package/packages/core/dist/server/otlp-receiver.d.ts.map +1 -1
  63. package/packages/core/dist/server/otlp-receiver.js +18 -1
  64. package/packages/core/dist/server/otlp-receiver.js.map +1 -1
  65. package/packages/core/dist/server/otlp-receiver.test.js +1 -1
  66. package/packages/core/dist/server/otlp-receiver.test.js.map +1 -1
  67. package/packages/core/dist/server/server.d.ts.map +1 -1
  68. package/packages/core/dist/server/server.js +3 -2
  69. package/packages/core/dist/server/server.js.map +1 -1
  70. package/packages/core/dist/server/server.test.d.ts +1 -1
  71. package/packages/core/dist/server/server.test.js +8 -8
  72. package/packages/core/dist/server/settings.d.ts +1 -0
  73. package/packages/core/dist/server/settings.d.ts.map +1 -1
  74. package/packages/core/dist/server/settings.js +18 -0
  75. package/packages/core/dist/server/settings.js.map +1 -1
  76. package/packages/core/dist/workflow/tandem-workflow-templates.test.js +7 -5
  77. package/packages/core/dist/workflow/tandem-workflow-templates.test.js.map +1 -1
  78. package/packages/core/dist/workflow/workflow-migration.test.js +6 -5
  79. package/packages/core/dist/workflow/workflow-migration.test.js.map +1 -1
  80. package/packages/core/dist/workflow/workflow-team-templates.test.d.ts +17 -0
  81. package/packages/core/dist/workflow/workflow-team-templates.test.d.ts.map +1 -0
  82. package/packages/core/dist/workflow/workflow-team-templates.test.js +275 -0
  83. package/packages/core/dist/workflow/workflow-team-templates.test.js.map +1 -0
  84. package/pennyfarthing-dist/agents/dev.md +19 -4
  85. package/pennyfarthing-dist/agents/devops.md +2 -10
  86. package/pennyfarthing-dist/agents/reviewer-preflight.md +4 -5
  87. package/pennyfarthing-dist/agents/reviewer.md +17 -4
  88. package/pennyfarthing-dist/agents/sm-finish.md +1 -1
  89. package/pennyfarthing-dist/agents/sm-setup.md +7 -7
  90. package/pennyfarthing-dist/agents/sm.md +16 -29
  91. package/pennyfarthing-dist/agents/tea.md +2 -2
  92. package/pennyfarthing-dist/agents/testing-runner.md +1 -1
  93. package/pennyfarthing-dist/commands/pf-architect.md +1 -1
  94. package/pennyfarthing-dist/commands/pf-ba.md +1 -1
  95. package/pennyfarthing-dist/commands/pf-chore.md +2 -2
  96. package/pennyfarthing-dist/commands/pf-dev.md +1 -1
  97. package/pennyfarthing-dist/commands/pf-devops.md +1 -1
  98. package/pennyfarthing-dist/commands/pf-epic.md +6 -6
  99. package/pennyfarthing-dist/commands/pf-git.md +10 -10
  100. package/pennyfarthing-dist/commands/pf-health-check.md +31 -12
  101. package/pennyfarthing-dist/commands/pf-help.md +12 -12
  102. package/pennyfarthing-dist/commands/pf-orchestrator.md +1 -1
  103. package/pennyfarthing-dist/commands/pf-pm.md +1 -1
  104. package/pennyfarthing-dist/commands/pf-prime.md +8 -8
  105. package/pennyfarthing-dist/commands/pf-reviewer.md +1 -1
  106. package/pennyfarthing-dist/commands/pf-session.md +7 -7
  107. package/pennyfarthing-dist/commands/pf-sm.md +1 -1
  108. package/pennyfarthing-dist/commands/pf-sprint.md +7 -7
  109. package/pennyfarthing-dist/commands/pf-tea.md +1 -1
  110. package/pennyfarthing-dist/commands/pf-tech-writer.md +1 -1
  111. package/pennyfarthing-dist/commands/pf-theme.md +9 -9
  112. package/pennyfarthing-dist/commands/pf-ux-designer.md +1 -1
  113. package/pennyfarthing-dist/commands/pf-work.md +1 -1
  114. package/pennyfarthing-dist/gates/{confidence-sm.md → confidence.md} +16 -17
  115. package/pennyfarthing-dist/gates/dev-exit.md +75 -0
  116. package/pennyfarthing-dist/gates/merge-ready.md +49 -0
  117. package/pennyfarthing-dist/gates/release-ready.md +95 -0
  118. package/pennyfarthing-dist/gates/reviewer-preflight-check.md +90 -0
  119. package/pennyfarthing-dist/gates/sm-setup-exit.md +82 -0
  120. package/pennyfarthing-dist/guides/agent-behavior.md +129 -20
  121. package/pennyfarthing-dist/guides/agent-coordination.md +10 -10
  122. package/pennyfarthing-dist/guides/agent-tag-taxonomy.md +6 -6
  123. package/pennyfarthing-dist/guides/agent-template-tactical.md +1 -1
  124. package/pennyfarthing-dist/guides/bell-mode.md +1 -1
  125. package/pennyfarthing-dist/guides/bikerack.md +10 -10
  126. package/pennyfarthing-dist/guides/brownfield-tools.md +24 -24
  127. package/pennyfarthing-dist/guides/command-tag-taxonomy.md +1 -1
  128. package/pennyfarthing-dist/guides/gate-schema.md +2 -2
  129. package/pennyfarthing-dist/guides/gates.md +10 -5
  130. package/pennyfarthing-dist/guides/handoff-cli.md +8 -8
  131. package/pennyfarthing-dist/guides/hooks.md +27 -27
  132. package/pennyfarthing-dist/guides/prime.md +2 -2
  133. package/pennyfarthing-dist/guides/reflector.md +1 -1
  134. package/pennyfarthing-dist/guides/skill-schema.md +6 -6
  135. package/pennyfarthing-dist/guides/tandem-protocol.md +3 -3
  136. package/pennyfarthing-dist/guides/workflow-schema.md +1 -1
  137. package/pennyfarthing-dist/guides/worktree-mode.md +3 -3
  138. package/pennyfarthing-dist/guides/xml-tags.md +8 -8
  139. package/pennyfarthing-dist/scripts/README.md +4 -4
  140. package/pennyfarthing-dist/scripts/core/agent-session.sh +2 -5
  141. package/pennyfarthing-dist/scripts/core/check-context.sh +1 -1
  142. package/pennyfarthing-dist/scripts/core/pf.sh +5 -0
  143. package/pennyfarthing-dist/scripts/core/phase-check-start.sh +2 -5
  144. package/pennyfarthing-dist/scripts/core/prime.sh +2 -25
  145. package/pennyfarthing-dist/scripts/git/README.md +14 -14
  146. package/pennyfarthing-dist/scripts/git/create-feature-branches.sh +2 -3
  147. package/pennyfarthing-dist/scripts/git/git-status-all.sh +2 -3
  148. package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +2 -3
  149. package/pennyfarthing-dist/scripts/git/worktree-manager.sh +2 -4
  150. package/pennyfarthing-dist/scripts/hooks/README.md +6 -6
  151. package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +3 -3
  152. package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +3 -3
  153. package/pennyfarthing-dist/scripts/hooks/context-warning.sh +3 -3
  154. package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +3 -3
  155. package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +5 -4
  156. package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +2 -1
  157. package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +3 -3
  158. package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +3 -3
  159. package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +3 -3
  160. package/pennyfarthing-dist/scripts/hooks/session-start.sh +3 -3
  161. package/pennyfarthing-dist/scripts/hooks/session-stop.sh +3 -3
  162. package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +3 -3
  163. package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +3 -4
  164. package/pennyfarthing-dist/scripts/lib/env.sh +34 -0
  165. package/pennyfarthing-dist/scripts/lib/find-root.sh +5 -0
  166. package/pennyfarthing-dist/scripts/lib/run-pf.sh +43 -0
  167. package/pennyfarthing-dist/scripts/misc/README.md +1 -1
  168. package/pennyfarthing-dist/scripts/misc/statusline.sh +3 -3
  169. package/pennyfarthing-dist/scripts/sprint/README.md +21 -21
  170. package/pennyfarthing-dist/scripts/workflow/README.md +2 -2
  171. package/pennyfarthing-dist/scripts/workflow/finish-story.sh +2 -16
  172. package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +3 -3
  173. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +3 -3
  174. package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +3 -3
  175. package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +3 -3
  176. package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +3 -3
  177. package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +3 -3
  178. package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +3 -3
  179. package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +3 -3
  180. package/pennyfarthing-dist/skills/pf-bc/examples.md +23 -23
  181. package/pennyfarthing-dist/skills/pf-bc/skill.md +17 -17
  182. package/pennyfarthing-dist/skills/pf-bc/usage.md +8 -8
  183. package/pennyfarthing-dist/skills/pf-jira/SKILL.md +15 -15
  184. package/pennyfarthing-dist/skills/pf-jira/examples.md +48 -48
  185. package/pennyfarthing-dist/skills/pf-jira/usage.md +15 -15
  186. package/pennyfarthing-dist/skills/pf-settings/skill.md +42 -0
  187. package/pennyfarthing-dist/skills/pf-sprint/examples.md +80 -80
  188. package/pennyfarthing-dist/skills/pf-sprint/skill.md +35 -35
  189. package/pennyfarthing-dist/skills/pf-sprint/usage.md +30 -30
  190. package/pennyfarthing-dist/skills/pf-theme/examples.md +15 -15
  191. package/pennyfarthing-dist/skills/pf-theme/skill.md +6 -6
  192. package/pennyfarthing-dist/skills/pf-theme/usage.md +5 -5
  193. package/pennyfarthing-dist/skills/pf-workflow/examples.md +27 -27
  194. package/pennyfarthing-dist/skills/pf-workflow/skill.md +11 -11
  195. package/pennyfarthing-dist/skills/pf-workflow/usage.md +11 -11
  196. package/pennyfarthing-dist/skills/skill-registry.yaml +34 -19
  197. package/pennyfarthing-dist/templates/pyproject.toml +27 -0
  198. package/pennyfarthing-dist/templates/settings.local.json.template +11 -11
  199. package/pennyfarthing-dist/workflows/bdd-tandem.yaml +7 -3
  200. package/pennyfarthing-dist/workflows/bdd-team.yaml +89 -0
  201. package/pennyfarthing-dist/workflows/bdd.yaml +7 -3
  202. package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-05-import-to-future.md +1 -1
  203. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-01-analyze.md +1 -1
  204. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-04-verify.md +1 -1
  205. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +1 -1
  206. package/pennyfarthing-dist/workflows/installation-check/steps/step-01-foundation.md +77 -0
  207. package/pennyfarthing-dist/workflows/installation-check/steps/step-02-commands.md +82 -0
  208. package/pennyfarthing-dist/workflows/installation-check/steps/step-03-hooks.md +121 -0
  209. package/pennyfarthing-dist/workflows/installation-check/steps/step-04-scripts.md +83 -0
  210. package/pennyfarthing-dist/workflows/installation-check/steps/step-05-layout.md +81 -0
  211. package/pennyfarthing-dist/workflows/installation-check/steps/step-06-legacy.md +94 -0
  212. package/pennyfarthing-dist/workflows/installation-check/steps/step-07-tools.md +80 -0
  213. package/pennyfarthing-dist/workflows/installation-check/steps/step-08-summary.md +99 -0
  214. package/pennyfarthing-dist/workflows/installation-check/workflow.yaml +47 -0
  215. package/pennyfarthing-dist/workflows/project-setup/steps/step-01-discover.md +47 -0
  216. package/pennyfarthing-dist/workflows/tdd-tandem.yaml +7 -3
  217. package/pennyfarthing-dist/workflows/tdd-team.yaml +80 -0
  218. package/pennyfarthing-dist/workflows/tdd.yaml +7 -3
  219. package/pennyfarthing-dist/workflows/trivial.yaml +7 -3
  220. package/pennyfarthing_scripts/__init__.py +1 -1
  221. package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  222. package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  223. package/pennyfarthing_scripts/__pycache__/cli.cpython-311.pyc +0 -0
  224. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  225. package/pennyfarthing_scripts/__pycache__/context.cpython-311.pyc +0 -0
  226. package/pennyfarthing_scripts/__pycache__/context.cpython-314.pyc +0 -0
  227. package/pennyfarthing_scripts/bc/__pycache__/__init__.cpython-311.pyc +0 -0
  228. package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-311.pyc +0 -0
  229. package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-314.pyc +0 -0
  230. package/pennyfarthing_scripts/bc/__pycache__/focus.cpython-311.pyc +0 -0
  231. package/pennyfarthing_scripts/bc/__pycache__/focus.cpython-314.pyc +0 -0
  232. package/pennyfarthing_scripts/bc/__pycache__/split.cpython-314.pyc +0 -0
  233. package/pennyfarthing_scripts/bc/cli.py +23 -2
  234. package/pennyfarthing_scripts/bc/focus.py +1 -0
  235. package/pennyfarthing_scripts/bc/split.py +52 -0
  236. package/pennyfarthing_scripts/bellmode_hook.py +2 -5
  237. package/pennyfarthing_scripts/bikerack/__pycache__/__init__.cpython-311.pyc +0 -0
  238. package/pennyfarthing_scripts/bikerack/__pycache__/audit_log_panel.cpython-314.pyc +0 -0
  239. package/pennyfarthing_scripts/bikerack/__pycache__/background_panel.cpython-314.pyc +0 -0
  240. package/pennyfarthing_scripts/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
  241. package/pennyfarthing_scripts/bikerack/__pycache__/changed_panel.cpython-314.pyc +0 -0
  242. package/pennyfarthing_scripts/bikerack/__pycache__/cli.cpython-311.pyc +0 -0
  243. package/pennyfarthing_scripts/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
  244. package/pennyfarthing_scripts/bikerack/__pycache__/context_meter_footer.cpython-314.pyc +0 -0
  245. package/pennyfarthing_scripts/bikerack/__pycache__/debug_panel.cpython-314.pyc +0 -0
  246. package/pennyfarthing_scripts/bikerack/__pycache__/diffs_panel.cpython-314.pyc +0 -0
  247. package/pennyfarthing_scripts/bikerack/__pycache__/events.cpython-314.pyc +0 -0
  248. package/pennyfarthing_scripts/bikerack/__pycache__/git_panel.cpython-314.pyc +0 -0
  249. package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-311.pyc +0 -0
  250. package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
  251. package/pennyfarthing_scripts/bikerack/__pycache__/portrait_resolver.cpython-314.pyc +0 -0
  252. package/pennyfarthing_scripts/bikerack/__pycache__/progress_panel.cpython-314.pyc +0 -0
  253. package/pennyfarthing_scripts/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
  254. package/pennyfarthing_scripts/bikerack/__pycache__/story_detail_data.cpython-314.pyc +0 -0
  255. package/pennyfarthing_scripts/bikerack/__pycache__/story_detail_screen.cpython-314.pyc +0 -0
  256. package/pennyfarthing_scripts/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
  257. package/pennyfarthing_scripts/bikerack/__pycache__/ws_client.cpython-314.pyc +0 -0
  258. package/pennyfarthing_scripts/bikerack/audit_log_panel.py +48 -6
  259. package/pennyfarthing_scripts/bikerack/context_meter_footer.py +53 -3
  260. package/pennyfarthing_scripts/bikerack/launcher.py +6 -6
  261. package/pennyfarthing_scripts/bikerack/progress_panel.py +0 -1
  262. package/pennyfarthing_scripts/bikerack/sprint_panel.py +1 -1
  263. package/pennyfarthing_scripts/bikerack/story_detail_data.py +4 -1
  264. package/pennyfarthing_scripts/bikerack/story_detail_screen.py +2 -1
  265. package/pennyfarthing_scripts/bikerack/tui.py +214 -10
  266. package/pennyfarthing_scripts/bikerack/ws_client.py +2 -2
  267. package/pennyfarthing_scripts/cli.py +5 -0
  268. package/pennyfarthing_scripts/common/__pycache__/__init__.cpython-311.pyc +0 -0
  269. package/pennyfarthing_scripts/common/__pycache__/config.cpython-311.pyc +0 -0
  270. package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
  271. package/pennyfarthing_scripts/common/__pycache__/output.cpython-311.pyc +0 -0
  272. package/pennyfarthing_scripts/common/__pycache__/pr_config.cpython-314.pyc +0 -0
  273. package/pennyfarthing_scripts/common/config.py +29 -2
  274. package/pennyfarthing_scripts/consultation/__pycache__/__init__.cpython-311.pyc +0 -0
  275. package/pennyfarthing_scripts/consultation/__pycache__/__init__.cpython-314.pyc +0 -0
  276. package/pennyfarthing_scripts/consultation/__pycache__/cli.cpython-311.pyc +0 -0
  277. package/pennyfarthing_scripts/consultation/__pycache__/cli.cpython-314.pyc +0 -0
  278. package/pennyfarthing_scripts/consultation/cli.py +3 -3
  279. package/pennyfarthing_scripts/context.py +3 -3
  280. package/pennyfarthing_scripts/deadcode/__pycache__/__init__.cpython-311.pyc +0 -0
  281. package/pennyfarthing_scripts/deadcode/__pycache__/cli.cpython-311.pyc +0 -0
  282. package/pennyfarthing_scripts/epic/__pycache__/__init__.cpython-311.pyc +0 -0
  283. package/pennyfarthing_scripts/epic/__pycache__/cli.cpython-311.pyc +0 -0
  284. package/pennyfarthing_scripts/git/hooks_installer.py +2 -3
  285. package/pennyfarthing_scripts/git/status_all.py +1 -1
  286. package/pennyfarthing_scripts/git/worktree.py +2 -2
  287. package/pennyfarthing_scripts/git_group/__pycache__/__init__.cpython-311.pyc +0 -0
  288. package/pennyfarthing_scripts/git_group/__pycache__/cli.cpython-311.pyc +0 -0
  289. package/pennyfarthing_scripts/git_group/__pycache__/cli.cpython-314.pyc +0 -0
  290. package/pennyfarthing_scripts/handoff/__pycache__/__init__.cpython-311.pyc +0 -0
  291. package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-311.pyc +0 -0
  292. package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-314.pyc +0 -0
  293. package/pennyfarthing_scripts/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
  294. package/pennyfarthing_scripts/handoff/__pycache__/marker.cpython-314.pyc +0 -0
  295. package/pennyfarthing_scripts/handoff/__pycache__/phase_check.cpython-314.pyc +0 -0
  296. package/pennyfarthing_scripts/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
  297. package/pennyfarthing_scripts/healthscore/__pycache__/__init__.cpython-311.pyc +0 -0
  298. package/pennyfarthing_scripts/healthscore/__pycache__/analyze.cpython-311.pyc +0 -0
  299. package/pennyfarthing_scripts/healthscore/__pycache__/cli.cpython-311.pyc +0 -0
  300. package/pennyfarthing_scripts/healthscore/__pycache__/models.cpython-311.pyc +0 -0
  301. package/pennyfarthing_scripts/hooks/__init__.py +8 -3
  302. package/pennyfarthing_scripts/hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  303. package/pennyfarthing_scripts/hooks/__pycache__/__init__.cpython-314.pyc +0 -0
  304. package/pennyfarthing_scripts/hooks/__pycache__/bell_mode.cpython-311.pyc +0 -0
  305. package/pennyfarthing_scripts/hooks/__pycache__/bell_mode.cpython-314.pyc +0 -0
  306. package/pennyfarthing_scripts/hooks/__pycache__/cli.cpython-311.pyc +0 -0
  307. package/pennyfarthing_scripts/hooks/__pycache__/cli.cpython-314.pyc +0 -0
  308. package/pennyfarthing_scripts/hooks/__pycache__/context_breaker.cpython-311.pyc +0 -0
  309. package/pennyfarthing_scripts/hooks/__pycache__/context_breaker.cpython-314.pyc +0 -0
  310. package/pennyfarthing_scripts/hooks/__pycache__/context_warning.cpython-311.pyc +0 -0
  311. package/pennyfarthing_scripts/hooks/__pycache__/context_warning.cpython-314.pyc +0 -0
  312. package/pennyfarthing_scripts/hooks/__pycache__/cyclist_pretooluse.cpython-311.pyc +0 -0
  313. package/pennyfarthing_scripts/hooks/__pycache__/cyclist_pretooluse.cpython-314.pyc +0 -0
  314. package/pennyfarthing_scripts/hooks/__pycache__/pre_edit_check.cpython-311.pyc +0 -0
  315. package/pennyfarthing_scripts/hooks/__pycache__/pre_edit_check.cpython-314.pyc +0 -0
  316. package/pennyfarthing_scripts/hooks/__pycache__/reflector_check.cpython-311.pyc +0 -0
  317. package/pennyfarthing_scripts/hooks/__pycache__/reflector_check.cpython-314.pyc +0 -0
  318. package/pennyfarthing_scripts/hooks/__pycache__/schema_validation.cpython-311.pyc +0 -0
  319. package/pennyfarthing_scripts/hooks/__pycache__/schema_validation.cpython-314.pyc +0 -0
  320. package/pennyfarthing_scripts/hooks/__pycache__/session_start.cpython-311.pyc +0 -0
  321. package/pennyfarthing_scripts/hooks/__pycache__/session_start.cpython-314.pyc +0 -0
  322. package/pennyfarthing_scripts/hooks/__pycache__/session_stop.cpython-314.pyc +0 -0
  323. package/pennyfarthing_scripts/hooks/__pycache__/sprint_yaml_validation.cpython-311.pyc +0 -0
  324. package/pennyfarthing_scripts/hooks/__pycache__/sprint_yaml_validation.cpython-314.pyc +0 -0
  325. package/pennyfarthing_scripts/hooks/__pycache__/statusline.cpython-311.pyc +0 -0
  326. package/pennyfarthing_scripts/hooks/__pycache__/statusline.cpython-314.pyc +0 -0
  327. package/pennyfarthing_scripts/hooks/bell_mode.py +0 -1
  328. package/pennyfarthing_scripts/hooks/pre_edit_check.py +0 -1
  329. package/pennyfarthing_scripts/hooks/reflector_check.py +1 -2
  330. package/pennyfarthing_scripts/hooks/schema_validation.py +0 -1
  331. package/pennyfarthing_scripts/hooks/session_start.py +6 -8
  332. package/pennyfarthing_scripts/hooks/statusline.py +10 -1
  333. package/pennyfarthing_scripts/hotspots/__pycache__/__init__.cpython-311.pyc +0 -0
  334. package/pennyfarthing_scripts/hotspots/__pycache__/analyze.cpython-311.pyc +0 -0
  335. package/pennyfarthing_scripts/hotspots/__pycache__/cli.cpython-311.pyc +0 -0
  336. package/pennyfarthing_scripts/hotspots/__pycache__/models.cpython-311.pyc +0 -0
  337. package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-311.pyc +0 -0
  338. package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-311.pyc +0 -0
  339. package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-311.pyc +0 -0
  340. package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-311.pyc +0 -0
  341. package/pennyfarthing_scripts/jira/__pycache__/client.cpython-311.pyc +0 -0
  342. package/pennyfarthing_scripts/jira/__pycache__/create.cpython-311.pyc +0 -0
  343. package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-311.pyc +0 -0
  344. package/pennyfarthing_scripts/jira/__pycache__/operations.cpython-311.pyc +0 -0
  345. package/pennyfarthing_scripts/jira/__pycache__/reconcile.cpython-311.pyc +0 -0
  346. package/pennyfarthing_scripts/jira/__pycache__/story.cpython-311.pyc +0 -0
  347. package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-311.pyc +0 -0
  348. package/pennyfarthing_scripts/launch/__pycache__/__init__.cpython-311.pyc +0 -0
  349. package/pennyfarthing_scripts/launch/__pycache__/cli.cpython-311.pyc +0 -0
  350. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  351. package/pennyfarthing_scripts/prime/heatmap.py +3 -15
  352. package/pennyfarthing_scripts/session/__pycache__/__init__.cpython-311.pyc +0 -0
  353. package/pennyfarthing_scripts/session/__pycache__/cli.cpython-311.pyc +0 -0
  354. package/pennyfarthing_scripts/settings/__init__.py +0 -0
  355. package/pennyfarthing_scripts/settings/__pycache__/__init__.cpython-314.pyc +0 -0
  356. package/pennyfarthing_scripts/settings/__pycache__/cli.cpython-314.pyc +0 -0
  357. package/pennyfarthing_scripts/settings/__pycache__/settings.cpython-314.pyc +0 -0
  358. package/pennyfarthing_scripts/settings/cli.py +55 -0
  359. package/pennyfarthing_scripts/settings/settings.py +98 -0
  360. package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-311.pyc +0 -0
  361. package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-311.pyc +0 -0
  362. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-311.pyc +0 -0
  363. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  364. package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-311.pyc +0 -0
  365. package/pennyfarthing_scripts/sprint/__pycache__/epic_update.cpython-311.pyc +0 -0
  366. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-311.pyc +0 -0
  367. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  368. package/pennyfarthing_scripts/sprint/__pycache__/status.cpython-311.pyc +0 -0
  369. package/pennyfarthing_scripts/sprint/__pycache__/story_add.cpython-311.pyc +0 -0
  370. package/pennyfarthing_scripts/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
  371. package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-311.pyc +0 -0
  372. package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
  373. package/pennyfarthing_scripts/sprint/__pycache__/validate_cmd.cpython-311.pyc +0 -0
  374. package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-311.pyc +0 -0
  375. package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-311.pyc +0 -0
  376. package/pennyfarthing_scripts/sprint/__pycache__/yaml_io.cpython-311.pyc +0 -0
  377. package/pennyfarthing_scripts/sprint/cli.py +121 -0
  378. package/pennyfarthing_scripts/sprint/loader.py +154 -3
  379. package/pennyfarthing_scripts/sprint/story_update.py +7 -0
  380. package/pennyfarthing_scripts/tests/__pycache__/test_bikerack.cpython-314-pytest-9.0.2.pyc +0 -0
  381. package/pennyfarthing_scripts/tests/__pycache__/test_tui_focus.cpython-314-pytest-9.0.2.pyc +0 -0
  382. package/pennyfarthing_scripts/tests/__pycache__/test_tui_panel_persistence.cpython-314-pytest-9.0.2.pyc +0 -0
  383. package/pennyfarthing_scripts/tests/test_bikerack.py +26 -26
  384. package/pennyfarthing_scripts/tests/test_confidence_sm_gate.py +17 -16
  385. package/pennyfarthing_scripts/tests/test_dialogue_manager.py +0 -1
  386. package/pennyfarthing_scripts/tests/test_resolve_gate_file_field.py +45 -47
  387. package/pennyfarthing_scripts/tests/test_workflow_list_team.py +143 -0
  388. package/pennyfarthing_scripts/theme/__pycache__/__init__.cpython-311.pyc +0 -0
  389. package/pennyfarthing_scripts/theme/__pycache__/cli.cpython-311.pyc +0 -0
  390. package/pennyfarthing_scripts/validate/__pycache__/__init__.cpython-311.pyc +0 -0
  391. package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-311.pyc +0 -0
  392. package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +0 -0
  393. package/pennyfarthing_scripts/validate/adapters/team_mode.py +323 -0
  394. package/pennyfarthing_scripts/workflow/__pycache__/__init__.cpython-311.pyc +0 -0
  395. package/pennyfarthing_scripts/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
  396. package/pennyfarthing_scripts/workflow/__pycache__/cli.cpython-311.pyc +0 -0
  397. package/pennyfarthing_scripts/workflow/__pycache__/cli.cpython-314.pyc +0 -0
  398. package/pennyfarthing_scripts/workflow/__pycache__/helpers.cpython-314.pyc +0 -0
  399. package/pennyfarthing_scripts/workflow/__pycache__/scale.cpython-311.pyc +0 -0
  400. package/pennyfarthing_scripts/workflow/__pycache__/scale.cpython-314.pyc +0 -0
  401. package/pennyfarthing_scripts/workflow/__pycache__/state.cpython-311.pyc +0 -0
  402. package/pennyfarthing_scripts/workflow/__pycache__/state.cpython-314.pyc +0 -0
  403. package/pennyfarthing_scripts/workflow/cli.py +15 -14
  404. package/pennyfarthing_scripts/workflow/state.py +0 -1
  405. package/pennyfarthing_scripts/workflow/team_lifecycle.py +3 -4
@@ -12,8 +12,35 @@ import { findNodeModulesPath } from '../utils/node-modules.js';
12
12
  import { ALL_SYMLINKS, CORE_AGENTS } from '../utils/constants.js';
13
13
  import { getPfVersion, installPfCli } from '../utils/python.js';
14
14
  import { LEGACY_HOOK_MIGRATIONS, migrateHookPaths } from '../utils/settings.js';
15
+ /**
16
+ * Category-to-check-function mapping for --category filtering.
17
+ * Each category maps to the check functions that produce results in that group.
18
+ */
19
+ export const CATEGORY_CHECKS = {
20
+ 'installation': ['checkInstallation', 'checkCoreFiles'],
21
+ 'commands': ['checkCommandsAndSkills', 'checkUserFilesBasic'],
22
+ 'hooks': ['checkSettingsHooks'],
23
+ 'scripts': ['checkHooks', 'checkGitHooks'],
24
+ 'layout': ['checkDirectories', 'checkFileLayout'],
25
+ 'legacy': ['checkLegacyFiles', 'checkLegacyStatuslinePath', 'checkLegacyHookCommands'],
26
+ 'tools': ['checkCyclist', 'checkPfCli'],
27
+ };
15
28
  export async function doctorCommand(options) {
16
29
  const projectRoot = process.cwd();
30
+ // Handle --list-categories
31
+ if (options.listCategories) {
32
+ console.log('Available check categories:');
33
+ for (const [name, checks] of Object.entries(CATEGORY_CHECKS)) {
34
+ console.log(` ${name.padEnd(14)} ${checks.join(', ')}`);
35
+ }
36
+ return;
37
+ }
38
+ // Validate --category if provided
39
+ if (options.category && !CATEGORY_CHECKS[options.category]) {
40
+ logger.error(`Unknown category: ${options.category}`);
41
+ logger.info(`Available categories: ${Object.keys(CATEGORY_CHECKS).join(', ')}`);
42
+ process.exit(1);
43
+ }
17
44
  // Handle dogfood mode - run checks for framework/orchestrator development
18
45
  if (options.dogfood) {
19
46
  const dogfoodScript = join(projectRoot, 'pennyfarthing-dist/scripts/misc/doctor-dogfood.sh');
@@ -45,20 +72,58 @@ export async function doctorCommand(options) {
45
72
  logger.newline();
46
73
  // Resolve node_modules path for checks that need it
47
74
  const nodeModulesPath = findNodeModulesPath(projectRoot);
48
- // Run checks
49
- results.push(...checkInstallation(projectRoot, manifest));
50
- results.push(...checkCoreFiles(projectRoot, manifest));
51
- results.push(...checkCommandsAndSkills(projectRoot, nodeModulesPath));
52
- results.push(...checkUserFiles(projectRoot));
53
- results.push(...checkDirectories(projectRoot));
54
- results.push(...checkHooks(projectRoot));
55
- results.push(...checkGitHooks(projectRoot, nodeModulesPath));
56
- results.push(...checkFileLayout(projectRoot));
57
- results.push(...checkLegacyFiles(projectRoot));
58
- results.push(checkLegacyStatuslinePath(projectRoot));
59
- results.push(checkLegacyHookCommands(projectRoot));
60
- results.push(...checkCyclist(projectRoot));
61
- results.push(checkPfCli(nodeModulesPath));
75
+ // Determine which checks to run
76
+ const activeChecks = options.category
77
+ ? new Set(CATEGORY_CHECKS[options.category])
78
+ : null; // null = run all
79
+ // Run checks — when category is specified, only run matching subset
80
+ if (activeChecks) {
81
+ // Category-filtered run
82
+ if (activeChecks.has('checkInstallation'))
83
+ results.push(...checkInstallation(projectRoot, manifest));
84
+ if (activeChecks.has('checkCoreFiles'))
85
+ results.push(...checkCoreFiles(projectRoot, manifest));
86
+ if (activeChecks.has('checkCommandsAndSkills'))
87
+ results.push(...checkCommandsAndSkills(projectRoot, nodeModulesPath));
88
+ if (activeChecks.has('checkUserFilesBasic'))
89
+ results.push(...checkUserFilesBasic(projectRoot));
90
+ if (activeChecks.has('checkSettingsHooks'))
91
+ results.push(...checkSettingsHooks(projectRoot));
92
+ if (activeChecks.has('checkDirectories'))
93
+ results.push(...checkDirectories(projectRoot));
94
+ if (activeChecks.has('checkHooks'))
95
+ results.push(...checkHooks(projectRoot));
96
+ if (activeChecks.has('checkGitHooks'))
97
+ results.push(...checkGitHooks(projectRoot, nodeModulesPath));
98
+ if (activeChecks.has('checkFileLayout'))
99
+ results.push(...checkFileLayout(projectRoot));
100
+ if (activeChecks.has('checkLegacyFiles'))
101
+ results.push(...checkLegacyFiles(projectRoot));
102
+ if (activeChecks.has('checkLegacyStatuslinePath'))
103
+ results.push(checkLegacyStatuslinePath(projectRoot));
104
+ if (activeChecks.has('checkLegacyHookCommands'))
105
+ results.push(checkLegacyHookCommands(projectRoot));
106
+ if (activeChecks.has('checkCyclist'))
107
+ results.push(...checkCyclist(projectRoot));
108
+ if (activeChecks.has('checkPfCli'))
109
+ results.push(checkPfCli(nodeModulesPath));
110
+ }
111
+ else {
112
+ // Full run — original behavior
113
+ results.push(...checkInstallation(projectRoot, manifest));
114
+ results.push(...checkCoreFiles(projectRoot, manifest));
115
+ results.push(...checkCommandsAndSkills(projectRoot, nodeModulesPath));
116
+ results.push(...checkUserFiles(projectRoot));
117
+ results.push(...checkDirectories(projectRoot));
118
+ results.push(...checkHooks(projectRoot));
119
+ results.push(...checkGitHooks(projectRoot, nodeModulesPath));
120
+ results.push(...checkFileLayout(projectRoot));
121
+ results.push(...checkLegacyFiles(projectRoot));
122
+ results.push(checkLegacyStatuslinePath(projectRoot));
123
+ results.push(checkLegacyHookCommands(projectRoot));
124
+ results.push(...checkCyclist(projectRoot));
125
+ results.push(checkPfCli(nodeModulesPath));
126
+ }
62
127
  // Output results
63
128
  if (options.json) {
64
129
  console.log(JSON.stringify(results, null, 2));
@@ -124,7 +189,7 @@ export async function doctorCommand(options) {
124
189
  process.exit(1);
125
190
  }
126
191
  }
127
- function checkInstallation(projectRoot, manifest) {
192
+ export function checkInstallation(projectRoot, manifest) {
128
193
  const results = [];
129
194
  // Check manifest exists
130
195
  results.push({
@@ -134,7 +199,7 @@ function checkInstallation(projectRoot, manifest) {
134
199
  });
135
200
  return results;
136
201
  }
137
- function checkCoreFiles(projectRoot, manifest) {
202
+ export function checkCoreFiles(projectRoot, manifest) {
138
203
  const results = [];
139
204
  const installationType = manifest?.installationType || 'copy';
140
205
  const nodeModulesPath = findNodeModulesPath(projectRoot);
@@ -196,7 +261,7 @@ function checkCoreFiles(projectRoot, manifest) {
196
261
  * Check commands and skills are properly copied (not symlinked) and up to date.
197
262
  * Commands and skills are file copies since v11.3.0 to avoid node_modules drift.
198
263
  */
199
- function checkCommandsAndSkills(projectRoot, nodeModulesPath) {
264
+ export function checkCommandsAndSkills(projectRoot, _nodeModulesPath) {
200
265
  const results = [];
201
266
  // Use assetsPath for source resolution (correct pf-* prefix in dogfood)
202
267
  let assetsPath = null;
@@ -480,6 +545,91 @@ function checkSymlinks(projectRoot, nodeModulesPath) {
480
545
  }
481
546
  return results;
482
547
  }
548
+ /**
549
+ * Check basic user files only (project dir, sidecars, persona config, settings.local.json existence).
550
+ * Does NOT include the settings hook checks — use checkSettingsHooks() for those.
551
+ * Used by --category commands to separate user file checks from hook configuration checks.
552
+ */
553
+ export function checkUserFilesBasic(projectRoot) {
554
+ const results = [];
555
+ const manifest = readManifest(projectRoot);
556
+ const installationType = manifest?.installationType || 'copy';
557
+ // Check project directory
558
+ const projectDir = join(projectRoot, '.claude/project');
559
+ results.push({
560
+ name: 'project/directory',
561
+ status: pathExists(projectDir) ? 'pass' : 'warn',
562
+ detail: pathExists(projectDir) ? undefined : 'Run init to create'
563
+ });
564
+ // Check agent sidecars (now in .pennyfarthing/sidecars/)
565
+ const sidecarsDir = join(projectRoot, '.pennyfarthing/sidecars');
566
+ if (pathExists(sidecarsDir)) {
567
+ const existingSidecars = CORE_AGENTS.filter(a => pathExists(join(sidecarsDir, a)));
568
+ results.push({
569
+ name: 'project/sidecars',
570
+ status: existingSidecars.length > 0 ? 'pass' : 'warn',
571
+ detail: `${existingSidecars.length} agent sidecars configured`
572
+ });
573
+ }
574
+ // Check persona config at canonical location
575
+ const personaConfig = join(projectRoot, '.pennyfarthing/config.local.yaml');
576
+ results.push({
577
+ name: 'persona-config',
578
+ status: pathExists(personaConfig) ? 'pass' : 'warn',
579
+ detail: pathExists(personaConfig) ? undefined : 'No theme configured'
580
+ });
581
+ // Check settings.local.json exists (CRITICAL - registers hooks with Claude Code)
582
+ const settingsLocal = join(projectRoot, '.claude/settings.local.json');
583
+ if (!pathExists(settingsLocal)) {
584
+ results.push({
585
+ name: 'settings.local.json',
586
+ status: 'fail',
587
+ detail: 'Missing - hooks not registered with Claude Code!',
588
+ fix: () => {
589
+ createSettingsLocalJson(projectRoot, installationType);
590
+ }
591
+ });
592
+ }
593
+ else {
594
+ results.push({
595
+ name: 'settings.local.json',
596
+ status: 'pass',
597
+ detail: undefined
598
+ });
599
+ }
600
+ return results;
601
+ }
602
+ /**
603
+ * Check all settings hook configurations in settings.local.json.
604
+ * Returns results for all 9 hook checks (session-start, otel, auto-load-sm,
605
+ * stop, post-tool-use, benchmark-permissions, context-circuit-breaker,
606
+ * schema-validation, sprint-yaml-validation).
607
+ * Used by --category hooks for targeted hook configuration checking.
608
+ */
609
+ export function checkSettingsHooks(projectRoot) {
610
+ const results = [];
611
+ const manifest = readManifest(projectRoot);
612
+ const installationType = manifest?.installationType || 'copy';
613
+ const settingsLocal = join(projectRoot, '.claude/settings.local.json');
614
+ if (!pathExists(settingsLocal)) {
615
+ results.push({
616
+ name: 'settings.local.json',
617
+ status: 'fail',
618
+ detail: 'Missing - cannot check hook configuration'
619
+ });
620
+ return results;
621
+ }
622
+ results.push(checkSessionStartHooks(projectRoot, installationType));
623
+ results.push(checkOtelAutoStart(projectRoot, installationType));
624
+ results.push(checkAutoLoadSmHook(projectRoot));
625
+ results.push(checkStopHook(projectRoot, installationType));
626
+ results.push(checkPostToolUseHook(projectRoot, installationType));
627
+ results.push(checkBenchmarkPermissions(projectRoot));
628
+ results.push(checkContextCircuitBreaker(projectRoot, installationType));
629
+ results.push(checkSchemaValidationHook(projectRoot, installationType));
630
+ results.push(checkSprintYamlValidationHook(projectRoot, installationType));
631
+ return results;
632
+ }
483
633
  function checkUserFiles(projectRoot) {
484
634
  const results = [];
485
635
  // Detect installation type from manifest
@@ -530,6 +680,9 @@ function checkUserFiles(projectRoot) {
530
680
  // Check SessionStart hooks are configured (critical for PROJECT_ROOT)
531
681
  const hookCheck = checkSessionStartHooks(projectRoot, installationType);
532
682
  results.push(hookCheck);
683
+ // Check OTEL auto-configuration (WheelHub auto-start + telemetry env vars)
684
+ const otelCheck = checkOtelAutoStart(projectRoot, installationType);
685
+ results.push(otelCheck);
533
686
  // Check auto-load-sm hook is configured (auto-invokes /sm on new sessions)
534
687
  const autoLoadSmCheck = checkAutoLoadSmHook(projectRoot);
535
688
  results.push(autoLoadSmCheck);
@@ -654,7 +807,7 @@ function checkSessionStartHooks(projectRoot, installationType) {
654
807
  const hasSessionStartHook = settings.hooks.SessionStart.some((entry) => {
655
808
  if (typeof entry === 'object' && entry !== null) {
656
809
  const hookEntry = entry;
657
- return hookEntry.hooks?.some(h => h.command?.includes('pf hooks session-start') || h.command?.includes('session-start.sh'));
810
+ return hookEntry.hooks?.some(h => h.command?.includes('pf.sh hooks session-start') || h.command?.includes('pf hooks session-start') || h.command?.includes('session-start.sh'));
658
811
  }
659
812
  return false;
660
813
  });
@@ -682,6 +835,96 @@ function checkSessionStartHooks(projectRoot, installationType) {
682
835
  };
683
836
  }
684
837
  }
838
+ /**
839
+ * Check that session-start hook uses `pf hooks session-start` (Python version)
840
+ * which handles WheelHub auto-start + OTEL env var configuration.
841
+ * Legacy .sh hooks only set 2 of 5 required OTEL vars, breaking telemetry.
842
+ */
843
+ function checkOtelAutoStart(projectRoot, installationType) {
844
+ const settingsPath = join(projectRoot, '.claude/settings.local.json');
845
+ try {
846
+ const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
847
+ if (!settings.hooks?.SessionStart) {
848
+ return {
849
+ name: 'settings/otel-auto-start',
850
+ status: 'warn',
851
+ detail: 'No SessionStart hooks — WheelHub auto-start and OTEL not configured',
852
+ };
853
+ }
854
+ // Check if `pf hooks session-start` is used (Python version with full OTEL support)
855
+ const hasPfHooksSessionStart = settings.hooks.SessionStart.some((entry) => {
856
+ if (typeof entry === 'object' && entry !== null) {
857
+ const hookEntry = entry;
858
+ return hookEntry.hooks?.some(h => h.command?.includes('pf.sh hooks session-start') || h.command?.includes('pf hooks session-start'));
859
+ }
860
+ return false;
861
+ });
862
+ if (hasPfHooksSessionStart) {
863
+ return {
864
+ name: 'settings/otel-auto-start',
865
+ status: 'pass',
866
+ detail: undefined,
867
+ };
868
+ }
869
+ // Check if using legacy .sh (only sets 2 of 5 OTEL vars)
870
+ const hasLegacySh = settings.hooks.SessionStart.some((entry) => {
871
+ if (typeof entry === 'object' && entry !== null) {
872
+ const hookEntry = entry;
873
+ return hookEntry.hooks?.some(h => h.command?.includes('session-start.sh'));
874
+ }
875
+ return false;
876
+ });
877
+ if (hasLegacySh) {
878
+ return {
879
+ name: 'settings/otel-auto-start',
880
+ status: 'warn',
881
+ detail: 'Using legacy session-start.sh — missing WheelHub auto-start and 3 OTEL env vars. Migrate to `pf hooks session-start`',
882
+ fix: () => {
883
+ migrateSessionStartToPfHooks(projectRoot);
884
+ },
885
+ };
886
+ }
887
+ return {
888
+ name: 'settings/otel-auto-start',
889
+ status: 'warn',
890
+ detail: 'session-start hook not found — WheelHub auto-start and OTEL not configured',
891
+ fix: () => {
892
+ addSessionStartHooks(projectRoot, installationType);
893
+ },
894
+ };
895
+ }
896
+ catch {
897
+ return {
898
+ name: 'settings/otel-auto-start',
899
+ status: 'warn',
900
+ detail: 'Could not parse settings.local.json',
901
+ };
902
+ }
903
+ }
904
+ /**
905
+ * Migrate legacy session-start.sh hooks to `pf hooks session-start`
906
+ */
907
+ function migrateSessionStartToPfHooks(projectRoot) {
908
+ const settingsPath = join(projectRoot, '.claude/settings.local.json');
909
+ try {
910
+ const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
911
+ if (Array.isArray(settings.hooks?.SessionStart)) {
912
+ for (const entry of settings.hooks.SessionStart) {
913
+ if (typeof entry === 'object' && entry !== null && Array.isArray(entry.hooks)) {
914
+ for (const h of entry.hooks) {
915
+ if (h.command?.includes('session-start.sh')) {
916
+ h.command = '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks session-start';
917
+ }
918
+ }
919
+ }
920
+ }
921
+ }
922
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
923
+ }
924
+ catch {
925
+ // Silent fail — doctor will re-report on next run
926
+ }
927
+ }
685
928
  /**
686
929
  * Check that auto-load-sm hook is configured in SessionStart
687
930
  * This auto-invokes /sm agent on new session start
@@ -832,7 +1075,7 @@ function checkStopHook(projectRoot, installationType) {
832
1075
  const hasReflectorHook = settings.hooks.Stop.some((entry) => {
833
1076
  if (typeof entry === 'object' && entry !== null) {
834
1077
  const hookEntry = entry;
835
- return hookEntry.hooks?.some(h => h.command?.includes('pf hooks reflector-check') || h.command?.includes('question-reflector-check'));
1078
+ return hookEntry.hooks?.some(h => h.command?.includes('pf.sh hooks reflector-check') || h.command?.includes('pf hooks reflector-check') || h.command?.includes('question-reflector-check'));
836
1079
  }
837
1080
  return false;
838
1081
  });
@@ -883,7 +1126,7 @@ function checkContextCircuitBreaker(projectRoot, installationType) {
883
1126
  const hasCircuitBreaker = settings.hooks.PreToolUse.some((entry) => {
884
1127
  if (typeof entry === 'object' && entry !== null) {
885
1128
  const hookEntry = entry;
886
- return hookEntry.hooks?.some(h => h.command?.includes('pf hooks context-breaker') || h.command?.includes('context-circuit-breaker'));
1129
+ return hookEntry.hooks?.some(h => h.command?.includes('pf.sh hooks context-breaker') || h.command?.includes('pf hooks context-breaker') || h.command?.includes('context-circuit-breaker'));
887
1130
  }
888
1131
  return false;
889
1132
  });
@@ -921,7 +1164,7 @@ function addContextCircuitBreaker(projectRoot, _installationType) {
921
1164
  hooks: [
922
1165
  {
923
1166
  type: 'command',
924
- command: 'pf hooks context-breaker'
1167
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks context-breaker'
925
1168
  }
926
1169
  ]
927
1170
  };
@@ -1009,7 +1252,7 @@ function addSchemaValidationHook(projectRoot, _installationType) {
1009
1252
  hooks: [
1010
1253
  {
1011
1254
  type: 'command',
1012
- command: 'pf hooks schema-validation'
1255
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks schema-validation'
1013
1256
  }
1014
1257
  ]
1015
1258
  };
@@ -1059,7 +1302,7 @@ function checkPostToolUseHook(projectRoot, installationType) {
1059
1302
  const hasBellModeHook = settings.hooks.PostToolUse.some((entry) => {
1060
1303
  if (typeof entry === 'object' && entry !== null) {
1061
1304
  const hookEntry = entry;
1062
- return hookEntry.hooks?.some(h => h.command?.includes('pf hooks bell-mode') || h.command?.includes('bell-mode-hook'));
1305
+ return hookEntry.hooks?.some(h => h.command?.includes('pf.sh hooks bell-mode') || h.command?.includes('pf hooks bell-mode') || h.command?.includes('bell-mode-hook'));
1063
1306
  }
1064
1307
  return false;
1065
1308
  });
@@ -1098,7 +1341,7 @@ function addPostToolUseHook(projectRoot, _installationType) {
1098
1341
  hooks: [
1099
1342
  {
1100
1343
  type: 'command',
1101
- command: 'pf hooks bell-mode'
1344
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks bell-mode'
1102
1345
  }
1103
1346
  ]
1104
1347
  };
@@ -1148,7 +1391,7 @@ function checkSprintYamlValidationHook(projectRoot, installationType) {
1148
1391
  const hasSprintYamlValidation = settings.hooks.PostToolUse.some((entry) => {
1149
1392
  if (typeof entry === 'object' && entry !== null) {
1150
1393
  const hookEntry = entry;
1151
- return hookEntry.hooks?.some(h => h.command?.includes('pf hooks sprint-yaml') || h.command?.includes('sprint-yaml-validation'));
1394
+ return hookEntry.hooks?.some(h => h.command?.includes('pf.sh hooks sprint-yaml') || h.command?.includes('pf hooks sprint-yaml') || h.command?.includes('sprint-yaml-validation'));
1152
1395
  }
1153
1396
  return false;
1154
1397
  });
@@ -1187,7 +1430,7 @@ function addSprintYamlValidationHook(projectRoot, _installationType) {
1187
1430
  hooks: [
1188
1431
  {
1189
1432
  type: 'command',
1190
- command: 'pf hooks sprint-yaml'
1433
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks sprint-yaml'
1191
1434
  }
1192
1435
  ]
1193
1436
  };
@@ -1225,7 +1468,7 @@ function addStopHook(projectRoot, _installationType) {
1225
1468
  hooks: [
1226
1469
  {
1227
1470
  type: 'command',
1228
- command: 'pf hooks reflector-check'
1471
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks reflector-check'
1229
1472
  }
1230
1473
  ]
1231
1474
  },
@@ -1234,7 +1477,7 @@ function addStopHook(projectRoot, _installationType) {
1234
1477
  hooks: [
1235
1478
  {
1236
1479
  type: 'command',
1237
- command: 'pf hooks session-stop'
1480
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks session-stop'
1238
1481
  }
1239
1482
  ]
1240
1483
  }
@@ -1282,7 +1525,7 @@ function addSessionStartHooks(projectRoot, _installationType) {
1282
1525
  hooks: [
1283
1526
  {
1284
1527
  type: 'command',
1285
- command: 'pf hooks session-start'
1528
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks session-start'
1286
1529
  }
1287
1530
  ]
1288
1531
  },
@@ -1376,7 +1619,7 @@ function createSettingsLocalJson(projectRoot, _installationType) {
1376
1619
  hooks: [
1377
1620
  {
1378
1621
  type: 'command',
1379
- command: 'pf hooks session-start'
1622
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks session-start'
1380
1623
  }
1381
1624
  ]
1382
1625
  },
@@ -1404,7 +1647,7 @@ function createSettingsLocalJson(projectRoot, _installationType) {
1404
1647
  hooks: [
1405
1648
  {
1406
1649
  type: 'command',
1407
- command: 'pf hooks reflector-check'
1650
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks reflector-check'
1408
1651
  }
1409
1652
  ]
1410
1653
  },
@@ -1413,7 +1656,7 @@ function createSettingsLocalJson(projectRoot, _installationType) {
1413
1656
  hooks: [
1414
1657
  {
1415
1658
  type: 'command',
1416
- command: 'pf hooks session-stop'
1659
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks session-stop'
1417
1660
  }
1418
1661
  ]
1419
1662
  }
@@ -1424,7 +1667,7 @@ function createSettingsLocalJson(projectRoot, _installationType) {
1424
1667
  hooks: [
1425
1668
  {
1426
1669
  type: 'command',
1427
- command: 'pf hooks bell-mode'
1670
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks bell-mode'
1428
1671
  }
1429
1672
  ]
1430
1673
  },
@@ -1433,7 +1676,7 @@ function createSettingsLocalJson(projectRoot, _installationType) {
1433
1676
  hooks: [
1434
1677
  {
1435
1678
  type: 'command',
1436
- command: 'pf hooks sprint-yaml'
1679
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks sprint-yaml'
1437
1680
  }
1438
1681
  ]
1439
1682
  }
@@ -1444,7 +1687,7 @@ function createSettingsLocalJson(projectRoot, _installationType) {
1444
1687
  hooks: [
1445
1688
  {
1446
1689
  type: 'command',
1447
- command: 'pf hooks pre-edit-check'
1690
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks pre-edit-check'
1448
1691
  }
1449
1692
  ]
1450
1693
  },
@@ -1453,7 +1696,7 @@ function createSettingsLocalJson(projectRoot, _installationType) {
1453
1696
  hooks: [
1454
1697
  {
1455
1698
  type: 'command',
1456
- command: 'pf hooks schema-validation'
1699
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks schema-validation'
1457
1700
  }
1458
1701
  ]
1459
1702
  },
@@ -1462,7 +1705,7 @@ function createSettingsLocalJson(projectRoot, _installationType) {
1462
1705
  hooks: [
1463
1706
  {
1464
1707
  type: 'command',
1465
- command: 'pf hooks context-warning'
1708
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks context-warning'
1466
1709
  }
1467
1710
  ]
1468
1711
  },
@@ -1471,7 +1714,7 @@ function createSettingsLocalJson(projectRoot, _installationType) {
1471
1714
  hooks: [
1472
1715
  {
1473
1716
  type: 'command',
1474
- command: 'pf hooks context-breaker'
1717
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks context-breaker'
1475
1718
  }
1476
1719
  ]
1477
1720
  },
@@ -1479,7 +1722,7 @@ function createSettingsLocalJson(projectRoot, _installationType) {
1479
1722
  hooks: [
1480
1723
  {
1481
1724
  type: 'command',
1482
- command: 'pf hooks cyclist-pretooluse'
1725
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks cyclist-pretooluse'
1483
1726
  }
1484
1727
  ]
1485
1728
  }
@@ -1487,13 +1730,13 @@ function createSettingsLocalJson(projectRoot, _installationType) {
1487
1730
  },
1488
1731
  statusLine: {
1489
1732
  type: 'command',
1490
- command: 'pf hooks statusline'
1733
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks statusline'
1491
1734
  }
1492
1735
  };
1493
1736
  ensureDirSync(join(projectRoot, '.claude'));
1494
1737
  writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
1495
1738
  }
1496
- function checkDirectories(projectRoot) {
1739
+ export function checkDirectories(projectRoot) {
1497
1740
  const results = [];
1498
1741
  const dirs = [
1499
1742
  { path: 'sprint', name: 'dir/sprint' },
@@ -1509,7 +1752,7 @@ function checkDirectories(projectRoot) {
1509
1752
  }
1510
1753
  return results;
1511
1754
  }
1512
- function checkHooks(projectRoot) {
1755
+ export function checkHooks(projectRoot) {
1513
1756
  const results = [];
1514
1757
  // Detect installation type from manifest
1515
1758
  const manifest = readManifest(projectRoot);
@@ -1566,7 +1809,7 @@ function checkHooks(projectRoot) {
1566
1809
  * symlinked rather than copied, since symlinks stay current automatically.
1567
1810
  * Provides --fix to refresh stale hooks or replace copies with symlinks.
1568
1811
  */
1569
- function checkGitHooks(projectRoot, nodeModulesPath) {
1812
+ export function checkGitHooks(projectRoot, nodeModulesPath) {
1570
1813
  const results = [];
1571
1814
  // Use git rev-parse to find the actual git dir (handles worktrees where .git is a file)
1572
1815
  const gitDirResult = spawnSync('git', ['rev-parse', '--git-dir'], { cwd: projectRoot, encoding: 'utf8' });
@@ -1710,7 +1953,7 @@ function checkGitHooks(projectRoot, nodeModulesPath) {
1710
1953
  * Check Cyclist installation health (if installed as a workspace package)
1711
1954
  * Detects node-pty spawn-helper permission issues that cause posix_spawnp failures
1712
1955
  */
1713
- function checkCyclist(projectRoot) {
1956
+ export function checkCyclist(projectRoot) {
1714
1957
  const results = [];
1715
1958
  // Detect Cyclist package — check common locations
1716
1959
  const cyclistLocations = [
@@ -1812,7 +2055,7 @@ function checkCyclist(projectRoot) {
1812
2055
  * Check if the pf CLI is installed and working.
1813
2056
  * The pf CLI is required for agent commands (e.g., `pf agent start "dev"`).
1814
2057
  */
1815
- function checkPfCli(nodeModulesPath) {
2058
+ export function checkPfCli(nodeModulesPath) {
1816
2059
  const version = getPfVersion();
1817
2060
  if (version) {
1818
2061
  return {
@@ -2049,7 +2292,7 @@ export function checkLegacyStatuslinePath(projectRoot) {
2049
2292
  const pathMatch = command.match(/(?:\"\$CLAUDE_PROJECT_DIR\"\/)?([^\s"]+)/);
2050
2293
  const currentPath = pathMatch ? pathMatch[1] : command;
2051
2294
  // Check if it's the canonical pf hooks command or the legacy .sh path
2052
- if (command === 'pf hooks statusline' || currentPath.includes('misc/statusline.sh') || command.includes('misc/statusline.sh')) {
2295
+ if (command.includes('pf.sh hooks statusline') || command === 'pf hooks statusline' || currentPath.includes('misc/statusline.sh') || command.includes('misc/statusline.sh')) {
2053
2296
  return {
2054
2297
  name: 'settings/statusline-path',
2055
2298
  status: 'pass',
@@ -2070,7 +2313,7 @@ export function checkLegacyStatuslinePath(projectRoot) {
2070
2313
  const updatedSettings = { ...settings };
2071
2314
  updatedSettings.statusLine = {
2072
2315
  type: 'command',
2073
- command: 'pf hooks statusline'
2316
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/scripts/core/pf.sh hooks statusline'
2074
2317
  };
2075
2318
  writeFileSync(settingsPath, JSON.stringify(updatedSettings, null, 2));
2076
2319
  }
@@ -2089,7 +2332,7 @@ export function checkLegacyStatuslinePath(projectRoot) {
2089
2332
  * be migrated to `pf hooks` commands. The .sh scripts still work (they're shims)
2090
2333
  * but `pf hooks` is the canonical path — faster, no shell indirection.
2091
2334
  */
2092
- function checkLegacyHookCommands(projectRoot) {
2335
+ export function checkLegacyHookCommands(projectRoot) {
2093
2336
  const settingsPath = join(projectRoot, '.claude/settings.local.json');
2094
2337
  if (!pathExists(settingsPath)) {
2095
2338
  return { name: 'legacy/hook-commands', status: 'pass', detail: 'No settings file' };
@@ -2126,7 +2369,7 @@ function checkLegacyHookCommands(projectRoot) {
2126
2369
  }
2127
2370
  // Also check statusLine
2128
2371
  const statusLine = settings.statusLine;
2129
- if (statusLine?.command && statusLine.command !== 'pf hooks statusline') {
2372
+ if (statusLine?.command && !statusLine.command.includes('pf.sh hooks statusline') && statusLine.command !== 'pf hooks statusline') {
2130
2373
  for (const shName of Object.keys(LEGACY_HOOK_MIGRATIONS)) {
2131
2374
  if (statusLine.command.includes(shName)) {
2132
2375
  legacyCount++;
@@ -2148,7 +2391,7 @@ function checkLegacyHookCommands(projectRoot) {
2148
2391
  }
2149
2392
  }
2150
2393
  // Migrate statusLine
2151
- if (statusLine?.command && statusLine.command !== 'pf hooks statusline') {
2394
+ if (statusLine?.command && !statusLine.command.includes('pf.sh hooks statusline') && statusLine.command !== 'pf hooks statusline') {
2152
2395
  for (const [shName, pfCommand] of Object.entries(LEGACY_HOOK_MIGRATIONS)) {
2153
2396
  if (statusLine.command.includes(shName)) {
2154
2397
  statusLine.command = pfCommand;
@@ -2261,6 +2504,26 @@ export function checkFileLayout(projectRoot) {
2261
2504
  status: 'pass',
2262
2505
  detail: undefined
2263
2506
  });
2507
+ // Check execute permission on each shell script in project hooks
2508
+ try {
2509
+ const hookFiles = readdirSync(projectHooksPath).filter(f => f.endsWith('.sh'));
2510
+ for (const file of hookFiles) {
2511
+ const hookPath = join(projectHooksPath, file);
2512
+ const stats = statSync(hookPath);
2513
+ const isExecutable = (stats.mode & 0o111) !== 0;
2514
+ results.push({
2515
+ name: `layout/project-hooks/${file}`,
2516
+ status: isExecutable ? 'pass' : 'warn',
2517
+ detail: isExecutable ? undefined : 'Not executable — will cause Permission denied on session start',
2518
+ fix: isExecutable ? undefined : () => {
2519
+ chmodSync(hookPath, 0o755);
2520
+ }
2521
+ });
2522
+ }
2523
+ }
2524
+ catch {
2525
+ // Ignore read errors
2526
+ }
2264
2527
  }
2265
2528
  // 8. Old project hooks at .claude/project/hooks/
2266
2529
  const oldProjectHooksPath = join(projectRoot, '.claude/project/hooks');
@@ -2290,6 +2553,17 @@ export function checkFileLayout(projectRoot) {
2290
2553
  }
2291
2554
  removeSync(oldProjectHooksPath);
2292
2555
  }
2556
+ // Ensure migrated shell scripts are executable
2557
+ try {
2558
+ for (const file of readdirSync(projectHooksPath)) {
2559
+ if (file.endsWith('.sh')) {
2560
+ chmodSync(join(projectHooksPath, file), 0o755);
2561
+ }
2562
+ }
2563
+ }
2564
+ catch {
2565
+ // Ignore
2566
+ }
2293
2567
  }
2294
2568
  });
2295
2569
  }