@pennyfarthing/core 11.3.7 → 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 (405) hide show
  1. package/README.md +1 -1
  2. package/package.json +17 -16
  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/hooks/cyclist-pretooluse-hook.sh +0 -0
  230. package/pennyfarthing-dist/pf/hotspots/__pycache__/__init__.cpython-314.pyc +0 -0
  231. package/pennyfarthing-dist/pf/hotspots/__pycache__/analyze.cpython-314.pyc +0 -0
  232. package/pennyfarthing-dist/pf/hotspots/__pycache__/cli.cpython-314.pyc +0 -0
  233. package/pennyfarthing-dist/pf/hotspots/__pycache__/models.cpython-314.pyc +0 -0
  234. package/pennyfarthing-dist/pf/jira/__pycache__/__init__.cpython-314.pyc +0 -0
  235. package/pennyfarthing-dist/pf/jira/__pycache__/bidirectional.cpython-314.pyc +0 -0
  236. package/pennyfarthing-dist/pf/jira/__pycache__/claim.cpython-314.pyc +0 -0
  237. package/pennyfarthing-dist/pf/jira/__pycache__/cli.cpython-314.pyc +0 -0
  238. package/pennyfarthing-dist/pf/jira/__pycache__/client.cpython-314.pyc +0 -0
  239. package/pennyfarthing-dist/pf/jira/__pycache__/create.cpython-314.pyc +0 -0
  240. package/pennyfarthing-dist/pf/jira/__pycache__/epic.cpython-314.pyc +0 -0
  241. package/pennyfarthing-dist/pf/jira/__pycache__/operations.cpython-314.pyc +0 -0
  242. package/pennyfarthing-dist/pf/jira/__pycache__/reconcile.cpython-314.pyc +0 -0
  243. package/pennyfarthing-dist/pf/jira/__pycache__/story.cpython-314.pyc +0 -0
  244. package/pennyfarthing-dist/pf/jira/__pycache__/sync.cpython-314.pyc +0 -0
  245. package/pennyfarthing-dist/pf/launch/__pycache__/__init__.cpython-314.pyc +0 -0
  246. package/pennyfarthing-dist/pf/launch/__pycache__/cli.cpython-314.pyc +0 -0
  247. package/pennyfarthing-dist/pf/prime/__pycache__/__init__.cpython-314.pyc +0 -0
  248. package/pennyfarthing-dist/pf/prime/__pycache__/cli.cpython-314.pyc +0 -0
  249. package/pennyfarthing-dist/pf/prime/__pycache__/loader.cpython-314.pyc +0 -0
  250. package/pennyfarthing-dist/pf/prime/__pycache__/models.cpython-314.pyc +0 -0
  251. package/pennyfarthing-dist/pf/prime/__pycache__/persona.cpython-314.pyc +0 -0
  252. package/pennyfarthing-dist/pf/prime/__pycache__/session.cpython-314.pyc +0 -0
  253. package/pennyfarthing-dist/pf/prime/__pycache__/tiers.cpython-314.pyc +0 -0
  254. package/pennyfarthing-dist/pf/prime/__pycache__/workflow.cpython-314.pyc +0 -0
  255. package/pennyfarthing-dist/pf/session/__pycache__/__init__.cpython-314.pyc +0 -0
  256. package/pennyfarthing-dist/pf/session/__pycache__/cli.cpython-314.pyc +0 -0
  257. package/pennyfarthing-dist/pf/settings/__pycache__/__init__.cpython-314.pyc +0 -0
  258. package/pennyfarthing-dist/pf/settings/__pycache__/cli.cpython-314.pyc +0 -0
  259. package/pennyfarthing-dist/pf/settings/__pycache__/settings.cpython-314.pyc +0 -0
  260. package/pennyfarthing-dist/pf/settings/settings.py +44 -8
  261. package/pennyfarthing-dist/pf/sprint/__pycache__/__init__.cpython-314.pyc +0 -0
  262. package/pennyfarthing-dist/pf/sprint/__pycache__/archive.cpython-314.pyc +0 -0
  263. package/pennyfarthing-dist/pf/sprint/__pycache__/archive_epic.cpython-314.pyc +0 -0
  264. package/pennyfarthing-dist/pf/sprint/__pycache__/cli.cpython-314.pyc +0 -0
  265. package/pennyfarthing-dist/pf/sprint/__pycache__/epic_add.cpython-314.pyc +0 -0
  266. package/pennyfarthing-dist/pf/sprint/__pycache__/epic_update.cpython-314.pyc +0 -0
  267. package/pennyfarthing-dist/pf/sprint/__pycache__/loader.cpython-314.pyc +0 -0
  268. package/pennyfarthing-dist/pf/sprint/__pycache__/status.cpython-314.pyc +0 -0
  269. package/pennyfarthing-dist/pf/sprint/__pycache__/story_add.cpython-314.pyc +0 -0
  270. package/pennyfarthing-dist/pf/sprint/__pycache__/story_finish.cpython-314.pyc +0 -0
  271. package/pennyfarthing-dist/pf/sprint/__pycache__/story_update.cpython-314.pyc +0 -0
  272. package/pennyfarthing-dist/pf/sprint/__pycache__/validate_cmd.cpython-314.pyc +0 -0
  273. package/pennyfarthing-dist/pf/sprint/__pycache__/validator.cpython-314.pyc +0 -0
  274. package/pennyfarthing-dist/pf/sprint/__pycache__/work.cpython-314.pyc +0 -0
  275. package/pennyfarthing-dist/pf/sprint/__pycache__/yaml_io.cpython-314.pyc +0 -0
  276. package/pennyfarthing-dist/pf/sprint/story_finish.py +14 -2
  277. package/pennyfarthing-dist/pf/sprint/validator.py +7 -7
  278. package/pennyfarthing-dist/pf/tests/__pycache__/__init__.cpython-314.pyc +0 -0
  279. package/pennyfarthing-dist/pf/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
  280. package/pennyfarthing-dist/pf/tests/__pycache__/test_sprint_validator.cpython-314-pytest-9.0.2.pyc +0 -0
  281. package/pennyfarthing-dist/pf/tests/test_sprint_validator.py +44 -0
  282. package/pennyfarthing-dist/pf/theme/__pycache__/__init__.cpython-314.pyc +0 -0
  283. package/pennyfarthing-dist/pf/theme/__pycache__/cli.cpython-314.pyc +0 -0
  284. package/pennyfarthing-dist/pf/validate/__pycache__/__init__.cpython-314.pyc +0 -0
  285. package/pennyfarthing-dist/pf/validate/__pycache__/cli.cpython-314.pyc +0 -0
  286. package/pennyfarthing-dist/pf/workflow/__pycache__/__init__.cpython-314.pyc +0 -0
  287. package/pennyfarthing-dist/pf/workflow/__pycache__/cli.cpython-314.pyc +0 -0
  288. package/pennyfarthing-dist/pf/workflow/__pycache__/helpers.cpython-314.pyc +0 -0
  289. package/pennyfarthing-dist/pf/workflow/__pycache__/scale.cpython-314.pyc +0 -0
  290. package/pennyfarthing-dist/pf/workflow/__pycache__/state.cpython-314.pyc +0 -0
  291. package/pennyfarthing-dist/scripts/core/agent-session.sh +0 -0
  292. package/pennyfarthing-dist/scripts/core/check-context.sh +0 -0
  293. package/pennyfarthing-dist/scripts/core/dialogue-manager.sh +0 -0
  294. package/pennyfarthing-dist/scripts/core/pf.sh +0 -0
  295. package/pennyfarthing-dist/scripts/core/phase-check-start.sh +0 -0
  296. package/pennyfarthing-dist/scripts/core/prime.sh +0 -0
  297. package/pennyfarthing-dist/scripts/cyclist/is-cyclist.sh +0 -0
  298. package/pennyfarthing-dist/scripts/git/create-feature-branches.sh +0 -0
  299. package/pennyfarthing-dist/scripts/git/git-status-all.sh +0 -0
  300. package/pennyfarthing-dist/scripts/git/install-git-hooks.sh +0 -0
  301. package/pennyfarthing-dist/scripts/git/release.sh +0 -0
  302. package/pennyfarthing-dist/scripts/git/worktree-manager.sh +0 -0
  303. package/pennyfarthing-dist/scripts/health/drift-detection.sh +0 -0
  304. package/pennyfarthing-dist/scripts/hooks/bell-mode-hook.sh +0 -0
  305. package/pennyfarthing-dist/scripts/hooks/context-circuit-breaker.sh +0 -0
  306. package/pennyfarthing-dist/scripts/hooks/context-warning.sh +0 -0
  307. package/pennyfarthing-dist/scripts/hooks/cyclist-pretooluse-hook.sh +0 -0
  308. package/pennyfarthing-dist/scripts/hooks/dispatcher-template.sh +0 -0
  309. package/pennyfarthing-dist/scripts/hooks/otel-auto-config.sh +0 -0
  310. package/pennyfarthing-dist/scripts/hooks/post-merge.sh +0 -0
  311. package/pennyfarthing-dist/scripts/hooks/pre-commit.sh +0 -0
  312. package/pennyfarthing-dist/scripts/hooks/pre-edit-check.sh +0 -0
  313. package/pennyfarthing-dist/scripts/hooks/pre-push.sh +0 -0
  314. package/pennyfarthing-dist/scripts/hooks/question-reflector-check.sh +0 -0
  315. package/pennyfarthing-dist/scripts/hooks/question_reflector_check.py +0 -0
  316. package/pennyfarthing-dist/scripts/hooks/schema-validation.sh +0 -0
  317. package/pennyfarthing-dist/scripts/hooks/session-start.sh +0 -0
  318. package/pennyfarthing-dist/scripts/hooks/session-stop.sh +0 -0
  319. package/pennyfarthing-dist/scripts/hooks/sprint-yaml-validation.sh +0 -0
  320. package/pennyfarthing-dist/scripts/hooks/welcome-hook.sh +0 -0
  321. package/pennyfarthing-dist/scripts/jira/create-jira-epic.sh +0 -0
  322. package/pennyfarthing-dist/scripts/jira/create-jira-story.sh +0 -0
  323. package/pennyfarthing-dist/scripts/jira/jira-claim-story.sh +0 -0
  324. package/pennyfarthing-dist/scripts/jira/jira-reconcile.sh +0 -0
  325. package/pennyfarthing-dist/scripts/jira/jira-sync-story.sh +0 -0
  326. package/pennyfarthing-dist/scripts/jira/sync-epic-jira.sh +0 -0
  327. package/pennyfarthing-dist/scripts/lib/background-tasks.sh +0 -0
  328. package/pennyfarthing-dist/scripts/lib/checkpoint.sh +0 -0
  329. package/pennyfarthing-dist/scripts/lib/common.sh +0 -0
  330. package/pennyfarthing-dist/scripts/lib/env.sh +0 -0
  331. package/pennyfarthing-dist/scripts/lib/file-lock.sh +0 -0
  332. package/pennyfarthing-dist/scripts/lib/find-root.sh +1 -1
  333. package/pennyfarthing-dist/scripts/lib/logging.sh +0 -0
  334. package/pennyfarthing-dist/scripts/lib/retry.sh +0 -0
  335. package/pennyfarthing-dist/scripts/lib/run-pf.sh +0 -0
  336. package/pennyfarthing-dist/scripts/maintenance/migrate-theme-schema.mjs +0 -0
  337. package/pennyfarthing-dist/scripts/maintenance/sidecar-health.sh +0 -0
  338. package/pennyfarthing-dist/scripts/misc/add-short-names.sh +0 -0
  339. package/pennyfarthing-dist/scripts/misc/add_short_names.py +0 -0
  340. package/pennyfarthing-dist/scripts/misc/backlog.sh +0 -0
  341. package/pennyfarthing-dist/scripts/misc/check-status.sh +0 -0
  342. package/pennyfarthing-dist/scripts/misc/find-related-work.sh +0 -0
  343. package/pennyfarthing-dist/scripts/misc/generate-skill-docs.sh +0 -0
  344. package/pennyfarthing-dist/scripts/misc/log-skill-usage.sh +0 -0
  345. package/pennyfarthing-dist/scripts/misc/migrate-bmad-workflow.sh +0 -0
  346. package/pennyfarthing-dist/scripts/misc/migrate_bmad_workflow.py +0 -1
  347. package/pennyfarthing-dist/scripts/misc/repo-scan.sh +0 -0
  348. package/pennyfarthing-dist/scripts/misc/repo-utils.sh +0 -0
  349. package/pennyfarthing-dist/scripts/misc/run-ci.sh +0 -0
  350. package/pennyfarthing-dist/scripts/misc/run-timestamp.sh +0 -0
  351. package/pennyfarthing-dist/scripts/misc/session-cleanup.sh +0 -0
  352. package/pennyfarthing-dist/scripts/misc/skill-usage-report.sh +0 -0
  353. package/pennyfarthing-dist/scripts/misc/statusline.sh +0 -0
  354. package/pennyfarthing-dist/scripts/misc/uninstall.sh +0 -0
  355. package/pennyfarthing-dist/scripts/misc/validate-subagent-frontmatter.sh +0 -0
  356. package/pennyfarthing-dist/scripts/portraits/generate-portraits.py +13 -13
  357. package/pennyfarthing-dist/scripts/portraits/generate-portraits.sh +0 -0
  358. package/pennyfarthing-dist/scripts/portraits/generate-tandem-portraits.sh +0 -0
  359. package/pennyfarthing-dist/scripts/story/create-story.sh +0 -0
  360. package/pennyfarthing-dist/scripts/story/size-story.sh +0 -0
  361. package/pennyfarthing-dist/scripts/story/story-template.sh +0 -0
  362. package/pennyfarthing-dist/scripts/tests/check.test.sh +0 -0
  363. package/pennyfarthing-dist/scripts/tests/dev-story-workflow-import.test.sh +0 -0
  364. package/pennyfarthing-dist/scripts/tests/epics-and-stories-workflow-import.test.sh +0 -0
  365. package/pennyfarthing-dist/scripts/tests/handoff-phase-update.test.sh +0 -0
  366. package/pennyfarthing-dist/scripts/tests/implementation-readiness-workflow-import.test.sh +0 -0
  367. package/pennyfarthing-dist/scripts/tests/migrate-bmad-workflow.test.sh +0 -0
  368. package/pennyfarthing-dist/scripts/tests/prd-workflow-import.test.sh +0 -0
  369. package/pennyfarthing-dist/scripts/tests/project-context-workflow-import.test.sh +0 -0
  370. package/pennyfarthing-dist/scripts/tests/test-character-voice.sh +0 -0
  371. package/pennyfarthing-dist/scripts/tests/test-drift-detection.sh +0 -0
  372. package/pennyfarthing-dist/scripts/tests/test-post-merge-hook.sh +0 -0
  373. package/pennyfarthing-dist/scripts/tests/test-session-checkpoint.sh +0 -0
  374. package/pennyfarthing-dist/scripts/tests/test-solo-command.sh +0 -0
  375. package/pennyfarthing-dist/scripts/tests/ux-design-workflow-import.test.sh +0 -0
  376. package/pennyfarthing-dist/scripts/theme/list-themes.sh +0 -0
  377. package/pennyfarthing-dist/scripts/validation/validate-agent-schema.sh +0 -0
  378. package/pennyfarthing-dist/scripts/workflow/check.py +4 -6
  379. package/pennyfarthing-dist/scripts/workflow/check.sh +0 -0
  380. package/pennyfarthing-dist/scripts/workflow/complete-step.py +2 -2
  381. package/pennyfarthing-dist/scripts/workflow/finish-story.sh +0 -0
  382. package/pennyfarthing-dist/scripts/workflow/fix-session-phase.sh +0 -0
  383. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.py +0 -0
  384. package/pennyfarthing-dist/scripts/workflow/get-workflow-type.sh +0 -0
  385. package/pennyfarthing-dist/scripts/workflow/list-workflows.sh +0 -0
  386. package/pennyfarthing-dist/scripts/workflow/phase-owner.sh +0 -0
  387. package/pennyfarthing-dist/scripts/workflow/resume-workflow.sh +0 -0
  388. package/pennyfarthing-dist/scripts/workflow/show-workflow.sh +0 -0
  389. package/pennyfarthing-dist/scripts/workflow/start-workflow.sh +0 -0
  390. package/pennyfarthing-dist/scripts/workflow/workflow-status.sh +0 -0
  391. package/pennyfarthing-dist/skills/pf-story/scripts/create-story.sh +0 -0
  392. package/pennyfarthing-dist/skills/pf-story/scripts/size-story.sh +0 -0
  393. package/pennyfarthing-dist/skills/pf-story/scripts/story-template.sh +0 -0
  394. package/pennyfarthing-dist/skills/skill-registry.yaml +19 -0
  395. package/pennyfarthing-dist/workflows/release/steps/step-10-publish.md +41 -9
  396. package/pennyfarthing-dist/workflows/tdd-tandem.yaml +15 -2
  397. package/packages/core/dist/workflow/__test_context_watch__/.session/.tandem-turn-counter +0 -1
  398. package/packages/core/dist/workflow/__test_context_watch__/.session/95-6-session.md +0 -3
  399. package/packages/core/dist/workflow/__test_context_watch__/.session/95-6-tandem-architect.md +0 -6
  400. package/packages/core/dist/workflow/__test_file_watch__/.session/95-4-tandem-architect.md +0 -6
  401. package/packages/core/dist/workflow/__test_file_watch__/workdir/trigger.ts +0 -1
  402. package/packages/core/dist/workflow/__test_tool_watch__/.session/95-5-tandem-architect.md +0 -6
  403. package/packages/core/dist/workflow/__test_tool_watch__/.session/95-5-tandem-toolcalls.jsonl +0 -1
  404. package/pennyfarthing-dist/pf/bikerack/changed_panel.py +0 -201
  405. package/scripts/README.md +0 -41
