@pennyfarthing/core 11.1.0 → 11.2.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 (263) hide show
  1. package/README.md +8 -8
  2. package/package.json +16 -14
  3. package/packages/core/dist/cli/utils/constants.d.ts +1 -1
  4. package/packages/core/dist/cli/utils/constants.d.ts.map +1 -1
  5. package/packages/core/dist/cli/utils/constants.js +2 -1
  6. package/packages/core/dist/cli/utils/constants.js.map +1 -1
  7. package/packages/core/dist/consultation/dialogue-manager.d.ts +75 -0
  8. package/packages/core/dist/consultation/dialogue-manager.d.ts.map +1 -0
  9. package/packages/core/dist/consultation/dialogue-manager.js +334 -0
  10. package/packages/core/dist/consultation/dialogue-manager.js.map +1 -0
  11. package/packages/core/dist/consultation/dialogue-manager.test.d.ts +19 -0
  12. package/packages/core/dist/consultation/dialogue-manager.test.d.ts.map +1 -0
  13. package/packages/core/dist/consultation/dialogue-manager.test.js +444 -0
  14. package/packages/core/dist/consultation/dialogue-manager.test.js.map +1 -0
  15. package/packages/core/dist/server/api/git.d.ts +13 -1
  16. package/packages/core/dist/server/api/git.d.ts.map +1 -1
  17. package/packages/core/dist/server/api/git.js +53 -34
  18. package/packages/core/dist/server/api/git.js.map +1 -1
  19. package/packages/core/dist/server/otlp-receiver.d.ts +16 -11
  20. package/packages/core/dist/server/otlp-receiver.d.ts.map +1 -1
  21. package/packages/core/dist/server/otlp-receiver.js +185 -24
  22. package/packages/core/dist/server/otlp-receiver.js.map +1 -1
  23. package/packages/core/dist/server/otlp-receiver.test.d.ts +21 -0
  24. package/packages/core/dist/server/otlp-receiver.test.d.ts.map +1 -0
  25. package/packages/core/dist/server/otlp-receiver.test.js +446 -0
  26. package/packages/core/dist/server/otlp-receiver.test.js.map +1 -0
  27. package/packages/core/dist/shared/portrait-resolver.d.ts +9 -0
  28. package/packages/core/dist/shared/portrait-resolver.d.ts.map +1 -1
  29. package/packages/core/dist/shared/portrait-resolver.js +27 -0
  30. package/packages/core/dist/shared/portrait-resolver.js.map +1 -1
  31. package/packages/core/dist/shared/portrait-resolver.test.js +47 -1
  32. package/packages/core/dist/shared/portrait-resolver.test.js.map +1 -1
  33. package/packages/core/dist/shared/skill-search.test.js +2 -2
  34. package/packages/core/dist/shared/tandem-portrait-inventory.test.d.ts +13 -0
  35. package/packages/core/dist/shared/tandem-portrait-inventory.test.d.ts.map +1 -0
  36. package/packages/core/dist/shared/tandem-portrait-inventory.test.js +126 -0
  37. package/packages/core/dist/shared/tandem-portrait-inventory.test.js.map +1 -0
  38. package/pennyfarthing-dist/agents/dev.md +1 -1
  39. package/pennyfarthing-dist/agents/reviewer.md +1 -1
  40. package/pennyfarthing-dist/agents/sm-setup.md +1 -1
  41. package/pennyfarthing-dist/agents/sm.md +2 -2
  42. package/pennyfarthing-dist/agents/tea.md +1 -1
  43. package/pennyfarthing-dist/agents/testing-runner.md +2 -1
  44. package/pennyfarthing-dist/commands/pf-chore.md +2 -2
  45. package/pennyfarthing-dist/commands/pf-standalone.md +7 -2
  46. package/pennyfarthing-dist/guides/agent-behavior.md +1 -1
  47. package/pennyfarthing-dist/guides/agent-tag-taxonomy.md +1 -1
  48. package/pennyfarthing-dist/guides/bikerack.md +3 -3
  49. package/pennyfarthing-dist/guides/hooks.md +1 -1
  50. package/pennyfarthing-dist/guides/worktree-mode.md +3 -3
  51. package/pennyfarthing-dist/guides/xml-tags.md +2 -2
  52. package/pennyfarthing-dist/scripts/README.md +1 -1
  53. package/pennyfarthing-dist/scripts/core/agent-session.sh +0 -0
  54. package/pennyfarthing-dist/scripts/core/check-context.sh +1 -1
  55. package/pennyfarthing-dist/scripts/core/dialogue-manager.sh +322 -0
  56. package/pennyfarthing-dist/scripts/core/phase-check-start.sh +0 -0
  57. package/pennyfarthing-dist/scripts/core/prime.sh +0 -0
  58. package/pennyfarthing-dist/scripts/cyclist/is-cyclist.sh +0 -0
  59. package/pennyfarthing-dist/scripts/git/README.md +24 -14
  60. package/pennyfarthing-dist/scripts/git/create-feature-branches.sh +5 -266
  61. package/pennyfarthing-dist/scripts/git/git-status-all.sh +5 -151
  62. package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +6 -144
  63. package/pennyfarthing-dist/scripts/git/release.sh +0 -0
  64. package/pennyfarthing-dist/scripts/git/worktree-manager.sh +5 -496
  65. package/pennyfarthing-dist/scripts/health/drift-detection.sh +0 -0
  66. package/pennyfarthing-dist/scripts/hooks/README.md +1 -1
  67. package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +1 -1
  68. package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +0 -0
  69. package/pennyfarthing-dist/scripts/hooks/context-warning.sh +0 -0
  70. package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +0 -0
  71. package/pennyfarthing-dist/scripts/hooks/dispatcher-template.sh +0 -0
  72. package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +9 -11
  73. package/pennyfarthing-dist/scripts/hooks/post-merge.sh +0 -0
  74. package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +0 -0
  75. package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +0 -0
  76. package/pennyfarthing-dist/scripts/hooks/pre-push.sh +0 -0
  77. package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +0 -0
  78. package/pennyfarthing-dist/scripts/hooks/question_reflector_check.py +0 -0
  79. package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +0 -0
  80. package/pennyfarthing-dist/scripts/hooks/session-start.sh +0 -0
  81. package/pennyfarthing-dist/scripts/hooks/session-stop.sh +0 -0
  82. package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +0 -0
  83. package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +1 -1
  84. package/pennyfarthing-dist/scripts/jira/create-jira-epic.sh +0 -0
  85. package/pennyfarthing-dist/scripts/jira/create-jira-story.sh +0 -0
  86. package/pennyfarthing-dist/scripts/jira/jira-claim-story.sh +0 -0
  87. package/pennyfarthing-dist/scripts/jira/jira-reconcile.sh +0 -0
  88. package/pennyfarthing-dist/scripts/jira/jira-sync-story.sh +0 -0
  89. package/pennyfarthing-dist/scripts/jira/sync-epic-jira.sh +0 -0
  90. package/pennyfarthing-dist/scripts/lib/background-tasks.sh +0 -0
  91. package/pennyfarthing-dist/scripts/lib/checkpoint.sh +0 -0
  92. package/pennyfarthing-dist/scripts/lib/common.sh +0 -0
  93. package/pennyfarthing-dist/scripts/lib/file-lock.sh +0 -0
  94. package/pennyfarthing-dist/scripts/lib/logging.sh +0 -0
  95. package/pennyfarthing-dist/scripts/lib/retry.sh +0 -0
  96. package/pennyfarthing-dist/scripts/maintenance/migrate-theme-schema.mjs +0 -0
  97. package/pennyfarthing-dist/scripts/maintenance/sidecar-health.sh +0 -0
  98. package/pennyfarthing-dist/scripts/misc/add-short-names.sh +0 -0
  99. package/pennyfarthing-dist/scripts/misc/add_short_names.py +0 -0
  100. package/pennyfarthing-dist/scripts/misc/backlog.sh +0 -0
  101. package/pennyfarthing-dist/scripts/misc/check-status.sh +0 -0
  102. package/pennyfarthing-dist/scripts/misc/find-related-work.sh +0 -0
  103. package/pennyfarthing-dist/scripts/misc/generate-skill-docs.sh +0 -0
  104. package/pennyfarthing-dist/scripts/misc/log-skill-usage.sh +0 -0
  105. package/pennyfarthing-dist/scripts/misc/migrate-bmad-workflow.sh +0 -0
  106. package/pennyfarthing-dist/scripts/misc/migrate_bmad_workflow.py +0 -0
  107. package/pennyfarthing-dist/scripts/misc/repo-scan.sh +0 -0
  108. package/pennyfarthing-dist/scripts/misc/repo-utils.sh +0 -0
  109. package/pennyfarthing-dist/scripts/misc/run-ci.sh +0 -0
  110. package/pennyfarthing-dist/scripts/misc/run-timestamp.sh +0 -0
  111. package/pennyfarthing-dist/scripts/misc/session-cleanup.sh +0 -0
  112. package/pennyfarthing-dist/scripts/misc/skill-usage-report.sh +0 -0
  113. package/pennyfarthing-dist/scripts/misc/statusline.sh +0 -0
  114. package/pennyfarthing-dist/scripts/misc/uninstall.sh +0 -0
  115. package/pennyfarthing-dist/scripts/misc/validate-subagent-frontmatter.sh +0 -0
  116. package/pennyfarthing-dist/scripts/portraits/generate-portraits.sh +0 -0
  117. package/pennyfarthing-dist/scripts/portraits/generate-tandem-portraits.sh +76 -0
  118. package/pennyfarthing-dist/scripts/story/create-story.sh +0 -0
  119. package/pennyfarthing-dist/scripts/story/size-story.sh +0 -0
  120. package/pennyfarthing-dist/scripts/story/story-template.sh +0 -0
  121. package/pennyfarthing-dist/scripts/tests/check.test.sh +0 -0
  122. package/pennyfarthing-dist/scripts/tests/dev-story-workflow-import.test.sh +0 -0
  123. package/pennyfarthing-dist/scripts/tests/epics-and-stories-workflow-import.test.sh +0 -0
  124. package/pennyfarthing-dist/scripts/tests/handoff-phase-update.test.sh +0 -0
  125. package/pennyfarthing-dist/scripts/tests/implementation-readiness-workflow-import.test.sh +0 -0
  126. package/pennyfarthing-dist/scripts/tests/migrate-bmad-workflow.test.sh +0 -0
  127. package/pennyfarthing-dist/scripts/tests/prd-workflow-import.test.sh +0 -0
  128. package/pennyfarthing-dist/scripts/tests/project-context-workflow-import.test.sh +0 -0
  129. package/pennyfarthing-dist/scripts/tests/test-character-voice.sh +0 -0
  130. package/pennyfarthing-dist/scripts/tests/test-drift-detection.sh +0 -0
  131. package/pennyfarthing-dist/scripts/tests/test-post-merge-hook.sh +0 -0
  132. package/pennyfarthing-dist/scripts/tests/test-session-checkpoint.sh +0 -0
  133. package/pennyfarthing-dist/scripts/tests/test-solo-command.sh +0 -0
  134. package/pennyfarthing-dist/scripts/tests/ux-design-workflow-import.test.sh +0 -0
  135. package/pennyfarthing-dist/scripts/theme/list-themes.sh +0 -0
  136. package/pennyfarthing-dist/scripts/validation/validate-agent-schema.sh +0 -0
  137. package/pennyfarthing-dist/scripts/workflow/check.py +0 -0
  138. package/pennyfarthing-dist/scripts/workflow/check.sh +0 -0
  139. package/pennyfarthing-dist/scripts/workflow/complete-step.py +0 -0
  140. package/pennyfarthing-dist/scripts/workflow/finish-story.sh +0 -0
  141. package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +4 -221
  142. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.py +0 -0
  143. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +5 -13
  144. package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +4 -123
  145. package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +4 -33
  146. package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +4 -156
  147. package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +4 -131
  148. package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +4 -249
  149. package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +4 -160
  150. package/pennyfarthing-dist/skills/pf-bc/usage.md +1 -1
  151. package/pennyfarthing-dist/skills/pf-jira/examples.md +5 -2
  152. package/pennyfarthing-dist/skills/pf-story/scripts/create-story.sh +0 -0
  153. package/pennyfarthing-dist/skills/pf-story/scripts/size-story.sh +0 -0
  154. package/pennyfarthing-dist/skills/pf-story/scripts/story-template.sh +0 -0
  155. package/pennyfarthing-dist/skills/pf-workflow/examples.md +27 -16
  156. package/pennyfarthing-dist/skills/pf-workflow/skill.md +9 -12
  157. package/pennyfarthing-dist/skills/pf-workflow/usage.md +33 -8
  158. package/pennyfarthing-dist/workflows/bdd-tandem.yaml +18 -6
  159. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-01-analyze.md +1 -1
  160. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-04-verify.md +1 -1
  161. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +1 -1
  162. package/pennyfarthing-dist/workflows/review-tandem.yaml +65 -0
  163. package/pennyfarthing-dist/workflows/tdd-tandem.yaml +16 -8
  164. package/pennyfarthing_scripts/CLAUDE.md +26 -4
  165. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  166. package/pennyfarthing_scripts/__pycache__/hooks.cpython-314.pyc +0 -0
  167. package/pennyfarthing_scripts/__pycache__/pretooluse_hook.cpython-314.pyc +0 -0
  168. package/pennyfarthing_scripts/__pycache__/session_start_hook.cpython-314.pyc +0 -0
  169. package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-314.pyc +0 -0
  170. package/pennyfarthing_scripts/bc/cli.py +3 -5
  171. package/pennyfarthing_scripts/bikerack/__pycache__/background_panel.cpython-314.pyc +0 -0
  172. package/pennyfarthing_scripts/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
  173. package/pennyfarthing_scripts/bikerack/__pycache__/changed_panel.cpython-314.pyc +0 -0
  174. package/pennyfarthing_scripts/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
  175. package/pennyfarthing_scripts/bikerack/__pycache__/debug_panel.cpython-314.pyc +0 -0
  176. package/pennyfarthing_scripts/bikerack/__pycache__/diffs_panel.cpython-314.pyc +0 -0
  177. package/pennyfarthing_scripts/bikerack/__pycache__/git_panel.cpython-314.pyc +0 -0
  178. package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
  179. package/pennyfarthing_scripts/bikerack/__pycache__/portrait.cpython-314.pyc +0 -0
  180. package/pennyfarthing_scripts/bikerack/__pycache__/progress_panel.cpython-314.pyc +0 -0
  181. package/pennyfarthing_scripts/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
  182. package/pennyfarthing_scripts/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
  183. package/pennyfarthing_scripts/bikerack/__pycache__/ws_client.cpython-314.pyc +0 -0
  184. package/pennyfarthing_scripts/bikerack/background_panel.py +86 -5
  185. package/pennyfarthing_scripts/bikerack/base_panel.py +62 -0
  186. package/pennyfarthing_scripts/bikerack/changed_panel.py +32 -28
  187. package/pennyfarthing_scripts/bikerack/cli.py +10 -11
  188. package/pennyfarthing_scripts/bikerack/debug_panel.py +31 -1
  189. package/pennyfarthing_scripts/bikerack/diffs_panel.py +74 -17
  190. package/pennyfarthing_scripts/bikerack/git_panel.py +103 -33
  191. package/pennyfarthing_scripts/bikerack/launcher.py +15 -15
  192. package/pennyfarthing_scripts/bikerack/progress_panel.py +315 -0
  193. package/pennyfarthing_scripts/bikerack/sprint_panel.py +158 -26
  194. package/pennyfarthing_scripts/bikerack/tui.py +336 -30
  195. package/pennyfarthing_scripts/bikerack/ws_client.py +2 -2
  196. package/pennyfarthing_scripts/cli.py +37 -65
  197. package/pennyfarthing_scripts/consultation/__init__.py +1 -0
  198. package/pennyfarthing_scripts/consultation/__pycache__/__init__.cpython-314.pyc +0 -0
  199. package/pennyfarthing_scripts/consultation/__pycache__/cli.cpython-314.pyc +0 -0
  200. package/pennyfarthing_scripts/consultation/cli.py +149 -0
  201. package/pennyfarthing_scripts/consultation/dialogue_manager.py +417 -0
  202. package/pennyfarthing_scripts/context.py +3 -3
  203. package/pennyfarthing_scripts/epic/__pycache__/__init__.cpython-314.pyc +0 -0
  204. package/pennyfarthing_scripts/epic/__pycache__/cli.cpython-314.pyc +0 -0
  205. package/pennyfarthing_scripts/git/__init__.py +12 -1
  206. package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
  207. package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
  208. package/pennyfarthing_scripts/git/__pycache__/hooks_installer.cpython-314.pyc +0 -0
  209. package/pennyfarthing_scripts/git/__pycache__/repos.cpython-314.pyc +0 -0
  210. package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
  211. package/pennyfarthing_scripts/git/__pycache__/worktree.cpython-314.pyc +0 -0
  212. package/pennyfarthing_scripts/git/create_branches.py +3 -4
  213. package/pennyfarthing_scripts/git/hooks_installer.py +152 -0
  214. package/pennyfarthing_scripts/git/repos.py +196 -0
  215. package/pennyfarthing_scripts/git/status_all.py +27 -11
  216. package/pennyfarthing_scripts/git/worktree.py +302 -0
  217. package/pennyfarthing_scripts/git_group/__pycache__/__init__.cpython-314.pyc +0 -0
  218. package/pennyfarthing_scripts/git_group/__pycache__/cli.cpython-314.pyc +0 -0
  219. package/pennyfarthing_scripts/git_group/cli.py +143 -40
  220. package/pennyfarthing_scripts/handoff/__pycache__/__init__.cpython-314.pyc +0 -0
  221. package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-314.pyc +0 -0
  222. package/pennyfarthing_scripts/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
  223. package/pennyfarthing_scripts/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
  224. package/pennyfarthing_scripts/handoff/complete_phase.py +12 -0
  225. package/pennyfarthing_scripts/handoff/resolve_gate.py +5 -14
  226. package/pennyfarthing_scripts/hooks/cyclist-pretooluse-hook.sh +0 -0
  227. package/pennyfarthing_scripts/hooks.py +3 -17
  228. package/pennyfarthing_scripts/pretooluse_hook.py +1 -1
  229. package/pennyfarthing_scripts/prime/__pycache__/heatmap.cpython-314.pyc +0 -0
  230. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  231. package/pennyfarthing_scripts/prime/heatmap.py +655 -0
  232. package/pennyfarthing_scripts/session/__pycache__/__init__.cpython-314.pyc +0 -0
  233. package/pennyfarthing_scripts/session/__pycache__/cli.cpython-314.pyc +0 -0
  234. package/pennyfarthing_scripts/session_start_hook.py +1 -1
  235. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  236. package/pennyfarthing_scripts/sprint/loader.py +15 -1
  237. package/pennyfarthing_scripts/sprint/story_finish.py +14 -0
  238. package/pennyfarthing_scripts/tests/__pycache__/test_handoff_cli.cpython-314-pytest-9.0.2.pyc +0 -0
  239. package/pennyfarthing_scripts/tests/__pycache__/test_handoff_e2e.cpython-314-pytest-9.0.2.pyc +0 -0
  240. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
  241. package/pennyfarthing_scripts/tests/test_bikerack.py +51 -51
  242. package/pennyfarthing_scripts/tests/test_dialogue_manager.py +811 -0
  243. package/pennyfarthing_scripts/tests/test_handoff_cli.py +16 -11
  244. package/pennyfarthing_scripts/tests/test_workflow_check.py +2 -3
  245. package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +0 -0
  246. package/pennyfarthing_scripts/validate/adapters/tandem_awareness.py +254 -0
  247. package/pennyfarthing_scripts/validate/cli.py +17 -5
  248. package/pennyfarthing_scripts/workflow/__init__.py +40 -0
  249. package/pennyfarthing_scripts/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
  250. package/pennyfarthing_scripts/workflow/__pycache__/cli.cpython-314.pyc +0 -0
  251. package/pennyfarthing_scripts/workflow/__pycache__/helpers.cpython-314.pyc +0 -0
  252. package/pennyfarthing_scripts/workflow/__pycache__/scale.cpython-314.pyc +0 -0
  253. package/pennyfarthing_scripts/workflow/__pycache__/state.cpython-314.pyc +0 -0
  254. package/pennyfarthing_scripts/workflow/cli.py +1099 -0
  255. package/pennyfarthing_scripts/workflow/helpers.py +241 -0
  256. package/pennyfarthing_scripts/{workflow.py → workflow/scale.py} +0 -104
  257. package/pennyfarthing_scripts/workflow/state.py +112 -0
  258. package/scripts/README.md +41 -0
  259. package/pennyfarthing-dist/skills/pf-workflow/scripts/list-workflows.sh +0 -91
  260. package/pennyfarthing-dist/skills/pf-workflow/scripts/resume-workflow.sh +0 -163
  261. package/pennyfarthing-dist/skills/pf-workflow/scripts/show-workflow.sh +0 -138
  262. package/pennyfarthing-dist/skills/pf-workflow/scripts/start-workflow.sh +0 -273
  263. package/pennyfarthing-dist/skills/pf-workflow/scripts/workflow-status.sh +0 -167
