@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,219 @@
1
+ /**
2
+ * Syntax Highlighting Utilities
3
+ *
4
+ * Story MSSCI-13970: TypeScript syntax highlighting utilities extracted from
5
+ * js/components/message-view/syntax-highlighter.js
6
+ *
7
+ * Features:
8
+ * - Tokenizer-based highlighting (avoids regex conflicts)
9
+ * - Multi-language support: JS/TS, Python, Go, Rust, Bash
10
+ * - Theme-aware via CSS classes
11
+ */
12
+
13
+ // Keyword sets by language family
14
+ const jsKeywords = new Set([
15
+ 'const', 'let', 'var', 'function', 'return', 'if', 'else', 'for', 'while',
16
+ 'do', 'switch', 'case', 'break', 'continue', 'new', 'this', 'class',
17
+ 'extends', 'import', 'export', 'from', 'default', 'async', 'await', 'try',
18
+ 'catch', 'finally', 'throw', 'typeof', 'instanceof', 'in', 'of', 'true',
19
+ 'false', 'null', 'undefined', 'void',
20
+ ]);
21
+
22
+ const pyKeywords = new Set([
23
+ 'def', 'class', 'return', 'if', 'elif', 'else', 'for', 'while', 'break',
24
+ 'continue', 'import', 'from', 'as', 'try', 'except', 'finally', 'raise',
25
+ 'with', 'lambda', 'yield', 'True', 'False', 'None', 'and', 'or', 'not',
26
+ 'in', 'is', 'pass', 'self',
27
+ ]);
28
+
29
+ const goKeywords = new Set([
30
+ 'func', 'return', 'if', 'else', 'for', 'range', 'switch', 'case', 'break',
31
+ 'continue', 'go', 'defer', 'chan', 'select', 'type', 'struct', 'interface',
32
+ 'map', 'package', 'import', 'var', 'const', 'true', 'false', 'nil', 'make',
33
+ 'new', 'append', 'len', 'cap', 'error',
34
+ ]);
35
+
36
+ const rustKeywords = new Set([
37
+ 'fn', 'let', 'mut', 'return', 'if', 'else', 'for', 'while', 'loop', 'match',
38
+ 'break', 'continue', 'impl', 'struct', 'enum', 'trait', 'pub', 'use', 'mod',
39
+ 'crate', 'self', 'super', 'where', 'async', 'await', 'true', 'false', 'Some',
40
+ 'None', 'Ok', 'Err',
41
+ ]);
42
+
43
+ const shKeywords = new Set([
44
+ 'if', 'then', 'else', 'elif', 'fi', 'for', 'while', 'do', 'done', 'case',
45
+ 'esac', 'function', 'return', 'exit', 'export', 'source', 'alias', 'cd',
46
+ 'echo', 'printf', 'read', 'local',
47
+ ]);
48
+
49
+ /**
50
+ * Supported language identifiers for syntax highlighting.
51
+ */
52
+ export type SupportedLanguage =
53
+ | 'js' | 'javascript' | 'ts' | 'typescript' | 'jsx' | 'tsx'
54
+ | 'python' | 'py'
55
+ | 'go' | 'golang'
56
+ | 'rust' | 'rs'
57
+ | 'bash' | 'sh' | 'shell' | 'zsh';
58
+
59
+ /**
60
+ * Get keywords set for a language.
61
+ * @param lang - Language identifier
62
+ * @returns Keywords for the language, or empty set if unsupported
63
+ */
64
+ export function getKeywordsForLanguage(lang: string): Set<string> {
65
+ const language = lang.toLowerCase();
66
+
67
+ if (['js', 'javascript', 'ts', 'typescript', 'jsx', 'tsx'].includes(language)) {
68
+ return jsKeywords;
69
+ }
70
+ if (['python', 'py'].includes(language)) {
71
+ return pyKeywords;
72
+ }
73
+ if (['go', 'golang'].includes(language)) {
74
+ return goKeywords;
75
+ }
76
+ if (['rust', 'rs'].includes(language)) {
77
+ return rustKeywords;
78
+ }
79
+ if (['bash', 'sh', 'shell', 'zsh'].includes(language)) {
80
+ return shKeywords;
81
+ }
82
+
83
+ return new Set();
84
+ }
85
+
86
+ /**
87
+ * Check if a language is supported for syntax highlighting.
88
+ * @param lang - Language identifier
89
+ * @returns true if language is supported
90
+ */
91
+ export function isSupportedLanguage(lang: string): boolean {
92
+ return getKeywordsForLanguage(lang).size > 0;
93
+ }
94
+
95
+ /**
96
+ * Check if language uses // for comments.
97
+ * @param lang - Language identifier
98
+ * @returns true if language uses slash comments
99
+ */
100
+ function usesSlashComments(lang: string): boolean {
101
+ const language = lang.toLowerCase();
102
+ return ['js', 'javascript', 'ts', 'typescript', 'jsx', 'tsx', 'go', 'golang', 'rust', 'rs'].includes(language);
103
+ }
104
+
105
+ /**
106
+ * Check if language uses # for comments.
107
+ * @param lang - Language identifier
108
+ * @returns true if language uses hash comments
109
+ */
110
+ function usesHashComments(lang: string): boolean {
111
+ const language = lang.toLowerCase();
112
+ return ['python', 'py', 'bash', 'sh', 'shell', 'zsh'].includes(language);
113
+ }
114
+
115
+ /**
116
+ * Highlight a single line of code using tokenizer approach.
117
+ * @param line - Line to highlight (already HTML-escaped)
118
+ * @param keywords - Keywords set for the language
119
+ * @param lang - Language for comment detection
120
+ * @returns HTML with highlighting spans
121
+ */
122
+ function highlightLine(line: string, keywords: Set<string>, lang: string): string {
123
+ if (!line.trim()) return line;
124
+
125
+ // Handle full-line comments
126
+ const trimmed = line.trim();
127
+ if (usesSlashComments(lang) && trimmed.startsWith('//')) {
128
+ return `<span class="comment">${line}</span>`;
129
+ }
130
+ if (usesHashComments(lang) && trimmed.startsWith('#')) {
131
+ return `<span class="comment">${line}</span>`;
132
+ }
133
+
134
+ // Tokenize the line character by character
135
+ let result = '';
136
+ let i = 0;
137
+
138
+ while (i < line.length) {
139
+ // Check for HTML-escaped double quote string: &quot;...&quot;
140
+ if (line.slice(i, i + 6) === '&quot;') {
141
+ const endIdx = line.indexOf('&quot;', i + 6);
142
+ if (endIdx !== -1) {
143
+ result += `<span class="string">${line.slice(i, endIdx + 6)}</span>`;
144
+ i = endIdx + 6;
145
+ continue;
146
+ }
147
+ }
148
+
149
+ // Check for HTML-escaped single quote string: &#39;...&#39; or &#039;...&#039;
150
+ if (line.slice(i, i + 5) === '&#39;' || line.slice(i, i + 6) === '&#039;') {
151
+ const quoteLen = line.slice(i, i + 6) === '&#039;' ? 6 : 5;
152
+ const quote = line.slice(i, i + quoteLen);
153
+ const endIdx = line.indexOf(quote, i + quoteLen);
154
+ if (endIdx !== -1) {
155
+ result += `<span class="string">${line.slice(i, endIdx + quoteLen)}</span>`;
156
+ i = endIdx + quoteLen;
157
+ continue;
158
+ }
159
+ }
160
+
161
+ // Check for word (identifier/keyword)
162
+ if (/[a-zA-Z_]/.test(line[i])) {
163
+ let word = '';
164
+ while (i < line.length && /[a-zA-Z0-9_]/.test(line[i])) {
165
+ word += line[i];
166
+ i++;
167
+ }
168
+ if (keywords.has(word)) {
169
+ result += `<span class="keyword">${word}</span>`;
170
+ } else if (line[i] === '(') {
171
+ result += `<span class="function">${word}</span>`;
172
+ } else {
173
+ result += word;
174
+ }
175
+ continue;
176
+ }
177
+
178
+ // Check for number
179
+ if (/[0-9]/.test(line[i])) {
180
+ let num = '';
181
+ while (i < line.length && /[0-9.]/.test(line[i])) {
182
+ num += line[i];
183
+ i++;
184
+ }
185
+ result += `<span class="number">${num}</span>`;
186
+ continue;
187
+ }
188
+
189
+ // Default: pass through character
190
+ result += line[i];
191
+ i++;
192
+ }
193
+
194
+ return result;
195
+ }
196
+
197
+ /**
198
+ * Highlight code with syntax highlighting.
199
+ * @param code - The code to highlight (already HTML escaped)
200
+ * @param lang - Language identifier (js, ts, python, etc.)
201
+ * @returns HTML with syntax highlighting spans
202
+ */
203
+ export function highlightCode(code: string, lang: string): string {
204
+ if (!code || !lang) return code || '';
205
+
206
+ const keywords = getKeywordsForLanguage(lang);
207
+
208
+ // Process line by line
209
+ const lines = code.split('\n');
210
+ const highlightedLines = lines.map((line) => highlightLine(line, keywords, lang));
211
+
212
+ return highlightedLines.join('\n');
213
+ }
214
+
215
+ export default {
216
+ highlightCode,
217
+ getKeywordsForLanguage,
218
+ isSupportedLanguage,
219
+ };
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Tool Intent Summarizer
3
+ *
4
+ * Story 74-1: Generates human-readable summaries of tool use intent.
5
+ */
6
+
7
+ const MAX_LENGTH = 70;
8
+
9
+ /**
10
+ * Truncates a string to the max length, adding ellipsis if needed.
11
+ * Also removes newlines to ensure single-line output.
12
+ */
13
+ function truncate(str: string, maxLen: number = MAX_LENGTH - 10): string {
14
+ // Replace newlines with spaces for single-line output
15
+ const singleLine = str.replace(/\n/g, ' ');
16
+ if (singleLine.length <= maxLen) return singleLine;
17
+ return singleLine.slice(0, maxLen - 3) + '...';
18
+ }
19
+
20
+ /**
21
+ * Safely gets a string value from input, returning empty string for null/undefined.
22
+ */
23
+ function getString(input: Record<string, unknown> | null | undefined, key: string): string {
24
+ if (!input) return '';
25
+ const value = input[key];
26
+ if (value === null || value === undefined || value === '') return '';
27
+ return String(value);
28
+ }
29
+
30
+ /**
31
+ * Extracts the hostname from a URL.
32
+ */
33
+ function extractHostname(url: string): string {
34
+ try {
35
+ const parsed = new URL(url);
36
+ return parsed.hostname;
37
+ } catch {
38
+ return url;
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Detects special bash command patterns and returns a semantic summary.
44
+ */
45
+ function summarizeBashCommand(command: string): string {
46
+ const cmd = command.toLowerCase();
47
+
48
+ // Package install commands
49
+ if (
50
+ cmd.startsWith('npm install') ||
51
+ cmd.startsWith('npm i ') ||
52
+ cmd === 'npm i' ||
53
+ cmd.startsWith('pnpm install') ||
54
+ cmd.startsWith('pnpm add') ||
55
+ cmd.startsWith('yarn install') ||
56
+ cmd.startsWith('yarn add')
57
+ ) {
58
+ return 'Installing dependencies';
59
+ }
60
+
61
+ // Test commands
62
+ if (
63
+ cmd.startsWith('npm test') ||
64
+ cmd.startsWith('npm run test') ||
65
+ cmd.startsWith('pnpm test') ||
66
+ cmd.startsWith('pnpm run test') ||
67
+ cmd.startsWith('yarn test') ||
68
+ cmd.startsWith('vitest') ||
69
+ cmd.startsWith('jest')
70
+ ) {
71
+ return 'Running tests';
72
+ }
73
+
74
+ // Build commands
75
+ if (
76
+ cmd.startsWith('npm run build') ||
77
+ cmd.startsWith('pnpm run build') ||
78
+ cmd.startsWith('pnpm build') ||
79
+ cmd.startsWith('yarn build')
80
+ ) {
81
+ return 'Building project';
82
+ }
83
+
84
+ // TypeScript type checking
85
+ if (cmd.startsWith('tsc')) {
86
+ return 'Type checking';
87
+ }
88
+
89
+ // Linting
90
+ if (cmd.startsWith('eslint') || cmd.startsWith('npm run lint') || cmd.startsWith('pnpm lint')) {
91
+ return 'Linting code';
92
+ }
93
+
94
+ // Formatting
95
+ if (cmd.startsWith('prettier')) {
96
+ return 'Formatting code';
97
+ }
98
+
99
+ // Default: show the command
100
+ return `Running ${truncate(command, MAX_LENGTH - 8)}`;
101
+ }
102
+
103
+ /**
104
+ * Generates a human-readable summary of what a tool is doing based on its name and input.
105
+ *
106
+ * @param toolName - The name of the tool (Read, Bash, Glob, Grep, Write, Edit, Task, etc.)
107
+ * @param input - The input parameters passed to the tool
108
+ * @returns A human-readable summary string
109
+ *
110
+ * @example
111
+ * generateToolIntentSummary('Read', { file_path: '/src/foo.ts' })
112
+ * // Returns: "Reading /src/foo.ts"
113
+ *
114
+ * @example
115
+ * generateToolIntentSummary('Bash', { command: 'npm install' })
116
+ * // Returns: "Installing dependencies"
117
+ */
118
+ export function generateToolIntentSummary(
119
+ toolName: string,
120
+ input: Record<string, unknown>
121
+ ): string {
122
+ // Handle null/undefined input
123
+ const safeInput = input ?? {};
124
+
125
+ switch (toolName) {
126
+ case 'Read': {
127
+ const filePath = getString(safeInput, 'file_path');
128
+ if (!filePath) return 'Read';
129
+ return truncate(`Reading ${filePath}`);
130
+ }
131
+
132
+ case 'Bash': {
133
+ // Prefer Claude's description if provided (human-readable intent)
134
+ const description = getString(safeInput, 'description');
135
+ if (description) return truncate(description);
136
+ // Fall back to command-based summarization
137
+ const command = getString(safeInput, 'command');
138
+ if (!command) return 'Bash';
139
+ return summarizeBashCommand(command);
140
+ }
141
+
142
+ case 'Glob': {
143
+ const pattern = getString(safeInput, 'pattern');
144
+ if (!pattern) return 'Glob';
145
+ return truncate(`Finding ${pattern} files`);
146
+ }
147
+
148
+ case 'Grep': {
149
+ const pattern = getString(safeInput, 'pattern');
150
+ if (!pattern) return 'Grep';
151
+ // Remove outer quotes if present for display
152
+ const displayPattern = pattern.replace(/^["']|["']$/g, '');
153
+ return truncate(`Searching for '${displayPattern}'`);
154
+ }
155
+
156
+ case 'Write': {
157
+ const filePath = getString(safeInput, 'file_path');
158
+ if (!filePath) return 'Write';
159
+ return truncate(`Creating ${filePath}`);
160
+ }
161
+
162
+ case 'Edit': {
163
+ const filePath = getString(safeInput, 'file_path');
164
+ if (!filePath) return 'Edit';
165
+ return truncate(`Editing ${filePath}`);
166
+ }
167
+
168
+ case 'Task': {
169
+ const subagentType = getString(safeInput, 'subagent_type');
170
+ if (!subagentType) return 'Task';
171
+ return `Launching ${subagentType} agent`;
172
+ }
173
+
174
+ case 'WebFetch': {
175
+ const url = getString(safeInput, 'url');
176
+ if (!url) return 'WebFetch';
177
+ const hostname = extractHostname(url);
178
+ return truncate(`Fetching ${hostname}`);
179
+ }
180
+
181
+ case 'WebSearch': {
182
+ const query = getString(safeInput, 'query');
183
+ if (!query) return 'WebSearch';
184
+ return truncate(`Searching web for '${query}'`);
185
+ }
186
+
187
+ default: {
188
+ // Unknown tool: show tool name with truncated JSON
189
+ try {
190
+ const jsonStr = JSON.stringify(safeInput);
191
+ if (jsonStr === '{}') return toolName;
192
+ const maxJsonLen = MAX_LENGTH - toolName.length - 3; // space + parens
193
+ return `${toolName} (${truncate(jsonStr, maxJsonLen)})`;
194
+ } catch {
195
+ return toolName;
196
+ }
197
+ }
198
+ }
199
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Tool Stack Grouper Utility
3
+ *
4
+ * Groups consecutive tool_use messages into collapsible stacks.
5
+ * Story MSSCI-13400 - Tool use stack between messages
6
+ */
7
+
8
+ export interface ToolUseMessage {
9
+ type: 'tool_use';
10
+ tool_name: string;
11
+ tool_id: string;
12
+ input: Record<string, unknown>;
13
+ timestamp: number;
14
+ isStreaming?: boolean;
15
+ }
16
+
17
+ export interface ToolStackData {
18
+ stackId: string;
19
+ tools: ToolUseMessage[];
20
+ count: number;
21
+ isActive: boolean;
22
+ timestamp: number;
23
+ }
24
+
25
+ interface Message {
26
+ type: string;
27
+ timestamp: number;
28
+ tool_name?: string;
29
+ tool_id?: string;
30
+ input?: Record<string, unknown>;
31
+ isStreaming?: boolean;
32
+ content?: string;
33
+ }
34
+
35
+ /**
36
+ * Groups consecutive tool_use messages into stacks.
37
+ *
38
+ * Rules:
39
+ * - tool_result messages do NOT break the stack (they're paired with tool_use elsewhere)
40
+ * - assistant/user messages DO break the stack
41
+ * - Single tool_use messages ARE grouped (consistent rendering with multi-tool stacks)
42
+ * - Returns array of ToolStackData, each representing 1+ consecutive tools
43
+ *
44
+ * @param messages - Array of messages to process
45
+ * @returns Array of tool stacks (only stacks with 2+ tools)
46
+ */
47
+ /**
48
+ * Generate a stable stack ID from the first tool's ID.
49
+ * This ensures the same stack keeps the same key across re-renders,
50
+ * preventing React from remounting and losing collapse state.
51
+ */
52
+ function generateStableStackId(tools: ToolUseMessage[]): string {
53
+ if (tools.length === 0) return 'stack-empty';
54
+ // Use first tool's ID as anchor - it won't change as more tools are added
55
+ return `stack-${tools[0].tool_id}`;
56
+ }
57
+
58
+ export function groupToolsIntoStacks(messages: Message[]): ToolStackData[] {
59
+ const stacks: ToolStackData[] = [];
60
+ let currentTools: ToolUseMessage[] = [];
61
+
62
+ for (const msg of messages) {
63
+ if (msg.type === 'tool_use') {
64
+ // Add to current stack
65
+ currentTools.push({
66
+ type: 'tool_use',
67
+ tool_name: msg.tool_name || '',
68
+ tool_id: msg.tool_id || '',
69
+ input: msg.input || {},
70
+ timestamp: msg.timestamp,
71
+ isStreaming: msg.isStreaming,
72
+ });
73
+ } else if (msg.type === 'tool_result') {
74
+ // tool_result does NOT break the stack - it's matched separately
75
+ continue;
76
+ } else {
77
+ // assistant, user, or other message types break the stack
78
+ // Create stack for any tools (single tools also get stacked for consistent rendering)
79
+ if (currentTools.length >= 1) {
80
+ const lastTool = currentTools[currentTools.length - 1];
81
+ stacks.push({
82
+ stackId: generateStableStackId(currentTools),
83
+ tools: [...currentTools],
84
+ count: currentTools.length,
85
+ isActive: lastTool.isStreaming === true,
86
+ timestamp: currentTools[0].timestamp,
87
+ });
88
+ }
89
+ currentTools = [];
90
+ }
91
+ }
92
+
93
+ // Handle remaining tools at end of messages
94
+ if (currentTools.length >= 1) {
95
+ const lastTool = currentTools[currentTools.length - 1];
96
+ stacks.push({
97
+ stackId: generateStableStackId(currentTools),
98
+ tools: [...currentTools],
99
+ count: currentTools.length,
100
+ isActive: lastTool.isStreaming === true,
101
+ timestamp: currentTools[0].timestamp,
102
+ });
103
+ }
104
+
105
+ return stacks;
106
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Tool type color mappings
3
+ *
4
+ * Story MSSCI-13402 - Tool use visual design polish
5
+ *
6
+ * Provides color values and CSS class names for each tool type.
7
+ */
8
+
9
+ /** Tool type to color hex mapping */
10
+ export const TOOL_TYPE_COLORS: Record<string, string> = {
11
+ Read: '#3b82f6', // blue
12
+ Write: '#f97316', // orange
13
+ Bash: '#22c55e', // green
14
+ Glob: '#a855f7', // purple
15
+ Grep: '#06b6d4', // cyan
16
+ Edit: '#eab308', // yellow
17
+ Task: '#ec4899', // pink
18
+ };
19
+
20
+ /** Default color for unknown tools */
21
+ export const DEFAULT_TOOL_COLOR = '#6b7280'; // gray
22
+
23
+ /**
24
+ * Get the color hex value for a tool type
25
+ *
26
+ * @param toolName - The name of the tool
27
+ * @returns Color hex string
28
+ */
29
+ export function getToolTypeColor(toolName: string): string {
30
+ return TOOL_TYPE_COLORS[toolName] || DEFAULT_TOOL_COLOR;
31
+ }
32
+
33
+ /**
34
+ * Get the CSS class name for a tool type
35
+ *
36
+ * @param toolName - The name of the tool
37
+ * @returns CSS class name like "tool-read" or "tool-unknown"
38
+ */
39
+ export function getToolTypeClass(toolName: string): string {
40
+ const knownTools = ['Read', 'Write', 'Bash', 'Glob', 'Grep', 'Edit', 'Task'];
41
+ if (knownTools.includes(toolName)) {
42
+ return `tool-${toolName.toLowerCase()}`;
43
+ }
44
+ return '';
45
+ }
@@ -84,7 +84,6 @@ def bc():
84
84
  workflow - Focus on Workflow panel
85
85
  background - Focus on Background panel
86
86
  audit-log - Focus on Audit Log panel
87
- changed - Focus on Changed panel
88
87
  ac - Focus on Acceptance Criteria panel
89
88
  debug - Focus on Debug panel
90
89
  settings - Focus on Settings panel
@@ -31,7 +31,6 @@ VALID_PANELS = [
31
31
  "workflow",
32
32
  "background",
33
33
  "audit-log",
34
- "changed",
35
34
  "ac",
36
35
  "debug",
37
36
  "progress",
@@ -22,7 +22,6 @@ PANEL_ICONS: dict[str, tuple[str, str]] = {
22
22
  "workflow": ("\uf126", "W"), # nf-fa-code_fork
23
23
  "background": ("\uf110", "B"), # nf-fa-spinner
24
24
  "audit-log": ("\uf15c", "L"), # nf-fa-file_text
25
- "changed": ("\uf044", "C"), # nf-fa-pencil_square_o
26
25
  "ac": ("\uf00c", "A"), # nf-fa-check
27
26
  "debug": ("\uf188", "d"), # nf-fa-bug
28
27
  "settings": ("\uf013", "S"), # nf-fa-gear
@@ -1,8 +1,6 @@
1
1
  """Cross-panel event bus for BikeRack TUI (Story 110-1).
2
2
 
3
3
  Defines Textual Message subclasses for inter-panel communication.
4
- First use case: NavigateToFile posted by ChangedPanel, handled by App
5
- to switch to DiffsPanel filtered to that file.
6
4
  """
7
5
 
8
6
  from __future__ import annotations
@@ -17,11 +15,7 @@ class PanelEvent(Message):
17
15
 
18
16
 
19
17
  class NavigateToFile(PanelEvent):
20
- """Navigate to a specific file in the diffs panel.
21
-
22
- Posted by ChangedPanel when user presses Enter on a selected file.
23
- Handled by BikeRackApp to switch to DiffsPanel and navigate to that file.
24
- """
18
+ """Navigate to a specific file in the diffs panel."""
25
19
 
26
20
  def __init__(self, path: str) -> None:
27
21
  super().__init__()