@pennyfarthing/core 10.0.5 → 10.1.0

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 (303) hide show
  1. package/README.md +9 -7
  2. package/package.json +15 -11
  3. package/packages/core/dist/cli/commands/doctor.d.ts.map +1 -1
  4. package/packages/core/dist/cli/commands/doctor.js +251 -4
  5. package/packages/core/dist/cli/commands/doctor.js.map +1 -1
  6. package/packages/core/dist/cli/commands/init.d.ts +7 -0
  7. package/packages/core/dist/cli/commands/init.d.ts.map +1 -1
  8. package/packages/core/dist/cli/commands/init.js +41 -8
  9. package/packages/core/dist/cli/commands/init.js.map +1 -1
  10. package/packages/core/dist/cli/commands/update.d.ts.map +1 -1
  11. package/packages/core/dist/cli/commands/update.js +26 -0
  12. package/packages/core/dist/cli/commands/update.js.map +1 -1
  13. package/packages/core/dist/cli/index.js +1 -1
  14. package/packages/core/dist/cli/index.js.map +1 -1
  15. package/packages/core/dist/cli/utils/python.d.ts +22 -0
  16. package/packages/core/dist/cli/utils/python.d.ts.map +1 -0
  17. package/packages/core/dist/cli/utils/python.js +102 -0
  18. package/packages/core/dist/cli/utils/python.js.map +1 -0
  19. package/packages/core/dist/cli/utils/settings.d.ts.map +1 -1
  20. package/packages/core/dist/cli/utils/settings.js +10 -0
  21. package/packages/core/dist/cli/utils/settings.js.map +1 -1
  22. package/pennyfarthing-dist/agents/README.md +1 -3
  23. package/pennyfarthing-dist/agents/architect.md +0 -6
  24. package/pennyfarthing-dist/agents/devops.md +0 -6
  25. package/pennyfarthing-dist/agents/orchestrator.md +0 -6
  26. package/pennyfarthing-dist/agents/pm.md +0 -6
  27. package/pennyfarthing-dist/agents/sm.md +0 -6
  28. package/pennyfarthing-dist/commands/architect.md +11 -3
  29. package/pennyfarthing-dist/commands/close-epic.md +24 -131
  30. package/pennyfarthing-dist/commands/create-theme.md +14 -24
  31. package/pennyfarthing-dist/commands/dev.md +11 -3
  32. package/pennyfarthing-dist/commands/devops.md +11 -3
  33. package/pennyfarthing-dist/commands/health-check.md +1 -3
  34. package/pennyfarthing-dist/commands/help.md +8 -12
  35. package/pennyfarthing-dist/commands/list-themes.md +14 -16
  36. package/pennyfarthing-dist/commands/orchestrator.md +11 -3
  37. package/pennyfarthing-dist/commands/parallel-work.md +1 -3
  38. package/pennyfarthing-dist/commands/pm.md +11 -3
  39. package/pennyfarthing-dist/commands/prime.md +6 -6
  40. package/pennyfarthing-dist/commands/reviewer.md +11 -3
  41. package/pennyfarthing-dist/commands/run-ci.md +1 -1
  42. package/pennyfarthing-dist/commands/set-theme.md +14 -51
  43. package/pennyfarthing-dist/commands/setup.md +1 -1
  44. package/pennyfarthing-dist/commands/show-theme.md +14 -16
  45. package/pennyfarthing-dist/commands/sm.md +11 -3
  46. package/pennyfarthing-dist/commands/tea.md +11 -3
  47. package/pennyfarthing-dist/commands/tech-writer.md +11 -3
  48. package/pennyfarthing-dist/commands/theme-maker.md +14 -671
  49. package/pennyfarthing-dist/commands/theme.md +95 -0
  50. package/pennyfarthing-dist/commands/ux-designer.md +11 -3
  51. package/pennyfarthing-dist/commands/work.md +3 -5
  52. package/pennyfarthing-dist/guides/agent-coordination.md +11 -13
  53. package/pennyfarthing-dist/guides/agent-template-tactical.md +2 -3
  54. package/pennyfarthing-dist/guides/command-tag-taxonomy.md +212 -0
  55. package/pennyfarthing-dist/guides/hooks.md +5 -5
  56. package/pennyfarthing-dist/guides/patterns/fan-out-fan-in-pattern.md +3 -3
  57. package/pennyfarthing-dist/guides/patterns/helper-delegation-pattern.md +9 -59
  58. package/pennyfarthing-dist/guides/patterns/tdd-flow-pattern.md +4 -5
  59. package/pennyfarthing-dist/guides/prime.md +2 -2
  60. package/pennyfarthing-dist/guides/skill-schema.md +4 -4
  61. package/pennyfarthing-dist/scripts/core/agent-session.sh +6 -2
  62. package/pennyfarthing-dist/scripts/core/check-context.sh +0 -0
  63. package/pennyfarthing-dist/scripts/core/handoff-marker.sh +0 -0
  64. package/pennyfarthing-dist/scripts/core/phase-check-start.sh +0 -0
  65. package/pennyfarthing-dist/scripts/core/prime.sh +8 -10
  66. package/pennyfarthing-dist/scripts/cyclist/is-cyclist.sh +0 -0
  67. package/pennyfarthing-dist/scripts/git/create-feature-branches.sh +0 -0
  68. package/pennyfarthing-dist/scripts/git/git-status-all.sh +0 -0
  69. package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +8 -6
  70. package/pennyfarthing-dist/scripts/git/release.sh +0 -0
  71. package/pennyfarthing-dist/scripts/git/worktree-manager.sh +0 -0
  72. package/pennyfarthing-dist/scripts/health/drift-detection.sh +0 -0
  73. package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +0 -0
  74. package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +0 -0
  75. package/pennyfarthing-dist/scripts/hooks/context-warning.sh +0 -0
  76. package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +0 -0
  77. package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +0 -0
  78. package/pennyfarthing-dist/scripts/hooks/post-merge.sh +12 -5
  79. package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +4 -3
  80. package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +0 -0
  81. package/pennyfarthing-dist/scripts/hooks/pre-push.sh +11 -5
  82. package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +0 -0
  83. package/pennyfarthing-dist/scripts/hooks/question_reflector_check.py +0 -0
  84. package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +0 -0
  85. package/pennyfarthing-dist/scripts/hooks/session-start.sh +0 -0
  86. package/pennyfarthing-dist/scripts/hooks/session-stop.sh +0 -0
  87. package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +0 -0
  88. package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +0 -0
  89. package/pennyfarthing-dist/scripts/jira/create-jira-epic.sh +0 -0
  90. package/pennyfarthing-dist/scripts/jira/create-jira-story.sh +0 -0
  91. package/pennyfarthing-dist/scripts/jira/jira-claim-story.sh +0 -0
  92. package/pennyfarthing-dist/scripts/jira/jira-reconcile.sh +0 -0
  93. package/pennyfarthing-dist/scripts/jira/jira-sync-story.sh +0 -0
  94. package/pennyfarthing-dist/scripts/jira/sync-epic-jira.sh +0 -0
  95. package/pennyfarthing-dist/scripts/lib/background-tasks.sh +0 -0
  96. package/pennyfarthing-dist/scripts/lib/checkpoint.sh +0 -0
  97. package/pennyfarthing-dist/scripts/lib/common.sh +0 -0
  98. package/pennyfarthing-dist/scripts/lib/file-lock.sh +0 -0
  99. package/pennyfarthing-dist/scripts/lib/logging.sh +0 -0
  100. package/pennyfarthing-dist/scripts/lib/retry.sh +0 -0
  101. package/pennyfarthing-dist/scripts/maintenance/migrate-theme-schema.mjs +0 -0
  102. package/pennyfarthing-dist/scripts/maintenance/sidecar-health.sh +0 -0
  103. package/pennyfarthing-dist/scripts/misc/README.md +1 -1
  104. package/pennyfarthing-dist/scripts/misc/add-short-names.sh +0 -0
  105. package/pennyfarthing-dist/scripts/misc/add_short_names.py +0 -0
  106. package/pennyfarthing-dist/scripts/misc/backlog.sh +0 -0
  107. package/pennyfarthing-dist/scripts/misc/check-status.sh +0 -0
  108. package/pennyfarthing-dist/scripts/misc/find-related-work.sh +0 -0
  109. package/pennyfarthing-dist/scripts/misc/generate-skill-docs.sh +0 -0
  110. package/pennyfarthing-dist/scripts/misc/log-skill-usage.sh +0 -0
  111. package/pennyfarthing-dist/scripts/misc/migrate-bmad-workflow.sh +0 -0
  112. package/pennyfarthing-dist/scripts/misc/migrate_bmad_workflow.py +0 -0
  113. package/pennyfarthing-dist/scripts/misc/repo-scan.sh +0 -0
  114. package/pennyfarthing-dist/scripts/misc/repo-utils.sh +0 -0
  115. package/pennyfarthing-dist/scripts/misc/run-ci.sh +0 -0
  116. package/pennyfarthing-dist/scripts/misc/run-timestamp.sh +0 -0
  117. package/pennyfarthing-dist/scripts/misc/session-cleanup.sh +0 -0
  118. package/pennyfarthing-dist/scripts/misc/skill-usage-report.sh +0 -0
  119. package/pennyfarthing-dist/scripts/misc/statusline.sh +0 -0
  120. package/pennyfarthing-dist/scripts/misc/uninstall.sh +0 -0
  121. package/pennyfarthing-dist/scripts/misc/validate-subagent-frontmatter.sh +1 -2
  122. package/pennyfarthing-dist/scripts/portraits/generate-portraits.sh +0 -0
  123. package/pennyfarthing-dist/scripts/story/create-story.sh +0 -0
  124. package/pennyfarthing-dist/scripts/story/size-story.sh +0 -0
  125. package/pennyfarthing-dist/scripts/story/story-template.sh +0 -0
  126. package/pennyfarthing-dist/scripts/test/ensure-swebench-data.sh +0 -0
  127. package/pennyfarthing-dist/scripts/test/swebench-judge.py +0 -0
  128. package/pennyfarthing-dist/scripts/test/test-cache.sh +0 -0
  129. package/pennyfarthing-dist/scripts/test/test-setup.sh +0 -0
  130. package/pennyfarthing-dist/scripts/tests/check.test.sh +0 -0
  131. package/pennyfarthing-dist/scripts/tests/dev-story-workflow-import.test.sh +0 -0
  132. package/pennyfarthing-dist/scripts/tests/epics-and-stories-workflow-import.test.sh +0 -0
  133. package/pennyfarthing-dist/scripts/tests/handoff-phase-update.test.sh +5 -5
  134. package/pennyfarthing-dist/scripts/tests/implementation-readiness-workflow-import.test.sh +0 -0
  135. package/pennyfarthing-dist/scripts/tests/migrate-bmad-workflow.test.sh +0 -0
  136. package/pennyfarthing-dist/scripts/tests/prd-workflow-import.test.sh +0 -0
  137. package/pennyfarthing-dist/scripts/tests/project-context-workflow-import.test.sh +0 -0
  138. package/pennyfarthing-dist/scripts/tests/test-character-voice.sh +0 -0
  139. package/pennyfarthing-dist/scripts/tests/test-drift-detection.sh +3 -79
  140. package/pennyfarthing-dist/scripts/tests/test-post-merge-hook.sh +0 -0
  141. package/pennyfarthing-dist/scripts/tests/test-session-checkpoint.sh +0 -0
  142. package/pennyfarthing-dist/scripts/tests/test-solo-command.sh +0 -0
  143. package/pennyfarthing-dist/scripts/tests/ux-design-workflow-import.test.sh +0 -0
  144. package/pennyfarthing-dist/scripts/theme/README.md +1 -1
  145. package/pennyfarthing-dist/scripts/theme/compute-theme-tiers.sh +0 -0
  146. package/pennyfarthing-dist/scripts/theme/compute_theme_tiers.py +0 -0
  147. package/pennyfarthing-dist/scripts/theme/list-themes.sh +0 -0
  148. package/pennyfarthing-dist/scripts/theme/update-theme-tiers.sh +0 -0
  149. package/pennyfarthing-dist/scripts/validation/validate-agent-schema.sh +0 -1
  150. package/pennyfarthing-dist/scripts/workflow/check.py +0 -0
  151. package/pennyfarthing-dist/scripts/workflow/check.sh +0 -0
  152. package/pennyfarthing-dist/scripts/workflow/complete-step.py +0 -0
  153. package/pennyfarthing-dist/scripts/workflow/finish-story.sh +62 -17
  154. package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +0 -0
  155. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.py +0 -0
  156. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +0 -0
  157. package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +0 -0
  158. package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +0 -0
  159. package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +0 -0
  160. package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +0 -0
  161. package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +0 -0
  162. package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +0 -0
  163. package/pennyfarthing-dist/skills/dev-patterns/SKILL.md +2 -2
  164. package/pennyfarthing-dist/skills/skill-registry.yaml +20 -16
  165. package/pennyfarthing-dist/skills/story/scripts/create-story.sh +0 -0
  166. package/pennyfarthing-dist/skills/story/scripts/size-story.sh +0 -0
  167. package/pennyfarthing-dist/skills/story/scripts/story-template.sh +0 -0
  168. package/pennyfarthing-dist/skills/theme/skill.md +290 -75
  169. package/pennyfarthing-dist/skills/theme-creation/SKILL.md +23 -166
  170. package/pennyfarthing-dist/skills/workflow/scripts/list-workflows.sh +0 -0
  171. package/pennyfarthing-dist/skills/workflow/scripts/resume-workflow.sh +0 -0
  172. package/pennyfarthing-dist/skills/workflow/scripts/show-workflow.sh +0 -0
  173. package/pennyfarthing-dist/skills/workflow/scripts/start-workflow.sh +0 -0
  174. package/pennyfarthing-dist/skills/workflow/scripts/workflow-status.sh +0 -0
  175. package/pennyfarthing-dist/skills/workflow/skill.md +4 -4
  176. package/pennyfarthing-dist/templates/agent-scopes.yaml.template +0 -11
  177. package/pennyfarthing-dist/templates/auto-load-sm.sh.template +14 -0
  178. package/pennyfarthing-dist/templates/settings.local.json.template +9 -0
  179. package/pennyfarthing-dist/workflows/2party-tdd.yaml +399 -0
  180. package/pennyfarthing-dist/workflows/epics-and-stories/steps/step-05-import-to-future.md +41 -24
  181. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  182. package/pennyfarthing_scripts/__pycache__/hooks.cpython-314.pyc +0 -0
  183. package/pennyfarthing_scripts/__pycache__/schema_validation_hook.cpython-314.pyc +0 -0
  184. package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
  185. package/pennyfarthing_scripts/cli.py +15 -0
  186. package/pennyfarthing_scripts/codemarkers/__init__.py +19 -0
  187. package/pennyfarthing_scripts/codemarkers/__main__.py +6 -0
  188. package/pennyfarthing_scripts/codemarkers/__pycache__/__init__.cpython-314.pyc +0 -0
  189. package/pennyfarthing_scripts/codemarkers/__pycache__/__main__.cpython-314.pyc +0 -0
  190. package/pennyfarthing_scripts/codemarkers/__pycache__/analyze.cpython-314.pyc +0 -0
  191. package/pennyfarthing_scripts/codemarkers/__pycache__/cli.cpython-314.pyc +0 -0
  192. package/pennyfarthing_scripts/codemarkers/__pycache__/formatters.cpython-314.pyc +0 -0
  193. package/pennyfarthing_scripts/codemarkers/__pycache__/models.cpython-314.pyc +0 -0
  194. package/pennyfarthing_scripts/codemarkers/analyze.py +326 -0
  195. package/pennyfarthing_scripts/codemarkers/cli.py +129 -0
  196. package/pennyfarthing_scripts/codemarkers/formatters.py +89 -0
  197. package/pennyfarthing_scripts/codemarkers/models.py +45 -0
  198. package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
  199. package/pennyfarthing_scripts/common/__pycache__/themes.cpython-314.pyc +0 -0
  200. package/pennyfarthing_scripts/complexity/__init__.py +15 -0
  201. package/pennyfarthing_scripts/complexity/__main__.py +6 -0
  202. package/pennyfarthing_scripts/complexity/__pycache__/__init__.cpython-314.pyc +0 -0
  203. package/pennyfarthing_scripts/complexity/__pycache__/__main__.cpython-314.pyc +0 -0
  204. package/pennyfarthing_scripts/complexity/__pycache__/analyze.cpython-314.pyc +0 -0
  205. package/pennyfarthing_scripts/complexity/__pycache__/cli.cpython-314.pyc +0 -0
  206. package/pennyfarthing_scripts/complexity/__pycache__/formatters.cpython-314.pyc +0 -0
  207. package/pennyfarthing_scripts/complexity/__pycache__/models.cpython-314.pyc +0 -0
  208. package/pennyfarthing_scripts/complexity/analyze.py +207 -0
  209. package/pennyfarthing_scripts/complexity/cli.py +78 -0
  210. package/pennyfarthing_scripts/complexity/formatters.py +64 -0
  211. package/pennyfarthing_scripts/complexity/models.py +32 -0
  212. package/pennyfarthing_scripts/deadcode/__init__.py +6 -0
  213. package/pennyfarthing_scripts/deadcode/__main__.py +6 -0
  214. package/pennyfarthing_scripts/deadcode/__pycache__/__init__.cpython-314.pyc +0 -0
  215. package/pennyfarthing_scripts/deadcode/__pycache__/__main__.cpython-314.pyc +0 -0
  216. package/pennyfarthing_scripts/deadcode/__pycache__/analyze.cpython-314.pyc +0 -0
  217. package/pennyfarthing_scripts/deadcode/__pycache__/cli.cpython-314.pyc +0 -0
  218. package/pennyfarthing_scripts/deadcode/__pycache__/formatters.cpython-314.pyc +0 -0
  219. package/pennyfarthing_scripts/deadcode/__pycache__/models.cpython-314.pyc +0 -0
  220. package/pennyfarthing_scripts/deadcode/analyze.py +323 -0
  221. package/pennyfarthing_scripts/deadcode/cli.py +163 -0
  222. package/pennyfarthing_scripts/deadcode/formatters.py +106 -0
  223. package/pennyfarthing_scripts/deadcode/models.py +54 -0
  224. package/pennyfarthing_scripts/dependencies/__init__.py +20 -0
  225. package/pennyfarthing_scripts/dependencies/__main__.py +5 -0
  226. package/pennyfarthing_scripts/dependencies/__pycache__/__init__.cpython-314.pyc +0 -0
  227. package/pennyfarthing_scripts/dependencies/__pycache__/__main__.cpython-314.pyc +0 -0
  228. package/pennyfarthing_scripts/dependencies/__pycache__/analyze.cpython-314.pyc +0 -0
  229. package/pennyfarthing_scripts/dependencies/__pycache__/cli.cpython-314.pyc +0 -0
  230. package/pennyfarthing_scripts/dependencies/__pycache__/formatters.cpython-314.pyc +0 -0
  231. package/pennyfarthing_scripts/dependencies/__pycache__/models.cpython-314.pyc +0 -0
  232. package/pennyfarthing_scripts/dependencies/analyze.py +155 -0
  233. package/pennyfarthing_scripts/dependencies/cli.py +72 -0
  234. package/pennyfarthing_scripts/dependencies/formatters.py +63 -0
  235. package/pennyfarthing_scripts/dependencies/models.py +39 -0
  236. package/pennyfarthing_scripts/healthscore/__init__.py +21 -0
  237. package/pennyfarthing_scripts/healthscore/__main__.py +6 -0
  238. package/pennyfarthing_scripts/healthscore/__pycache__/__init__.cpython-314.pyc +0 -0
  239. package/pennyfarthing_scripts/healthscore/__pycache__/__main__.cpython-314.pyc +0 -0
  240. package/pennyfarthing_scripts/healthscore/__pycache__/analyze.cpython-314.pyc +0 -0
  241. package/pennyfarthing_scripts/healthscore/__pycache__/cli.cpython-314.pyc +0 -0
  242. package/pennyfarthing_scripts/healthscore/__pycache__/formatters.cpython-314.pyc +0 -0
  243. package/pennyfarthing_scripts/healthscore/__pycache__/models.cpython-314.pyc +0 -0
  244. package/pennyfarthing_scripts/healthscore/analyze.py +161 -0
  245. package/pennyfarthing_scripts/healthscore/cli.py +76 -0
  246. package/pennyfarthing_scripts/healthscore/formatters.py +46 -0
  247. package/pennyfarthing_scripts/healthscore/models.py +44 -0
  248. package/pennyfarthing_scripts/hooks/cyclist-pretooluse-hook.sh +0 -0
  249. package/pennyfarthing_scripts/hotspots/__pycache__/__init__.cpython-314.pyc +0 -0
  250. package/pennyfarthing_scripts/hotspots/__pycache__/__main__.cpython-314.pyc +0 -0
  251. package/pennyfarthing_scripts/hotspots/__pycache__/analyze.cpython-314.pyc +0 -0
  252. package/pennyfarthing_scripts/hotspots/__pycache__/cli.cpython-314.pyc +0 -0
  253. package/pennyfarthing_scripts/hotspots/__pycache__/formatters.cpython-314.pyc +0 -0
  254. package/pennyfarthing_scripts/hotspots/__pycache__/models.cpython-314.pyc +0 -0
  255. package/pennyfarthing_scripts/hotspots/analyze.py +27 -0
  256. package/pennyfarthing_scripts/hotspots/cli.py +10 -8
  257. package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-314.pyc +0 -0
  258. package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
  259. package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
  260. package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
  261. package/pennyfarthing_scripts/jira/__pycache__/create.cpython-314.pyc +0 -0
  262. package/pennyfarthing_scripts/jira/__pycache__/operations.cpython-314.pyc +0 -0
  263. package/pennyfarthing_scripts/jira/__pycache__/reconcile.cpython-314.pyc +0 -0
  264. package/pennyfarthing_scripts/jira/bidirectional.py +42 -15
  265. package/pennyfarthing_scripts/jira/cli.py +4 -1
  266. package/pennyfarthing_scripts/jira/client.py +28 -0
  267. package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
  268. package/pennyfarthing_scripts/prime/__pycache__/models.cpython-314.pyc +0 -0
  269. package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
  270. package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
  271. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  272. package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
  273. package/pennyfarthing_scripts/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
  274. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  275. package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
  276. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  277. package/pennyfarthing_scripts/sprint/__pycache__/story_add.cpython-314.pyc +0 -0
  278. package/pennyfarthing_scripts/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
  279. package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
  280. package/pennyfarthing_scripts/sprint/__pycache__/validate_cmd.cpython-314.pyc +0 -0
  281. package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
  282. package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-314.pyc +0 -0
  283. package/pennyfarthing_scripts/sprint/__pycache__/yaml_io.cpython-314.pyc +0 -0
  284. package/pennyfarthing_scripts/sprint/archive_epic.py +198 -94
  285. package/pennyfarthing_scripts/sprint/cli.py +29 -19
  286. package/pennyfarthing_scripts/sprint/story_add.py +202 -27
  287. package/pennyfarthing_scripts/sprint/story_finish.py +211 -0
  288. package/pennyfarthing_scripts/sprint/work.py +27 -3
  289. package/pennyfarthing_scripts/tests/__pycache__/test_codemarkers.cpython-314-pytest-9.0.2.pyc +0 -0
  290. package/pennyfarthing_scripts/tests/__pycache__/test_healthscore.cpython-314-pytest-9.0.2.pyc +0 -0
  291. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc +0 -0
  292. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
  293. package/pennyfarthing_scripts/tests/__pycache__/test_validate_cmd.cpython-314-pytest-9.0.2.pyc +0 -0
  294. package/pennyfarthing_scripts/tests/__pycache__/test_yaml_io.cpython-314-pytest-9.0.2.pyc +0 -0
  295. package/pennyfarthing_scripts/tests/test_codemarkers.py +682 -0
  296. package/pennyfarthing_scripts/tests/test_healthscore.py +524 -0
  297. package/pennyfarthing_scripts/theme/__init__.py +5 -0
  298. package/pennyfarthing_scripts/theme/__main__.py +6 -0
  299. package/pennyfarthing_scripts/theme/__pycache__/__init__.cpython-314.pyc +0 -0
  300. package/pennyfarthing_scripts/theme/__pycache__/cli.cpython-314.pyc +0 -0
  301. package/pennyfarthing_scripts/theme/cli.py +286 -0
  302. package/scripts/README.md +53 -0
  303. package/pennyfarthing-dist/agents/workflow-status-check.md +0 -96
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Pennyfarthing
2
2
 
