@pennyfarthing/core 11.3.8 → 11.4.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 (304) hide show
  1. package/README.md +1 -1
  2. package/package.json +4 -1
  3. package/packages/core/dist/public/css/react.css +1 -1
  4. package/packages/core/dist/public/js/react/react.js +24 -24
  5. package/packages/core/src/public/App.tsx +356 -0
  6. package/packages/core/src/public/components/AgentLoadDialog.tsx +202 -0
  7. package/packages/core/src/public/components/AgentPopup.tsx +308 -0
  8. package/packages/core/src/public/components/ApprovalModal/ApprovalModal.css +35 -0
  9. package/packages/core/src/public/components/ApprovalModal/index.tsx +632 -0
  10. package/packages/core/src/public/components/BikeRackIndex.tsx +53 -0
  11. package/packages/core/src/public/components/BikeRackWorkspace.tsx +217 -0
  12. package/packages/core/src/public/components/CommandPalette.tsx +554 -0
  13. package/packages/core/src/public/components/ConfirmDialog.tsx +168 -0
  14. package/packages/core/src/public/components/ContextIndicator/ContextIndicator.css +85 -0
  15. package/packages/core/src/public/components/ContextIndicator/index.tsx +330 -0
  16. package/packages/core/src/public/components/ContextSparkline.tsx +56 -0
  17. package/packages/core/src/public/components/ControlBar.tsx +636 -0
  18. package/packages/core/src/public/components/DeadCodeDialog.tsx +169 -0
  19. package/packages/core/src/public/components/DiffViewer.tsx +585 -0
  20. package/packages/core/src/public/components/DockviewWorkspace.tsx +749 -0
  21. package/packages/core/src/public/components/Editor.tsx +630 -0
  22. package/packages/core/src/public/components/ErrorBoundary.tsx +67 -0
  23. package/packages/core/src/public/components/FileTree.tsx +379 -0
  24. package/packages/core/src/public/components/FontPicker/FontPicker.css +276 -0
  25. package/packages/core/src/public/components/FontPicker/index.tsx +430 -0
  26. package/packages/core/src/public/components/FullFileTree.tsx +237 -0
  27. package/packages/core/src/public/components/HealthGauge.tsx +181 -0
  28. package/packages/core/src/public/components/Message.tsx +225 -0
  29. package/packages/core/src/public/components/MessageList.tsx +98 -0
  30. package/packages/core/src/public/components/MessageView.tsx +400 -0
  31. package/packages/core/src/public/components/ModeSwitch/ModeSwitch.css +165 -0
  32. package/packages/core/src/public/components/ModeSwitch/index.tsx +372 -0
  33. package/packages/core/src/public/components/PersonaHeader.tsx +242 -0
  34. package/packages/core/src/public/components/ProjectInfoBar.tsx +45 -0
  35. package/packages/core/src/public/components/QuickActions.tsx +267 -0
  36. package/packages/core/src/public/components/SpanTimeline.tsx +352 -0
  37. package/packages/core/src/public/components/StandalonePanel.tsx +82 -0
  38. package/packages/core/src/public/components/StatsStrip.tsx +162 -0
  39. package/packages/core/src/public/components/StreamingContent.tsx +77 -0
  40. package/packages/core/src/public/components/SubagentSpan.tsx +180 -0
  41. package/packages/core/src/public/components/TandemPortrait.tsx +72 -0
  42. package/packages/core/src/public/components/ThemePalette/ThemePalette.css +179 -0
  43. package/packages/core/src/public/components/ThemePalette/index.tsx +326 -0
  44. package/packages/core/src/public/components/ToolCallBlock.tsx +252 -0
  45. package/packages/core/src/public/components/ToolStack.tsx +209 -0
  46. package/packages/core/src/public/components/ToolStatus.tsx +57 -0
  47. package/packages/core/src/public/components/dialogs/CodeMarkersDialog.tsx +169 -0
  48. package/packages/core/src/public/components/dialogs/ComplexityDialog.tsx +163 -0
  49. package/packages/core/src/public/components/dialogs/DependenciesDialog.tsx +120 -0
  50. package/packages/core/src/public/components/dialogs/HotspotsDialog.tsx +451 -0
  51. package/packages/core/src/public/components/dialogs/ToolDialog.tsx +43 -0
  52. package/packages/core/src/public/components/panel-registry.ts +13 -0
  53. package/packages/core/src/public/components/panels/ACPanel.tsx +93 -0
  54. package/packages/core/src/public/components/panels/AcceptanceCriteriaPanel.tsx +104 -0
  55. package/packages/core/src/public/components/panels/AuditLogPanel.tsx +489 -0
  56. package/packages/core/src/public/components/panels/BackgroundPanel.tsx +115 -0
  57. package/packages/core/src/public/components/panels/BikeLanePanel.tsx +214 -0
  58. package/packages/core/src/public/components/panels/DebugPanel.tsx +344 -0
  59. package/packages/core/src/public/components/panels/DiffView.tsx +109 -0
  60. package/packages/core/src/public/components/panels/DiffsPanel.tsx +56 -0
  61. package/packages/core/src/public/components/panels/GitPanel.tsx +260 -0
  62. package/packages/core/src/public/components/panels/HotspotsPanel.tsx +365 -0
  63. package/packages/core/src/public/components/panels/MessageFeed.tsx +39 -0
  64. package/packages/core/src/public/components/panels/MessagePanel.tsx +497 -0
  65. package/packages/core/src/public/components/panels/ProgressPanel.tsx +189 -0
  66. package/packages/core/src/public/components/panels/SettingsPanel.tsx +361 -0
  67. package/packages/core/src/public/components/panels/SprintPanel.tsx +723 -0
  68. package/packages/core/src/public/components/panels/TandemPanel.tsx +104 -0
  69. package/packages/core/src/public/components/panels/TaskTracker.tsx +48 -0
  70. package/packages/core/src/public/components/panels/TeamPanel.tsx +64 -0
  71. package/packages/core/src/public/components/panels/TeamRoster.tsx +67 -0
  72. package/packages/core/src/public/components/panels/TodoPanel.tsx +142 -0
  73. package/packages/core/src/public/components/panels/WorkflowPanel.tsx +224 -0
  74. package/packages/core/src/public/components/panels/index.ts +24 -0
  75. package/packages/core/src/public/components/ui/alert-dialog.tsx +139 -0
  76. package/packages/core/src/public/components/ui/badge.tsx +36 -0
  77. package/packages/core/src/public/components/ui/button.tsx +57 -0
  78. package/packages/core/src/public/components/ui/checkbox.tsx +28 -0
  79. package/packages/core/src/public/components/ui/collapsible.tsx +9 -0
  80. package/packages/core/src/public/components/ui/command.tsx +151 -0
  81. package/packages/core/src/public/components/ui/dialog.tsx +120 -0
  82. package/packages/core/src/public/components/ui/popover.tsx +31 -0
  83. package/packages/core/src/public/components/ui/progress.tsx +28 -0
  84. package/packages/core/src/public/components/ui/scroll-area.tsx +46 -0
  85. package/packages/core/src/public/components/ui/select.tsx +157 -0
  86. package/packages/core/src/public/components/ui/separator.tsx +29 -0
  87. package/packages/core/src/public/components/ui/skeleton.tsx +15 -0
  88. package/packages/core/src/public/components/ui/switch.tsx +27 -0
  89. package/packages/core/src/public/components/ui/toggle-group.tsx +59 -0
  90. package/packages/core/src/public/components/ui/toggle.tsx +43 -0
  91. package/packages/core/src/public/components/ui/tooltip.tsx +30 -0
  92. package/packages/core/src/public/contexts/ClaudeContext.tsx +311 -0
  93. package/packages/core/src/public/contexts/MessageQueueContext.tsx +143 -0
  94. package/packages/core/src/public/css/theme-browser.css +550 -0
  95. package/packages/core/src/public/css/theme-system.css +630 -0
  96. package/packages/core/src/public/hooks/index.ts +49 -0
  97. package/packages/core/src/public/hooks/useAgentLoad.ts +105 -0
  98. package/packages/core/src/public/hooks/useBackgroundTasks.ts +131 -0
  99. package/packages/core/src/public/hooks/useClaude.ts +234 -0
  100. package/packages/core/src/public/hooks/useCodeMarkers.ts +101 -0
  101. package/packages/core/src/public/hooks/useColorScheme.ts +42 -0
  102. package/packages/core/src/public/hooks/useCommandHistory.ts +99 -0
  103. package/packages/core/src/public/hooks/useComplexity.ts +80 -0
  104. package/packages/core/src/public/hooks/useDeadCode.ts +99 -0
  105. package/packages/core/src/public/hooks/useDependencies.ts +82 -0
  106. package/packages/core/src/public/hooks/useDiffs.ts +143 -0
  107. package/packages/core/src/public/hooks/useFileBrowser.ts +73 -0
  108. package/packages/core/src/public/hooks/useFocusPanel.ts +137 -0
  109. package/packages/core/src/public/hooks/useGitStatus.ts +233 -0
  110. package/packages/core/src/public/hooks/useHealthScore.ts +71 -0
  111. package/packages/core/src/public/hooks/useHotspots.ts +123 -0
  112. package/packages/core/src/public/hooks/useLayoutPersistence.ts +141 -0
  113. package/packages/core/src/public/hooks/useMarkdownParser.ts +36 -0
  114. package/packages/core/src/public/hooks/useMarkerActions.ts +234 -0
  115. package/packages/core/src/public/hooks/useMessageQueue.ts +380 -0
  116. package/packages/core/src/public/hooks/useMessageStream.ts +131 -0
  117. package/packages/core/src/public/hooks/usePersona.ts +112 -0
  118. package/packages/core/src/public/hooks/usePlanModeExit.ts +105 -0
  119. package/packages/core/src/public/hooks/useResponsiveLayout.ts +173 -0
  120. package/packages/core/src/public/hooks/useSprint.ts +157 -0
  121. package/packages/core/src/public/hooks/useStatsStrip.ts +204 -0
  122. package/packages/core/src/public/hooks/useStory.ts +135 -0
  123. package/packages/core/src/public/hooks/useSubagentHelper.ts +64 -0
  124. package/packages/core/src/public/hooks/useSyntaxHighlighter.ts +52 -0
  125. package/packages/core/src/public/hooks/useTabCompletion.ts +124 -0
  126. package/packages/core/src/public/hooks/useTandemObservations.ts +165 -0
  127. package/packages/core/src/public/hooks/useTeamMembers.ts +273 -0
  128. package/packages/core/src/public/hooks/useTodos.ts +93 -0
  129. package/packages/core/src/public/hooks/useUserAvatar.ts +54 -0
  130. package/packages/core/src/public/images/cyclist-dark.png +0 -0
  131. package/packages/core/src/public/images/cyclist-light.png +0 -0
  132. package/packages/core/src/public/index.html +14 -0
  133. package/packages/core/src/public/index.tsx +10 -0
  134. package/packages/core/src/public/lib/utils.ts +6 -0
  135. package/packages/core/src/public/styles/dockview-theme.css +376 -0
  136. package/packages/core/src/public/styles/tailwind.css +4454 -0
  137. package/packages/core/src/public/types/message.ts +51 -0
  138. package/packages/core/src/public/utils/avatar-service.ts +73 -0
  139. package/packages/core/src/public/utils/color-presets.ts +940 -0
  140. package/packages/core/src/public/utils/font-presets.ts +362 -0
  141. package/packages/core/src/public/utils/formatDuration.ts +14 -0
  142. package/packages/core/src/public/utils/markdown.ts +249 -0
  143. package/packages/core/src/public/utils/messageFilters.ts +128 -0
  144. package/packages/core/src/public/utils/slash-commands.ts +341 -0
  145. package/packages/core/src/public/utils/subagent-display.ts +146 -0
  146. package/packages/core/src/public/utils/syntax.ts +219 -0
  147. package/packages/core/src/public/utils/toolIntentSummarizer.ts +199 -0
  148. package/packages/core/src/public/utils/toolStackGrouper.ts +106 -0
  149. package/packages/core/src/public/utils/toolTypeColors.ts +45 -0
  150. package/pennyfarthing-dist/pf/__pycache__/__init__.cpython-314.pyc +0 -0
  151. package/pennyfarthing-dist/pf/__pycache__/cli.cpython-314.pyc +0 -0
  152. package/pennyfarthing-dist/pf/__pycache__/context.cpython-314.pyc +0 -0
  153. package/pennyfarthing-dist/pf/bc/__pycache__/__init__.cpython-314.pyc +0 -0
  154. package/pennyfarthing-dist/pf/bc/__pycache__/cli.cpython-314.pyc +0 -0
  155. package/pennyfarthing-dist/pf/bc/__pycache__/focus.cpython-314.pyc +0 -0
  156. package/pennyfarthing-dist/pf/bc/__pycache__/split.cpython-314.pyc +0 -0
  157. package/pennyfarthing-dist/pf/bc/cli.py +0 -1
  158. package/pennyfarthing-dist/pf/bc/focus.py +0 -1
  159. package/pennyfarthing-dist/pf/bikerack/__pycache__/__init__.cpython-314.pyc +0 -0
  160. package/pennyfarthing-dist/pf/bikerack/__pycache__/base_panel.cpython-314.pyc +0 -0
  161. package/pennyfarthing-dist/pf/bikerack/__pycache__/cli.cpython-314.pyc +0 -0
  162. package/pennyfarthing-dist/pf/bikerack/__pycache__/git_panel.cpython-314.pyc +0 -0
  163. package/pennyfarthing-dist/pf/bikerack/__pycache__/launcher.cpython-314.pyc +0 -0
  164. package/pennyfarthing-dist/pf/bikerack/__pycache__/portrait_resolver.cpython-314.pyc +0 -0
  165. package/pennyfarthing-dist/pf/bikerack/__pycache__/sprint_panel.cpython-314.pyc +0 -0
  166. package/pennyfarthing-dist/pf/bikerack/__pycache__/story_detail_data.cpython-314.pyc +0 -0
  167. package/pennyfarthing-dist/pf/bikerack/__pycache__/story_detail_screen.cpython-314.pyc +0 -0
  168. package/pennyfarthing-dist/pf/bikerack/__pycache__/tui.cpython-314.pyc +0 -0
  169. package/pennyfarthing-dist/pf/bikerack/base_panel.py +0 -1
  170. package/pennyfarthing-dist/pf/bikerack/events.py +1 -7
  171. package/pennyfarthing-dist/pf/bikerack/git_panel.py +273 -10
  172. package/pennyfarthing-dist/pf/bikerack/portrait_resolver.py +21 -0
  173. package/pennyfarthing-dist/pf/bikerack/sprint_panel.py +58 -1
  174. package/pennyfarthing-dist/pf/bikerack/tui.py +5 -20
  175. package/pennyfarthing-dist/pf/bmad/__pycache__/__init__.cpython-314.pyc +0 -0
  176. package/pennyfarthing-dist/pf/bmad/__pycache__/cli.cpython-314.pyc +0 -0
  177. package/pennyfarthing-dist/pf/bmad/__pycache__/parser.cpython-314.pyc +0 -0
  178. package/pennyfarthing-dist/pf/bmad/parser.py +15 -9
  179. package/pennyfarthing-dist/pf/codemarkers/__pycache__/__init__.cpython-314.pyc +0 -0
  180. package/pennyfarthing-dist/pf/codemarkers/__pycache__/analyze.cpython-314.pyc +0 -0
  181. package/pennyfarthing-dist/pf/codemarkers/__pycache__/models.cpython-314.pyc +0 -0
  182. package/pennyfarthing-dist/pf/common/__pycache__/__init__.cpython-314.pyc +0 -0
  183. package/pennyfarthing-dist/pf/common/__pycache__/config.cpython-314.pyc +0 -0
  184. package/pennyfarthing-dist/pf/common/__pycache__/output.cpython-314.pyc +0 -0
  185. package/pennyfarthing-dist/pf/common/__pycache__/pr_config.cpython-314.pyc +0 -0
  186. package/pennyfarthing-dist/pf/common/__pycache__/themes.cpython-314.pyc +0 -0
  187. package/pennyfarthing-dist/pf/common/pr_config.py +27 -2
  188. package/pennyfarthing-dist/pf/complexity/__pycache__/__init__.cpython-314.pyc +0 -0
  189. package/pennyfarthing-dist/pf/complexity/__pycache__/analyze.cpython-314.pyc +0 -0
  190. package/pennyfarthing-dist/pf/complexity/__pycache__/models.cpython-314.pyc +0 -0
  191. package/pennyfarthing-dist/pf/consultation/__pycache__/__init__.cpython-314.pyc +0 -0
  192. package/pennyfarthing-dist/pf/consultation/__pycache__/cli.cpython-314.pyc +0 -0
  193. package/pennyfarthing-dist/pf/deadcode/__pycache__/__init__.cpython-314.pyc +0 -0
  194. package/pennyfarthing-dist/pf/deadcode/__pycache__/analyze.cpython-314.pyc +0 -0
  195. package/pennyfarthing-dist/pf/deadcode/__pycache__/cli.cpython-314.pyc +0 -0
  196. package/pennyfarthing-dist/pf/deadcode/__pycache__/models.cpython-314.pyc +0 -0
  197. package/pennyfarthing-dist/pf/dependencies/__pycache__/__init__.cpython-314.pyc +0 -0
  198. package/pennyfarthing-dist/pf/dependencies/__pycache__/analyze.cpython-314.pyc +0 -0
  199. package/pennyfarthing-dist/pf/dependencies/__pycache__/models.cpython-314.pyc +0 -0
  200. package/pennyfarthing-dist/pf/epic/__pycache__/__init__.cpython-314.pyc +0 -0
  201. package/pennyfarthing-dist/pf/epic/__pycache__/cli.cpython-314.pyc +0 -0
  202. package/pennyfarthing-dist/pf/git_group/__pycache__/__init__.cpython-314.pyc +0 -0
  203. package/pennyfarthing-dist/pf/git_group/__pycache__/cli.cpython-314.pyc +0 -0
  204. package/pennyfarthing-dist/pf/handoff/__pycache__/__init__.cpython-314.pyc +0 -0
  205. package/pennyfarthing-dist/pf/handoff/__pycache__/cli.cpython-314.pyc +0 -0
  206. package/pennyfarthing-dist/pf/handoff/__pycache__/complete_phase.cpython-314.pyc +0 -0
  207. package/pennyfarthing-dist/pf/handoff/__pycache__/marker.cpython-314.pyc +0 -0
  208. package/pennyfarthing-dist/pf/handoff/__pycache__/phase_check.cpython-314.pyc +0 -0
  209. package/pennyfarthing-dist/pf/handoff/__pycache__/resolve_gate.cpython-314.pyc +0 -0
  210. package/pennyfarthing-dist/pf/healthscore/__pycache__/__init__.cpython-314.pyc +0 -0
  211. package/pennyfarthing-dist/pf/healthscore/__pycache__/__main__.cpython-314.pyc +0 -0
  212. package/pennyfarthing-dist/pf/healthscore/__pycache__/analyze.cpython-314.pyc +0 -0
  213. package/pennyfarthing-dist/pf/healthscore/__pycache__/cli.cpython-314.pyc +0 -0
  214. package/pennyfarthing-dist/pf/healthscore/__pycache__/formatters.cpython-314.pyc +0 -0
  215. package/pennyfarthing-dist/pf/healthscore/__pycache__/models.cpython-314.pyc +0 -0
  216. package/pennyfarthing-dist/pf/hooks/__pycache__/__init__.cpython-314.pyc +0 -0
  217. package/pennyfarthing-dist/pf/hooks/__pycache__/bell_mode.cpython-314.pyc +0 -0
  218. package/pennyfarthing-dist/pf/hooks/__pycache__/cli.cpython-314.pyc +0 -0
  219. package/pennyfarthing-dist/pf/hooks/__pycache__/context_breaker.cpython-314.pyc +0 -0
  220. package/pennyfarthing-dist/pf/hooks/__pycache__/context_warning.cpython-314.pyc +0 -0
  221. package/pennyfarthing-dist/pf/hooks/__pycache__/cyclist_pretooluse.cpython-314.pyc +0 -0
  222. package/pennyfarthing-dist/pf/hooks/__pycache__/pre_edit_check.cpython-314.pyc +0 -0
  223. package/pennyfarthing-dist/pf/hooks/__pycache__/reflector_check.cpython-314.pyc +0 -0
  224. package/pennyfarthing-dist/pf/hooks/__pycache__/schema_validation.cpython-314.pyc +0 -0
  225. package/pennyfarthing-dist/pf/hooks/__pycache__/session_start.cpython-314.pyc +0 -0
  226. package/pennyfarthing-dist/pf/hooks/__pycache__/session_stop.cpython-314.pyc +0 -0
  227. package/pennyfarthing-dist/pf/hooks/__pycache__/sprint_yaml_validation.cpython-314.pyc +0 -0
  228. package/pennyfarthing-dist/pf/hooks/__pycache__/statusline.cpython-314.pyc +0 -0
  229. package/pennyfarthing-dist/pf/hotspots/__pycache__/__init__.cpython-314.pyc +0 -0
  230. package/pennyfarthing-dist/pf/hotspots/__pycache__/analyze.cpython-314.pyc +0 -0
  231. package/pennyfarthing-dist/pf/hotspots/__pycache__/cli.cpython-314.pyc +0 -0
  232. package/pennyfarthing-dist/pf/hotspots/__pycache__/models.cpython-314.pyc +0 -0
  233. package/pennyfarthing-dist/pf/jira/__pycache__/__init__.cpython-314.pyc +0 -0
  234. package/pennyfarthing-dist/pf/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
  235. package/pennyfarthing-dist/pf/jira/__pycache__/claim.cpython-314.pyc +0 -0
  236. package/pennyfarthing-dist/pf/jira/__pycache__/cli.cpython-314.pyc +0 -0
  237. package/pennyfarthing-dist/pf/jira/__pycache__/client.cpython-314.pyc +0 -0
  238. package/pennyfarthing-dist/pf/jira/__pycache__/create.cpython-314.pyc +0 -0
  239. package/pennyfarthing-dist/pf/jira/__pycache__/epic.cpython-314.pyc +0 -0
  240. package/pennyfarthing-dist/pf/jira/__pycache__/operations.cpython-314.pyc +0 -0
  241. package/pennyfarthing-dist/pf/jira/__pycache__/reconcile.cpython-314.pyc +0 -0
  242. package/pennyfarthing-dist/pf/jira/__pycache__/story.cpython-314.pyc +0 -0
  243. package/pennyfarthing-dist/pf/jira/__pycache__/sync.cpython-314.pyc +0 -0
  244. package/pennyfarthing-dist/pf/launch/__pycache__/__init__.cpython-314.pyc +0 -0
  245. package/pennyfarthing-dist/pf/launch/__pycache__/cli.cpython-314.pyc +0 -0
  246. package/pennyfarthing-dist/pf/prime/__pycache__/__init__.cpython-314.pyc +0 -0
  247. package/pennyfarthing-dist/pf/prime/__pycache__/cli.cpython-314.pyc +0 -0
  248. package/pennyfarthing-dist/pf/prime/__pycache__/loader.cpython-314.pyc +0 -0
  249. package/pennyfarthing-dist/pf/prime/__pycache__/models.cpython-314.pyc +0 -0
  250. package/pennyfarthing-dist/pf/prime/__pycache__/persona.cpython-314.pyc +0 -0
  251. package/pennyfarthing-dist/pf/prime/__pycache__/session.cpython-314.pyc +0 -0
  252. package/pennyfarthing-dist/pf/prime/__pycache__/tiers.cpython-314.pyc +0 -0
  253. package/pennyfarthing-dist/pf/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  254. package/pennyfarthing-dist/pf/session/__pycache__/__init__.cpython-314.pyc +0 -0
  255. package/pennyfarthing-dist/pf/session/__pycache__/cli.cpython-314.pyc +0 -0
  256. package/pennyfarthing-dist/pf/settings/__pycache__/__init__.cpython-314.pyc +0 -0
  257. package/pennyfarthing-dist/pf/settings/__pycache__/cli.cpython-314.pyc +0 -0
  258. package/pennyfarthing-dist/pf/settings/__pycache__/settings.cpython-314.pyc +0 -0
  259. package/pennyfarthing-dist/pf/settings/settings.py +44 -8
  260. package/pennyfarthing-dist/pf/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
  261. package/pennyfarthing-dist/pf/sprint/__pycache__/archive.cpython-314.pyc +0 -0
  262. package/pennyfarthing-dist/pf/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
  263. package/pennyfarthing-dist/pf/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  264. package/pennyfarthing-dist/pf/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
  265. package/pennyfarthing-dist/pf/sprint/__pycache__/epic_update.cpython-314.pyc +0 -0
  266. package/pennyfarthing-dist/pf/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  267. package/pennyfarthing-dist/pf/sprint/__pycache__/status.cpython-314.pyc +0 -0
  268. package/pennyfarthing-dist/pf/sprint/__pycache__/story_add.cpython-314.pyc +0 -0
  269. package/pennyfarthing-dist/pf/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
  270. package/pennyfarthing-dist/pf/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
  271. package/pennyfarthing-dist/pf/sprint/__pycache__/validate_cmd.cpython-314.pyc +0 -0
  272. package/pennyfarthing-dist/pf/sprint/__pycache__/validator.cpython-314.pyc +0 -0
  273. package/pennyfarthing-dist/pf/sprint/__pycache__/work.cpython-314.pyc +0 -0
  274. package/pennyfarthing-dist/pf/sprint/__pycache__/yaml_io.cpython-314.pyc +0 -0
  275. package/pennyfarthing-dist/pf/sprint/story_finish.py +14 -2
  276. package/pennyfarthing-dist/pf/sprint/validator.py +7 -7
  277. package/pennyfarthing-dist/pf/tests/__pycache__/__init__.cpython-314.pyc +0 -0
  278. package/pennyfarthing-dist/pf/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
  279. package/pennyfarthing-dist/pf/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
  280. package/pennyfarthing-dist/pf/tests/test_sprint_validator.py +44 -0
  281. package/pennyfarthing-dist/pf/theme/__pycache__/__init__.cpython-314.pyc +0 -0
  282. package/pennyfarthing-dist/pf/theme/__pycache__/cli.cpython-314.pyc +0 -0
  283. package/pennyfarthing-dist/pf/validate/__pycache__/__init__.cpython-314.pyc +0 -0
  284. package/pennyfarthing-dist/pf/validate/__pycache__/cli.cpython-314.pyc +0 -0
  285. package/pennyfarthing-dist/pf/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
  286. package/pennyfarthing-dist/pf/workflow/__pycache__/cli.cpython-314.pyc +0 -0
  287. package/pennyfarthing-dist/pf/workflow/__pycache__/helpers.cpython-314.pyc +0 -0
  288. package/pennyfarthing-dist/pf/workflow/__pycache__/scale.cpython-314.pyc +0 -0
  289. package/pennyfarthing-dist/pf/workflow/__pycache__/state.cpython-314.pyc +0 -0
  290. package/pennyfarthing-dist/scripts/lib/find-root.sh +1 -1
  291. package/pennyfarthing-dist/scripts/misc/migrate_bmad_workflow.py +0 -1
  292. package/pennyfarthing-dist/scripts/portraits/generate-portraits.py +13 -13
  293. package/pennyfarthing-dist/scripts/workflow/check.py +4 -6
  294. package/pennyfarthing-dist/scripts/workflow/complete-step.py +2 -2
  295. package/pennyfarthing-dist/skills/skill-registry.yaml +19 -0
  296. package/pennyfarthing-dist/workflows/tdd-tandem.yaml +15 -2
  297. package/packages/core/dist/workflow/__test_context_watch__/.session/.tandem-turn-counter +0 -1
  298. package/packages/core/dist/workflow/__test_context_watch__/.session/95-6-session.md +0 -3
  299. package/packages/core/dist/workflow/__test_context_watch__/.session/95-6-tandem-architect.md +0 -6
  300. package/packages/core/dist/workflow/__test_file_watch__/.session/95-4-tandem-architect.md +0 -6
  301. package/packages/core/dist/workflow/__test_file_watch__/workdir/trigger.ts +0 -1
  302. package/packages/core/dist/workflow/__test_tool_watch__/.session/95-5-tandem-architect.md +0 -6
  303. package/packages/core/dist/workflow/__test_tool_watch__/.session/95-5-tandem-toolcalls.jsonl +0 -1
  304. package/pennyfarthing-dist/pf/bikerack/changed_panel.py +0 -201
