@assistkick/create 1.9.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/dist/src/scaffolder.d.ts +12 -1
  2. package/dist/src/scaffolder.js +40 -3
  3. package/dist/src/scaffolder.js.map +1 -1
  4. package/package.json +1 -1
  5. package/templates/assistkick-product-system/package.json +1 -1
  6. package/templates/assistkick-product-system/packages/backend/package.json +1 -0
  7. package/templates/assistkick-product-system/packages/backend/src/mcp/permission_mcp_server.ts +196 -0
  8. package/templates/assistkick-product-system/packages/backend/src/routes/agents.ts +31 -7
  9. package/templates/assistkick-product-system/packages/backend/src/routes/auth.ts +15 -12
  10. package/templates/assistkick-product-system/packages/backend/src/routes/chat_files.test.ts +95 -0
  11. package/templates/assistkick-product-system/packages/backend/src/routes/chat_files.ts +97 -0
  12. package/templates/assistkick-product-system/packages/backend/src/routes/chat_permission.ts +94 -0
  13. package/templates/assistkick-product-system/packages/backend/src/routes/chat_sessions.ts +189 -0
  14. package/templates/assistkick-product-system/packages/backend/src/routes/chat_upload.test.ts +131 -0
  15. package/templates/assistkick-product-system/packages/backend/src/routes/chat_upload.ts +94 -0
  16. package/templates/assistkick-product-system/packages/backend/src/routes/files.test.ts +12 -3
  17. package/templates/assistkick-product-system/packages/backend/src/routes/files.ts +2 -2
  18. package/templates/assistkick-product-system/packages/backend/src/routes/git.ts +391 -23
  19. package/templates/assistkick-product-system/packages/backend/src/routes/git_branches.test.ts +306 -0
  20. package/templates/assistkick-product-system/packages/backend/src/routes/git_connect.test.ts +133 -0
  21. package/templates/assistkick-product-system/packages/backend/src/routes/pipeline.ts +66 -9
  22. package/templates/assistkick-product-system/packages/backend/src/routes/preview.ts +204 -0
  23. package/templates/assistkick-product-system/packages/backend/src/routes/projects.test.ts +205 -0
  24. package/templates/assistkick-product-system/packages/backend/src/routes/projects.ts +37 -9
  25. package/templates/assistkick-product-system/packages/backend/src/routes/skills.test.ts +139 -0
  26. package/templates/assistkick-product-system/packages/backend/src/routes/skills.ts +95 -0
  27. package/templates/assistkick-product-system/packages/backend/src/routes/terminal.ts +5 -4
  28. package/templates/assistkick-product-system/packages/backend/src/routes/users.ts +4 -4
  29. package/templates/assistkick-product-system/packages/backend/src/routes/video.ts +8 -8
  30. package/templates/assistkick-product-system/packages/backend/src/routes/workflow_groups.ts +5 -5
  31. package/templates/assistkick-product-system/packages/backend/src/routes/workflows.ts +6 -6
  32. package/templates/assistkick-product-system/packages/backend/src/server.ts +107 -27
  33. package/templates/assistkick-product-system/packages/backend/src/services/agent_service.test.ts +105 -203
  34. package/templates/assistkick-product-system/packages/backend/src/services/agent_service.ts +76 -266
  35. package/templates/assistkick-product-system/packages/backend/src/services/chat_cli_bridge.test.ts +427 -0
  36. package/templates/assistkick-product-system/packages/backend/src/services/chat_cli_bridge.ts +345 -0
  37. package/templates/assistkick-product-system/packages/backend/src/services/chat_message_repository.test.ts +170 -0
  38. package/templates/assistkick-product-system/packages/backend/src/services/chat_message_repository.ts +106 -0
  39. package/templates/assistkick-product-system/packages/backend/src/services/chat_session_service.test.ts +217 -0
  40. package/templates/assistkick-product-system/packages/backend/src/services/chat_session_service.ts +188 -0
  41. package/templates/assistkick-product-system/packages/backend/src/services/chat_ws_handler.test.ts +1243 -0
  42. package/templates/assistkick-product-system/packages/backend/src/services/chat_ws_handler.ts +894 -0
  43. package/templates/assistkick-product-system/packages/backend/src/services/coherence-review.ts +3 -3
  44. package/templates/assistkick-product-system/packages/backend/src/services/dev_command_detector.test.ts +85 -0
  45. package/templates/assistkick-product-system/packages/backend/src/services/dev_command_detector.ts +54 -0
  46. package/templates/assistkick-product-system/packages/backend/src/services/email_service.ts +13 -10
  47. package/templates/assistkick-product-system/packages/backend/src/services/init.ts +11 -3
  48. package/templates/assistkick-product-system/packages/backend/src/services/invitation_service.ts +1 -1
  49. package/templates/assistkick-product-system/packages/backend/src/services/password_reset_service.ts +1 -1
  50. package/templates/assistkick-product-system/packages/backend/src/services/permission_service.test.ts +243 -0
  51. package/templates/assistkick-product-system/packages/backend/src/services/permission_service.ts +259 -0
  52. package/templates/assistkick-product-system/packages/backend/src/services/preview_server_manager.test.ts +172 -0
  53. package/templates/assistkick-product-system/packages/backend/src/services/preview_server_manager.ts +225 -0
  54. package/templates/assistkick-product-system/packages/backend/src/services/project_service.test.ts +29 -0
  55. package/templates/assistkick-product-system/packages/backend/src/services/project_service.ts +17 -0
  56. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.test.ts +255 -0
  57. package/templates/assistkick-product-system/packages/backend/src/services/project_workspace_service.ts +300 -25
  58. package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.test.ts +44 -0
  59. package/templates/assistkick-product-system/packages/backend/src/services/pty_session_manager.ts +62 -7
  60. package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.test.ts +77 -6
  61. package/templates/assistkick-product-system/packages/backend/src/services/ssh_key_service.ts +149 -14
  62. package/templates/assistkick-product-system/packages/backend/src/services/terminal_ws_handler.ts +2 -1
  63. package/templates/assistkick-product-system/packages/backend/src/services/title_generator_service.test.ts +45 -0
  64. package/templates/assistkick-product-system/packages/backend/src/services/title_generator_service.ts +157 -0
  65. package/templates/assistkick-product-system/packages/backend/src/services/tts_service.ts +4 -3
  66. package/templates/assistkick-product-system/packages/backend/src/services/video_render_service.ts +3 -3
  67. package/templates/assistkick-product-system/packages/frontend/package.json +5 -0
  68. package/templates/assistkick-product-system/packages/frontend/src/App.tsx +2 -0
  69. package/templates/assistkick-product-system/packages/frontend/src/api/client.ts +336 -5
  70. package/templates/assistkick-product-system/packages/frontend/src/components/AgentsView.tsx +192 -12
  71. package/templates/assistkick-product-system/packages/frontend/src/components/AttachmentPreviewList.tsx +98 -0
  72. package/templates/assistkick-product-system/packages/frontend/src/components/AutocompleteDropdown.tsx +65 -0
  73. package/templates/assistkick-product-system/packages/frontend/src/components/ChatAttachButton.tsx +56 -0
  74. package/templates/assistkick-product-system/packages/frontend/src/components/ChatDropZone.tsx +80 -0
  75. package/templates/assistkick-product-system/packages/frontend/src/components/ChatMessageBubble.tsx +155 -0
  76. package/templates/assistkick-product-system/packages/frontend/src/components/ChatMessageContent.tsx +182 -0
  77. package/templates/assistkick-product-system/packages/frontend/src/components/ChatMessageInput.tsx +233 -0
  78. package/templates/assistkick-product-system/packages/frontend/src/components/ChatSessionSidebar.tsx +218 -0
  79. package/templates/assistkick-product-system/packages/frontend/src/components/ChatStopButton.tsx +32 -0
  80. package/templates/assistkick-product-system/packages/frontend/src/components/ChatTodoSidebar.tsx +113 -0
  81. package/templates/assistkick-product-system/packages/frontend/src/components/ChatView.tsx +842 -0
  82. package/templates/assistkick-product-system/packages/frontend/src/components/CommitMessageModal.tsx +82 -0
  83. package/templates/assistkick-product-system/packages/frontend/src/components/DiagramOverlay.tsx +160 -0
  84. package/templates/assistkick-product-system/packages/frontend/src/components/EditorTabBar.tsx +5 -5
  85. package/templates/assistkick-product-system/packages/frontend/src/components/FileTree.tsx +9 -10
  86. package/templates/assistkick-product-system/packages/frontend/src/components/FileTreeInlineInput.tsx +5 -5
  87. package/templates/assistkick-product-system/packages/frontend/src/components/FilesView.tsx +112 -41
  88. package/templates/assistkick-product-system/packages/frontend/src/components/GraphLegend.tsx +2 -2
  89. package/templates/assistkick-product-system/packages/frontend/src/components/HighlightedText.tsx +87 -0
  90. package/templates/assistkick-product-system/packages/frontend/src/components/ImageLightbox.tsx +192 -0
  91. package/templates/assistkick-product-system/packages/frontend/src/components/KanbanView.tsx +2 -2
  92. package/templates/assistkick-product-system/packages/frontend/src/components/MentionPill.tsx +33 -0
  93. package/templates/assistkick-product-system/packages/frontend/src/components/MermaidBlock.tsx +148 -0
  94. package/templates/assistkick-product-system/packages/frontend/src/components/PermissionDialog.tsx +91 -0
  95. package/templates/assistkick-product-system/packages/frontend/src/components/PermissionModeSelector.tsx +229 -0
  96. package/templates/assistkick-product-system/packages/frontend/src/components/ProjectSelector.tsx +249 -83
  97. package/templates/assistkick-product-system/packages/frontend/src/components/QueuedMessageBubble.tsx +38 -0
  98. package/templates/assistkick-product-system/packages/frontend/src/components/SidePanel.tsx +212 -117
  99. package/templates/assistkick-product-system/packages/frontend/src/components/SystemPromptAccordion.tsx +48 -0
  100. package/templates/assistkick-product-system/packages/frontend/src/components/TaskIcon.tsx +11 -0
  101. package/templates/assistkick-product-system/packages/frontend/src/components/TerminalView.tsx +25 -9
  102. package/templates/assistkick-product-system/packages/frontend/src/components/ToolDiffView.tsx +114 -0
  103. package/templates/assistkick-product-system/packages/frontend/src/components/ToolResultCard.tsx +87 -0
  104. package/templates/assistkick-product-system/packages/frontend/src/components/ToolUseCard.tsx +149 -0
  105. package/templates/assistkick-product-system/packages/frontend/src/components/Toolbar.tsx +25 -8
  106. package/templates/assistkick-product-system/packages/frontend/src/components/UnifiedGitWidget.tsx +722 -0
  107. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/GroupNode.tsx +2 -0
  108. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/NodePalette.tsx +2 -1
  109. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/ProgrammableNode.tsx +178 -0
  110. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowCanvas.tsx +3 -0
  111. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/WorkflowMonitorModal.tsx +103 -9
  112. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/monitor_nodes.tsx +26 -2
  113. package/templates/assistkick-product-system/packages/frontend/src/components/workflow/workflow_types.ts +42 -1
  114. package/templates/assistkick-product-system/packages/frontend/src/hooks/useDocumentTitle.ts +11 -0
  115. package/templates/assistkick-product-system/packages/frontend/src/hooks/useProjects.ts +1 -0
  116. package/templates/assistkick-product-system/packages/frontend/src/hooks/use_chat_stream.ts +826 -0
  117. package/templates/assistkick-product-system/packages/frontend/src/hooks/use_file_tree_cache.ts +69 -0
  118. package/templates/assistkick-product-system/packages/frontend/src/hooks/use_mention_autocomplete.ts +284 -0
  119. package/templates/assistkick-product-system/packages/frontend/src/lib/attachment_manager.test.ts +183 -0
  120. package/templates/assistkick-product-system/packages/frontend/src/lib/attachment_manager.ts +150 -0
  121. package/templates/assistkick-product-system/packages/frontend/src/lib/chat_message_helpers.test.ts +305 -0
  122. package/templates/assistkick-product-system/packages/frontend/src/lib/chat_message_helpers.ts +113 -0
  123. package/templates/assistkick-product-system/packages/frontend/src/lib/context_usage_helpers.test.ts +157 -0
  124. package/templates/assistkick-product-system/packages/frontend/src/lib/context_usage_helpers.ts +95 -0
  125. package/templates/assistkick-product-system/packages/frontend/src/lib/mermaid_helpers.test.ts +65 -0
  126. package/templates/assistkick-product-system/packages/frontend/src/lib/mermaid_helpers.ts +110 -0
  127. package/templates/assistkick-product-system/packages/frontend/src/lib/message_queue.ts +66 -0
  128. package/templates/assistkick-product-system/packages/frontend/src/lib/tool_use_summary.test.ts +124 -0
  129. package/templates/assistkick-product-system/packages/frontend/src/lib/tool_use_summary.ts +112 -0
  130. package/templates/assistkick-product-system/packages/frontend/src/routes/AgentsRoute.tsx +2 -0
  131. package/templates/assistkick-product-system/packages/frontend/src/routes/ChatRoute.tsx +8 -0
  132. package/templates/assistkick-product-system/packages/frontend/src/routes/CoherenceRoute.tsx +2 -0
  133. package/templates/assistkick-product-system/packages/frontend/src/routes/DashboardLayout.tsx +0 -4
  134. package/templates/assistkick-product-system/packages/frontend/src/routes/DesignSystemRoute.tsx +2 -0
  135. package/templates/assistkick-product-system/packages/frontend/src/routes/FilesRoute.tsx +2 -0
  136. package/templates/assistkick-product-system/packages/frontend/src/routes/GraphRoute.tsx +2 -0
  137. package/templates/assistkick-product-system/packages/frontend/src/routes/KanbanRoute.tsx +2 -0
  138. package/templates/assistkick-product-system/packages/frontend/src/routes/TerminalRoute.tsx +2 -0
  139. package/templates/assistkick-product-system/packages/frontend/src/routes/UsersRoute.tsx +2 -0
  140. package/templates/assistkick-product-system/packages/frontend/src/routes/VideographyRoute.tsx +2 -0
  141. package/templates/assistkick-product-system/packages/frontend/src/routes/WorkflowsRoute.tsx +2 -0
  142. package/templates/assistkick-product-system/packages/frontend/src/routes/accept_invitation.tsx +2 -0
  143. package/templates/assistkick-product-system/packages/frontend/src/routes/forgot_password.tsx +2 -0
  144. package/templates/assistkick-product-system/packages/frontend/src/routes/login.tsx +2 -0
  145. package/templates/assistkick-product-system/packages/frontend/src/routes/register.tsx +2 -0
  146. package/templates/assistkick-product-system/packages/frontend/src/routes/reset_password.tsx +2 -0
  147. package/templates/assistkick-product-system/packages/frontend/src/stores/useAttachmentStore.ts +66 -0
  148. package/templates/assistkick-product-system/packages/frontend/src/stores/useChatSessionStore.ts +107 -0
  149. package/templates/assistkick-product-system/packages/frontend/src/stores/useMessageQueueStore.ts +110 -0
  150. package/templates/assistkick-product-system/packages/frontend/src/stores/usePreviewStore.ts +78 -0
  151. package/templates/assistkick-product-system/packages/frontend/src/stores/useProjectStore.ts +7 -0
  152. package/templates/assistkick-product-system/packages/frontend/src/stores/useSidePanelStore.ts +6 -1
  153. package/templates/assistkick-product-system/packages/frontend/src/styles/index.css +30 -357
  154. package/templates/assistkick-product-system/packages/frontend/src/utils/parse_node_markdown.test.ts +115 -0
  155. package/templates/assistkick-product-system/packages/frontend/src/utils/parse_node_markdown.ts +91 -0
  156. package/templates/assistkick-product-system/packages/frontend/src/utils/preview_utils.test.ts +30 -0
  157. package/templates/assistkick-product-system/packages/frontend/src/utils/preview_utils.ts +3 -0
  158. package/templates/assistkick-product-system/packages/shared/db/migrations/0015_magenta_jazinda.sql +1 -0
  159. package/templates/assistkick-product-system/packages/shared/db/migrations/0016_giant_xorn.sql +1 -0
  160. package/templates/assistkick-product-system/packages/shared/db/migrations/0017_sloppy_mentor.sql +6 -0
  161. package/templates/assistkick-product-system/packages/shared/db/migrations/0018_vengeful_kabuki.sql +9 -0
  162. package/templates/assistkick-product-system/packages/shared/db/migrations/0019_careful_sentinels.sql +8 -0
  163. package/templates/assistkick-product-system/packages/shared/db/migrations/0020_clever_spot.sql +27 -0
  164. package/templates/assistkick-product-system/packages/shared/db/migrations/0021_graceful_hex.sql +1 -0
  165. package/templates/assistkick-product-system/packages/shared/db/migrations/0022_short_kingpin.sql +1 -0
  166. package/templates/assistkick-product-system/packages/shared/db/migrations/0023_ambiguous_sharon_carter.sql +1 -0
  167. package/templates/assistkick-product-system/packages/shared/db/migrations/0024_fat_unus.sql +1 -0
  168. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0015_snapshot.json +1552 -0
  169. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0016_snapshot.json +1560 -0
  170. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0017_snapshot.json +1598 -0
  171. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0018_snapshot.json +1657 -0
  172. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0019_snapshot.json +1709 -0
  173. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0020_snapshot.json +1733 -0
  174. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0021_snapshot.json +1740 -0
  175. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0022_snapshot.json +1755 -0
  176. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0023_snapshot.json +1762 -0
  177. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/0024_snapshot.json +1769 -0
  178. package/templates/assistkick-product-system/packages/shared/db/migrations/meta/_journal.json +70 -0
  179. package/templates/assistkick-product-system/packages/shared/db/schema.ts +40 -1
  180. package/templates/assistkick-product-system/packages/shared/lib/claude-service.test.ts +236 -0
  181. package/templates/assistkick-product-system/packages/shared/lib/claude-service.ts +46 -5
  182. package/templates/assistkick-product-system/packages/shared/lib/git_workflow.ts +65 -39
  183. package/templates/assistkick-product-system/packages/shared/lib/programmable_node_executor.test.ts +173 -0
  184. package/templates/assistkick-product-system/packages/shared/lib/programmable_node_executor.ts +213 -0
  185. package/templates/assistkick-product-system/packages/shared/lib/validator.test.ts +70 -0
  186. package/templates/assistkick-product-system/packages/shared/lib/validator.ts +17 -1
  187. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.test.ts +803 -27
  188. package/templates/assistkick-product-system/packages/shared/lib/workflow_engine.ts +502 -68
  189. package/templates/assistkick-product-system/packages/shared/lib/workflow_orchestrator.ts +4 -4
  190. package/templates/assistkick-product-system/packages/shared/package.json +2 -1
  191. package/templates/assistkick-product-system/packages/shared/test_fixtures/hanging_stream.mjs +46 -0
  192. package/templates/assistkick-product-system/packages/shared/tools/add_node.test.ts +44 -0
  193. package/templates/assistkick-product-system/packages/shared/tools/add_node.ts +7 -0
  194. package/templates/assistkick-product-system/packages/shared/tools/remove_node.ts +2 -1
  195. package/templates/assistkick-product-system/packages/shared/tools/resolve_question.ts +2 -1
  196. package/templates/assistkick-product-system/packages/shared/tools/update_node.ts +2 -1
  197. package/templates/assistkick-product-system/tests/message_queue.test.ts +178 -0
  198. package/templates/assistkick-product-system/tests/message_queue_per_session.test.ts +143 -0
  199. package/templates/skills/assistkick-bootstrap/SKILL.md +26 -26
  200. package/templates/skills/assistkick-code-reviewer/SKILL.md +45 -46
  201. package/templates/skills/assistkick-db-explorer/SKILL.md +13 -13
  202. package/templates/skills/assistkick-debugger/SKILL.md +23 -23
  203. package/templates/skills/assistkick-developer/SKILL.md +59 -63
  204. package/templates/skills/assistkick-interview/SKILL.md +26 -26
  205. package/templates/skills/assistkick-video-composition-agent/SKILL.md +231 -0
  206. package/templates/skills/assistkick-video-script-writer/SKILL.md +136 -0
