@pennyfarthing/core 11.0.0 → 11.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (401) hide show
  1. package/README.md +81 -23
  2. package/package.json +1 -1
  3. package/packages/core/dist/cli/utils/010-detect-remove-old-packages.test.d.ts +20 -0
  4. package/packages/core/dist/cli/utils/010-detect-remove-old-packages.test.d.ts.map +1 -0
  5. package/packages/core/dist/cli/utils/010-detect-remove-old-packages.test.js +278 -0
  6. package/packages/core/dist/cli/utils/010-detect-remove-old-packages.test.js.map +1 -0
  7. package/packages/core/dist/cli/utils/constants.d.ts +8 -2
  8. package/packages/core/dist/cli/utils/constants.d.ts.map +1 -1
  9. package/packages/core/dist/cli/utils/constants.js +4 -1
  10. package/packages/core/dist/cli/utils/constants.js.map +1 -1
  11. package/packages/core/dist/cli/utils/constants.test.d.ts +10 -0
  12. package/packages/core/dist/cli/utils/constants.test.d.ts.map +1 -0
  13. package/packages/core/dist/cli/utils/constants.test.js +38 -0
  14. package/packages/core/dist/cli/utils/constants.test.js.map +1 -0
  15. package/packages/core/dist/consultation/consultation-protocol.d.ts +139 -0
  16. package/packages/core/dist/consultation/consultation-protocol.d.ts.map +1 -0
  17. package/packages/core/dist/consultation/consultation-protocol.js +178 -0
  18. package/packages/core/dist/consultation/consultation-protocol.js.map +1 -0
  19. package/packages/core/dist/consultation/consultation-protocol.test.d.ts +20 -0
  20. package/packages/core/dist/consultation/consultation-protocol.test.d.ts.map +1 -0
  21. package/packages/core/dist/consultation/consultation-protocol.test.js +474 -0
  22. package/packages/core/dist/consultation/consultation-protocol.test.js.map +1 -0
  23. package/packages/core/dist/consultation/dialogue-manager.d.ts +75 -0
  24. package/packages/core/dist/consultation/dialogue-manager.d.ts.map +1 -0
  25. package/packages/core/dist/consultation/dialogue-manager.js +334 -0
  26. package/packages/core/dist/consultation/dialogue-manager.js.map +1 -0
  27. package/packages/core/dist/consultation/dialogue-manager.test.d.ts +19 -0
  28. package/packages/core/dist/consultation/dialogue-manager.test.d.ts.map +1 -0
  29. package/packages/core/dist/consultation/dialogue-manager.test.js +444 -0
  30. package/packages/core/dist/consultation/dialogue-manager.test.js.map +1 -0
  31. package/packages/core/dist/public/js/react/react.js +3 -3
  32. package/packages/core/dist/scripts/theme-detail.test.d.ts +10 -0
  33. package/packages/core/dist/scripts/theme-detail.test.js +199 -0
  34. package/packages/core/dist/server/api/git.d.ts +13 -1
  35. package/packages/core/dist/server/api/git.d.ts.map +1 -1
  36. package/packages/core/dist/server/api/git.js +53 -34
  37. package/packages/core/dist/server/api/git.js.map +1 -1
  38. package/packages/core/dist/server/api/health-score.d.ts.map +1 -1
  39. package/packages/core/dist/server/api/health-score.js +25 -1
  40. package/packages/core/dist/server/api/health-score.js.map +1 -1
  41. package/packages/core/dist/server/api/settings.d.ts.map +1 -1
  42. package/packages/core/dist/server/api/settings.js +63 -1
  43. package/packages/core/dist/server/api/settings.js.map +1 -1
  44. package/packages/core/dist/server/api/theme-agents.d.ts.map +1 -1
  45. package/packages/core/dist/server/api/theme-agents.js +61 -0
  46. package/packages/core/dist/server/api/theme-agents.js.map +1 -1
  47. package/packages/core/dist/server/server.d.ts.map +1 -1
  48. package/packages/core/dist/server/server.js +17 -12
  49. package/packages/core/dist/server/server.js.map +1 -1
  50. package/packages/core/dist/shared/skill-search.test.js +2 -2
  51. package/packages/core/dist/workflow/gate-file-validation.d.ts +49 -0
  52. package/packages/core/dist/workflow/gate-file-validation.d.ts.map +1 -0
  53. package/packages/core/dist/workflow/gate-file-validation.js +157 -0
  54. package/packages/core/dist/workflow/gate-file-validation.js.map +1 -0
  55. package/packages/core/dist/workflow/gate-file-validation.test.d.ts +19 -0
  56. package/packages/core/dist/workflow/gate-file-validation.test.d.ts.map +1 -0
  57. package/packages/core/dist/workflow/gate-file-validation.test.js +536 -0
  58. package/packages/core/dist/workflow/gate-file-validation.test.js.map +1 -0
  59. package/packages/core/dist/workflow/gate-schema-validation.test.d.ts +14 -0
  60. package/packages/core/dist/workflow/gate-schema-validation.test.d.ts.map +1 -0
  61. package/packages/core/dist/workflow/gate-schema-validation.test.js +339 -0
  62. package/packages/core/dist/workflow/gate-schema-validation.test.js.map +1 -0
  63. package/packages/core/dist/workflow/handoff.js +2 -2
  64. package/packages/core/dist/workflow/handoff.js.map +1 -1
  65. package/packages/core/dist/workflow/handoff.test.js +16 -0
  66. package/packages/core/dist/workflow/handoff.test.js.map +1 -1
  67. package/packages/core/dist/workflow/workflow-schema.d.ts +4 -2
  68. package/packages/core/dist/workflow/workflow-schema.d.ts.map +1 -1
  69. package/packages/core/dist/workflow/workflow-schema.js +43 -8
  70. package/packages/core/dist/workflow/workflow-schema.js.map +1 -1
  71. package/pennyfarthing-dist/agents/README.md +6 -14
  72. package/pennyfarthing-dist/agents/architect.md +43 -29
  73. package/pennyfarthing-dist/agents/ba.md +30 -29
  74. package/pennyfarthing-dist/agents/dev.md +32 -43
  75. package/pennyfarthing-dist/agents/devops.md +57 -21
  76. package/pennyfarthing-dist/agents/orchestrator.md +3 -10
  77. package/pennyfarthing-dist/agents/pm.md +45 -31
  78. package/pennyfarthing-dist/agents/reviewer.md +20 -66
  79. package/pennyfarthing-dist/agents/sm-setup.md +2 -2
  80. package/pennyfarthing-dist/agents/sm.md +8 -30
  81. package/pennyfarthing-dist/agents/tea.md +25 -41
  82. package/pennyfarthing-dist/agents/tech-writer.md +33 -90
  83. package/pennyfarthing-dist/agents/ux-designer.md +39 -39
  84. package/pennyfarthing-dist/commands/benchmark-control.md +8 -64
  85. package/pennyfarthing-dist/commands/benchmark.md +8 -480
  86. package/pennyfarthing-dist/commands/job-fair.md +8 -97
  87. package/pennyfarthing-dist/commands/pf-benchmark-control.md +70 -0
  88. package/pennyfarthing-dist/commands/pf-benchmark.md +486 -0
  89. package/pennyfarthing-dist/commands/pf-chore.md +4 -4
  90. package/pennyfarthing-dist/commands/pf-ci.md +40 -0
  91. package/pennyfarthing-dist/commands/pf-close-epic.md +9 -27
  92. package/pennyfarthing-dist/commands/pf-continue-session.md +9 -213
  93. package/pennyfarthing-dist/commands/pf-create-branches-from-story.md +11 -353
  94. package/pennyfarthing-dist/commands/pf-docs.md +28 -0
  95. package/pennyfarthing-dist/commands/pf-epic.md +67 -0
  96. package/pennyfarthing-dist/commands/pf-git-cleanup.md +11 -52
  97. package/pennyfarthing-dist/commands/pf-git.md +75 -0
  98. package/pennyfarthing-dist/commands/pf-help.md +110 -128
  99. package/pennyfarthing-dist/commands/pf-job-fair.md +102 -0
  100. package/pennyfarthing-dist/commands/pf-new-work.md +9 -18
  101. package/pennyfarthing-dist/commands/pf-parallel-work.md +6 -66
  102. package/pennyfarthing-dist/commands/pf-release.md +11 -76
  103. package/pennyfarthing-dist/commands/pf-repo-status.md +11 -44
  104. package/pennyfarthing-dist/commands/pf-run-ci.md +8 -111
  105. package/pennyfarthing-dist/commands/pf-session.md +51 -0
  106. package/pennyfarthing-dist/commands/pf-solo.md +447 -0
  107. package/pennyfarthing-dist/commands/pf-sprint-planning.md +8 -104
  108. package/pennyfarthing-dist/commands/pf-standalone.md +1 -1
  109. package/pennyfarthing-dist/commands/pf-start-epic.md +9 -163
  110. package/pennyfarthing-dist/commands/pf-sync-epic-to-jira.md +8 -179
  111. package/pennyfarthing-dist/commands/pf-sync-work-with-sprint.md +8 -368
  112. package/pennyfarthing-dist/commands/pf-update-domain-docs.md +8 -78
  113. package/pennyfarthing-dist/commands/solo.md +8 -442
  114. package/pennyfarthing-dist/guides/agent-behavior.md +13 -13
  115. package/pennyfarthing-dist/guides/agent-coordination.md +7 -7
  116. package/pennyfarthing-dist/guides/agent-tag-taxonomy.md +6 -5
  117. package/pennyfarthing-dist/guides/bikerack.md +128 -0
  118. package/pennyfarthing-dist/guides/brownfield-tools.md +133 -0
  119. package/pennyfarthing-dist/guides/command-tag-taxonomy.md +2 -2
  120. package/pennyfarthing-dist/guides/gate-schema.md +227 -0
  121. package/pennyfarthing-dist/guides/gates.md +120 -0
  122. package/pennyfarthing-dist/guides/handoff-cli.md +116 -0
  123. package/pennyfarthing-dist/guides/hooks.md +86 -4
  124. package/pennyfarthing-dist/guides/output-styles.md +65 -0
  125. package/pennyfarthing-dist/guides/patterns/approval-gates-pattern.md +5 -5
  126. package/pennyfarthing-dist/guides/patterns/tdd-flow-pattern.md +4 -4
  127. package/pennyfarthing-dist/guides/prompt-patterns.md +5 -5
  128. package/pennyfarthing-dist/guides/reflector.md +4 -4
  129. package/pennyfarthing-dist/guides/session-artifacts.md +1 -1
  130. package/pennyfarthing-dist/guides/skill-schema.md +1 -1
  131. package/pennyfarthing-dist/guides/tandem-protocol.md +13 -1
  132. package/pennyfarthing-dist/guides/worktree-mode.md +3 -3
  133. package/pennyfarthing-dist/guides/xml-tags.md +5 -4
  134. package/pennyfarthing-dist/personas/themes/hogans-heroes.yaml +11 -22
  135. package/pennyfarthing-dist/personas/themes/stephen-king.yaml +13 -24
  136. package/pennyfarthing-dist/scripts/core/dialogue-manager.sh +322 -0
  137. package/pennyfarthing-dist/scripts/core/phase-check-start.sh +1 -1
  138. package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +19 -14
  139. package/pennyfarthing-dist/scripts/portraits/generate-portraits.py +191 -57
  140. package/pennyfarthing-dist/scripts/portraits/generate-portraits.sh +26 -10
  141. package/pennyfarthing-dist/skills/pf-changelog/SKILL.md +4 -4
  142. package/pennyfarthing-dist/skills/pf-sprint/skill.md +1 -1
  143. package/pennyfarthing-dist/skills/skill-registry.schema.json +4 -0
  144. package/pennyfarthing-dist/skills/skill-registry.yaml +5 -0
  145. package/pennyfarthing-dist/workflows/2party-tdd.yaml +11 -0
  146. package/pennyfarthing-dist/workflows/agent-docs.yaml +2 -0
  147. package/pennyfarthing-dist/workflows/bdd-tandem.yaml +4 -0
  148. package/pennyfarthing-dist/workflows/bdd.yaml +4 -0
  149. package/pennyfarthing-dist/workflows/git-cleanup/steps/step-05-complete.md +1 -1
  150. package/pennyfarthing-dist/workflows/tdd-tandem.yaml +3 -0
  151. package/pennyfarthing-dist/workflows/tdd.yaml +3 -0
  152. package/pennyfarthing-dist/workflows/trivial.yaml +2 -0
  153. package/pennyfarthing_scripts/__pycache__/__init__.cpython-314.pyc +0 -0
  154. package/pennyfarthing_scripts/__pycache__/bellmode_hook.cpython-314.pyc +0 -0
  155. package/pennyfarthing_scripts/__pycache__/cli.cpython-314.pyc +0 -0
  156. package/pennyfarthing_scripts/__pycache__/config.cpython-314.pyc +0 -0
  157. package/pennyfarthing_scripts/__pycache__/context.cpython-314.pyc +0 -0
  158. package/pennyfarthing_scripts/__pycache__/hooks.cpython-314.pyc +0 -0
  159. package/pennyfarthing_scripts/__pycache__/jira_bidirectional_sync.cpython-314.pyc +0 -0
  160. package/pennyfarthing_scripts/__pycache__/jira_epic_creation.cpython-314.pyc +0 -0
  161. package/pennyfarthing_scripts/__pycache__/jira_sync.cpython-314.pyc +0 -0
  162. package/pennyfarthing_scripts/__pycache__/jira_sync_story.cpython-314.pyc +0 -0
  163. package/pennyfarthing_scripts/__pycache__/output.cpython-314.pyc +0 -0
  164. package/pennyfarthing_scripts/__pycache__/patch_mode.cpython-314.pyc +0 -0
  165. package/pennyfarthing_scripts/__pycache__/pretooluse_hook.cpython-314.pyc +0 -0
  166. package/pennyfarthing_scripts/__pycache__/schema_validation_hook.cpython-314.pyc +0 -0
  167. package/pennyfarthing_scripts/__pycache__/session_start_hook.cpython-314.pyc +0 -0
  168. package/pennyfarthing_scripts/__pycache__/workflow.cpython-314.pyc +0 -0
  169. package/pennyfarthing_scripts/bc/__pycache__/__init__.cpython-314.pyc +0 -0
  170. package/pennyfarthing_scripts/bc/__pycache__/cli.cpython-314.pyc +0 -0
  171. package/pennyfarthing_scripts/bc/__pycache__/focus.cpython-314.pyc +0 -0
  172. package/pennyfarthing_scripts/bikerack/__pycache__/__init__.cpython-314.pyc +0 -0
  173. package/pennyfarthing_scripts/bikerack/__pycache__/__main__.cpython-314.pyc +0 -0
  174. package/pennyfarthing_scripts/bikerack/__pycache__/background_panel.cpython-314.pyc +0 -0
  175. package/pennyfarthing_scripts/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
  176. package/pennyfarthing_scripts/bikerack/__pycache__/changed_panel.cpython-314.pyc +0 -0
  177. package/pennyfarthing_scripts/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
  178. package/pennyfarthing_scripts/bikerack/__pycache__/debug_panel.cpython-314.pyc +0 -0
  179. package/pennyfarthing_scripts/bikerack/__pycache__/diffs_panel.cpython-314.pyc +0 -0
  180. package/pennyfarthing_scripts/bikerack/__pycache__/git_panel.cpython-314.pyc +0 -0
  181. package/pennyfarthing_scripts/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
  182. package/pennyfarthing_scripts/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
  183. package/pennyfarthing_scripts/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
  184. package/pennyfarthing_scripts/bikerack/__pycache__/ws_client.cpython-314.pyc +0 -0
  185. package/pennyfarthing_scripts/bikerack/cli.py +10 -11
  186. package/pennyfarthing_scripts/bikerack/debug_panel.py +218 -0
  187. package/pennyfarthing_scripts/bikerack/diffs_panel.py +203 -27
  188. package/pennyfarthing_scripts/brownfield/__pycache__/__init__.cpython-314.pyc +0 -0
  189. package/pennyfarthing_scripts/brownfield/__pycache__/__main__.cpython-314.pyc +0 -0
  190. package/pennyfarthing_scripts/brownfield/__pycache__/cli.cpython-314.pyc +0 -0
  191. package/pennyfarthing_scripts/brownfield/__pycache__/discover.cpython-314.pyc +0 -0
  192. package/pennyfarthing_scripts/cli.py +114 -0
  193. package/pennyfarthing_scripts/codemarkers/__pycache__/__init__.cpython-314.pyc +0 -0
  194. package/pennyfarthing_scripts/codemarkers/__pycache__/__main__.cpython-314.pyc +0 -0
  195. package/pennyfarthing_scripts/codemarkers/__pycache__/analyze.cpython-314.pyc +0 -0
  196. package/pennyfarthing_scripts/codemarkers/__pycache__/cli.cpython-314.pyc +0 -0
  197. package/pennyfarthing_scripts/codemarkers/__pycache__/formatters.cpython-314.pyc +0 -0
  198. package/pennyfarthing_scripts/codemarkers/__pycache__/models.cpython-314.pyc +0 -0
  199. package/pennyfarthing_scripts/common/__pycache__/__init__.cpython-314.pyc +0 -0
  200. package/pennyfarthing_scripts/common/__pycache__/config.cpython-314.pyc +0 -0
  201. package/pennyfarthing_scripts/common/__pycache__/output.cpython-314.pyc +0 -0
  202. package/pennyfarthing_scripts/common/__pycache__/themes.cpython-314.pyc +0 -0
  203. package/pennyfarthing_scripts/complexity/__pycache__/__init__.cpython-314.pyc +0 -0
  204. package/pennyfarthing_scripts/complexity/__pycache__/__main__.cpython-314.pyc +0 -0
  205. package/pennyfarthing_scripts/complexity/__pycache__/analyze.cpython-314.pyc +0 -0
  206. package/pennyfarthing_scripts/complexity/__pycache__/cli.cpython-314.pyc +0 -0
  207. package/pennyfarthing_scripts/complexity/__pycache__/formatters.cpython-314.pyc +0 -0
  208. package/pennyfarthing_scripts/complexity/__pycache__/models.cpython-314.pyc +0 -0
  209. package/pennyfarthing_scripts/deadcode/__pycache__/__init__.cpython-314.pyc +0 -0
  210. package/pennyfarthing_scripts/deadcode/__pycache__/__main__.cpython-314.pyc +0 -0
  211. package/pennyfarthing_scripts/deadcode/__pycache__/analyze.cpython-314.pyc +0 -0
  212. package/pennyfarthing_scripts/deadcode/__pycache__/cli.cpython-314.pyc +0 -0
  213. package/pennyfarthing_scripts/deadcode/__pycache__/formatters.cpython-314.pyc +0 -0
  214. package/pennyfarthing_scripts/deadcode/__pycache__/models.cpython-314.pyc +0 -0
  215. package/pennyfarthing_scripts/dependencies/__pycache__/__init__.cpython-314.pyc +0 -0
  216. package/pennyfarthing_scripts/dependencies/__pycache__/__main__.cpython-314.pyc +0 -0
  217. package/pennyfarthing_scripts/dependencies/__pycache__/analyze.cpython-314.pyc +0 -0
  218. package/pennyfarthing_scripts/dependencies/__pycache__/cli.cpython-314.pyc +0 -0
  219. package/pennyfarthing_scripts/dependencies/__pycache__/formatters.cpython-314.pyc +0 -0
  220. package/pennyfarthing_scripts/dependencies/__pycache__/models.cpython-314.pyc +0 -0
  221. package/pennyfarthing_scripts/epic/__init__.py +0 -0
  222. package/pennyfarthing_scripts/epic/cli.py +64 -0
  223. package/pennyfarthing_scripts/gate/__init__.py +1 -0
  224. package/pennyfarthing_scripts/gate/__pycache__/__init__.cpython-314.pyc +0 -0
  225. package/pennyfarthing_scripts/gate/__pycache__/cli.cpython-314.pyc +0 -0
  226. package/pennyfarthing_scripts/gate/__pycache__/validate.cpython-314.pyc +0 -0
  227. package/pennyfarthing_scripts/gate/cli.py +56 -0
  228. package/pennyfarthing_scripts/gate/validate.py +266 -0
  229. package/pennyfarthing_scripts/git/__pycache__/__init__.cpython-314.pyc +0 -0
  230. package/pennyfarthing_scripts/git/__pycache__/create_branches.cpython-314.pyc +0 -0
  231. package/pennyfarthing_scripts/git/__pycache__/status_all.cpython-314.pyc +0 -0
  232. package/pennyfarthing_scripts/git_group/__init__.py +0 -0
  233. package/pennyfarthing_scripts/git_group/cli.py +100 -0
  234. package/pennyfarthing_scripts/handoff/__init__.py +1 -0
  235. package/pennyfarthing_scripts/handoff/__pycache__/__init__.cpython-314.pyc +0 -0
  236. package/pennyfarthing_scripts/handoff/__pycache__/cli.cpython-314.pyc +0 -0
  237. package/pennyfarthing_scripts/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
  238. package/pennyfarthing_scripts/handoff/__pycache__/gate_file.cpython-314.pyc +0 -0
  239. package/pennyfarthing_scripts/handoff/__pycache__/gate_runner.cpython-314.pyc +0 -0
  240. package/pennyfarthing_scripts/handoff/__pycache__/marker.cpython-314.pyc +0 -0
  241. package/pennyfarthing_scripts/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
  242. package/pennyfarthing_scripts/handoff/cli.py +120 -0
  243. package/pennyfarthing_scripts/handoff/complete_phase.py +155 -0
  244. package/pennyfarthing_scripts/handoff/gate_file.py +105 -0
  245. package/pennyfarthing_scripts/handoff/gate_runner.py +152 -0
  246. package/pennyfarthing_scripts/handoff/marker.py +109 -0
  247. package/pennyfarthing_scripts/handoff/resolve_gate.py +152 -0
  248. package/pennyfarthing_scripts/healthscore/__pycache__/__init__.cpython-314.pyc +0 -0
  249. package/pennyfarthing_scripts/healthscore/__pycache__/__main__.cpython-314.pyc +0 -0
  250. package/pennyfarthing_scripts/healthscore/__pycache__/analyze.cpython-314.pyc +0 -0
  251. package/pennyfarthing_scripts/healthscore/__pycache__/cli.cpython-314.pyc +0 -0
  252. package/pennyfarthing_scripts/healthscore/__pycache__/formatters.cpython-314.pyc +0 -0
  253. package/pennyfarthing_scripts/healthscore/__pycache__/models.cpython-314.pyc +0 -0
  254. package/pennyfarthing_scripts/hotspots/__pycache__/__init__.cpython-314.pyc +0 -0
  255. package/pennyfarthing_scripts/hotspots/__pycache__/__main__.cpython-314.pyc +0 -0
  256. package/pennyfarthing_scripts/hotspots/__pycache__/analyze.cpython-314.pyc +0 -0
  257. package/pennyfarthing_scripts/hotspots/__pycache__/cli.cpython-314.pyc +0 -0
  258. package/pennyfarthing_scripts/hotspots/__pycache__/formatters.cpython-314.pyc +0 -0
  259. package/pennyfarthing_scripts/hotspots/__pycache__/models.cpython-314.pyc +0 -0
  260. package/pennyfarthing_scripts/jira/__pycache__/__init__.cpython-314.pyc +0 -0
  261. package/pennyfarthing_scripts/jira/__pycache__/__main__.cpython-314.pyc +0 -0
  262. package/pennyfarthing_scripts/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
  263. package/pennyfarthing_scripts/jira/__pycache__/claim.cpython-314.pyc +0 -0
  264. package/pennyfarthing_scripts/jira/__pycache__/cli.cpython-314.pyc +0 -0
  265. package/pennyfarthing_scripts/jira/__pycache__/client.cpython-314.pyc +0 -0
  266. package/pennyfarthing_scripts/jira/__pycache__/create.cpython-314.pyc +0 -0
  267. package/pennyfarthing_scripts/jira/__pycache__/epic.cpython-314.pyc +0 -0
  268. package/pennyfarthing_scripts/jira/__pycache__/operations.cpython-314.pyc +0 -0
  269. package/pennyfarthing_scripts/jira/__pycache__/reconcile.cpython-314.pyc +0 -0
  270. package/pennyfarthing_scripts/jira/__pycache__/story.cpython-314.pyc +0 -0
  271. package/pennyfarthing_scripts/jira/__pycache__/sync.cpython-314.pyc +0 -0
  272. package/pennyfarthing_scripts/launch/__pycache__/__init__.cpython-314.pyc +0 -0
  273. package/pennyfarthing_scripts/launch/__pycache__/cli.cpython-314.pyc +0 -0
  274. package/pennyfarthing_scripts/migration/__pycache__/__init__.cpython-314.pyc +0 -0
  275. package/pennyfarthing_scripts/migration/__pycache__/session.cpython-314.pyc +0 -0
  276. package/pennyfarthing_scripts/migration/__pycache__/skill.cpython-314.pyc +0 -0
  277. package/pennyfarthing_scripts/migration/__pycache__/step.cpython-314.pyc +0 -0
  278. package/pennyfarthing_scripts/migration/__pycache__/validate.cpython-314.pyc +0 -0
  279. package/pennyfarthing_scripts/preflight/__pycache__/__init__.cpython-314.pyc +0 -0
  280. package/pennyfarthing_scripts/preflight/__pycache__/__main__.cpython-314.pyc +0 -0
  281. package/pennyfarthing_scripts/preflight/__pycache__/cli.cpython-314.pyc +0 -0
  282. package/pennyfarthing_scripts/preflight/__pycache__/finish.cpython-314.pyc +0 -0
  283. package/pennyfarthing_scripts/prime/__pycache__/__init__.cpython-314.pyc +0 -0
  284. package/pennyfarthing_scripts/prime/__pycache__/cli.cpython-314.pyc +0 -0
  285. package/pennyfarthing_scripts/prime/__pycache__/loader.cpython-314.pyc +0 -0
  286. package/pennyfarthing_scripts/prime/__pycache__/models.cpython-314.pyc +0 -0
  287. package/pennyfarthing_scripts/prime/__pycache__/persona.cpython-314.pyc +0 -0
  288. package/pennyfarthing_scripts/prime/__pycache__/session.cpython-314.pyc +0 -0
  289. package/pennyfarthing_scripts/prime/__pycache__/tiers.cpython-314.pyc +0 -0
  290. package/pennyfarthing_scripts/prime/__pycache__/version_sentinel.cpython-314.pyc +0 -0
  291. package/pennyfarthing_scripts/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  292. package/pennyfarthing_scripts/prime/workflow.py +39 -0
  293. package/pennyfarthing_scripts/session/__init__.py +0 -0
  294. package/pennyfarthing_scripts/session/cli.py +87 -0
  295. package/pennyfarthing_scripts/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
  296. package/pennyfarthing_scripts/sprint/__pycache__/__main__.cpython-314.pyc +0 -0
  297. package/pennyfarthing_scripts/sprint/__pycache__/archive.cpython-314.pyc +0 -0
  298. package/pennyfarthing_scripts/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
  299. package/pennyfarthing_scripts/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  300. package/pennyfarthing_scripts/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
  301. package/pennyfarthing_scripts/sprint/__pycache__/epic_update.cpython-314.pyc +0 -0
  302. package/pennyfarthing_scripts/sprint/__pycache__/import_epic.cpython-314.pyc +0 -0
  303. package/pennyfarthing_scripts/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  304. package/pennyfarthing_scripts/sprint/__pycache__/status.cpython-314.pyc +0 -0
  305. package/pennyfarthing_scripts/sprint/__pycache__/story_add.cpython-314.pyc +0 -0
  306. package/pennyfarthing_scripts/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
  307. package/pennyfarthing_scripts/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
  308. package/pennyfarthing_scripts/sprint/__pycache__/validate_cmd.cpython-314.pyc +0 -0
  309. package/pennyfarthing_scripts/sprint/__pycache__/validator.cpython-314.pyc +0 -0
  310. package/pennyfarthing_scripts/sprint/__pycache__/work.cpython-314.pyc +0 -0
  311. package/pennyfarthing_scripts/sprint/__pycache__/yaml_io.cpython-314.pyc +0 -0
  312. package/pennyfarthing_scripts/sprint/story_finish.py +14 -0
  313. package/pennyfarthing_scripts/story/__pycache__/__init__.cpython-314.pyc +0 -0
  314. package/pennyfarthing_scripts/story/__pycache__/__main__.cpython-314.pyc +0 -0
  315. package/pennyfarthing_scripts/story/__pycache__/cli.cpython-314.pyc +0 -0
  316. package/pennyfarthing_scripts/story/__pycache__/create.cpython-314.pyc +0 -0
  317. package/pennyfarthing_scripts/story/__pycache__/size.cpython-314.pyc +0 -0
  318. package/pennyfarthing_scripts/story/__pycache__/template.cpython-314.pyc +0 -0
  319. package/pennyfarthing_scripts/tests/__pycache__/__init__.cpython-314.pyc +0 -0
  320. package/pennyfarthing_scripts/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
  321. package/pennyfarthing_scripts/tests/__pycache__/test_108_2_remove_handoff_fallback.cpython-314-pytest-9.0.2.pyc +0 -0
  322. package/pennyfarthing_scripts/tests/__pycache__/test_archive_epic.cpython-314-pytest-9.0.2.pyc +0 -0
  323. package/pennyfarthing_scripts/tests/__pycache__/test_bc.cpython-314-pytest-9.0.2.pyc +0 -0
  324. package/pennyfarthing_scripts/tests/__pycache__/test_bikerack.cpython-314-pytest-9.0.2.pyc +0 -0
  325. package/pennyfarthing_scripts/tests/__pycache__/test_brownfield.cpython-314-pytest-9.0.2.pyc +0 -0
  326. package/pennyfarthing_scripts/tests/__pycache__/test_cli_modules.cpython-314-pytest-9.0.2.pyc +0 -0
  327. package/pennyfarthing_scripts/tests/__pycache__/test_cli_normalization.cpython-314-pytest-9.0.2.pyc +0 -0
  328. package/pennyfarthing_scripts/tests/__pycache__/test_codemarkers.cpython-314-pytest-9.0.2.pyc +0 -0
  329. package/pennyfarthing_scripts/tests/__pycache__/test_common.cpython-314-pytest-9.0.2.pyc +0 -0
  330. package/pennyfarthing_scripts/tests/__pycache__/test_epic_shard_validation.cpython-314-pytest-9.0.2.pyc +0 -0
  331. package/pennyfarthing_scripts/tests/__pycache__/test_gate_file_resolution.cpython-314-pytest-9.0.2.pyc +0 -0
  332. package/pennyfarthing_scripts/tests/__pycache__/test_gate_runner.cpython-314-pytest-9.0.2.pyc +0 -0
  333. package/pennyfarthing_scripts/tests/__pycache__/test_git_utils.cpython-314-pytest-9.0.2.pyc +0 -0
  334. package/pennyfarthing_scripts/tests/__pycache__/test_handoff_cli.cpython-314-pytest-9.0.2.pyc +0 -0
  335. package/pennyfarthing_scripts/tests/__pycache__/test_handoff_e2e.cpython-314-pytest-9.0.2.pyc +0 -0
  336. package/pennyfarthing_scripts/tests/__pycache__/test_healthscore.cpython-314-pytest-9.0.2.pyc +0 -0
  337. package/pennyfarthing_scripts/tests/__pycache__/test_jira_package.cpython-314-pytest-9.0.2.pyc +0 -0
  338. package/pennyfarthing_scripts/tests/__pycache__/test_package_structure.cpython-314-pytest-9.0.2.pyc +0 -0
  339. package/pennyfarthing_scripts/tests/__pycache__/test_patch_mode.cpython-314-pytest-9.0.2.pyc +0 -0
  340. package/pennyfarthing_scripts/tests/__pycache__/test_prime.cpython-314-pytest-9.0.2.pyc +0 -0
  341. package/pennyfarthing_scripts/tests/__pycache__/test_resolve_gate_file_field.cpython-314-pytest-9.0.2.pyc +0 -0
  342. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_package.cpython-314-pytest-9.0.2.pyc +0 -0
  343. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_panel.cpython-314-pytest-9.0.2.pyc +0 -0
  344. package/pennyfarthing_scripts/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
  345. package/pennyfarthing_scripts/tests/__pycache__/test_story_add.cpython-314-pytest-9.0.2.pyc +0 -0
  346. package/pennyfarthing_scripts/tests/__pycache__/test_story_package.cpython-314-pytest-9.0.2.pyc +0 -0
  347. package/pennyfarthing_scripts/tests/__pycache__/test_story_update.cpython-314-pytest-9.0.2.pyc +0 -0
  348. package/pennyfarthing_scripts/tests/__pycache__/test_tiers.cpython-314-pytest-9.0.2.pyc +0 -0
  349. package/pennyfarthing_scripts/tests/__pycache__/test_token_counting.cpython-314-pytest-9.0.2.pyc +0 -0
  350. package/pennyfarthing_scripts/tests/__pycache__/test_topology_loader.cpython-314-pytest-9.0.2.pyc +0 -0
  351. package/pennyfarthing_scripts/tests/__pycache__/test_tui_focus.cpython-314-pytest-9.0.2.pyc +0 -0
  352. package/pennyfarthing_scripts/tests/__pycache__/test_tui_panel_persistence.cpython-314-pytest-9.0.2.pyc +0 -0
  353. package/pennyfarthing_scripts/tests/__pycache__/test_validate_cmd.cpython-314-pytest-9.0.2.pyc +0 -0
  354. package/pennyfarthing_scripts/tests/__pycache__/test_version_sentinel.cpython-314-pytest-9.0.2.pyc +0 -0
  355. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_check.cpython-314-pytest-9.0.2.pyc +0 -0
  356. package/pennyfarthing_scripts/tests/__pycache__/test_yaml_io.cpython-314-pytest-9.0.2.pyc +0 -0
  357. package/pennyfarthing_scripts/tests/test_108_1_gate_migration.py +540 -0
  358. package/pennyfarthing_scripts/tests/test_108_2_remove_handoff_fallback.py +339 -0
  359. package/pennyfarthing_scripts/tests/test_confidence_sm_evaluation.py +253 -0
  360. package/pennyfarthing_scripts/tests/test_confidence_sm_gate.py +315 -0
  361. package/pennyfarthing_scripts/tests/test_gate_file_resolution.py +341 -0
  362. package/pennyfarthing_scripts/tests/test_gate_runner.py +620 -0
  363. package/pennyfarthing_scripts/tests/test_handoff_cli.py +929 -0
  364. package/pennyfarthing_scripts/tests/test_handoff_e2e.py +454 -0
  365. package/pennyfarthing_scripts/tests/test_resolve_gate_file_field.py +464 -0
  366. package/pennyfarthing_scripts/theme/__pycache__/__init__.cpython-314.pyc +0 -0
  367. package/pennyfarthing_scripts/theme/__pycache__/cli.cpython-314.pyc +0 -0
  368. package/pennyfarthing_scripts/validate/__pycache__/__init__.cpython-314.pyc +0 -0
  369. package/pennyfarthing_scripts/validate/__pycache__/cli.cpython-314.pyc +0 -0
  370. package/pennyfarthing_scripts/validate/adapters/__pycache__/__init__.cpython-314.pyc +0 -0
  371. package/pennyfarthing_scripts/validate/adapters/__pycache__/agent.cpython-314.pyc +0 -0
  372. package/pennyfarthing_scripts/validate/adapters/__pycache__/schema.cpython-314.pyc +0 -0
  373. package/pennyfarthing_scripts/validate/adapters/__pycache__/skill_command.cpython-314.pyc +0 -0
  374. package/pennyfarthing_scripts/validate/adapters/__pycache__/sprint.cpython-314.pyc +0 -0
  375. package/pennyfarthing_scripts/validate/adapters/__pycache__/workflow.cpython-314.pyc +0 -0
  376. package/pennyfarthing_scripts/validate/adapters/skill_command.py +200 -0
  377. package/pennyfarthing_scripts/validate/adapters/workflow.py +64 -0
  378. package/pennyfarthing_scripts/validate/cli.py +15 -4
  379. package/packages/core/dist/scripts/benchmark-integration.d.ts +0 -182
  380. package/packages/core/dist/scripts/benchmark-integration.d.ts.map +0 -1
  381. package/packages/core/dist/scripts/benchmark-integration.js +0 -691
  382. package/packages/core/dist/scripts/benchmark-integration.js.map +0 -1
  383. package/packages/core/dist/scripts/job-fair-aggregator.d.ts +0 -150
  384. package/packages/core/dist/scripts/job-fair-aggregator.d.ts.map +0 -1
  385. package/packages/core/dist/scripts/job-fair-aggregator.js +0 -547
  386. package/packages/core/dist/scripts/job-fair-aggregator.js.map +0 -1
  387. package/pennyfarthing-dist/agents/handoff.md +0 -250
  388. package/pennyfarthing-dist/agents/sm-handoff.md +0 -152
  389. package/pennyfarthing-dist/scripts/core/handoff-marker.sh +0 -112
  390. package/pennyfarthing-dist/scripts/hooks/__pycache__/question_reflector_check.cpython-314.pyc +0 -0
  391. package/pennyfarthing_scripts/__pycache__/__init__.cpython-311.pyc +0 -0
  392. package/pennyfarthing_scripts/__pycache__/jira.cpython-314.pyc +0 -0
  393. package/pennyfarthing_scripts/__pycache__/sprint.cpython-314.pyc +0 -0
  394. package/pennyfarthing_scripts/__pycache__/workflow.cpython-311.pyc +0 -0
  395. package/pennyfarthing_scripts/jira/__pycache__/compat.cpython-314.pyc +0 -0
  396. package/pennyfarthing_scripts/jira/__pycache__/mappings.cpython-314.pyc +0 -0
  397. package/pennyfarthing_scripts/jira/__pycache__/models.cpython-314.pyc +0 -0
  398. package/pennyfarthing_scripts/migration/__pycache__/__main__.cpython-314.pyc +0 -0
  399. package/pennyfarthing_scripts/migration/__pycache__/cli.cpython-314.pyc +0 -0
  400. package/pennyfarthing_scripts/prime/__pycache__/__main__.cpython-314.pyc +0 -0
  401. package/pennyfarthing_scripts/tests/__pycache__/test_workflow_cli.cpython-314-pytest-9.0.2.pyc +0 -0