@@ -11,7 +11,27 @@ import yaml
11
11
  from pf.common.config import get_project_root, load_pennyfarthing_config
12
12
 
13
13
  # Top-level keys to show in `pf settings show` (skip layout/panel blobs)
14
- SHOW_KEYS = ("theme", "workflow", "jira", "display", "split", "last_panel")
14
+ SHOW_KEYS = ("theme", "bell_mode", "relay_mode", "permission_mode", "workflow", "jira", "display", "split", "last_panel")
15
+
16
+ # Default values for all known settings
17
+ DEFAULTS: dict[str, Any] = {
18
+ "theme": "firefly",
19
+ "bell_mode": "standard",
20
+ "relay_mode": False,
21
+ "permission_mode": "standard",
22
+ "workflow": {
23
+ "bell_mode": False,
24
+ "git_monitor": False,
25
+ "relay_mode": False,
26
+ "permission_mode": "standard",
27
+ "pr_mode": "draft",
28
+ "pr_merge": "auto",
29
+ },
30
+ "display": {
31
+ "colorPreset": "catppuccin",
32
+ },
33
+ "last_panel": "sprint",
34
+ }
15
35
 
16
36
 
17
37
  def _coerce_value(value: str) -> Any:
@@ -74,25 +94,41 @@ def set_setting(key: str, value: str) -> dict:
74
94
  return config