@@ -5,15 +5,16 @@ Usage:
5
5
  pf git [COMMAND] [ARGS]...
6
6
 
7
7
  Commands:
8
- status Check git status of all project repos
9
- cleanup Organize changes into proper commits/branches
10
- branches Create feature branches from a story
11
- release Interactive release with verification gates
8
+ status Check git status of all project repos
9
+ cleanup Organize changes into proper commits/branches
10
+ branches Create feature branches from a story
11
+ worktree Manage git worktrees for parallel work
12
+ install-hooks Install git hooks with .d/ dispatcher pattern
12
13
  """
13
14
 
14
- import click
15
+ import asyncio
15
16
 
16
- from pennyfarthing_scripts.common.config import get_project_root
17
+ import click
17
18
 
18
19
 
19
20
  @click.group()
@@ -22,10 +23,11 @@ def git():
22
23
 
23
24
  \b
24
25
  Commands:
25
- status - Check git status of all repos
26
- cleanup - Organize changes into commits/branches
27
- branches - Create feature branches from a story
28
- release - Interactive release with verification gates
26
+ status - Check git status of all repos
27
+ cleanup - Organize changes into commits/branches
28
+ branches - Create feature branches from a story
29
+ worktree - Manage git worktrees for parallel work
30
+ install-hooks - Install git hooks with .d/ dispatcher
29
31
  """
