@assistkick/create 1.9.0 → 1.11.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 (206) hide show
  1. package/dist/src/scaffolder.d.ts +12 -1
  2. package/dist/src/scaffolder.js +40 -3
  3. package/dist/src/scaffolder.js.map +1 -1
  4. package/package.json +1 -1
  5. package/templates/assistkick-product-system/package.json +1 -1
  6. package/templates/assistkick-product-system/packages/backend/package.json +1 -0
  7. package/templates/assistkick-product-system/packages/backend/src/mcp/permission_mcp_server.ts +196 -0
  8. package/templates/assistkick-product-system/packages/backend/src/routes/agents.ts +31 -7
  9. package/templates/assistkick-product-system/packages/backend/src/routes/auth.ts +15 -12
  10. package/templates/assistkick-product-system/packages/backend/src/routes/chat_files.test.ts +95 -0
  11. package/templates/assistkick-product-system/packages/backend/src/routes/chat_files.ts +97 -0
  12. package/templates/assistkick-product-system/packages/backend/src/routes/chat_permission.ts +94 -0
  13. package/templates/assistkick-product-system/packages/backend/src/routes/chat_sessions.ts +189 -0
  14. package/templates/assistkick-product-system/packages/backend/src/routes/chat_upload.test.ts +131 -0
  15. package/templates/assistkick-product-system/packages/backend/src/routes/chat_upload.ts +94 -0
  16. package/templates/assistkick-product-system/packages/backend/src/routes/files.test.ts +12 -3
  17. package/templates/assistkick-product-system/packages/backend/src/routes/files.ts +2 -2
  18. package/templates/assistkick-product-system/packages/backend/src/routes/git.ts +391 -23
  19. package/templates/assistkick-product-system/packages/backend/src/routes/git_branches.test.ts +306 -0
  20. package/templates/assistkick-product-system/packages/backend/src/routes/git_connect.test.ts +133 -0
  21. package/templates/assistkick-product-system/packages/backend/src/routes/pipeline.ts +66 -9
  22. package/templates/assistkick-product-system/packages/backend/src/routes/preview.ts +204 -0
  23. package/templates/assistkick-product-system/packages/backend/src/routes/projects.test.ts +205 -0
  24. package/templates/assistkick-product-system/packages/backend/src/routes/projects.ts +37 -9
  25. package/templates/assistkick-product-system/packages/backend/src/routes/skills.test.ts +139 -0
  26. package/templates/assistkick-product-system/packages/backend/src/routes/skills.ts +95 -0
  27. package/templates/assistkick-product-system/packages/backend/src/routes/terminal.ts +5 -4
  28. package/templates/assistkick-product-system/packages/backend/src/routes/users.ts +4 -4
  29. package/templates/assistkick-product-system/packages/backend/src/routes/video.ts +8 -8
  30. package/templates/assistkick-product-system/packages/backend/src/routes/workflow_groups.ts +5 -5
  31. package/templates/assistkick-product-system/packages/backend/src/routes/workflows.ts +6 -6
  32. package/templates/assistkick-product-system/packages/backend/src/server.ts +107 -27
  33. package/templates/assistkick-product-system/packages/backend/src/services/agent_service.test.ts +105 -203
  34. package/templates/assistkick-product-system/packages/backend/src/services/agent_service.ts +76 -266
  35. package/templates/assistkick-product-system/packages/backend/src/services/chat_cli_bridge.test.ts +427 -0
  36. package/templates/assistkick-product-system/packages/backend/src/services/chat_cli_bridge.ts +345 -0
  37. package/templates/assistkick-product-system/packages/backend/src/services/chat_message_repository.test.ts +170 -0
  38. package/templates/assistkick-product-system/packages/backend/src/services/chat_message_repository.ts +106 -0
  39. package/templates/assistkick-product-system/packages/backend/src/services/chat_session_service.test.ts +217 -0
  40. package/templates/assistkick-product-system/packages/backend/src/services/chat_session_service.ts +188 -0
  41. package/templates/assistkick-product-system/packages/backend/src/services/chat_ws_handler.test.ts +1243 -0
  42. package/templates/assistkick-product-system/packages/backend/src/services/chat_ws_handler.ts +894 -0
  43. package/templates/assistkick-product-system/packages/backend/src/services/coherence-review.ts +3 -3
  44. package/templates/assistkick-product-system/packages/backend/src/services/dev_command_detector.test.ts +85 -0
  45. package/templates/assistkick-product-system/packages/backend/src/services/dev_command_detector.ts +54 -0
  46. package/templates/assistkick-product-system/packages/backend/src/services/email_service.ts +13 -10
  47. package/templates/assistkick-product-system/packages/backend/src/services/init.ts +11 -3
  48. package/templates/assistkick-product-system/packages/backend/src/services/invitation_service.ts +1 -1
  49. package/templates/assistkick-product-system/packages/backend/src/services/password_reset_service.ts +1 -1
  50. package/templates/assistkick-product-system/packages/backend/src/services/permission_service.test.ts +243 -0
  51. package/templates/assistkick-product-system/packages/backend/src/services/permission_service.ts +259 -0
  52. package/templates/assistkick-product-system/packages/backend/src/services/preview_server_manager.test.ts +172 -0
  53. package/templates/assistkick-product-system/packages/backend/src/services/preview_server_manager.ts +225 -0
  54. package/templates/assistkick-product-system/packages/backend/src/services/project_service.test.ts +29 -0
  55. package/templates/assistkick-product-system/packages/backend/src/services/project_service.ts +17 -0
  56. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.test.ts +255 -0
  57. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.ts +300 -25
  58. package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.test.ts +44 -0
  59. package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.ts +62 -7
  60. package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.test.ts +77 -6
  61. package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.ts +149 -14
  62. package/templates/assistkick-product-system/packages/backend/src/services/terminal_ws_handler.ts +2 -1
  63. package/templates/assistkick-product-system/packages/backend/src/services/title_generator_service.test.ts +45 -0
  64. package/templates/assistkick-product-system/packages/backend/src/services/title_generator_service.ts +157 -0
  65. package/templates/assistkick-product-system/packages/backend/src/services/tts_service.ts +4 -3
  66. package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.ts +3 -3
  67. package/templates/assistkick-product-system/packages/frontend/package.json +5 -0
  68. package/templates/assistkick-product-system/packages/frontend/src/App.tsx +2 -0
  69. package/templates/assistkick-product-system/packages/frontend/src/api/client.ts +336 -5
  70. package/templates/assistkick-product-system/packages/frontend/src/components/AgentsView.tsx +192 -12
  71. package/templates/assistkick-product-system/packages/frontend/src/components/AttachmentPreviewList.tsx +98 -0
  72. package/templates/assistkick-product-system/packages/frontend/src/components/AutocompleteDropdown.tsx +65 -0
  73. package/templates/assistkick-product-system/packages/frontend/src/components/ChatAttachButton.tsx +56 -0
  74. package/templates/assistkick-product-system/packages/frontend/src/components/ChatDropZone.tsx +80 -0
  75. package/templates/assistkick-product-system/packages/frontend/src/components/ChatMessageBubble.tsx +155 -0
  76. package/templates/assistkick-product-system/packages/frontend/src/components/ChatMessageContent.tsx +182 -0
  77. package/templates/assistkick-product-system/packages/frontend/src/components/ChatMessageInput.tsx +233 -0
  78. package/templates/assistkick-product-system/packages/frontend/src/components/ChatSessionSidebar.tsx +218 -0
  79. package/templates/assistkick-product-system/packages/frontend/src/components/ChatStopButton.tsx +32 -0
  80. package/templates/assistkick-product-system/packages/frontend/src/components/ChatTodoSidebar.tsx +113 -0
  81. package/templates/assistkick-product-system/packages/frontend/src/components/ChatView.tsx +842 -0
  82. package/templates/assistkick-product-system/packages/frontend/src/components/CommitMessageModal.tsx +82 -0
  83. package/templates/assistkick-product-system/packages/frontend/src/components/DiagramOverlay.tsx +160 -0
  84. package/templates/assistkick-product-system/packages/frontend/src/components/EditorTabBar.tsx +5 -5
  85. package/templates/assistkick-product-system/packages/frontend/src/components/FileTree.tsx +9 -10
  86. package/templates/assistkick-product-system/packages/frontend/src/components/FileTreeInlineInput.tsx +5 -5
  87. package/templates/assistkick-product-system/packages/frontend/src/components/FilesView.tsx +112 -41
  88. package/templates/assistkick-product-system/packages/frontend/src/components/GraphLegend.tsx +2 -2
  89. package/templates/assistkick-product-system/packages/frontend/src/components/HighlightedText.tsx +87 -0
  90. package/templates/assistkick-product-system/packages/frontend/src/components/ImageLightbox.tsx +192 -0
  91. package/templates/assistkick-product-system/packages/frontend/src/components/KanbanView.tsx +2 -2
  92. package/templates/assistkick-product-system/packages/frontend/src/components/MentionPill.tsx +33 -0
  93. package/templates/assistkick-product-system/packages/frontend/src/components/MermaidBlock.tsx +148 -0
  94. package/templates/assistkick-product-system/packages/frontend/src/components/PermissionDialog.tsx +91 -0
  95. package/templates/assistkick-product-system/packages/frontend/src/components/PermissionModeSelector.tsx +229 -0
  96. package/templates/assistkick-product-system/packages/frontend/src/components/ProjectSelector.tsx +249 -83
  97. package/templates/assistkick-product-system/packages/frontend/src/components/QueuedMessageBubble.tsx +38 -0
  98. package/templates/assistkick-product-system/packages/frontend/src/components/SidePanel.tsx +212 -117
  99. package/templates/assistkick-product-system/packages/frontend/src/components/SystemPromptAccordion.tsx +48 -0
  100. package/templates/assistkick-product-system/packages/frontend/src/components/TaskIcon.tsx +11 -0
  101. package/templates/assistkick-product-system/packages/frontend/src/components/TerminalView.tsx +25 -9
  102. package/templates/assistkick-product-system/packages/frontend/src/components/ToolDiffView.tsx +114 -0
  103. package/templates/assistkick-product-system/packages/frontend/src/components/ToolResultCard.tsx +87 -0
  104. package/templates/assistkick-product-system/packages/frontend/src/components/ToolUseCard.tsx +149 -0
  105. package/templates/assistkick-product-system/packages/frontend/src/components/Toolbar.tsx +25 -8
  106. package/templates/assistkick-product-system/packages/frontend/src/components/UnifiedGitWidget.tsx +722 -0
  107. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/GroupNode.tsx +2 -0
  108. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/NodePalette.tsx +2 -1
  109. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/ProgrammableNode.tsx +178 -0
  110. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowCanvas.tsx +3 -0
  111. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowMonitorModal.tsx +103 -9
  112. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/monitor_nodes.tsx +26 -2
  113. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.ts +42 -1
  114. package/templates/assistkick-product-system/packages/frontend/src/hooks/useDocumentTitle.ts +11 -0
  115. package/templates/assistkick-product-system/packages/frontend/src/hooks/useProjects.ts +1 -0
  116. package/templates/assistkick-product-system/packages/frontend/src/hooks/use_chat_stream.ts +826 -0
  117. package/templates/assistkick-product-system/packages/frontend/src/hooks/use_file_tree_cache.ts +69 -0
  118. package/templates/assistkick-product-system/packages/frontend/src/hooks/use_mention_autocomplete.ts +284 -0
  119. package/templates/assistkick-product-system/packages/frontend/src/lib/attachment_manager.test.ts +183 -0
  120. package/templates/assistkick-product-system/packages/frontend/src/lib/attachment_manager.ts +150 -0
  121. package/templates/assistkick-product-system/packages/frontend/src/lib/chat_message_helpers.test.ts +305 -0
  122. package/templates/assistkick-product-system/packages/frontend/src/lib/chat_message_helpers.ts +113 -0
  123. package/templates/assistkick-product-system/packages/frontend/src/lib/context_usage_helpers.test.ts +157 -0
  124. package/templates/assistkick-product-system/packages/frontend/src/lib/context_usage_helpers.ts +95 -0
  125. package/templates/assistkick-product-system/packages/frontend/src/lib/mermaid_helpers.test.ts +65 -0
  126. package/templates/assistkick-product-system/packages/frontend/src/lib/mermaid_helpers.ts +110 -0
  127. package/templates/assistkick-product-system/packages/frontend/src/lib/message_queue.ts +66 -0
  128. package/templates/assistkick-product-system/packages/frontend/src/lib/tool_use_summary.test.ts +124 -0
  129. package/templates/assistkick-product-system/packages/frontend/src/lib/tool_use_summary.ts +112 -0
  130. package/templates/assistkick-product-system/packages/frontend/src/routes/AgentsRoute.tsx +2 -0
  131. package/templates/assistkick-product-system/packages/frontend/src/routes/ChatRoute.tsx +8 -0
  132. package/templates/assistkick-product-system/packages/frontend/src/routes/CoherenceRoute.tsx +2 -0
  133. package/templates/assistkick-product-system/packages/frontend/src/routes/DashboardLayout.tsx +0 -4
  134. package/templates/assistkick-product-system/packages/frontend/src/routes/DesignSystemRoute.tsx +2 -0
  135. package/templates/assistkick-product-system/packages/frontend/src/routes/FilesRoute.tsx +2 -0
  136. package/templates/assistkick-product-system/packages/frontend/src/routes/GraphRoute.tsx +2 -0
  137. package/templates/assistkick-product-system/packages/frontend/src/routes/KanbanRoute.tsx +2 -0
  138. package/templates/assistkick-product-system/packages/frontend/src/routes/TerminalRoute.tsx +2 -0
  139. package/templates/assistkick-product-system/packages/frontend/src/routes/UsersRoute.tsx +2 -0
  140. package/templates/assistkick-product-system/packages/frontend/src/routes/VideographyRoute.tsx +2 -0
  141. package/templates/assistkick-product-system/packages/frontend/src/routes/WorkflowsRoute.tsx +2 -0
  142. package/templates/assistkick-product-system/packages/frontend/src/routes/accept_invitation.tsx +2 -0
  143. package/templates/assistkick-product-system/packages/frontend/src/routes/forgot_password.tsx +2 -0
  144. package/templates/assistkick-product-system/packages/frontend/src/routes/login.tsx +2 -0
  145. package/templates/assistkick-product-system/packages/frontend/src/routes/register.tsx +2 -0
  146. package/templates/assistkick-product-system/packages/frontend/src/routes/reset_password.tsx +2 -0
  147. package/templates/assistkick-product-system/packages/frontend/src/stores/useAttachmentStore.ts +66 -0
  148. package/templates/assistkick-product-system/packages/frontend/src/stores/useChatSessionStore.ts +107 -0
  149. package/templates/assistkick-product-system/packages/frontend/src/stores/useMessageQueueStore.ts +110 -0
  150. package/templates/assistkick-product-system/packages/frontend/src/stores/usePreviewStore.ts +78 -0
  151. package/templates/assistkick-product-system/packages/frontend/src/stores/useProjectStore.ts +7 -0
  152. package/templates/assistkick-product-system/packages/frontend/src/stores/useSidePanelStore.ts +6 -1
  153. package/templates/assistkick-product-system/packages/frontend/src/styles/index.css +30 -357
  154. package/templates/assistkick-product-system/packages/frontend/src/utils/parse_node_markdown.test.ts +115 -0
  155. package/templates/assistkick-product-system/packages/frontend/src/utils/parse_node_markdown.ts +91 -0
  156. package/templates/assistkick-product-system/packages/frontend/src/utils/preview_utils.test.ts +30 -0
  157. package/templates/assistkick-product-system/packages/frontend/src/utils/preview_utils.ts +3 -0
  158. package/templates/assistkick-product-system/packages/shared/db/migrations/0015_magenta_jazinda.sql +1 -0
  159. package/templates/assistkick-product-system/packages/shared/db/migrations/0016_giant_xorn.sql +1 -0
  160. package/templates/assistkick-product-system/packages/shared/db/migrations/0017_sloppy_mentor.sql +6 -0
  161. package/templates/assistkick-product-system/packages/shared/db/migrations/0018_vengeful_kabuki.sql +9 -0
  162. package/templates/assistkick-product-system/packages/shared/db/migrations/0019_careful_sentinels.sql +8 -0
  163. package/templates/assistkick-product-system/packages/shared/db/migrations/0020_clever_spot.sql +27 -0
  164. package/templates/assistkick-product-system/packages/shared/db/migrations/0021_graceful_hex.sql +1 -0
  165. package/templates/assistkick-product-system/packages/shared/db/migrations/0022_short_kingpin.sql +1 -0
  166. package/templates/assistkick-product-system/packages/shared/db/migrations/0023_ambiguous_sharon_carter.sql +1 -0
  167. package/templates/assistkick-product-system/packages/shared/db/migrations/0024_fat_unus.sql +1 -0
  168. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0015_snapshot.json +1552 -0
  169. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0016_snapshot.json +1560 -0
  170. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0017_snapshot.json +1598 -0
  171. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0018_snapshot.json +1657 -0
  172. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0019_snapshot.json +1709 -0
  173. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0020_snapshot.json +1733 -0
  174. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0021_snapshot.json +1740 -0
  175. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0022_snapshot.json +1755 -0
  176. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0023_snapshot.json +1762 -0
  177. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0024_snapshot.json +1769 -0
  178. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/_journal.json +70 -0
  179. package/templates/assistkick-product-system/packages/shared/db/schema.ts +40 -1
  180. package/templates/assistkick-product-system/packages/shared/lib/claude-service.test.ts +236 -0
  181. package/templates/assistkick-product-system/packages/shared/lib/claude-service.ts +46 -5
  182. package/templates/assistkick-product-system/packages/shared/lib/git_workflow.ts +65 -39
  183. package/templates/assistkick-product-system/packages/shared/lib/programmable_node_executor.test.ts +173 -0
  184. package/templates/assistkick-product-system/packages/shared/lib/programmable_node_executor.ts +213 -0
  185. package/templates/assistkick-product-system/packages/shared/lib/validator.test.ts +70 -0
  186. package/templates/assistkick-product-system/packages/shared/lib/validator.ts +17 -1
  187. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.test.ts +803 -27
  188. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.ts +502 -68
  189. package/templates/assistkick-product-system/packages/shared/lib/workflow_orchestrator.ts +4 -4
  190. package/templates/assistkick-product-system/packages/shared/package.json +2 -1
  191. package/templates/assistkick-product-system/packages/shared/test_fixtures/hanging_stream.mjs +46 -0
  192. package/templates/assistkick-product-system/packages/shared/tools/add_node.test.ts +44 -0
  193. package/templates/assistkick-product-system/packages/shared/tools/add_node.ts +7 -0
  194. package/templates/assistkick-product-system/packages/shared/tools/remove_node.ts +2 -1
  195. package/templates/assistkick-product-system/packages/shared/tools/resolve_question.ts +2 -1
  196. package/templates/assistkick-product-system/packages/shared/tools/update_node.ts +2 -1
  197. package/templates/assistkick-product-system/tests/message_queue.test.ts +178 -0
  198. package/templates/assistkick-product-system/tests/message_queue_per_session.test.ts +143 -0
  199. package/templates/skills/assistkick-bootstrap/SKILL.md +26 -26
  200. package/templates/skills/assistkick-code-reviewer/SKILL.md +45 -46
  201. package/templates/skills/assistkick-db-explorer/SKILL.md +13 -13
  202. package/templates/skills/assistkick-debugger/SKILL.md +23 -23
  203. package/templates/skills/assistkick-developer/SKILL.md +59 -63
  204. package/templates/skills/assistkick-interview/SKILL.md +26 -26
  205. package/templates/skills/assistkick-video-composition-agent/SKILL.md +231 -0
  206. package/templates/skills/assistkick-video-script-writer/SKILL.md +136 -0
