@assistkick/create 1.8.0 → 1.10.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 (26) hide show
  1. package/package.json +1 -1
  2. package/templates/assistkick-product-system/packages/backend/src/routes/git.ts +1 -1
  3. package/templates/assistkick-product-system/packages/backend/src/routes/kanban.ts +18 -2
  4. package/templates/assistkick-product-system/packages/backend/src/routes/workflows.ts +9 -5
  5. package/templates/assistkick-product-system/packages/backend/src/server.ts +1 -22
  6. package/templates/assistkick-product-system/packages/backend/src/services/init.ts +16 -0
  7. package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.ts +20 -6
  8. package/templates/assistkick-product-system/packages/backend/src/services/workflow_service.ts +30 -7
  9. package/templates/assistkick-product-system/packages/frontend/src/api/client.ts +2 -2
  10. package/templates/assistkick-product-system/packages/frontend/src/components/IterationCommentModal.tsx +80 -0
  11. package/templates/assistkick-product-system/packages/frontend/src/components/KanbanView.tsx +67 -2
  12. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/GenerateTTSNode.tsx +52 -0
  13. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/RebuildBundleNode.tsx +20 -0
  14. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/RenderVideoNode.tsx +72 -0
  15. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowCanvas.tsx +6 -0
  16. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowMonitorModal.tsx +9 -0
  17. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/monitor_nodes.tsx +39 -1
  18. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.ts +30 -1
  19. package/templates/assistkick-product-system/packages/shared/db/migrations/0014_nifty_punisher.sql +15 -0
  20. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0014_snapshot.json +1545 -0
  21. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/_journal.json +7 -0
  22. package/templates/assistkick-product-system/packages/shared/db/schema.ts +1 -0
  23. package/templates/assistkick-product-system/packages/shared/lib/claude-service.ts +1 -1
  24. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.test.ts +247 -1
  25. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.ts +158 -2
  26. package/templates/assistkick-product-system/tests/video_render_service.test.ts +6 -4
@@ -0,0 +1,72 @@
1
+ import React, { useCallback } from 'react';
2
+ import { Handle, Position, useReactFlow } from '@xyflow/react';
3
+ import type { NodeProps } from '@xyflow/react';
4
+ import { Film } from 'lucide-react';
5
+
6
+ export function RenderVideoNode({ id, data, selected }: NodeProps) {
7
+ const { updateNodeData } = useReactFlow();
8
+ const compositionId = (data.compositionId as string) || '';
9
+ const resolution = (data.resolution as string) || '1920x1080';
10
+ const fileOutputPrefix = (data.fileOutputPrefix as string) || '';
11
+
12
+ const handleCompositionChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
13
+ updateNodeData(id, { compositionId: e.target.value });
14
+ }, [id, updateNodeData]);
15
+
16
+ const handleResolutionChange = useCallback((e: React.ChangeEvent<HTMLSelectElement>) => {
17
+ updateNodeData(id, { resolution: e.target.value });
18
+ }, [id, updateNodeData]);
19
+
20
+ const handlePrefixChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
21
+ updateNodeData(id, { fileOutputPrefix: e.target.value });
22
+ }, [id, updateNodeData]);
23
+
24
+ return (
25
+ <div className={`px-4 py-3 rounded-lg border bg-surface-raised text-content min-w-[200px] ${
26
+ selected ? 'border-accent shadow-[0_0_0_1px_var(--accent)]' : 'border-edge'
27
+ }`}>
28
+ <Handle type="target" position={Position.Top} className="!w-3 !h-3 !bg-accent !border-2 !border-surface" />
29
+ <div className="flex items-center gap-2 mb-2">
30
+ <Film size={14} className="text-rose-400" />
31
+ <span className="text-[12px] font-semibold">Render Video</span>
32
+ </div>
33
+ <div className="space-y-1.5">
34
+ <div>
35
+ <label className="text-[10px] text-content-muted uppercase tracking-wider">Composition</label>
36
+ <input
37
+ type="text"
38
+ value={compositionId}
39
+ onChange={handleCompositionChange}
40
+ className="w-full px-2 py-1 rounded border border-edge bg-surface text-[11px] text-content"
41
+ placeholder="auto from context"
42
+ />
43
+ </div>
44
+ <div>
45
+ <label className="text-[10px] text-content-muted uppercase tracking-wider">Resolution</label>
46
+ <select
47
+ value={resolution}
48
+ onChange={handleResolutionChange}
49
+ className="w-full px-2 py-1 rounded border border-edge bg-surface text-[11px] text-content appearance-none"
50
+ >
51
+ <option value="1920x1080">1920x1080 (16:9)</option>
52
+ <option value="1080x1920">1080x1920 (9:16)</option>
53
+ <option value="1280x720">1280x720 (16:9)</option>
54
+ <option value="720x1280">720x1280 (9:16)</option>
55
+ <option value="1080x1080">1080x1080 (1:1)</option>
56
+ </select>
57
+ </div>
58
+ <div>
59
+ <label className="text-[10px] text-content-muted uppercase tracking-wider">Output prefix</label>
60
+ <input
61
+ type="text"
62
+ value={fileOutputPrefix}
63
+ onChange={handlePrefixChange}
64
+ className="w-full px-2 py-1 rounded border border-edge bg-surface text-[11px] text-content"
65
+ placeholder="auto"
66
+ />
67
+ </div>
68
+ </div>
69
+ <Handle type="source" position={Position.Bottom} className="!w-3 !h-3 !bg-accent !border-2 !border-surface" />
70
+ </div>
71
+ );
72
+ }
@@ -27,6 +27,9 @@ import { CheckCardPositionNode } from './CheckCardPositionNode';
27
27
  import { CheckCycleCountNode } from './CheckCycleCountNode';
