@assistkick/create 1.6.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 (214) hide show
  1. package/package.json +2 -2
  2. package/templates/assistkick-product-system/.env.example +1 -0
  3. package/templates/assistkick-product-system/local.db +0 -0
  4. package/templates/assistkick-product-system/package.json +4 -2
  5. package/templates/assistkick-product-system/packages/backend/package.json +2 -0
  6. package/templates/assistkick-product-system/packages/backend/src/routes/agents.ts +165 -0
  7. package/templates/assistkick-product-system/packages/backend/src/routes/files.test.ts +358 -0
  8. package/templates/assistkick-product-system/packages/backend/src/routes/files.ts +356 -0
  9. package/templates/assistkick-product-system/packages/backend/src/routes/git.ts +96 -1
  10. package/templates/assistkick-product-system/packages/backend/src/routes/graph.ts +1 -0
  11. package/templates/assistkick-product-system/packages/backend/src/routes/kanban.ts +43 -4
  12. package/templates/assistkick-product-system/packages/backend/src/routes/pipeline.ts +200 -84
  13. package/templates/assistkick-product-system/packages/backend/src/routes/projects.ts +6 -3
  14. package/templates/assistkick-product-system/packages/backend/src/routes/terminal.ts +53 -17
  15. package/templates/assistkick-product-system/packages/backend/src/routes/video.ts +218 -0
  16. package/templates/assistkick-product-system/packages/backend/src/routes/workflow_groups.ts +119 -0
  17. package/templates/assistkick-product-system/packages/backend/src/routes/workflows.ts +154 -0
  18. package/templates/assistkick-product-system/packages/backend/src/server.ts +81 -9
  19. package/templates/assistkick-product-system/packages/backend/src/services/agent_service.test.ts +489 -0
  20. package/templates/assistkick-product-system/packages/backend/src/services/agent_service.ts +416 -0
  21. package/templates/assistkick-product-system/packages/backend/src/services/bundle_service.test.ts +189 -0
  22. package/templates/assistkick-product-system/packages/backend/src/services/bundle_service.ts +182 -0
  23. package/templates/assistkick-product-system/packages/backend/src/services/init.ts +28 -78
  24. package/templates/assistkick-product-system/packages/backend/src/services/project_service.test.ts +16 -0
  25. package/templates/assistkick-product-system/packages/backend/src/services/project_service.ts +73 -2
  26. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.test.ts +4 -4
  27. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.ts +87 -11
  28. package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.test.ts +210 -69
  29. package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.ts +210 -215
  30. package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.test.ts +162 -0
  31. package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.ts +148 -0
  32. package/templates/assistkick-product-system/packages/backend/src/services/terminal_ws_handler.ts +11 -5
  33. package/templates/assistkick-product-system/packages/backend/src/services/tts_service.test.ts +64 -0
  34. package/templates/assistkick-product-system/packages/backend/src/services/tts_service.ts +134 -0
  35. package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.test.ts +256 -0
  36. package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.ts +258 -0
  37. package/templates/assistkick-product-system/packages/backend/src/services/workflow_group_service.ts +106 -0
  38. package/templates/assistkick-product-system/packages/backend/src/services/workflow_service.test.ts +275 -0
  39. package/templates/assistkick-product-system/packages/backend/src/services/workflow_service.ts +222 -0
  40. package/templates/assistkick-product-system/packages/frontend/index.html +3 -0
  41. package/templates/assistkick-product-system/packages/frontend/package-lock.json +800 -11
  42. package/templates/assistkick-product-system/packages/frontend/package.json +11 -1
  43. package/templates/assistkick-product-system/packages/frontend/src/App.tsx +24 -7
  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 +383 -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 +193 -64
  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 +226 -291
  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 +40 -66
  65. package/templates/assistkick-product-system/packages/frontend/src/components/SidePanel.tsx +55 -115
  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 +155 -77
  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 +270 -0
  81. package/templates/assistkick-product-system/packages/frontend/src/components/ds/KanbanCardShowcase.tsx +37 -0
  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 +207 -0
  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/useGraph.ts +6 -21
  107. package/templates/assistkick-product-system/packages/frontend/src/hooks/useProjects.ts +15 -80
  108. package/templates/assistkick-product-system/packages/frontend/src/hooks/useToast.tsx +16 -3
  109. package/templates/assistkick-product-system/packages/frontend/src/pages/accept_invitation_page.tsx +30 -27
  110. package/templates/assistkick-product-system/packages/frontend/src/pages/forgot_password_page.tsx +18 -15
  111. package/templates/assistkick-product-system/packages/frontend/src/pages/register_page.tsx +21 -18
  112. package/templates/assistkick-product-system/packages/frontend/src/pages/reset_password_page.tsx +28 -25
  113. package/templates/assistkick-product-system/packages/frontend/src/routes/AgentsRoute.tsx +6 -0
  114. package/templates/assistkick-product-system/packages/frontend/src/routes/CoherenceRoute.tsx +19 -0
  115. package/templates/assistkick-product-system/packages/frontend/src/routes/DashboardLayout.tsx +54 -0
  116. package/templates/assistkick-product-system/packages/frontend/src/routes/DesignSystemRoute.tsx +6 -0
  117. package/templates/assistkick-product-system/packages/frontend/src/routes/FilesRoute.tsx +13 -0
  118. package/templates/assistkick-product-system/packages/frontend/src/routes/GraphRoute.tsx +93 -0
  119. package/templates/assistkick-product-system/packages/frontend/src/routes/KanbanRoute.tsx +30 -0
  120. package/templates/assistkick-product-system/packages/frontend/src/routes/TerminalRoute.tsx +9 -0
  121. package/templates/assistkick-product-system/packages/frontend/src/routes/UsersRoute.tsx +6 -0
  122. package/templates/assistkick-product-system/packages/frontend/src/routes/VideographyRoute.tsx +13 -0
  123. package/templates/assistkick-product-system/packages/frontend/src/routes/WorkflowsRoute.tsx +6 -0
  124. package/templates/assistkick-product-system/packages/frontend/src/stores/useGitModalStore.ts +14 -0
  125. package/templates/assistkick-product-system/packages/frontend/src/stores/useGraphStore.ts +36 -0
  126. package/templates/assistkick-product-system/packages/frontend/src/stores/useGraphUIStore.ts +25 -0
  127. package/templates/assistkick-product-system/packages/frontend/src/stores/useProjectStore.ts +90 -0
  128. package/templates/assistkick-product-system/packages/frontend/src/stores/useQaSheetStore.ts +27 -0
  129. package/templates/assistkick-product-system/packages/frontend/src/stores/useSidePanelStore.ts +76 -0
  130. package/templates/assistkick-product-system/packages/frontend/src/styles/index.css +336 -3632
  131. package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.test.ts +167 -0
  132. package/templates/assistkick-product-system/packages/frontend/src/utils/auto_save_service.ts +101 -0
  133. package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.test.ts +42 -0
  134. package/templates/assistkick-product-system/packages/frontend/src/utils/composition_matcher.ts +17 -0
  135. package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.test.ts +145 -0
  136. package/templates/assistkick-product-system/packages/frontend/src/utils/file_utils.ts +42 -0
  137. package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.test.ts +4 -10
  138. package/templates/assistkick-product-system/packages/frontend/src/utils/task_status.ts +19 -1
  139. package/templates/assistkick-product-system/packages/frontend/vite.config.ts +7 -1
  140. package/templates/assistkick-product-system/packages/shared/db/local.db +0 -0
  141. package/templates/assistkick-product-system/packages/shared/db/migrations/0004_tidy_matthew_murdock.sql +9 -0
  142. package/templates/assistkick-product-system/packages/shared/db/migrations/0005_mysterious_falcon.sql +692 -0
  143. package/templates/assistkick-product-system/packages/shared/db/migrations/0006_next_venom.sql +9 -0
  144. package/templates/assistkick-product-system/packages/shared/db/migrations/0007_deep_barracuda.sql +39 -0
  145. package/templates/assistkick-product-system/packages/shared/db/migrations/0008_puzzling_hannibal_king.sql +1 -0
  146. package/templates/assistkick-product-system/packages/shared/db/migrations/0009_amused_beast.sql +8 -0
  147. package/templates/assistkick-product-system/packages/shared/db/migrations/0010_spotty_moira_mactaggert.sql +9 -0
  148. package/templates/assistkick-product-system/packages/shared/db/migrations/0011_goofy_snowbird.sql +3 -0
  149. package/templates/assistkick-product-system/packages/shared/db/migrations/0011_supreme_doctor_octopus.sql +3 -0
  150. package/templates/assistkick-product-system/packages/shared/db/migrations/0013_reflective_prowler.sql +15 -0
  151. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0004_snapshot.json +921 -0
  152. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0005_snapshot.json +1042 -0
  153. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0006_snapshot.json +1101 -0
  154. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0007_snapshot.json +1336 -0
  155. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0008_snapshot.json +1275 -0
  156. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0009_snapshot.json +1327 -0
  157. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0010_snapshot.json +1393 -0
  158. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0011_snapshot.json +1436 -0
  159. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0013_snapshot.json +1538 -0
  160. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/_journal.json +70 -0
  161. package/templates/assistkick-product-system/packages/shared/db/schema.ts +113 -0
  162. package/templates/assistkick-product-system/packages/shared/lib/claude-service.ts +32 -7
  163. package/templates/assistkick-product-system/packages/shared/lib/constants.ts +9 -0
  164. package/templates/assistkick-product-system/packages/shared/lib/git_workflow.ts +12 -4
  165. package/templates/assistkick-product-system/packages/shared/lib/graph.ts +16 -5
  166. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.test.ts +1753 -0
  167. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.ts +1281 -0
  168. package/templates/assistkick-product-system/packages/shared/lib/workflow_orchestrator.ts +211 -0
  169. package/templates/assistkick-product-system/packages/shared/tools/add_node.test.ts +43 -0
  170. package/templates/assistkick-product-system/packages/shared/tools/add_node.ts +13 -2
  171. package/templates/assistkick-product-system/packages/shared/tools/get_kanban.ts +1 -1
  172. package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.test.ts +226 -0
  173. package/templates/assistkick-product-system/packages/shared/tools/migrate_epics.ts +251 -0
  174. package/templates/assistkick-product-system/packages/shared/tools/update_node.ts +2 -2
  175. package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.test.ts +10 -0
  176. package/templates/assistkick-product-system/packages/shared/utils/hello_workflow.ts +6 -0
  177. package/templates/assistkick-product-system/packages/video/Root.tsx +85 -0
  178. package/templates/assistkick-product-system/packages/video/components/email_scene.tsx +231 -0
  179. package/templates/assistkick-product-system/packages/video/components/outro_scene.tsx +153 -0
  180. package/templates/assistkick-product-system/packages/video/components/part_divider.tsx +90 -0
  181. package/templates/assistkick-product-system/packages/video/components/scene.tsx +226 -0
  182. package/templates/assistkick-product-system/packages/video/components/theme.ts +22 -0
  183. package/templates/assistkick-product-system/packages/video/components/title_scene.tsx +169 -0
  184. package/templates/assistkick-product-system/packages/video/components/video_split_layout.tsx +84 -0
  185. package/templates/assistkick-product-system/packages/video/compositions/.gitkeep +0 -0
  186. package/templates/assistkick-product-system/packages/video/index.ts +4 -0
  187. package/templates/assistkick-product-system/packages/video/package.json +28 -0
  188. package/templates/assistkick-product-system/packages/video/remotion.config.ts +11 -0
  189. package/templates/assistkick-product-system/packages/video/scripts/process_script.test.ts +326 -0
  190. package/templates/assistkick-product-system/packages/video/scripts/process_script.ts +630 -0
  191. package/templates/assistkick-product-system/packages/video/style.css +1 -0
  192. package/templates/assistkick-product-system/packages/video/tsconfig.json +18 -0
  193. package/templates/assistkick-product-system/tests/graph_legend.test.ts +2 -1
  194. package/templates/assistkick-product-system/tests/video_render_service.test.ts +179 -0
  195. package/templates/assistkick-product-system/tests/web_terminal.test.ts +219 -455
  196. package/templates/assistkick-product-system/tests/workflow_integration.test.ts +341 -0
  197. package/templates/skills/assistkick-bootstrap/SKILL.md +3 -3
  198. package/templates/skills/assistkick-code-reviewer/SKILL.md +2 -2
  199. package/templates/skills/assistkick-debugger/SKILL.md +2 -2
  200. package/templates/skills/assistkick-developer/SKILL.md +6 -3
  201. package/templates/skills/assistkick-developer/references/react_development_guidelines.md +225 -0
  202. package/templates/skills/assistkick-interview/SKILL.md +2 -2
  203. package/templates/skills/product-system/graph.json +1890 -0
  204. package/templates/skills/product-system/kanban.json +304 -0
  205. package/templates/skills/product-system/nodes/comp_001.md +56 -0
  206. package/templates/skills/product-system/nodes/comp_002.md +57 -0
  207. package/templates/skills/product-system/nodes/data_001.md +51 -0
  208. package/templates/skills/product-system/nodes/data_002.md +40 -0
  209. package/templates/skills/product-system/nodes/data_004.md +38 -0
  210. package/templates/skills/product-system/nodes/dec_001.md +34 -0
  211. package/templates/skills/product-system/nodes/dec_016.md +32 -0
  212. package/templates/skills/product-system/nodes/feat_008.md +30 -0
  213. package/templates/skills/video-composition-agent/SKILL.md +232 -0
  214. package/templates/skills/video-script-writer/SKILL.md +136 -0