30
32
  pass
31
33
 
@@ -37,21 +39,38 @@ def status(brief: bool):
37
39
 
38
40
  Shows branch, uncommitted changes, and ahead/behind status for each repo.
39
41
  """
40
- import subprocess
42
+ from pennyfarthing_scripts.git.repos import get_repo_paths, load_repos_config
43
+ from pennyfarthing_scripts.git.status_all import (
44
+ format_status_brief,
45
+ format_status_full,
46
+ format_summary,
47
+ get_all_repo_status,
48
+ )
41
49
 
42
- root = get_project_root()
43
- script = root / ".pennyfarthing" / "scripts" / "git" / "git-status-all.sh"
50
+ repo_paths = get_repo_paths()
51
+ config = load_repos_config()
44
52
 
45
- if not script.is_file():
46
- click.echo("Error: git-status-all.sh not found", err=True)
47
- raise SystemExit(1)
53
+ # Build repo list with per-repo upstream refs
54
+ repos_with_upstream: list[tuple[str, object, str]] = []
55
+ for name, path in repo_paths:
56
+ upstream = config[name].upstream_ref if name in config else "origin/main"
57
+ repos_with_upstream.append((name, path, upstream))
58
+
59
+ statuses = asyncio.run(get_all_repo_status(repos_with_upstream))
48
60
 
49
- cmd = [str(script)]
50
61
  if brief:
51
- cmd.append("--brief")
62
+ click.echo(format_status_brief(statuses))
63
+ else:
64
+ click.echo("━" * 40)
65
+ click.echo(" Git Status - All Repos")
66
+ click.echo("━" * 40)
67
+ click.echo()
68
+ click.echo(format_status_full(statuses))
69
+ click.echo("━" * 40)
70
+ click.echo(format_summary(statuses))
52
71
 
53
- result = subprocess.run(cmd, cwd=str(root))
54
- raise SystemExit(result.returncode)
72
+ has_issues = any(not s.is_clean or s.has_unpushed for s in statuses)
73
+ raise SystemExit(1 if has_issues else 0)
55
74
 
56
75
 
57
76
  @git.command()
@@ -67,34 +86,118 @@ def cleanup():
67
86
 
68
87
 
69
88
  @git.command()
70
- @click.argument("story_id")
71
- def branches(story_id: str):
72
- """Create feature branches in both repos from a story.
89
+ @click.argument("branch_name")
90
+ @click.option(
91
+ "--repos",
92
+ type=click.Choice(["all", "api", "ui"]),
93
+ default="all",
94
+ help="Which repos to target",
95
+ )
96
+ def branches(branch_name: str, repos: str):
97
+ """Create feature branches across all configured repos.
73
98
 
74
99
  \b
75
100
  Arguments:
76
- STORY_ID - The story ID to create branches for (e.g., 86-3)
101
+ BRANCH_NAME - The branch name to create (e.g., feat/86-3-file-upload)
77
102
  """
