@assistkick/create 1.7.0 → 1.8.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 (200) hide show
  1. package/dist/bin/create.js +0 -0
  2. package/package.json +9 -7
  3. package/templates/assistkick-product-system/.env.example +1 -0
  4. package/templates/assistkick-product-system/local.db +0 -0
  5. package/templates/assistkick-product-system/package.json +4 -2
  6. package/templates/assistkick-product-system/packages/backend/package.json +2 -0
  7. package/templates/assistkick-product-system/packages/backend/src/routes/agents.ts +165 -0
  8. package/templates/assistkick-product-system/packages/backend/src/routes/files.test.ts +358 -0
  9. package/templates/assistkick-product-system/packages/backend/src/routes/files.ts +356 -0
  10. package/templates/assistkick-product-system/packages/backend/src/routes/git.ts +96 -1
  11. package/templates/assistkick-product-system/packages/backend/src/routes/graph.ts +1 -0
  12. package/templates/assistkick-product-system/packages/backend/src/routes/kanban.ts +43 -4
  13. package/templates/assistkick-product-system/packages/backend/src/routes/pipeline.ts +200 -84
  14. package/templates/assistkick-product-system/packages/backend/src/routes/projects.ts +6 -3
  15. package/templates/assistkick-product-system/packages/backend/src/routes/terminal.ts +53 -17
  16. package/templates/assistkick-product-system/packages/backend/src/routes/video.ts +218 -0
  17. package/templates/assistkick-product-system/packages/backend/src/routes/workflow_groups.ts +119 -0
  18. package/templates/assistkick-product-system/packages/backend/src/routes/workflows.ts +154 -0
  19. package/templates/assistkick-product-system/packages/backend/src/server.ts +81 -9
  20. package/templates/assistkick-product-system/packages/backend/src/services/agent_service.test.ts +489 -0
  21. package/templates/assistkick-product-system/packages/backend/src/services/agent_service.ts +416 -0
  22. package/templates/assistkick-product-system/packages/backend/src/services/bundle_service.test.ts +189 -0
  23. package/templates/assistkick-product-system/packages/backend/src/services/bundle_service.ts +182 -0
  24. package/templates/assistkick-product-system/packages/backend/src/services/init.ts +28 -78
  25. package/templates/assistkick-product-system/packages/backend/src/services/project_service.test.ts +16 -0
  26. package/templates/assistkick-product-system/packages/backend/src/services/project_service.ts +73 -2
  27. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.test.ts +4 -4
  28. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.ts +87 -11
  29. package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.test.ts +210 -69
  30. package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.ts +210 -215
  31. package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.test.ts +162 -0
  32. package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.ts +148 -0
  33. package/templates/assistkick-product-system/packages/backend/src/services/terminal_ws_handler.ts +11 -5
  34. package/templates/assistkick-product-system/packages/backend/src/services/tts_service.test.ts +64 -0
  35. package/templates/assistkick-product-system/packages/backend/src/services/tts_service.ts +134 -0
  36. package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.test.ts +256 -0
  37. package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.ts +258 -0
  38. package/templates/assistkick-product-system/packages/backend/src/services/workflow_group_service.ts +106 -0
  39. package/templates/assistkick-product-system/packages/backend/src/services/workflow_service.test.ts +275 -0
  40. package/templates/assistkick-product-system/packages/backend/src/services/workflow_service.ts +222 -0
  41. package/templates/assistkick-product-system/packages/frontend/package-lock.json +3455 -0
  42. package/templates/assistkick-product-system/packages/frontend/package.json +6 -0
  43. package/templates/assistkick-product-system/packages/frontend/src/App.tsx +8 -0
  44. package/templates/assistkick-product-system/packages/frontend/src/api/client.ts +456 -16
  45. package/templates/assistkick-product-system/packages/frontend/src/api/client_files.test.ts +172 -0
  46. package/templates/assistkick-product-system/packages/frontend/src/api/client_video.test.ts +238 -0
  47. package/templates/assistkick-product-system/packages/frontend/src/components/AgentsView.tsx +307 -0
  48. package/templates/assistkick-product-system/packages/frontend/src/components/CoherenceView.tsx +82 -66
  49. package/templates/assistkick-product-system/packages/frontend/src/components/CompositionPlaceholder.tsx +97 -0
  50. package/templates/assistkick-product-system/packages/frontend/src/components/DesignSystemView.tsx +20 -0
  51. package/templates/assistkick-product-system/packages/frontend/src/components/EditorTabBar.tsx +57 -0
  52. package/templates/assistkick-product-system/packages/frontend/src/components/FileTree.tsx +313 -0
  53. package/templates/assistkick-product-system/packages/frontend/src/components/FileTreeContextMenu.tsx +61 -0
  54. package/templates/assistkick-product-system/packages/frontend/src/components/FileTreeInlineInput.tsx +73 -0
  55. package/templates/assistkick-product-system/packages/frontend/src/components/FilesView.tsx +404 -0
  56. package/templates/assistkick-product-system/packages/frontend/src/components/GitRepoModal.tsx +187 -56
  57. package/templates/assistkick-product-system/packages/frontend/src/components/GraphLegend.tsx +71 -73
  58. package/templates/assistkick-product-system/packages/frontend/src/components/GraphSettings.tsx +8 -8
  59. package/templates/assistkick-product-system/packages/frontend/src/components/GraphView.tsx +1 -1
  60. package/templates/assistkick-product-system/packages/frontend/src/components/InviteUserDialog.tsx +15 -11
  61. package/templates/assistkick-product-system/packages/frontend/src/components/KanbanView.tsx +202 -171
  62. package/templates/assistkick-product-system/packages/frontend/src/components/LoginPage.tsx +14 -14
  63. package/templates/assistkick-product-system/packages/frontend/src/components/ProjectSelector.tsx +54 -33
  64. package/templates/assistkick-product-system/packages/frontend/src/components/QaIssueSheet.tsx +32 -49
  65. package/templates/assistkick-product-system/packages/frontend/src/components/SidePanel.tsx +43 -48
  66. package/templates/assistkick-product-system/packages/frontend/src/components/TerminalView.tsx +121 -52
  67. package/templates/assistkick-product-system/packages/frontend/src/components/Toolbar.tsx +20 -14
  68. package/templates/assistkick-product-system/packages/frontend/src/components/UsersView.tsx +52 -52
  69. package/templates/assistkick-product-system/packages/frontend/src/components/VideoGallery.tsx +313 -0
  70. package/templates/assistkick-product-system/packages/frontend/src/components/VideographyView.tsx +250 -0
  71. package/templates/assistkick-product-system/packages/frontend/src/components/WorkflowsView.tsx +474 -0
  72. package/templates/assistkick-product-system/packages/frontend/src/components/ds/AccentBorderList.tsx +53 -0
  73. package/templates/assistkick-product-system/packages/frontend/src/components/ds/Button.tsx +87 -0
  74. package/templates/assistkick-product-system/packages/frontend/src/components/ds/ButtonGroup.tsx +29 -0
  75. package/templates/assistkick-product-system/packages/frontend/src/components/ds/ButtonShowcase.tsx +221 -0
  76. package/templates/assistkick-product-system/packages/frontend/src/components/ds/CardGlass.tsx +141 -0
  77. package/templates/assistkick-product-system/packages/frontend/src/components/ds/CompletionRing.tsx +30 -0
  78. package/templates/assistkick-product-system/packages/frontend/src/components/ds/ContentCard.tsx +34 -0
  79. package/templates/assistkick-product-system/packages/frontend/src/components/ds/IconButton.tsx +74 -0
  80. package/templates/assistkick-product-system/packages/frontend/src/components/ds/KanbanCard.tsx +103 -87
  81. package/templates/assistkick-product-system/packages/frontend/src/components/ds/KanbanCardShowcase.tsx +9 -188
  82. package/templates/assistkick-product-system/packages/frontend/src/components/ds/Kbd.tsx +11 -0
  83. package/templates/assistkick-product-system/packages/frontend/src/components/ds/KindBadge.tsx +21 -0
  84. package/templates/assistkick-product-system/packages/frontend/src/components/ds/NavBarSidekick.tsx +81 -37
  85. package/templates/assistkick-product-system/packages/frontend/src/components/ds/SidePanelShowcase.tsx +370 -0
  86. package/templates/assistkick-product-system/packages/frontend/src/components/ds/SideSheet.tsx +64 -0
  87. package/templates/assistkick-product-system/packages/frontend/src/components/ds/StatusDot.tsx +18 -0
  88. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/CheckCardPositionNode.tsx +36 -0
  89. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/CheckCycleCountNode.tsx +60 -0
  90. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/EndNode.tsx +42 -0
  91. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/GroupNode.tsx +189 -0
  92. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/NodePalette.tsx +123 -0
  93. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/RunAgentNode.tsx +51 -0
  94. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/SetCardMetadataNode.tsx +53 -0
  95. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/StartNode.tsx +18 -0
  96. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/TransitionCardNode.tsx +59 -0
  97. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowCanvas.tsx +335 -0
  98. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowMonitorModal.tsx +634 -0
  99. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/autoLayout.ts +103 -0
  100. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/edgeColors.ts +35 -0
  101. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/monitor_nodes.tsx +208 -0
  102. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.test.ts +119 -0
  103. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.ts +107 -0
  104. package/templates/assistkick-product-system/packages/frontend/src/constants/graph.ts +13 -11
  105. package/templates/assistkick-product-system/packages/frontend/src/hooks/useAutoSave.ts +75 -0
  106. package/templates/assistkick-product-system/packages/frontend/src/hooks/useToast.tsx +16 -3
  107. package/templates/assistkick-product-system/packages/frontend/src/pages/accept_invitation_page.tsx +30 -27
  108. package/templates/assistkick-product-system/packages/frontend/src/pages/forgot_password_page.tsx +18 -15
  109. package/templates/assistkick-product-system/packages/frontend/src/pages/register_page.tsx +21 -18
  110. package/templates/assistkick-product-system/packages/frontend/src/pages/reset_password_page.tsx +28 -25
  111. package/templates/assistkick-product-system/packages/frontend/src/routes/AgentsRoute.tsx +6 -0
  112. package/templates/assistkick-product-system/packages/frontend/src/routes/CoherenceRoute.tsx +1 -1
  113. package/templates/assistkick-product-system/packages/frontend/src/routes/DashboardLayout.tsx +2 -2
  114. package/templates/assistkick-product-system/packages/frontend/src/routes/FilesRoute.tsx +13 -0
  115. package/templates/assistkick-product-system/packages/frontend/src/routes/GraphRoute.tsx +2 -2
  116. package/templates/assistkick-product-system/packages/frontend/src/routes/VideographyRoute.tsx +13 -0
  117. package/templates/assistkick-product-system/packages/frontend/src/routes/WorkflowsRoute.tsx +6 -0
  118. package/templates/assistkick-product-system/packages/frontend/src/stores/useProjectStore.ts +6 -3
  119. package/templates/assistkick-product-system/packages/frontend/src/stores/useSidePanelStore.ts +4 -4
  120. package/templates/assistkick-product-system/packages/frontend/src/styles/index.css +275 -3535
  121. package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.test.ts +167 -0
  122. package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.ts +101 -0
  123. package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.test.ts +42 -0
  124. package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.ts +17 -0
  125. package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.test.ts +145 -0
  126. package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.ts +42 -0
  127. package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.test.ts +4 -10
  128. package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.ts +19 -1
  129. package/templates/assistkick-product-system/packages/frontend/vite.config.ts +5 -0
  130. package/templates/assistkick-product-system/packages/shared/db/local.db +0 -0
  131. package/templates/assistkick-product-system/packages/shared/db/migrations/0004_tidy_matthew_murdock.sql +9 -0
  132. package/templates/assistkick-product-system/packages/shared/db/migrations/0005_mysterious_falcon.sql +692 -0
  133. package/templates/assistkick-product-system/packages/shared/db/migrations/0006_next_venom.sql +9 -0
  134. package/templates/assistkick-product-system/packages/shared/db/migrations/0007_deep_barracuda.sql +39 -0
  135. package/templates/assistkick-product-system/packages/shared/db/migrations/0008_puzzling_hannibal_king.sql +1 -0
  136. package/templates/assistkick-product-system/packages/shared/db/migrations/0009_amused_beast.sql +8 -0
  137. package/templates/assistkick-product-system/packages/shared/db/migrations/0010_spotty_moira_mactaggert.sql +9 -0
  138. package/templates/assistkick-product-system/packages/shared/db/migrations/0011_goofy_snowbird.sql +3 -0
  139. package/templates/assistkick-product-system/packages/shared/db/migrations/0011_supreme_doctor_octopus.sql +3 -0
  140. package/templates/assistkick-product-system/packages/shared/db/migrations/0013_reflective_prowler.sql +15 -0
  141. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0004_snapshot.json +921 -0
  142. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0005_snapshot.json +1042 -0
  143. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0006_snapshot.json +1101 -0
  144. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0007_snapshot.json +1336 -0
  145. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0008_snapshot.json +1275 -0
  146. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0009_snapshot.json +1327 -0
  147. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0010_snapshot.json +1393 -0
  148. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0011_snapshot.json +1436 -0
  149. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0013_snapshot.json +1538 -0
  150. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/_journal.json +70 -0
  151. package/templates/assistkick-product-system/packages/shared/db/schema.ts +113 -0
  152. package/templates/assistkick-product-system/packages/shared/lib/claude-service.ts +32 -7
  153. package/templates/assistkick-product-system/packages/shared/lib/constants.ts +9 -0
  154. package/templates/assistkick-product-system/packages/shared/lib/git_workflow.ts +12 -4
  155. package/templates/assistkick-product-system/packages/shared/lib/graph.ts +5 -0
  156. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.test.ts +1753 -0
  157. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.ts +1281 -0
  158. package/templates/assistkick-product-system/packages/shared/lib/workflow_orchestrator.ts +211 -0
  159. package/templates/assistkick-product-system/packages/shared/tools/add_node.test.ts +43 -0
  160. package/templates/assistkick-product-system/packages/shared/tools/add_node.ts +13 -2
  161. package/templates/assistkick-product-system/packages/shared/tools/get_kanban.ts +1 -1
  162. package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.test.ts +226 -0
  163. package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.ts +251 -0
  164. package/templates/assistkick-product-system/packages/shared/tools/update_node.ts +2 -2
  165. package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.test.ts +10 -0
  166. package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.ts +6 -0
  167. package/templates/assistkick-product-system/packages/video/Root.tsx +85 -0
  168. package/templates/assistkick-product-system/packages/video/components/email_scene.tsx +231 -0
  169. package/templates/assistkick-product-system/packages/video/components/outro_scene.tsx +153 -0
  170. package/templates/assistkick-product-system/packages/video/components/part_divider.tsx +90 -0
  171. package/templates/assistkick-product-system/packages/video/components/scene.tsx +226 -0
  172. package/templates/assistkick-product-system/packages/video/components/theme.ts +22 -0
  173. package/templates/assistkick-product-system/packages/video/components/title_scene.tsx +169 -0
  174. package/templates/assistkick-product-system/packages/video/components/video_split_layout.tsx +84 -0
  175. package/templates/assistkick-product-system/packages/video/compositions/.gitkeep +0 -0
  176. package/templates/assistkick-product-system/packages/video/index.ts +4 -0
  177. package/templates/assistkick-product-system/packages/video/package.json +28 -0
  178. package/templates/assistkick-product-system/packages/video/remotion.config.ts +11 -0
  179. package/templates/assistkick-product-system/packages/video/scripts/process_script.test.ts +326 -0
  180. package/templates/assistkick-product-system/packages/video/scripts/process_script.ts +630 -0
  181. package/templates/assistkick-product-system/packages/video/style.css +1 -0
  182. package/templates/assistkick-product-system/packages/video/tsconfig.json +18 -0
  183. package/templates/assistkick-product-system/tests/graph_legend.test.ts +2 -1
  184. package/templates/assistkick-product-system/tests/video_render_service.test.ts +179 -0
  185. package/templates/assistkick-product-system/tests/web_terminal.test.ts +219 -455
  186. package/templates/assistkick-product-system/tests/workflow_integration.test.ts +341 -0
  187. package/templates/skills/assistkick-developer/SKILL.md +3 -0
  188. package/templates/skills/assistkick-developer/references/react_development_guidelines.md +225 -0
  189. package/templates/skills/product-system/graph.json +1890 -0
  190. package/templates/skills/product-system/kanban.json +304 -0
  191. package/templates/skills/product-system/nodes/comp_001.md +56 -0
  192. package/templates/skills/product-system/nodes/comp_002.md +57 -0
  193. package/templates/skills/product-system/nodes/data_001.md +51 -0
  194. package/templates/skills/product-system/nodes/data_002.md +40 -0
  195. package/templates/skills/product-system/nodes/data_004.md +38 -0
  196. package/templates/skills/product-system/nodes/dec_001.md +34 -0
  197. package/templates/skills/product-system/nodes/dec_016.md +32 -0
  198. package/templates/skills/product-system/nodes/feat_008.md +30 -0
  199. package/templates/skills/video-composition-agent/SKILL.md +232 -0
  200. package/templates/skills/video-script-writer/SKILL.md +136 -0