@@ -14,22 +14,27 @@
14
14
 
15
15
  # Determine project directory
16
16
  PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
17
- PORT_FILE="$PROJECT_DIR/.cyclist-port"
18
17
 
19
- # Check if port file exists
20
- if [[ -f "$PORT_FILE" ]]; then
21
- # Read the port number
22
- PORT=$(cat "$PORT_FILE" 2>/dev/null)
18
+ # Check .cyclist-port first, then .bikerack-port as fallback
19
+ PORT=""
20
+ for PORT_FILE in "$PROJECT_DIR/.cyclist-port" "$PROJECT_DIR/.bikerack-port"; do
21
+ if [[ -f "$PORT_FILE" ]]; then
22
+ PORT=$(cat "$PORT_FILE" 2>/dev/null)
23
+ # Validate port is a number
24
+ if [[ "$PORT" =~ ^[0-9]+$ ]]; then
25
+ break
26
+ fi
27
+ PORT=""
28
+ fi
29
+ done
23
30
 
24
- # Validate port is a number
25
- if [[ "$PORT" =~ ^[0-9]+$ ]]; then
26
- # Set OTEL environment variables
27
- export OTEL_EXPORTER_OTLP_PROTOCOL="http/json"
28
- export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:$PORT"
31
+ # Configure OTEL if a valid port was found
32
+ if [[ -n "$PORT" ]]; then
33
+ export OTEL_EXPORTER_OTLP_PROTOCOL="http/json"
34
+ export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:$PORT"
29
35
 