78
- import subprocess
103
+ from pennyfarthing_scripts.git.create_branches import (
104
+ create_feature_branches,
105
+ detect_worktree,
106
+ filter_repos,
107
+ format_results,
108
+ )
109
+ from pennyfarthing_scripts.git.repos import get_repo_paths
110
+
111
+ is_worktree, worktree_name, _ = detect_worktree()
112
+ if is_worktree:
113
+ click.echo(f"📂 Detected worktree: {worktree_name}")
114
+ else:
115
+ click.echo("📂 Using main checkout")
116
+
117
+ all_repos = get_repo_paths()
118
+ filtered = filter_repos(all_repos, repos)
119
+
120
+ if not filtered:
121
+ click.echo(f"No repos match filter: {repos}", err=True)
122
+ raise SystemExit(1)
79
123
 
80
- root = get_project_root()
81
- script = root / ".pennyfarthing" / "scripts" / "git" / "create-branches.sh"
124
+ results = asyncio.run(create_feature_branches(filtered, branch_name))
125
+ click.echo(format_results(results, branch_name))
82
126
 
83
- if not script.is_file():
84
- click.echo("Error: create-branches.sh not found", err=True)
85
- raise SystemExit(1)
127
+ from pennyfarthing_scripts.git.create_branches import BranchAction
86
128
 