28
28
  import { SetCardMetadataNode } from './SetCardMetadataNode';
29
29
  import { GroupNode } from './GroupNode';
30
+ import { RebuildBundleNode } from './RebuildBundleNode';
31
+ import { GenerateTTSNode } from './GenerateTTSNode';
32
+ import { RenderVideoNode } from './RenderVideoNode';
30
33
  import { NODE_DEFAULTS } from './workflow_types';
31
34
  import type { WorkflowNodeType } from './workflow_types';
32
35
  import { autoLayoutNodes } from './autoLayout';
@@ -41,6 +44,9 @@ const NODE_TYPE_MAP: NodeTypes = {
41
44
  checkCycleCount: CheckCycleCountNode,
42
45
  setCardMetadata: SetCardMetadataNode,
43
46
  group: GroupNode,
47
+ rebuildBundle: RebuildBundleNode,
48
+ generateTTS: GenerateTTSNode,
49
+ renderVideo: RenderVideoNode,
44
50
  };
45
51
 
46
52
  interface ContextMenuState {
@@ -25,6 +25,9 @@ import {
25
25
  MonitorCheckCycleCountNode,
26
26
  MonitorSetCardMetadataNode,
27
27
  MonitorGroupNode,
28
+ MonitorRebuildBundleNode,
29
+ MonitorGenerateTTSNode,
30
+ MonitorRenderVideoNode,
28
31
  } from './monitor_nodes';
29
32
  import { colorizeEdges } from './edgeColors';
30
33
 
@@ -113,6 +116,9 @@ const MONITOR_NODE_TYPES: NodeTypes = {
113
116
  checkCycleCount: MonitorCheckCycleCountNode,
114
117
  setCardMetadata: MonitorSetCardMetadataNode,
115
118
  group: MonitorGroupNode,
119
+ rebuildBundle: MonitorRebuildBundleNode,
120
+ generateTTS: MonitorGenerateTTSNode,
121
+ renderVideo: MonitorRenderVideoNode,
116
122
  };
117
123
 
118
124
  function getNodeTypeLabel(type: string): string {
@@ -125,6 +131,9 @@ function getNodeTypeLabel(type: string): string {
125
131
  case 'checkCycleCount': return 'Check Cycles';
126
132
  case 'setCardMetadata': return 'Set Metadata';
127
133
  case 'group': return 'Group';
134
+ case 'rebuildBundle': return 'Rebuild Bundle';
135
+ case 'generateTTS': return 'Generate TTS';
136
+ case 'renderVideo': return 'Render Video';
128
137
  default: return type;
129
138
  }
130
139
  }
@@ -9,7 +9,7 @@ import { Handle, Position } from '@xyflow/react';
9
9
  import type { NodeProps } from '@xyflow/react';
10
10
  import {
11
11
  Play, Square, ArrowLeftRight, Bot, MapPin, RefreshCw, Tag, Layers,
12
- CircleDot, CheckCircle2, AlertTriangle, Clock,
12
+ CircleDot, CheckCircle2, AlertTriangle, Clock, Package, Volume2, Film,
13
13
  } from 'lucide-react';
14
14
 
15
15
  /* ── Execution status styling ── */
@@ -206,3 +206,41 @@ export function MonitorGroupNode({ id, data }: NodeProps) {
206
206
  />
207
207
  );
208
208
  }
