@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,341 @@
1
+ /**
2
+ * Tests for Kanban Workflow Integration — dependency ordering,
3
+ * status mapping, and default workflow resolution logic.
4
+ * Uses node:test built-in runner.
5
+ */
6
+ import { describe, it } from 'node:test';
7
+ import assert from 'node:assert/strict';
8
+
9
+ /**
10
+ * Dependency-checking logic matching WorkflowOrchestrator.runLoop.
11
+ * Tests the pure data transformation.
12
+ */
13
+ const checkDeps = (featureId: string, graphData: any, kanbanData: any): boolean => {
14
+ const deps = graphData.edges
15
+ .filter((e: any) => e.from === featureId && e.relation === 'depends_on')
16
+ .map((e: any) => e.to)
17
+ .filter((depId: string) => graphData.nodes.some((n: any) => n.id === depId && n.type === 'feature'));
18
+ return deps.some((depId: string) =>
19
+ !kanbanData[depId] || kanbanData[depId].column !== 'done'
20
+ );
21
+ };
22
+
23
+ /**
24
+ * Simulates the Play All ordering: find next processable feature using
25
+ * the workflow orchestrator's dependency checking logic.
26
+ */
27
+ const findNextProcessable = (
28
+ todoCards: { id: string; devBlocked: boolean }[],
29
+ graphData: any,
30
+ kanbanData: any,
31
+ ) => {
32
+ for (const card of todoCards) {
33
+ if (card.devBlocked) continue;
34
+ if (!checkDeps(card.id, graphData, kanbanData)) return card;
35
+ }
36
+ return null;
37
+ };
38
+
39
+ /**
40
+ * Maps workflow status to display label, matching the frontend
41
+ * WORKFLOW_STATUS_LABELS constant.
42
+ */
43
+ const WORKFLOW_STATUS_LABELS: Record<string, string> = {
44
+ running: 'Running',
45
+ completed: 'Completed',
46
+ failed: 'Failed',
47
+ idle: 'Idle',
48
+ };
49
+
50
+ /**
51
+ * Default workflow resolution logic — project-specific first, then global.
52
+ */
53
+ const resolveDefault = (
54
+ workflows: { id: string; projectId: string | null; isDefault: number }[],
55
+ projectId?: string,
56
+ ) => {
57
+ if (projectId) {
58
+ const projectDefault = workflows.find(
59
+ w => w.projectId === projectId && w.isDefault === 1,
60
+ );
61
+ if (projectDefault) return projectDefault;
62
+ }
63
+ // Fall back to global default
64
+ return workflows.find(w => w.projectId === null && w.isDefault === 1) || null;
65
+ };
66
+
67
+ describe('WorkflowOrchestrator dependency ordering', () => {
68
+ it('unblocked when all deps are done', () => {
69
+ const graphData = {
70
+ nodes: [
71
+ { id: 'feat_001', type: 'feature' },
72
+ { id: 'feat_002', type: 'feature' },
73
+ ],
74
+ edges: [{ from: 'feat_001', to: 'feat_002', relation: 'depends_on' }],
75
+ };
76
+ const kanbanData = {
77
+ feat_001: { column: 'todo' },
78
+ feat_002: { column: 'done' },
79
+ };
80
+ assert.equal(checkDeps('feat_001', graphData, kanbanData), false);
81
+ });
82
+
83
+ it('blocked when a dep is not done', () => {
84
+ const graphData = {
85
+ nodes: [
86
+ { id: 'feat_001', type: 'feature' },
87
+ { id: 'feat_002', type: 'feature' },
88
+ ],
89
+ edges: [{ from: 'feat_001', to: 'feat_002', relation: 'depends_on' }],
90
+ };
91
+ const kanbanData = {
92
+ feat_001: { column: 'todo' },
93
+ feat_002: { column: 'in_progress' },
94
+ };
95
+ assert.equal(checkDeps('feat_001', graphData, kanbanData), true);
96
+ });
97
+
98
+ it('unblocked when no depends_on edges', () => {
99
+ const graphData = {
100
+ nodes: [{ id: 'feat_001', type: 'feature' }],
101
+ edges: [],
102
+ };
103
+ const kanbanData = { feat_001: { column: 'todo' } };
104
+ assert.equal(checkDeps('feat_001', graphData, kanbanData), false);
105
+ });
106
+
107
+ it('ignores non-feature dependency targets', () => {
108
+ const graphData = {
109
+ nodes: [
110
+ { id: 'feat_001', type: 'feature' },
111
+ { id: 'comp_001', type: 'component' },
112
+ ],
113
+ edges: [{ from: 'feat_001', to: 'comp_001', relation: 'depends_on' }],
114
+ };
115
+ const kanbanData = { feat_001: { column: 'todo' } };
116
+ assert.equal(checkDeps('feat_001', graphData, kanbanData), false);
117
+ });
118
+
119
+ it('ignores non-depends_on relations', () => {
120
+ const graphData = {
121
+ nodes: [
122
+ { id: 'feat_001', type: 'feature' },
123
+ { id: 'feat_002', type: 'feature' },
124
+ ],
125
+ edges: [{ from: 'feat_001', to: 'feat_002', relation: 'relates_to' }],
126
+ };
127
+ const kanbanData = {
128
+ feat_001: { column: 'todo' },
129
+ feat_002: { column: 'todo' },
130
+ };
131
+ assert.equal(checkDeps('feat_001', graphData, kanbanData), false);
132
+ });
133
+ });
134
+
135
+ describe('findNextProcessable', () => {
136
+ const graphData = {
137
+ nodes: [
138
+ { id: 'feat_001', type: 'feature' },
139
+ { id: 'feat_002', type: 'feature' },
140
+ { id: 'feat_003', type: 'feature' },
141
+ ],
142
+ edges: [
143
+ { from: 'feat_002', to: 'feat_001', relation: 'depends_on' },
144
+ ],
145
+ };
146
+
147
+ it('returns first unblocked feature', () => {
148
+ const kanbanData = {
149
+ feat_001: { column: 'todo' },
150
+ feat_002: { column: 'todo' },
151
+ };
152
+ const cards = [
153
+ { id: 'feat_001', devBlocked: false },
154
+ { id: 'feat_002', devBlocked: false },
155
+ ];
156
+
157
+ const next = findNextProcessable(cards, graphData, kanbanData);
158
+ assert.equal(next!.id, 'feat_001');
159
+ });
160
+
161
+ it('skips features with unmet dependencies', () => {
162
+ const kanbanData = {
163
+ feat_001: { column: 'todo' },
164
+ feat_002: { column: 'todo' },
165
+ feat_003: { column: 'todo' },
166
+ };
167
+ const cards = [
168
+ { id: 'feat_002', devBlocked: false }, // depends on feat_001 in todo
169
+ { id: 'feat_001', devBlocked: false }, // no deps
170
+ { id: 'feat_003', devBlocked: false }, // no deps
171
+ ];
172
+
173
+ const next = findNextProcessable(cards, graphData, kanbanData);
174
+ assert.equal(next!.id, 'feat_001');
175
+ });
176
+
177
+ it('skips devBlocked features', () => {
178
+ const kanbanData = {
179
+ feat_001: { column: 'todo' },
180
+ feat_003: { column: 'todo' },
181
+ };
182
+ const cards = [
183
+ { id: 'feat_001', devBlocked: true },
184
+ { id: 'feat_003', devBlocked: false },
185
+ ];
186
+
187
+ const next = findNextProcessable(cards, graphData, kanbanData);
188
+ assert.equal(next!.id, 'feat_003');
189
+ });
190
+
191
+ it('returns null when all features are blocked', () => {
192
+ const kanbanData = {
193
+ feat_002: { column: 'todo' },
194
+ };
195
+ const cards = [
196
+ { id: 'feat_002', devBlocked: false }, // depends on feat_001 not done
197
+ ];
198
+
199
+ const next = findNextProcessable(cards, graphData, kanbanData);
200
+ assert.equal(next, null);
201
+ });
202
+ });
203
+
204
+ describe('Workflow status labels', () => {
205
+ it('maps running to Running', () => {
206
+ assert.equal(WORKFLOW_STATUS_LABELS['running'], 'Running');
207
+ });
208
+
209
+ it('maps completed to Completed', () => {
210
+ assert.equal(WORKFLOW_STATUS_LABELS['completed'], 'Completed');
211
+ });
212
+
213
+ it('maps failed to Failed', () => {
214
+ assert.equal(WORKFLOW_STATUS_LABELS['failed'], 'Failed');
215
+ });
216
+
217
+ it('maps idle to Idle', () => {
218
+ assert.equal(WORKFLOW_STATUS_LABELS['idle'], 'Idle');
219
+ });
220
+ });
221
+
222
+ describe('Workflow status resolution', () => {
223
+ it('running status with agent name indicates active agent node', () => {
224
+ const status = { status: 'running', currentAgentName: 'Developer', error: null };
225
+ assert.equal(status.status, 'running');
226
+ assert.equal(status.currentAgentName, 'Developer');
227
+ assert.equal(status.error, null);
228
+ });
229
+
230
+ it('running status without agent name indicates non-agent node', () => {
231
+ const status = { status: 'running', currentAgentName: null, error: null };
232
+ assert.equal(status.currentAgentName, null);
233
+ });
234
+
235
+ it('failed status includes error message', () => {
236
+ const status = { status: 'failed', currentAgentName: null, error: 'Agent crashed' };
237
+ assert.equal(status.status, 'failed');
238
+ assert.equal(status.error, 'Agent crashed');
239
+ });
240
+
241
+ it('idle status when no execution exists', () => {
242
+ const status = { status: 'idle', featureId: 'feat_001' };
243
+ assert.equal(status.status, 'idle');
244
+ });
245
+
246
+ it('completed status has no error', () => {
247
+ const status = { status: 'completed', error: null };
248
+ assert.equal(status.status, 'completed');
249
+ assert.equal(status.error, null);
250
+ });
251
+ });
252
+
253
+ describe('Default workflow resolution', () => {
254
+ const workflows = [
255
+ { id: 'wf-global', projectId: null, isDefault: 1 },
256
+ { id: 'wf-proj-1', projectId: 'proj_001', isDefault: 1 },
257
+ { id: 'wf-proj-2', projectId: 'proj_001', isDefault: 0 },
258
+ { id: 'wf-other', projectId: 'proj_002', isDefault: 1 },
259
+ ];
260
+
261
+ it('resolves project-specific default when available', () => {
262
+ const result = resolveDefault(workflows, 'proj_001');
263
+ assert.equal(result!.id, 'wf-proj-1');
264
+ });
265
+
266
+ it('falls back to global default when no project default', () => {
267
+ const result = resolveDefault(workflows, 'proj_999');
268
+ assert.equal(result!.id, 'wf-global');
269
+ });
270
+
271
+ it('uses global default when no projectId provided', () => {
272
+ const result = resolveDefault(workflows);
273
+ assert.equal(result!.id, 'wf-global');
274
+ });
275
+
276
+ it('returns null when no default exists', () => {
277
+ const noDefaults = [
278
+ { id: 'wf-1', projectId: null, isDefault: 0 },
279
+ ];
280
+ const result = resolveDefault(noDefaults);
281
+ assert.equal(result, null);
282
+ });
283
+
284
+ it('prefers project-specific over global', () => {
285
+ const result = resolveDefault(workflows, 'proj_002');
286
+ assert.equal(result!.id, 'wf-other');
287
+ assert.equal(result!.projectId, 'proj_002');
288
+ });
289
+ });
290
+
291
+ describe('Resume button visibility logic', () => {
292
+ it('shows resume for failed workflow on in_progress card', () => {
293
+ const card = { column: 'in_progress' };
294
+ const workflowStatus = 'failed';
295
+ const isActive = workflowStatus === 'running';
296
+ const showResume = ['in_progress', 'in_review'].includes(card.column)
297
+ && !isActive
298
+ && workflowStatus === 'failed';
299
+ assert.equal(showResume, true);
300
+ });
301
+
302
+ it('shows resume for failed workflow on in_review card', () => {
303
+ const card = { column: 'in_review' };
304
+ const workflowStatus = 'failed';
305
+ const isActive = workflowStatus === 'running';
306
+ const showResume = ['in_progress', 'in_review'].includes(card.column)
307
+ && !isActive
308
+ && workflowStatus === 'failed';
309
+ assert.equal(showResume, true);
310
+ });
311
+
312
+ it('hides resume for running workflow', () => {
313
+ const card = { column: 'in_progress' };
314
+ const workflowStatus = 'running';
315
+ const isActive = workflowStatus === 'running';
316
+ const showResume = ['in_progress', 'in_review'].includes(card.column)
317
+ && !isActive
318
+ && workflowStatus === 'failed';
319
+ assert.equal(showResume, false);
320
+ });
321
+
322
+ it('hides resume for todo card', () => {
323
+ const card = { column: 'todo' };
324
+ const workflowStatus = 'failed';
325
+ const isActive = workflowStatus === 'running';
326
+ const showResume = ['in_progress', 'in_review'].includes(card.column)
327
+ && !isActive
328
+ && workflowStatus === 'failed';
329
+ assert.equal(showResume, false);
330
+ });
331
+
332
+ it('hides resume for completed workflow', () => {
333
+ const card = { column: 'in_progress' };
334
+ const workflowStatus = 'completed';
335
+ const isActive = workflowStatus === 'running';
336
+ const showResume = ['in_progress', 'in_review'].includes(card.column)
337
+ && !isActive
338
+ && workflowStatus === 'failed';
339
+ assert.equal(showResume, false);
340
+ });
341
+ });
@@ -226,8 +226,8 @@ npx tsx packages/shared/tools/search_nodes.ts --project-id <project_id> --comple
226
226
  npx tsx packages/shared/tools/get_node.ts <node_id> --project-id <project_id>