87
- result = subprocess.run([str(script), story_id], cwd=str(root))
88
- raise SystemExit(result.returncode)
129
+ has_errors = any(r.action == BranchAction.ERROR for r in results)
130
+ raise SystemExit(1 if has_errors else 0)
89
131
 
90
132
 
91
- @git.command()
92
- def release():
93
- """Interactive release with verification gates.
133
+ # Worktree subgroup
134
+ @git.group()
135
+ def worktree():
136
+ """Manage git worktrees for parallel development.
137
+
138
+ \b
139
+ Commands:
140
+ create - Create worktree(s) for parallel work
141
+ remove - Remove worktree and clean up
142
+ list - List all active worktrees
143
+ status - Show detailed worktree status
144
+ """
145
+ pass
94
146
 
95
- Starts the release stepped workflow via BikeLane.
96
- Equivalent to: /pf-workflow start release
147
+
148
+ @worktree.command("create")
149
+ @click.argument("name")
150
+ @click.argument("branch")
151
+ @click.option(
152
+ "--repos",
153
+ default="all",
154
+ help="Repos filter: all, api, ui, or comma-separated names",
155
+ )
156
+ def worktree_create(name: str, branch: str, repos: str):
157
+ """Create worktree(s) for parallel work.
158
+
159
+ \b
160
+ Arguments:
161
+ NAME - Worktree name (e.g., wt-5-3a)
162
+ BRANCH - Branch name (e.g., feat/5-3a-file-upload)
97
163
  """
98
- click.echo("Starting release workflow...")
99
- click.echo("Run: /pf-workflow start release")
100
- click.echo("Or: pf workflow start release")
164
+ from pennyfarthing_scripts.git.worktree import create_worktree
165
+
166
+ raise SystemExit(create_worktree(name, branch, repos))
167
+
168
+
169
+ @worktree.command("remove")
170
+ @click.argument("name")
171
+ def worktree_remove(name: str):
172
+ """Remove worktree and clean up."""
173
+ from pennyfarthing_scripts.git.worktree import remove_worktree
174
+
175
+ raise SystemExit(remove_worktree(name))
176
+
177
+
178
+ @worktree.command("list")
179
+ def worktree_list():
180
+ """List all active worktrees."""
181
+ from pennyfarthing_scripts.git.worktree import list_worktrees
182
+
183
+ raise SystemExit(list_worktrees())
184
+
185
+
186
+ @worktree.command("status")
187
+ def worktree_status():
188
+ """Show detailed worktree status."""
189
+ from pennyfarthing_scripts.git.worktree import show_worktree_status
190
+
191
+ raise SystemExit(show_worktree_status())
192
+
193
+
194
+ @git.command("install-hooks")
195
+ def install_hooks():
196
+ """Install git hooks with .d/ dispatcher pattern.
197
+
198
+ Creates .d/ directories, symlinks pennyfarthing hooks, and
199
+ migrates existing user hooks.
200
+ """
201
+ from pennyfarthing_scripts.git.hooks_installer import install_git_hooks
202
+
203
+ raise SystemExit(install_git_hooks())
@@ -53,6 +53,18 @@ def complete_phase(
53
53
  }
54
54
 
55
55
  content = session_path.read_text()
56
+
57
+ # Guard: require assessment section before allowing gated phase transitions.
58
+ # Skip/manual transitions (e.g. setup→implement) don't need assessments.
59
+ if gate_type not in ("skip", "manual", "-", None, "") and not re.search(
60
+ r"^##\s+.*Assessment", content, re.MULTILINE
61
+ ):
62
+ return {
63
+ "status": "error",
64
+ "session_file": str(session_path),
65
+ "error": "No assessment found in session file. Write your assessment before completing the phase.",
66
+ }
67
+
56
68
  now = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
57
69
 
58
70
  from_agent = _get_phase_agent(project_root, workflow, from_phase)
@@ -1,14 +1,14 @@
1
1
  """Resolve gate for current workflow phase.
