@assistkick/create 1.7.0 → 1.9.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/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 +61 -6
  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 +158 -0
  19. package/templates/assistkick-product-system/packages/backend/src/server.ts +60 -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 +43 -77
  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 +245 -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 +458 -18
  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/IterationCommentModal.tsx +80 -0
  62. package/templates/assistkick-product-system/packages/frontend/src/components/KanbanView.tsx +263 -167
  63. package/templates/assistkick-product-system/packages/frontend/src/components/LoginPage.tsx +14 -14
  64. package/templates/assistkick-product-system/packages/frontend/src/components/ProjectSelector.tsx +54 -33
  65. package/templates/assistkick-product-system/packages/frontend/src/components/QaIssueSheet.tsx +32 -49
  66. package/templates/assistkick-product-system/packages/frontend/src/components/SidePanel.tsx +43 -48
  67. package/templates/assistkick-product-system/packages/frontend/src/components/TerminalView.tsx +121 -52
  68. package/templates/assistkick-product-system/packages/frontend/src/components/Toolbar.tsx +20 -14
  69. package/templates/assistkick-product-system/packages/frontend/src/components/UsersView.tsx +52 -52
  70. package/templates/assistkick-product-system/packages/frontend/src/components/VideoGallery.tsx +313 -0
  71. package/templates/assistkick-product-system/packages/frontend/src/components/VideographyView.tsx +250 -0
  72. package/templates/assistkick-product-system/packages/frontend/src/components/WorkflowsView.tsx +474 -0
  73. package/templates/assistkick-product-system/packages/frontend/src/components/ds/AccentBorderList.tsx +53 -0
  74. package/templates/assistkick-product-system/packages/frontend/src/components/ds/Button.tsx +87 -0
  75. package/templates/assistkick-product-system/packages/frontend/src/components/ds/ButtonGroup.tsx +29 -0
  76. package/templates/assistkick-product-system/packages/frontend/src/components/ds/ButtonShowcase.tsx +221 -0
  77. package/templates/assistkick-product-system/packages/frontend/src/components/ds/CardGlass.tsx +141 -0
  78. package/templates/assistkick-product-system/packages/frontend/src/components/ds/CompletionRing.tsx +30 -0
  79. package/templates/assistkick-product-system/packages/frontend/src/components/ds/ContentCard.tsx +34 -0
  80. package/templates/assistkick-product-system/packages/frontend/src/components/ds/IconButton.tsx +74 -0
  81. package/templates/assistkick-product-system/packages/frontend/src/components/ds/KanbanCard.tsx +103 -87
  82. package/templates/assistkick-product-system/packages/frontend/src/components/ds/KanbanCardShowcase.tsx +9 -188
  83. package/templates/assistkick-product-system/packages/frontend/src/components/ds/Kbd.tsx +11 -0
  84. package/templates/assistkick-product-system/packages/frontend/src/components/ds/KindBadge.tsx +21 -0
  85. package/templates/assistkick-product-system/packages/frontend/src/components/ds/NavBarSidekick.tsx +81 -37
  86. package/templates/assistkick-product-system/packages/frontend/src/components/ds/SidePanelShowcase.tsx +370 -0
  87. package/templates/assistkick-product-system/packages/frontend/src/components/ds/SideSheet.tsx +64 -0
  88. package/templates/assistkick-product-system/packages/frontend/src/components/ds/StatusDot.tsx +18 -0
  89. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/CheckCardPositionNode.tsx +36 -0
  90. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/CheckCycleCountNode.tsx +60 -0
  91. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/EndNode.tsx +42 -0
  92. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/GenerateTTSNode.tsx +52 -0
  93. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/GroupNode.tsx +189 -0
  94. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/NodePalette.tsx +123 -0
  95. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/RebuildBundleNode.tsx +20 -0
  96. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/RenderVideoNode.tsx +72 -0
  97. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/RunAgentNode.tsx +51 -0
  98. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/SetCardMetadataNode.tsx +53 -0
  99. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/StartNode.tsx +18 -0
  100. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/TransitionCardNode.tsx +59 -0
  101. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowCanvas.tsx +341 -0
  102. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowMonitorModal.tsx +643 -0
  103. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/autoLayout.ts +103 -0
  104. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/edgeColors.ts +35 -0
  105. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/monitor_nodes.tsx +246 -0
  106. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.test.ts +119 -0
  107. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.ts +136 -0
  108. package/templates/assistkick-product-system/packages/frontend/src/constants/graph.ts +13 -11
  109. package/templates/assistkick-product-system/packages/frontend/src/hooks/useAutoSave.ts +75 -0
  110. package/templates/assistkick-product-system/packages/frontend/src/hooks/useToast.tsx +16 -3
  111. package/templates/assistkick-product-system/packages/frontend/src/pages/accept_invitation_page.tsx +30 -27
  112. package/templates/assistkick-product-system/packages/frontend/src/pages/forgot_password_page.tsx +18 -15
  113. package/templates/assistkick-product-system/packages/frontend/src/pages/register_page.tsx +21 -18
  114. package/templates/assistkick-product-system/packages/frontend/src/pages/reset_password_page.tsx +28 -25
  115. package/templates/assistkick-product-system/packages/frontend/src/routes/AgentsRoute.tsx +6 -0
  116. package/templates/assistkick-product-system/packages/frontend/src/routes/CoherenceRoute.tsx +1 -1
  117. package/templates/assistkick-product-system/packages/frontend/src/routes/DashboardLayout.tsx +2 -2
  118. package/templates/assistkick-product-system/packages/frontend/src/routes/FilesRoute.tsx +13 -0
  119. package/templates/assistkick-product-system/packages/frontend/src/routes/GraphRoute.tsx +2 -2
  120. package/templates/assistkick-product-system/packages/frontend/src/routes/VideographyRoute.tsx +13 -0
  121. package/templates/assistkick-product-system/packages/frontend/src/routes/WorkflowsRoute.tsx +6 -0
  122. package/templates/assistkick-product-system/packages/frontend/src/stores/useProjectStore.ts +6 -3
  123. package/templates/assistkick-product-system/packages/frontend/src/stores/useSidePanelStore.ts +4 -4
  124. package/templates/assistkick-product-system/packages/frontend/src/styles/index.css +275 -3535
  125. package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.test.ts +167 -0
  126. package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.ts +101 -0
  127. package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.test.ts +42 -0
  128. package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.ts +17 -0
  129. package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.test.ts +145 -0
  130. package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.ts +42 -0
  131. package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.test.ts +4 -10
  132. package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.ts +19 -1
  133. package/templates/assistkick-product-system/packages/frontend/vite.config.ts +5 -0
  134. package/templates/assistkick-product-system/packages/shared/db/local.db +0 -0
  135. package/templates/assistkick-product-system/packages/shared/db/migrations/0004_tidy_matthew_murdock.sql +9 -0
  136. package/templates/assistkick-product-system/packages/shared/db/migrations/0005_mysterious_falcon.sql +692 -0
  137. package/templates/assistkick-product-system/packages/shared/db/migrations/0006_next_venom.sql +9 -0
  138. package/templates/assistkick-product-system/packages/shared/db/migrations/0007_deep_barracuda.sql +39 -0
  139. package/templates/assistkick-product-system/packages/shared/db/migrations/0008_puzzling_hannibal_king.sql +1 -0
  140. package/templates/assistkick-product-system/packages/shared/db/migrations/0009_amused_beast.sql +8 -0
  141. package/templates/assistkick-product-system/packages/shared/db/migrations/0010_spotty_moira_mactaggert.sql +9 -0
  142. package/templates/assistkick-product-system/packages/shared/db/migrations/0011_goofy_snowbird.sql +3 -0
  143. package/templates/assistkick-product-system/packages/shared/db/migrations/0011_supreme_doctor_octopus.sql +3 -0
  144. package/templates/assistkick-product-system/packages/shared/db/migrations/0013_reflective_prowler.sql +15 -0
  145. package/templates/assistkick-product-system/packages/shared/db/migrations/0014_nifty_punisher.sql +15 -0
  146. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0004_snapshot.json +921 -0
  147. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0005_snapshot.json +1042 -0
  148. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0006_snapshot.json +1101 -0
  149. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0007_snapshot.json +1336 -0
  150. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0008_snapshot.json +1275 -0
  151. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0009_snapshot.json +1327 -0
  152. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0010_snapshot.json +1393 -0
  153. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0011_snapshot.json +1436 -0
  154. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0013_snapshot.json +1538 -0
  155. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0014_snapshot.json +1545 -0
  156. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/_journal.json +77 -0
  157. package/templates/assistkick-product-system/packages/shared/db/schema.ts +114 -0
  158. package/templates/assistkick-product-system/packages/shared/lib/claude-service.ts +32 -7
  159. package/templates/assistkick-product-system/packages/shared/lib/constants.ts +9 -0
  160. package/templates/assistkick-product-system/packages/shared/lib/git_workflow.ts +12 -4
  161. package/templates/assistkick-product-system/packages/shared/lib/graph.ts +5 -0
  162. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.test.ts +1999 -0
  163. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.ts +1437 -0
  164. package/templates/assistkick-product-system/packages/shared/lib/workflow_orchestrator.ts +211 -0
  165. package/templates/assistkick-product-system/packages/shared/tools/add_node.test.ts +43 -0
  166. package/templates/assistkick-product-system/packages/shared/tools/add_node.ts +13 -2
  167. package/templates/assistkick-product-system/packages/shared/tools/get_kanban.ts +1 -1
  168. package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.test.ts +226 -0
  169. package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.ts +251 -0
  170. package/templates/assistkick-product-system/packages/shared/tools/update_node.ts +2 -2
  171. package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.test.ts +10 -0
  172. package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.ts +6 -0
  173. package/templates/assistkick-product-system/packages/video/Root.tsx +85 -0
  174. package/templates/assistkick-product-system/packages/video/components/email_scene.tsx +231 -0
  175. package/templates/assistkick-product-system/packages/video/components/outro_scene.tsx +153 -0
  176. package/templates/assistkick-product-system/packages/video/components/part_divider.tsx +90 -0
  177. package/templates/assistkick-product-system/packages/video/components/scene.tsx +226 -0
  178. package/templates/assistkick-product-system/packages/video/components/theme.ts +22 -0
  179. package/templates/assistkick-product-system/packages/video/components/title_scene.tsx +169 -0
  180. package/templates/assistkick-product-system/packages/video/components/video_split_layout.tsx +84 -0
  181. package/templates/assistkick-product-system/packages/video/compositions/.gitkeep +0 -0
  182. package/templates/assistkick-product-system/packages/video/index.ts +4 -0
  183. package/templates/assistkick-product-system/packages/video/package.json +28 -0
  184. package/templates/assistkick-product-system/packages/video/remotion.config.ts +11 -0
  185. package/templates/assistkick-product-system/packages/video/scripts/process_script.test.ts +326 -0
  186. package/templates/assistkick-product-system/packages/video/scripts/process_script.ts +630 -0
  187. package/templates/assistkick-product-system/packages/video/style.css +1 -0
  188. package/templates/assistkick-product-system/packages/video/tsconfig.json +18 -0
  189. package/templates/assistkick-product-system/tests/graph_legend.test.ts +2 -1
  190. package/templates/assistkick-product-system/tests/video_render_service.test.ts +181 -0
  191. package/templates/assistkick-product-system/tests/web_terminal.test.ts +219 -455
  192. package/templates/assistkick-product-system/tests/workflow_integration.test.ts +341 -0
  193. package/templates/skills/assistkick-developer/SKILL.md +3 -0
  194. package/templates/skills/assistkick-developer/references/react_development_guidelines.md +225 -0
  195. package/templates/skills/product-system/graph.json +1890 -0
  196. package/templates/skills/product-system/kanban.json +304 -0
  197. package/templates/skills/product-system/nodes/comp_001.md +56 -0
  198. package/templates/skills/product-system/nodes/comp_002.md +57 -0
  199. package/templates/skills/product-system/nodes/data_001.md +51 -0
  200. package/templates/skills/product-system/nodes/data_002.md +40 -0
  201. package/templates/skills/product-system/nodes/data_004.md +38 -0
  202. package/templates/skills/product-system/nodes/dec_001.md +34 -0
  203. package/templates/skills/product-system/nodes/dec_016.md +32 -0
  204. package/templates/skills/product-system/nodes/feat_008.md +30 -0
  205. package/templates/skills/video-composition-agent/SKILL.md +232 -0
  206. package/templates/skills/video-script-writer/SKILL.md +136 -0
