@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
@@ -0,0 +1,251 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * migrate_epics — One-time migration that converts 4 existing epic-like feature nodes
5
+ * to the new epic node type.
6
+ *
7
+ * Target nodes: feat_a20d3b2d, feat_21ad113d, feat_e75c9f47, feat_c4d0e19b
8
+ *
9
+ * For each target node:
10
+ * 1. Changes type from 'feature' to 'epic'
11
+ * 2. Squishes all sections into a single Description + adds empty Scope
12
+ * 3. Regenerates ID from feat_ to epic_ prefix
13
+ * 4. Updates all edge references (fromId/toId) and kanban entries
14
+ * 5. Sets completeness to 1.0
15
+ *
16
+ * Usage:
17
+ * npx tsx packages/shared/tools/migrate_epics.ts --project-id <id>
18
+ * npx tsx packages/shared/tools/migrate_epics.ts --project-id <id> --dry-run
19
+ */
20
+
21
+ import { program } from 'commander';
22
+ import chalk from 'chalk';
23
+ import { eq } from 'drizzle-orm';
24
+ import { getDb, closeDb } from '../lib/db.js';
25
+ import { nodes, edges, kanban, reviewMeta } from '../db/schema.js';
26
+
27
+ program
28
+ .requiredOption('--project-id <id>', 'Project ID')
29
+ .option('--dry-run', 'Report what would be changed without writing to database')
30
+ .parse();
31
+
32
+ const opts = program.opts();
33
+
34
+ const TARGET_FEAT_IDS = [
35
+ 'feat_a20d3b2d',
36
+ 'feat_21ad113d',
37
+ 'feat_e75c9f47',
38
+ 'feat_c4d0e19b',
39
+ ];
40
+
41
+ /**
42
+ * Parse a section-based markdown body into { sectionName: content }.
43
+ */
44
+ const parseSections = (body: string): Record<string, string> => {
45
+ const sections: Record<string, string> = {};
46
+ const lines = body.split('\n');
47
+ let currentSection: string | null = null;
48
+ let currentLines: string[] = [];
49
+
50
+ for (const line of lines) {
51
+ const match = line.match(/^## (.+)$/);
52
+ if (match) {
53
+ if (currentSection !== null) {
54
+ sections[currentSection] = currentLines.join('\n').trim();
55
+ }
56
+ currentSection = match[1].trim();
57
+ currentLines = [];
58
+ } else if (currentSection !== null) {
59
+ currentLines.push(line);
60
+ }
61
+ }
62
+ if (currentSection !== null) {
63
+ sections[currentSection] = currentLines.join('\n').trim();
64
+ }
65
+
66
+ return sections;
67
+ };
68
+
69
+ /**
70
+ * Squish all feature sections into a single Description for the epic body.
71
+ * Preserves original section headers as ### sub-headings within Description.
72
+ * Skips the Relations section (auto-generated, not part of the body content).
73
+ */
74
+ const squishSections = (sections: Record<string, string>): string => {
75
+ const parts: string[] = [];
76
+
77
+ for (const [name, content] of Object.entries(sections)) {
78
+ if (name === 'Relations') continue;
79
+ if (!content.trim()) continue;
80
+ parts.push(`### ${name}`);
81
+ parts.push(content.trim());
82
+ parts.push('');
83
+ }
84
+
85
+ return parts.join('\n').trim();
86
+ };
87
+
88
+ /**
89
+ * Convert a feat_ ID to an epic_ ID by replacing the prefix.
90
+ */
91
+ const toEpicId = (featId: string): string => {
92
+ return featId.replace(/^feat_/, 'epic_');
93
+ };
94
+
95
+ /**
96
+ * Migrate a single feature node to an epic node.
97
+ */
98
+ const migrateNode = async (
99
+ db: ReturnType<typeof getDb>,
100
+ featId: string,
101
+ dryRun: boolean,
102
+ ): Promise<{ oldId: string; newId: string; name: string }> => {
103
+ const epicId = toEpicId(featId);
104
+
105
+ // Read the existing node
106
+ const rows = await db.select().from(nodes).where(eq(nodes.id, featId));
107
+ const row = rows[0];
108
+ if (!row) {
109
+ throw new Error(`Node not found: ${featId}`);
110
+ }
111
+
112
+ // Parse existing body and squish into epic format
113
+ const sections = parseSections(row.body || '');
114
+ const squished = squishSections(sections);
115
+
116
+ const epicBody = `## Description\n${squished}\n\n## Scope\n`;
117
+
118
+ const now = new Date().toISOString();
119
+
120
+ if (!dryRun) {
121
+ // 1. Insert the new epic node
122
+ await db.insert(nodes).values({
123
+ id: epicId,
124
+ type: 'epic',
125
+ name: row.name,
126
+ status: 'defined',
127
+ priority: row.priority,
128
+ completeness: 1.0,
129
+ openQuestionsCount: 0,
130
+ kind: null,
131
+ createdAt: row.createdAt,
132
+ updatedAt: now,
133
+ body: epicBody,
134
+ projectId: row.projectId,
135
+ });
136
+
137
+ // 2. Update edges where this node is the source (fromId)
138
+ const fromEdges = await db.select().from(edges).where(eq(edges.fromId, featId));
139
+ // Delete all old edges originating from the feat ID
140
+ await db.delete(edges).where(eq(edges.fromId, featId));
141
+ // Re-insert with the new epic ID
142
+ for (const edge of fromEdges) {
143
+ await db.insert(edges).values({
144
+ fromId: epicId,
145
+ relation: edge.relation,
146
+ toId: edge.toId,
147
+ projectId: edge.projectId,
148
+ }).onConflictDoNothing();
149
+ }
150
+
151
+ // 3. Update edges where this node is the target (toId)
152
+ const toEdges = await db.select().from(edges).where(eq(edges.toId, featId));
153
+ // Delete all old edges pointing to the feat ID
154
+ await db.delete(edges).where(eq(edges.toId, featId));
155
+ // Re-insert with the new epic ID
156
+ for (const edge of toEdges) {
157
+ await db.insert(edges).values({
158
+ fromId: edge.fromId,
159
+ relation: edge.relation,
160
+ toId: epicId,
161
+ projectId: edge.projectId,
162
+ }).onConflictDoNothing();
163
+ }
164
+
165
+ // 4. Update kanban entry
166
+ const kanbanRows = await db.select().from(kanban).where(eq(kanban.nodeId, featId));
167
+ if (kanbanRows.length > 0) {
168
+ const entry = kanbanRows[0];
169
+ await db.delete(kanban).where(eq(kanban.nodeId, featId));
170
+ await db.insert(kanban).values({
171
+ nodeId: epicId,
172
+ columnName: entry.columnName,
173
+ position: entry.position,
174
+ projectId: entry.projectId,
175
+ });
176
+ }
177
+
178
+ // 5. Migrate reviewMeta (kanban metadata) to new ID
179
+ const oldMetaKey = `kanban:${featId}`;
180
+ const newMetaKey = `kanban:${epicId}`;
181
+ const metaRows = await db.select().from(reviewMeta).where(eq(reviewMeta.key, oldMetaKey));
182
+ if (metaRows.length > 0) {
183
+ await db.delete(reviewMeta).where(eq(reviewMeta.key, oldMetaKey));
184
+ await db.insert(reviewMeta).values({
185
+ key: newMetaKey,
186
+ value: metaRows[0].value,
187
+ projectId: metaRows[0].projectId,
188
+ });
189
+ }
190
+
191
+ // 6. Delete the old feature node
192
+ await db.delete(nodes).where(eq(nodes.id, featId));
193
+ }
194
+
195
+ return { oldId: featId, newId: epicId, name: row.name };
196
+ };
197
+
198
+ const main = async () => {
199
+ const dryRun = opts.dryRun || false;
200
+ const db = getDb();
201
+
202
+ if (dryRun) {
203
+ console.log(chalk.cyan('=== DRY RUN — no database writes will occur ===\n'));
204
+ }
205
+
206
+ console.log(chalk.bold('Migrating epic-like features to epic node type...\n'));
207
+
208
+ const results: { oldId: string; newId: string; name: string }[] = [];
209
+ const errors: { featId: string; error: string }[] = [];
210
+
211
+ for (const featId of TARGET_FEAT_IDS) {
212
+ try {
213
+ console.log(chalk.blue(` Migrating ${featId}...`));
214
+ const result = await migrateNode(db, featId, dryRun);
215
+ results.push(result);
216
+ console.log(chalk.green(` ✓ ${result.oldId} → ${result.newId} (${result.name})`));
217
+ } catch (err) {
218
+ const message = err instanceof Error ? err.message : String(err);
219
+ errors.push({ featId, error: message });
220
+ console.log(chalk.red(` ✗ ${featId}: ${message}`));
221
+ }
222
+ }
223
+
224
+ // Summary
225
+ console.log(chalk.bold('\n=== Migration Summary ==='));
226
+ console.log(` Migrated: ${results.length}`);
227
+ console.log(` Errors: ${errors.length}`);
228
+
229
+ for (const r of results) {
230
+ console.log(` ${r.oldId} → ${r.newId} (${r.name})`);
231
+ }
232
+
233
+ if (errors.length > 0) {
234
+ console.log(chalk.red('\n Errors:'));
235
+ for (const e of errors) {
236
+ console.log(chalk.red(` ${e.featId}: ${e.error}`));
237
+ }
238
+ }
239
+
240
+ if (dryRun) {
241
+ console.log(chalk.cyan('\n=== DRY RUN complete — no changes were made ==='));
242
+ }
243
+
244
+ closeDb();
245
+ };
246
+
247
+ main().catch((err) => {
248
+ console.error(chalk.red(`Migration failed: ${err.message}`));
249
+ closeDb();
250
+ process.exit(1);
251
+ });
@@ -109,9 +109,9 @@ const opts = program.opts();
109
109
  }