2
2
 
3
- Reads workflow YAML, finds current phase gate, checks for assessment
4
- section in session file, and returns a structured RESOLVE_RESULT.
3
+ Reads workflow YAML, finds current phase gate, and returns gate info.
4
+ Assessment checks are enforced in complete_phase (not here) to avoid
5
+ race conditions where agents call resolve-gate before writing assessments.
5
6
 
6
7
  Story: 105-1 (Script-First Handoff)
7
8
  """
8
9
 
9
10
  from __future__ import annotations
10
11
 
11
- import re
12
12
  from pathlib import Path
13
13
 
14
14
  import yaml
@@ -95,22 +95,13 @@ def resolve_gate(
95
95
  assessment_found=True,
96
96
  )
97
97
 
98
- session_path = project_root / ".session" / f"{story_id}-session.md"
99
- assessment_found = False
100
- if session_path.exists():
101
- content = session_path.read_text()
102
- assessment_found = bool(
103
- re.search(r"^##\s+.*Assessment", content, re.MULTILINE)
104
- )
105
-
106
- status = "ready" if assessment_found else "blocked"
107
98
  return _result(
108
- status=status,
99
+ status="ready",
109
100
  gate_type=gate_type,
110
101
  gate_file=gate_file,
111
102
  next_agent=next_agent,
112
103
  next_phase=next_phase,
113
- assessment_found=assessment_found,
104
+ assessment_found=True,
114
105
  )
115
106
 
116
107
 
@@ -30,10 +30,7 @@ import yaml
30
30
 
31
31
  # WheelHub port file - central coordination server for all communication
32
32
  # Per ADR-0004: "the hub where all communication converges"
33
- CYCLIST_PORT_FILE = ".cyclist-port"
34
-
35
- # Legacy approval port file (deprecated, for backwards compatibility during migration)
36
- CYCLIST_APPROVAL_PORT_FILE_LEGACY = ".cyclist-approval-port"
33
+ CYCLIST_PORT_FILE = ".wheelhub-port"
37
34
 
38
35
  # Default port if file not found
39
36
  DEFAULT_CYCLIST_PORT = 7431
@@ -51,7 +48,7 @@ def find_project_root(start_dir: Path | None = None) -> Path | None:
51
48
  """Find the project root by looking for marker files.