209
+
210
+ export function MonitorRebuildBundleNode({ id, data }: NodeProps) {
211
+ return (
212
+ <MonitorNodeWrapper
213
+ id={id}
214
+ data={data as Record<string, unknown>}
215
+ icon={<Package size={14} className="text-orange-400" />}
216
+ label="Rebuild Bundle"
217
+ detail="Remotion webpack build"
218
+ />
219
+ );
220
+ }
221
+
222
+ export function MonitorGenerateTTSNode({ id, data }: NodeProps) {
223
+ const d = data as Record<string, unknown>;
224
+ return (
225
+ <MonitorNodeWrapper
226
+ id={id}
227
+ data={d}
228
+ icon={<Volume2 size={14} className="text-emerald-400" />}
229
+ label="Generate TTS"
230
+ detail={(d.scriptPath as string) || 'script.md'}
231
+ />
232
+ );
233
+ }
234
+
235
+ export function MonitorRenderVideoNode({ id, data }: NodeProps) {
236
+ const d = data as Record<string, unknown>;
237
+ return (
238
+ <MonitorNodeWrapper
239
+ id={id}
240
+ data={d}
241
+ icon={<Film size={14} className="text-rose-400" />}
242
+ label="Render Video"
243
+ detail={`${(d.compositionId as string) || 'auto'} @ ${(d.resolution as string) || '1920x1080'}`}
244
+ />
245
+ );
246
+ }
@@ -9,6 +9,9 @@ export const WORKFLOW_NODE_TYPES = [
9
9
  'setCardMetadata',
10
10
  'end',
11
11
  'group',
12
+ 'rebuildBundle',
13
+ 'generateTTS',
14
+ 'renderVideo',
12
15
  ] as const;
13
16
 
14
17
  export type WorkflowNodeType = typeof WORKFLOW_NODE_TYPES[number];
@@ -69,6 +72,23 @@ export interface GroupNodeData {
69
72
  outputHandles: string[];
70
73
  }
71
74
 
75
+ export interface RebuildBundleData {
76
+ // no config needed — triggers Remotion webpack bundler
77
+ }
78
+
79
+ export interface GenerateTTSData {
80
+ scriptPath: string;
81
+ force: boolean;
82
+ voiceId: string;
83
+ }
84
+
85
+ export interface RenderVideoData {
86
+ compositionId: string;
87
+ resolution: string;
88
+ aspectRatio: string;
89
+ fileOutputPrefix: string;
90
+ }
91
+
72
92
  export type WorkflowNodeData =
73
93
  | ({ type: 'start' } & StartNodeData)
74
94
  | ({ type: 'transitionCard' } & TransitionCardData)