227
227
  npx tsx packages/shared/tools/get_node.ts --name "Node Name" --project-id <project_id>
228
228
  ```
229
- Returns full .md content plus a Relations section listing all connected nodes
230
- with direction, relation type, name, type, and status.
229
+ Returns the node content (formatted as markdown) plus a Relations section listing
230
+ all connected nodes with direction, relation type, name, type, and status.
231
231
 
232
232
  ### add_node
233
233
  ```
@@ -314,7 +314,7 @@ The `search_nodes --query` tool returns graph-aware relevance-ranked results.
314
314
  - **relates_to** — loose association (weakest)
315
315
 
316
316
  ## Rules
317
- 1. Never read or write graph.tson or .md files directly — always use the tools
317
+ 1. All graph data is stored in SQLite (via Drizzle ORM) — always use the tools above to read and write data
318
318
  2. Always call get_node before update_node — never update blind
319
319
  3. Always record answers via resolve_question — don't just add a note
320
320
  4. Nodes should reflect reality (codebase + user statements), not inferences —
@@ -119,7 +119,7 @@ npx tsx packages/shared/tools/move_card.ts <feature_id> todo --project-id <proje
119
119
  npx tsx packages/shared/tools/get_node.ts <node_id> --project-id <project_id>
120
120
  npx tsx packages/shared/tools/get_node.ts --name "Node Name" --project-id <project_id>