52
49
 
53
50
  Searches for (in order):
54
- 1. .cyclist-port or .cyclist-approval-port (Cyclist is running)
51
+ 1. .wheelhub-port (WheelHub is running)
55
52
  2. .pennyfarthing directory
56
53
  3. .claude directory
57
54
 
@@ -68,8 +65,6 @@ def find_project_root(start_dir: Path | None = None) -> Path | None:
68
65
  # Check for Cyclist port files first (indicates Cyclist is running)
69
66
  if (current / CYCLIST_PORT_FILE).exists():
70
67
  return current
71
- if (current / CYCLIST_APPROVAL_PORT_FILE_LEGACY).exists():
72
- return current
73
68
  # Fall back to directory markers
74
69
  if (current / ".pennyfarthing").is_dir():
75
70
  return current
@@ -89,7 +84,7 @@ def read_port_file(file_name: str, project_root: Path | None = None) -> int | No
89
84
  """Read a port number from a Cyclist port file.
90
85
 
91
86
  Args:
92
- file_name: Name of the port file (.cyclist-port or .cyclist-approval-port)
87
+ file_name: Name of the port file (e.g. .wheelhub-port)
93
88
  project_root: Project root directory (auto-detected if not provided)
94
89
 
95
90
  Returns:
@@ -130,18 +125,9 @@ def get_cyclist_port(project_root: Path | None = None) -> int:
130
125
  if port:
131
126
  return port
132
127
 
133
- # Fallback to legacy approval port file during migration
134
- legacy_port = read_port_file(CYCLIST_APPROVAL_PORT_FILE_LEGACY, project_root)
135
- if legacy_port:
136
- return legacy_port
137
-
138
128
  return DEFAULT_CYCLIST_PORT
139
129
 
140
130
 
141
- # Alias for backwards compatibility
142
- get_approval_port = get_cyclist_port
143
-
144
-
145
131
  # =============================================================================
146
132
  # Settings Loading
147
133
  # =============================================================================
@@ -8,7 +8,7 @@ via HTTP to get approval decisions.
8
8
 
9
9
  Flow:
10
10
  1. Claude Code calls this script with tool info via stdin (JSON)
11
- 2. Script reads port from .cyclist-port in project directory
11
+ 2. Script reads port from .wheelhub-port in project directory
12
12
  3. Script sends request to WheelHub's /api/hook-request endpoint
13
13
  4. WheelHub shows approval modal, user decides
14
14
  5. Script receives response, outputs JSON decision to stdout