@pennyfarthing/core 11.0.0-alpha.0 → 11.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 (401) hide show
  1. package/README.md +84 -26
  2. package/package.json +14 -16
  3. package/packages/core/dist/cli/cyclist-migration.test.js +2 -1
  4. package/packages/core/dist/cli/cyclist-migration.test.js.map +1 -1
  5. package/packages/core/dist/cli/ocean-profiles.test.js +5 -4
  6. package/packages/core/dist/cli/ocean-profiles.test.js.map +1 -1
  7. package/packages/core/dist/cli/theme-maker.test.js +5 -4
  8. package/packages/core/dist/cli/theme-maker.test.js.map +1 -1
  9. package/packages/core/dist/cli/utils/010-detect-remove-old-packages.test.d.ts +20 -0
  10. package/packages/core/dist/cli/utils/010-detect-remove-old-packages.test.d.ts.map +1 -0
  11. package/packages/core/dist/cli/utils/010-detect-remove-old-packages.test.js +278 -0
  12. package/packages/core/dist/cli/utils/010-detect-remove-old-packages.test.js.map +1 -0
  13. package/packages/core/dist/cli/utils/constants.d.ts +7 -1
  14. package/packages/core/dist/cli/utils/constants.d.ts.map +1 -1
  15. package/packages/core/dist/cli/utils/constants.js +2 -0
  16. package/packages/core/dist/cli/utils/constants.js.map +1 -1
  17. package/packages/core/dist/cli/utils/constants.test.d.ts +10 -0
  18. package/packages/core/dist/cli/utils/constants.test.d.ts.map +1 -0
  19. package/packages/core/dist/cli/utils/constants.test.js +38 -0
  20. package/packages/core/dist/cli/utils/constants.test.js.map +1 -0
  21. package/packages/core/dist/consultation/consultation-protocol.d.ts +139 -0
  22. package/packages/core/dist/consultation/consultation-protocol.d.ts.map +1 -0
  23. package/packages/core/dist/consultation/consultation-protocol.js +178 -0
  24. package/packages/core/dist/consultation/consultation-protocol.js.map +1 -0
  25. package/packages/core/dist/consultation/consultation-protocol.test.d.ts +20 -0
  26. package/packages/core/dist/consultation/consultation-protocol.test.d.ts.map +1 -0
  27. package/packages/core/dist/consultation/consultation-protocol.test.js +474 -0
  28. package/packages/core/dist/consultation/consultation-protocol.test.js.map +1 -0
  29. package/packages/core/dist/public/js/react/react.js +30 -30
  30. package/packages/core/dist/scripts/generate-report.test.js +2 -2
  31. package/packages/core/dist/scripts/generate-spider-report.test.js +2 -2
  32. package/packages/core/dist/scripts/generate-spider.test.js +2 -1
  33. package/packages/core/dist/scripts/generate-spider.test.js.map +1 -1
  34. package/packages/core/dist/server/api/file-browser.d.ts.map +1 -1
  35. package/packages/core/dist/server/api/file-browser.js +19 -1
  36. package/packages/core/dist/server/api/file-browser.js.map +1 -1
  37. package/packages/core/dist/server/api/git-fetch-cooldown.test.d.ts +10 -0
  38. package/packages/core/dist/server/api/git-fetch-cooldown.test.d.ts.map +1 -0
  39. package/packages/core/dist/server/api/git-fetch-cooldown.test.js +30 -0
  40. package/packages/core/dist/server/api/git-fetch-cooldown.test.js.map +1 -0
  41. package/packages/core/dist/server/api/git.d.ts +8 -0
  42. package/packages/core/dist/server/api/git.d.ts.map +1 -1
  43. package/packages/core/dist/server/api/git.js +37 -10
  44. package/packages/core/dist/server/api/git.js.map +1 -1
  45. package/packages/core/dist/server/api/health-score.d.ts.map +1 -1
  46. package/packages/core/dist/server/api/health-score.js +25 -1
  47. package/packages/core/dist/server/api/health-score.js.map +1 -1
  48. package/packages/core/dist/server/api/index.d.ts +1 -1
  49. package/packages/core/dist/server/api/index.d.ts.map +1 -1
  50. package/packages/core/dist/server/api/index.js +1 -1
  51. package/packages/core/dist/server/api/index.js.map +1 -1
  52. package/packages/core/dist/server/api/settings.d.ts.map +1 -1
  53. package/packages/core/dist/server/api/settings.js +73 -2
  54. package/packages/core/dist/server/api/settings.js.map +1 -1
  55. package/packages/core/dist/server/api/theme-agents.d.ts.map +1 -1
  56. package/packages/core/dist/server/api/theme-agents.js +61 -0
  57. package/packages/core/dist/server/api/theme-agents.js.map +1 -1
  58. package/packages/core/dist/server/otlp-receiver.d.ts +35 -13
  59. package/packages/core/dist/server/otlp-receiver.d.ts.map +1 -1
  60. package/packages/core/dist/server/otlp-receiver.js +76 -16
  61. package/packages/core/dist/server/otlp-receiver.js.map +1 -1
  62. package/packages/core/dist/server/paths.d.ts.map +1 -1
  63. package/packages/core/dist/server/paths.js +11 -1
  64. package/packages/core/dist/server/paths.js.map +1 -1
  65. package/packages/core/dist/server/server.d.ts +3 -1
  66. package/packages/core/dist/server/server.d.ts.map +1 -1
  67. package/packages/core/dist/server/server.js +23 -16
  68. package/packages/core/dist/server/server.js.map +1 -1
  69. package/packages/core/dist/server/server.test.js.map +1 -1
  70. package/packages/core/dist/workflow/gate-file-validation.d.ts +49 -0
  71. package/packages/core/dist/workflow/gate-file-validation.d.ts.map +1 -0
  72. package/packages/core/dist/workflow/gate-file-validation.js +157 -0
  73. package/packages/core/dist/workflow/gate-file-validation.js.map +1 -0
  74. package/packages/core/dist/workflow/gate-file-validation.test.d.ts +19 -0
  75. package/packages/core/dist/workflow/gate-file-validation.test.d.ts.map +1 -0
  76. package/packages/core/dist/workflow/gate-file-validation.test.js +536 -0
  77. package/packages/core/dist/workflow/gate-file-validation.test.js.map +1 -0
  78. package/packages/core/dist/workflow/gate-schema-validation.test.d.ts +14 -0
  79. package/packages/core/dist/workflow/gate-schema-validation.test.d.ts.map +1 -0
  80. package/packages/core/dist/workflow/gate-schema-validation.test.js +339 -0
  81. package/packages/core/dist/workflow/gate-schema-validation.test.js.map +1 -0
  82. package/packages/core/dist/workflow/handoff.js +2 -2
  83. package/packages/core/dist/workflow/handoff.js.map +1 -1
  84. package/packages/core/dist/workflow/handoff.test.js +16 -0
  85. package/packages/core/dist/workflow/handoff.test.js.map +1 -1
  86. package/packages/core/dist/workflow/variable-resolver.test.js +1 -1
  87. package/packages/core/dist/workflow/variable-resolver.test.js.map +1 -1
  88. package/packages/core/dist/workflow/workflow-migration.test.js +4 -3
  89. package/packages/core/dist/workflow/workflow-migration.test.js.map +1 -1
  90. package/packages/core/dist/workflow/workflow-schema.d.ts +4 -2
  91. package/packages/core/dist/workflow/workflow-schema.d.ts.map +1 -1
  92. package/packages/core/dist/workflow/workflow-schema.js +43 -8
  93. package/packages/core/dist/workflow/workflow-schema.js.map +1 -1
  94. package/pennyfarthing-dist/agents/README.md +6 -14
  95. package/pennyfarthing-dist/agents/architect.md +43 -30
  96. package/pennyfarthing-dist/agents/ba.md +30 -29
  97. package/pennyfarthing-dist/agents/dev.md +76 -41
  98. package/pennyfarthing-dist/agents/devops.md +57 -21
  99. package/pennyfarthing-dist/agents/orchestrator.md +3 -11
  100. package/pennyfarthing-dist/agents/pm.md +45 -31
  101. package/pennyfarthing-dist/agents/reviewer.md +20 -66
  102. package/pennyfarthing-dist/agents/sm-setup.md +2 -2
  103. package/pennyfarthing-dist/agents/sm.md +8 -30
  104. package/pennyfarthing-dist/agents/tea.md +25 -41
  105. package/pennyfarthing-dist/agents/tech-writer.md +33 -90
  106. package/pennyfarthing-dist/agents/ux-designer.md +39 -40
  107. package/pennyfarthing-dist/commands/benchmark-control.md +8 -64
  108. package/pennyfarthing-dist/commands/benchmark.md +8 -480
  109. package/pennyfarthing-dist/commands/job-fair.md +8 -97
  110. package/pennyfarthing-dist/commands/pf-benchmark-control.md +70 -0
  111. package/pennyfarthing-dist/commands/pf-benchmark.md +486 -0
  112. package/pennyfarthing-dist/commands/pf-chore.md +4 -4
  113. package/pennyfarthing-dist/commands/pf-ci.md +40 -0
  114. package/pennyfarthing-dist/commands/pf-close-epic.md +9 -27
  115. package/pennyfarthing-dist/commands/pf-continue-session.md +9 -213
  116. package/pennyfarthing-dist/commands/pf-create-branches-from-story.md +11 -353
  117. package/pennyfarthing-dist/commands/pf-docs.md +28 -0
  118. package/pennyfarthing-dist/commands/pf-epic.md +67 -0
  119. package/pennyfarthing-dist/commands/pf-git-cleanup.md +11 -52
  120. package/pennyfarthing-dist/commands/pf-git.md +75 -0
  121. package/pennyfarthing-dist/commands/pf-help.md +110 -128
  122. package/pennyfarthing-dist/commands/pf-job-fair.md +102 -0
  123. package/pennyfarthing-dist/commands/pf-new-work.md +9 -18
  124. package/pennyfarthing-dist/commands/pf-parallel-work.md +6 -66
  125. package/pennyfarthing-dist/commands/pf-release.md +11 -76
  126. package/pennyfarthing-dist/commands/pf-repo-status.md +11 -44
  127. package/pennyfarthing-dist/commands/pf-run-ci.md +8 -111
  128. package/pennyfarthing-dist/commands/pf-session.md +51 -0
  129. package/pennyfarthing-dist/commands/pf-solo.md +447 -0
  130. package/pennyfarthing-dist/commands/pf-sprint-planning.md +8 -104
  131. package/pennyfarthing-dist/commands/pf-standalone.md +1 -1
  132. package/pennyfarthing-dist/commands/pf-start-epic.md +9 -163
  133. package/pennyfarthing-dist/commands/pf-sync-epic-to-jira.md +8 -179
  134. package/pennyfarthing-dist/commands/pf-sync-work-with-sprint.md +8 -368
  135. package/pennyfarthing-dist/commands/pf-update-domain-docs.md +8 -78
  136. package/pennyfarthing-dist/commands/solo.md +8 -442
  137. package/pennyfarthing-dist/guides/agent-behavior.md +14 -14
  138. package/pennyfarthing-dist/guides/agent-coordination.md +7 -7
  139. package/pennyfarthing-dist/guides/agent-tag-taxonomy.md +6 -6
  140. package/pennyfarthing-dist/guides/bikerack.md +128 -0
  141. package/pennyfarthing-dist/guides/brownfield-tools.md +133 -0
  142. package/pennyfarthing-dist/guides/command-tag-taxonomy.md +2 -2
  143. package/pennyfarthing-dist/guides/gate-schema.md +227 -0
  144. package/pennyfarthing-dist/guides/gates.md +120 -0
  145. package/pennyfarthing-dist/guides/handoff-cli.md +116 -0
  146. package/pennyfarthing-dist/guides/hooks.md +86 -4
  147. package/pennyfarthing-dist/guides/output-styles.md +65 -0
  148. package/pennyfarthing-dist/guides/patterns/approval-gates-pattern.md +5 -5
  149. package/pennyfarthing-dist/guides/patterns/tdd-flow-pattern.md +4 -4
  150. package/pennyfarthing-dist/guides/prompt-patterns.md +5 -5
  151. package/pennyfarthing-dist/guides/reflector.md +4 -4
  152. package/pennyfarthing-dist/guides/session-artifacts.md +1 -1
  153. package/pennyfarthing-dist/guides/skill-schema.md +1 -1
  154. package/pennyfarthing-dist/guides/tandem-protocol.md +13 -1
  155. package/pennyfarthing-dist/guides/worktree-mode.md +3 -3
  156. package/pennyfarthing-dist/guides/xml-tags.md +5 -4
  157. package/pennyfarthing-dist/personas/themes/hogans-heroes.yaml +11 -22
  158. package/pennyfarthing-dist/personas/themes/stephen-king.yaml +13 -24
  159. package/pennyfarthing-dist/scripts/core/agent-session.sh +0 -0
  160. package/pennyfarthing-dist/scripts/core/check-context.sh +0 -0
  161. package/pennyfarthing-dist/scripts/core/phase-check-start.sh +1 -1
  162. package/pennyfarthing-dist/scripts/core/prime.sh +0 -0
  163. package/pennyfarthing-dist/scripts/cyclist/is-cyclist.sh +0 -0
  164. package/pennyfarthing-dist/scripts/git/create-feature-branches.sh +0 -0
  165. package/pennyfarthing-dist/scripts/git/git-status-all.sh +0 -0
  166. package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +0 -0
  167. package/pennyfarthing-dist/scripts/git/release.sh +0 -0
  168. package/pennyfarthing-dist/scripts/git/worktree-manager.sh +0 -0
  169. package/pennyfarthing-dist/scripts/health/drift-detection.sh +0 -0
  170. package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +0 -0
  171. package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +0 -0
  172. package/pennyfarthing-dist/scripts/hooks/context-warning.sh +0 -0
  173. package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +0 -0
  174. package/pennyfarthing-dist/scripts/hooks/dispatcher-template.sh +0 -0
  175. package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +19 -14
  176. package/pennyfarthing-dist/scripts/hooks/post-merge.sh +0 -0
  177. package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +0 -0
  178. package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +0 -0
  179. package/pennyfarthing-dist/scripts/hooks/pre-push.sh +0 -0
  180. package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +0 -0
  181. package/pennyfarthing-dist/scripts/hooks/question_reflector_check.py +0 -0
  182. package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +0 -0
  183. package/pennyfarthing-dist/scripts/hooks/session-start.sh +0 -0
  184. package/pennyfarthing-dist/scripts/hooks/session-stop.sh +0 -0
  185. package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +0 -0
  186. package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +0 -0
  187. package/pennyfarthing-dist/scripts/jira/create-jira-epic.sh +0 -0
  188. package/pennyfarthing-dist/scripts/jira/create-jira-story.sh +0 -0
  189. package/pennyfarthing-dist/scripts/jira/jira-claim-story.sh +0 -0
  190. package/pennyfarthing-dist/scripts/jira/jira-reconcile.sh +0 -0
  191. package/pennyfarthing-dist/scripts/jira/jira-sync-story.sh +0 -0
  192. package/pennyfarthing-dist/scripts/jira/sync-epic-jira.sh +0 -0
  193. package/pennyfarthing-dist/scripts/lib/background-tasks.sh +0 -0
  194. package/pennyfarthing-dist/scripts/lib/checkpoint.sh +0 -0
  195. package/pennyfarthing-dist/scripts/lib/common.sh +0 -0
  196. package/pennyfarthing-dist/scripts/lib/file-lock.sh +0 -0
  197. package/pennyfarthing-dist/scripts/lib/logging.sh +0 -0
  198. package/pennyfarthing-dist/scripts/lib/retry.sh +0 -0
  199. package/pennyfarthing-dist/scripts/maintenance/migrate-theme-schema.mjs +0 -0
  200. package/pennyfarthing-dist/scripts/maintenance/sidecar-health.sh +0 -0
  201. package/pennyfarthing-dist/scripts/misc/add-short-names.sh +0 -0
  202. package/pennyfarthing-dist/scripts/misc/add_short_names.py +0 -0
  203. package/pennyfarthing-dist/scripts/misc/backlog.sh +0 -0
  204. package/pennyfarthing-dist/scripts/misc/check-status.sh +0 -0
  205. package/pennyfarthing-dist/scripts/misc/find-related-work.sh +0 -0
  206. package/pennyfarthing-dist/scripts/misc/generate-skill-docs.sh +0 -0
  207. package/pennyfarthing-dist/scripts/misc/log-skill-usage.sh +0 -0
  208. package/pennyfarthing-dist/scripts/misc/migrate-bmad-workflow.sh +0 -0
  209. package/pennyfarthing-dist/scripts/misc/migrate_bmad_workflow.py +0 -0
  210. package/pennyfarthing-dist/scripts/misc/repo-scan.sh +0 -0
  211. package/pennyfarthing-dist/scripts/misc/repo-utils.sh +0 -0
  212. package/pennyfarthing-dist/scripts/misc/run-ci.sh +0 -0
  213. package/pennyfarthing-dist/scripts/misc/run-timestamp.sh +0 -0
  214. package/pennyfarthing-dist/scripts/misc/session-cleanup.sh +0 -0
  215. package/pennyfarthing-dist/scripts/misc/skill-usage-report.sh +0 -0
  216. package/pennyfarthing-dist/scripts/misc/statusline.sh +0 -0
  217. package/pennyfarthing-dist/scripts/misc/uninstall.sh +0 -0
  218. package/pennyfarthing-dist/scripts/misc/validate-subagent-frontmatter.sh +0 -0
  219. package/pennyfarthing-dist/scripts/portraits/generate-portraits.py +191 -57
  220. package/pennyfarthing-dist/scripts/portraits/generate-portraits.sh +26 -10
  221. package/pennyfarthing-dist/scripts/story/create-story.sh +0 -0
  222. package/pennyfarthing-dist/scripts/story/size-story.sh +0 -0
  223. package/pennyfarthing-dist/scripts/story/story-template.sh +0 -0
  224. package/pennyfarthing-dist/scripts/tests/check.test.sh +0 -0
  225. package/pennyfarthing-dist/scripts/tests/dev-story-workflow-import.test.sh +0 -0
  226. package/pennyfarthing-dist/scripts/tests/epics-and-stories-workflow-import.test.sh +0 -0
  227. package/pennyfarthing-dist/scripts/tests/handoff-phase-update.test.sh +0 -0
  228. package/pennyfarthing-dist/scripts/tests/implementation-readiness-workflow-import.test.sh +0 -0
  229. package/pennyfarthing-dist/scripts/tests/migrate-bmad-workflow.test.sh +0 -0
  230. package/pennyfarthing-dist/scripts/tests/prd-workflow-import.test.sh +0 -0
  231. package/pennyfarthing-dist/scripts/tests/project-context-workflow-import.test.sh +0 -0
  232. package/pennyfarthing-dist/scripts/tests/test-character-voice.sh +0 -0
  233. package/pennyfarthing-dist/scripts/tests/test-drift-detection.sh +0 -0
  234. package/pennyfarthing-dist/scripts/tests/test-post-merge-hook.sh +0 -0
  235. package/pennyfarthing-dist/scripts/tests/test-session-checkpoint.sh +0 -0
  236. package/pennyfarthing-dist/scripts/tests/test-solo-command.sh +0 -0
  237. package/pennyfarthing-dist/scripts/tests/ux-design-workflow-import.test.sh +0 -0
  238. package/pennyfarthing-dist/scripts/theme/list-themes.sh +0 -0
  239. package/pennyfarthing-dist/scripts/validation/validate-agent-schema.sh +0 -0
  240. package/pennyfarthing-dist/scripts/workflow/check.py +0 -0
  241. package/pennyfarthing-dist/scripts/workflow/check.sh +0 -0
  242. package/pennyfarthing-dist/scripts/workflow/complete-step.py +0 -0
  243. package/pennyfarthing-dist/scripts/workflow/finish-story.sh +0 -0
  244. package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +0 -0
  245. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.py +0 -0
  246. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +0 -0
  247. package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +0 -0
  248. package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +0 -0
  249. package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +0 -0
  250. package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +0 -0
  251. package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +0 -0
  252. package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +0 -0
  253. package/pennyfarthing-dist/skills/pf-changelog/SKILL.md +4 -4
  254. package/pennyfarthing-dist/skills/pf-sprint/skill.md +1 -1
  255. package/pennyfarthing-dist/skills/pf-story/scripts/create-story.sh +0 -0
  256. package/pennyfarthing-dist/skills/pf-story/scripts/size-story.sh +0 -0
  257. package/pennyfarthing-dist/skills/pf-story/scripts/story-template.sh +0 -0
  258. package/pennyfarthing-dist/skills/pf-systematic-debugging/SKILL.md +0 -1
  259. package/pennyfarthing-dist/skills/pf-workflow/scripts/list-workflows.sh +0 -0
  260. package/pennyfarthing-dist/skills/pf-workflow/scripts/resume-workflow.sh +0 -0
  261. package/pennyfarthing-dist/skills/pf-workflow/scripts/show-workflow.sh +0 -0
  262. package/pennyfarthing-dist/skills/pf-workflow/scripts/start-workflow.sh +0 -0
  263. package/pennyfarthing-dist/skills/pf-workflow/scripts/workflow-status.sh +0 -0
  264. package/pennyfarthing-dist/skills/skill-registry.schema.json +4 -0
  265. package/pennyfarthing-dist/skills/skill-registry.yaml +8 -21
  266. package/pennyfarthing-dist/workflows/2party-tdd.yaml +11 -0
  267. package/pennyfarthing-dist/workflows/agent-docs.yaml +2 -0
  268. package/pennyfarthing-dist/workflows/bdd-tandem.yaml +4 -0
  269. package/pennyfarthing-dist/workflows/bdd.yaml +4 -0
  270. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +1 -1
  271. package/pennyfarthing-dist/workflows/tdd-tandem.yaml +3 -0
  272. package/pennyfarthing-dist/workflows/tdd.yaml +3 -0
  273. package/pennyfarthing-dist/workflows/trivial.yaml +2 -0
  274. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  275. package/pennyfarthing_scripts/__pycache__/context.cpython-314.pyc +0 -0
  276. package/pennyfarthing_scripts/__pycache__/session_start_hook.cpython-314.pyc +0 -0
  277. package/pennyfarthing_scripts/bc/__pycache__/__init__.cpython-314.pyc +0 -0
  278. package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-314.pyc +0 -0
  279. package/pennyfarthing_scripts/bc/__pycache__/focus.cpython-314.pyc +0 -0
  280. package/pennyfarthing_scripts/bikerack/__pycache__/__init__.cpython-314.pyc +0 -0
  281. package/pennyfarthing_scripts/bikerack/__pycache__/background_panel.cpython-314.pyc +0 -0
  282. package/pennyfarthing_scripts/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
  283. package/pennyfarthing_scripts/bikerack/__pycache__/changed_panel.cpython-314.pyc +0 -0
  284. package/pennyfarthing_scripts/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
  285. package/pennyfarthing_scripts/bikerack/__pycache__/debug_panel.cpython-314.pyc +0 -0
  286. package/pennyfarthing_scripts/bikerack/__pycache__/diffs_panel.cpython-314.pyc +0 -0
  287. package/pennyfarthing_scripts/bikerack/__pycache__/git_panel.cpython-314.pyc +0 -0
  288. package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
  289. package/pennyfarthing_scripts/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
  290. package/pennyfarthing_scripts/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
  291. package/pennyfarthing_scripts/bikerack/__pycache__/ws_client.cpython-314.pyc +0 -0
  292. package/pennyfarthing_scripts/bikerack/changed_panel.py +105 -0
  293. package/pennyfarthing_scripts/bikerack/debug_panel.py +218 -0
  294. package/pennyfarthing_scripts/bikerack/diffs_panel.py +203 -27
  295. package/pennyfarthing_scripts/cli.py +114 -0
  296. package/pennyfarthing_scripts/epic/__init__.py +0 -0
  297. package/pennyfarthing_scripts/epic/cli.py +64 -0
  298. package/pennyfarthing_scripts/gate/__init__.py +1 -0
  299. package/pennyfarthing_scripts/gate/__pycache__/__init__.cpython-314.pyc +0 -0
  300. package/pennyfarthing_scripts/gate/__pycache__/cli.cpython-314.pyc +0 -0
  301. package/pennyfarthing_scripts/gate/__pycache__/validate.cpython-314.pyc +0 -0
  302. package/pennyfarthing_scripts/gate/cli.py +56 -0
  303. package/pennyfarthing_scripts/gate/validate.py +266 -0
  304. package/pennyfarthing_scripts/git_group/__init__.py +0 -0
  305. package/pennyfarthing_scripts/git_group/cli.py +100 -0
  306. package/pennyfarthing_scripts/handoff/__init__.py +1 -0
  307. package/pennyfarthing_scripts/handoff/__pycache__/__init__.cpython-314.pyc +0 -0
  308. package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-314.pyc +0 -0
  309. package/pennyfarthing_scripts/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
  310. package/pennyfarthing_scripts/handoff/__pycache__/gate_file.cpython-314.pyc +0 -0
  311. package/pennyfarthing_scripts/handoff/__pycache__/gate_runner.cpython-314.pyc +0 -0
  312. package/pennyfarthing_scripts/handoff/__pycache__/marker.cpython-314.pyc +0 -0
  313. package/pennyfarthing_scripts/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
  314. package/pennyfarthing_scripts/handoff/cli.py +120 -0
  315. package/pennyfarthing_scripts/handoff/complete_phase.py +155 -0
  316. package/pennyfarthing_scripts/handoff/gate_file.py +105 -0
  317. package/pennyfarthing_scripts/handoff/gate_runner.py +152 -0
  318. package/pennyfarthing_scripts/handoff/marker.py +109 -0
  319. package/pennyfarthing_scripts/handoff/resolve_gate.py +152 -0
  320. package/pennyfarthing_scripts/healthscore/__pycache__/__main__.cpython-314.pyc +0 -0
  321. package/pennyfarthing_scripts/healthscore/__pycache__/analyze.cpython-314.pyc +0 -0
  322. package/pennyfarthing_scripts/hooks/cyclist-pretooluse-hook.sh +0 -0
  323. package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
  324. package/pennyfarthing_scripts/launch/__pycache__/__init__.cpython-314.pyc +0 -0
  325. package/pennyfarthing_scripts/launch/__pycache__/cli.cpython-314.pyc +0 -0
  326. package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
  327. package/pennyfarthing_scripts/prime/__pycache__/version_sentinel.cpython-314.pyc +0 -0
  328. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  329. package/pennyfarthing_scripts/prime/workflow.py +39 -0
  330. package/pennyfarthing_scripts/session/__init__.py +0 -0
  331. package/pennyfarthing_scripts/session/cli.py +87 -0
  332. package/pennyfarthing_scripts/session_start_hook.py +4 -4
  333. package/pennyfarthing_scripts/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
  334. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  335. package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
  336. package/pennyfarthing_scripts/sprint/__pycache__/epic_update.cpython-314.pyc +0 -0
  337. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  338. package/pennyfarthing_scripts/sprint/__pycache__/story_add.cpython-314.pyc +0 -0
  339. package/pennyfarthing_scripts/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
  340. package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
  341. package/pennyfarthing_scripts/sprint/__pycache__/validate_cmd.cpython-314.pyc +0 -0
  342. package/pennyfarthing_scripts/sprint/__pycache__/yaml_io.cpython-314.pyc +0 -0
  343. package/pennyfarthing_scripts/sprint/archive_epic.py +8 -0
  344. package/pennyfarthing_scripts/tests/__pycache__/test_108_2_remove_handoff_fallback.cpython-314-pytest-9.0.2.pyc +0 -0
  345. package/pennyfarthing_scripts/tests/__pycache__/test_archive_epic.cpython-314-pytest-9.0.2.pyc +0 -0
  346. package/pennyfarthing_scripts/tests/__pycache__/test_bc.cpython-314-pytest-9.0.2.pyc +0 -0
  347. package/pennyfarthing_scripts/tests/__pycache__/test_bikerack.cpython-314-pytest-9.0.2.pyc +0 -0
  348. package/pennyfarthing_scripts/tests/__pycache__/test_cli_normalization.cpython-314-pytest-9.0.2.pyc +0 -0
  349. package/pennyfarthing_scripts/tests/__pycache__/test_gate_file_resolution.cpython-314-pytest-9.0.2.pyc +0 -0
  350. package/pennyfarthing_scripts/tests/__pycache__/test_gate_runner.cpython-314-pytest-9.0.2.pyc +0 -0
  351. package/pennyfarthing_scripts/tests/__pycache__/test_handoff_cli.cpython-314-pytest-9.0.2.pyc +0 -0
  352. package/pennyfarthing_scripts/tests/__pycache__/test_handoff_e2e.cpython-314-pytest-9.0.2.pyc +0 -0
  353. package/pennyfarthing_scripts/tests/__pycache__/test_resolve_gate_file_field.cpython-314-pytest-9.0.2.pyc +0 -0
  354. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_panel.cpython-314-pytest-9.0.2.pyc +0 -0
  355. package/pennyfarthing_scripts/tests/__pycache__/test_topology_loader.cpython-314-pytest-9.0.2.pyc +0 -0
  356. package/pennyfarthing_scripts/tests/__pycache__/test_tui_focus.cpython-314-pytest-9.0.2.pyc +0 -0
  357. package/pennyfarthing_scripts/tests/__pycache__/test_tui_panel_persistence.cpython-314-pytest-9.0.2.pyc +0 -0
  358. package/pennyfarthing_scripts/tests/__pycache__/test_version_sentinel.cpython-314-pytest-9.0.2.pyc +0 -0
  359. package/pennyfarthing_scripts/tests/__pycache__/test_yaml_io.cpython-314-pytest-9.0.2.pyc +0 -0
  360. package/pennyfarthing_scripts/tests/test_108_1_gate_migration.py +540 -0
  361. package/pennyfarthing_scripts/tests/test_108_2_remove_handoff_fallback.py +339 -0
  362. package/pennyfarthing_scripts/tests/test_archive_epic.py +1 -2
  363. package/pennyfarthing_scripts/tests/test_confidence_sm_evaluation.py +253 -0
  364. package/pennyfarthing_scripts/tests/test_confidence_sm_gate.py +315 -0
  365. package/pennyfarthing_scripts/tests/test_gate_file_resolution.py +341 -0
  366. package/pennyfarthing_scripts/tests/test_gate_runner.py +620 -0
  367. package/pennyfarthing_scripts/tests/test_handoff_cli.py +929 -0
  368. package/pennyfarthing_scripts/tests/test_handoff_e2e.py +454 -0
  369. package/pennyfarthing_scripts/tests/test_resolve_gate_file_field.py +464 -0
  370. package/pennyfarthing_scripts/theme/__pycache__/cli.cpython-314.pyc +0 -0
  371. package/pennyfarthing_scripts/validate/adapters/__pycache__/workflow.cpython-314.pyc +0 -0
  372. package/pennyfarthing_scripts/validate/adapters/skill_command.py +200 -0
  373. package/pennyfarthing_scripts/validate/adapters/workflow.py +64 -0
  374. package/pennyfarthing_scripts/validate/cli.py +15 -4
  375. package/packages/core/dist/benchmark/package-exports.test.d.ts.map +0 -1
  376. package/packages/core/dist/benchmark/package-exports.test.js.map +0 -1
  377. package/packages/core/dist/scripts/benchmark-integration.d.ts +0 -182
  378. package/packages/core/dist/scripts/benchmark-integration.d.ts.map +0 -1
  379. package/packages/core/dist/scripts/benchmark-integration.js +0 -691
  380. package/packages/core/dist/scripts/benchmark-integration.js.map +0 -1
  381. package/packages/core/dist/scripts/benchmark-integration.test.d.ts +0 -13
  382. package/packages/core/dist/scripts/benchmark-integration.test.d.ts.map +0 -1
  383. package/packages/core/dist/scripts/benchmark-integration.test.js +0 -680
  384. package/packages/core/dist/scripts/benchmark-integration.test.js.map +0 -1
  385. package/packages/core/dist/scripts/debugging-scenarios.test.d.ts +0 -18
  386. package/packages/core/dist/scripts/debugging-scenarios.test.d.ts.map +0 -1
  387. package/packages/core/dist/scripts/debugging-scenarios.test.js +0 -317
  388. package/packages/core/dist/scripts/debugging-scenarios.test.js.map +0 -1
  389. package/packages/core/dist/scripts/job-fair-aggregator.d.ts +0 -150
  390. package/packages/core/dist/scripts/job-fair-aggregator.d.ts.map +0 -1
  391. package/packages/core/dist/scripts/job-fair-aggregator.js +0 -547
  392. package/packages/core/dist/scripts/job-fair-aggregator.js.map +0 -1
  393. package/packages/core/dist/scripts/job-fair-aggregator.test.d.ts +0 -14
  394. package/packages/core/dist/scripts/job-fair-aggregator.test.d.ts.map +0 -1
  395. package/packages/core/dist/scripts/job-fair-aggregator.test.js +0 -616
  396. package/packages/core/dist/scripts/job-fair-aggregator.test.js.map +0 -1
  397. package/pennyfarthing-dist/agents/handoff.md +0 -250
  398. package/pennyfarthing-dist/agents/sm-handoff.md +0 -152
  399. package/pennyfarthing-dist/scripts/core/handoff-marker.sh +0 -112
  400. package/pennyfarthing-dist/skills/pf-dev-patterns/SKILL.md +0 -461
  401. package/scripts/README.md +0 -41