75
95
 
76
96
 
97
+ def _deep_merge(base: dict, override: dict) -> dict:
98
+ """Deep-merge override into base, returning a new dict."""
99
+ result = dict(base)
100
+ for k, v in override.items():
101
+ if k in result and isinstance(result[k], dict) and isinstance(v, dict):
102
+ result[k] = _deep_merge(result[k], v)
103
+ else:
104
+ result[k] = v
105
+ return result
106
+
107
+
77
108
  def show_settings() -> str:
78
- """Format interesting top-level settings for display."""
109
+ """Format all settings with defaults, annotating which are user-set."""
79
110
  config = load_pennyfarthing_config()
111
+ merged = _deep_merge(DEFAULTS, config)
80
112
  lines: list[str] = []
81
113
 
82
114
  for key in SHOW_KEYS:
83
- if key not in config:
115
+ if key not in merged:
84
116
  continue
85
- val = config[key]
117
+ val = merged[key]
118
+ is_set = key in config
119
+ suffix = "" if is_set else " # (default)"
86
120
  if isinstance(val, dict):
87
121
  lines.append(f"{key}:")
122
+ user_dict = config.get(key, {}) if isinstance(config.get(key), dict) else {}
88
123
  for k, v in val.items():
124
+ k_suffix = "" if k in user_dict else " # (default)"
89
125
  if isinstance(v, dict):