@@ -0,0 +1,217 @@
1
+ import { describe, it, mock, beforeEach } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { ChatSessionService } from './chat_session_service.ts';
4
+
5
+ const createMockDb = () => {
6
+ let selectResults: any[][] = [];
7
+ let selectCallIndex = 0;
8
+ let deleteCallCount = 0;
9
+
10
+ const db = {
11
+ select: mock.fn(() => ({
12
+ from: mock.fn(() => ({
13
+ where: mock.fn(() => {
14
+ const result = selectResults[selectCallIndex] || [];
15
+ selectCallIndex++;
16
+ return Object.assign(result, {
17
+ orderBy: mock.fn(() => result),
18
+ });
19
+ }),
20
+ orderBy: mock.fn(() => {
21
+ const result = selectResults[selectCallIndex] || [];
22
+ selectCallIndex++;
23
+ return result;
24
+ }),
25
+ })),
26
+ })),
27
+ insert: mock.fn(() => ({
28
+ values: mock.fn(() => Promise.resolve()),
29
+ })),
30
+ update: mock.fn(() => ({
31
+ set: mock.fn(() => ({
32
+ where: mock.fn(() => Promise.resolve()),
33
+ })),
34
+ })),
35
+ delete: mock.fn(() => ({
36
+ where: mock.fn(() => {
37
+ deleteCallCount++;
38
+ return Promise.resolve();
39
+ }),
40
+ })),
41
+ _setSelectResults: (results: any[][]) => {
42
+ selectResults = results;
43
+ selectCallIndex = 0;
44
+ },
45
+ _getDeleteCallCount: () => deleteCallCount,
46
+ };
47
+
48
+ return db;
49
+ };
50
+
51
+ const createService = (db: any) => {
52
+ return new ChatSessionService({
53
+ getDb: () => db,
54
+ log: mock.fn(),
55
+ });
56
+ };
57
+
58
+ describe('ChatSessionService', () => {
59
+ let db: ReturnType<typeof createMockDb>;
60
+ let service: ChatSessionService;
61
+
62
+ beforeEach(() => {
63
+ db = createMockDb();
64
+ service = createService(db);
65
+ });
66
+
67
+ describe('generateId', () => {
68
+ it('returns an id with csess_ prefix', () => {
69
+ const id = service.generateId();
70
+ assert.ok(id.startsWith('csess_'));
71
+ assert.ok(id.length > 6);
72
+ });
73
+
74
+ it('generates unique ids', () => {
75
+ const id1 = service.generateId();
76
+ const id2 = service.generateId();
77
+ assert.notEqual(id1, id2);
78
+ });
79
+ });
80
+
81
+ describe('formatSessionName', () => {
82
+ it('formats date as Chat DD/MM/YY HH:MM', () => {
83
+ const date = new Date('2026-03-11T14:30:00.000Z');
84
+ const name = service.formatSessionName(date);
85
+ // Format depends on timezone, but pattern should match
86
+ assert.ok(name.startsWith('Chat '));
87
+ assert.match(name, /^Chat \d{2}\/\d{2}\/\d{2} \d{2}:\d{2}$/);
88
+ });
89
+ });
90
+
91
+ describe('listSessions', () => {
92
+ it('returns sessions for a project', async () => {
93
+ const mockSessions = [
94
+ {
95
+ id: 'csess_abc',
96
+ claudeSessionId: 'uuid-1',
97
+ projectId: 'proj_001',
98
+ name: 'Chat 11/03/26 14:30',
99
+ permissionMode: null,
100
+ createdAt: '2026-03-11T14:30:00.000Z',
101
+ updatedAt: '2026-03-11T14:30:00.000Z',
102
+ },
103
+ ];
104
+ db._setSelectResults([mockSessions]);
105
+
106
+ const sessions = await service.listSessions('proj_001');
107
+ assert.equal(sessions.length, 1);
108
+ assert.equal(sessions[0].id, 'csess_abc');
109
+ assert.equal(sessions[0].projectId, 'proj_001');
110
+ });
111
+
112
+ it('returns empty array when no sessions exist', async () => {
113
+ db._setSelectResults([[]]);
114
+ const sessions = await service.listSessions('proj_001');
115
+ assert.equal(sessions.length, 0);
116
+ });
117
+ });
118
+
119
+ describe('getSession', () => {
120
+ it('returns session when found', async () => {
121
+ const mockSession = {
122
+ id: 'csess_abc',
123
+ claudeSessionId: 'uuid-1',
124
+ projectId: 'proj_001',
125
+ name: 'Chat 11/03/26 14:30',
126
+ permissionMode: null,
127
+ createdAt: '2026-03-11T14:30:00.000Z',
128
+ updatedAt: '2026-03-11T14:30:00.000Z',
129
+ };
130
+ db._setSelectResults([[mockSession]]);
131
+
132
+ const session = await service.getSession('csess_abc');
133
+ assert.ok(session);
134
+ assert.equal(session.id, 'csess_abc');
135
+ });
136
+
137
+ it('returns undefined when session not found', async () => {
138
+ db._setSelectResults([[]]);
139
+ const session = await service.getSession('csess_nonexistent');
140
+ assert.equal(session, undefined);
141
+ });
142
+ });
143
+
144
+ describe('createSession', () => {
145
+ it('creates a session and returns it', async () => {
146
+ const session = await service.createSession('proj_001');
147
+
148
+ assert.ok(session.id.startsWith('csess_'));
149
+ assert.ok(session.claudeSessionId);
150
+ assert.equal(session.projectId, 'proj_001');
151
+ assert.ok(session.name.startsWith('Chat '));
152
+ assert.equal(session.permissionMode, null);
153
+ assert.ok(session.createdAt);
154
+ assert.ok(session.updatedAt);
155
+
156
+ assert.equal(db.insert.mock.calls.length, 1);
157
+ });
158
+ });
159
+
160
+ describe('renameSession', () => {
161
+ it('renames an existing session', async () => {
162
+ const mockSession = {
163
+ id: 'csess_abc',
164
+ claudeSessionId: 'uuid-1',
165
+ projectId: 'proj_001',
166
+ name: 'Old Name',
167
+ permissionMode: null,
168
+ createdAt: '2026-03-11T14:30:00.000Z',
169
+ updatedAt: '2026-03-11T14:30:00.000Z',
170
+ };
171
+ db._setSelectResults([[mockSession]]);
172
+
173
+ const result = await service.renameSession('csess_abc', 'New Name');
174
+ assert.ok(result);
175
+ assert.equal(result.name, 'New Name');
176
+ assert.equal(db.update.mock.calls.length, 1);
177
+ });
178
+
179
+ it('returns undefined when session not found', async () => {
180
+ db._setSelectResults([[]]);
181
+ const result = await service.renameSession('csess_nonexistent', 'New Name');
182
+ assert.equal(result, undefined);
183
+ });
184
+ });
185
+
186
+ describe('deleteSession', () => {
187
+ it('deletes an existing session', async () => {
188
+ const mockSession = {
189
+ id: 'csess_abc',
190
+ claudeSessionId: 'uuid-1',
191
+ projectId: 'proj_001',
192
+ name: 'Chat 11/03/26 14:30',
193
+ permissionMode: null,
194
+ createdAt: '2026-03-11T14:30:00.000Z',
195
+ updatedAt: '2026-03-11T14:30:00.000Z',
196
+ };
197
+ db._setSelectResults([[mockSession]]);
198
+
199
+ const result = await service.deleteSession('csess_abc');
200
+ assert.equal(result, true);
201
+ assert.equal(db.delete.mock.calls.length, 1);
202
+ });
203
+
204
+ it('returns false when session not found', async () => {
205
+ db._setSelectResults([[]]);
206
+ const result = await service.deleteSession('csess_nonexistent');
207
+ assert.equal(result, false);
208
+ });
209
+ });
210
+
211
+ describe('touchSession', () => {
212
+ it('updates the updatedAt timestamp', async () => {
213
+ await service.touchSession('csess_abc');
214
+ assert.equal(db.update.mock.calls.length, 1);
215
+ });
216
+ });
217
+ });
@@ -0,0 +1,188 @@
1
+ /**
2
+ * ChatSessionService — manages chat v2 sessions scoped to projects.
3
+ * Sessions are persisted in the database. Each session maps to a
4
+ * Claude Code session ID (UUID) used by the CLI bridge.
5
+ */
6
+
7
+ import { randomBytes, randomUUID } from 'node:crypto';
8
+ import { desc, eq, and } from 'drizzle-orm';
9
+ import { chatSessions } from '@assistkick/shared/db/schema.js';
10
+
11
+ interface ChatSessionServiceDeps {
12
+ getDb: () => any;
13
+ log: (tag: string, ...args: unknown[]) => void;
14
+ }
15
+
16
+ export interface ChatSessionInfo {
17
+ id: string;
18
+ claudeSessionId: string;
19
+ projectId: string;
20
+ name: string;
21
+ permissionMode: string | null;
22
+ continuationContext: string | null;
23
+ systemPrompt: string | null;
24
+ contextUsageJson: string | null;
25
+ createdAt: string;
26
+ updatedAt: string;
27
+ }
28
+
29
+ export class ChatSessionService {
30
+ private readonly getDb: ChatSessionServiceDeps['getDb'];
31
+ private readonly log: ChatSessionServiceDeps['log'];
32
+
33
+ constructor({ getDb, log }: ChatSessionServiceDeps) {
34
+ this.getDb = getDb;
35
+ this.log = log;
36
+ }
37
+
38
+ generateId = (): string => {
39
+ const hex = randomBytes(3).toString('hex');
40
+ return `csess_${hex}`;
41
+ };
42
+
43
+ formatSessionName = (date: Date): string => {
44
+ const dd = String(date.getDate()).padStart(2, '0');
45
+ const mm = String(date.getMonth() + 1).padStart(2, '0');
46
+ const yy = String(date.getFullYear()).slice(2);
47
+ const hh = String(date.getHours()).padStart(2, '0');
48
+ const min = String(date.getMinutes()).padStart(2, '0');
49
+ return `Chat ${dd}/${mm}/${yy} ${hh}:${min}`;
50
+ };
51
+
52
+ /** List all chat sessions for a given project, newest first. */
53
+ listSessions = async (projectId: string): Promise<ChatSessionInfo[]> => {
54
+ const db = this.getDb();
55
+ const rows = await db
56
+ .select()
57
+ .from(chatSessions)
58
+ .where(eq(chatSessions.projectId, projectId))
59
+ .orderBy(desc(chatSessions.updatedAt));
60
+ return rows.map((r: any) => ({
61
+ id: r.id,
62
+ claudeSessionId: r.claudeSessionId,
63
+ projectId: r.projectId,
64
+ name: r.name,
65
+ permissionMode: r.permissionMode,
66
+ continuationContext: r.continuationContext,
67
+ systemPrompt: r.systemPrompt,
68
+ contextUsageJson: r.contextUsageJson,
69
+ createdAt: r.createdAt,
70
+ updatedAt: r.updatedAt,
71
+ }));
72
+ };
73
+
74
+ /** Get a single session by ID. */
75
+ getSession = async (sessionId: string): Promise<ChatSessionInfo | undefined> => {
76
+ const db = this.getDb();
77
+ const rows = await db
78
+ .select()
79
+ .from(chatSessions)
80
+ .where(eq(chatSessions.id, sessionId));
81
+ if (rows.length === 0) return undefined;
82
+ const r = rows[0];
83
+ return {
84
+ id: r.id,
85
+ claudeSessionId: r.claudeSessionId,
86
+ projectId: r.projectId,
87
+ name: r.name,
88
+ permissionMode: r.permissionMode,
89
+ continuationContext: r.continuationContext,
90
+ systemPrompt: r.systemPrompt,
91
+ contextUsageJson: r.contextUsageJson,
92
+ createdAt: r.createdAt,
93
+ updatedAt: r.updatedAt,
94
+ };
95
+ };
96
+
97
+ /** Create a new chat session for a project, optionally with compacted continuation context. */
98
+ createSession = async (projectId: string, continuationContext?: string): Promise<ChatSessionInfo> => {
99
+ const id = this.generateId();
100
+ const claudeSessionId = randomUUID();
101
+ const now = new Date();
102
+ const name = this.formatSessionName(now);
103
+ const nowIso = now.toISOString();
104
+
105
+ const db = this.getDb();
106
+ await db.insert(chatSessions).values({
107
+ id,
108
+ claudeSessionId,
109
+ projectId,
110
+ name,
111
+ continuationContext: continuationContext ?? null,
112
+ createdAt: nowIso,
113
+ updatedAt: nowIso,
114
+ });
115
+
116
+ this.log('CHAT_SESSION', `Created session "${name}" (${id}) for project ${projectId}${continuationContext ? ' [with continuation]' : ''}`);
117
+
118
+ return {
119
+ id,
120
+ claudeSessionId,
121
+ projectId,
122
+ name,
123
+ permissionMode: null,
124
+ continuationContext: continuationContext ?? null,
125
+ systemPrompt: null,
126
+ contextUsageJson: null,
127
+ createdAt: nowIso,
128
+ updatedAt: nowIso,
129
+ };
130
+ };
131
+
132
+ /** Rename a chat session. */
133
+ renameSession = async (sessionId: string, name: string): Promise<ChatSessionInfo | undefined> => {
134
+ const session = await this.getSession(sessionId);
135
+ if (!session) return undefined;
136
+
137
+ const db = this.getDb();
138
+ const now = new Date().toISOString();
139
+ await db
140
+ .update(chatSessions)
141
+ .set({ name, updatedAt: now })
142
+ .where(eq(chatSessions.id, sessionId));
143
+
144
+ this.log('CHAT_SESSION', `Renamed session ${sessionId} to "${name}"`);
145
+
146
+ return { ...session, name, updatedAt: now };
147
+ };
148
+
149
+ /** Delete a chat session permanently. */
150
+ deleteSession = async (sessionId: string): Promise<boolean> => {
151
+ const session = await this.getSession(sessionId);
152
+ if (!session) return false;
153
+
154
+ const db = this.getDb();
155
+ await db.delete(chatSessions).where(eq(chatSessions.id, sessionId));
156
+
157
+ this.log('CHAT_SESSION', `Deleted session ${sessionId}`);
158
+ return true;
159
+ };
160
+
161
+ /** Touch the updatedAt timestamp for a session. */
162
+ touchSession = async (sessionId: string): Promise<void> => {
163
+ const db = this.getDb();
164
+ const now = new Date().toISOString();
165
+ await db
166
+ .update(chatSessions)
167
+ .set({ updatedAt: now })
168
+ .where(eq(chatSessions.id, sessionId));
169
+ };
170
+
171
+ /** Update context usage JSON for a session. */
172
+ updateContextUsage = async (sessionId: string, contextUsageJson: string): Promise<void> => {
173
+ const db = this.getDb();
174
+ await db
175
+ .update(chatSessions)
176
+ .set({ contextUsageJson })
177
+ .where(eq(chatSessions.id, sessionId));
178
+ };
179
+
180
+ /** Update the composed system prompt for a session. */
181
+ updateSystemPrompt = async (sessionId: string, systemPrompt: string): Promise<void> => {
182
+ const db = this.getDb();
183
+ await db
184
+ .update(chatSessions)
185
+ .set({ systemPrompt })
186
+ .where(eq(chatSessions.id, sessionId));
187
+ };
188
+ }