@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
@@ -0,0 +1,489 @@
1
+ import { describe, it, mock, beforeEach } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { writeFileSync, mkdirSync, rmSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { tmpdir } from 'node:os';
6
+ import { AgentService } from './agent_service.ts';
7
+
8
+ const MOCK_AGENT = {
9
+ id: 'agent-1',
10
+ name: 'Test Agent',
11
+ promptTemplate: 'You are a test agent.',
12
+ projectId: null,
13
+ isDefault: 0,
14
+ createdAt: '2024-01-01T00:00:00Z',
15
+ updatedAt: '2024-01-01T00:00:00Z',
16
+ };
17
+
18
+ const MOCK_DEFAULT_AGENT = {
19
+ id: 'default-dev',
20
+ name: 'Default Developer',
21
+ promptTemplate: 'Original prompt template',
22
+ projectId: null,
23
+ isDefault: 1,
24
+ createdAt: '2024-01-01T00:00:00Z',
25
+ updatedAt: '2024-01-01T00:00:00Z',
26
+ };
27
+
28
+ const MOCK_VIDEO_SCRIPT_AGENT = {
29
+ id: 'default-vsw',
30
+ name: 'Default Video Script Writer',
31
+ promptTemplate: 'Original video script prompt',
32
+ projectId: null,
33
+ isDefault: 1,
34
+ createdAt: '2024-01-01T00:00:00Z',
35
+ updatedAt: '2024-01-01T00:00:00Z',
36
+ };
37
+
38
+ const MOCK_VIDEO_COMPOSITION_AGENT = {
39
+ id: 'default-vca',
40
+ name: 'Default Video Composition Agent',
41
+ promptTemplate: 'Original video composition prompt',
42
+ projectId: null,
43
+ isDefault: 1,
44
+ createdAt: '2024-01-01T00:00:00Z',
45
+ updatedAt: '2024-01-01T00:00:00Z',
46
+ };
47
+
48
+ const createMockDb = () => {
49
+ let selectResults: any[][] = [];
50
+ let selectCallIndex = 0;
51
+
52
+ const getNextResult = () => {
53
+ const result = selectResults[selectCallIndex] || [];
54
+ selectCallIndex++;
55
+ return result;
56
+ };
57
+
58
+ const db = {
59
+ select: mock.fn(() => {
60
+ const selectObj = {
61
+ from: mock.fn(() => {
62
+ // Make from() return a thenable (for direct await without .where())
63
+ // that also has .where() and .leftJoin() for chaining
64
+ const result = getNextResult();
65
+ const fromObj = Object.assign(Promise.resolve(result), {
66
+ where: mock.fn(() => {
67
+ return result;
68
+ }),
69
+ leftJoin: mock.fn(() => ({
70
+ where: mock.fn(() => {
71
+ return getNextResult();
72
+ }),
73
+ })),
74
+ });
75
+ return fromObj;
76
+ }),
77
+ };
78
+ return selectObj;
79
+ }),
80
+ insert: mock.fn(() => ({
81
+ values: mock.fn(() => Promise.resolve()),
82
+ })),
83
+ update: mock.fn(() => ({
84
+ set: mock.fn(() => ({
85
+ where: mock.fn(() => Promise.resolve()),
86
+ })),
87
+ })),
88
+ delete: mock.fn(() => ({
89
+ where: mock.fn(() => Promise.resolve()),
90
+ })),
91
+ _setSelectResults: (results: any[][]) => {
92
+ selectResults = results;
93
+ selectCallIndex = 0;
94
+ },
95
+ };
96
+
97
+ return db;
98
+ };
99
+
100
+ const createService = (db: any) => {
101
+ return new AgentService({
102
+ getDb: () => db,
103
+ log: mock.fn(),
104
+ skillPaths: {
105
+ developer: '/fake/developer/SKILL.md',
106
+ reviewer: '/fake/reviewer/SKILL.md',
107
+ debugger: '/fake/debugger/SKILL.md',
108
+ },
109
+ });
110
+ };
111
+
112
+ /** Create a service with a real skill file for the video script writer. */
113
+ const createServiceWithVideoSkill = (db: any, skillDir: string) => {
114
+ return new AgentService({
115
+ getDb: () => db,
116
+ log: mock.fn(),
117
+ skillPaths: {
118
+ developer: '/fake/developer/SKILL.md',
119
+ reviewer: '/fake/reviewer/SKILL.md',
120
+ debugger: '/fake/debugger/SKILL.md',
121
+ videoScriptWriter: join(skillDir, 'SKILL.md'),
122
+ },
123
+ });
124
+ };
125
+
126
+ /** Create a service with a real skill file for the video composition agent. */
127
+ const createServiceWithCompositionSkill = (db: any, skillDir: string) => {
128
+ return new AgentService({
129
+ getDb: () => db,
130
+ log: mock.fn(),
131
+ skillPaths: {
132
+ developer: '/fake/developer/SKILL.md',
133
+ reviewer: '/fake/reviewer/SKILL.md',
134
+ debugger: '/fake/debugger/SKILL.md',
135
+ videoCompositionAgent: join(skillDir, 'SKILL.md'),
136
+ },
137
+ });
138
+ };
139
+
140
+ describe('AgentService', () => {
141
+ let db: ReturnType<typeof createMockDb>;
142
+ let service: AgentService;
143
+
144
+ beforeEach(() => {
145
+ db = createMockDb();
146
+ service = createService(db);
147
+ });
148
+
149
+ describe('listAgents', () => {
150
+ it('returns global agents when scope is global', async () => {
151
+ db._setSelectResults([[MOCK_AGENT, MOCK_DEFAULT_AGENT]]);
152
+
153
+ const result = await service.listAgents('global');
154
+
155
+ assert.equal(result.length, 2);
156
+ assert.equal(db.select.mock.calls.length, 1);
157
+ });
158
+
159
+ it('returns project-scoped agents', async () => {
160
+ const projectAgent = { ...MOCK_AGENT, projectId: 'proj_001' };
161
+ db._setSelectResults([[projectAgent]]);
162
+
163
+ const result = await service.listAgents('project', 'proj_001');
164
+
165
+ assert.equal(result.length, 1);
166
+ assert.equal(result[0].projectId, 'proj_001');
167
+ });
168
+
169
+ it('throws when project scope without projectId', async () => {
170
+ await assert.rejects(
171
+ () => service.listAgents('project'),
172
+ { message: 'projectId is required for project scope' },
173
+ );
174
+ });
175
+ });
176
+
177
+ describe('getById', () => {
178
+ it('returns agent when found', async () => {
179
+ db._setSelectResults([[MOCK_AGENT]]);
180
+
181
+ const result = await service.getById('agent-1');
182
+
183
+ assert.ok(result);
184
+ assert.equal(result.name, 'Test Agent');
185
+ });
186
+
187
+ it('returns null when not found', async () => {
188
+ db._setSelectResults([[]]);
189
+
190
+ const result = await service.getById('nonexistent');
191
+
192
+ assert.equal(result, null);
193
+ });
194
+ });
195
+
196
+ describe('create', () => {
197
+ it('creates an agent with correct fields', async () => {
198
+ const result = await service.create({
199
+ name: 'New Agent',
200
+ promptTemplate: 'You are a new agent.',
201
+ });
202
+
203
+ assert.ok(result.id);
204
+ assert.equal(result.name, 'New Agent');
205
+ assert.equal(result.promptTemplate, 'You are a new agent.');
206
+ assert.equal(result.projectId, null);
207
+ assert.equal(result.isDefault, 0);
208
+ assert.ok(result.createdAt);
209
+ assert.equal(db.insert.mock.calls.length, 1);
210
+ });
211
+
212
+ it('creates a project-scoped agent', async () => {
213
+ const result = await service.create({
214
+ name: 'Project Agent',
215
+ promptTemplate: 'Prompt',
216
+ projectId: 'proj_001',
217
+ });
218
+
219
+ assert.equal(result.projectId, 'proj_001');
220
+ });
221
+ });
222
+
223
+ describe('update', () => {
224
+ it('updates agent name', async () => {
225
+ db._setSelectResults([[MOCK_AGENT]]);
226
+
227
+ const result = await service.update('agent-1', { name: 'Updated Name' });
228
+
229
+ assert.equal(result.name, 'Updated Name');
230
+ assert.equal(db.update.mock.calls.length, 1);
231
+ });
232
+
233
+ it('updates agent promptTemplate', async () => {
234
+ db._setSelectResults([[MOCK_AGENT]]);
235
+
236
+ const result = await service.update('agent-1', { promptTemplate: 'New prompt' });
237
+
238
+ assert.equal(result.promptTemplate, 'New prompt');
239
+ });
240
+
241
+ it('allows editing default agents', async () => {
242
+ db._setSelectResults([[MOCK_DEFAULT_AGENT]]);
243
+
244
+ const result = await service.update('default-dev', { name: 'Custom Developer' });
245
+
246
+ assert.equal(result.name, 'Custom Developer');
247
+ });
248
+
249
+ it('throws when agent not found', async () => {
250
+ db._setSelectResults([[]]);
251
+
252
+ await assert.rejects(
253
+ () => service.update('nonexistent', { name: 'X' }),
254
+ { message: 'Agent not found' },
255
+ );
256
+ });
257
+ });
258
+
259
+ describe('delete', () => {
260
+ it('deletes a non-default, unreferenced agent', async () => {
261
+ // First call: getById, second call: workflows list (empty)
262
+ db._setSelectResults([[MOCK_AGENT], []]);
263
+
264
+ await service.delete('agent-1');
265
+
266
+ assert.equal(db.delete.mock.calls.length, 1);
267
+ });
268
+
269
+ it('throws when agent not found', async () => {
270
+ db._setSelectResults([[]]);
271
+
272
+ await assert.rejects(
273
+ () => service.delete('nonexistent'),
274
+ { message: 'Agent not found' },
275
+ );
276
+ });
277
+
278
+ it('throws when deleting a default agent', async () => {
279
+ db._setSelectResults([[MOCK_DEFAULT_AGENT]]);
280
+
281
+ await assert.rejects(
282
+ () => service.delete('default-dev'),
283
+ { message: 'Cannot delete a default agent' },
284
+ );
285
+ });
286
+
287
+ it('throws when agent is referenced in a workflow', async () => {
288
+ const workflow = { id: 'wf-1', graphData: '{"nodes":[{"data":{"agentId":"agent-1"}}]}' };
289
+ db._setSelectResults([[MOCK_AGENT], [workflow]]);
290
+
291
+ await assert.rejects(
292
+ () => service.delete('agent-1'),
293
+ { message: 'Cannot delete an agent that is referenced in a workflow' },
294
+ );
295
+ });
296
+ });
297
+
298
+ describe('resetToDefault', () => {
299
+ it('throws when agent not found', async () => {
300
+ db._setSelectResults([[]]);
301
+
302
+ await assert.rejects(
303
+ () => service.resetToDefault('nonexistent'),
304
+ { message: 'Agent not found' },
305
+ );
306
+ });
307
+
308
+ it('throws when agent is not a default agent', async () => {
309
+ db._setSelectResults([[MOCK_AGENT]]);
310
+
311
+ await assert.rejects(
312
+ () => service.resetToDefault('agent-1'),
313
+ { message: 'Only default agents can be reset' },
314
+ );
315
+ });
316
+
317
+ it('throws when stage cannot be inferred from name', async () => {
318
+ const unknownDefault = { ...MOCK_DEFAULT_AGENT, name: 'Unknown Agent' };
319
+ db._setSelectResults([[unknownDefault]]);
320
+
321
+ await assert.rejects(
322
+ () => service.resetToDefault('default-dev'),
323
+ { message: 'Cannot determine stage for this default agent' },
324
+ );
325
+ });
326
+ });
327
+
328
+ describe('video script writer support', () => {
329
+ let skillDir: string;
330
+
331
+ beforeEach(() => {
332
+ skillDir = join(tmpdir(), `agent-test-${Date.now()}`);
333
+ mkdirSync(skillDir, { recursive: true });
334
+ writeFileSync(join(skillDir, 'SKILL.md'), '# Video Script Writer\nTest skill content.');
335
+ });
336
+
337
+ it('syncDefaults creates missing video script writer agent', async () => {
338
+ const vsDb = createMockDb();
339
+ const vsService = createServiceWithVideoSkill(vsDb, skillDir);
340
+
341
+ // syncDefaults queries isDefault=1 agents — return empty (no existing defaults)
342
+ vsDb._setSelectResults([[]]);
343
+
344
+ await vsService.syncDefaults();
345
+
346
+ // Should have called insert for all 4 default agents whose skill path exists
347
+ // developer/reviewer/debugger will fail because /fake/ doesn't exist
348
+ // only videoScriptWriter should succeed
349
+ assert.equal(vsDb.insert.mock.calls.length, 1);
350
+
351
+ rmSync(skillDir, { recursive: true, force: true });
352
+ });
353
+
354
+ it('syncDefaults skips video script writer when it already exists', async () => {
355
+ const vsDb = createMockDb();
356
+ const vsService = createServiceWithVideoSkill(vsDb, skillDir);
357
+
358
+ // syncDefaults finds the video script writer already in DB
359
+ vsDb._setSelectResults([[MOCK_VIDEO_SCRIPT_AGENT]]);
360
+
361
+ await vsService.syncDefaults();
362
+
363
+ // Should not insert any new agents (video script writer exists, others fail due to /fake/ path)
364
+ assert.equal(vsDb.insert.mock.calls.length, 0);
365
+
366
+ rmSync(skillDir, { recursive: true, force: true });
367
+ });
368
+
369
+ it('resetToDefault works for video script writer agent', async () => {
370
+ const vsDb = createMockDb();
371
+ const vsService = createServiceWithVideoSkill(vsDb, skillDir);
372
+
373
+ vsDb._setSelectResults([[MOCK_VIDEO_SCRIPT_AGENT]]);
374
+
375
+ const result = await vsService.resetToDefault('default-vsw');
376
+
377
+ assert.ok(result.promptTemplate.includes('Video Script Writer'));
378
+ assert.ok(result.promptTemplate.includes('{{featureDescription}}'));
379
+ assert.ok(result.promptTemplate.includes('{{compositionName}}'));
380
+ assert.ok(result.promptTemplate.includes('{{worktreePath}}'));
381
+ assert.ok(result.promptTemplate.includes('{{iterationComments}}'));
382
+
383
+ rmSync(skillDir, { recursive: true, force: true });
384
+ });
385
+
386
+ it('template includes fenced directive format documentation', async () => {
387
+ const vsDb = createMockDb();
388
+ const vsService = createServiceWithVideoSkill(vsDb, skillDir);
389
+
390
+ vsDb._setSelectResults([[MOCK_VIDEO_SCRIPT_AGENT]]);
391
+
392
+ const result = await vsService.resetToDefault('default-vsw');
393
+
394
+ // The SKILL.md mentions fenced directives; template sections add task context
395
+ assert.ok(result.promptTemplate.includes('Data Isolation'));
396
+ assert.ok(result.promptTemplate.includes('Work Summary'));
397
+
398
+ rmSync(skillDir, { recursive: true, force: true });
399
+ });
400
+ });
401
+
402
+ describe('video composition agent support', () => {
403
+ let skillDir: string;
404
+
405
+ beforeEach(() => {
406
+ skillDir = join(tmpdir(), `agent-composition-test-${Date.now()}`);
407
+ mkdirSync(skillDir, { recursive: true });
408
+ writeFileSync(join(skillDir, 'SKILL.md'), '# Video Composition Agent\nTest composition skill content.');
409
+ });
410
+
411
+ it('syncDefaults creates missing video composition agent', async () => {
412
+ const vcDb = createMockDb();
413
+ const vcService = createServiceWithCompositionSkill(vcDb, skillDir);
414
+
415
+ // No existing default agents
416
+ vcDb._setSelectResults([[]]);
417
+
418
+ await vcService.syncDefaults();
419
+
420
+ // Only videoCompositionAgent should succeed (others have /fake/ paths)
421
+ assert.equal(vcDb.insert.mock.calls.length, 1);
422
+
423
+ rmSync(skillDir, { recursive: true, force: true });
424
+ });
425
+
426
+ it('syncDefaults skips video composition agent when it already exists', async () => {
427
+ const vcDb = createMockDb();
428
+ const vcService = createServiceWithCompositionSkill(vcDb, skillDir);
429
+
430
+ // Video composition agent already in DB
431
+ vcDb._setSelectResults([[MOCK_VIDEO_COMPOSITION_AGENT]]);
432
+
433
+ await vcService.syncDefaults();
434
+
435
+ // Should not insert any new agents
436
+ assert.equal(vcDb.insert.mock.calls.length, 0);
437
+
438
+ rmSync(skillDir, { recursive: true, force: true });
439
+ });
440
+
441
+ it('resetToDefault works for video composition agent', async () => {
442
+ const vcDb = createMockDb();
443
+ const vcService = createServiceWithCompositionSkill(vcDb, skillDir);
444
+
445
+ vcDb._setSelectResults([[MOCK_VIDEO_COMPOSITION_AGENT]]);
446
+
447
+ const result = await vcService.resetToDefault('default-vca');
448
+
449
+ assert.ok(result.promptTemplate.includes('Video Composition Agent'));
450
+ assert.ok(result.promptTemplate.includes('{{featureDescription}}'));
451
+ assert.ok(result.promptTemplate.includes('{{compositionName}}'));
452
+ assert.ok(result.promptTemplate.includes('{{worktreePath}}'));
453
+ assert.ok(result.promptTemplate.includes('{{iterationComments}}'));
454
+
455
+ rmSync(skillDir, { recursive: true, force: true });
456
+ });
457
+
458
+ it('template includes composition-specific sections', async () => {
459
+ const vcDb = createMockDb();
460
+ const vcService = createServiceWithCompositionSkill(vcDb, skillDir);
461
+
462
+ vcDb._setSelectResults([[MOCK_VIDEO_COMPOSITION_AGENT]]);
463
+
464
+ const result = await vcService.resetToDefault('default-vca');
465
+
466
+ assert.ok(result.promptTemplate.includes('Data Isolation'));
467
+ assert.ok(result.promptTemplate.includes('Component Reuse'));
468
+ assert.ok(result.promptTemplate.includes('Work Summary'));
469
+
470
+ rmSync(skillDir, { recursive: true, force: true });
471
+ });
472
+
473
+ it('inferStageFromName correctly identifies video composition agent', async () => {
474
+ const vcDb = createMockDb();
475
+ const vcService = createServiceWithCompositionSkill(vcDb, skillDir);
476
+
477
+ // Test with a custom-named agent that includes "video composition"
478
+ const customAgent = { ...MOCK_VIDEO_COMPOSITION_AGENT, name: 'Custom Video Composition Bot' };
479
+ vcDb._setSelectResults([[customAgent]]);
480
+
481
+ const result = await vcService.resetToDefault('default-vca');
482
+
483
+ // Should resolve to videoCompositionAgent stage and build from skill
484
+ assert.ok(result.promptTemplate.includes('Video Composition Agent'));
485
+
486
+ rmSync(skillDir, { recursive: true, force: true });
487
+ });
488
+ });
489
+ });