@@ -1,88 +1,204 @@
1
1
  /**
2
- * Pipeline API routes — start/status/unblock dev pipeline + orchestrator.
2
+ * Workflow execution routes — start/resume/status for workflow executions + orchestrator.
3
+ * Replaces the old pipeline routes with WorkflowEngine-based endpoints.
3
4
  */
4
5
 
5
6
  import { Router } from 'express';
6
- import { log, pipeline, orchestrator } from '../services/init.js';
7
-
8
- const router: Router = Router();
9
-
10
- // POST /api/kanban/:id/develop
11
- router.post('/:id/develop', async (req, res) => {
12
- const featureId = req.params.id;
13
- try {
14
- const result = await pipeline.start(featureId);
15
- res.status(result.status).json(result.error ? { error: result.error } : { started: result.started, feature_id: result.feature_id });
16
- } catch (err: any) {
17
- log('DEVELOP', `UNEXPECTED ERROR: ${err.message}`);
18
- res.status(500).json({ error: err.message });
19
- }
20
- });
21
-
22
- // GET /api/kanban/:id/pipeline
23
- router.get('/:id/pipeline', async (req, res) => {
24
- const featureId = req.params.id;
25
- const result = await pipeline.getStatus(featureId);
26
- res.json(result);
27
- });
28
-
29
- // POST /api/kanban/:id/resume
30
- router.post('/:id/resume', async (req, res) => {
31
- const featureId = req.params.id;
32
- try {
33
- const result = await pipeline.resume(featureId);
34
- res.status(result.status).json(result.error ? { error: result.error } : { resumed: result.resumed, feature_id: result.feature_id });
35
- } catch (err: any) {
36
- log('RESUME', `UNEXPECTED ERROR: ${err.message}`);
37
- res.status(500).json({ error: err.message });
38
- }
39
- });
40
-
41
- // POST /api/kanban/:id/unblock
42
- router.post('/:id/unblock', async (req, res) => {
43
- const featureId = req.params.id;
44
- try {
45
- const result = await pipeline.unblock(featureId);
46
- res.status(result.status).json(result.error ? { error: result.error } : { unblocked: result.unblocked, feature_id: result.feature_id });
47
- } catch (err: any) {
48
- log('UNBLOCK', `ERROR: ${err.message}`);
49
- res.status(500).json({ error: err.message });
50
- }
51
- });
52
-
53
- // --- Orchestrator (Play All) endpoints ---
54
-
55
- // POST /api/pipeline/play-all
56
- router.post('/play-all', async (req, res) => {
57
- const projectId = req.query.project_id as string | undefined;
58
- try {
59
- const result = orchestrator.startPlayAll(projectId);
60
- const resolved = await result;
61
- res.status(resolved.status).json(
62
- resolved.error ? { error: resolved.error } : { started: resolved.started }
63
- );
64
- } catch (err: any) {
65
- log('ORCHESTRATOR', `UNEXPECTED ERROR: ${err.message}`);
66
- res.status(500).json({ error: err.message });
67
- }
68
- });
69
-
70
- // POST /api/pipeline/stop-all
71
- router.post('/stop-all', (_req, res) => {
72
- try {
73
- const result = orchestrator.stopPlayAll();
74
- res.status(result.status).json(
75
- result.error ? { error: result.error } : { stopped: result.stopped }
76
- );
77
- } catch (err: any) {
78
- log('ORCHESTRATOR', `UNEXPECTED ERROR: ${err.message}`);
79
- res.status(500).json({ error: err.message });
80
- }
81
- });
82
-
83
- // GET /api/pipeline/orchestrator-status
84
- router.get('/orchestrator-status', (_req, res) => {
85
- res.json(orchestrator.getStatus());
86
- });
87
-
88
- export default router;
7
+ import type { WorkflowEngine } from '@assistkick/shared/lib/workflow_engine.js';
8
+ import type { WorkflowOrchestrator } from '@assistkick/shared/lib/workflow_orchestrator.js';
9
+ import type { WorkflowService } from '../services/workflow_service.js';
10
+
11
+ interface WorkflowExecutionRoutesDeps {
12
+ workflowEngine: WorkflowEngine;
13
+ orchestrator: WorkflowOrchestrator;
14
+ workflowService: WorkflowService;
15
+ log: (tag: string, ...args: any[]) => void;
16
+ }
17
+
18
+ export const createWorkflowExecutionRoutes = ({
19
+ workflowEngine,
20
+ orchestrator,
21
+ workflowService,
22
+ log,
23
+ }: WorkflowExecutionRoutesDeps): Router => {
24
+ const router: Router = Router();
25
+
26
+ // POST /api/kanban/:id/workflow start a workflow execution for a feature
27
+ router.post('/:id/workflow', async (req, res) => {
28
+ const featureId = req.params.id;
29
+ const { workflowId, projectId } = req.body || {};
30
+ log('WORKFLOW', `POST /api/kanban/${featureId}/workflow workflowId=${workflowId || 'default'}`);
31
+
32
+ if (!projectId) {
33
+ res.status(400).json({ error: 'projectId is required' });
34
+ return;
35
+ }
36
+
37
+ try {
38
+ // Resolve workflow ID: use provided workflowId, or find the default
39
+ let resolvedWorkflowId = workflowId;
40
+ if (!resolvedWorkflowId) {
41
+ const defaultWorkflow = await workflowService.resolveDefault(projectId);
42
+ if (!defaultWorkflow) {
43
+ res.status(400).json({ error: 'No default workflow found. Please specify a workflowId or set a default workflow.' });
44
+ return;
45
+ }
46
+ resolvedWorkflowId = defaultWorkflow.id;
47
+ }
48
+
49
+ const result = await workflowEngine.start(featureId, resolvedWorkflowId, projectId);
50
+ res.json({ started: true, feature_id: featureId, executionId: result.executionId });
51
+ } catch (err: any) {
52
+ log('WORKFLOW', `START ERROR: ${err.message}`);
53
+ res.status(500).json({ error: err.message });
54
+ }
55
+ });
56
+
57
+ // GET /api/kanban/:id/workflow-status get workflow execution state for a feature
58
+ router.get('/:id/workflow-status', async (req, res) => {
59
+ const featureId = req.params.id;
60
+ try {
61
+ const status = await workflowEngine.getStatus(featureId);
62
+ res.json(status);
63
+ } catch (err: any) {
64
+ log('WORKFLOW', `STATUS ERROR: ${err.message}`);
65
+ res.status(500).json({ error: err.message });
66
+ }
67
+ });
68
+
69
+ // POST /api/kanban/:id/workflow/resume — resume a failed/interrupted workflow
70
+ router.post('/:id/workflow/resume', async (req, res) => {
71
+ const featureId = req.params.id;
72
+ log('WORKFLOW', `POST /api/kanban/${featureId}/workflow/resume`);
73
+
74
+ try {
75
+ // Find the most recent resumable execution for this feature
76
+ const executionId = await workflowEngine.findResumableExecution(featureId);
77
+ if (!executionId) {
78
+ res.status(404).json({ error: `No resumable workflow execution found for feature ${featureId}` });
79
+ return;
80
+ }
81
+
82
+ await workflowEngine.resume(executionId);
83
+ res.json({ resumed: true, feature_id: featureId, executionId });
84
+ } catch (err: any) {
85
+ log('WORKFLOW', `RESUME ERROR: ${err.message}`);
86
+ res.status(500).json({ error: err.message });
87
+ }
88
+ });
89
+
90
+ // POST /api/kanban/:id/workflow/force-next — skip current node and advance to next
91
+ router.post('/:id/workflow/force-next', async (req, res) => {
92
+ const featureId = req.params.id;
93
+ const { nodeId } = req.body || {};
94
+ log('WORKFLOW', `POST /api/kanban/${featureId}/workflow/force-next nodeId=${nodeId}`);
95
+
96
+ if (!nodeId) {
97
+ res.status(400).json({ error: 'nodeId is required' });
98
+ return;
99
+ }
100
+
101
+ try {
102
+ await workflowEngine.forceNextNode(featureId, nodeId);
103
+ res.json({ success: true, feature_id: featureId, skippedNodeId: nodeId });
104
+ } catch (err: any) {
105
+ log('WORKFLOW', `FORCE-NEXT ERROR: ${err.message}`);
106
+ res.status(500).json({ error: err.message });
107
+ }
108
+ });
109
+
110
+ // POST /api/kanban/:id/workflow/restart-node — restart the current node
111
+ router.post('/:id/workflow/restart-node', async (req, res) => {
112
+ const featureId = req.params.id;
113
+ const { nodeId } = req.body || {};
114
+ log('WORKFLOW', `POST /api/kanban/${featureId}/workflow/restart-node nodeId=${nodeId}`);
115
+
116
+ if (!nodeId) {
117
+ res.status(400).json({ error: 'nodeId is required' });
118
+ return;
119
+ }
120
+
121
+ try {
122
+ await workflowEngine.restartNode(featureId, nodeId);
123
+ res.json({ success: true, feature_id: featureId, restartedNodeId: nodeId });
124
+ } catch (err: any) {
125
+ log('WORKFLOW', `RESTART-NODE ERROR: ${err.message}`);
126
+ res.status(500).json({ error: err.message });
127
+ }
128
+ });
129
+
130
+ // GET /api/kanban/:id/workflow-monitor — get full execution monitor data for a feature
131
+ router.get('/:id/workflow-monitor', async (req, res) => {
132
+ const featureId = req.params.id;
133
+ try {
134
+ const data = await workflowEngine.getMonitorData(featureId);
135
+ if (!data) {
136
+ res.json({ monitor: null });
137
+ return;
138
+ }
139
+ res.json({ monitor: data });
140
+ } catch (err: any) {
141
+ log('WORKFLOW', `MONITOR ERROR: ${err.message}`);
142
+ res.status(500).json({ error: err.message });
143
+ }
144
+ });
145
+
146
+ // GET /api/kanban/:id/workflow-metrics — get per-agent KPI metrics for a feature's latest execution
147
+ router.get('/:id/workflow-metrics', async (req, res) => {
148
+ const featureId = req.params.id;
149
+ try {
150
+ const status = await workflowEngine.getStatus(featureId);
151
+ const executionId = status.executionId as string | undefined;
152
+ if (!executionId) {
153
+ res.json({ metrics: [] });
154
+ return;
155
+ }
156
+ const metrics = await workflowEngine.getExecutionMetrics(executionId);
157
+ res.json({ metrics });
158
+ } catch (err: any) {
159
+ log('WORKFLOW', `METRICS ERROR: ${err.message}`);
160
+ res.status(500).json({ error: err.message });
161
+ }
162
+ });
163
+
164
+ // --- Orchestrator (Play All) endpoints ---
165
+
166
+ // POST /api/pipeline/play-all
167
+ router.post('/play-all', async (req, res) => {
168
+ const projectId = req.query.project_id as string | undefined;
169
+ const epicId = req.query.epic_id as string | undefined;
170
+ if (!projectId) {
171
+ res.status(400).json({ error: 'project_id query parameter is required' });
172
+ return;
173
+ }
174
+ try {
175
+ const result = await orchestrator.startPlayAll(projectId, epicId);
176
+ res.status(result.status).json(
177
+ result.error ? { error: result.error } : { started: result.started },
178
+ );
179
+ } catch (err: any) {
180
+ log('ORCHESTRATOR', `UNEXPECTED ERROR: ${err.message}`);
181
+ res.status(500).json({ error: err.message });
182
+ }
183
+ });
184
+
185
+ // POST /api/pipeline/stop-all
186
+ router.post('/stop-all', (_req, res) => {
187
+ try {
188
+ const result = orchestrator.stopPlayAll();
189
+ res.status(result.status).json(
190
+ result.error ? { error: result.error } : { stopped: result.stopped },
191
+ );
192
+ } catch (err: any) {
193
+ log('ORCHESTRATOR', `UNEXPECTED ERROR: ${err.message}`);
194
+ res.status(500).json({ error: err.message });
195
+ }
196
+ });
197
+
198
+ // GET /api/pipeline/orchestrator-status
199
+ router.get('/orchestrator-status', (_req, res) => {
200
+ res.json(orchestrator.getStatus());
201
+ });
202
+
203
+ return router;
204
+ };
@@ -33,16 +33,19 @@ export const createProjectRoutes = ({ projectService, log }: ProjectRoutesDeps):
33
33
 