90
126
  nested = yaml.dump({k: v}, default_flow_style=False, sort_keys=False)
91
- for line in nested.rstrip().split("\n"):
92
- lines.append(f" {line}")
127
+ for i, line in enumerate(nested.rstrip().split("\n")):
128
+ lines.append(f" {line}{k_suffix if i == 0 else ''}")
93
129
  else:
94
- lines.append(f" {k}: {v}")
130
+ lines.append(f" {k}: {v}{k_suffix}")
95
131
  else:
96
- lines.append(f"{key}: {val}")
132
+ lines.append(f"{key}: {val}{suffix}")
97
133
 
98
134
  return "\n".join(lines) if lines else "(no settings found)"
@@ -133,10 +133,14 @@ def finish_story(
133
133
  )
134
134
 
135
135
  if dry_run:
136
+ from pf.common.pr_config import get_pr_merge_mode
137
+
136
138
  steps.append({"step": 1, "action": f"Archive session → {archive_dir / archive_name}"})
137
139
  if dialogue_path.exists():
138
140
  steps.append({"step": "1b", "action": f"Archive dialogue → {archive_dir / dialogue_archive_name}"})
139
- if pr_number:
141
+ if pr_number and get_pr_merge_mode() == "human":
142
+ steps.append({"step": 2, "action": f"PR #{pr_number} — waiting for human review and merge"})
143
+ elif pr_number:
140
144
  steps.append({"step": 2, "action": f"Merge PR #{pr_number} (squash, delete branch)"})