@@ -77,7 +97,10 @@ export type WorkflowNodeData =
77
97
  | ({ type: 'checkCycleCount' } & CheckCycleCountData)
78
98
  | ({ type: 'setCardMetadata' } & SetCardMetadataData)
79
99
  | ({ type: 'end' } & EndNodeData)
80
- | ({ type: 'group' } & GroupNodeData);
100
+ | ({ type: 'group' } & GroupNodeData)
101
+ | ({ type: 'rebuildBundle' } & RebuildBundleData)
102
+ | ({ type: 'generateTTS' } & GenerateTTSData)
103
+ | ({ type: 'renderVideo' } & RenderVideoData);
81
104
 
82
105
  export interface NodePaletteItem {
83
106
  type: WorkflowNodeType;
@@ -93,6 +116,9 @@ export const NODE_PALETTE_ITEMS: NodePaletteItem[] = [
93
116
  { type: 'checkCycleCount', label: 'Check Cycle Count', description: 'Branch by iteration count' },
94
117
  { type: 'setCardMetadata', label: 'Set Card Metadata', description: 'Set key/value on card' },
95
118
  { type: 'end', label: 'End', description: 'Workflow exit point' },
119
+ { type: 'rebuildBundle', label: 'Rebuild Bundle', description: 'Build Remotion webpack bundle' },
120
+ { type: 'generateTTS', label: 'Generate TTS', description: 'Generate text-to-speech audio' },
121
+ { type: 'renderVideo', label: 'Render Video', description: 'Render composition to MP4' },
96
122
  ];
97
123
 
98
124
  export const NODE_DEFAULTS: Record<string, () => Record<string, unknown>> = {
@@ -104,4 +130,7 @@ export const NODE_DEFAULTS: Record<string, () => Record<string, unknown>> = {
104
130
  setCardMetadata: () => ({ key: '', value: '' }),
105
131
  end: () => ({ statusType: 'success' }),
106
132
  group: () => ({ groupId: '', groupName: '', internalNodes: [], internalEdges: [], inputHandles: [], outputHandles: [] }),
133
+ rebuildBundle: () => ({}),
134
+ generateTTS: () => ({ scriptPath: '', force: false, voiceId: '' }),
135
+ renderVideo: () => ({ compositionId: '', resolution: '1920x1080', aspectRatio: '', fileOutputPrefix: '' }),
107
136
  };
@@ -0,0 +1,15 @@
1
+ ALTER TABLE `workflows` ADD `trigger_column` text;--> statement-breakpoint
2
+ -- Seed video agents with fixed IDs (syncDefaults will update their templates on startup)
3
+ INSERT OR IGNORE INTO agents (id, name, prompt_template, project_id, is_default, created_at, updated_at) VALUES
4
+ ('a1b2c3d4-e5f6-7890-abcd-ef1234567890', 'Default Video Script Writer', 'Placeholder — synced on startup from SKILL.md', NULL, 1, '2026-03-11T00:00:00.000Z', '2026-03-11T00:00:00.000Z'),
5
+ ('b2c3d4e5-f6a7-8901-bcde-f12345678901', 'Default Video Composition Agent', 'Placeholder — synced on startup from SKILL.md', NULL, 1, '2026-03-11T00:00:00.000Z', '2026-03-11T00:00:00.000Z');--> statement-breakpoint
6
+ -- Seed three column-gated video workflows
7
+ -- 1. Script Generation (TODO column) — scriptWriter agent produces script markdown
8
+ INSERT INTO workflows (id, name, description, project_id, feature_type, trigger_column, is_default, graph_data, created_at, updated_at) VALUES
9
+ ('wf-video-script-gen', 'Video Script Generation', 'Generate video script from feature description. Available when video feature is in TODO.', NULL, 'video', 'todo', 0, '{"nodes":[{"id":"start","type":"start","position":{"x":50,"y":250},"data":{"label":"Start"}},{"id":"agent_script","type":"runAgent","position":{"x":300,"y":250},"data":{"label":"Run Script Writer","agentId":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","agentName":"Default Video Script Writer"}},{"id":"transition_review","type":"transitionCard","position":{"x":600,"y":250},"data":{"label":"Move to Review","fromColumn":"todo","toColumn":"in_review"}},{"id":"end_success","type":"end","position":{"x":900,"y":250},"data":{"label":"End (Success)","outcome":"success"}}],"edges":[{"id":"e1","source":"start","target":"agent_script"},{"id":"e2","source":"agent_script","target":"transition_review"},{"id":"e3","source":"transition_review","target":"end_success"}]}', '2026-03-11T00:00:00.000Z', '2026-03-11T00:00:00.000Z');--> statement-breakpoint
10
+ -- 2. Generate Preview (Review column) — compositionBuilder agent + bundle + TTS
11
+ INSERT INTO workflows (id, name, description, project_id, feature_type, trigger_column, is_default, graph_data, created_at, updated_at) VALUES
12
+ ('wf-video-gen-preview', 'Video Generate Preview', 'Build composition, rebuild bundle, and generate TTS audio. Available when video feature is in Review.', NULL, 'video', 'in_review', 0, '{"nodes":[{"id":"start","type":"start","position":{"x":50,"y":250},"data":{"label":"Start"}},{"id":"agent_comp","type":"runAgent","position":{"x":300,"y":250},"data":{"label":"Run Composition Builder","agentId":"b2c3d4e5-f6a7-8901-bcde-f12345678901","agentName":"Default Video Composition Agent"}},{"id":"rebuild","type":"rebuildBundle","position":{"x":600,"y":250},"data":{"label":"Rebuild Bundle"}},{"id":"tts","type":"generateTTS","position":{"x":900,"y":250},"data":{"label":"Generate TTS","scriptPath":"script.md","force":false,"voiceId":""}},{"id":"transition_qa","type":"transitionCard","position":{"x":1200,"y":250},"data":{"label":"Move to QA","fromColumn":"in_review","toColumn":"qa"}},{"id":"end_success","type":"end","position":{"x":1500,"y":250},"data":{"label":"End (Success)","outcome":"success"}}],"edges":[{"id":"e1","source":"start","target":"agent_comp"},{"id":"e2","source":"agent_comp","target":"rebuild"},{"id":"e3","source":"rebuild","target":"tts"},{"id":"e4","source":"tts","target":"transition_qa"},{"id":"e5","source":"transition_qa","target":"end_success"}]}', '2026-03-11T00:00:00.000Z', '2026-03-11T00:00:00.000Z');--> statement-breakpoint
13
+ -- 3. Rendering Pipeline (QA column) — renderVideo with configurable settings
14
+ INSERT INTO workflows (id, name, description, project_id, feature_type, trigger_column, is_default, graph_data, created_at, updated_at) VALUES
15
+ ('wf-video-render', 'Video Rendering Pipeline', 'Render video with configurable resolution and output settings. Available when video feature is in QA.', NULL, 'video', 'qa', 0, '{"nodes":[{"id":"start","type":"start","position":{"x":50,"y":250},"data":{"label":"Start"}},{"id":"render","type":"renderVideo","position":{"x":300,"y":250},"data":{"label":"Render Video","compositionId":"","resolution":"1920x1080","aspectRatio":"","fileOutputPrefix":""}},{"id":"transition_done","type":"transitionCard","position":{"x":600,"y":250},"data":{"label":"Move to Done","fromColumn":"qa","toColumn":"done"}},{"id":"end_success","type":"end","position":{"x":900,"y":250},"data":{"label":"End (Success)","outcome":"success"}}],"edges":[{"id":"e1","source":"start","target":"render"},{"id":"e2","source":"render","target":"transition_done"},{"id":"e3","source":"transition_done","target":"end_success"}]}', '2026-03-11T00:00:00.000Z', '2026-03-11T00:00:00.000Z');