3
- **v10.0.5** | *The outer loop goes once, the inner loop goes many times.*
3
+ **v10.1.0** | *The outer loop goes once, the inner loop goes many times.*
4
4
 
5
5
  <img src="pennyfarthing.png" alt="Pennyfarthing Logo" width="75" style="float:left; margin:10px">
6
6
 
@@ -15,8 +15,8 @@ A Claude Code agent orchestration framework built around three pillars: a flexib
15
15
  A multi-agent system with customizable BikeLane workflows for structured software development:
16
16
 
17
17
  - **10 Coordinated Agents** - SM, TEA, Dev, Reviewer, Architect, PM, Tech Writer, UX Designer, DevOps, Orchestrator
18
- - **8 BikeLane Workflows** - Phased (TDD, BDD, Trivial), Stepped (Architecture, Release, Git Cleanup)
19
- - **49 Slash Commands** - Entry points for agent activation and workflows
18
+ - **9 BikeLane Workflows** - Phased (TDD, BDD, Trivial, 2pTDD), Stepped (Architecture, Release, Git Cleanup)
19
+ - **50 Slash Commands** - Entry points for agent activation and workflows
20
20
  - **22 Skills** - Reusable knowledge domains (testing, code-review, jira, mermaid, etc.)
21
21
  - **Prime Context System** - Tiered context injection assembles agent definition, persona, session state, and sidecar memory
