@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
@@ -0,0 +1,104 @@
1
+ /**
2
+ * TandemPanel - Tandem consultation dialogue viewer
3
+ *
4
+ * Story 86-11: Cyclist: Tandem dialogue panel
5
+ *
6
+ * Features:
7
+ * - Display tandem observation exchanges with agent portraits
8
+ * - Real-time updates via WebSocket
9
+ * - Outcome badges (applied/deferred/rejected)
10
+ * - Metrics summary (exchange count, token overhead, confidence)
11
+ * - Empty state when no tandem activity
12
+ */
13
+
14
+ import React, { useState } from 'react';
15
+ import { useTandemObservations } from '../../hooks/useTandemObservations';
16
+
17
+ // =============================================================================
18
+ // TandemPanel Component
19
+ // =============================================================================
20
+
21
+ export function TandemPanel(): React.ReactElement {
22
+ const { header, observations, metrics, isLoading, error } = useTandemObservations();
23
+ const [portraitError, setPortraitError] = useState(false);
24
+
25
+ const showEmptyState = !isLoading && !error && !header && observations.length === 0;
26
+
27
+ return (
28
+ <div className="tandem-panel">
29
+ {/* Error state */}
30
+ {error && (
31
+ <div data-testid="tandem-error" className="tandem-error">
32
+ Connection failed — tandem data unavailable
33
+ </div>
34
+ )}
35
+
36
+ {/* Observer header with portrait and role */}
37
+ {header && (
38
+ <div className="tandem-header">
39
+ {!portraitError ? (
40
+ <img
41
+ data-testid="observer-portrait"
42
+ src={`/portraits/${header.theme}/medium/${header.slug}.png`}
43
+ alt={header.character}
44
+ className="observer-portrait"
45
+ onError={() => setPortraitError(true)}
46
+ />
47
+ ) : (
48
+ <span data-testid="observer-portrait" className="observer-portrait-fallback" aria-hidden="true">🤖</span>
49
+ )}
50
+ <div className="tandem-header-info">
51
+ <span className="observer-name">{header.character}</span>
52
+ <span data-testid="observer-role-badge" className="observer-role-badge">
53
+ {header.observer}
54
+ </span>
55
+ <span data-testid="observer-phase" className="observer-phase">{header.phase}</span>
56
+ </div>
57
+ </div>
58
+ )}
59
+
60
+ {/* Empty state */}
61
+ {showEmptyState && (
62
+ <div data-testid="tandem-empty-state" className="tandem-empty-state">
63
+ No tandem activity
64
+ </div>
65
+ )}
66
+
67
+ {/* Observation cards */}
68
+ {observations.map((obs, i) => (
69
+ <div key={obs.timestamp + i} data-testid="tandem-observation" className="tandem-observation">
70
+ <div className="observation-time">{obs.time}</div>
71
+ <div className="observation-trigger">
72
+ <span className="trigger-scope">{obs.trigger.scope}</span>
73
+ <span className="trigger-detail">{obs.trigger.detail}</span>
74
+ </div>
75
+ <div className="observation-content">{obs.content}</div>
76
+ {obs.outcome && (
77
+ <span
78
+ data-testid="outcome-badge"
79
+ data-outcome={obs.outcome}
80
+ className={`outcome-badge outcome-${obs.outcome}`}
81
+ >
82
+ {obs.outcome}
83
+ </span>
84
+ )}
85
+ </div>
86
+ ))}
87
+
88
+ {/* Metrics summary */}
89
+ {metrics && (
90
+ <div data-testid="tandem-metrics" className="tandem-metrics">
91
+ <div className="metric">{metrics.exchangeCount} exchanges</div>
92
+ <div className="metric">{metrics.tokenOverhead}% overhead</div>
93
+ <div className="confidence-distribution">
94
+ <span>High: {metrics.confidenceDistribution.high}</span>
95
+ <span>Medium: {metrics.confidenceDistribution.medium}</span>
96
+ <span>Low: {metrics.confidenceDistribution.low}</span>
97
+ </div>
98
+ </div>
99
+ )}
100
+ </div>
101
+ );
102
+ }
103
+
104
+ export default TandemPanel;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * TaskTracker - Shared task list with dependencies
3
+ *
4
+ * Story 86-12: Cyclist: Native team panel
5
+ */
6
+
7
+ import React from 'react';
8
+ import type { TaskListItem } from '../../hooks/useTeamMembers';
9
+
10
+ export interface TaskTrackerProps {
11
+ tasks: TaskListItem[];
12
+ }
13
+
14
+ export function TaskTracker({ tasks }: TaskTrackerProps): React.ReactElement {
15
+ if (tasks.length === 0) {
16
+ return (
17
+ <div data-testid="task-tracker">
18
+ <div data-testid="tasks-empty">No tasks</div>
19
+ </div>
20
+ );
21
+ }
22
+
23
+ const completed = tasks.filter(t => t.status === 'completed').length;
24
+
25
+ return (
26
+ <div data-testid="task-tracker">
27
+ <div data-testid="task-progress-summary">
28
+ {completed} of {tasks.length} completed
29
+ </div>
30
+ {tasks.map(task => (
31
+ <div key={task.id} data-testid="task-item">
32
+ <span>{task.title}</span>
33
+ <span data-testid="task-owner">{task.owner ?? 'unassigned'}</span>
34
+ <span data-testid="task-status" data-status={task.status}>
35
+ {task.status}
36
+ </span>
37
+ {task.blockedBy && task.blockedBy.length > 0 && (
38
+ <span data-testid="task-blocked-indicator">
39
+ Blocked by: {task.blockedBy.join(', ')}
40
+ </span>
41
+ )}
42
+ </div>
43
+ ))}
44
+ </div>
45
+ );
46
+ }
47
+
48
+ export default TaskTracker;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * TeamPanel - Native team visualization panel
3
+ *
4
+ * Story 86-12: Cyclist: Native team panel
5
+ *
6
+ * Features:
7
+ * - Team member roster with persona portraits and status
8
+ * - Task list with completion progress and dependencies
9
+ * - Message feed between agents
10
+ * - Click agent to view output
11
+ * - Hidden when native teams not active
12
+ */
13
+
14
+ import React, { useState } from 'react';
15
+ import { useTeamMembers } from '../../hooks/useTeamMembers';
16
+ import { TeamRoster } from './TeamRoster';
17
+ import { TaskTracker } from './TaskTracker';
18
+ import { MessageFeed } from './MessageFeed';
19
+ import type { TeamMember } from '../../hooks/useTeamMembers';
20
+
21
+ export function TeamPanel(): React.ReactElement {
22
+ const { isActive, teamName, members, tasks, messages, isLoading, error } = useTeamMembers();
23
+ const [selectedMember, setSelectedMember] = useState<TeamMember | null>(null);
24
+
25
+ return (
26
+ <div data-testid="team-panel" className="team-panel">
27
+ {isLoading && (
28
+ <div data-testid="team-loading">Connecting to team...</div>
29
+ )}
30
+
31
+ {error && (
32
+ <div data-testid="team-error">Connection failed — team data unavailable</div>
33
+ )}
34
+
35
+ {!isActive && (
36
+ <div data-testid="team-empty-state">No active team</div>
37
+ )}
38
+
39
+ {teamName && (
40
+ <div data-testid="team-name">{teamName}</div>
41
+ )}
42
+
43
+ <TeamRoster members={members} onMemberClick={setSelectedMember} />
44
+ <TaskTracker tasks={tasks} />
45
+ <MessageFeed messages={messages} />
46
+
47
+ {selectedMember && (
48
+ <div data-testid="agent-output-view">
49
+ <div>{selectedMember.name}</div>
50
+ <div>Role: {selectedMember.agentType}</div>
51
+ <div>Status: {selectedMember.status}</div>
52
+ <button
53
+ data-testid="agent-output-close"
54
+ onClick={() => setSelectedMember(null)}
55
+ >
56
+ Close
57
+ </button>
58
+ </div>
59
+ )}
60
+ </div>
61
+ );
62
+ }
63
+
64
+ export default TeamPanel;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * TeamRoster - Agent member list with portraits and status
3
+ *
4
+ * Story 86-12: Cyclist: Native team panel
5
+ */
6
+
7
+ import React, { useState } from 'react';
8
+ import type { TeamMember } from '../../hooks/useTeamMembers';
9
+
10
+ export interface TeamRosterProps {
11
+ members: TeamMember[];
12
+ onMemberClick?: (member: TeamMember) => void;
13
+ }
14
+
15
+ function MemberPortrait({ member }: { member: TeamMember }) {
16
+ const [imgError, setImgError] = useState(false);
17
+ const src = member.portrait
18
+ ? `/portraits/${member.portrait.theme}/medium/${member.portrait.slug}.png`
19
+ : '';
20
+
21
+ return (
22
+ <div data-testid="member-portrait">
23
+ {!imgError && src ? (
24
+ <img
25
+ src={src}
26
+ alt={member.name}
27
+ onError={() => setImgError(true)}
28
+ />
29
+ ) : (
30
+ <span>🤖</span>
31
+ )}
32
+ </div>
33
+ );
34
+ }
35
+
36
+ export function TeamRoster({ members, onMemberClick }: TeamRosterProps): React.ReactElement {
37
+ if (members.length === 0) {
38
+ return (
39
+ <div data-testid="team-roster">
40
+ <div data-testid="roster-empty">No team members</div>
41
+ </div>
42
+ );
43
+ }
44
+
45
+ return (
46
+ <div data-testid="team-roster">
47
+ {members.map(member => (
48
+ <div
49
+ key={member.name}
50
+ data-testid="team-member"
51
+ role="button"
52
+ onClick={() => onMemberClick?.(member)}
53
+ >
54
+ <MemberPortrait member={member} />
55
+ <span>{member.name}</span>
56
+ <span data-testid="member-role" title={member.agentType} className={`role-badge role-${member.agentType}`} />
57
+ <span data-testid="member-status" data-status={member.status}>
58
+ {member.status}
59
+ </span>
60
+ {member.currentTask && <span>{member.currentTask}</span>}
61
+ </div>
62
+ ))}
63
+ </div>
64
+ );
65
+ }
66
+
67
+ export default TeamRoster;
@@ -0,0 +1,142 @@
1
+ /**
2
+ * TodoPanel - Task list with progress panel
3
+ *
4
+ * Extracted from ProgressPanel as part of MSSCI-14188.
5
+ * Shows todos grouped by status with progress bar.
6
+ *
7
+ * Story: MSSCI-14188 - Split Progress panel into Workflow, AC, and Todo panels
8
+ * Epic: epic-76 (Dockview Panel Migration)
9
+ */
10
+
11
+ import React from 'react';
12
+ import { Skeleton } from '@/components/ui/skeleton';
13
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
14
+ import { useTodos, TodoItem } from '../../hooks/useTodos';
15
+
16
+ // =============================================================================
17
+ // Todo Item Component
18
+ // =============================================================================
19
+
20
+ function TodoItemView({ todo }: { todo: TodoItem }): React.ReactElement {
21
+ const statusIcon = {
22
+ pending: '\u25CB',
23
+ in_progress: '\u25CF',
24
+ completed: '\u2713',
25
+ }[todo.status];
26
+
27
+ const statusClass = `todo-item todo-${todo.status}`;
28
+
29
+ // Use activeForm for in_progress (more descriptive), content for others
30
+ const displayText = todo.status === 'in_progress' && todo.activeForm
31
+ ? todo.activeForm
32
+ : todo.content;
33
+
34
+ return (
35
+ <div className={statusClass} data-testid={`todo-${todo.id}`}>
36
+ <span className="todo-status">{statusIcon}</span>
37
+ <span className="todo-subject">{displayText}</span>
38
+ {todo.blockedBy && todo.blockedBy.length > 0 && (
39
+ <TooltipProvider delayDuration={300}>
40
+ <Tooltip>
41
+ <TooltipTrigger asChild>
42
+ <span className="todo-blocked">
43
+ (blocked)
44
+ </span>
45
+ </TooltipTrigger>
46
+ <TooltipContent>{`Blocked by: ${todo.blockedBy.join(', ')}`}</TooltipContent>
47
+ </Tooltip>
48
+ </TooltipProvider>
49
+ )}
50
+ </div>
51
+ );
52
+ }
53
+
54
+ // =============================================================================
55
+ // TodoPanel Component
56
+ // =============================================================================
57
+
58
+ export function TodoPanel(): React.ReactElement {
59
+ const { todos, isLoading, error } = useTodos();
60
+
61
+ if (isLoading) {
62
+ return (
63
+ <div className="todo-panel loading" data-testid="todo-panel">
64
+ <div className="space-y-2 p-2">
65
+ <Skeleton className="h-3 w-full" />
66
+ <Skeleton className="h-4 w-5/6" />
67
+ <Skeleton className="h-4 w-3/4" />
68
+ <Skeleton className="h-4 w-4/5" />
69
+ </div>
70
+ </div>
71
+ );
72
+ }
73
+
74
+ if (error) {
75
+ return (
76
+ <div className="todo-panel error" data-testid="todo-panel">
77
+ <div className="error-message">{error.message}</div>
78
+ </div>
79
+ );
80
+ }
81
+
82
+ if (todos.length === 0) {
83
+ return (
84
+ <div className="todo-panel" data-testid="todo-panel">
85
+ <div className="placeholder">No active tasks</div>
86
+ </div>
87
+ );
88
+ }
89
+
90
+ // Group by status
91
+ const inProgress = todos.filter(t => t.status === 'in_progress');
92
+ const pending = todos.filter(t => t.status === 'pending');
93
+ const completed = todos.filter(t => t.status === 'completed');
94
+
95
+ // Calculate progress
96
+ const totalCount = todos.length;
97
+ const completedCount = completed.length;
98
+ const progressPercent = totalCount > 0 ? Math.round((completedCount / totalCount) * 100) : 0;
99
+
100
+ return (
101
+ <div className="todo-panel" data-testid="todo-panel">
102
+ <div className="todo-content">
103
+ <div className="progress-bar-container">
104
+ <div
105
+ className="progress-bar"
106
+ style={{ width: `${progressPercent}%` }}
107
+ />
108
+ <span className="progress-text">{completedCount}/{totalCount}</span>
109
+ </div>
110
+
111
+ {inProgress.length > 0 && (
112
+ <div className="todo-section">
113
+ <h4>In Progress</h4>
114
+ {inProgress.map(todo => (
115
+ <TodoItemView key={todo.id} todo={todo} />
116
+ ))}
117
+ </div>
118
+ )}
119
+
120
+ {pending.length > 0 && (
121
+ <div className="todo-section">
122
+ <h4>Pending</h4>
123
+ {pending.map(todo => (
124
+ <TodoItemView key={todo.id} todo={todo} />
125
+ ))}
126
+ </div>
127
+ )}
128
+
129
+ {completed.length > 0 && (
130
+ <div className="todo-section todo-completed">
131
+ <h4>Completed ({completed.length})</h4>
132
+ {completed.map(todo => (
133
+ <TodoItemView key={todo.id} todo={todo} />
134
+ ))}
135
+ </div>
136
+ )}
137
+ </div>
138
+ </div>
139
+ );
140
+ }
141
+
142
+ export default TodoPanel;
@@ -0,0 +1,224 @@
1
+ /**
2
+ * WorkflowPanel - BikeLane phase visualization panel
3
+ *
4
+ * Extracted from ProgressPanel as part of MSSCI-14188.
5
+ * Shows workflow type badge (TDD/BDD/Trivial) and phase progress.
6
+ * MSSCI-14300: Added stepped workflow "Step N of M" display.
7
+ * MSSCI-14301: Added available workflows discovery list.
8
+ *
9
+ * Story: MSSCI-14188 - Split Progress panel into Workflow, AC, and Todo panels
10
+ * Epic: epic-76 (Dockview Panel Migration)
11
+ */
12
+
13
+ import React, { useCallback } from 'react';
14
+ import { Badge } from '@/components/ui/badge';
15
+ import { Button } from '@/components/ui/button';
16
+ import { Skeleton } from '@/components/ui/skeleton';
17
+ import { useClaudeContext } from '../../contexts/ClaudeContext';
18
+ import { useStory } from '../../hooks/useStory';
19
+ import type { WorkflowPhase, AvailableWorkflow } from '../../../story-parser.js';
20
+
21
+ // =============================================================================
22
+ // Helper Functions
23
+ // =============================================================================
24
+
25
+ function getPhaseIcon(status: 'done' | 'current' | 'pending'): string {
26
+ switch (status) {
27
+ case 'done':
28
+ return '\u2713';
29
+ case 'current':
30
+ return '\u25CF';
31
+ case 'pending':
32
+ default:
33
+ return '\u25CB';
34
+ }
35
+ }
36
+
37
+ function formatWorkflowType(type: string | null): string {
38
+ if (!type) return '\u2014';
39
+
40
+ const upperTypes = ['tdd', 'bdd'];
41
+ if (upperTypes.includes(type.toLowerCase())) {
42
+ return type.toUpperCase();
43
+ }
44
+
45
+ return type.charAt(0).toUpperCase() + type.slice(1);
46
+ }
47
+
48
+ // =============================================================================
49
+ // Phase Step Component (for phased workflows)
50
+ // =============================================================================
51
+
52
+ function PhaseStep({ phase, isLast }: { phase: WorkflowPhase; isLast: boolean }): React.ReactElement {
53
+ const icon = getPhaseIcon(phase.status);
54
+ const statusClass = `phase-step ${phase.status}`;
55
+
56
+ return (
57
+ <>
58
+ <div className={statusClass}>
59
+ <span className="phase-icon">{icon}</span>
60
+ <span className="phase-label">{phase.label}</span>
61
+ </div>
62
+ {!isLast && <span className="phase-arrow">{'\u2192'}</span>}
63
+ </>
64
+ );
65
+ }
66
+
67
+ // =============================================================================
68
+ // Stepped Progress Component (for stepped workflows)
69
+ // =============================================================================
70
+
71
+ function SteppedProgress({ phases }: { phases: WorkflowPhase[] }): React.ReactElement {
72
+ const total = phases.length;
73
+ const currentIndex = phases.findIndex(p => p.status === 'current');
74
+ const doneCount = phases.filter(p => p.status === 'done').length;
75
+
76
+ // If all done, current step = total; otherwise use 1-based index of current
77
+ const currentStep = currentIndex >= 0 ? currentIndex + 1 : (doneCount === total ? total : 1);
78
+ const currentPhase = currentIndex >= 0 ? phases[currentIndex] : null;
79
+
80
+ return (
81
+ <div className="stepped-progress">
82
+ <span className="stepped-counter">Step {currentStep} of {total}</span>
83
+ {currentPhase && (
84
+ <span className="stepped-current-label">{currentPhase.label}</span>
85
+ )}
86
+ </div>
87
+ );
88
+ }
89
+
90
+ // =============================================================================
91
+ // Available Workflows List (MSSCI-14301)
92
+ // =============================================================================
93
+
94
+ function AvailableWorkflowsList({ workflows, onStart }: {
95
+ workflows: AvailableWorkflow[];
96
+ onStart?: (workflow: AvailableWorkflow) => void;
97
+ }): React.ReactElement {
98
+ return (
99
+ <div className="available-workflows">
100
+ <div className="available-workflows-header">
101
+ <span className="available-workflows-title">Available Workflows ({workflows.length})</span>
102
+ </div>
103
+ <div className="available-workflows-list">
104
+ {workflows.map((wf) => (
105
+ <div
106
+ key={wf.name}
107
+ className="workflow-entry"
108
+ data-testid="workflow-entry"
109
+ data-workflow-entry-type={wf.type}
110
+ >
111
+ <div className="workflow-entry-header">
112
+ <span className="workflow-entry-name">{wf.name}</span>
113
+ <Badge variant="outline" className="workflow-entry-type-badge">
114
+ {wf.type}
115
+ </Badge>
116
+ </div>
117
+ <div className="workflow-entry-description">{wf.description}</div>
118
+ {wf.type === 'stepped' && (
119
+ <div className="workflow-entry-hint">/workflow start {wf.name}</div>
120
+ )}
121
+ <div className="workflow-entry-footer">
122
+ <Button
123
+ variant="ghost"
124
+ size="sm"
125
+ className="workflow-start-button"
126
+ data-testid="workflow-start-button"
127
+ onClick={() => onStart?.(wf)}
128
+ >
129
+ Start
130
+ </Button>
131
+ </div>
132
+ </div>
133
+ ))}
134
+ </div>
135
+ </div>
136
+ );
137
+ }
138
+
139
+ // =============================================================================
140
+ // WorkflowPanel Component
141
+ // =============================================================================
142
+
143
+ export function WorkflowPanel(): React.ReactElement {
144
+ const { story, isLoading, error, availableWorkflows } = useStory();
145
+ const { send, isConnected } = useClaudeContext();
146
+
147
+ const handleStartWorkflow = useCallback((wf: AvailableWorkflow) => {
148
+ if (!isConnected) return;
149
+ send(`/workflow start ${wf.name}`);
150
+ }, [send, isConnected]);
151
+
152
+ if (isLoading) {
153
+ return (
154
+ <div className="workflow-panel loading" data-testid="workflow-panel">
155
+ <div className="space-y-2 p-2">
156
+ <Skeleton className="h-6 w-16 rounded-full" />
157
+ <div className="flex gap-2 items-center">
158
+ <Skeleton className="h-4 w-12" />
159
+ <Skeleton className="h-4 w-12" />
160
+ <Skeleton className="h-4 w-12" />
161
+ </div>
162
+ </div>
163
+ </div>
164
+ );
165
+ }
166
+
167
+ if (error) {
168
+ return (
169
+ <div className="workflow-panel error" data-testid="workflow-panel">
170
+ <div className="error-message">{error.message}</div>
171
+ </div>
172
+ );
173
+ }
174
+
175
+ const workflowType = story?.workflow ?? null;
176
+ const phases = story?.workflowPhases ?? null;
177
+ const isStepped = story?.workflowType === 'stepped';
178
+
179
+ if (!workflowType && (!phases || phases.length === 0)) {
180
+ // MSSCI-14301: Show available workflows when no active workflow
181
+ if (availableWorkflows && availableWorkflows.length > 0) {
182
+ return (
183
+ <div className="workflow-panel" data-testid="workflow-panel">
184
+ <AvailableWorkflowsList workflows={availableWorkflows} onStart={handleStartWorkflow} />
185
+ </div>
186
+ );
187
+ }
188
+ return (
189
+ <div className="workflow-panel" data-testid="workflow-panel">
190
+ <div className="placeholder">No active workflow</div>
191
+ </div>
192
+ );
193
+ }
194
+
195
+ const formattedType = formatWorkflowType(workflowType);
196
+
197
+ return (
198
+ <div className="workflow-panel" data-testid="workflow-panel">
199
+ <div className="workflow-content">
200
+ <Badge variant="secondary" className="workflow-type-badge" data-workflow-type={workflowType || ''}>
201
+ {formattedType}
202
+ </Badge>
203
+
204
+ {phases && phases.length > 0 && (
205
+ isStepped ? (
206
+ <SteppedProgress phases={phases} />
207
+ ) : (
208
+ <div className="phase-progress">
209
+ {phases.map((phase, index) => (
210
+ <PhaseStep
211
+ key={phase.name}
212
+ phase={phase}
213
+ isLast={index === phases.length - 1}
214
+ />
215
+ ))}
216
+ </div>
217
+ )
218
+ )}
219
+ </div>
220
+ </div>
221
+ );
222
+ }
223
+
224
+ export default WorkflowPanel;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Panel Components Index
3
+ *
4
+ * Story MSSCI-12717 - React Migration
5
+ * Story MSSCI-14188 - Split Progress panel into Workflow, AC, and Todo panels
6
+ */
7
+
8
+ export { MessagePanel } from './MessagePanel';
9
+ export { SprintPanel, EnhancedSprintPanel } from './SprintPanel';
10
+ export { GitPanel } from './GitPanel';
11
+ export { WorkflowPanel } from './WorkflowPanel';
12
+ export { ACPanel } from './ACPanel';
13
+ export { TodoPanel } from './TodoPanel';
14
+ export { BackgroundPanel } from './BackgroundPanel';
15
+ export { DiffsPanel } from './DiffsPanel';
16
+ export { DiffView } from './DiffView';
17
+ export { DebugPanel } from './DebugPanel';
18
+ export { SettingsPanel } from './SettingsPanel';
19
+ export { AuditLogPanel } from './AuditLogPanel';
20
+ export { ProgressPanel } from './ProgressPanel';
21
+ export { TandemPanel } from './TandemPanel';
22
+ // Legacy exports - kept for backwards compatibility and tests
23
+ export { AcceptanceCriteriaPanel, ConnectedAcceptanceCriteriaPanel } from './AcceptanceCriteriaPanel';
24
+ export { BikeLanePanel, ConnectedBikeLanePanel } from './BikeLanePanel';