@@ -0,0 +1,65 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { resolveMermaidTheme, isMermaidLanguage, clampZoom, MIN_ZOOM, MAX_ZOOM } from './mermaid_helpers.ts';
4
+
5
+ describe('resolveMermaidTheme', () => {
6
+ it('returns "base" for light theme', () => {
7
+ assert.equal(resolveMermaidTheme('light'), 'base');
8
+ });
9
+
10
+ it('returns "base" for dark theme', () => {
11
+ assert.equal(resolveMermaidTheme('dark'), 'base');
12
+ });
13
+
14
+ it('returns "base" when data-theme is null', () => {
15
+ assert.equal(resolveMermaidTheme(null), 'base');
16
+ });
17
+
18
+ it('returns "base" for unknown theme values', () => {
19
+ assert.equal(resolveMermaidTheme('contrast'), 'base');
20
+ });
21
+ });
22
+
23
+ describe('isMermaidLanguage', () => {
24
+ it('returns true for "mermaid"', () => {
25
+ assert.equal(isMermaidLanguage('mermaid'), true);
26
+ });
27
+
28
+ it('returns false for other languages', () => {
29
+ assert.equal(isMermaidLanguage('javascript'), false);
30
+ assert.equal(isMermaidLanguage('python'), false);
31
+ assert.equal(isMermaidLanguage(''), false);
32
+ });
33
+
34
+ it('is case-sensitive', () => {
35
+ assert.equal(isMermaidLanguage('Mermaid'), false);
36
+ assert.equal(isMermaidLanguage('MERMAID'), false);
37
+ });
38
+ });
39
+
40
+ describe('clampZoom', () => {
41
+ it('applies positive delta', () => {
42
+ assert.equal(clampZoom(1, 0.15), 1.15);
43
+ });
44
+
45
+ it('applies negative delta', () => {
46
+ const result = clampZoom(1, -0.15);
47
+ assert.ok(Math.abs(result - 0.85) < 0.001);
48
+ });
49
+
50
+ it('clamps at MAX_ZOOM', () => {
51
+ assert.equal(clampZoom(MAX_ZOOM, 1), MAX_ZOOM);
52
+ });
53
+
54
+ it('clamps at MIN_ZOOM', () => {
55
+ assert.equal(clampZoom(MIN_ZOOM, -1), MIN_ZOOM);
56
+ });
57
+
58
+ it('does not go below MIN_ZOOM from large negative delta', () => {
59
+ assert.equal(clampZoom(0.5, -10), MIN_ZOOM);
60
+ });
61
+
62
+ it('does not exceed MAX_ZOOM from large positive delta', () => {
63
+ assert.equal(clampZoom(4, 10), MAX_ZOOM);
64
+ });
65
+ });
@@ -0,0 +1,110 @@
1
+ /**
2
+ * mermaid_helpers — pure utility functions for mermaid diagram rendering.
3
+ *
4
+ * Extracts testable logic from the MermaidBlock component:
5
+ * theme resolution, zoom clamping, and language detection.
6
+ */
7
+
8
+ const MIN_ZOOM = 0.25;
9
+ const MAX_ZOOM = 5;
10
+
11
+ /**
12
+ * Resolves the mermaid theme name from the data-theme attribute value.
13
+ * Uses 'base' theme so we can fully control colors via themeVariables.
14
+ */
15
+ export const resolveMermaidTheme = (_dataTheme: string | null): 'base' => {
16
+ return 'base';
17
+ };
18
+
19
+ /**
20
+ * Reads the current CSS custom properties from the document and returns
21
+ * mermaid themeVariables that align diagrams with the design system.
22
+ */
23
+ export const getDesignSystemThemeVariables = (): Record<string, string> => {
24
+ const s = getComputedStyle(document.documentElement);
25
+ const v = (name: string) => s.getPropertyValue(name).trim();
26
+
27
+ const bg = v('--bg-primary') || '#0c0c0e';
28
+ const bgAlt = v('--bg-secondary') || '#18191c';
29
+ const bgRaised = v('--bg-tertiary') || '#222326';
30
+ const border = v('--border-color') || '#2e2f33';
31
+ const text = v('--text-primary') || '#f0f0f0';
32
+ const textSecondary = v('--text-secondary') || '#9a9ba0';
33
+ const accent = v('--accent') || '#b5e853';
34
+ const error = v('--error-color') || '#f06060';
35
+
36
+ // Use text-secondary for lines/borders so they're visible against dark backgrounds
37
+ const lineStroke = textSecondary;
38
+ // Use a slightly lighter border for node outlines
39
+ const nodeBorderColor = textSecondary;
40
+
41
+ return {
42
+ // General
43
+ background: bg,
44
+ primaryColor: bgRaised,
45
+ primaryBorderColor: nodeBorderColor,
46
+ primaryTextColor: text,
47
+ secondaryColor: bgAlt,
48
+ secondaryBorderColor: nodeBorderColor,
49
+ secondaryTextColor: text,
50
+ tertiaryColor: bgRaised,
51
+ tertiaryBorderColor: nodeBorderColor,
52
+ tertiaryTextColor: text,
53
+
54
+ // Lines & labels
55
+ lineColor: lineStroke,
56
+ textColor: text,
57
+ mainBkg: bgRaised,
58
+ nodeBorder: nodeBorderColor,
59
+ clusterBkg: bgAlt,
60
+ clusterBorder: nodeBorderColor,
61
+ titleColor: text,
62
+
63
+ // Edge labels
64
+ edgeLabelBackground: bgRaised,
65
+
66
+ // Fonts
67
+ fontFamily: '"Inter", system-ui, -apple-system, sans-serif',
68
+ fontSize: '13px',
69
+
70
+ // Flowchart-specific
71
+ nodeTextColor: text,
72
+
73
+ // Accents for special states
74
+ errorBkgColor: error,
75
+ errorTextColor: text,
76
+
77
+ // Sequence diagrams
78
+ actorBkg: bgRaised,
79
+ actorBorder: nodeBorderColor,
80
+ actorTextColor: text,
81
+ actorLineColor: lineStroke,
82
+ signalColor: text,
83
+ signalTextColor: text,
84
+ labelBoxBkgColor: bgAlt,
85
+ labelBoxBorderColor: nodeBorderColor,
86
+ labelTextColor: text,
87
+ loopTextColor: textSecondary,
88
+ noteBkgColor: bgAlt,
89
+ noteBorderColor: nodeBorderColor,
90
+ noteTextColor: text,
91
+ activationBkgColor: bgRaised,
92
+ activationBorderColor: accent,
93
+ };
94
+ };
95
+
96
+ /**
97
+ * Returns true if a code block language identifier indicates a mermaid block.
98
+ */
99
+ export const isMermaidLanguage = (language: string): boolean => {
100
+ return language === 'mermaid';
101
+ };
102
+
103
+ /**
104
+ * Clamps a zoom level after applying a delta, keeping it within bounds.
105
+ */
106
+ export const clampZoom = (current: number, delta: number): number => {
107
+ return Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, current + delta));
108
+ };
109
+
110
+ export { MIN_ZOOM, MAX_ZOOM };
@@ -0,0 +1,66 @@
1
+ /**
2
+ * MessageQueue — pure logic for managing a queue of user messages.
3
+ *
4
+ * Messages are enqueued while Claude is still responding to the current one.
5
+ * When the current CLI invocation completes, all queued messages are drained,
6
+ * merged into a single prompt, and sent as the next invocation.
7
+ *
8
+ * This class is framework-agnostic — the Zustand store wraps it for React.
9
+ */
10
+
11
+ export interface QueuedMessage {
12
+ id: string;
13
+ text: string;
14
+ createdAt: number;
15
+ }
16
+
17
+ export class MessageQueue {
18
+ private queue: QueuedMessage[] = [];
19
+ private idCounter = 0;
20
+
21
+ private nextId = (): string => {
22
+ return `q_${++this.idCounter}_${Date.now()}`;
23
+ };
24
+
25
+ getQueue = (): readonly QueuedMessage[] => {
26
+ return this.queue;
27
+ };
28
+
29
+ getLength = (): number => {
30
+ return this.queue.length;
31
+ };
32
+
33
+ enqueue = (text: string): QueuedMessage => {
34
+ const msg: QueuedMessage = {
35
+ id: this.nextId(),
36
+ text,
37
+ createdAt: Date.now(),
38
+ };
39
+ this.queue = [...this.queue, msg];
40
+ return msg;
41
+ };
42
+
43
+ remove = (id: string): boolean => {
44
+ const len = this.queue.length;
45
+ this.queue = this.queue.filter((m) => m.id !== id);
46
+ return this.queue.length < len;
47
+ };
48
+
49
+ clear = (): void => {
50
+ this.queue = [];
51
+ };
52
+
53
+ drainAll = (): QueuedMessage[] => {
54
+ const messages = this.queue;
55
+ this.queue = [];
56
+ return messages;
57
+ };
58
+
59
+ /**
60
+ * Merge all queued message texts into a single prompt string.
61
+ * Uses double newline as separator.
62
+ */
63
+ static mergeTexts = (messages: QueuedMessage[]): string => {
64
+ return messages.map((m) => m.text).join('\n\n');
65
+ };
66
+ }
@@ -0,0 +1,124 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { summarizeToolUse, toolIcon } from './tool_use_summary.ts';
4
+
5
+ describe('summarizeToolUse', () => {
6
+ it('summarizes Read with file path', () => {
7
+ assert.equal(
8
+ summarizeToolUse('Read', { file_path: 'src/auth.ts' }),
9
+ 'Read src/auth.ts',
10
+ );
11
+ });
12
+
13
+ it('summarizes Read without file path', () => {
14
+ assert.equal(summarizeToolUse('Read', {}), 'Read file');
15
+ });
16
+
17
+ it('summarizes Write with file path', () => {
18
+ assert.equal(
19
+ summarizeToolUse('Write', { file_path: 'src/index.ts' }),
20
+ 'Write src/index.ts',
21
+ );
22
+ });
23
+
24
+ it('summarizes Edit with file path', () => {
25
+ assert.equal(
26
+ summarizeToolUse('Edit', { file_path: 'src/main.ts' }),
27
+ 'Edit src/main.ts',
28
+ );
29
+ });
30
+
31
+ it('summarizes Bash with command', () => {
32
+ assert.equal(
33
+ summarizeToolUse('Bash', { command: 'npm test' }),
34
+ 'Bash: npm test',
35
+ );
36
+ });
37
+
38
+ it('truncates long Bash commands', () => {
39
+ const longCmd = 'a'.repeat(200);
40
+ const result = summarizeToolUse('Bash', { command: longCmd });
41
+ assert.ok(result.length <= 121); // 120 + ellipsis char
42
+ assert.ok(result.endsWith('…'));
43
+ });
44
+
45
+ it('summarizes Grep with pattern and path', () => {
46
+ assert.equal(
47
+ summarizeToolUse('Grep', { pattern: 'TODO', path: 'src/' }),
48
+ 'Grep "TODO" in src/',
49
+ );
50
+ });
51
+
52
+ it('summarizes Grep with pattern only', () => {
53
+ assert.equal(
54
+ summarizeToolUse('Grep', { pattern: 'import' }),
55
+ 'Grep "import"',
56
+ );
57
+ });
58
+
59
+ it('summarizes Glob with pattern', () => {
60
+ assert.equal(
61
+ summarizeToolUse('Glob', { pattern: '**/*.ts' }),
62
+ 'Glob **/*.ts',
63
+ );
64
+ });
65
+
66
+ it('summarizes Glob with pattern and path', () => {
67
+ assert.equal(
68
+ summarizeToolUse('Glob', { pattern: '*.tsx', path: 'src/components' }),
69
+ 'Glob *.tsx in src/components',
70
+ );
71
+ });
72
+
73
+ it('summarizes Agent with description', () => {
74
+ assert.equal(
75
+ summarizeToolUse('Agent', { description: 'Explore codebase' }),
76
+ 'Agent: Explore codebase',
77
+ );
78
+ });
79
+
80
+ it('summarizes Agent without description', () => {
81
+ assert.equal(summarizeToolUse('Agent', {}), 'Agent');
82
+ });
83
+
84
+ it('summarizes TodoWrite', () => {
85
+ assert.equal(summarizeToolUse('TodoWrite', {}), 'Update task list');
86
+ });
87
+
88
+ it('summarizes WebSearch with query', () => {
89
+ assert.equal(
90
+ summarizeToolUse('WebSearch', { query: 'react 19 features' }),
91
+ 'Search: react 19 features',
92
+ );
93
+ });
94
+
95
+ it('summarizes WebFetch with url', () => {
96
+ assert.equal(
97
+ summarizeToolUse('WebFetch', { url: 'https://example.com' }),
98
+ 'Fetch: https://example.com',
99
+ );
100
+ });
101
+
102
+ it('returns tool name for unknown tools', () => {
103
+ assert.equal(summarizeToolUse('CustomTool', { foo: 'bar' }), 'CustomTool');
104
+ });
105
+ });
106
+
107
+ describe('toolIcon', () => {
108
+ it('returns correct icons for known tools', () => {
109
+ assert.equal(toolIcon('Read'), '📖');
110
+ assert.equal(toolIcon('Write'), '📝');
111
+ assert.equal(toolIcon('Edit'), '✏️');
112
+ assert.equal(toolIcon('Bash'), '🔧');
113
+ assert.equal(toolIcon('Grep'), '🔍');
114
+ assert.equal(toolIcon('Glob'), '📂');
115
+ assert.equal(toolIcon('Agent'), '🤖');
116
+ assert.equal(toolIcon('TodoWrite'), '📋');
117
+ assert.equal(toolIcon('WebSearch'), '🌐');
118
+ assert.equal(toolIcon('WebFetch'), '🌐');
119
+ });
120
+
121
+ it('returns default icon for unknown tools', () => {
122
+ assert.equal(toolIcon('SomeTool'), '⚙️');
123
+ });
124
+ });
@@ -0,0 +1,112 @@
1
+ /**
2
+ * tool_use_summary — generates brief human-readable summaries for Claude tool
3
+ * invocations. Used by ToolUseCard to display a collapsed one-line description
4
+ * of what the tool is doing (e.g. "Read src/auth.ts", "Bash: npm test").
5
+ */
6
+
7
+ /** Maximum length for summary text before truncation. */
8
+ const MAX_SUMMARY_LEN = 120;
9
+
10
+ const truncate = (text: string, max: number): string =>
11
+ text.length > max ? `${text.slice(0, max)}…` : text;
12
+
13
+ /**
14
+ * Generate a brief summary string from a tool_use block's name and input.
15
+ * Returns a one-line description suitable for display in a collapsed card.
16
+ */
17
+ export const summarizeToolUse = (
18
+ name: string,
19
+ input: Record<string, unknown>,
20
+ ): string => {
21
+ switch (name) {
22
+ case 'Read': {
23
+ const path = input.file_path as string | undefined;
24
+ return path ? `Read ${path}` : 'Read file';
25
+ }
26
+
27
+ case 'Write': {
28
+ const path = input.file_path as string | undefined;
29
+ return path ? `Write ${path}` : 'Write file';
30
+ }
31
+
32
+ case 'Edit': {
33
+ const path = input.file_path as string | undefined;
34
+ return path ? `Edit ${path}` : 'Edit file';
35
+ }
36
+
37
+ case 'Bash': {
38
+ const cmd = input.command as string | undefined;
39
+ return cmd ? truncate(`Bash: ${cmd}`, MAX_SUMMARY_LEN) : 'Bash';
40
+ }
41
+
42
+ case 'Grep': {
43
+ const pattern = input.pattern as string | undefined;
44
+ const path = input.path as string | undefined;
45
+ const parts = ['Grep'];
46
+ if (pattern) parts.push(`"${pattern}"`);
47
+ if (path) parts.push(`in ${path}`);
48
+ return truncate(parts.join(' '), MAX_SUMMARY_LEN);
49
+ }
50
+
51
+ case 'Glob': {
52
+ const pattern = input.pattern as string | undefined;
53
+ const path = input.path as string | undefined;
54
+ const parts = ['Glob'];
55
+ if (pattern) parts.push(pattern);
56
+ if (path) parts.push(`in ${path}`);
57
+ return truncate(parts.join(' '), MAX_SUMMARY_LEN);
58
+ }
59
+
60
+ case 'Agent': {
61
+ const desc = input.description as string | undefined;
62
+ return desc ? truncate(`Agent: ${desc}`, MAX_SUMMARY_LEN) : 'Agent';
63
+ }
64
+
65
+ case 'TodoWrite': {
66
+ return 'Update task list';
67
+ }
68
+
69
+ case 'WebSearch': {
70
+ const query = input.query as string | undefined;
71
+ return query ? truncate(`Search: ${query}`, MAX_SUMMARY_LEN) : 'Web search';
72
+ }
73
+
74
+ case 'WebFetch': {
75
+ const url = input.url as string | undefined;
76
+ return url ? truncate(`Fetch: ${url}`, MAX_SUMMARY_LEN) : 'Web fetch';
77
+ }
78
+
79
+ default:
80
+ return name;
81
+ }
82
+ };
83
+
84
+ /**
85
+ * Return a short label for the tool type, used for the card icon area.
86
+ */
87
+ export const toolIcon = (name: string): string => {
88
+ switch (name) {
89
+ case 'Read':
90
+ return '📖';
91
+ case 'Write':
92
+ return '📝';
93
+ case 'Edit':
94
+ return '✏️';
95
+ case 'Bash':
96
+ return '🔧';
97
+ case 'Grep':
98
+ return '🔍';
99
+ case 'Glob':
100
+ return '📂';
101
+ case 'Agent':
102
+ return '🤖';
103
+ case 'TodoWrite':
104
+ return '📋';
105
+ case 'WebSearch':
106
+ return '🌐';
107
+ case 'WebFetch':
108
+ return '🌐';
109
+ default:
110
+ return '⚙️';
111
+ }
112
+ };
@@ -1,6 +1,8 @@
1
1
  import React from 'react';