121
121
  ```
122
- Returns full .md content plus a Relations section listing all connected nodes with direction, relation type, name, type, and status.
122
+ Returns the node content (formatted as markdown) plus a Relations section listing all connected nodes with direction, relation type, name, type, and status.
123
123
 
124
124
  ### search_nodes
125
125
  ```
@@ -148,7 +148,7 @@ npx tsx packages/shared/tools/get_status.ts --project-id <project_id>
148
148
  2. Never approve without reading the full spec, all connected nodes, and related done features first
149
149
  3. Never approve without running tests
150
150
  4. Always provide actionable rejection notes — reference specific acceptance criteria, integration issues, or connected node requirements
151
- 5. Never read or write graph.tson, kanban.tson, or node .md files directly — always use the tools
151
+ 5. All graph data is stored in SQLite (via Drizzle ORM) — always use the tools above to read and write data
152
152
  6. If you find a spec gap during review, add an open question via `update_node` and flag it to the user
153
153
  7. All tool commands must be run from the `assistkick-product-system/` directory
154
154
  8. Review one feature at a time — complete the verdict before moving to the next
@@ -156,7 +156,7 @@ npx tsx packages/shared/tools/move_card.ts <feature_id> todo --project-id <proje
156
156
  npx tsx packages/shared/tools/get_node.ts <node_id> --project-id <project_id>