@@ -10,15 +10,21 @@
10
10
  "clean": "rm -rf dist"
11
11
  },
12
12
  "dependencies": {
13
+ "@monaco-editor/react": "^4.7.0",
14
+ "@remotion/media": "^4.0.434",
15
+ "@remotion/player": "^4.0.434",
16
+ "@remotion/transitions": "^4.0.434",
13
17
  "@xterm/addon-fit": "^0.11.0",
14
18
  "@xterm/addon-web-links": "^0.12.0",
15
19
  "@xterm/xterm": "^6.0.0",
20
+ "@xyflow/react": "^12.10.1",
16
21
  "d3": "^7.9.0",
17
22
  "lucide-react": "^0.577.0",
18
23
  "marked": "^15.0.0",
19
24
  "react": "^19.1.0",
20
25
  "react-dom": "^19.1.0",
21
26
  "react-router-dom": "^7.6.0",
27
+ "remotion": "^4.0.434",
22
28
  "zustand": "^5.0.11"
23
29
  },
24
30
  "devDependencies": {
@@ -13,6 +13,10 @@ import { CoherenceRoute } from './routes/CoherenceRoute';
13
13
  import { UsersRoute } from './routes/UsersRoute';
14
14
  import { TerminalRoute } from './routes/TerminalRoute';
15
15
  import { DesignSystemRoute } from './routes/DesignSystemRoute';
16
+ import { AgentsRoute } from './routes/AgentsRoute';
17
+ import { WorkflowsRoute } from './routes/WorkflowsRoute';
18
+ import { FilesRoute } from './routes/FilesRoute';
19
+ import { VideographyRoute } from './routes/VideographyRoute';
16
20
 
17
21
  export function App() {
18
22
  return (
@@ -30,7 +34,11 @@ export function App() {
30
34
  <Route path="/coherence" element={<CoherenceRoute />} />
31
35
  <Route path="/users" element={<UsersRoute />} />
32
36
  <Route path="/terminal" element={<TerminalRoute />} />
37
+ <Route path="/agents" element={<AgentsRoute />} />
38
+ <Route path="/workflows" element={<WorkflowsRoute />} />
39
+ <Route path="/files" element={<FilesRoute />} />
33
40
  <Route path="/design-system" element={<DesignSystemRoute />} />
41
+ <Route path="/videography" element={<VideographyRoute />} />
34
42
  </Route>
35
43
  </Route>
36
44
  </Routes>
@@ -1,3 +1,91 @@
1
+ export interface VideoCompositionMeta {
2
+ id: string;
3
+ durationInFrames: number;
4
+ fps: number;
5
+ width: number;
6
+ height: number;
7
+ folder?: string;
8
+ }
9
+
10
+ export interface VideoBundleStatus {
11
+ ready: boolean;
12
+ building: boolean;
13
+ bundlePath: string | null;
14
+ lastBuiltAt: string | null;
15
+ error: string | null;
16
+ }
17
+
18
+ export interface VideoRenderStatus {
19
+ id: string;
20
+ status: 'queued' | 'rendering' | 'complete' | 'failed';
21
+ progress: number;
22
+ filePath: string | null;
23
+ fileSize: number | null;
24
+ durationSeconds: number | null;
25
+ error: string | null;
26
+ compositionId: string;
27
+ resolution: string | null;
28
+ createdAt: string;
29
+ completedAt: string | null;
30
+ }
31
+
32
+ export interface AgentMetrics {
33
+ nodeId: string;
34
+ agentName: string | null;
35
+ toolCalls: Record<string, number> | null;
36
+ costUsd: number | null;
37
+ numTurns: number | null;
38
+ stopReason: string | null;
39
+ model: string | null;
40
+ contextWindowPct: number | null;
41
+ status: string;
42
+ }
43
+
44
+ export interface ToolCallEntry {
45
+ id: string;
46
+ timestamp: string;
47
+ toolName: string;
48
+ target: string;
49
+ }
50
+
51
+ export interface NodeExecutionData {
52
+ id: string;
53
+ nodeId: string;
54
+ status: string;
55
+ startedAt: string | null;
56
+ completedAt: string | null;
57
+ outputData: {
58
+ agentId?: string;
59
+ agentName?: string;
60
+ output?: string;
61
+ toolCalls?: Record<string, number>;
62
+ costUsd?: number;
63
+ numTurns?: number;
64
+ stopReason?: string;
65
+ model?: string;
66
+ contextWindowPct?: number;
67
+ } | null;
68
+ error: string | null;
69
+ attempt: number;
70
+ toolCalls: ToolCallEntry[];
71
+ }
72
+
73
+ export interface WorkflowMonitorData {
74
+ executionId: string;
75
+ workflowId: string;
76
+ workflowName: string;
77
+ featureId: string;
78
+ status: string;
79
+ currentNodeId: string | null;
80
+ startedAt: string;
81
+ updatedAt: string;
82
+ graphData: {
83
+ nodes: Array<{ id: string; type: string; position: { x: number; y: number }; data: Record<string, unknown> }>;
84
+ edges: Array<{ id: string; source: string; target: string; sourceHandle?: string; data?: { label?: string } }>;
85
+ };
86
+ nodeExecutions: Record<string, NodeExecutionData>;
87
+ }
88
+
1
89
  /**
2
90
  * API client for fetching graph data and node content.
3
91
  * All HTTP calls to the server go through this class.
@@ -66,11 +154,11 @@ export class ApiClient {
66
154
  return resp.json();
67
155
  };
68
156
 
69
- moveCard = async (featureId: string, column: string) => {
157
+ moveCard = async (featureId: string, column: string, comment?: string) => {
70
158
  const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/kanban/${featureId}/move`, {
71
159
  method: 'POST',
72
160
  headers: { 'Content-Type': 'application/json' },
73
- body: JSON.stringify({ column }),
161
+ body: JSON.stringify({ column, ...(comment ? { comment } : {}) }),
74
162
  });
75
163
  if (!resp.ok) {
76
164
  const err = await resp.json();
@@ -107,22 +195,35 @@ export class ApiClient {
107
195
  return resp.json();
108
196
  };
109
197
 
110
- startPipeline = async (featureId: string) => {
111
- const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/kanban/${featureId}/develop`, {
198
+ startWorkflow = async (featureId: string, workflowId?: string, projectId?: string) => {
199
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/kanban/${featureId}/workflow`, {
112
200
  method: 'POST',
113
201
  headers: { 'Content-Type': 'application/json' },
202
+ body: JSON.stringify({ workflowId, projectId }),
114
203
  });
115
204
  if (!resp.ok) {
116
- let msg = `Failed to start pipeline: ${resp.status}`;
205
+ let msg = `Failed to start workflow: ${resp.status}`;
117
206
  try { const err = await resp.json(); msg = err.error || msg; } catch { /* non-JSON response */ }
118
207
  throw new Error(msg);
119
208
  }
120
209
  return resp.json();
121
210
  };
122
211
 
123
- getPipelineStatus = async (featureId: string) => {
124
- const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/kanban/${featureId}/pipeline`);
125
- if (!resp.ok) throw new Error(`Failed to get pipeline status: ${resp.status}`);
212
+ getWorkflowStatus = async (featureId: string) => {
213
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/kanban/${featureId}/workflow-status`);
214
+ if (!resp.ok) throw new Error(`Failed to get workflow status: ${resp.status}`);
215
+ return resp.json();
216
+ };
217
+
218
+ getWorkflowMonitor = async (featureId: string): Promise<{ monitor: WorkflowMonitorData | null }> => {
219
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/kanban/${featureId}/workflow-monitor`);
220
+ if (!resp.ok) throw new Error(`Failed to get workflow monitor: ${resp.status}`);
221
+ return resp.json();
222
+ };
223
+
224
+ getWorkflowMetrics = async (featureId: string): Promise<{ metrics: AgentMetrics[] }> => {
225
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/kanban/${featureId}/workflow-metrics`);
226
+ if (!resp.ok) throw new Error(`Failed to get workflow metrics: ${resp.status}`);
126
227
  return resp.json();