141
145
  else:
142
146
  steps.append({"step": 2, "action": "No PR to merge"})
@@ -162,7 +166,15 @@ def finish_story(
162
166
  steps.append({"step": "1b", "action": "archive_dialogue", "dest": str(dialogue_dest)})
163
167
 
164
168
  # --- Step 2: Merge PR ---
165
- if pr_number:
169
+ from pf.common.pr_config import get_pr_merge_mode
170
+
171
+ pr_merge_mode = get_pr_merge_mode()
172
+ if pr_number and pr_merge_mode == "human":
173
+ steps.append({
174
+ "step": 2, "action": "merge_pr", "pr": pr_number,
175
+ "mode": "human", "message": f"PR #{pr_number} ready for human review and merge",
176
+ })
177
+ elif pr_number:
166
178
  result = _run(["gh", "pr", "merge", pr_number, "--squash", "--delete-branch"])
167
179
  if result.returncode == 0:
168
180
  steps.append({"step": 2, "action": "merge_pr", "pr": pr_number})
@@ -66,11 +66,11 @@ class ValidationResult:
66
66
 
67
67
  VALID_SPRINT_STATUSES = {"active", "closed"}
68
68
  VALID_STORY_STATUSES = {"backlog", "ready", "in_progress", "done", "canceled", "planning"}
69
- JIRA_KEY_PATTERN = re.compile(r"^MSSCI-\d{5}$")
69
+ JIRA_KEY_PATTERN = re.compile(r"^[A-Z][A-Z0-9_]+-\d+(\s*/\s*[A-Z][A-Z0-9_]+-\d+)*$")
70
70
  ISO_DATE_PATTERN = re.compile(r"^\d{4}-\d{2}-\d{2}$")
71
71
 
72
72
  # Required fields for sprint section
73
- REQUIRED_SPRINT_FIELDS = {"number", "jira_sprint_id", "goal", "start_date", "end_date", "status"}
73
+ REQUIRED_SPRINT_FIELDS = {"goal", "start_date", "end_date", "status"}
74
74
 
75
75
  # Required fields for story
76
76
  REQUIRED_STORY_FIELDS = {"id", "title", "status", "points"}
@@ -103,7 +103,7 @@ def validate_sprint(data: dict[str, Any]) -> ValidationResult:
103
103
  """Validate sprint-level structure and fields.
104
104
 
105
105
  Validates:
106
- - Required fields present (number, jira_sprint_id, goal, start_date, end_date, status)
106
+ - Required fields present (goal, start_date, end_date, status)
107
107
  - status is valid value (active, closed)
108
108
  - dates are ISO format
109
109
 
@@ -159,7 +159,7 @@ def validate_story(story: dict[str, Any], epic_id: str, story_index: int = 0) ->
159
159
  - Required fields present (id, title, status, points)
160
160
  - status is valid value (backlog, ready, in_progress, done, canceled)
161
161
  - points is numeric
162
- - jira key follows pattern MSSCI-NNNNN if present
162
+ - jira key follows PROJECT-NUMBER pattern if present
163
163
  - branch follows convention if present
164
164
 
165
165
  Args:
@@ -204,7 +204,7 @@ def validate_story(story: dict[str, Any], epic_id: str, story_index: int = 0) ->
204
204
  jira_key = str(story["jira"])
205
205
  if not JIRA_KEY_PATTERN.match(jira_key):
206
206
  result.add_error(
207
- f"Invalid Jira key format '{jira_key}'. Expected MSSCI-NNNNN",
207
+ f"Invalid Jira key format '{jira_key}'. Expected PROJECT-NUMBER format (e.g., DPGD-17, MSSCI-12345)",
208
208
  f"{base_path}.jira",
209
209
  )
210
210
 
@@ -286,7 +286,7 @@ def validate_epic_shard(epic: dict[str, Any]) -> ValidationResult:
286
286
  - stories is a list
287
287
  - Each story has required fields (id, title, points, status)
288
288
  - No duplicate story IDs within the epic
289
- - jira key follows MSSCI-NNNNN pattern if present
289
+ - jira key follows PROJECT-NUMBER pattern if present
290
290
 
291
291
  Args:
292
292
  epic: Epic shard dict to validate
@@ -328,7 +328,7 @@ def validate_epic_shard(epic: dict[str, Any]) -> ValidationResult:
328
328
  jira_key = str(epic["jira"])
329
329
  if not JIRA_KEY_PATTERN.match(jira_key):
330
330
  result.add_error(
331
- f"Invalid Jira key format '{jira_key}'. Expected MSSCI-NNNNN",
331
+ f"Invalid Jira key format '{jira_key}'. Expected PROJECT-NUMBER format (e.g., DPGD-17, MSSCI-12345)",
332
332
  "epic.jira",
333
333
  )
334
334
 
@@ -288,6 +288,50 @@ class TestStoryValidation:
288
288
 
289
289
  assert result.valid is True
290
290
 
291
+ def test_short_project_key_passes(self) -> None:
292
+ """Short project key with fewer digits should pass (e.g., DPGD-17)."""
293
+ story = {
294
+ "id": "63-1",
295
+ "title": "Test",
296
+ "status": "backlog",
297
+ "points": 3,
298
+ "jira": "DPGD-17",
299
+ }
300
+
301
+ result = validate_story(story, "epic-63")
302
+
303
+ assert result.valid is True
304
+
305
+ def test_compound_jira_key_passes(self) -> None:
306
+ """Compound Jira key with slash separator should pass (e.g., DPGD-10 / DPGD-17)."""
307
+ story = {
308
+ "id": "63-1",
309
+ "title": "Test",
310
+ "status": "backlog",
311
+ "points": 3,
312
+ "jira": "DPGD-10 / DPGD-17",
313
+ }
314
+
315
+ result = validate_story(story, "epic-63")
316
+
317
+ assert result.valid is True
318
+
319
+ def test_sprint_without_number_and_jira_sprint_id_passes(self) -> None:
320
+ """Sprint without number and jira_sprint_id should pass (BMAD-imported sprints)."""
321
+ data = {
322
+ "sprint": {
323
+ "goal": "BMAD imported sprint",
324
+ "start_date": "2026-01-20",
325
+ "end_date": "2026-02-02",
326
+ "status": "active",
327
+ },
328
+ "epics": [],
329
+ }
330
+
331
+ result = validate_sprint(data)
332
+
333
+ assert result.valid is True
334
+
291
335
  def test_error_path_includes_epic_context(self) -> None:
292
336
  """Error path should include epic ID for context."""
293
337
  story = {"id": "63-1", "title": "Test", "points": 3} # Missing status
@@ -28,7 +28,7 @@ if [[ -n "$_caller_script" ]]; then
28
28
  _pkg="${_pkg%/pennyfarthing-dist}"
29
29
  _pkg="${_pkg%/.pennyfarthing}"
30
30
  if [[ "$_pkg" == */node_modules/* ]]; then
31
- PROJECT_ROOT="${_pkg%/node_modules/*}"
31
+ PROJECT_ROOT="${_pkg%%/node_modules/*}"
32
32
  elif [[ -d "$_pkg/.pennyfarthing" ]]; then
33
33
  PROJECT_ROOT="$_pkg"
34
34
  elif [[ -d "$_pkg/../.pennyfarthing" ]]; then
@@ -14,7 +14,6 @@ Usage: python migrate_bmad_workflow.py [--dry-run] <source-dir> [target-dir]
14
14
 
15
15
  import argparse
16
16
  import re
17
- import shutil
18
17
  import sys
19
18
  from pathlib import Path
20
19
 
@@ -21,8 +21,8 @@ import argparse
21
21
  import os
22
22
  import sys
23
23
  import warnings
24
- from pathlib import Path
25
24
  from datetime import datetime
25
+ from pathlib import Path
26
26
 
27
27
  # Suppress progress bars before importing torch/diffusers
28
28
  os.environ["TQDM_DISABLE"] = "1"
@@ -38,7 +38,7 @@ except ImportError:
38
38
 
39
39
  try:
40
40
  import torch
41
- from diffusers import StableDiffusionXLPipeline, DPMSolverMultistepScheduler
41
+ from diffusers import DPMSolverMultistepScheduler, StableDiffusionXLPipeline
42
42
  from diffusers.utils import logging as diffusers_logging
43
43
  from PIL import Image
44
44
  HAS_TORCH = True
@@ -188,7 +188,7 @@ def generate_portrait_filename(short_name: str, ocean: dict) -> str:
188
188
 
189
189
  def parse_theme_file(theme_path: Path) -> dict:
190
190
  """Parse theme YAML file to extract visual prompts for each agent."""
191
- with open(theme_path, "r", encoding="utf-8") as f:
191
+ with open(theme_path, encoding="utf-8") as f:
192
192
  data = yaml.safe_load(f)
193
193
 
194
194
  theme_metadata = data.get("theme", {})
@@ -310,7 +310,7 @@ def load_pipeline(engine_name: str):
310
310
  }
311
311
  else:
312
312
  model_id = engine["model_id"]
313
- print(f"\nLoading SDXL model on MPS...")
313
+ print("\nLoading SDXL model on MPS...")
314
314
  print(f" Model: {model_id}")
315
315
  print(" (First run downloads ~6.5GB model)")
316
316
 
@@ -369,14 +369,14 @@ def generate_portrait(pipeline_components: dict, prompt: str, engine_name: str,
369
369
  pipe = pipeline_components["pipe"]
370
370
  generator = torch.Generator().manual_seed(seed)
371
371
 
372
- kwargs = dict(
373
- prompt=prompt,
374
- width=gen_size,
375
- height=gen_size,
376
- num_inference_steps=engine["num_inference_steps"],
377
- guidance_scale=engine["guidance_scale"],
378
- generator=generator,
379
- )
372
+ kwargs = {
373
+ "prompt": prompt,
374
+ "width": gen_size,
375
+ "height": gen_size,
376
+ "num_inference_steps": engine["num_inference_steps"],
377
+ "guidance_scale": engine["guidance_scale"],
378
+ "generator": generator,
379
+ }
380
380
  if engine["supports_negative_prompt"]:
381
381
  kwargs["negative_prompt"] = "color, grayscale, photorealistic, blurry, deformed"
382
382
 
@@ -445,7 +445,7 @@ def main():
445
445
  print(f" Searched: {CUSTOM_THEMES_DIR}")
446
446
  sys.exit(1)
447
447
 
448
- print(f"Theme sources:")
448
+ print("Theme sources:")
449
449
  print(f" Built-in: {BUILTIN_THEMES_DIR}")
450
450
  for src in pkg_sources:
451
451
  print(f" Package: {src}")
@@ -22,13 +22,11 @@ Returns exit code 0 on all passing, non-zero on any failure.
22
22
  import argparse
23
23
  import asyncio
24
24
  import json
25
- import os
26
25
  import shutil
27
26
  import sys
28
27
  from dataclasses import dataclass, field
29
28
  from enum import Enum
30
29
  from pathlib import Path
31
- from typing import Optional
32
30
 
33
31
 
34
32
  class CheckStatus(Enum):
@@ -41,8 +39,8 @@ class CheckStatus(Enum):
41
39
  class CheckResult:
42
40
  name: str
43
41
  status: CheckStatus
44
- command: Optional[str] = None
45
- message: Optional[str] = None
42
+ command: str | None = None
43
+ message: str | None = None
46
44
 
47
45
 
48
46
  @dataclass
@@ -105,12 +103,12 @@ class Colors:
105
103
  COLORS = Colors()
106
104
 
107
105
 
108
- def print_pass(name: str, command: Optional[str] = None) -> None:
106
+ def print_pass(name: str, command: str | None = None) -> None:
109
107
  suffix = f" ({command})" if command else ""
110
108
  print(f" {COLORS.GREEN}[PASS]{COLORS.NC} {name}{suffix}")
111
109
 
112
110
 
113
- def print_fail(name: str, command: Optional[str] = None) -> None:
111
+ def print_fail(name: str, command: str | None = None) -> None:
114
112
  suffix = f" ({command})" if command else ""
115
113
  print(f" {COLORS.RED}[FAIL]{COLORS.NC} {name}{suffix}")
116
114
 
@@ -15,7 +15,7 @@ If --step N provided, completes that specific step instead of current step.
15
15
  import argparse
16
16
  import re
17
17
  import sys
18
- from datetime import datetime, timezone
18
+ from datetime import UTC, datetime
19
19
  from pathlib import Path
20
20
 
21
21
  try:
@@ -231,7 +231,7 @@ def main() -> int:
231
231
  new_status = "completed" if completed_count >= step_count else "in_progress"
232
232
 
233
233
  # Update timestamp
234
- now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
234
+ now = datetime.now(UTC).strftime("%Y-%m-%dT%H:%M:%SZ")
235
235
 
236
236
  # Update session file content
237
237
  content = re.sub(
@@ -23,6 +23,25 @@ skills:
23
23
  keywords: [react, reflection, planning, tool-use, chain-of-thought]
24
24
  allowed_tools: [Read, Glob, Grep, Task] # Reference material - read-only
25
25
 
26
+ pf-bc:
27
+ name: pf-bc
28
+ description: Panel focus management for Cyclist — set, clear, save, and load panel layouts
29
+ category: tools
30
+ tags: [cyclist, panels, layout, bikerack]
31
+ version: "1.0.0"
32
+ prerequisites: []
33
+ examples:
34
+ - context: Focusing the Sprint panel
35
+ invocation: /bc sprint
36
+ - context: Saving current layout
37
+ invocation: /bc save my-layout
38
+ - context: Resetting panel focus
39
+ invocation: /bc reset
40
+ anti_patterns:
41
+ - Don't manually edit config.local.yaml to set panel focus — use this skill
42
+ related_skills: [pf-cyclist]
43
+ keywords: [bikerack, cyclist, panel, focus, layout, dockview]
44
+
26
45
  pf-changelog:
27
46
  name: pf-changelog
28
47
  description: Maintain changelogs following Keep a Changelog format with conventional commits