30
- # Optional: Log for debugging (can be silenced by setting CYCLIST_QUIET=1)
31
- if [[ -z "$CYCLIST_QUIET" ]]; then
32
- echo "[otel-auto-config] Configured OTEL to http://localhost:$PORT" >&2
33
- fi
36
+ # Optional: Log for debugging (can be silenced by setting CYCLIST_QUIET=1)
37
+ if [[ -z "$CYCLIST_QUIET" ]]; then
38
+ echo "[otel-auto-config] Configured OTEL to http://localhost:$PORT (from $PORT_FILE)" >&2
34
39
  fi
35
40
  fi
@@ -2,7 +2,8 @@
2
2
  """
3
3
  Individual Portrait Generator for Pennyfarthing Themes
4
4
 
5
- Generates individual portraits per theme using Stable Diffusion SDXL on M3 Max (MPS).
5
+ Generates individual portraits per theme on M3 Max (MPS).
6
+ Supports multiple engines: SDXL (Stable Diffusion XL) and Flux.
6
7
  Reads visual prompts from theme YAML files in three locations:
7
8
  - Package: packages/themes-*/themes/ (output to packages/themes-*/portraits/)
8
9
  - Built-in: pennyfarthing-dist/personas/themes/ (output to pennyfarthing-dist/personas/portraits/)
@@ -11,9 +12,9 @@ Reads visual prompts from theme YAML files in three locations:
11
12
  Output: {portraits-dir}/{theme}/{slug}-{OCEAN}.png (512x512px each)
12
13
 
13
14
  Usage:
14
- python3 scripts/generate-portraits.py [--dry-run] [--theme THEME]
15
- python3 scripts/generate-portraits.py --theme gilligans-island --dry-run
16
- python3 scripts/generate-portraits.py --role ba --skip-existing
15
+ python3 scripts/generate-portraits.py [--dry-run] [--theme THEME] [--engine ENGINE]
16
+ python3 scripts/generate-portraits.py --engine flux --theme gilligans-island --dry-run
17
+ python3 scripts/generate-portraits.py --engine sdxl --role ba --skip-existing
17
18
  """