127
228
  };
128
229
 
@@ -192,8 +293,11 @@ export class ApiClient {
192
293
 
193
294
  // --- Orchestrator (Play All) ---
194
295
 
195
- startPlayAll = async (projectId?: string) => {
196
- const qs = projectId ? `?project_id=${encodeURIComponent(projectId)}` : '';
296
+ startPlayAll = async (projectId?: string, epicId?: string) => {
297
+ const params = new URLSearchParams();
298
+ if (projectId) params.set('project_id', projectId);
299
+ if (epicId) params.set('epic_id', epicId);
300
+ const qs = params.toString() ? `?${params}` : '';
197
301
  const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/pipeline/play-all${qs}`, {
198
302
  method: 'POST',
199
303
  headers: { 'Content-Type': 'application/json' },
@@ -225,26 +329,41 @@ export class ApiClient {
225
329
  return resp.json();
226
330
  };
227
331
 
228
- resumePipeline = async (featureId: string) => {
229
- const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/kanban/${featureId}/resume`, {
332
+ resumeWorkflow = async (featureId: string) => {
333
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/kanban/${featureId}/workflow/resume`, {
334
+ method: 'POST',
335
+ headers: { 'Content-Type': 'application/json' },
336
+ });
337
+ if (!resp.ok) {
338
+ let msg = `Failed to resume workflow: ${resp.status}`;
339
+ try { const err = await resp.json(); msg = err.error || msg; } catch { /* non-JSON response */ }
340
+ throw new Error(msg);
341
+ }
342
+ return resp.json();
343
+ };
344
+
345
+ forceNextNode = async (featureId: string, nodeId: string) => {
346
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/kanban/${featureId}/workflow/force-next`, {
230
347
  method: 'POST',
231
348
  headers: { 'Content-Type': 'application/json' },
349
+ body: JSON.stringify({ nodeId }),
232
350
  });
233
351
  if (!resp.ok) {
234
- let msg = `Failed to resume pipeline: ${resp.status}`;
352
+ let msg = `Failed to force next: ${resp.status}`;
235
353
  try { const err = await resp.json(); msg = err.error || msg; } catch { /* non-JSON response */ }
236
354
  throw new Error(msg);
237
355
  }
238
356
  return resp.json();
239
357
  };
240
358
 
241
- unblockCard = async (featureId: string) => {
242
- const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/kanban/${featureId}/unblock`, {
359
+ restartNode = async (featureId: string, nodeId: string) => {
360
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/kanban/${featureId}/workflow/restart-node`, {
243
361
  method: 'POST',
244
362
  headers: { 'Content-Type': 'application/json' },
363
+ body: JSON.stringify({ nodeId }),
245
364
  });
246
365
  if (!resp.ok) {
247
- let msg = `Failed to unblock card: ${resp.status}`;
366
+ let msg = `Failed to restart node: ${resp.status}`;
248
367
  try { const err = await resp.json(); msg = err.error || msg; } catch { /* non-JSON response */ }
249
368
  throw new Error(msg);
250
369
  }
@@ -366,11 +485,11 @@ export class ApiClient {
366
485
  return resp.json();
367
486
  };
368
487
 
369
- createProject = async (name: string) => {
488
+ createProject = async (name: string, type?: string) => {
370
489
  const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/projects`, {
371
490
  method: 'POST',
372
491
  headers: { 'Content-Type': 'application/json' },
373
- body: JSON.stringify({ name }),
492
+ body: JSON.stringify({ name, ...(type ? { type } : {}) }),
374
493
  });
375
494
  if (!resp.ok) {
376
495
  const data = await resp.json();
@@ -489,6 +608,31 @@ export class ApiClient {
489
608
  return resp.json();
490
609
  };
491
610
 
611
+ generateSshKey = async (projectId: string) => {
612
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/projects/${projectId}/git/ssh/generate`, {
613
+ method: 'POST',
614
+ headers: { 'Content-Type': 'application/json' },
615
+ });
616
+ if (!resp.ok) {
617
+ const data = await resp.json();
618
+ throw new Error(data.error || `Failed to generate SSH key: ${resp.status}`);
619
+ }
620
+ return resp.json();
621
+ };
622
+
623
+ connectSshRepo = async (projectId: string, repoUrl: string, baseBranch?: string) => {
624
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/projects/${projectId}/git/ssh/connect`, {
625
+ method: 'POST',
626
+ headers: { 'Content-Type': 'application/json' },
627
+ body: JSON.stringify({ repoUrl, baseBranch }),
628
+ });
629
+ if (!resp.ok) {
630
+ const data = await resp.json();
631
+ throw new Error(data.error || `Failed to connect repo via SSH: ${resp.status}`);
632
+ }
633
+ return resp.json();
634
+ };
635
+
492
636
  listTerminalSessions = async () => {
493
637
  const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/terminal/sessions`);
494
638
  if (!resp.ok) throw new Error(`Failed to list terminal sessions: ${resp.status}`);
@@ -508,6 +652,23 @@ export class ApiClient {
508
652
  return resp.json();
509
653
  };
510
654
 
655
+ uploadTerminalImage = async (file: File): Promise<{ path: string }> => {
656
+ const buffer = await file.arrayBuffer();
657
+ const resp = await this.fetchWithRefresh(
658
+ `${this.baseUrl}/api/terminal/upload-image?filename=${encodeURIComponent(file.name)}`,
659
+ {
660
+ method: 'POST',
661
+ headers: { 'Content-Type': 'application/octet-stream' },
662
+ body: buffer,
663
+ },
664
+ );
665
+ if (!resp.ok) {
666
+ const data = await resp.json();
667
+ throw new Error(data.error || `Failed to upload image: ${resp.status}`);
668
+ }
669
+ return resp.json();
670
+ };
671
+
511
672
  killTerminalSession = async (sessionId: string) => {
512
673
  const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/terminal/sessions/${sessionId}`, {
513
674
  method: 'DELETE',
@@ -519,6 +680,285 @@ export class ApiClient {
519
680
  return resp.json();
520
681
  };
521
682
 
683
+ // --- Agent management ---
684
+
685
+ fetchAgents = async (scope: 'global' | 'project', projectId?: string) => {
686
+ const params = new URLSearchParams({ scope });
687
+ if (projectId) params.set('projectId', projectId);
688
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/agents?${params}`);
689
+ if (!resp.ok) throw new Error(`Failed to fetch agents: ${resp.status}`);
690
+ return resp.json();
691
+ };
692
+
693
+ fetchAgent = async (id: string) => {
694
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/agents/${id}`);
695
+ if (!resp.ok) throw new Error(`Failed to fetch agent: ${resp.status}`);
696
+ return resp.json();
697
+ };
698
+
699
+ createAgent = async (data: { name: string; promptTemplate: string; projectId?: string }) => {
700
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/agents`, {
701
+ method: 'POST',
702
+ headers: { 'Content-Type': 'application/json' },
703
+ body: JSON.stringify(data),
704
+ });
705
+ if (!resp.ok) {
706
+ const err = await resp.json();
707
+ throw new Error(err.error || `Failed to create agent: ${resp.status}`);
708
+ }
709
+ return resp.json();
710
+ };
711
+
712
+ updateAgent = async (id: string, data: { name?: string; promptTemplate?: string }) => {
713
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/agents/${id}`, {
714
+ method: 'PUT',
715
+ headers: { 'Content-Type': 'application/json' },
716
+ body: JSON.stringify(data),
717
+ });
718
+ if (!resp.ok) {
719
+ const err = await resp.json();
720
+ throw new Error(err.error || `Failed to update agent: ${resp.status}`);
721
+ }
722
+ return resp.json();
723
+ };
724
+
725
+ deleteAgent = async (id: string) => {
726
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/agents/${id}`, {
727
+ method: 'DELETE',
728
+ });
729
+ if (!resp.ok) {
730
+ const err = await resp.json();
731
+ throw new Error(err.error || `Failed to delete agent: ${resp.status}`);
732
+ }
733
+ return resp.json();
734
+ };
735
+
736
+ resetAgent = async (id: string) => {
737
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/agents/${id}/reset`, {
738
+ method: 'POST',
739
+ headers: { 'Content-Type': 'application/json' },
740
+ });
741
+ if (!resp.ok) {
742
+ const err = await resp.json();
743
+ throw new Error(err.error || `Failed to reset agent: ${resp.status}`);
744
+ }
745
+ return resp.json();
746
+ };
747
+
748
+ // --- Workflow management ---
749
+
750
+ fetchWorkflows = async (projectId?: string) => {
751
+ const params = projectId ? `?projectId=${encodeURIComponent(projectId)}` : '';
752
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/workflows${params}`);
753
+ if (!resp.ok) throw new Error(`Failed to fetch workflows: ${resp.status}`);
754
+ return resp.json();
755
+ };
756
+
757
+ fetchWorkflow = async (id: string) => {
758
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/workflows/${id}`);
759
+ if (!resp.ok) throw new Error(`Failed to fetch workflow: ${resp.status}`);
760
+ return resp.json();
761
+ };
762
+
763
+ createWorkflow = async (data: { name: string; description?: string; projectId?: string; graphData: string }) => {
764
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/workflows`, {
765
+ method: 'POST',
766
+ headers: { 'Content-Type': 'application/json' },
767
+ body: JSON.stringify(data),
768
+ });
769
+ if (!resp.ok) {
770
+ const err = await resp.json();
771
+ throw new Error(err.error || `Failed to create workflow: ${resp.status}`);
772
+ }
773
+ return resp.json();
774
+ };
775
+
776
+ updateWorkflow = async (id: string, data: { name?: string; description?: string; graphData?: string }) => {
777
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/workflows/${id}`, {
778
+ method: 'PUT',
779
+ headers: { 'Content-Type': 'application/json' },
780
+ body: JSON.stringify(data),
781
+ });
782
+ if (!resp.ok) {
783
+ const err = await resp.json();
784
+ throw new Error(err.error || `Failed to update workflow: ${resp.status}`);
785
+ }
786
+ return resp.json();
787
+ };
788
+
789
+ deleteWorkflow = async (id: string) => {
790
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/workflows/${id}`, {
791
+ method: 'DELETE',
792
+ });
793
+ if (!resp.ok) {
794
+ const err = await resp.json();
795
+ throw new Error(err.error || `Failed to delete workflow: ${resp.status}`);
796
+ }
797
+ return resp.json();
798
+ };
799
+
800
+ // --- Workflow group management ---
801
+
802
+ fetchWorkflowGroups = async (projectId?: string) => {
803
+ const params = projectId ? `?projectId=${encodeURIComponent(projectId)}` : '';
804
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/workflow-groups${params}`);
805
+ if (!resp.ok) throw new Error(`Failed to fetch workflow groups: ${resp.status}`);
806
+ return resp.json();
807
+ };
808
+
809
+ createWorkflowGroup = async (data: { name: string; projectId?: string; graphData: string }) => {
810
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/workflow-groups`, {
811
+ method: 'POST',
812
+ headers: { 'Content-Type': 'application/json' },
813
+ body: JSON.stringify(data),
814
+ });
815
+ if (!resp.ok) {
816
+ const err = await resp.json();
817
+ throw new Error(err.error || `Failed to create workflow group: ${resp.status}`);
818
+ }
819
+ return resp.json();
820
+ };
821
+
822
+ deleteWorkflowGroup = async (id: string) => {
823
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/workflow-groups/${id}`, {
824
+ method: 'DELETE',
825
+ });
826
+ if (!resp.ok) {
827
+ const err = await resp.json();
828
+ throw new Error(err.error || `Failed to delete workflow group: ${resp.status}`);
829
+ }
830
+ return resp.json();
831
+ };
832
+
833
+ setDefaultWorkflow = async (id: string) => {
834
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/workflows/${id}/set-default`, {
835
+ method: 'POST',
836
+ headers: { 'Content-Type': 'application/json' },
837
+ });
838
+ if (!resp.ok) {
839
+ const err = await resp.json();
840
+ throw new Error(err.error || `Failed to set default workflow: ${resp.status}`);
841
+ }
842
+ return resp.json();
843
+ };
844
+
845
+ // --- File system ---
846
+
847
+ fetchFileTree = async (projectId: string) => {
848
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/projects/${projectId}/files`);
849
+ if (!resp.ok) throw new Error(`Failed to fetch file tree: ${resp.status}`);
850
+ return resp.json();
851
+ };
852
+
853
+ fetchFileContent = async (projectId: string, path: string) => {
854
+ const resp = await this.fetchWithRefresh(
855
+ `${this.baseUrl}/api/projects/${projectId}/files/content?path=${encodeURIComponent(path)}`,
856
+ );
857
+ if (!resp.ok) throw new Error(`Failed to fetch file content: ${resp.status}`);
858
+ return resp.json();
859
+ };
860
+
861
+ saveFileContent = async (projectId: string, path: string, content: string) => {
862
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/projects/${projectId}/files/content`, {
863
+ method: 'PUT',
864
+ headers: { 'Content-Type': 'application/json' },
865
+ body: JSON.stringify({ path, content }),
866
+ });
867
+ if (!resp.ok) throw new Error(`Failed to save file: ${resp.status}`);
868
+ return resp.json();
869
+ };
870
+
871
+ createFile = async (projectId: string, path: string, type: 'file' | 'directory') => {
872
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/projects/${projectId}/files`, {
873
+ method: 'POST',
874
+ headers: { 'Content-Type': 'application/json' },
875
+ body: JSON.stringify({ path, type }),
876
+ });
877
+ if (!resp.ok) {
878
+ const data = await resp.json();
879
+ throw new Error(data.error || `Failed to create ${type}: ${resp.status}`);
880
+ }
881
+ return resp.json();
882
+ };
883
+
884
+ deleteFile = async (projectId: string, path: string) => {
885
+ const resp = await this.fetchWithRefresh(
886
+ `${this.baseUrl}/api/projects/${projectId}/files?path=${encodeURIComponent(path)}`,
887
+ { method: 'DELETE' },
888
+ );
889
+ if (!resp.ok) {
890
+ const data = await resp.json();
891
+ throw new Error(data.error || `Failed to delete file: ${resp.status}`);
892
+ }
893
+ return resp.json();
894
+ };
895
+
896
+ renameFile = async (projectId: string, oldPath: string, newPath: string) => {
897
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/projects/${projectId}/files`, {
898
+ method: 'PATCH',
899
+ headers: { 'Content-Type': 'application/json' },
900
+ body: JSON.stringify({ oldPath, newPath }),
901
+ });
902
+ if (!resp.ok) {
903
+ const data = await resp.json();
904
+ throw new Error(data.error || `Failed to rename file: ${resp.status}`);
905
+ }
906
+ return resp.json();
907
+ };
908
+
909
+ // --- Video / Remotion ---
910
+
911
+ fetchVideoCompositions = async (): Promise<{ compositions: VideoCompositionMeta[] }> => {
912
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/video/compositions`);
913
+ if (!resp.ok) throw new Error(`Failed to fetch video compositions: ${resp.status}`);
914
+ return resp.json();
915
+ };
916
+
917
+ fetchVideoBundleStatus = async (): Promise<VideoBundleStatus> => {
918
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/video/bundle/status`);
919
+ if (!resp.ok) throw new Error(`Failed to fetch bundle status: ${resp.status}`);
920
+ return resp.json();
921
+ };
922
+
923
+ rebuildVideoBundle = async (): Promise<VideoBundleStatus> => {
924
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/video/bundle/rebuild`, {
925
+ method: 'POST',
926
+ headers: { 'Content-Type': 'application/json' },
927
+ });
928
+ if (!resp.ok) {
929
+ let msg = `Failed to rebuild bundle: ${resp.status}`;
930
+ try { const err = await resp.json(); msg = err.error || msg; } catch { /* non-JSON */ }
931
+ throw new Error(msg);
932
+ }
933
+ return resp.json();
934
+ };
935
+
936
+ fetchVideoRenders = async (projectId: string): Promise<{ renders: VideoRenderStatus[] }> => {
937
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/video/renders?projectId=${encodeURIComponent(projectId)}`);
938
+ if (!resp.ok) throw new Error(`Failed to fetch video renders: ${resp.status}`);
939
+ return resp.json();
940
+ };
941
+
942
+ fetchVideoRenderStatus = async (renderId: string): Promise<VideoRenderStatus> => {
943
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/video/render/${encodeURIComponent(renderId)}`);
944
+ if (!resp.ok) throw new Error(`Failed to fetch render status: ${resp.status}`);
945
+ return resp.json();
946
+ };
947
+
948
+ deleteVideoRender = async (renderId: string): Promise<void> => {
949
+ const resp = await this.fetchWithRefresh(`${this.baseUrl}/api/video/render/${encodeURIComponent(renderId)}`, {
950
+ method: 'DELETE',
951
+ });
952
+ if (!resp.ok) {
953
+ const data = await resp.json();
954
+ throw new Error(data.error || `Failed to delete render: ${resp.status}`);
955
+ }
956
+ };
957
+
958
+ getVideoFileUrl = (renderId: string): string => {
959
+ return `${this.baseUrl}/api/video/file/${encodeURIComponent(renderId)}`;
960
+ };
961
+
522
962
  acceptInvitation = async (token: string, email: string, password: string) => {
523
963
  const resp = await fetch(`${this.baseUrl}/api/auth/accept-invitation`, {
524
964
  method: 'POST',