157
157
  npx tsx packages/shared/tools/get_node.ts --name "Node Name" --project-id <project_id>
158
158
  ```
159
- Returns full .md content plus a Relations section listing all connected nodes with direction, relation type, name, type, and status.
159
+ Returns the node content (formatted as markdown) plus a Relations section listing all connected nodes with direction, relation type, name, type, and status.
160
160
 
161
161
  ### search_nodes
162
162
  ```
@@ -201,7 +201,7 @@ npx tsx packages/shared/tools/get_status.ts --project-id <project_id>
201
201
 
202
202
  ## Rules
203
203
  1. Never write production code — you only read, run tests, add temporary debug logs, and investigate
204
- 2. Never read or write graph.tson, kanban.tson, or node .md files directly — always use the tools
204
+ 2. All graph data is stored in SQLite (via Drizzle ORM) — always use the tools above to read and write data
205
205
  3. Always call `get_node` on affected features AND their related nodes before investigating
206
206
  4. Always create a bugfix feature with detailed findings — the developer should be able to fix it without re-investigating
207
207
  5. Include specific file paths, line numbers, root cause analysis, and fix guidance in your bugfix notes
@@ -13,6 +13,9 @@ with the project spec and kanban exclusively through the tools below.
13
13
 
14
14
  All tools live in `assistkick-product-system/packages/shared/tools/` and are run with `npx tsx`.