34
34
  // POST /api/projects — create a new project
35
35
  router.post('/', async (req, res) => {
36
- const { name } = req.body;
37
- log('PROJECTS', `POST /api/projects name="${name}"`);
36
+ const { name, type } = req.body;
37
+ log('PROJECTS', `POST /api/projects name="${name}" type="${type || 'software'}"`);
38
38
 
39
39
  if (!name || typeof name !== 'string' || !name.trim()) {
40
40
  res.status(400).json({ error: 'Project name is required' });
41
41
  return;
42
42
  }
43
43
 
44
+ const validTypes = ['software', 'video'];
45
+ const projectType = type && validTypes.includes(type) ? type : 'software';
46
+
44
47
  try {
45
- const project = await projectService.create(name.trim());
48
+ const project = await projectService.create(name.trim(), projectType);
46
49
  res.status(201).json({ project });
47
50
  } catch (err: any) {
48
51
  log('PROJECTS', `Create project failed: ${err.message}`);
@@ -8,7 +8,12 @@
8
8
  */
9
9
 
10
10
  import { Router } from 'express';
11
+ import express from 'express';
11
12
  import type { Request, Response } from 'express';
13
+ import { writeFile, mkdir } from 'node:fs/promises';
14
+ import { tmpdir } from 'node:os';
15
+ import { join, extname } from 'node:path';
16
+ import { randomBytes } from 'node:crypto';
12
17
  import type { PtySessionManager } from '../services/pty_session_manager.js';
13
18
 
14
19
  interface TerminalRoutesDeps {
@@ -31,13 +36,13 @@ export const createTerminalRoutes = ({ ptyManager, log }: TerminalRoutesDeps): R
31
36
  router.use(requireAdmin as any);
32
37
 
33
38
  // GET /api/terminal/sessions
34
- router.get('/sessions', (_req, res) => {
35
- const sessions = ptyManager.listSessions();
39
+ router.get('/sessions', async (_req, res) => {
40
+ const sessions = await ptyManager.listSessions();
36
41
  res.json({ sessions });
37
42
  });
38
43
 
39
44
  // POST /api/terminal/sessions
40
- router.post('/sessions', (req, res) => {
45
+ router.post('/sessions', async (req, res) => {
41
46
  const { projectId, projectName } = req.body;
42
47
 
43
48
  if (!projectId || typeof projectId !== 'string') {
@@ -49,31 +54,62 @@ export const createTerminalRoutes = ({ ptyManager, log }: TerminalRoutesDeps): R
49
54
  return;
50
55
  }
51
56
 
52
- const session = ptyManager.createSession(projectId.trim(), projectName.trim(), 80, 24);
57
+ const session = await ptyManager.createSession(projectId.trim(), projectName.trim(), 80, 24);
53
58
  log('TERMINAL', `Created session "${session.name}" for project ${projectId}`);
54
- res.status(201).json({
55
- session: {
56
- id: session.id,
57
- name: session.name,
58
- projectId: session.projectId,
59
- projectName: session.projectName,
60
- state: session.state,
61
- createdAt: session.createdAt.toISOString(),
62
- },
63
- });
59
+ res.status(201).json({ session });
64
60
  });
65
61
 
62
+ // POST /api/terminal/upload-image — accept a dropped image, save to temp, return path
63
+ const UPLOAD_DIR = join(tmpdir(), 'assistkick-terminal-uploads');
64
+ const ALLOWED_IMAGE_EXTS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.bmp']);
65
+
66
+ router.post(
67
+ '/upload-image',
68
+ express.raw({ limit: '10mb', type: 'application/octet-stream' }) as any,
69
+ async (req: Request, res: Response) => {
70
+ const filename = req.query.filename as string | undefined;
71
+ if (!filename) {
72
+ res.status(400).json({ error: 'filename query parameter is required' });
73
+ return;
74
+ }
75
+
76
+ const ext = extname(filename).toLowerCase();
77
+ if (!ALLOWED_IMAGE_EXTS.has(ext)) {
78
+ res.status(400).json({ error: `Unsupported image type: ${ext}` });
79
+ return;
80
+ }
81
+
82
+ const body = req.body as Buffer;
83
+ if (!body || body.length === 0) {
84
+ res.status(400).json({ error: 'Empty file body' });
85
+ return;
86
+ }
87
+
88
+ try {
89
+ await mkdir(UPLOAD_DIR, { recursive: true });
90
+ const uniqueName = `${Date.now()}-${randomBytes(4).toString('hex')}${ext}`;
91
+ const filePath = join(UPLOAD_DIR, uniqueName);
92
+ await writeFile(filePath, body);
93
+ log('TERMINAL', `Uploaded image: ${filePath} (${body.length} bytes)`);
94
+ res.json({ path: filePath });
95
+ } catch (err: any) {
96
+ log('TERMINAL', `Image upload failed: ${err.message}`);
97
+ res.status(500).json({ error: 'Failed to save image' });
98
+ }
99
+ },
100
+ );
101
+
66
102
  // DELETE /api/terminal/sessions/:id
67
- router.delete('/sessions/:id', (req, res) => {
103
+ router.delete('/sessions/:id', async (req, res) => {
68
104
  const { id } = req.params;
69
- const session = ptyManager.getSession(id);
105
+ const session = await ptyManager.getSession(id);
70
106
 
71
107
  if (!session) {
72
108
  res.status(404).json({ error: 'Session not found' });
73
109
  return;
74
110
  }
75
111
 
76
- ptyManager.destroySession(id);
112
+ await ptyManager.destroySession(id);
77
113
  log('TERMINAL', `Killed session ${id}`);
78
114
  res.json({ ok: true });
79
115
  });
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Video routes — TTS audio generation, bundle management, composition listing, and rendering.
3
+ *
4
+ * POST /api/video/generate-tts — generate TTS audio from a video script
5
+ * GET /api/video/compositions — list available Remotion compositions
6
+ * GET /api/video/bundle/status — get bundle build status
7
+ * POST /api/video/bundle/rebuild — trigger a bundle rebuild
8
+ * GET /api/video/renders — list all renders for a project
9
+ * POST /api/video/render — start a video render
10
+ * GET /api/video/render/:id — get render status
11
+ * DELETE /api/video/render/:id — delete a render and its file
12
+ * GET /api/video/file/:renderId — serve rendered MP4 file
13
+ */
14
+
15
+ import { Router } from 'express';
16
+ import express from 'express';
17
+ import { existsSync } from 'node:fs';
18
+ import type { TtsService } from '../services/tts_service.js';
19
+ import type { BundleService } from '../services/bundle_service.js';
20
+ import type { VideoRenderService } from '../services/video_render_service.js';
21
+
22
+ interface VideoRoutesDeps {
23
+ ttsService: TtsService;
24
+ bundleService: BundleService;
25
+ videoRenderService: VideoRenderService;
26
+ log: (tag: string, ...args: any[]) => void;
27
+ }
28
+
29
+ export const createVideoRoutes = ({ ttsService, bundleService, videoRenderService, log }: VideoRoutesDeps): Router => {
30
+ const router: Router = Router();
31
+
32
+ // POST /api/video/generate-tts
33
+ router.post('/generate-tts', async (req, res) => {
34
+ const { scriptPath, projectId, featureId, force, voiceId } = req.body;
35
+ log('VIDEO', `POST /api/video/generate-tts project=${projectId} feature=${featureId}`);
36
+
37
+ if (!scriptPath || typeof scriptPath !== 'string') {
38
+ res.status(400).json({ error: 'scriptPath is required' });
39
+ return;
40
+ }
41
+
42
+ if (!projectId || typeof projectId !== 'string') {
43
+ res.status(400).json({ error: 'projectId is required' });
44
+ return;
45
+ }
46
+
47
+ if (!featureId || typeof featureId !== 'string') {
48
+ res.status(400).json({ error: 'featureId is required' });
49
+ return;
50
+ }
51
+
52
+ if (!process.env.ELEVENLABS_API_KEY) {
53
+ res.status(500).json({ error: 'ELEVENLABS_API_KEY is not configured on the server.' });
54
+ return;
55
+ }
56
+
57
+ try {
58
+ const result = await ttsService.generate({
59
+ scriptPath,
60
+ projectId,
61
+ featureId,
62
+ force: force === true,
63
+ voiceId: typeof voiceId === 'string' ? voiceId : undefined,
64
+ });
65
+
66
+ res.json(result);
67
+ } catch (err: any) {
68
+ log('VIDEO', `TTS generation failed: ${err.message}`);
69
+ res.status(500).json({ error: err.message });
70
+ }
71
+ });
72
+
73
+ // GET /api/video/compositions — list available Remotion compositions with metadata
74
+ router.get('/compositions', (_req, res) => {
75
+ log('VIDEO', 'GET /api/video/compositions');
76
+ try {
77
+ const compositions = bundleService.listCompositions();
78
+ res.json({ compositions });
79
+ } catch (err: any) {
80
+ log('VIDEO', `Failed to list compositions: ${err.message}`);
81
+ res.status(500).json({ error: 'Failed to list compositions' });
82
+ }
83
+ });
84
+
85
+ // GET /api/video/bundle/status — get current bundle status
86
+ router.get('/bundle/status', (_req, res) => {
87
+ log('VIDEO', 'GET /api/video/bundle/status');
88
+ res.json(bundleService.getStatus());
89
+ });
90
+
91
+ // POST /api/video/bundle/rebuild — trigger a bundle rebuild
92
+ router.post('/bundle/rebuild', async (_req, res) => {
93
+ log('VIDEO', 'POST /api/video/bundle/rebuild');
94
+ try {
95
+ const status = await bundleService.buildBundle();
96
+ res.json(status);
97
+ } catch (err: any) {
98
+ log('VIDEO', `Bundle rebuild failed: ${err.message}`);
99
+ res.status(500).json({ error: 'Bundle rebuild failed' });
100
+ }
101
+ });
102
+
103
+ // GET /api/video/renders — list all renders for a project
104
+ router.get('/renders', async (req, res) => {
105
+ const projectId = req.query.projectId as string | undefined;
106
+ log('VIDEO', `GET /api/video/renders project=${projectId}`);
107
+
108
+ if (!projectId || typeof projectId !== 'string') {
109
+ res.status(400).json({ error: 'projectId query parameter is required' });
110
+ return;
111
+ }
112
+
113
+ try {
114
+ const renders = await videoRenderService.listRenders(projectId);
115
+ res.json({ renders });
116
+ } catch (err: any) {
117
+ log('VIDEO', `Failed to list renders: ${err.message}`);
118
+ res.status(500).json({ error: 'Failed to list renders' });
119
+ }
120
+ });
121
+
122
+ // POST /api/video/render — start a video render
123
+ router.post('/render', async (req, res) => {
124
+ const { compositionId, projectId, featureId, resolution, aspectRatio, fileOutputPrefix } = req.body;
125
+ log('VIDEO', `POST /api/video/render composition=${compositionId} project=${projectId} feature=${featureId}`);
126
+
127
+ if (!compositionId || typeof compositionId !== 'string') {
128
+ res.status(400).json({ error: 'compositionId is required' });
129
+ return;
130
+ }
131
+
132
+ if (!projectId || typeof projectId !== 'string') {
133
+ res.status(400).json({ error: 'projectId is required' });
134
+ return;
135
+ }
136
+
137
+ if (!featureId || typeof featureId !== 'string') {
138
+ res.status(400).json({ error: 'featureId is required' });
139
+ return;
140
+ }
141
+
142
+ try {
143
+ const status = await videoRenderService.startRender({
144
+ compositionId,
145
+ projectId,
146
+ featureId,
147
+ resolution: typeof resolution === 'string' ? resolution : undefined,
148
+ aspectRatio: typeof aspectRatio === 'string' ? aspectRatio : undefined,
149
+ fileOutputPrefix: typeof fileOutputPrefix === 'string' ? fileOutputPrefix : undefined,
150
+ });
151
+
152
+ res.json(status);
153
+ } catch (err: any) {
154
+ log('VIDEO', `Render start failed: ${err.message}`);
155
+ res.status(500).json({ error: err.message });
156
+ }
157
+ });
158
+
159
+ // GET /api/video/render/:id — get render status
160
+ router.get('/render/:id', async (req, res) => {
161
+ const { id } = req.params;
162
+ log('VIDEO', `GET /api/video/render/${id}`);
163
+
164
+ try {
165
+ const status = await videoRenderService.getStatus(id);
166
+ if (!status) {
167
+ res.status(404).json({ error: 'Render not found' });
168
+ return;
169
+ }
170
+ res.json(status);
171
+ } catch (err: any) {
172
+ log('VIDEO', `Failed to get render status: ${err.message}`);
173
+ res.status(500).json({ error: err.message });
174
+ }
175
+ });
176
+
177
+ // DELETE /api/video/render/:id — delete a render and its MP4 file
178
+ router.delete('/render/:id', async (req, res) => {
179
+ const { id } = req.params;
180
+ log('VIDEO', `DELETE /api/video/render/${id}`);
181
+
182
+ try {
183
+ const deleted = await videoRenderService.deleteRender(id);
184
+ if (!deleted) {
185
+ res.status(404).json({ error: 'Render not found' });
186
+ return;
187
+ }
188
+ res.json({ ok: true });
189
+ } catch (err: any) {
190
+ log('VIDEO', `Failed to delete render: ${err.message}`);
191
+ res.status(500).json({ error: err.message });
192
+ }
193
+ });
194
+
195
+ // GET /api/video/file/:renderId — serve the rendered MP4 file
196
+ router.get('/file/:renderId', async (req, res) => {
197
+ const { renderId } = req.params;
198
+ log('VIDEO', `GET /api/video/file/${renderId}`);
199
+
200
+ try {
201
+ const filePath = await videoRenderService.getFilePath(renderId);
202
+ if (!filePath || !existsSync(filePath)) {
203
+ res.status(404).json({ error: 'Rendered file not found' });
204
+ return;
205
+ }
206
+ res.sendFile(filePath);
207
+ } catch (err: any) {
208
+ log('VIDEO', `Failed to serve render file: ${err.message}`);
209
+ res.status(500).json({ error: err.message });
210
+ }
211
+ });
212
+
213
+ // Serve the built bundle as static files at /api/video/bundle/
214
+ const bundleDir = bundleService.getBundleDir();
215
+ router.use('/bundle', express.static(bundleDir, { maxAge: '1h' }));
216
+
217
+ return router;
218
+ };