22
22
  - **Automatic Handoffs** - Context-aware agent transitions via subagent delegation
@@ -266,14 +266,16 @@ your-project/
266
266
  └── {story-id}-session.md # Active work session
267
267
  ```
268
268
 
269
- ## What's New in v10.0.0
269
+ ## What's New in v10.1.0
270
270
 
271
- - **Clean Install Consolidation** - All managed files under `.pennyfarthing/` instead of scattered across `.claude/`, `.git/hooks/`, `.session/`. Legacy installs migrated automatically via `pennyfarthing update`
272
- - **Tool Use Approval System** - Hook-based permission flow with severity classification, grant persistence, agent-level scoping, and workflow presets. Replaces legacy IPC approval
273
- - **Smooth Plan Mode Exit** - Tirepump choice UI for transitioning out of plan mode
271
+ - **Codebase Health Dashboard** Health score gauge, dead code analysis, code markers, complexity metrics, and dependency tracking all with Cyclist dialogs
272
+ - **Tool Dialog System** Shared ToolDialog component with standardized dialogs across all diagnostic tools
273
+ - **2party-TDD Workflow** New workflow for pair-programming TDD with review rejection loops
274
+ - **Cross-File Reference Validator** — Detects broken references across agent definitions, workflows, skills, and guides
274
275
 
275
276
  ### Previous Highlights
276
277
 
278
+ - **v10.0** - Clean install consolidation, tool use approval system, plan mode exit UI
277
279
  - **v9.3** - Theme packages (97 themes across 7 packs), release workflow, shadcn/ui migration
278
280
  - **v9.0** - Dockview panel system, React 19 rewrite, tool visualization, prime context, bell/relay modes
279
281
  - **v8.x** - BikeLane workflows, scientific benchmarking, JobFair, agent sidecars
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pennyfarthing/core",
3
- "version": "10.0.5",
3
+ "version": "10.1.0",
4
4
  "description": "Claude Code agent framework with TDD workflow and persona system",
5
5
  "type": "module",
6
6
  "bin": {
@@ -30,6 +30,18 @@
30
30
  "bugs": {
31
31
  "url": "https://github.com/1898andCo/pennyfarthing/issues"
32
32
  },
33
+ "scripts": {
34
+ "prepublishOnly": "find pennyfarthing-dist/scripts -name '*.sh' ! -name 'find-root.sh' -exec chmod +x {} +",
35
+ "postinstall": "node scripts/postinstall.cjs",
36
+ "build": "pnpm --filter @pennyfarthing/shared build && pnpm --filter @pennyfarthing/core build && pnpm --filter @pennyfarthing/cyclist build && ./scripts/generate-skill-docs.sh",
37
+ "docs": "./scripts/generate-skill-docs.sh",
38
+ "dev": "pnpm -r --parallel dev",
39
+ "test": "pnpm -r test",
40
+ "lint": "eslint 'packages/*/src/**/*.ts' --max-warnings 0",
41
+ "validate:refs": "node scripts/validate-refs.js",
42
+ "test:validate-refs": "node --test scripts/validate-refs.test.js",
43
+ "clean": "pnpm -r clean"
44
+ },
33
45
  "dependencies": {
34
46
  "@pennyfarthing/shared": "^10.0.3",
35
47
  "chalk": "^5.3.0",
@@ -49,13 +61,5 @@
49
61
  "engines": {
50
62
  "node": ">=18.0.0"
51
63
  },
52
- "scripts": {
53
- "postinstall": "node scripts/postinstall.cjs",
54
- "build": "pnpm --filter @pennyfarthing/shared build && pnpm --filter @pennyfarthing/core build && pnpm --filter @pennyfarthing/cyclist build && ./scripts/generate-skill-docs.sh",
55
- "docs": "./scripts/generate-skill-docs.sh",
56
- "dev": "pnpm -r --parallel dev",
57
- "test": "pnpm -r test",
58
- "lint": "eslint 'packages/*/src/**/*.ts' --max-warnings 0",
59
- "clean": "pnpm -r clean"
60
- }
61
- }
64
+ "packageManager": "pnpm@9.0.0"
65
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AAqBA,UAAU,aAAa;IACrB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAoHzE;AAmyCD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,EAAE,CAgKnE;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,CAkF1E;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,EAAE,CAsIlE"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AAsBA,UAAU,aAAa;IACrB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA2HzE;AA+iDD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,EAAE,CAgKnE;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,CAkF1E;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,EAAE,CAsIlE"}
@@ -10,14 +10,15 @@ import { pathExists, isDirectory, isSymlink, fileMatchesHash } from '../utils/fi
10
10
  import { getPackageVersion } from '../utils/version.js';
11
11
  import { findNodeModulesPath } from '../utils/node-modules.js';
12
12
  import { ALL_SYMLINKS, CORE_AGENTS } from '../utils/constants.js';
13
+ import { getPfVersion, installPfCli } from '../utils/python.js';
13
14
  export async function doctorCommand(options) {
14
15
  const projectRoot = process.cwd();
15
- // Handle dogfooding mode - run the dogfood script instead
16
+ // Handle dogfood mode - run checks for framework/orchestrator development
16
17
  if (options.dogfood) {
17
18
  const dogfoodScript = join(projectRoot, 'pennyfarthing-dist/scripts/misc/doctor-dogfood.sh');
18
19
  if (!existsSync(dogfoodScript)) {
19
- logger.error('Dogfood mode requires the pennyfarthing repo (pennyfarthing-dist/ not found)');
20
- logger.info('This flag is for developers working on pennyfarthing itself.');
20
+ logger.error('Dogfood mode requires pennyfarthing-dist/ (framework repo or orchestrator with inlined pennyfarthing/)');
21
+ logger.info('This flag is for framework development and orchestrator repos.');
21
22
  process.exit(1);
22
23
  }
23
24
  const args = options.fix ? ['--fix'] : [];
@@ -41,16 +42,20 @@ export async function doctorCommand(options) {
41
42
  logger.info(`Version: ${installedVersion} (installed) / ${packageVersion} (package)`);
42
43
  logger.info(`Mode: ${installationType}${installationType === 'symlink' ? ' (node_modules)' : ' (file copies)'}`);
43
44
  logger.newline();
45
+ // Resolve node_modules path for checks that need it
46
+ const nodeModulesPath = findNodeModulesPath(projectRoot);
44
47
  // Run checks
45
48
  results.push(...checkInstallation(projectRoot, manifest));
46
49
  results.push(...checkCoreFiles(projectRoot, manifest));
47
50
  results.push(...checkUserFiles(projectRoot));
48
51
  results.push(...checkDirectories(projectRoot));
49
52
  results.push(...checkHooks(projectRoot));
53
+ results.push(...checkGitHooks(projectRoot, nodeModulesPath));
50
54
  results.push(...checkFileLayout(projectRoot));
51
55
  results.push(...checkLegacyFiles(projectRoot));
52
56
  results.push(checkLegacyStatuslinePath(projectRoot));
53
57
  results.push(...checkCyclist(projectRoot));
58
+ results.push(checkPfCli(nodeModulesPath));
54
59
  // Output results
55
60
  if (options.json) {
56
61
  console.log(JSON.stringify(results, null, 2));
@@ -63,9 +68,11 @@ export async function doctorCommand(options) {
63
68
  { name: 'User Files', filter: (r) => r.name.startsWith('project/') || r.name.startsWith('persona') || r.name.startsWith('settings') },
64
69
  { name: 'Directories', filter: (r) => r.name.startsWith('dir/') },
65
70
  { name: 'Hooks', filter: (r) => r.name.startsWith('hook/') },
71
+ { name: 'Git Hooks', filter: (r) => r.name.startsWith('git-hook/') },
66
72
  { name: 'File Layout', filter: (r) => r.name.startsWith('layout/') },
67
73
  { name: 'Legacy Files', filter: (r) => r.name.startsWith('legacy/') },
68
- { name: 'Cyclist', filter: (r) => r.name.startsWith('cyclist/') }
74
+ { name: 'Cyclist', filter: (r) => r.name.startsWith('cyclist/') },
75
+ { name: 'Tools', filter: (r) => r.name.startsWith('tools/') }
69
76
  ];
70
77
  for (const category of categories) {
71
78
  const categoryResults = results.filter(category.filter);
@@ -314,6 +321,9 @@ function checkUserFiles(projectRoot) {
314
321
  // Check SessionStart hooks are configured (critical for PROJECT_ROOT)
315
322
  const hookCheck = checkSessionStartHooks(projectRoot, installationType);
316
323
  results.push(hookCheck);
324
+ // Check auto-load-sm hook is configured (auto-invokes /sm on new sessions)
325
+ const autoLoadSmCheck = checkAutoLoadSmHook(projectRoot);
326
+ results.push(autoLoadSmCheck);
317
327
  // Check Stop hook is configured (question reflector enforcement)
318
328
  const stopHookCheck = checkStopHook(projectRoot, installationType);
319
329
  results.push(stopHookCheck);
@@ -462,6 +472,133 @@ function checkSessionStartHooks(projectRoot, installationType) {
462
472
  };
463
473
  }
464
474
  }
475
+ /**
476
+ * Check that auto-load-sm hook is configured in SessionStart
477
+ * This auto-invokes /sm agent on new session start
478
+ */
479
+ function checkAutoLoadSmHook(projectRoot) {
480
+ const settingsPath = join(projectRoot, '.claude/settings.local.json');
481
+ try {
482
+ const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
483
+ if (!settings.hooks?.SessionStart) {
484
+ return {
485
+ name: 'settings/auto-load-sm',
486
+ status: 'warn',
487
+ detail: 'Missing SessionStart hooks - auto-load-sm not configured',
488
+ fix: () => {
489
+ addAutoLoadSmHook(projectRoot);
490
+ }
491
+ };
492
+ }
493
+ const hasAutoLoadSm = settings.hooks.SessionStart.some((entry) => {
494
+ if (typeof entry === 'object' && entry !== null) {
495
+ const hookEntry = entry;
496
+ return hookEntry.hooks?.some(h => h.command?.includes('auto-load-sm'));
497
+ }
498
+ return false;
499
+ });
500
+ if (!hasAutoLoadSm) {
501
+ return {
502
+ name: 'settings/auto-load-sm',
503
+ status: 'warn',
504
+ detail: 'auto-load-sm not configured - /sm will not auto-invoke on startup',
505
+ fix: () => {
506
+ addAutoLoadSmHook(projectRoot);
507
+ }
508
+ };
509
+ }
510
+ // Also check that the hook script file exists
511
+ const hookScript = join(projectRoot, '.pennyfarthing/project/hooks/auto-load-sm.sh');
512
+ if (!pathExists(hookScript)) {
513
+ return {
514
+ name: 'settings/auto-load-sm',
515
+ status: 'warn',
516
+ detail: 'Hook registered but script missing at .pennyfarthing/project/hooks/auto-load-sm.sh',
517
+ fix: () => {
518
+ ensureDirSync(dirname(hookScript));
519
+ writeFileSync(hookScript, `#!/usr/bin/env bash
520
+ # Auto-load /sm agent on new session start
521
+ set -euo pipefail
522
+
523
+ cat <<'HOOKEOF'
524
+ {
525
+ "hookSpecificOutput": {
526
+ "hookEventName": "SessionStart",
527
+ "additionalContext": "IMPORTANT: Auto-invoke the /sm agent immediately. Run the Skill tool with skill: \\"sm\\" as your first action. Do not greet the user or do anything else first."
528
+ }
529
+ }
530
+ HOOKEOF
531
+ `, { mode: 0o755 });
532
+ }
533
+ };
534
+ }
535
+ return {
536
+ name: 'settings/auto-load-sm',
537
+ status: 'pass',
538
+ detail: undefined
539
+ };
540
+ }
541
+ catch {
542
+ return {
543
+ name: 'settings/auto-load-sm',
544
+ status: 'warn',
545
+ detail: 'Could not parse settings.local.json'
546
+ };
547
+ }
548
+ }
549
+ /**
550
+ * Fix function: Add auto-load-sm hook to SessionStart in settings.local.json
551
+ */
552
+ function addAutoLoadSmHook(projectRoot) {
553
+ const settingsPath = join(projectRoot, '.claude/settings.local.json');
554
+ const requiredHook = {
555
+ matcher: 'startup',
556
+ hooks: [
557
+ {
558
+ type: 'command',
559
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/project/hooks/auto-load-sm.sh'
560
+ }
561
+ ]
562
+ };
563
+ let settings = {};
564
+ if (pathExists(settingsPath)) {
565
+ try {
566
+ settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
567
+ }
568
+ catch {
569
+ // Start fresh if parse fails
570
+ }
571
+ }
572
+ if (!settings.hooks) {
573
+ settings.hooks = {};
574
+ }
575
+ const hooks = settings.hooks;
576
+ if (!hooks.SessionStart) {
577
+ hooks.SessionStart = [requiredHook];
578
+ }
579
+ else if (Array.isArray(hooks.SessionStart)) {
580
+ hooks.SessionStart = [...hooks.SessionStart, requiredHook];
581
+ }
582
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
583
+ // Also ensure the hook script exists
584
+ const hookScript = join(projectRoot, '.pennyfarthing/project/hooks/auto-load-sm.sh');
585
+ if (!pathExists(hookScript)) {
586
+ ensureDirSync(dirname(hookScript));
587
+ writeFileSync(hookScript, `#!/usr/bin/env bash
588
+ # Auto-load /sm agent on new session start
589
+ set -euo pipefail
590
+
591
+ cat <<'HOOKEOF'
592
+ {
593
+ "hookSpecificOutput": {
594
+ "hookEventName": "SessionStart",
595
+ "additionalContext": "IMPORTANT: Auto-invoke the /sm agent immediately. Run the Skill tool with skill: \\"sm\\" as your first action. Do not greet the user or do anything else first."
596
+ }
597
+ }
598
+ HOOKEOF
599
+ `, { mode: 0o755 });
600
+ }
601
+ }
465
602
  /**
466
603
  * Check that Stop hook is properly configured in settings.local.json
467
604
  * This is needed for question reflector enforcement in Cyclist
@@ -936,6 +1073,15 @@ function addSessionStartHooks(projectRoot, installationType) {
936
1073
  command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/project/hooks/setup-env.sh'
937
1074
  }
938
1075
  ]
1076
+ },
1077
+ {
1078
+ matcher: 'startup',
1079
+ hooks: [
1080
+ {
1081
+ type: 'command',
1082
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/project/hooks/auto-load-sm.sh'
1083
+ }
1084
+ ]
939
1085
  }
940
1086
  ];
941
1087
  let settings = {};
@@ -1021,6 +1167,15 @@ function createSettingsLocalJson(projectRoot, installationType) {
1021
1167
  command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/project/hooks/setup-env.sh'
1022
1168
  }
1023
1169
  ]
1170
+ },
1171
+ {
1172
+ matcher: 'startup',
1173
+ hooks: [
1174
+ {
1175
+ type: 'command',
1176
+ command: '"$CLAUDE_PROJECT_DIR"/.pennyfarthing/project/hooks/auto-load-sm.sh'
1177
+ }
1178
+ ]
1024
1179
  }
1025
1180
  ],
1026
1181
  PostToolUse: [
@@ -1158,6 +1313,74 @@ function checkHooks(projectRoot) {
1158
1313
  }
1159
1314
  return results;
1160
1315
  }
1316
+ /**
1317
+ * Check git hooks in .git/hooks/ are up-to-date with package source.
1318
+ * Detects stale copies that were installed by `pennyfarthing init` but never refreshed.
1319
+ * Provides --fix to overwrite stale hooks with current package content.
1320
+ */
1321
+ function checkGitHooks(projectRoot, nodeModulesPath) {
1322
+ const results = [];
1323
+ const gitHooksDir = join(projectRoot, '.git/hooks');
1324
+ if (!pathExists(gitHooksDir)) {
1325
+ return results;
1326
+ }
1327
+ if (!nodeModulesPath) {
1328
+ return results;
1329
+ }
1330
+ const hooks = [
1331
+ { source: 'pre-commit.sh', dest: 'pre-commit', marker: 'pennyfarthing' },
1332
+ { source: 'pre-push.sh', dest: 'pre-push', marker: 'pennyfarthing' },
1333
+ { source: 'post-merge.sh', dest: 'post-merge', marker: 'pennyfarthing' },
1334
+ ];
1335
+ for (const hook of hooks) {
1336
+ const sourcePath = join(nodeModulesPath, 'scripts/hooks', hook.source);
1337
+ const destPath = join(gitHooksDir, hook.dest);
1338
+ if (!pathExists(sourcePath)) {
1339
+ continue;
1340
+ }
1341
+ if (!pathExists(destPath)) {
1342
+ results.push({
1343
+ name: `git-hook/${hook.dest}`,
1344
+ status: 'warn',
1345
+ detail: 'Not installed',
1346
+ fix: () => {
1347
+ const content = readFileSync(sourcePath, 'utf8');
1348
+ writeFileSync(destPath, content, { mode: 0o755 });
1349
+ }
1350
+ });
1351
+ continue;
1352
+ }
1353
+ const existingContent = readFileSync(destPath, 'utf8');
1354
+ // Only check hooks that are ours (contain the marker)
1355
+ if (!existingContent.includes(hook.marker)) {
1356
+ results.push({
1357
+ name: `git-hook/${hook.dest}`,
1358
+ status: 'pass',
1359
+ detail: 'Custom (non-pennyfarthing)'
1360
+ });
1361
+ continue;
1362
+ }
1363
+ const sourceContent = readFileSync(sourcePath, 'utf8');
1364
+ if (existingContent === sourceContent) {
1365
+ results.push({
1366
+ name: `git-hook/${hook.dest}`,
1367
+ status: 'pass',
1368
+ detail: undefined
1369
+ });
1370
+ }
1371
+ else {
1372
+ results.push({
1373
+ name: `git-hook/${hook.dest}`,
1374
+ status: 'warn',
1375
+ detail: 'Stale — content differs from package',
1376
+ fix: () => {
1377
+ writeFileSync(destPath, sourceContent, { mode: 0o755 });
1378
+ }
1379
+ });
1380
+ }
1381
+ }
1382
+ return results;
1383
+ }
1161
1384
  /**
1162
1385
  * Check Cyclist installation health (if installed as a workspace package)
1163
1386
  * Detects node-pty spawn-helper permission issues that cause posix_spawnp failures
@@ -1260,6 +1483,30 @@ function checkCyclist(projectRoot) {
1260
1483
  }
1261
1484
  return results;
1262
1485
  }
1486
+ /**
1487
+ * Check if the pf CLI is installed and working.
1488
+ * The pf CLI is required for agent commands (e.g., `pf agent start "dev"`).
1489
+ */
1490
+ function checkPfCli(nodeModulesPath) {
1491
+ const version = getPfVersion();
1492
+ if (version) {
1493
+ return {
1494
+ name: 'tools/pf-cli',
1495
+ status: 'pass',
1496
+ detail: version
1497
+ };
1498
+ }
1499
+ return {
1500
+ name: 'tools/pf-cli',
1501
+ status: 'warn',
1502
+ detail: 'pf CLI not found — agent commands will not work',
1503
+ fix: () => {
1504
+ if (!installPfCli(nodeModulesPath)) {
1505
+ throw new Error('Neither uv nor pipx available. Install manually: uv tool install pennyfarthing-scripts');
1506
+ }
1507
+ }
1508
+ };
1509
+ }
1263
1510
  /**
1264
1511
  * Known legacy statusline paths from various Pennyfarthing versions.
1265
1512
  * These should be detected and cleaned up when proper statusline exists.