15
15
 
16
+ ## References
17
+ - For React development best practices, see [react_development_guidelines.md](references/react_development_guidelines.md)
18
+
16
19
  ## Session Start Protocol
17
20
  1. Call `get_status` — understand current project state
18
21
  2. Call `get_kanban --column todo` — identify which features are available to implement
@@ -120,7 +123,7 @@ npx tsx packages/shared/tools/search_nodes.ts --project-id <project_id> --comple
120
123
  npx tsx packages/shared/tools/get_node.ts <node_id> --project-id <project_id>
121
124
  npx tsx packages/shared/tools/get_node.ts --name "Node Name" --project-id <project_id>
122
125
  ```
123
- Returns full .md content plus a Relations section listing all connected nodes with direction, relation type, name, type, and status.
126
+ Returns the node content (formatted as markdown) plus a Relations section listing all connected nodes with direction, relation type, name, type, and status.
124
127
 
125
128
  ### add_node
126
129
  ```
@@ -178,9 +181,9 @@ npx tsx packages/shared/tools/rebuild_index.ts --project-id <project_id> --dry-r
178
181
  ```
179
182
 
180
183
  ## Rules
181
- 1. Never read or write graph.tson or node .md files directly — always use the tools
184
+ 1. All graph data is stored in SQLite (via Drizzle ORM) — always use the tools above to read and write data
182
185
  2. Always call `get_node` on a feature AND its related nodes before implementing — understand the full context
183
- 3. Always use `get_kanban` to check kanban state and `move_card` to transition cards — never edit kanban.tson directly
186
+ 3. Always use `get_kanban` to check kanban state and `move_card` to transition cards
184
187
  4. Call `move_card <feat_id> in_progress` before starting work, `move_card <feat_id> in_review` when done
185
188
  5. Follow all coding standards from `nfr_001` — arrow functions, DI, single responsibility, node:test
186
189
  6. Write tests for every feature — test logic and data transformations
@@ -0,0 +1,225 @@
1
+ # React Development Guidelines (2026)
2
+
3
+ ## Core Principles
4
+
5
+ ### Use React 19+ Features
6
+ - **React Compiler** is the default — do not manually wrap in `useMemo`, `useCallback`, or `React.memo` unless profiling shows a specific need. The compiler handles memoization automatically.
7
+ - **Server Components** are the default rendering model. Only add `'use client'` when the component needs interactivity (event handlers, state, effects, browser APIs).
8
+ - **Server Actions** (`'use server'`) for mutations — prefer over custom API routes for form submissions and data mutations.
9
+ - **`use()` hook** for reading promises and context inside render. Replaces many patterns that previously required `useEffect` + `useState`.
10
+ - **`useActionState`** for form state management — replaces the old `useFormState` pattern.
11
+ - **`useOptimistic`** for optimistic UI updates during async transitions.
12
+ - **`<form action={...}>`** — use the action prop on forms for progressive enhancement.
13
+
14
+ ### Component Design
15
+ - **One component per file** — every component lives in its own file. No exceptions. If you need a helper component, extract it to a separate file.
16
+ - **Never define a component inside another component** — nested component definitions cause remounts on every render and destroy state. Extract inner components to their own files and import them.
17
+ - **Function components only** — never use class components.
18
+ - **Arrow function exports** — `export const MyComponent = () => { ... }`.
19
+ - **Props destructuring** in the parameter list — `({ title, onClose }) => ...`.
20
+ - **Co-locate related code** — keep component, types, and styles in the same directory.
21
+ - **Single responsibility** — one component does one thing. Extract when a component exceeds ~100 lines or has multiple concerns.
22
+ - **Composition over configuration** — prefer children and render props over deeply nested prop objects.
23
+ - **Always check the design system first** — before creating any new component, check if a matching component already exists in the project's design system. Reuse and extend existing design system components rather than building from scratch.
24
+
25
+ ### State Management
26
+ - **Local state first** — `useState` for component-scoped state.
27
+ - **Zustand** for shared client state — lightweight, no boilerplate, works well with React 19.
28
+ - One store per domain concern (e.g., `useAuthStore`, `useUIStore`).
29
+ - Use selectors to subscribe to specific slices: `useStore(state => state.count)`.
30
+ - Keep stores flat — avoid deeply nested state objects.
31
+ - Use `immer` middleware only when state updates are genuinely complex.
32
+ - **Do not use** `useReducer` for complex state — Zustand stores are simpler and more testable.
33
+ - **URL state** for filters, pagination, search — use `useSearchParams` or the router.
34
+ - **Server state** — use the framework's data loading (Server Components, loaders) rather than client-side fetching libraries when possible.
35
+
36
+ ### Data Fetching
37
+ - **Server Components** for initial data loading — fetch directly in the component, no `useEffect`.
38
+ - **Server Actions** for mutations — define with `'use server'`, call from client components.
39
+ - **`use()` hook** to unwrap promises passed from Server Components to Client Components.
40
+ - **Avoid `useEffect` for fetching** — it causes waterfalls, race conditions, and no caching. Use the framework's data loading primitives instead.
41
+ - **When client-side fetching is needed** (real-time, user-triggered), use `fetch` + `use()` or a minimal wrapper. Keep it simple.
42
+
43
+ ### Hooks
44
+ - **Custom hooks** for reusable logic — prefix with `use`, keep them focused on one concern.
45
+ - **`useRef`** for DOM references and mutable values that don't trigger re-renders.
46
+ - **`useEffect`** only for synchronization with external systems (DOM manipulation, subscriptions, timers). Never for data fetching or derived state.
47
+ - **`useId`** for generating unique IDs for accessibility attributes.
48
+ - **No `useEffect` chains** — if one effect sets state that triggers another effect, restructure the logic.
49
+
50
+ ### Styling — Tailwind CSS v4 Migration
51
+ - **All new components must use Tailwind CSS v4 classes** — do not create new `.css` files or add new CSS rules to existing stylesheets.
52
+ - **Migrate existing CSS when touching a component** — if you modify a component that uses CSS, convert its styles to Tailwind classes as part of the same change.
53
+ - **Use Tailwind utilities directly in JSX** — `className="flex items-center gap-2 p-4 rounded-lg bg-white"`.
54
+ - **Use `@apply` sparingly** — only when a utility combination is repeated across many elements in the same component and extraction to a shared component isn't practical.
55
+ - **Design tokens via Tailwind theme** — use theme values (`text-primary`, `bg-surface`) rather than hardcoded hex colors.
56
+
57
+ ### TypeScript
58
+ - **Strict mode always** — `strict: true` in tsconfig.
59
+ - **Interface for component props** — `interface Props { ... }` defined above the component.
60
+ - **Avoid `any`** — use `unknown` and narrow, or define proper types.
61
+ - **Infer when possible** — don't annotate what TypeScript can infer (return types, simple variables).
62
+ - **Discriminated unions** for variant props — not boolean flags.
63
+ - **`satisfies`** operator for type-safe object literals while preserving inference.
64
+ - **Generic components** when the component works with multiple data shapes.
65
+
66
+ ## Patterns
67
+
68
+ ### File & Directory Structure
69
+ ```
70
+ src/
71
+ components/ # Shared UI components
72
+ button/
73
+ button.tsx
74
+ button.test.tsx
75
+ features/ # Feature-specific modules
76
+ auth/
77
+ components/
78
+ hooks/
79
+ stores/
80
+ actions/
81
+ lib/ # Utilities and helpers
82
+ types/ # Shared type definitions
83
+ ```
84
+
85
+ ### Component File Structure
86
+ ```tsx
87
+ // 1. Imports
88
+ import { useState } from 'react'
89
+ import { Button } from '@/components/button/button'
90
+
91
+ // 2. Types
92
+ interface Props {
93
+ title: string
94
+ onClose: () => void
95
+ }
96
+
97
+ // 3. Component
98
+ export const Dialog = ({ title, onClose }: Props) => {
99
+ const [isOpen, setIsOpen] = useState(false)
100
+
101
+ return (
102
+ <div role="dialog" aria-label={title}>
103
+ <h2>{title}</h2>
104
+ <Button onClick={onClose}>Close</Button>
105
+ </div>
106
+ )
107
+ }
108
+ ```
109
+
110
+ ### Zustand Store Pattern
111
+ ```tsx
112
+ import { create } from 'zustand'
113
+
114
+ interface AuthState {
115
+ user: User | null
116
+ isLoading: boolean
117
+ login: (credentials: Credentials) => Promise<void>
118
+ logout: () => void
119
+ }
120
+
121
+ export const useAuthStore = create<AuthState>((set) => ({
122
+ user: null,
123
+ isLoading: false,
124
+ login: async (credentials) => {
125
+ set({ isLoading: true })
126
+ const user = await authApi.login(credentials)
127
+ set({ user, isLoading: false })
128
+ },
129
+ logout: () => set({ user: null }),
130
+ }))
131
+
132
+ // Usage — always use selectors
133
+ const user = useAuthStore((s) => s.user)
134
+ ```
135
+
136
+ ### Server Component + Client Component Split
137
+ ```tsx
138
+ // page.tsx (Server Component — no 'use client')
139
+ import { db } from '@/lib/db'
140
+ import { ItemList } from './item_list'
141
+
142
+ export const Page = async () => {
143
+ const items = await db.query.items.findMany()
144
+ return <ItemList items={items} />
145
+ }
146
+
147
+ // item_list.tsx (Client Component — needs interactivity)
148
+ 'use client'
149
+ import { useState } from 'react'
150
+
151
+ interface Props {
152
+ items: Item[]
153
+ }
154
+
155
+ export const ItemList = ({ items }: Props) => {
156
+ const [filter, setFilter] = useState('')
157
+ const filtered = items.filter((i) => i.name.includes(filter))
158
+
159
+ return (
160
+ <div>
161
+ <input value={filter} onChange={(e) => setFilter(e.target.value)} />
162
+ {filtered.map((item) => <div key={item.id}>{item.name}</div>)}
163
+ </div>
164
+ )
165
+ }
166
+ ```
167
+
168
+ ### Form with Server Action
169
+ ```tsx
170
+ // actions.ts
171
+ 'use server'
172
+ import { db } from '@/lib/db'
173
+
174
+ export const createItem = async (formData: FormData) => {
175
+ const name = formData.get('name') as string
176
+ await db.insert(items).values({ name })
177
+ }
178
+
179
+ // form.tsx
180
+ 'use client'
181
+ import { useActionState } from 'react'
182
+ import { createItem } from './actions'
183
+
184
+ export const CreateItemForm = () => {
185
+ const [state, action, isPending] = useActionState(createItem, null)
186
+
187
+ return (
188
+ <form action={action}>
189
+ <input name="name" required />
190
+ <button type="submit" disabled={isPending}>
191
+ {isPending ? 'Creating...' : 'Create'}
192
+ </button>
193
+ </form>
194
+ )
195
+ }
196
+ ```
197
+
198
+ ## Anti-Patterns to Avoid
199
+
200
+ - **No `useEffect` for derived state** — compute during render instead.
201
+ - **No prop drilling beyond 2 levels** — use Zustand or composition.
202
+ - **No `index` as `key`** in dynamic lists — use stable unique IDs.
203
+ - **No barrel files** (`index.ts` re-exports) — they break tree-shaking and slow builds.
204
+ - **No default exports** — use named exports for better refactoring and imports.
205
+ - **No `React.FC`** — just type the props directly, it's simpler and more flexible.
206
+ - **No manual memoization** unless the React Compiler is explicitly disabled and profiling shows a need.
207
+ - **No `useEffect` + `setState` for data fetching** — use Server Components or the framework's data loading.
208
+ - **No new CSS files** — we are incrementally migrating from CSS to Tailwind CSS v4. All new components must use Tailwind classes exclusively. When touching existing components that use CSS, migrate their styles to Tailwind as part of the change.
209
+ - **No `any` type** — use `unknown` and narrow, or define the actual type.
210
+
211
+ ## Accessibility Baseline
212
+ - Every interactive element is keyboard accessible.
213
+ - Images have `alt` text; decorative images use `alt=""`.
214
+ - Form inputs have associated `<label>` elements.
215
+ - Use semantic HTML (`<nav>`, `<main>`, `<article>`, `<button>`) — not `div` with `onClick`.
216
+ - Color is never the sole indicator of state.
217
+ - Use `useId()` for dynamic `id`/`aria-*` attributes.
218
+
219
+ ## Performance
220
+ - **React Compiler handles memoization** — trust it, don't fight it.
221
+ - **Lazy load routes and heavy components** — `React.lazy()` + `Suspense`.
222
+ - **Virtualize long lists** — use `@tanstack/react-virtual` for lists > 100 items.
223
+ - **Avoid layout thrashing** — batch DOM reads/writes, use `useLayoutEffect` only when needed.
224
+ - **Image optimization** — use the framework's `<Image>` component or `loading="lazy"`.
225
+ - **Code split at the route level** — each route loads only what it needs.
@@ -94,7 +94,7 @@ npx tsx packages/shared/tools/search_nodes.ts --project-id <project_id> --comple
94
94
  npx tsx packages/shared/tools/get_node.ts <node_id> --project-id <project_id>
95
95
  npx tsx packages/shared/tools/get_node.ts --name "Node Name" --project-id <project_id>
96
96
  ```
97
- Returns full .md content plus a Relations section listing all connected nodes with direction, relation type, name, type, and status.
97
+ Returns the node content (formatted as markdown) plus a Relations section listing all connected nodes with direction, relation type, name, type, and status.
98
98
 
99
99
  ### add_node
100
100
  ```
@@ -219,7 +219,7 @@ The user's time is for product decisions, not answering questions about their
219
219
  own codebase that you have full access to.
220
220
 
221
221
  ## Rules
222
- 1. Never read or write graph.tson or .md files directly — always use the tools
222
+ 1. All graph data is stored in SQLite (via Drizzle ORM) — always use the tools above to read and write data
223
223
  2. Always call get_node before update_node — never update blind
224
224
  3. Always record answers via resolve_question — don't just add a note
225
225
  4. Nodes you create should reflect what the user actually said, not your inferences — use assumption node type for inferences