@@ -1,7 +1,8 @@
1
1
  import React from 'react';
2
2
  import {
3
- Play, AlertTriangle, Lock, CircleDot, Copy, Check,
3
+ Play, AlertTriangle, Lock, CircleDot, Copy, Check, ChevronDown,
4
4
  } from 'lucide-react';
5
+ import { KindBadge } from './KindBadge';
5
6
 
6
7
  /* ── Types ── */
7
8
 
@@ -13,36 +14,43 @@ export interface KanbanCardProps {
13
14
  kind?: string;
14
15
  rejectionCount: number;
15
16
  blocked: boolean;
16
- /** Pipeline status string */
17
+ /** Workflow execution status string */
17
18
  pipeline: string;
18
- /** Pipeline badge label (e.g. "Retry 2", "Running...") */
19
+ /** Status badge label (e.g. "Running", "Completed") */
19
20
  pipelineLabel?: string;
20
- /** Tool call counts — { Read: 6, Tools: 11, Write: 3, Edit: 2 } */
21
- toolCalls?: Record<string, number>;
22
- /** Session meta pills — e.g. ["Ctx 17%", "19t", "$0.3948", "opus-4"] */
23
- meta?: string[];
24
- /** Stop reason (e.g. "end_turn", "max_turns") */
25
- stopReason?: string;
26
21
  /** Number of issues / notes */
27
22
  issueCount: number;
28
23
  /** Issue button label override */
29
24
  issueLabel?: string;
30
25
  /** Show play button */
31
26
  showPlay?: boolean;
27
+ /** Show dropdown arrow next to play button for workflow selection */
28
+ showPlayDropdown?: boolean;
29
+ /** Show stop button (for epic cards when orchestrator is running) */
30
+ showStop?: boolean;
32
31
  /** Show resume button */
33
32
  showResume?: boolean;
34
33
  /** Whether copy was just triggered */
35
34
  copied?: boolean;
36
35
  /** Is this the currently-playing card in Play All */
37
36
  playAllActive?: boolean;
37
+ /** Name of the currently executing agent (when on a RunAgent node) */
38
+ agentName?: string | null;
39
+ /** Error message from a failed workflow execution */
40
+ errorMessage?: string | null;
41
+ /** Whether this card represents an epic node */
42
+ isEpic?: boolean;
38
43
 
39
44
  /* Callbacks */
40
45
  onClick?: () => void;
41
46
  onCopy?: () => void;
42
47
  onPlay?: () => void;
48
+ onPlayDropdown?: () => void;
49
+ onStop?: () => void;
43
50
  onResume?: () => void;
44
51
  onUnblock?: () => void;
45
52
  onIssuesClick?: () => void;
53
+ onMonitorClick?: () => void;
46
54
 
47
55
  /* Drag */
48
56
  draggable?: boolean;
@@ -50,57 +58,41 @@ export interface KanbanCardProps {
50
58
  onDragEnd?: (e: React.DragEvent) => void;
51
59
  }
52
60
 
53
- /* ── Pipeline style helpers ── */
61
+ /* ── Status style helpers ── */
54
62
 
55
- function pipelineCls(status: string): string {
56
- if (['developing', 'reviewing', 'testing', 'committing'].includes(status)) return 'bg-accent/10 text-accent animate-pulse';
63
+ function statusCls(status: string): string {
64
+ if (status === 'running') return 'bg-accent/10 text-accent animate-pulse';
57
65
  switch (status) {
58
66
  case 'completed': return 'bg-emerald-500/10 text-emerald-400';
59
67
  case 'failed': return 'bg-error/10 text-error';
60
- case 'blocked': return 'bg-error/10 text-error';
61
- case 'interrupted': return 'bg-amber-500/10 text-amber-400';
62
68
  default: return 'bg-white/[0.06] text-content-muted';
63
69
  }
64
70
  }
65
71
 
66
- function pipelineIcon(status: string) {
67
- if (['developing', 'reviewing', 'testing', 'committing'].includes(status)) return <CircleDot size={12} strokeWidth={2.5} />;
72
+ function statusIcon(status: string) {
73
+ if (status === 'running') return <CircleDot size={12} strokeWidth={2.5} />;
68
74
  if (status === 'failed') return <AlertTriangle size={12} strokeWidth={2.5} />;
69
75
  return null;
70
76
  }
71
77
 
72
- /* ── Kind badge ── */
78
+ /* ── Component ── */
73
79
 
74
- function KindBadge({ kind }: { kind: string }) {
75
- if (!kind || kind === 'new') return null;
76
- const cls = kind === 'improvement'
77
- ? 'text-blue-400 bg-blue-400/15'
78
- : 'text-amber-400 bg-amber-400/15';
79
- return (
80
- <span className={`rounded px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide ${cls}`}>
81
- {kind}
82
- </span>
83
- );
84
- }
85
-
86
- /* ── Card component ── */
87
-
88
- export function KanbanCard({
80
+ export const KanbanCard = ({
89
81
  id, name, pct, kind, rejectionCount, blocked, pipeline, pipelineLabel: pipLabel,
90
- toolCalls, meta, stopReason, issueCount, issueLabel, showPlay, showResume, copied,
91
- playAllActive,
92
- onClick, onCopy, onPlay, onResume, onUnblock, onIssuesClick,
82
+ issueCount, issueLabel, showPlay, showPlayDropdown, showStop, showResume, copied,
83
+ playAllActive, agentName, errorMessage,
84
+ isEpic,
85
+ onClick, onCopy, onPlay, onPlayDropdown, onStop, onResume, onUnblock, onIssuesClick, onMonitorClick,
93
86
  draggable, onDragStart, onDragEnd,
94
- }: KanbanCardProps) {
87
+ }: KanbanCardProps) => {
95
88
  const hasPipeline = pipeline !== 'idle' && !!pipLabel;
96
- const hasTools = toolCalls && Object.values(toolCalls).some(v => v > 0);
97
- const hasStatusRow = rejectionCount > 0 || hasPipeline || !!stopReason;
89
+ const hasStatusRow = rejectionCount > 0 || hasPipeline;
98
90
 
99
91
  const stripColor = blocked
100
92
  ? 'from-error/60 to-error/20'
101
93
  : rejectionCount >= 3
102
94
  ? 'from-error/40 to-amber-500/20'
103
- : ['developing', 'reviewing', 'testing', 'committing'].includes(pipeline)
95
+ : pipeline === 'running'
104
96
  ? 'from-accent/60 to-accent/20'
105
97
  : pipeline === 'completed'
106
98
  ? 'from-emerald-400/60 to-emerald-400/20'
@@ -115,7 +107,12 @@ export function KanbanCard({
115
107
 
116
108
  return (
117
109
  <div
118
- className="group relative shrink-0 overflow-hidden rounded-2xl bg-surface border border-edge shadow-lg shadow-black/10 backdrop-blur-sm transition-all duration-200 hover:border-content/15 hover:shadow-xl hover:shadow-black/15 cursor-pointer"
110
+ className={[
111
+ 'group relative shrink-0 overflow-hidden rounded-2xl border shadow-lg shadow-black/10 backdrop-blur-sm transition-all duration-200 hover:border-content/15 hover:shadow-xl hover:shadow-black/15 cursor-pointer',
112
+ isEpic
113
+ ? 'bg-gradient-to-br from-accent/[0.06] to-accent/[0.02] border-accent/15'
114
+ : 'bg-surface border-edge',
115
+ ].join(' ')}
119
116
  draggable={draggable}
120
117
  onDragStart={onDragStart}
121
118
  onDragEnd={onDragEnd}
@@ -142,18 +139,37 @@ export function KanbanCard({
142
139
  <span className="flex items-center gap-1.5 rounded-full bg-error/15 px-2.5 py-1 text-[11px] font-bold text-error backdrop-blur">
143
140
  <Lock size={11} strokeWidth={2.5} /> Blocked
144
141
  </span>
145
- ) : showPlay ? (
142
+ ) : showStop ? (
146
143
  <button
147
- className="flex h-7 w-7 items-center justify-center rounded-full bg-accent/10 text-accent backdrop-blur transition-all hover:bg-accent hover:text-surface hover:shadow-[0_0_12px_-2px_var(--accent)] cursor-pointer"
148
- title="Start automated development pipeline"
149
- onClick={(e) => { e.stopPropagation(); onPlay?.(); }}
144
+ className="flex h-7 w-7 items-center justify-center rounded-full border border-error text-error text-[12px] hover:bg-error hover:text-surface transition-colors cursor-pointer"
145
+ title="Stop processing epic"
146
+ onClick={(e) => { e.stopPropagation(); onStop?.(); }}
150
147
  >
151
- <Play size={12} strokeWidth={2.5} fill="currentColor" />
148
+ {'\u25A0'}
152
149
  </button>
150
+ ) : showPlay ? (
151
+ <div className="flex items-center">
152
+ <button
153
+ className="flex h-7 w-7 items-center justify-center rounded-full bg-accent/10 text-accent backdrop-blur transition-all hover:bg-accent hover:text-surface hover:shadow-[0_0_12px_-2px_var(--accent)] cursor-pointer"
154
+ title="Start default workflow"
155
+ onClick={(e) => { e.stopPropagation(); onPlay?.(); }}
156
+ >
157
+ <Play size={12} strokeWidth={2.5} fill="currentColor" />
158
+ </button>
159
+ {showPlayDropdown && (
160
+ <button
161
+ className="flex h-7 w-5 items-center justify-center rounded-r-full text-accent/60 hover:text-accent transition-colors cursor-pointer"
162
+ title="Choose workflow"
163
+ onClick={(e) => { e.stopPropagation(); onPlayDropdown?.(); }}
164
+ >
165
+ <ChevronDown size={12} strokeWidth={2.5} />
166
+ </button>
167
+ )}
168
+ </div>
153
169
  ) : showResume ? (
154
170
  <button
155
171
  className="flex h-7 w-7 items-center justify-center rounded-full bg-accent/10 text-accent backdrop-blur transition-all hover:bg-accent hover:text-surface hover:shadow-[0_0_12px_-2px_var(--accent)] cursor-pointer"
156
- title="Resume pipeline from last completed step"
172
+ title="Resume workflow from last completed step"
157
173
  onClick={(e) => { e.stopPropagation(); onResume?.(); }}
158
174
  >
159
175
  <Play size={12} strokeWidth={2.5} fill="currentColor" />
@@ -164,6 +180,26 @@ export function KanbanCard({
164
180
  {/* Name */}
165
181
  <div className="mt-2.5 text-[14px] font-semibold leading-snug text-content">{name}</div>
166
182
 
183
+ {/* Agent tag — shows when a RunAgent node is executing */}
184
+ {agentName && (
185
+ <div className="mt-2 inline-flex items-center gap-1.5 rounded-full bg-accent/10 px-2.5 py-1 text-[10px] font-semibold text-accent animate-pulse backdrop-blur">
186
+ <CircleDot size={10} strokeWidth={2.5} />
187
+ {agentName}
188
+ </div>
189
+ )}
190
+
191
+ {/* Error tag — shows when workflow failed, clickable to open monitor */}
192
+ {errorMessage && pipeline === 'failed' && (
193
+ <button
194
+ className="mt-2 inline-flex items-center gap-1.5 rounded-full bg-error/10 px-2.5 py-1 text-[10px] font-semibold text-error backdrop-blur cursor-pointer transition-opacity hover:opacity-80"
195
+ title={errorMessage}
196
+ onClick={(e) => { e.stopPropagation(); onMonitorClick?.(); }}
197
+ >
198
+ <AlertTriangle size={10} strokeWidth={2.5} />
199
+ Error
200
+ </button>
201
+ )}
202
+
167
203
  {/* Spec disc */}
168
204
  <div className="mt-3 flex items-center gap-2">
169
205
  <svg width="22" height="22" viewBox="0 0 22 22" className="shrink-0 -rotate-90">
@@ -183,46 +219,26 @@ export function KanbanCard({
183
219
  <span className="font-mono text-[11px] text-content-secondary">{pct}%</span>
184
220
  </div>
185
221
 
186
- {/* Stats pills */}
187
- {(hasTools || hasStatusRow) && (
188
- <div className="mt-3 space-y-1.5" onClick={(e) => e.stopPropagation()}>
189
- {/* Tool calls row */}
190
- {hasTools && (
191
- <div className="flex gap-1">
192
- {Object.entries(toolCalls!).filter(([, v]) => v > 0).map(([k, v]) => (
193
- <span key={k} className="flex-1 rounded-full bg-accent/10 py-1 text-center font-mono text-[10px] font-medium text-accent backdrop-blur">
194
- {k}: {v}
195
- </span>
196
- ))}
197
- </div>
198
- )}
199
- {/* Meta row */}
200
- {meta && meta.length > 0 && (
201
- <div className="flex gap-1">
202
- {meta.map(t => (
203
- <span key={t} className="flex-1 rounded-full bg-white/[0.08] py-1 text-center font-mono text-[10px] text-content backdrop-blur">{t}</span>
204
- ))}
205
- </div>
206
- )}
207
- {/* Status row: rejected + pipeline + stop reason */}
208
- {hasStatusRow && (
209
- <div className="flex gap-1">
210
- {rejectionCount > 0 && (
211
- <span className="flex-1 rounded-full bg-amber-400/15 py-1 text-center text-[10px] font-semibold text-amber-400 backdrop-blur">
212
- {rejectionCount}x rejected
213
- </span>
214
- )}
215
- {hasPipeline && (
216
- <span className={`flex-1 inline-flex items-center justify-center gap-1.5 rounded-full py-1 text-[10px] font-semibold backdrop-blur ${pipelineCls(pipeline)}`}>
217
- {pipelineIcon(pipeline)}
218
- {pipLabel}
219
- </span>
220
- )}
221
- {stopReason && (
222
- <span className="flex-1 rounded-full bg-accent-secondary/10 py-1 text-center font-mono text-[10px] text-accent-secondary backdrop-blur">{stopReason}</span>
223
- )}
224
- </div>
225
- )}
222
+ {/* Status pills */}
223
+ {hasStatusRow && (
224
+ <div className="mt-3" onClick={(e) => e.stopPropagation()}>
225
+ <div className="flex gap-1">
226
+ {rejectionCount > 0 && (
227
+ <span className="flex-1 rounded-full bg-amber-400/15 py-1 text-center text-[10px] font-semibold text-amber-400 backdrop-blur">
228
+ {rejectionCount}x rejected
229
+ </span>
230
+ )}
231
+ {hasPipeline && (
232
+ <button
233
+ className={`flex-1 inline-flex items-center justify-center gap-1.5 rounded-full py-1 text-[10px] font-semibold backdrop-blur cursor-pointer transition-opacity hover:opacity-80 ${statusCls(pipeline)}`}
234
+ onClick={(e) => { e.stopPropagation(); onMonitorClick?.(); }}
235
+ title="Open workflow monitor"
236
+ >
237
+ {statusIcon(pipeline)}
238
+ {pipLabel}
239
+ </button>
240
+ )}
241
+ </div>
226
242
  </div>
227
243
  )}
228
244
 
@@ -251,4 +267,4 @@ export function KanbanCard({
251
267
  </div>
252
268
  </div>
253
269
  );
254
- }
270
+ };
@@ -1,216 +1,37 @@
1
- import React from 'react';
2
- import {
3
- Play, AlertTriangle, Lock, CircleDot, Copy,
4
- } from 'lucide-react';
1
+ import { CardGlass } from './CardGlass';
2
+ import type { DemoCard } from './CardGlass';
5
3
 
6
4
  /* ── Demo data ── */
7
5
 
8
- interface DemoCard {
9
- id: string;
10
- name: string;
11
- pct: number;
12
- kind: 'new' | 'improvement' | 'bugfix';
13
- rejections: number;
14
- pipeline: 'idle' | 'active' | 'completed' | 'failed' | 'interrupted' | 'blocked';
15
- blocked: boolean;
16
- issues: number;
17
- stage: 'in_progress' | 'in_review' | 'qa';
18
- tools: { read: number; tools: number; write: number; edit: number };
19
- ctx: string;
20
- turns: number;
21
- cost: string;
22
- model: string;
23
- stop: string;
24
- }
25
-
26
6
  const DEMO_CARDS: DemoCard[] = [
27
7
  {
28
8
  id: 'feat_0f2d6616', name: 'TailwindCSS Integration', pct: 100, kind: 'new',
29
9
  rejections: 1, pipeline: 'interrupted', blocked: false, issues: 1,
30
- stage: 'in_review', tools: { read: 6, tools: 11, write: 0, edit: 0 },
31
- ctx: '17%', turns: 19, cost: '$0.3948', model: 'opus-4', stop: 'end_turn',
10
+ stage: 'in_review',
32
11
  },
33
12
  {
34
13
  id: 'feat_a3c71b02', name: 'Auth Flow Redesign', pct: 72, kind: 'improvement',
35
14
  rejections: 0, pipeline: 'active', blocked: false, issues: 0,
36
- stage: 'in_progress', tools: { read: 12, tools: 4, write: 3, edit: 7 },
37
- ctx: '34%', turns: 26, cost: '$0.5120', model: 'sonnet-4', stop: '',
15
+ stage: 'in_progress',
38
16
  },
39
17
  {
40
18
  id: 'feat_9e45f1d8', name: 'WebSocket Events', pct: 45, kind: 'new',
41
19
  rejections: 3, pipeline: 'failed', blocked: false, issues: 3,
42
- stage: 'qa', tools: { read: 2, tools: 8, write: 1, edit: 0 },
43
- ctx: '9%', turns: 11, cost: '$0.1840', model: 'opus-4', stop: 'max_turns',
20
+ stage: 'qa',
44
21
  },
45
22
  {
46
23
  id: 'feat_b7d20cc4', name: 'Rate Limiter Middleware', pct: 88, kind: 'bugfix',
47
24
  rejections: 0, pipeline: 'idle', blocked: true, issues: 0,
48
- stage: 'in_progress', tools: { read: 0, tools: 0, write: 0, edit: 0 },
49
- ctx: '', turns: 0, cost: '', model: '', stop: '',
25
+ stage: 'in_progress',
50
26
  },
51
27
  ];
52
28
 
53
- /* ── Pipeline helpers ── */
54
-
55
- function pipelineStyle(status: DemoCard['pipeline']): string {
56
- switch (status) {
57
- case 'active': return 'bg-accent/10 text-accent animate-pulse';
58
- case 'completed': return 'bg-emerald-500/10 text-emerald-400';
59
- case 'failed': return 'bg-error/10 text-error';
60
- case 'blocked': return 'bg-error/10 text-error';
61
- case 'interrupted': return 'bg-amber-500/10 text-amber-400';
62
- default: return '';
63
- }
64
- }
65
-
66
- function pipelineLabel(status: DemoCard['pipeline'], rejections: number): string {
67
- if (rejections >= 1 && status !== 'idle') return `Retry ${rejections + 1}`;
68
- switch (status) {
69
- case 'active': return 'Running...';
70
- case 'completed': return 'Completed';
71
- case 'failed': return 'Failed';
72
- case 'blocked': return 'Blocked';
73
- case 'interrupted': return 'Interrupted';
74
- default: return '';
75
- }
76
- }
77
-
78
- /* ── Kind badge ── */
79
-
80
- function KindBadge({ kind }: { kind: DemoCard['kind'] }) {
81
- if (kind === 'new') return null;
82
- const cls = kind === 'improvement'
83
- ? 'text-blue-400 bg-blue-400/15'
84
- : 'text-amber-400 bg-amber-400/15';
85
- return (
86
- <span className={`rounded px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide ${cls}`}>
87
- {kind}
88
- </span>
89
- );
90
- }
91
-
92
- /* ── Glass Card ── */
93
-
94
- function CardGlass({ card }: { card: DemoCard }) {
95
- const hasTools = Object.values(card.tools).some(v => v > 0);
96
- const hasPipeline = card.pipeline !== 'idle';
97
- const hasStatusRow = card.rejections > 0 || hasPipeline || card.stop;
98
- const stripColor = card.blocked
99
- ? 'from-error/60 to-error/20'
100
- : card.rejections >= 3
101
- ? 'from-error/40 to-amber-500/20'
102
- : card.pipeline === 'active'
103
- ? 'from-accent/60 to-accent/20'
104
- : card.pipeline === 'completed'
105
- ? 'from-emerald-400/60 to-emerald-400/20'
106
- : 'from-edge to-transparent';
107
-
108
- return (
109
- <div className="group relative overflow-hidden rounded-2xl bg-surface border border-edge shadow-lg shadow-black/10 backdrop-blur-sm transition-all duration-200 hover:border-content/15 hover:shadow-xl hover:shadow-black/15">
110
- <div className={`absolute left-0 top-0 h-full w-1 bg-gradient-to-b ${stripColor}`} />
111
- <div className="p-5 pl-6">
112
- {/* Header */}
113
- <div className="flex items-center gap-2.5">
114
- <span className="font-mono text-[12px] text-content-secondary">{card.id}</span>
115
- <KindBadge kind={card.kind} />
116
- <button className="flex h-6 w-6 items-center justify-center rounded-md bg-white/[0.08] text-content-secondary transition-colors hover:bg-white/15 hover:text-content cursor-pointer">
117
- <Copy size={12} strokeWidth={2} />
118
- </button>
119
- <div className="flex-1" />
120
- {card.blocked ? (
121
- <span className="flex items-center gap-1.5 rounded-full bg-error/15 px-2.5 py-1 text-[11px] font-bold text-error backdrop-blur">
122
- <Lock size={11} strokeWidth={2.5} /> Blocked
123
- </span>
124
- ) : card.pipeline === 'idle' ? (
125
- <button className="flex h-7 w-7 items-center justify-center rounded-full bg-accent/10 text-accent backdrop-blur transition-all hover:bg-accent hover:text-surface hover:shadow-[0_0_12px_-2px_var(--accent)] cursor-pointer">
126
- <Play size={12} strokeWidth={2.5} fill="currentColor" />
127
- </button>
128
- ) : null}
129
- </div>
130
-
131
- {/* Name */}
132
- <div className="mt-3 text-[15px] font-semibold leading-snug text-content">{card.name}</div>
133
-
134
- {/* Spec disc */}
135
- <div className="mt-3.5 flex items-center gap-2.5">
136
- <svg width="24" height="24" viewBox="0 0 24 24" className="shrink-0 -rotate-90">
137
- <circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" strokeWidth="2.5" className="text-white/5" />
138
- <circle
139
- cx="12" cy="12" r="10" fill="none" strokeWidth="2.5"
140
- stroke="url(#specGrad)" strokeLinecap="round"
141
- strokeDasharray={`${(card.pct / 100) * 2 * Math.PI * 10} ${2 * Math.PI * 10}`}
142
- />
143
- <defs>
144
- <linearGradient id="specGrad" x1="0" y1="0" x2="1" y2="1">
145
- <stop offset="0%" stopColor="var(--completeness-fill)" />
146
- <stop offset="100%" stopColor="var(--accent)" stopOpacity="0.6" />
147
- </linearGradient>
148
- </defs>
149
- </svg>
150
- <span className="font-mono text-[12px] text-content-secondary">{card.pct}%</span>
151
- </div>
152
-
153
- {/* Stats — evenly spaced pills */}
154
- {(hasTools || hasStatusRow) && (
155
- <div className="mt-3.5 space-y-2">
156
- {hasTools && (
157
- <div className="flex gap-1.5">
158
- {Object.entries(card.tools).filter(([, v]) => v > 0).map(([k, v]) => (
159
- <span key={k} className="flex-1 rounded-full bg-accent/10 py-1 text-center font-mono text-[11px] font-medium text-accent backdrop-blur">
160
- {k.charAt(0).toUpperCase() + k.slice(1)}: {v}
161
- </span>
162
- ))}
163
- </div>
164
- )}
165
- {hasTools && (
166
- <div className="flex gap-1.5">
167
- {[card.ctx && `Ctx ${card.ctx}`, card.turns > 0 && `${card.turns}t`, card.cost, card.model].filter(Boolean).map(t => (
168
- <span key={t as string} className="flex-1 rounded-full bg-white/[0.08] py-1 text-center font-mono text-[11px] text-content backdrop-blur">{t}</span>
169
- ))}
170
- </div>
171
- )}
172
- {hasStatusRow && (
173
- <div className="flex gap-1.5">
174
- {card.rejections > 0 && (
175
- <span className="flex-1 rounded-full bg-amber-400/15 py-1 text-center text-[11px] font-semibold text-amber-400 backdrop-blur">
176
- {card.rejections}x rejected
177
- </span>
178
- )}
179
- {hasPipeline && (
180
- <span className={`flex-1 inline-flex items-center justify-center gap-1.5 rounded-full py-1 text-[11px] font-semibold backdrop-blur ${pipelineStyle(card.pipeline)}`}>
181
- {card.pipeline === 'active' && <CircleDot size={12} strokeWidth={2.5} />}
182
- {card.pipeline === 'failed' && <AlertTriangle size={12} strokeWidth={2.5} />}
183
- {pipelineLabel(card.pipeline, card.rejections)}
184
- </span>
185
- )}
186
- {card.stop && (
187
- <span className="flex-1 rounded-full bg-accent-secondary/10 py-1 text-center font-mono text-[11px] text-accent-secondary backdrop-blur">{card.stop}</span>
188
- )}
189
- </div>
190
- )}
191
- </div>
192
- )}
193
-
194
- {/* Issues */}
195
- <button className={[
196
- 'mt-3.5 w-full rounded-full border py-2 text-center text-[12px] font-mono backdrop-blur transition-all cursor-pointer',
197
- card.issues > 0
198
- ? 'border-white/10 text-content-secondary hover:border-accent/30 hover:text-accent'
199
- : 'border-white/5 text-content-muted/50 hover:border-white/10 hover:text-content-muted',
200
- ].join(' ')}>
201
- {card.issues > 0 ? `${card.issues} issue${card.issues !== 1 ? 's' : ''} reported` : 'No issues'}
202
- </button>
203
- </div>
204
- </div>
205
- );
206
- }
207
-
208
- /* ── Showcase export ── */
29
+ /* ── Component ── */
209
30
 
210
- export function KanbanCardShowcase() {
31
+ export const KanbanCardShowcase = () => {
211
32
  return (
212
33
  <div className="grid grid-cols-2 gap-3 lg:grid-cols-4">
213
34
  {DEMO_CARDS.map(c => <CardGlass key={c.id} card={c} />)}
214
35
  </div>
215
36
  );
216
- }
37
+ };
@@ -0,0 +1,11 @@
1
+ import type { ReactNode } from 'react';
2
+
3
+ interface Props {
4
+ children: ReactNode;
5
+ }
6
+
7
+ export const Kbd = ({ children }: Props) => (
8
+ <kbd className="inline-block px-1.5 py-px text-[11px] font-inherit bg-surface-raised border border-edge rounded text-content">
9
+ {children}
10
+ </kbd>
11
+ );
@@ -0,0 +1,21 @@
1
+ /* ── Types ── */
2
+
3
+ interface Props {
4
+ kind: string;
5
+ }
6
+
7
+ /* ── Component ── */
8
+
9
+ export const KindBadge = ({ kind }: Props) => {
10
+ if (!kind || kind === 'new') return null;
11
+
12
+ const cls = kind === 'improvement'
13
+ ? 'text-blue-400 bg-blue-400/15'
14
+ : 'text-amber-400 bg-amber-400/15';
15
+
16
+ return (
17
+ <span className={`rounded px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide ${cls}`}>
18
+ {kind}
19
+ </span>
20
+ );
21
+ };