110
110
  await patchNode(id, patch);
111
111
 
112
- // Auto-add feature to kanban when it reaches defined + 100% completeness
112
+ // Auto-add feature/epic to kanban when it reaches defined + 100% completeness
113
113
  const finalStatus = opts.setStatus || meta.status;
114
- if (nodeMeta.type === 'feature' && finalStatus === 'defined' && meta.completeness >= 1) {
114
+ if ((nodeMeta.type === 'feature' || nodeMeta.type === 'epic') && finalStatus === 'defined' && meta.completeness >= 1) {
115
115
  const existing = await getKanbanEntry(id);
116
116
  if (!existing) {
117
117
  await saveKanbanEntry(id, { column: 'backlog', rejection_count: 0, notes: [] }, opts.projectId);
@@ -0,0 +1,10 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { helloWorkflow } from './hello_workflow.js';
4
+
5
+ describe('helloWorkflow', () => {
6
+ it('returns the expected greeting string', () => {
7
+ const result = helloWorkflow();
8
+ assert.equal(result, 'Hello from workflow!');
9
+ });
10
+ });
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Minimal smoke-test utility to validate the end-to-end workflow pipeline.
3
+ */
4
+ export const helloWorkflow = (): string => {
5
+ return 'Hello from workflow!';
6
+ };
@@ -0,0 +1,85 @@
1
+ /** @jsxImportSource react */
2
+ import { Composition, Folder } from "remotion";
3
+ import "./style.css";
4
+
5
+ /**
6
+ * Dynamically registers all compositions exported by files in compositions/.
7
+ * Each composition module should export:
8
+ * - component: React.FC (the composition component)
9
+ * - id: string (composition ID)
10
+ * - durationInFrames: number (default duration)
11
+ * - fps?: number (default 30)
12
+ * - width?: number (default 1920)
13
+ * - height?: number (default 1080)
14
+ * - defaultProps?: Record<string, unknown>
15
+ * - calculateMetadata?: CalculateMetadataFunction
16
+ *
17
+ * To add a new composition, create a file in compositions/ that exports
18
+ * these fields and import it in the registry below.
19
+ */
20
+
21
+ // ── Composition registry ─────────────────────────────────────────────────────
22
+ // Import composition modules here. Each entry becomes a Remotion composition.
23
+ // Example:
24
+ // import * as MyVideo from "./compositions/my_video";
25
+ // const compositions = [MyVideo];
26
+
27
+ export type CompositionEntry = {
28
+ id: string;
29
+ component: React.FC<Record<string, unknown>>;
30
+ durationInFrames: number;
31
+ fps?: number;
32
+ width?: number;
33
+ height?: number;
34
+ defaultProps?: Record<string, unknown>;
35
+ calculateMetadata?: unknown;
36
+ folder?: string;
37
+ };
38
+
39
+ export const compositions: CompositionEntry[] = [];
40
+
41
+ // ── Root ─────────────────────────────────────────────────────────────────────
42
+
43
+ export const RemotionRoot: React.FC = () => {
44
+ // Group compositions by folder
45
+ const folders = new Map<string | undefined, CompositionEntry[]>();
46
+ for (const entry of compositions) {
47
+ const key = entry.folder;
48
+ if (!folders.has(key)) folders.set(key, []);
49
+ folders.get(key)!.push(entry);
50
+ }
51
+
52
+ const renderComposition = (entry: CompositionEntry) => (
53
+ <Composition
54
+ key={entry.id}
55
+ id={entry.id}
56
+ component={entry.component}
57
+ durationInFrames={entry.durationInFrames}
58
+ fps={entry.fps ?? 30}
59
+ width={entry.width ?? 1920}
60
+ height={entry.height ?? 1080}
61
+ defaultProps={entry.defaultProps ?? {}}
62
+ calculateMetadata={
63
+ entry.calculateMetadata as
64
+ | React.ComponentProps<typeof Composition>["calculateMetadata"]
65
+ | undefined
66
+ }
67
+ />
68
+ );
69
+
70
+ return (
71
+ <>
72
+ {/* Ungrouped compositions */}
73
+ {(folders.get(undefined) ?? []).map(renderComposition)}
74
+
75
+ {/* Grouped into folders */}
76
+ {[...folders.entries()]
77
+ .filter(([key]) => key !== undefined)
78
+ .map(([folderName, entries]) => (
79
+ <Folder key={folderName} name={folderName!}>
80
+ {entries.map(renderComposition)}
81
+ </Folder>
82
+ ))}
83
+ </>
84
+ );
85
+ };
@@ -0,0 +1,231 @@
1
+ /** @jsxImportSource react */
2
+ import {
3
+ AbsoluteFill,
4
+ useCurrentFrame,
5
+ useVideoConfig,
6
+ interpolate,
7
+ spring,
8
+ } from "remotion";
9
+ import { colors, fonts } from "./theme";
10
+
11
+ type EmailSceneProps = {
12
+ stepNumber?: number;
13
+ partLabel?: string;
14
+ title?: string;
15
+ senderName?: string;
16
+ subject?: string;
17
+ emailLines?: string[];
18
+ };
19
+
20
+ export const EmailScene: React.FC<EmailSceneProps> = ({
21
+ stepNumber = 1,
22
+ partLabel = "Step",
23
+ title = "Email Received",
24
+ senderName = "System",
25
+ subject = "Welcome",
26
+ emailLines = [
27
+ "Welcome!",
28
+ "",
29
+ "Your account has been created.",
30
+ "",
31
+ "Getting Started:",
32
+ "1. Log in using your credentials",
33
+ "2. Update your profile",
34
+ "3. Explore the dashboard",
35
+ ],
36
+ }) => {
37
+ const frame = useCurrentFrame();
38
+ const { fps } = useVideoConfig();
39
+
40
+ const envelopeSpring = spring({
41
+ frame,
42
+ fps,
43
+ config: { damping: 15, stiffness: 200 },
44
+ });
45
+
46
+ const envelopeScale = interpolate(envelopeSpring, [0, 1], [0.5, 1]);
47
+ const envelopeOpacity = interpolate(envelopeSpring, [0, 1], [0, 1]);
48
+
49
+ const emailBodyOpacity = interpolate(
50
+ frame,
51
+ [0.8 * fps, 1.2 * fps],
52
+ [0, 1],
53
+ {
54
+ extrapolateLeft: "clamp",
55
+ extrapolateRight: "clamp",
56
+ },
57
+ );
58
+
59
+ const emailBodyY = interpolate(frame, [0.8 * fps, 1.2 * fps], [20, 0], {
60
+ extrapolateLeft: "clamp",
61
+ extrapolateRight: "clamp",
62
+ });
63
+
64
+ const visibleLines = Math.floor(
65
+ interpolate(frame, [1 * fps, 4.5 * fps], [0, emailLines.length], {
66
+ extrapolateLeft: "clamp",
67
+ extrapolateRight: "clamp",
68
+ }),
69
+ );
70
+
71
+ return (
72
+ <AbsoluteFill
73
+ style={{
74
+ backgroundColor: colors.baseDark,
75
+ fontFamily: fonts.sans,
76
+ display: "flex",
77
+ alignItems: "center",
78
+ justifyContent: "center",
79
+ }}
80
+ >
81
+ {/* Step badge */}
82
+ <div
83
+ style={{
84
+ position: "absolute",
85
+ top: 40,
86
+ left: 80,
87
+ display: "flex",
88
+ alignItems: "center",
89
+ gap: 16,
90
+ opacity: envelopeOpacity,
91
+ }}
92
+ >
93
+ <div
94
+ style={{
95
+ width: 48,
96
+ height: 48,
97
+ borderRadius: "50%",
98
+ backgroundColor: colors.accentSecondary,
99
+ display: "flex",
100
+ alignItems: "center",
101
+ justifyContent: "center",
102
+ color: "white",
103
+ fontSize: 24,
104
+ fontWeight: 700,
105
+ }}
106
+ >
107
+ {stepNumber}
108
+ </div>
109
+ <span
110
+ style={{
111
+ color: colors.accentSecondary,
112
+ fontSize: 18,
113
+ fontWeight: 600,
114
+ textTransform: "uppercase",
115
+ letterSpacing: 2,
116
+ }}
117
+ >
118
+ {partLabel}
119
+ </span>
120
+ </div>
121
+
122
+ {/* Title */}
123
+ <div
124
+ style={{
125
+ position: "absolute",
126
+ top: 110,
127
+ left: 80,
128
+ opacity: envelopeOpacity,
129
+ }}
130
+ >
131
+ <h1
132
+ style={{
133
+ color: "white",
134
+ fontSize: 44,
135
+ fontWeight: 700,
136
+ margin: 0,
137
+ }}
138
+ >
139
+ {title}
140
+ </h1>
141
+ </div>
142
+
143
+ {/* Email card */}
144
+ <div
145
+ style={{
146
+ position: "absolute",
147
+ top: 190,
148
+ left: 80,
149
+ right: 80,
150
+ bottom: 60,
151
+ opacity: emailBodyOpacity,
152
+ transform: `translateY(${emailBodyY}px)`,
153
+ }}
154
+ >
155
+ {/* Email header */}
156
+ <div
157
+ style={{
158
+ backgroundColor: "#2a2a4a",
159
+ borderRadius: "12px 12px 0 0",
160
+ padding: "16px 24px",
161
+ display: "flex",
162
+ alignItems: "center",
163
+ gap: 12,
164
+ }}
165
+ >
166
+ <div
167
+ style={{
168
+ width: 40,
169
+ height: 40,
170
+ borderRadius: "50%",
171
+ backgroundColor: colors.accentPrimary,
172
+ display: "flex",
173
+ alignItems: "center",
174
+ justifyContent: "center",
175
+ color: "white",
176
+ fontSize: 18,
177
+ fontWeight: 700,
178
+ }}
179
+ >
180
+ {senderName.charAt(0).toUpperCase()}
181
+ </div>
182
+ <div>
183
+ <div style={{ color: "white", fontSize: 16, fontWeight: 600 }}>
184
+ {senderName}
185
+ </div>
186
+ <div style={{ color: "rgba(255,255,255,0.5)", fontSize: 13 }}>
187
+ Subject: {subject}
188
+ </div>
189
+ </div>
190
+ </div>
191
+
192
+ {/* Email body */}
193
+ <div
194
+ style={{
195
+ backgroundColor: "#22223a",
196
+ borderRadius: "0 0 12px 12px",
197
+ padding: "24px 32px",
198
+ border: "1px solid rgba(255,255,255,0.08)",
199
+ borderTop: "none",
200
+ }}
201
+ >
202
+ {emailLines.slice(0, visibleLines).map((line, i) => (
203
+ <div
204
+ key={i}
205
+ style={{
206
+ color:
207
+ line.includes(":") && !line.startsWith(" ")
208
+ ? colors.accentSecondary
209
+ : line.startsWith("Welcome") ||
210
+ line.startsWith("Getting Started")
211
+ ? "white"
212
+ : "rgba(255,255,255,0.7)",
213
+ fontSize: line === "" ? 8 : 19,
214
+ fontWeight:
215
+ line.startsWith("Welcome") ||
216
+ line.startsWith("Getting Started")
217
+ ? 700
218
+ : 400,
219
+ fontFamily: line.includes("://") ? fonts.mono : fonts.sans,
220
+ lineHeight: line === "" ? 1 : 1.8,
221
+ minHeight: line === "" ? 12 : "auto",
222
+ }}
223
+ >
224
+ {line || "\u00A0"}
225
+ </div>
226
+ ))}
227
+ </div>
228
+ </div>
229
+ </AbsoluteFill>
230
+ );
231
+ };