2
2
  import { AgentsView } from '../components/AgentsView';
3
+ import { useDocumentTitle } from '../hooks/useDocumentTitle';
3
4
 
4
5
  export function AgentsRoute() {
6
+ useDocumentTitle('Agents');
5
7
  return <AgentsView />;
6
8
  }
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import { ChatView } from '../components/ChatView';
3
+ import { useDocumentTitle } from '../hooks/useDocumentTitle';
4
+
5
+ export function ChatRoute() {
6
+ useDocumentTitle('Chat');
7
+ return <ChatView visible={true} />;
8
+ }
@@ -3,8 +3,10 @@ import { CoherenceView } from '../components/CoherenceView';
3
3
  import { useGraphStore } from '../stores/useGraphStore';
4
4
  import { useProjectStore } from '../stores/useProjectStore';
5
5
  import { useSidePanelStore } from '../stores/useSidePanelStore';
6
+ import { useDocumentTitle } from '../hooks/useDocumentTitle';
6
7
 
7
8
  export function CoherenceRoute() {
9
+ useDocumentTitle('Coherence');
8
10
  const graphData = useGraphStore((s) => s.graphData);
9
11
  const selectedProjectId = useProjectStore((s) => s.selectedProjectId);
10
12
  const openSidePanel = useSidePanelStore((s) => s.open);
@@ -3,10 +3,8 @@ import { Outlet } from 'react-router-dom';
3
3
  import { Toolbar } from '../components/Toolbar';
4
4
  import { SidePanel } from '../components/SidePanel';
5
5
  import { QaIssueSheet } from '../components/QaIssueSheet';
6
- import { GitRepoModal } from '../components/GitRepoModal';
7
6
  import { useSidePanelStore } from '../stores/useSidePanelStore';
8
7
  import { useQaSheetStore } from '../stores/useQaSheetStore';
9
- import { useGitModalStore } from '../stores/useGitModalStore';
10
8
  import { useGraphUIStore } from '../stores/useGraphUIStore';
11
9
  import { useProjects } from '../hooks/useProjects';
12
10
  import { useGraph } from '../hooks/useGraph';
@@ -19,7 +17,6 @@ export function DashboardLayout() {
19
17
 
20
18
  const closeSidePanel = useSidePanelStore((s) => s.close);
21
19
  const closeQaSheet = useQaSheetStore((s) => s.close);
22
- const gitModalProject = useGitModalStore((s) => s.project);
23
20
  const setSettingsOpen = useGraphUIStore((s) => s.setSettingsOpen);
24
21
 
25
22
  // Global keyboard handler
@@ -47,7 +44,6 @@ export function DashboardLayout() {
47
44
  <QaIssueSheet />
48
45
  </div>
49
46
 
50
- {gitModalProject && <GitRepoModal />}
51
47
  </div>
52
48
  </ToastProvider>
53
49
  );
@@ -1,6 +1,8 @@
1
1
  import React from 'react';
2
2
  import { DesignSystemView } from '../components/DesignSystemView';
3
+ import { useDocumentTitle } from '../hooks/useDocumentTitle';
3
4
 
4
5
  export function DesignSystemRoute() {
6
+ useDocumentTitle('Design System');
5
7
  return <DesignSystemView visible={true} />;
6
8
  }
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
2
  import { FilesView } from '../components/FilesView';
3
3
  import { useProjectStore } from '../stores/useProjectStore';
4
+ import { useDocumentTitle } from '../hooks/useDocumentTitle';
4
5
 
5
6
  export function FilesRoute() {
7
+ useDocumentTitle('Files');
6
8
  const selectedProjectId = useProjectStore((s) => s.selectedProjectId);
7
9
 
8
10
  return (
@@ -6,8 +6,10 @@ import { GraphSettings } from '../components/GraphSettings';
6
6
  import { useGraphStore } from '../stores/useGraphStore';
7
7
  import { useGraphUIStore } from '../stores/useGraphUIStore';
8
8
  import { useSidePanelStore } from '../stores/useSidePanelStore';
9
+ import { useDocumentTitle } from '../hooks/useDocumentTitle';
9
10
 
10
11
  export function GraphRoute() {
12
+ useDocumentTitle('Graph');
11
13
  const graphData = useGraphStore((s) => s.graphData);
12
14
  const graphError = useGraphStore((s) => s.error);
13
15
  const openSidePanel = useSidePanelStore((s) => s.open);
@@ -4,8 +4,10 @@ import { useGraphStore } from '../stores/useGraphStore';
4
4
  import { useProjectStore } from '../stores/useProjectStore';
5
5
  import { useSidePanelStore } from '../stores/useSidePanelStore';
6
6
  import { useQaSheetStore } from '../stores/useQaSheetStore';
7
+ import { useDocumentTitle } from '../hooks/useDocumentTitle';
7
8
 
8
9
  export function KanbanRoute() {
10
+ useDocumentTitle('Kanban');
9
11
  const graphData = useGraphStore((s) => s.graphData);
10
12
  const selectedProjectId = useProjectStore((s) => s.selectedProjectId);
11
13
  const openSidePanel = useSidePanelStore((s) => s.open);
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
2
  import { TerminalView } from '../components/TerminalView';
3
3
  import { useProjectStore } from '../stores/useProjectStore';
4
+ import { useDocumentTitle } from '../hooks/useDocumentTitle';
4
5
 
5
6
  export function TerminalRoute() {
7
+ useDocumentTitle('Terminal');
6
8
  const projects = useProjectStore((s) => s.projects);
7
9
 
8
10
  return <TerminalView visible={true} projects={projects} />;
@@ -1,6 +1,8 @@
1
1
  import React from 'react';
2
2
  import { UsersView } from '../components/UsersView';
3
+ import { useDocumentTitle } from '../hooks/useDocumentTitle';
3
4
 
4
5
  export function UsersRoute() {
6
+ useDocumentTitle('Users');
5
7
  return <UsersView visible={true} />;
6
8
  }
@@ -1,8 +1,10 @@
1
1
  import React from 'react';
2
2
  import { VideographyView } from '../components/VideographyView';
3
3
  import { useProjectStore } from '../stores/useProjectStore';
4
+ import { useDocumentTitle } from '../hooks/useDocumentTitle';
4
5
 
5
6
  export function VideographyRoute() {
7
+ useDocumentTitle('Videography');
6
8
  const selectedProjectId = useProjectStore((s) => s.selectedProjectId);
7
9
 
8
10
  return (
@@ -1,6 +1,8 @@
1
1
  import React from 'react';
2
2
  import { WorkflowsView } from '../components/WorkflowsView';
3
+ import { useDocumentTitle } from '../hooks/useDocumentTitle';
3
4
 
4
5
  export function WorkflowsRoute() {
6
+ useDocumentTitle('Workflows');
5
7
  return <WorkflowsView />;
6
8
  }
@@ -2,8 +2,10 @@ import React from 'react';
2
2
  import { Navigate, useNavigate } from 'react-router-dom';
3
3
  import { AcceptInvitationPage } from '../pages/accept_invitation_page';
4
4
  import { useAuth } from '../hooks/useAuth';
5
+ import { useDocumentTitle } from '../hooks/useDocumentTitle';
5
6
 
6
7
  export function AcceptInvitationRoute() {
8
+ useDocumentTitle('Accept Invitation');
7
9
  const { user, loading } = useAuth();
8
10
  const navigate = useNavigate();
9
11
 
@@ -2,8 +2,10 @@ import React from 'react';
2
2
  import { Navigate } from 'react-router-dom';
3
3
  import { ForgotPasswordPage } from '../pages/forgot_password_page';
4
4
  import { useAuth } from '../hooks/useAuth';
5
+ import { useDocumentTitle } from '../hooks/useDocumentTitle';
5
6
 
6
7
  export function ForgotPasswordRoute() {
8
+ useDocumentTitle('Forgot Password');
7
9
  const { user, loading } = useAuth();
8
10
 
9
11
  if (loading) return null;
@@ -2,8 +2,10 @@ import React from 'react';
2
2
  import { Navigate, useNavigate } from 'react-router-dom';
3
3
  import { LoginPage } from '../components/LoginPage';
4
4
  import { useAuth } from '../hooks/useAuth';
5
+ import { useDocumentTitle } from '../hooks/useDocumentTitle';
5
6
 
6
7
  export function LoginRoute() {
8
+ useDocumentTitle('Login');
7
9
  const { user, loading } = useAuth();
8
10
  const navigate = useNavigate();
9
11
 
@@ -2,8 +2,10 @@ import React from 'react';
2
2
  import { Navigate, useNavigate } from 'react-router-dom';
3
3
  import { RegisterPage } from '../pages/register_page';
4
4
  import { useAuth } from '../hooks/useAuth';
5
+ import { useDocumentTitle } from '../hooks/useDocumentTitle';
5
6
 
6
7
  export function RegisterRoute() {
8
+ useDocumentTitle('Register');
7
9
  const { user, loading } = useAuth();
8
10
  const navigate = useNavigate();
9
11