@@ -0,0 +1,204 @@
1
+ /**
2
+ * useStatsStrip Hook
3
+ *
4
+ * React hook for fetching and subscribing to stats strip data.
5
+ * Story MSSCI-12699 - StatsStrip Component
6
+ *
7
+ * IPC DEPRECATED - Now uses WebSocket for all data:
8
+ * - /ws/context: Context percentage, used/total tokens
9
+ * - /ws/stats: Model name
10
+ * - /api/identity: PWD, Jira email, GitHub username (REST, fetched once)
11
+ */
12
+
13
+ import { useState, useEffect, useCallback, useRef } from 'react';
14
+
15
+ export interface ContextData {
16
+ percent: number;
17
+ used?: number;
18
+ total?: number;
19
+ }
20
+
21
+ export interface StatsData {
22
+ model: string | null;
23
+ }
24
+
25
+ export interface ProjectInfoData {
26
+ pwd: string;
27
+ jiraEmail: string | null;
28
+ githubUsername: string | null;
29
+ }
30
+
31
+ interface UseStatsStripResult {
32
+ context: ContextData | null;
33
+ stats: StatsData | null;
34
+ projectInfo: ProjectInfoData | null;
35
+ isLoading: boolean;
36
+ error: Error | null;
37
+ }
38
+
39
+ const WS_RECONNECT_DELAY = 2000;
40
+
41
+ export function useStatsStrip(): UseStatsStripResult {
42
+ const [context, setContext] = useState<ContextData | null>(null);
43
+ const [stats, setStats] = useState<StatsData | null>(null);
44
+ const [projectInfo, setProjectInfo] = useState<ProjectInfoData | null>(null);
45
+ const [isLoading, setIsLoading] = useState(true);
46
+ const [error, setError] = useState<Error | null>(null);
47
+
48
+ // Track load state for each source
49
+ const loadStateRef = useRef({
50
+ context: false,
51
+ stats: false,
52
+ projectInfo: false,
53
+ });
54
+
55
+ const checkLoadComplete = useCallback(() => {
56
+ const state = loadStateRef.current;
57
+ if (state.context && state.stats && state.projectInfo) {
58
+ setIsLoading(false);
59
+ }
60
+ }, []);
61
+
62
+ // WebSocket for /ws/context
63
+ useEffect(() => {
64
+ let ws: WebSocket | null = null;
65
+ let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
66
+ let mounted = true;
67
+
68
+ const connect = () => {
69
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
70
+ ws = new WebSocket(`${protocol}//${window.location.host}/ws/context`);
71
+
72
+ ws.onopen = () => {
73
+ console.log('[useStatsStrip] Context WebSocket connected');
74
+ };
75
+
76
+ ws.onmessage = (event) => {
77
+ try {
78
+ const data = JSON.parse(event.data);
79
+ if (data.type === 'init' || data.type === 'update') {
80
+ const ctx = data.context;
81
+ if (ctx) {
82
+ setContext({
83
+ percent: ctx.percent ?? 0,
84
+ used: ctx.tokens ?? undefined,
85
+ total: ctx.available ? (ctx.tokens ?? 0) + ctx.available : undefined,
86
+ });
87
+ }
88
+ if (!loadStateRef.current.context) {
89
+ loadStateRef.current.context = true;
90
+ checkLoadComplete();
91
+ }
92
+ }
93
+ } catch (err) {
94
+ console.error('[useStatsStrip] Failed to parse context message:', err);
95
+ }
96
+ };
97
+
98
+ ws.onclose = () => {
99
+ if (mounted) {
100
+ reconnectTimer = setTimeout(connect, WS_RECONNECT_DELAY);
101
+ }
102
+ };
103
+
104
+ ws.onerror = (err) => {
105
+ console.error('[useStatsStrip] Context WebSocket error:', err);
106
+ ws?.close();
107
+ };
108
+ };
109
+
110
+ connect();
111
+
112
+ return () => {
113
+ mounted = false;
114
+ if (reconnectTimer) clearTimeout(reconnectTimer);
115
+ ws?.close();
116
+ };
117
+ }, [checkLoadComplete]);
118
+
119
+ // WebSocket for /ws/stats
120
+ useEffect(() => {
121
+ let ws: WebSocket | null = null;
122
+ let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
123
+ let mounted = true;
124
+
125
+ const connect = () => {
126
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
127
+ ws = new WebSocket(`${protocol}//${window.location.host}/ws/stats`);
128
+
129
+ ws.onopen = () => {
130
+ console.log('[useStatsStrip] Stats WebSocket connected');
131
+ };
132
+
133
+ ws.onmessage = (event) => {
134
+ try {
135
+ const data = JSON.parse(event.data);
136
+ // Stats sends data directly (not wrapped in type)
137
+ setStats({ model: data.model ?? null });
138
+ // Also extract pwd from stats (updated on Bash tool completions)
139
+ if (data.pwd) {
140
+ setProjectInfo(prev => ({
141
+ pwd: data.pwd,
142
+ jiraEmail: prev?.jiraEmail ?? null,
143
+ githubUsername: prev?.githubUsername ?? null,
144
+ }));
145
+ }
146
+ if (!loadStateRef.current.stats) {
147
+ loadStateRef.current.stats = true;
148
+ checkLoadComplete();
149
+ }
150
+ } catch (err) {
151
+ console.error('[useStatsStrip] Failed to parse stats message:', err);
152
+ }
153
+ };
154
+
155
+ ws.onclose = () => {
156
+ if (mounted) {
157
+ reconnectTimer = setTimeout(connect, WS_RECONNECT_DELAY);
158
+ }
159
+ };
160
+
161
+ ws.onerror = (err) => {
162
+ console.error('[useStatsStrip] Stats WebSocket error:', err);
163
+ ws?.close();
164
+ };
165
+ };
166
+
167
+ connect();
168
+
169
+ return () => {
170
+ mounted = false;
171
+ if (reconnectTimer) clearTimeout(reconnectTimer);
172
+ ws?.close();
173
+ };
174
+ }, [checkLoadComplete]);
175
+
176
+ // REST for /api/identity (jiraEmail, githubUsername - fetched once)
177
+ // pwd comes from /ws/stats (updated on Bash tool completions)
178
+ useEffect(() => {
179
+ const fetchIdentity = async () => {
180
+ try {
181
+ const response = await fetch('/api/identity');
182
+ if (response.ok) {
183
+ const data = await response.json();
184
+ // Merge with existing projectInfo (preserve pwd from stats)
185
+ setProjectInfo(prev => ({
186
+ pwd: prev?.pwd ?? '',
187
+ jiraEmail: data.jiraEmail ?? null,
188
+ githubUsername: data.githubUsername ?? null,
189
+ }));
190
+ }
191
+ } catch (err) {
192
+ console.error('[useStatsStrip] Failed to fetch identity:', err);
193
+ setError(err instanceof Error ? err : new Error('Failed to fetch identity'));
194
+ } finally {
195
+ loadStateRef.current.projectInfo = true;
196
+ checkLoadComplete();
197
+ }
198
+ };
199
+
200
+ fetchIdentity();
201
+ }, [checkLoadComplete]);
202
+
203
+ return { context, stats, projectInfo, isLoading, error };
204
+ }
@@ -0,0 +1,135 @@
1
+ /**
2
+ * useStory Hook
3
+ *
4
+ * React hook for subscribing to story/sprint data.
5
+ * Story MSSCI-12717 - React Migration
6
+ * Story MSSCI-12860 - IPC to WebSocket Migration (Phase 1)
7
+ *
8
+ * Uses WebSocket /ws/story for real-time updates (no polling).
9
+ */
10
+
11
+ import { useState, useEffect, useRef } from 'react';
12
+
13
+ // Import types from story-parser for criteria and workflow
14
+ import type { CriteriaItem, WorkflowPhase, AvailableWorkflow } from '../../../story-parser.js';
15
+
16
+ export interface StoryData {
17
+ id: string;
18
+ title: string;
19
+ status?: string;
20
+ phase?: string;
21
+ workflow?: string;
22
+ points?: number;
23
+ epic?: string;
24
+ // MSSCI-12849: AC and BikeLane panel data
25
+ criteria?: CriteriaItem[] | null;
26
+ workflowPhases?: WorkflowPhase[] | null;
27
+ // MSSCI-14300: Distinguish phased vs stepped workflow rendering
28
+ workflowType?: string;
29
+ }
30
+
31
+ // Re-export types for panel components
32
+ export type { CriteriaItem, WorkflowPhase, AvailableWorkflow };
33
+
34
+ interface UseStoryResult {
35
+ story: StoryData | null;
36
+ isLoading: boolean;
37
+ error: Error | null;
38
+ // MSSCI-14301: Available workflows for discovery panel
39
+ availableWorkflows: AvailableWorkflow[] | null;
40
+ }
41
+
42
+ /** WebSocket message format from /ws/story */
43
+ interface StoryMessage {
44
+ type: 'init' | 'update';
45
+ id: string | null;
46
+ title: string | null;
47
+ phase?: string | null;
48
+ status?: string | null;
49
+ points?: number | null;
50
+ workflow?: WorkflowPhase[] | null;
51
+ workflowType?: string | null;
52
+ criteria?: CriteriaItem[] | null;
53
+ availableWorkflows?: AvailableWorkflow[] | null;
54
+ [key: string]: unknown;
55
+ }
56
+
57
+ /** Transform WebSocket message to StoryData */
58
+ function transformMessage(msg: StoryMessage): StoryData | null {
59
+ if (!msg.id) return null;
60
+ return {
61
+ id: msg.id,
62
+ title: msg.title ?? '',
63
+ status: msg.status ?? undefined,
64
+ phase: msg.phase ?? undefined,
65
+ points: msg.points ?? undefined,
66
+ criteria: msg.criteria,
67
+ workflowPhases: msg.workflow,
68
+ workflowType: msg.workflowType ?? undefined,
69
+ };
70
+ }
71
+
72
+ export function useStory(): UseStoryResult {
73
+ const [story, setStory] = useState<StoryData | null>(null);
74
+ const [isLoading, setIsLoading] = useState(true);
75
+ const [error, setError] = useState<Error | null>(null);
76
+ const [availableWorkflows, setAvailableWorkflows] = useState<AvailableWorkflow[] | null>(null);
77
+ const wsRef = useRef<WebSocket | null>(null);
78
+ const reconnectTimeoutRef = useRef<ReturnType<typeof setTimeout>>();
79
+
80
+ useEffect(() => {
81
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
82
+ const wsUrl = `${protocol}//${window.location.host}/ws/story`;
83
+
84
+ const connect = () => {
85
+ try {
86
+ wsRef.current = new WebSocket(wsUrl);
87
+
88
+ wsRef.current.onopen = () => {
89
+ console.debug('[useStory] WebSocket connected');
90
+ };
91
+
92
+ wsRef.current.onmessage = (event) => {
93
+ try {
94
+ const msg = JSON.parse(event.data) as StoryMessage;
95
+ if (msg.type === 'init' || msg.type === 'update') {
96
+ setStory(transformMessage(msg));
97
+ setAvailableWorkflows(msg.availableWorkflows ?? null);
98
+ setIsLoading(false);
99
+ setError(null);
100
+ }
101
+ } catch (err) {
102
+ console.error('[useStory] Failed to parse message:', err);
103
+ }
104
+ };
105
+
106
+ wsRef.current.onclose = () => {
107
+ console.debug('[useStory] WebSocket closed, reconnecting...');
108
+ reconnectTimeoutRef.current = setTimeout(connect, 2000);
109
+ };
110
+
111
+ wsRef.current.onerror = (err) => {
112
+ console.error('[useStory] WebSocket error:', err);
113
+ setError(new Error('WebSocket connection failed'));
114
+ };
115
+ } catch (err) {
116
+ console.error('[useStory] WebSocket init failed:', err);
117
+ setError(err instanceof Error ? err : new Error('Failed to connect'));
118
+ setIsLoading(false);
119
+ }
120
+ };
121
+
122
+ connect();
123
+
124
+ return () => {
125
+ if (reconnectTimeoutRef.current) {
126
+ clearTimeout(reconnectTimeoutRef.current);
127
+ }
128
+ if (wsRef.current) {
129
+ wsRef.current.close();
130
+ }
131
+ };
132
+ }, []);
133
+
134
+ return { story, isLoading, error, availableWorkflows };
135
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * useSubagentHelper Hook
3
+ *
4
+ * React hook for fetching themed helper data for subagent display.
5
+ * Story MSSCI-12776 - Theme-Aware Subagent Display Messages
6
+ *
7
+ * Combines persona lookup with helper resolution to provide
8
+ * themed helper information for subagent spans.
9
+ */
10
+
11
+ import { useState, useEffect } from 'react';
12
+ import { getAgentHelper, Helper } from '../utils/subagent-display';
13
+
14
+ export type { Helper };
15
+
16
+ export interface UseSubagentHelperResult {
17
+ helper: Helper | null;
18
+ isLoading: boolean;
19
+ error: Error | null;
20
+ }
21
+
22
+ export function useSubagentHelper(): UseSubagentHelperResult {
23
+ const [helper, setHelper] = useState<Helper | null>(null);
24
+ const [isLoading, setIsLoading] = useState(true);
25
+ const [error, setError] = useState<Error | null>(null);
26
+
27
+ useEffect(() => {
28
+ const fetchHelper = async (role: string) => {
29
+ try {
30
+ const helperData = await getAgentHelper(role);
31
+ setHelper(helperData);
32
+ setIsLoading(false);
33
+ } catch (err) {
34
+ setError(err instanceof Error ? err : new Error('Failed to fetch helper'));
35
+ setIsLoading(false);
36
+ }
37
+ };
38
+
39
+ // Connect to persona WebSocket for real-time updates
40
+ const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
41
+ const ws = new WebSocket(`${protocol}//${window.location.host}/ws/persona`);
42
+
43
+ ws.onmessage = (event) => {
44
+ try {
45
+ const persona = JSON.parse(event.data) as { role?: string } | null;
46
+ if (persona?.role) {
47
+ fetchHelper(persona.role);
48
+ }
49
+ } catch (err) {
50
+ setError(err instanceof Error ? err : new Error('Failed to parse persona'));
51
+ setIsLoading(false);
52
+ }
53
+ };
54
+
55
+ ws.onerror = () => {
56
+ setError(new Error('WebSocket connection failed'));
57
+ setIsLoading(false);
58
+ };
59
+
60
+ return () => ws.close();
61
+ }, []);
62
+
63
+ return { helper, isLoading, error };
64
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * useSyntaxHighlighter Hook
3
+ *
4
+ * Story MSSCI-13970: React hook for syntax highlighting code blocks.
5
+ * Extracted from js/components/message-view/syntax-highlighter.js
6
+ *
7
+ * Features:
8
+ * - Multi-language support (JS/TS, Python, Go, Rust, Bash)
9
+ * - Tokenizer-based highlighting (keyword, string, comment, number, function)
10
+ * - Theme-aware via CSS variables
11
+ * - Memoization for performance
12
+ */
13
+
14
+ import { useMemo } from 'react';
15
+ import { highlightCode, isSupportedLanguage } from '../utils/syntax';
16
+
17
+ export interface UseSyntaxHighlighterResult {
18
+ /** HTML string with syntax highlighting spans */
19
+ highlighted: string;
20
+ /** Whether the language is supported for highlighting */
21
+ isSupported: boolean;
22
+ }
23
+
24
+ /**
25
+ * React hook for syntax highlighting code blocks with memoization.
26
+ *
27
+ * @param code - Code to highlight (already HTML-escaped, or null)
28
+ * @param lang - Language identifier (js, ts, python, go, rust, bash, etc.)
29
+ * @returns Object with highlighted HTML and support status
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * const { highlighted, isSupported } = useSyntaxHighlighter(code, 'typescript');
34
+ * return <pre dangerouslySetInnerHTML={{ __html: highlighted }} />;
35
+ * ```
36
+ */
37
+ export function useSyntaxHighlighter(
38
+ code: string | null,
39
+ lang: string
40
+ ): UseSyntaxHighlighterResult {
41
+ const isSupported = useMemo(() => {
42
+ return lang ? isSupportedLanguage(lang) : false;
43
+ }, [lang]);
44
+
45
+ const highlighted = useMemo(() => {
46
+ if (!code) return '';
47
+ if (!lang || !isSupported) return code;
48
+ return highlightCode(code, lang);
49
+ }, [code, lang, isSupported]);
50
+
51
+ return { highlighted, isSupported };
52
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * useTabCompletion Hook
3
+ *
4
+ * React hook for slash command tab completion.
5
+ * Story MSSCI-12717 - React Migration
6
+ */
7
+
8
+ import { useState, useCallback, useMemo } from 'react';
9
+ import { SLASH_COMMANDS, trackCommandUsage, filterCommands as filterCommandsWithFrequency, SlashCommand } from '../utils/slash-commands';
10
+
11
+ interface CompletionState {
12
+ visible: boolean;
13
+ commands: SlashCommand[];
14
+ selectedIndex: number;
15
+ prefix: string;
16
+ }
17
+
18
+ interface UseTabCompletionResult {
19
+ state: CompletionState;
20
+ showCompletion: (prefix: string) => void;
21
+ hideCompletion: () => void;
22
+ updateCompletion: (prefix: string) => void;
23
+ navigateUp: () => void;
24
+ navigateDown: () => void;
25
+ selectCurrent: () => string | null;
26
+ isVisible: boolean;
27
+ }
28
+
29
+ export function useTabCompletion(commands?: SlashCommand[]): UseTabCompletionResult {
30
+ const allCommands = useMemo(() => commands || SLASH_COMMANDS, [commands]);
31
+
32
+ const [state, setState] = useState<CompletionState>({
33
+ visible: false,
34
+ commands: [],
35
+ selectedIndex: 0,
36
+ prefix: '',
37
+ });
38
+
39
+ const filterCommands = useCallback((prefix: string): SlashCommand[] => {
40
+ // Use frequency-aware filtering from slash-commands module
41
+ // Falls back to basic filtering if custom commands provided
42
+ if (commands) {
43
+ const search = prefix.toLowerCase();
44
+ return allCommands.filter(cmd =>
45
+ cmd.name.toLowerCase().startsWith(search)
46
+ );
47
+ }
48
+ return filterCommandsWithFrequency(prefix);
49
+ }, [allCommands, commands]);
50
+
51
+ const showCompletion = useCallback((prefix: string) => {
52
+ const filtered = filterCommands(prefix);
53
+ setState({
54
+ visible: true,
55
+ commands: filtered,
56
+ selectedIndex: 0,
57
+ prefix,
58
+ });
59
+ }, [filterCommands]);
60
+
61
+ const hideCompletion = useCallback(() => {
62
+ setState({
63
+ visible: false,
64
+ commands: [],
65
+ selectedIndex: 0,
66
+ prefix: '',
67
+ });
68
+ }, []);
69
+
70
+ const updateCompletion = useCallback((prefix: string) => {
71
+ const filtered = filterCommands(prefix);
72
+ if (filtered.length === 0) {
73
+ hideCompletion();
74
+ } else {
75
+ setState(prev => ({
76
+ ...prev,
77
+ commands: filtered,
78
+ selectedIndex: 0,
79
+ prefix,
80
+ }));
81
+ }
82
+ }, [filterCommands, hideCompletion]);
83
+
84
+ const navigateUp = useCallback(() => {
85
+ setState(prev => {
86
+ if (prev.commands.length === 0) return prev;
87
+ const newIndex = prev.selectedIndex <= 0
88
+ ? prev.commands.length - 1
89
+ : prev.selectedIndex - 1;
90
+ return { ...prev, selectedIndex: newIndex };
91
+ });
92
+ }, []);
93
+
94
+ const navigateDown = useCallback(() => {
95
+ setState(prev => {
96
+ if (prev.commands.length === 0) return prev;
97
+ const newIndex = prev.selectedIndex >= prev.commands.length - 1
98
+ ? 0
99
+ : prev.selectedIndex + 1;
100
+ return { ...prev, selectedIndex: newIndex };
101
+ });
102
+ }, []);
103
+
104
+ const selectCurrent = useCallback((): string | null => {
105
+ if (!state.visible || state.commands.length === 0) return null;
106
+ const selected = state.commands[state.selectedIndex];
107
+ hideCompletion();
108
+ if (selected?.name) {
109
+ trackCommandUsage(selected.name);
110
+ }
111
+ return selected?.name || null;
112
+ }, [state, hideCompletion]);
113
+
114
+ return {
115
+ state,
116
+ showCompletion,
117
+ hideCompletion,
118
+ updateCompletion,
119
+ navigateUp,
120
+ navigateDown,
121
+ selectCurrent,
122
+ isVisible: state.visible,
123
+ };
124
+ }