18
19
 
19
20
  import argparse
@@ -47,6 +48,11 @@ except ImportError as e:
47
48
  HAS_TORCH = False
48
49
  TORCH_ERROR = str(e)
49
50
 
51
+ # Native Flux repo (Black Forest Labs) — loaded lazily when --engine flux is used
52
+ HAS_FLUX = False
53
+ FLUX_ERROR = ""
54
+ FLUX_PROJECT_DIR = Path.home() / "Projects" / "flux"
55
+
50
56
  # CLIP tokenizer for accurate token counting (optional - falls back to word estimate)
51
57
  try:
52
58
  from transformers import CLIPTokenizer
@@ -81,18 +87,29 @@ BUILTIN_THEMES_DIR = PROJECT_ROOT / "pennyfarthing-dist" / "personas" / "themes"
81
87
  CUSTOM_THEMES_DIR = PROJECT_ROOT / ".claude" / "pennyfarthing" / "themes"
82
88
  BUILTIN_OUTPUT_DIR = PROJECT_ROOT / "pennyfarthing-dist" / "personas" / "portraits"
83
89
  PACKAGES_DIR = PROJECT_ROOT / "packages"
84
- MODEL_ID = "stabilityai/stable-diffusion-xl-base-1.0"
85
-
86
- # SDXL generates at 1024x1024, we'll resize to 512x512
87
- GENERATION_SIZE = 1024
88
- OUTPUT_SIZE = 512
89
-
90
- # Generation parameters
91
- NUM_INFERENCE_STEPS = 30
92
- GUIDANCE_SCALE = 7.5
93
-
94
- # CLIP token limit - prompts are truncated beyond this
95
- CLIP_MAX_TOKENS = 77
90
+ # Engine configurations
91
+ ENGINES = {
92
+ "sdxl": {
93
+ "model_id": "stabilityai/stable-diffusion-xl-base-1.0",
94
+ "generation_size": 1024,
95
+ "output_size": 512,
96
+ "num_inference_steps": 30,
97
+ "guidance_scale": 7.5,
98
+ "max_tokens": 77,
99
+ "supports_negative_prompt": True,
100
+ },
101
+ "flux": {
102
+ "model_name": "flux-schnell",
103
+ "generation_size": 1024,
104
+ "output_size": 512,
105
+ "num_inference_steps": 4,
106
+ "guidance_scale": 3.5,
107
+ "max_tokens": 256,
108
+ "supports_negative_prompt": False,
109
+ },
110
+ }
111
+
112
+ DEFAULT_ENGINE = "sdxl"
96
113
 