@@ -0,0 +1,64 @@
1
+ """
2
+ Epic CLI - Click-based CLI for epic lifecycle management.
3
+
4
+ Usage:
5
+ pf epic [COMMAND] [ARGS]...
6
+
7
+ Commands:
8
+ start Start an epic - move to current sprint and generate context
9
+ close Close an epic - verify completion and archive
10
+ """
11
+
12
+ import click
13
+
14
+
15
+ @click.group()
16
+ def epic():
17
+ """Epic lifecycle management.
18
+
19
+ \b
20
+ Commands:
21
+ start - Start an epic (move to sprint + generate tech context)
22
+ close - Close an epic (verify completion + archive)
23
+ """
24
+ pass
25
+
26
+
27
+ @epic.command()
28
+ @click.argument("epic_id")
29
+ def start(epic_id: str):
30
+ """Start an epic - move to current sprint and generate tech context.
31
+
32
+ \b
33
+ Arguments:
34
+ EPIC_ID - Epic identifier (e.g., 79, epic-79)
35
+ """
36
+ # Normalize epic ID
37
+ if not epic_id.startswith("epic-"):
38
+ epic_id = f"epic-{epic_id}"
39
+
40
+ click.echo(f"Starting epic {epic_id}...")
41
+ click.echo("1. Checking epic location in sprint files...")
42
+ click.echo("2. Moving to current sprint if needed...")
43
+ click.echo("3. Generating tech context via SM agent...")
44
+ click.echo(f"\nTo complete setup, run: /sm with task epic-tech-context for {epic_id}")
45
+
46
+
47
+ @epic.command()
48
+ @click.argument("epic_id")
49
+ def close(epic_id: str):
50
+ """Close an epic - verify completion, update status, and archive.
51
+
52
+ \b
53
+ Arguments:
54
+ EPIC_ID - Epic identifier (e.g., 79, epic-79)
55
+ """
56
+ # Normalize epic ID
57
+ if not epic_id.startswith("epic-"):
58
+ epic_id = f"epic-{epic_id}"
59
+
60
+ click.echo(f"Closing epic {epic_id}...")
61
+ click.echo("1. Verifying all stories are done...")
62
+ click.echo("2. Updating epic status to done...")
63
+ click.echo("3. Archiving epic context...")
64
+ click.echo(f"\nUse pf sprint epic archive {epic_id} to complete the archival.")
@@ -0,0 +1 @@
1
+ """Gate file operations — validation, inspection, and authoring tools."""
@@ -0,0 +1,56 @@
1
+ """Gate CLI — gate file operations.
2
+
3
+ Usage:
4
+ pf gate validate <file> # Validate a gate file
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import click
10
+
11
+ from pennyfarthing_scripts.common.output import error, info, success
12
+
13
+
14
+ @click.group()
15
+ def gate():
16
+ """Gate file operations.
17
+
18
+ \b
19
+ Commands:
20
+ validate - Validate a gate file for schema, depth, and cycles
21
+ """
22
+ pass
23
+
24
+
25
+ @gate.command("validate")
26
+ @click.argument("file", type=click.Path(exists=True))
27
+ def gate_validate(file: str):
28
+ """Validate a gate file for schema, cycles, and depth.
29
+
30
+ Checks:
31
+ - Schema: <gate name="...">, <purpose>, <pass>, <fail>
32
+ - Depth: nesting does not exceed 3 levels
33
+ - Cycles: no duplicate gate names
34
+ - Completeness: all required elements non-empty
35
+
36
+ Reports ALL errors at once. On success, prints a structure summary.
37
+ """
38
+ from pennyfarthing_scripts.gate.validate import validate_gate_file
39
+
40
+ result = validate_gate_file(file)
41
+
42
+ if result.valid:
43
+ name = result.gate_name or "(unnamed)"
44
+ success(f"Gate '{name}' is valid")
45
+ info(f" Model: {result.model or 'haiku'}")
46
+ if result.child_count > 0:
47
+ info(f" Depth: {result.depth} ({result.child_count} nested gate(s))")
48
+ else:
49
+ info(f" Depth: {result.depth} (no nesting)")
50
+ info(f" Children: {result.child_count}")
51
+ else:
52
+ name = result.gate_name or file
53
+ error(f"Gate '{name}' has {len(result.errors)} error(s):")
54
+ for err in result.errors:
55
+ error(f" - {err}")
56
+ raise SystemExit(1)
@@ -0,0 +1,266 @@
1
+ """Gate file validation — schema, depth, cycles, and mandatory elements.
2
+
3
+ Validates a gate file for:
4
+ 1. Schema: <gate name="..."> with <purpose>, <pass>, <fail>
5
+ 2. Depth: max nesting depth of 3
6
+ 3. Cycles: no duplicate gate names
7
+ 4. Completeness: all required elements non-empty
8
+
9
+ Reports ALL errors at once (not fail-fast).
10
+
11
+ Story: 107-3 (Gate authoring guide and validation command)
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import re
17
+ from dataclasses import dataclass, field
18
+ from pathlib import Path
19
+
20
+ MAX_DEPTH = 3
21
+
22
+
23
+ @dataclass
24
+ class GateInfo:
25
+ """Parsed gate metadata."""
26
+
27
+ name: str
28
+ model: str = "haiku"
29
+ depth: int = 0
30
+ children: list[str] = field(default_factory=list)
31
+
32
+
33
+ @dataclass
34
+ class ValidationResult:
35
+ """Result of gate file validation."""
36
+
37
+ valid: bool
38
+ gate_name: str | None = None
39
+ model: str | None = None
40
+ depth: int = 0
41
+ child_count: int = 0
42
+ errors: list[str] = field(default_factory=list)
43
+
44
+
45
+ def validate_gate_file(path: str | Path) -> ValidationResult:
46
+ """Validate a gate file for schema compliance, depth, and cycles.
47
+
48
+ Args:
49
+ path: Path to the gate file.
50
+
51
+ Returns:
52
+ ValidationResult with all errors collected.
53
+ """
54
+ path = Path(path)
55
+ errors: list[str] = []
56
+
57
+ if not path.exists():
58
+ return ValidationResult(valid=False, errors=[f"File not found: {path}"])
59
+
60
+ content = path.read_text()
61
+ if not content.strip():
62
+ return ValidationResult(valid=False, errors=["File is empty"])
63
+
64
+ # Tokenize all gate-related tags
65
+ tokens = _tokenize(content)
66
+
67
+ if not tokens:
68
+ return ValidationResult(
69
+ valid=False,
70
+ errors=["No <gate> tag found in file"],
71
+ )
72
+
73
+ # Parse gate structure and collect errors
74
+ gate_names: list[str] = []
75
+ root_name: str | None = None
76
+ root_model: str | None = None
77
+ max_depth = 0
78
+ depth = 0
79
+
80
+ for token in tokens:
81
+ if token["type"] == "gate_open":
82
+ name = token.get("name")
83
+ model = token.get("model", "haiku")
84
+
85
+ if depth == 0:
86
+ root_name = name
87
+ root_model = model
88
+
89
+ if not name:
90
+ errors.append(
91
+ f"Gate element at depth {depth} missing required 'name' attribute"
92
+ )
93
+ else:
94
+ # Check for duplicate names (cycle indicator)
95
+ if name in gate_names:
96
+ errors.append(
97
+ f"Duplicate gate name '{name}' — names must be unique within a file"
98
+ )
99
+ gate_names.append(name)
100
+
101
+ depth += 1
102
+ if depth - 1 > max_depth:
103
+ max_depth = depth - 1
104
+
105
+ # Depth limit check (depth is 0-indexed, so depth-1 after increment)
106
+ if depth - 1 > MAX_DEPTH:
107
+ gate_label = name or "(unnamed)"
108
+ errors.append(
109
+ f"Gate depth limit exceeded: '{gate_label}' at depth {depth - 1} (max {MAX_DEPTH})"
110
+ )
111
+
112
+ elif token["type"] == "gate_close":
113
+ depth -= 1
114
+
115
+ # Validate required child elements for each gate
116
+ _validate_required_elements(content, gate_names, errors)
117
+
118
+ child_count = len(gate_names) - 1 if len(gate_names) > 1 else 0
119
+
120
+ return ValidationResult(
121
+ valid=len(errors) == 0,
122
+ gate_name=root_name,
123
+ model=root_model,
124
+ depth=max_depth,
125
+ child_count=child_count,
126
+ errors=errors,
127
+ )
128
+
129
+
130
+ def _tokenize(content: str) -> list[dict]:
131
+ """Tokenize gate-related XML tags from content."""
132
+ tokens: list[dict] = []
133
+
134
+ # Match opening gate tags with attributes
135
+ open_pattern = re.compile(r"<gate\b([^>]*)>", re.IGNORECASE)
136
+ close_pattern = re.compile(r"</gate\s*>", re.IGNORECASE)
137
+
138
+ # Build a list of all tags with their positions
139
+ events: list[tuple[int, dict]] = []
140
+
141
+ for m in open_pattern.finditer(content):
142
+ attrs = m.group(1)
143
+ name_match = re.search(r'name="([^"]*)"', attrs)
144
+ model_match = re.search(r'model="([^"]*)"', attrs)
145
+ events.append((m.start(), {
146
+ "type": "gate_open",
147
+ "name": name_match.group(1) if name_match else None,
148
+ "model": model_match.group(1) if model_match else "haiku",
149
+ }))
150
+
151
+ for m in close_pattern.finditer(content):
152
+ events.append((m.start(), {"type": "gate_close"}))
153
+
154
+ # Sort by position
155
+ events.sort(key=lambda e: e[0])
156
+ tokens = [e[1] for e in events]
157
+
158
+ return tokens
159
+
160
+
161
+ def _validate_required_elements(
162
+ content: str,
163
+ gate_names: list[str],
164
+ errors: list[str],
165
+ ) -> None:
166
+ """Validate that each gate has <purpose>, <pass>, and <fail> elements.
167
+
168
+ For each gate, checks that required child elements exist and are non-empty
169
+ within the gate's scope.
170
+ """
171
+ # For simple files with one gate, check globally
172
+ # For nested files, check per-gate scope
173
+ if len(gate_names) <= 1:
174
+ name = gate_names[0] if gate_names else "(unnamed)"
175
+ _check_required_for_gate(content, name, errors)
176
+ else:
177
+ # Split content by gate scopes and validate each
178
+ # Use a stack-based approach to extract each gate's direct content
179
+ _validate_nested_gates(content, errors)
180
+
181
+
182
+ def _check_required_for_gate(
183
+ scope_content: str,
184
+ gate_name: str,
185
+ errors: list[str],
186
+ ) -> None:
187
+ """Check that a gate scope contains non-empty <purpose>, <pass>, <fail>."""
188
+ for element in ("purpose", "pass", "fail"):
189
+ pattern = re.compile(
190
+ rf"<{element}>(.*?)</{element}>",
191
+ re.DOTALL | re.IGNORECASE,
192
+ )
193
+ match = pattern.search(scope_content)
194
+ if not match:
195
+ errors.append(f"Gate '{gate_name}' missing required <{element}> block")
196
+ elif not match.group(1).strip():
197
+ errors.append(f"Gate '{gate_name}' has empty <{element}> block")
198
+
199
+
200
+ def _validate_nested_gates(content: str, errors: list[str]) -> None:
201
+ """Validate required elements for each gate in a nested structure."""
202
+ # Extract each gate's scope using a stack-based parser
203
+ open_pattern = re.compile(r"<gate\b([^>]*)>", re.IGNORECASE)
204
+ close_pattern = re.compile(r"</gate\s*>", re.IGNORECASE)
205
+
206
+ events: list[tuple[int, str, str | None]] = []
207
+
208
+ for m in open_pattern.finditer(content):
209
+ name_match = re.search(r'name="([^"]*)"', m.group(1))
210
+ name = name_match.group(1) if name_match else None
211
+ events.append((m.start(), "open", name))
212
+
213
+ for m in close_pattern.finditer(content):
214
+ events.append((m.start(), "close", None))
215
+
216
+ events.sort(key=lambda e: e[0])
217
+
218
+ # Stack-based scope extraction
219
+ stack: list[tuple[str | None, int]] = [] # (name, start_pos)
220
+
221
+ for pos, event_type, name in events:
222
+ if event_type == "open":
223
+ stack.append((name, pos))
224
+ elif event_type == "close" and stack:
225
+ gate_name, start_pos = stack.pop()
226
+ # Extract this gate's direct content (between open and close)
227
+ gate_scope = content[start_pos:pos]
228
+ # Remove nested gate content to check only direct children
229
+ direct_content = _remove_nested_gates(gate_scope)
230
+ label = gate_name or "(unnamed)"
231
+ _check_required_for_gate(direct_content, label, errors)
232
+
233
+
234
+ def _remove_nested_gates(scope: str) -> str:
235
+ """Remove nested <gate>...</gate> blocks from scope content.
236
+
237
+ Keeps the outermost gate's direct content only.
238
+ """
239
+ depth = 0
240
+ result: list[str] = []
241
+ i = 0
242
+
243
+ open_pattern = re.compile(r"<gate\b[^>]*>", re.IGNORECASE)
244
+ close_pattern = re.compile(r"</gate\s*>", re.IGNORECASE)
245
+
246
+ while i < len(scope):
247
+ open_match = open_pattern.match(scope, i)
248
+ close_match = close_pattern.match(scope, i)
249
+
250
+ if open_match:
251
+ depth += 1
252
+ if depth <= 1:
253
+ # Keep the outermost gate open tag
254
+ result.append(open_match.group())
255
+ i = open_match.end()
256
+ elif close_match:
257
+ if depth <= 1:
258
+ result.append(close_match.group())
259
+ depth -= 1
260
+ i = close_match.end()
261
+ else:
262
+ if depth <= 1:
263
+ result.append(scope[i])
264
+ i += 1
265
+
266
+ return "".join(result)
File without changes
@@ -0,0 +1,100 @@
1
+ """
2
+ Git CLI - Click-based CLI for git repository operations.
3
+
4
+ Usage:
5
+ pf git [COMMAND] [ARGS]...
6
+
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
12
+ """
13
+
14
+ import click
15
+
16
+ from pennyfarthing_scripts.common.config import get_project_root
17
+
18
+
19
+ @click.group()
20
+ def git():
21
+ """Repository operations across all configured repos.
22
+
23
+ \b
24
+ 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
29
+ """
30
+ pass
31
+
32
+
33
+ @git.command()
34
+ @click.option("--brief", is_flag=True, help="One-line-per-repo summary")
35
+ def status(brief: bool):
36
+ """Check git status of all project repos.
37
+
38
+ Shows branch, uncommitted changes, and ahead/behind status for each repo.
39
+ """
40
+ import subprocess
41
+
42
+ root = get_project_root()
43
+ script = root / ".pennyfarthing" / "scripts" / "git" / "git-status-all.sh"
44
+
45
+ if not script.is_file():
46
+ click.echo("Error: git-status-all.sh not found", err=True)
47
+ raise SystemExit(1)
48
+
49
+ cmd = [str(script)]
50
+ if brief:
51
+ cmd.append("--brief")
52
+
53
+ result = subprocess.run(cmd, cwd=str(root))
54
+ raise SystemExit(result.returncode)
55
+
56
+
57
+ @git.command()
58
+ def cleanup():
59
+ """Organize changes into proper commits and branches.
60
+
61
+ Starts the git-cleanup stepped workflow via BikeLane.
62
+ Equivalent to: /pf-workflow start git-cleanup
63
+ """
64
+ click.echo("Starting git-cleanup workflow...")
65
+ click.echo("Run: /pf-workflow start git-cleanup")
66
+ click.echo("Or: pf workflow start git-cleanup")
67
+
68
+
69
+ @git.command()
70
+ @click.argument("story_id")
71
+ def branches(story_id: str):
72
+ """Create feature branches in both repos from a story.
73
+
74
+ \b
75
+ Arguments:
76
+ STORY_ID - The story ID to create branches for (e.g., 86-3)
77
+ """
78
+ import subprocess
79
+
80
+ root = get_project_root()
81
+ script = root / ".pennyfarthing" / "scripts" / "git" / "create-branches.sh"
82
+
83
+ if not script.is_file():
84
+ click.echo("Error: create-branches.sh not found", err=True)
85
+ raise SystemExit(1)
86
+
87
+ result = subprocess.run([str(script), story_id], cwd=str(root))
88
+ raise SystemExit(result.returncode)
89
+
90
+
91
+ @git.command()
92
+ def release():
93
+ """Interactive release with verification gates.
94
+
95
+ Starts the release stepped workflow via BikeLane.
96
+ Equivalent to: /pf-workflow start release
97
+ """
98
+ click.echo("Starting release workflow...")
99
+ click.echo("Run: /pf-workflow start release")
100
+ click.echo("Or: pf workflow start release")
@@ -0,0 +1 @@
1
+ """Handoff CLI — Phase gate resolution and atomic session transitions."""
@@ -0,0 +1,120 @@
1
+ """Handoff CLI — Phase gate resolution, session transitions, and marker generation.
2
+
3
+ Usage:
4
+ pf handoff resolve-gate STORY_ID WORKFLOW PHASE
5
+ pf handoff complete-phase STORY_ID WORKFLOW FROM_PHASE TO_PHASE GATE_TYPE
6
+ pf handoff marker NEXT_AGENT [--error MESSAGE]
7
+
8
+ Stories: 105-1, 105-4 (Script-First Handoff)
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import click
14
+
15
+
16
+ @click.group()
17
+ def handoff():
18
+ """Phase gate resolution and session transitions.
19
+
20
+ \b
21
+ Commands:
22
+ resolve-gate - Resolve gate for current phase
23
+ complete-phase - Complete phase transition atomically
24
+ marker - Generate AGENT_COMMAND handoff marker
25
+ """
26
+ pass
27
+
28
+
29
+ @handoff.command("resolve-gate")
30
+ @click.argument("story_id")
31
+ @click.argument("workflow")
32
+ @click.argument("phase")
33
+ def resolve_gate_cmd(story_id: str, workflow: str, phase: str):
34
+ """Resolve the gate for the current workflow phase.
35
+
36
+ Reads workflow YAML, checks assessment, returns RESOLVE_RESULT.
37
+
38
+ \b
39
+ Arguments:
40
+ STORY_ID - Story identifier (e.g., 105-1)
41
+ WORKFLOW - Workflow name (e.g., tdd, trivial, patch)
42
+ PHASE - Current phase name (e.g., green, implement, fix)
43
+ """
44
+ from pennyfarthing_scripts.handoff.resolve_gate import resolve_gate
45
+
46
+ result = resolve_gate(story_id, workflow, phase)
47
+
48
+ import yaml
49
+
50
+ click.echo(yaml.dump({"RESOLVE_RESULT": result}, default_flow_style=False).rstrip())
51
+
52
+ if result.get("status") == "blocked":
53
+ raise SystemExit(1)
54
+
55
+
56
+ @handoff.command("complete-phase")
57
+ @click.argument("story_id")
58
+ @click.argument("workflow")
59
+ @click.argument("from_phase")
60
+ @click.argument("to_phase")
61
+ @click.argument("gate_type")
62
+ def complete_phase_cmd(
63
+ story_id: str,
64
+ workflow: str,
65
+ from_phase: str,
66
+ to_phase: str,
67
+ gate_type: str,
68
+ ):
69
+ """Complete a phase transition with atomic session update.
70
+
71
+ Updates session file: phase line, timestamps, history tables.
72
+
73
+ \b
74
+ Arguments:
75
+ STORY_ID - Story identifier (e.g., 105-1)
76
+ WORKFLOW - Workflow name (e.g., tdd, trivial)
77
+ FROM_PHASE - Phase being completed (e.g., green)
78
+ TO_PHASE - Phase being entered (e.g., review)
79
+ GATE_TYPE - Gate type that was passed (e.g., tests_pass)
80
+ """
81
+ from pennyfarthing_scripts.handoff.complete_phase import complete_phase
82
+
83
+ result = complete_phase(story_id, workflow, from_phase, to_phase, gate_type)
84
+
85
+ import yaml
86
+
87
+ click.echo(yaml.dump({"COMPLETE_RESULT": result}, default_flow_style=False).rstrip())
88
+
89
+ if result.get("status") == "error":
90
+ raise SystemExit(1)
91
+
92
+
93
+ @handoff.command("marker")
94
+ @click.argument("next_agent", required=False, default=None)
95
+ @click.option("--error", "error_msg", default=None, help="Generate error marker")
96
+ def marker_cmd(next_agent: str | None, error_msg: str | None):
97
+ """Generate AGENT_COMMAND handoff marker block.
98
+
99
+ Environment-aware marker generation. Detects Cyclist, relay mode,
100
+ and context usage to choose the appropriate marker type.
101
+
102
+ \b
103
+ Arguments:
104
+ NEXT_AGENT - Agent to hand off to (e.g., dev, tea, reviewer)
105
+
106
+ \b
107
+ Options:
108
+ --error MSG - Generate an error marker instead of a handoff
109
+ """
110
+ from pennyfarthing_scripts.handoff.marker import generate_marker
111
+
112
+ if not next_agent and not error_msg:
113
+ raise click.UsageError(
114
+ "Provide NEXT_AGENT or --error MESSAGE.\n\n"
115
+ "Examples:\n"
116
+ " pf handoff marker dev\n"
117
+ " pf handoff marker --error 'Tests failing'"
118
+ )
119
+
120
+ click.echo(generate_marker(next_agent, error=error_msg))