97
114
  # Role order for the 11 agents
98
115
  ROLES = [
@@ -124,7 +141,7 @@ def count_clip_tokens(text: str) -> int:
124
141
  return int(len(text.split()) * 1.3)
125
142
 
126
143
 
127
- def truncate_prompt_to_clip_limit(visual: str, style_suffix: str, max_tokens: int = CLIP_MAX_TOKENS) -> tuple[str, bool]:
144
+ def truncate_prompt_to_clip_limit(visual: str, style_suffix: str, max_tokens: int = 77) -> tuple[str, bool]:
128
145
  """Truncate prompt to fit within CLIP token limit.
129
146
 
130
147
  Strategy: Prioritize the visual description over the style suffix.
@@ -208,61 +225,167 @@ def parse_theme_file(theme_path: Path) -> dict:
208
225
  return result
209
226
 
210
227
 
211
- def build_portrait_prompt(visual: str, style_suffix: str = None) -> tuple[str, bool, int]:
212
- """Build a prompt for portrait generation with CLIP token limit enforcement.
228
+ def build_portrait_prompt(visual: str, style_suffix: str = None, max_tokens: int = 77) -> tuple[str, bool, int]:
229
+ """Build a prompt for portrait generation with token limit enforcement.
213
230
 
214
231
  Args:
215
232
  visual: The character's visual description from theme YAML
216
233
  style_suffix: Optional theme-specific style suffix. Falls back to DEFAULT_STYLE_SUFFIX.
234
+ max_tokens: Token limit for the engine (77 for SDXL/CLIP, 256 for Flux/T5).
217
235
 
218
236
  Returns:
219
237
  tuple: (prompt, was_truncated, token_count)
220
238
  """
221
239
  suffix = style_suffix if style_suffix is not None else DEFAULT_STYLE_SUFFIX
222
- prompt, was_truncated = truncate_prompt_to_clip_limit(visual, suffix)
240
+ prompt, was_truncated = truncate_prompt_to_clip_limit(visual, suffix, max_tokens=max_tokens)
223
241
  token_count = count_clip_tokens(prompt)
224
242
  return prompt, was_truncated, token_count
225
243
 
226
244
 
227
- def load_pipeline():
228
- """Load SDXL pipeline on MPS."""
229
- print("\nLoading SDXL model on MPS...")
230
- print("(First run downloads ~6.5GB model)")
231
-
232
- # Use float32 on MPS to avoid NaN issues with float16
233
- pipe = StableDiffusionXLPipeline.from_pretrained(
234
- MODEL_ID,
235
- torch_dtype=torch.float32,
236
- use_safetensors=True,
237
- )
245
+ def _load_flux_modules():
246
+ """Lazy-load the native Flux repo and return its modules."""
247
+ global HAS_FLUX, FLUX_ERROR
248
+
249
+ if not FLUX_PROJECT_DIR.exists():
250
+ FLUX_ERROR = f"Flux project not found at {FLUX_PROJECT_DIR}"
251
+ return None
252
+
253
+ # Add flux src to path so we can import it
254
+ flux_src = str(FLUX_PROJECT_DIR / "src")
255
+ if flux_src not in sys.path:
256
+ sys.path.insert(0, flux_src)
257
+
258
+ try:
259
+ from flux.sampling import denoise, get_noise, get_schedule, prepare, unpack
260
+ from flux.util import configs, load_ae, load_clip, load_flow_model, load_t5
261
+ HAS_FLUX = True
262
+ return {
263
+ "denoise": denoise,
264
+ "get_noise": get_noise,
265
+ "get_schedule": get_schedule,
266
+ "prepare": prepare,
267
+ "unpack": unpack,
268
+ "configs": configs,
269
+ "load_ae": load_ae,
270
+ "load_clip": load_clip,
271
+ "load_flow_model": load_flow_model,
272
+ "load_t5": load_t5,
273
+ }
274
+ except ImportError as e:
275
+ FLUX_ERROR = str(e)
276
+ return None
277
+
278
+
279
+ def load_pipeline(engine_name: str):
280
+ """Load the image generation pipeline for the specified engine.
281
+
282
+ Returns a dict with engine-specific components.
283
+ For SDXL: {"pipe": StableDiffusionXLPipeline}
284
+ For Flux: {"model", "ae", "t5", "clip", "flux_modules", "device"}
285
+ """
286
+ engine = ENGINES[engine_name]
287
+
288
+ if engine_name == "flux":
289
+ model_name = engine["model_name"]
290
+ print(f"\nLoading Flux ({model_name}) on MPS...")
291
+ print(" (First run downloads the model)")
292
+
293
+ flux = _load_flux_modules()
294
+ if flux is None:
295
+ print(f"Error loading Flux: {FLUX_ERROR}")
296
+ print(f"Expected Flux repo at: {FLUX_PROJECT_DIR}")
297
+ print("Install: git clone https://github.com/black-forest-labs/flux ~/Projects/flux")
298
+ sys.exit(1)
238
299
 
239
- pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
240
- pipe = pipe.to("mps")
241
- pipe.enable_attention_slicing()
300
+ device = torch.device("mps")
301
+ t5 = flux["load_t5"](device, max_length=256)
302
+ clip = flux["load_clip"](device)
303
+ model = flux["load_flow_model"](model_name, device=device)
304
+ ae = flux["load_ae"](model_name, device=device)
305
+
306
+ print("Flux models loaded.\n")
307
+ return {
308
+ "model": model, "ae": ae, "t5": t5, "clip": clip,
309
+ "flux_modules": flux, "device": device,
310
+ }
311
+ else:
312
+ model_id = engine["model_id"]
313
+ print(f"\nLoading SDXL model on MPS...")
314
+ print(f" Model: {model_id}")
315
+ print(" (First run downloads ~6.5GB model)")
316
+
317
+ pipe = StableDiffusionXLPipeline.from_pretrained(
318
+ model_id,
319
+ torch_dtype=torch.float32,
320
+ use_safetensors=True,
321
+ )
322
+ pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
323
+ pipe = pipe.to("mps")
324
+ pipe.enable_attention_slicing()
325
+
326
+ print("Model loaded.\n")
327
+ return {"pipe": pipe}
328
+
329
+
330
+ def generate_portrait(pipeline_components: dict, prompt: str, engine_name: str, seed: int = 42) -> "Image.Image":
331
+ """Generate a single portrait using the specified engine."""
332
+ engine = ENGINES[engine_name]
333
+ gen_size = engine["generation_size"]
334
+ out_size = engine["output_size"]
335
+
336
+ if engine_name == "flux":
337
+ from einops import rearrange
338
+
339
+ flux = pipeline_components["flux_modules"]
340
+ model = pipeline_components["model"]
341
+ ae = pipeline_components["ae"]
342
+ t5 = pipeline_components["t5"]
343
+ clip = pipeline_components["clip"]
344
+ device = pipeline_components["device"]
345
+
346
+ # Flux native sampling pipeline
347
+ x = flux["get_noise"](
348
+ 1, gen_size, gen_size,
349
+ device=device, dtype=torch.bfloat16, seed=seed,
350
+ )
351
+ inp = flux["prepare"](t5, clip, x, prompt=prompt)
352
+ timesteps = flux["get_schedule"](
353
+ engine["num_inference_steps"], inp["img"].shape[1], shift=False,
354
+ )
242
355
 
243
- print("Model loaded.\n")
244
- return pipe
356
+ with torch.no_grad():
357
+ x = flux["denoise"](model, **inp, timesteps=timesteps, guidance=engine["guidance_scale"])
245
358
 
359
+ x = flux["unpack"](x.float(), gen_size, gen_size)
360
+ with torch.autocast(device_type=device.type, dtype=torch.bfloat16):
361
+ x = ae.decode(x)
246
362
 
247
- def generate_portrait(pipe, prompt: str, seed: int = 42) -> "Image.Image":
248
- """Generate a single portrait."""
249
- # Use CPU generator for MPS compatibility
250
- generator = torch.Generator().manual_seed(seed)
363
+ # Convert tensor to PIL
364
+ x = x.clamp(-1, 1)
365
+ x = rearrange(x[0], "c h w -> h w c")
366
+ image = Image.fromarray((127.5 * (x + 1.0)).cpu().byte().numpy())
367
+ else:
368
+ # SDXL via diffusers
369
+ pipe = pipeline_components["pipe"]
370
+ generator = torch.Generator().manual_seed(seed)
251
371
 
252
- with torch.no_grad():
253
- result = pipe(
372
+ kwargs = dict(
254
373
  prompt=prompt,
255
- negative_prompt="color, grayscale, photorealistic, blurry, deformed",
256
- width=GENERATION_SIZE,
257
- height=GENERATION_SIZE,
258
- num_inference_steps=NUM_INFERENCE_STEPS,
259
- guidance_scale=GUIDANCE_SCALE,
374
+ width=gen_size,
375
+ height=gen_size,
376
+ num_inference_steps=engine["num_inference_steps"],
377
+ guidance_scale=engine["guidance_scale"],
260
378
  generator=generator,
261
379
  )
380
+ if engine["supports_negative_prompt"]:
381
+ kwargs["negative_prompt"] = "color, grayscale, photorealistic, blurry, deformed"
262
382
 
263
- image = result.images[0]
264
- # Resize to output size
265
- return image.resize((OUTPUT_SIZE, OUTPUT_SIZE), Image.Resampling.LANCZOS)
383
+ with torch.no_grad():
384
+ result = pipe(**kwargs)
385
+
386
+ image = result.images[0]
387
+
388
+ return image.resize((out_size, out_size), Image.Resampling.LANCZOS)
266
389
 
267
390
 
268
391
  def main():
@@ -273,8 +396,13 @@ def main():
273
396
  parser.add_argument("--seed", type=int, default=42, help="Random seed")
274
397
  parser.add_argument("--skip-existing", action="store_true", help="Skip existing files")
275
398
  parser.add_argument("--output-dir", type=str, help="Output to different directory (default: pennyfarthing-dist/personas/portraits)")
399
+ parser.add_argument("--engine", type=str, choices=list(ENGINES.keys()), default=DEFAULT_ENGINE,
400
+ help=f"Image generation engine (default: {DEFAULT_ENGINE})")
276
401
  args = parser.parse_args()
277
402
 
403
+ engine_name = args.engine
404
+ engine = ENGINES[engine_name]
405
+
278
406
  # Determine output directory override
279
407
  output_override = Path(args.output_dir) if args.output_dir else None
280
408
 
@@ -323,9 +451,11 @@ def main():
323
451
  print(f" Package: {src}")
324
452
  print(f" Custom: {CUSTOM_THEMES_DIR}")
325
453
  print(f"Found {len(theme_entries)} themes")
454
+ engine_label = engine.get("model_id", engine.get("model_name", engine_name))
455
+ print(f"Engine: {engine_name.upper()} ({engine_label})")
326
456
 
327
457
  if args.dry_run:
328
- print(f"\nCLIP token limit: {CLIP_MAX_TOKENS} tokens")
458
+ print(f"\nToken limit: {engine['max_tokens']} tokens ({engine_name.upper()})")
329
459
  print(f"Tokenizer: {'CLIP (accurate)' if HAS_CLIP_TOKENIZER else 'word estimate (fallback)'}")
330
460
  roles_to_show = [args.role] if args.role else ROLES
331
461
  print(f"\nDry run - portraits to generate (roles: {', '.join(roles_to_show)}):")
@@ -348,7 +478,9 @@ def main():
348
478
  status = "EXISTS" if out_path.exists() else "PENDING"
349
479
 
350
480
  # Check token count and truncation
351
- prompt, was_truncated, token_count = build_portrait_prompt(char["visual"], parsed["portrait_style"])
481
+ prompt, was_truncated, token_count = build_portrait_prompt(
482
+ char["visual"], parsed["portrait_style"], max_tokens=engine["max_tokens"]
483
+ )
352
484
  token_status = f"{token_count}tok"
353
485
  if was_truncated:
354
486
  token_status = f"⚠️ {token_count}tok TRUNCATED"
@@ -362,7 +494,7 @@ def main():
362
494
  if truncation_warnings:
363
495
  print(f"\n{'='*60}")
364
496
  print(f"⚠️ WARNING: {len(truncation_warnings)} prompts will be truncated!")
365
- print(f" CLIP limit is {CLIP_MAX_TOKENS} tokens. Consider shortening:")
497
+ print(f" Token limit is {engine['max_tokens']} ({engine_name.upper()}). Consider shortening:")
366
498
  for theme, role, filename in truncation_warnings[:10]:
367
499
  print(f" - {theme}/{filename} ({role})")
368
500
  if len(truncation_warnings) > 10:
@@ -376,7 +508,7 @@ def main():
376
508
  sys.exit(1)
377
509
 
378
510
  # Load model
379
- pipe = load_pipeline()
511
+ pipeline_components = load_pipeline(engine_name)
380
512
 
381
513
  # Track results
382
514
  successful = 0
@@ -406,7 +538,9 @@ def main():
406
538
  print(f" SKIP (exists): {char['filename']}")
407
539
  continue
408
540
 
409
- prompt, was_truncated, token_count = build_portrait_prompt(char["visual"], parsed["portrait_style"])
541
+ prompt, was_truncated, token_count = build_portrait_prompt(
542
+ char["visual"], parsed["portrait_style"], max_tokens=engine["max_tokens"]
543
+ )
410
544
 
411
545
  if was_truncated:
412
546
  truncated.append((theme, char["filename"], token_count))
@@ -417,7 +551,7 @@ def main():
417
551
  try:
418
552
  # Vary seed per character for diversity (base_seed + role_index)
419
553
  role_seed = args.seed + ROLES.index(role)
420
- image = generate_portrait(pipe, prompt, seed=role_seed)
554
+ image = generate_portrait(pipeline_components, prompt, engine_name, seed=role_seed)
421
555
  image.save(out_path, "PNG")
422
556
  successful += 1
423
557
  print(f" DONE: {char['filename']}")
@@ -430,7 +564,7 @@ def main():
430
564
  print(f"\n{'='*50}")
431
565
  print(f"Complete: {successful} portraits in {elapsed}")
432
566
  if truncated:
433
- print(f"Truncated: {len(truncated)} prompts exceeded {CLIP_MAX_TOKENS} token limit")
567
+ print(f"Truncated: {len(truncated)} prompts exceeded {engine['max_tokens']} token limit ({engine_name.upper()})")
434
568
  if failed:
435
569
  print(f"Failed: {len(failed)}")
436
570
  for t, r, e in failed:
@@ -5,8 +5,8 @@
5
5
  #
6
6
  # Usage:
7
7
  # ./scripts/generate-portraits.sh --theme arthurian-mythos --dry-run
8
- # ./scripts/generate-portraits.sh --theme shakespeare
9
- # ./scripts/generate-portraits.sh --theme star-trek-tos --output-dir /tmp/portraits
8
+ # ./scripts/generate-portraits.sh --engine flux --theme shakespeare
9
+ # ./scripts/generate-portraits.sh --engine sdxl --theme star-trek-tos --output-dir /tmp/portraits
10
10
  # ./scripts/generate-portraits.sh --help
11
11
 
12
12
  set -euo pipefail
@@ -20,12 +20,25 @@ while [[ ! -d "$_dir/.pennyfarthing" ]] && [[ ! -d "$_dir/pennyfarthing-dist" ]]
20
20
  done
21
21
  PROJECT_ROOT="$_dir"
22
22
 
23
- # Find Python venv - check multiple locations
24
- # Priority: VENV_DIR env var > project .venv > ~/.venvs/sd > ~/.venv
23
+ # Detect engine from args to select the right venv
25
24
  PYTHON_SCRIPT="$SCRIPT_DIR/generate-portraits.py"
25
+ ENGINE="sdxl"
26
+ prev_arg=""
27
+ for arg in "$@"; do
28
+ if [[ "$prev_arg" == "--engine" ]]; then
29
+ ENGINE="$arg"
30
+ break
31
+ fi
32
+ prev_arg="$arg"
33
+ done
26
34
 
35
+ # Find Python venv based on engine
36
+ # Flux uses ~/.venvs/flux, SDXL uses ~/.venvs/sd
37
+ # Priority: VENV_DIR env var > engine-specific > project .venv > fallback
27
38
  if [[ -z "${VENV_DIR:-}" ]]; then
28
- if [[ -d "$PROJECT_ROOT/.venv" ]]; then
39
+ if [[ "$ENGINE" == "flux" ]] && [[ -d "$HOME/.venvs/flux" ]]; then
40
+ VENV_DIR="$HOME/.venvs/flux"
41
+ elif [[ -d "$PROJECT_ROOT/.venv" ]]; then
29
42
  VENV_DIR="$PROJECT_ROOT/.venv"
30
43
  elif [[ -d "$HOME/.venvs/sd" ]]; then
31
44
  VENV_DIR="$HOME/.venvs/sd"
@@ -36,14 +49,17 @@ fi
36
49
 
37
50
  # Check venv exists
38
51
  if [[ -z "${VENV_DIR:-}" ]] || [[ ! -d "$VENV_DIR" ]]; then
39
- echo "Error: Virtual environment not found"
40
- echo "Searched: $PROJECT_ROOT/.venv, ~/.venvs/sd, ~/.venv"
52
+ echo "Error: Virtual environment not found for engine '$ENGINE'"
53
+ echo "Searched: $PROJECT_ROOT/.venv, ~/.venvs/sd, ~/.venvs/flux, ~/.venv"
41
54
  echo "Or set VENV_DIR=/path/to/venv"
42
- echo "Create with: python3 -m venv .venv"
43
- echo "Then install: pip install diffusers transformers accelerate torch pillow pyyaml"
55
+ if [[ "$ENGINE" == "flux" ]]; then
56
+ echo "For Flux: python3 -m venv ~/.venvs/flux && pip install torch einops transformers pillow pyyaml"
57
+ else
58
+ echo "For SDXL: python3 -m venv ~/.venvs/sd && pip install diffusers transformers accelerate torch pillow pyyaml"
59
+ fi
44
60
  exit 1
45
61
  fi
46
- echo "Using venv: $VENV_DIR"
62
+ echo "Using venv: $VENV_DIR (engine: $ENGINE)"
47
63
 
48
64
  # Check Python script exists
49
65
  if [[ ! -f "$PYTHON_SCRIPT" ]]; then
@@ -306,16 +306,16 @@ git tag -a "v$NEW_VERSION" -m "Release $NEW_VERSION"
306
306
  git push origin main --tags
307
307
  ```
308
308
 
309
- ## Integration with /release Command
309
+ ## Integration with /pf-git release Command
310
310
 
311
- The `/release` command in Pennyfarthing can use this skill for:
311
+ The `/pf-git release` command in Pennyfarthing can use this skill for:
312
312
 
313
313
  1. **Auto-detecting new commits** - Run conventional commit analysis since last tag
314
314
  2. **Generating changelog sections** - Create properly formatted entries
315
315
  3. **Version bumping** - Semantic versioning based on commit types
316
316
  4. **Tagging** - Create annotated tags for releases
317
317
 
318
- Reference in `/release` command:
318
+ Reference in `/pf-git release` command:
319
319
 
320
320
  ```markdown
321
321
  See the [Changelog Skill](/changelog) for patterns on:
@@ -382,4 +382,4 @@ npx conventional-changelog -p angular -i CHANGELOG.md -s
382
382
  - **Conventional Commits:** https://www.conventionalcommits.org/
383
383
  - **Semantic Versioning:** https://semver.org/
384
384
  - **conventional-changelog:** https://github.com/conventional-changelog/conventional-changelog
385
- - **Pennyfarthing Release:** `/release` command
385
+ - **Pennyfarthing Release:** `/pf-git release` command
@@ -71,7 +71,7 @@ Never manually edit `sprint/current-sprint.yaml`. Use `pf sprint` CLI commands f
71
71
  | `pf backlog` | `pf sprint backlog` |
72
72
  | `pf work` | `pf sprint work` |
73
73
  | `pf story` | `pf sprint story` |
74
- | `/new-work` | `/pf-sprint work` |
74
+ | `/pf-session new` | `/pf-sprint work` |
75
75
 
76
76
  ---
77
77
 
@@ -106,6 +106,10 @@
106
106
  "redirect": {
107
107
  "type": "string",
108
108
  "description": "Skill name to redirect to when deprecated"
109
+ },
110
+ "command_group": {
111
+ "type": "string",
112
+ "description": "Command group name from command-registry.yaml that this skill manages"
109
113
  }
110
114
  },
111
115
  "required": ["name", "description", "category", "tags"],
@@ -115,6 +115,7 @@ skills:
115
115
  category: project-management
116
116
  tags: [jira, issues, sprint]
117
117
  version: "1.0.0"
118
+ command_group: jira
118
119
  prerequisites: []
119
120
  examples:
120
121
  - context: Viewing sprint issues
@@ -200,6 +201,7 @@ skills:
200
201
  category: project-management
201
202
  tags: [permissions, security, tools]
202
203
  version: "1.0.0"
204
+ command_group: permissions
203
205
  prerequisites: []
204
206
  examples:
205
207
  - context: Viewing active grants
@@ -237,6 +239,7 @@ skills:
237
239
  category: project-management
238
240
  tags: [sprint, status, backlog, stories, epics]
239
241
  version: "2.0.0"
242
+ command_group: sprint
240
243
  prerequisites: []
241
244
  examples:
242
245
  - context: Checking sprint status
@@ -317,6 +320,7 @@ skills:
317
320
  category: theming
318
321
  tags: [personas, themes, customization, creation]
319
322
  version: "2.0.0"
323
+ command_group: theme
320
324
  prerequisites: []
321
325
  examples:
322
326
  - context: Listing available themes
@@ -357,6 +361,7 @@ skills:
357
361
  category: project-management
358
362
  tags: [workflow, phases, tdd]
359
363
  version: "1.0.0"
364
+ command_group: workflow
360
365
  prerequisites: []
361
366
  examples:
362
367
  - context: Listing available workflows
@@ -53,6 +53,7 @@ workflow:
53
53
  Present findings to user. User decides what to add/change.
54
54
  Update the story description with agreed refinements.
55
55
  gate:
56
+ file: gates/approval
56
57
  type: approval
57
58
  condition: User reviewed dev-perspective gaps and decided on each item
58
59
 
@@ -73,6 +74,7 @@ workflow:
73
74
  Present findings to user. User decides what to add/change.
74
75
  Update the story description with agreed refinements.
75
76
  gate:
77
+ file: gates/approval
76
78
  type: approval
77
79
  condition: User reviewed tea-perspective gaps and decided on each item
78
80
 
@@ -93,6 +95,7 @@ workflow:
93
95
 
94
96
  Fix any remaining issues, then hand off to TEA.
95
97
  gate:
98
+ file: gates/approval
96
99
  type: approval
97
100
  condition: Story passes quality check and is ready for TEA
98
101
 
@@ -122,6 +125,7 @@ workflow:
122
125
  In the TEA Assessment handoff, list what quality gates Dev
123
126
  should expect to pass beyond the functional tests.
124
127
  gate:
128
+ file: gates/tests-fail
125
129
  type: tests_fail
126
130
  condition: All acceptance criteria have test coverage, including quality gate check
127
131
 
@@ -154,6 +158,7 @@ workflow:
154
158
 
155
159
  Reviewer should only receive code that is verified clean.
156
160
  gate:
161
+ file: gates/quality-pass
157
162
  type: quality_pass
158
163
  condition: >
159
164
  All project quality gates pass locally — tests, lint, format,
@@ -181,6 +186,7 @@ workflow:
181
186
  - Set review_verdict: approved in story
182
187
  - DO NOT merge the PR — hand off to SM for finish phase
183
188
  gate:
189
+ file: gates/approval
184
190
  type: approval
185
191
  condition: Code review passed, no blocking issues
186
192
 
@@ -194,6 +200,7 @@ workflow:
194
200
  from the story. Write failing tests for each testable finding.
195
201
  Commit RED tests. Hand off to Dev.
196
202
  gate:
203
+ file: gates/tests-fail
197
204
  type: tests_fail
198
205
  condition: New tests cover reviewer findings
199
206
 
@@ -227,6 +234,7 @@ workflow:
227
234
  If verification fails, hand back to Dev with specifics.
228
235
  If verification passes, hand off to Reviewer for re-review.
229
236
  gate:
237
+ file: gates/quality-pass
230
238
  type: quality_pass
231
239
  condition: >
232
240
  All project quality gates pass. Each review finding verified
@@ -255,6 +263,7 @@ workflow:
255
263
  5. Only after user approves: create PR (draft by default,
256
264
  or per user pr_mode preference in pennyfarthing config)
257
265
  gate:
266
+ file: gates/approval
258
267
  type: approval
259
268
  condition: User has reviewed and approved PR content before submission
260
269
 
@@ -315,6 +324,7 @@ workflow:
315
324
 
316
325
  Present decisions to user. User has final say on every item.
317
326
  gate:
327
+ file: gates/approval
318
328
  type: approval
319
329
  condition: User has decided on every external review comment
320
330
 
@@ -340,6 +350,7 @@ workflow:
340
350
 
341
351
  Commit fixes. Push branch. Present all draft replies to user.
342
352
  gate:
353
+ file: gates/approval
343
354
  type: approval
344
355
  condition: >
345
356
  User has approved every draft reply. Fixes pass quality
@@ -28,6 +28,7 @@ workflow:
28
28
  input: [audit_report, proposed_changes]
29
29
  output: [updated_files]
30
30
  gate:
31
+ file: gates/validation
31
32
  type: validation
32
33
  condition: Agent files parse correctly, no broken references
33
34
 
@@ -36,6 +37,7 @@ workflow:
36
37
  input: [updated_files]
37
38
  output: [approval]
38
39
  gate:
40
+ file: gates/approval
39
41
  type: approval
40
42
  condition: |
41
43
  Documentation quality review: