@superblocksteam/vite-plugin-file-sync 2.0.170 → 2.0.171-next.1

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 (507) hide show
  1. package/dist/ai-service/agent/prompts/api-prompts.d.ts.map +1 -1
  2. package/dist/ai-service/agent/prompts/api-prompts.js +4 -6
  3. package/dist/ai-service/agent/prompts/api-prompts.js.map +1 -1
  4. package/dist/ai-service/agent/prompts/build-base-system-prompt.d.ts +3 -12
  5. package/dist/ai-service/agent/prompts/build-base-system-prompt.d.ts.map +1 -1
  6. package/dist/ai-service/agent/prompts/build-base-system-prompt.js +144 -71
  7. package/dist/ai-service/agent/prompts/build-base-system-prompt.js.map +1 -1
  8. package/dist/ai-service/agent/subagents/testing/types.d.ts +2 -1
  9. package/dist/ai-service/agent/subagents/testing/types.d.ts.map +1 -1
  10. package/dist/ai-service/agent/tool-message-utils.d.ts.map +1 -1
  11. package/dist/ai-service/agent/tool-message-utils.js +47 -36
  12. package/dist/ai-service/agent/tool-message-utils.js.map +1 -1
  13. package/dist/ai-service/agent/tool-result-metrics.d.ts +15 -0
  14. package/dist/ai-service/agent/tool-result-metrics.d.ts.map +1 -1
  15. package/dist/ai-service/agent/tool-result-metrics.js +28 -0
  16. package/dist/ai-service/agent/tool-result-metrics.js.map +1 -1
  17. package/dist/ai-service/agent/tools/apis/analysis.d.ts +24 -2
  18. package/dist/ai-service/agent/tools/apis/analysis.d.ts.map +1 -1
  19. package/dist/ai-service/agent/tools/apis/analysis.js +164 -11
  20. package/dist/ai-service/agent/tools/apis/analysis.js.map +1 -1
  21. package/dist/ai-service/agent/tools/apis/api-comparator.d.ts +2 -1
  22. package/dist/ai-service/agent/tools/apis/api-comparator.d.ts.map +1 -1
  23. package/dist/ai-service/agent/tools/apis/contained-source-reader.d.ts +11 -0
  24. package/dist/ai-service/agent/tools/apis/contained-source-reader.d.ts.map +1 -0
  25. package/dist/ai-service/agent/tools/apis/contained-source-reader.js +48 -0
  26. package/dist/ai-service/agent/tools/apis/contained-source-reader.js.map +1 -0
  27. package/dist/ai-service/agent/tools/apis/get-api-docs.d.ts +1 -1
  28. package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.d.ts +37 -10
  29. package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.d.ts.map +1 -1
  30. package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.js +140 -70
  31. package/dist/ai-service/agent/tools/apis/get-sdk-api-docs.js.map +1 -1
  32. package/dist/ai-service/agent/tools/apis/spreadsheet-csv.d.ts +6 -20
  33. package/dist/ai-service/agent/tools/apis/spreadsheet-csv.d.ts.map +1 -1
  34. package/dist/ai-service/agent/tools/apis/spreadsheet-csv.js +3 -3
  35. package/dist/ai-service/agent/tools/apis/spreadsheet-csv.js.map +1 -1
  36. package/dist/ai-service/agent/tools/apis/test-api-metrics.d.ts +8 -0
  37. package/dist/ai-service/agent/tools/apis/test-api-metrics.d.ts.map +1 -0
  38. package/dist/ai-service/agent/tools/apis/test-api-metrics.js +41 -0
  39. package/dist/ai-service/agent/tools/apis/test-api-metrics.js.map +1 -0
  40. package/dist/ai-service/agent/tools/apis/test-api-timing.d.ts +27 -0
  41. package/dist/ai-service/agent/tools/apis/test-api-timing.d.ts.map +1 -0
  42. package/dist/ai-service/agent/tools/apis/test-api-timing.js +25 -0
  43. package/dist/ai-service/agent/tools/apis/test-api-timing.js.map +1 -0
  44. package/dist/ai-service/agent/tools/apis/test-api.d.ts +6 -2
  45. package/dist/ai-service/agent/tools/apis/test-api.d.ts.map +1 -1
  46. package/dist/ai-service/agent/tools/apis/test-api.js +119 -20
  47. package/dist/ai-service/agent/tools/apis/test-api.js.map +1 -1
  48. package/dist/ai-service/agent/tools/apis/write-api.d.ts +2 -2
  49. package/dist/ai-service/agent/tools/build-capture-screenshot.d.ts +0 -18
  50. package/dist/ai-service/agent/tools/build-capture-screenshot.d.ts.map +1 -1
  51. package/dist/ai-service/agent/tools/build-capture-screenshot.js +2 -2
  52. package/dist/ai-service/agent/tools/build-capture-screenshot.js.map +1 -1
  53. package/dist/ai-service/agent/tools/build-copy-directory.d.ts +2 -2
  54. package/dist/ai-service/agent/tools/build-copy-file.d.ts +1 -1
  55. package/dist/ai-service/agent/tools/build-copy-utils.d.ts +8 -8
  56. package/dist/ai-service/agent/tools/build-delete-file.d.ts +1 -1
  57. package/dist/ai-service/agent/tools/build-install-packages.d.ts +32 -5
  58. package/dist/ai-service/agent/tools/build-install-packages.d.ts.map +1 -1
  59. package/dist/ai-service/agent/tools/build-install-packages.js +20 -19
  60. package/dist/ai-service/agent/tools/build-install-packages.js.map +1 -1
  61. package/dist/ai-service/agent/tools/build-manage-checklist.d.ts +3 -3
  62. package/dist/ai-service/agent/tools/build-manage-packages.d.ts +19 -0
  63. package/dist/ai-service/agent/tools/build-manage-packages.d.ts.map +1 -0
  64. package/dist/ai-service/agent/tools/build-manage-packages.js +220 -0
  65. package/dist/ai-service/agent/tools/build-manage-packages.js.map +1 -0
  66. package/dist/ai-service/agent/tools/build-read-file.d.ts +5 -14
  67. package/dist/ai-service/agent/tools/build-read-file.d.ts.map +1 -1
  68. package/dist/ai-service/agent/tools/build-read-file.js +5 -8
  69. package/dist/ai-service/agent/tools/build-read-file.js.map +1 -1
  70. package/dist/ai-service/agent/tools/databases/dev-database-tasks.d.ts +10 -98
  71. package/dist/ai-service/agent/tools/databases/dev-database-tasks.d.ts.map +1 -1
  72. package/dist/ai-service/agent/tools/databases/dev-database-tasks.js +1 -2
  73. package/dist/ai-service/agent/tools/databases/dev-database-tasks.js.map +1 -1
  74. package/dist/ai-service/agent/tools/debug-cache.d.ts +0 -12
  75. package/dist/ai-service/agent/tools/debug-cache.d.ts.map +1 -1
  76. package/dist/ai-service/agent/tools/debug-cache.js +1 -1
  77. package/dist/ai-service/agent/tools/debug-cache.js.map +1 -1
  78. package/dist/ai-service/agent/tools/get-logs.d.ts +3 -3
  79. package/dist/ai-service/agent/tools/get-security-findings.d.ts +1 -1
  80. package/dist/ai-service/agent/tools/index.d.ts +3 -3
  81. package/dist/ai-service/agent/tools/index.d.ts.map +1 -1
  82. package/dist/ai-service/agent/tools/index.js +2 -2
  83. package/dist/ai-service/agent/tools/index.js.map +1 -1
  84. package/dist/ai-service/agent/tools/integrations/errors.d.ts.map +1 -1
  85. package/dist/ai-service/agent/tools/integrations/errors.js +3 -0
  86. package/dist/ai-service/agent/tools/integrations/errors.js.map +1 -1
  87. package/dist/ai-service/agent/tools/integrations/execute-request.d.ts +42 -15
  88. package/dist/ai-service/agent/tools/integrations/execute-request.d.ts.map +1 -1
  89. package/dist/ai-service/agent/tools/integrations/execute-request.js +85 -11
  90. package/dist/ai-service/agent/tools/integrations/execute-request.js.map +1 -1
  91. package/dist/ai-service/agent/tools/integrations/managed-app-database.d.ts +0 -2
  92. package/dist/ai-service/agent/tools/integrations/managed-app-database.d.ts.map +1 -1
  93. package/dist/ai-service/agent/tools/integrations/managed-app-database.js +1 -1
  94. package/dist/ai-service/agent/tools/integrations/managed-app-database.js.map +1 -1
  95. package/dist/ai-service/agent/tools/integrations/metadata.d.ts.map +1 -1
  96. package/dist/ai-service/agent/tools/integrations/metadata.js +1 -0
  97. package/dist/ai-service/agent/tools/integrations/metadata.js.map +1 -1
  98. package/dist/ai-service/agent/tools/read-state-trackers.d.ts +16 -2
  99. package/dist/ai-service/agent/tools/read-state-trackers.d.ts.map +1 -1
  100. package/dist/ai-service/agent/tools/read-state-trackers.js +11 -2
  101. package/dist/ai-service/agent/tools/read-state-trackers.js.map +1 -1
  102. package/dist/ai-service/agent/tools/report-security-findings.d.ts +18 -18
  103. package/dist/ai-service/agent/tools.d.ts.map +1 -1
  104. package/dist/ai-service/agent/tools.js +13 -6
  105. package/dist/ai-service/agent/tools.js.map +1 -1
  106. package/dist/ai-service/agent/tools2/registry.d.ts +20 -1
  107. package/dist/ai-service/agent/tools2/registry.d.ts.map +1 -1
  108. package/dist/ai-service/agent/tools2/registry.js +55 -9
  109. package/dist/ai-service/agent/tools2/registry.js.map +1 -1
  110. package/dist/ai-service/agent/tools2/tools/bash.d.ts +1 -1
  111. package/dist/ai-service/agent/tools2/tools/git.d.ts +2 -2
  112. package/dist/ai-service/agent/tools2/tools/grep-metadata-ripgrep.d.ts.map +1 -1
  113. package/dist/ai-service/agent/tools2/tools/grep-metadata-ripgrep.js +25 -0
  114. package/dist/ai-service/agent/tools2/tools/grep-metadata-ripgrep.js.map +1 -1
  115. package/dist/ai-service/agent/tools2/tools/grep-metadata.d.ts +9 -3
  116. package/dist/ai-service/agent/tools2/tools/grep-metadata.d.ts.map +1 -1
  117. package/dist/ai-service/agent/tools2/tools/grep-metadata.js +17 -0
  118. package/dist/ai-service/agent/tools2/tools/grep-metadata.js.map +1 -1
  119. package/dist/ai-service/agent/tools2/tools/grep.d.ts +1 -1
  120. package/dist/ai-service/agent/tools2/tools/grep.d.ts.map +1 -1
  121. package/dist/ai-service/agent/tools2/tools/grep.js +50 -25
  122. package/dist/ai-service/agent/tools2/tools/grep.js.map +1 -1
  123. package/dist/ai-service/agent/tools2/tools/list-attachments.d.ts +1 -1
  124. package/dist/ai-service/agent/utils.d.ts.map +1 -1
  125. package/dist/ai-service/agent/utils.js +3 -3
  126. package/dist/ai-service/agent/utils.js.map +1 -1
  127. package/dist/ai-service/app-interface/file-system-interface.d.ts +0 -6
  128. package/dist/ai-service/app-interface/file-system-interface.d.ts.map +1 -1
  129. package/dist/ai-service/app-interface/file-system-interface.js +39 -97
  130. package/dist/ai-service/app-interface/file-system-interface.js.map +1 -1
  131. package/dist/ai-service/app-interface/filesystem/handlers/skill-handler.d.ts.map +1 -1
  132. package/dist/ai-service/app-interface/filesystem/handlers/skill-handler.js +2 -2
  133. package/dist/ai-service/app-interface/filesystem/handlers/skill-handler.js.map +1 -1
  134. package/dist/ai-service/app-interface/filesystem/validation.js +1 -1
  135. package/dist/ai-service/app-interface/filesystem/validation.js.map +1 -1
  136. package/dist/ai-service/app-interface/npm-package-lookup.d.ts +1 -3
  137. package/dist/ai-service/app-interface/npm-package-lookup.d.ts.map +1 -1
  138. package/dist/ai-service/app-interface/npm-package-lookup.js +1 -1
  139. package/dist/ai-service/app-interface/npm-package-lookup.js.map +1 -1
  140. package/dist/ai-service/app-interface/pnpm-lockfile.d.ts.map +1 -1
  141. package/dist/ai-service/app-interface/pnpm-lockfile.js +2 -4
  142. package/dist/ai-service/app-interface/pnpm-lockfile.js.map +1 -1
  143. package/dist/ai-service/app-interface/shell.d.ts +38 -15
  144. package/dist/ai-service/app-interface/shell.d.ts.map +1 -1
  145. package/dist/ai-service/app-interface/shell.js +646 -48
  146. package/dist/ai-service/app-interface/shell.js.map +1 -1
  147. package/dist/ai-service/app-interface/vulnerability-gate.d.ts +7 -0
  148. package/dist/ai-service/app-interface/vulnerability-gate.d.ts.map +1 -1
  149. package/dist/ai-service/app-interface/vulnerability-gate.js +16 -6
  150. package/dist/ai-service/app-interface/vulnerability-gate.js.map +1 -1
  151. package/dist/ai-service/app-layout.d.ts +3 -0
  152. package/dist/ai-service/app-layout.d.ts.map +1 -1
  153. package/dist/ai-service/app-layout.js +14 -0
  154. package/dist/ai-service/app-layout.js.map +1 -1
  155. package/dist/ai-service/app-skills/parser.d.ts +2 -1
  156. package/dist/ai-service/app-skills/parser.d.ts.map +1 -1
  157. package/dist/ai-service/attachments/store.d.ts +4 -4
  158. package/dist/ai-service/attachments/store.d.ts.map +1 -1
  159. package/dist/ai-service/chat/chat-session-store-metrics.d.ts +1 -2
  160. package/dist/ai-service/chat/chat-session-store-metrics.d.ts.map +1 -1
  161. package/dist/ai-service/chat/chat-session-store-metrics.js +0 -6
  162. package/dist/ai-service/chat/chat-session-store-metrics.js.map +1 -1
  163. package/dist/ai-service/chat/chat-session-store.d.ts +2 -11
  164. package/dist/ai-service/chat/chat-session-store.d.ts.map +1 -1
  165. package/dist/ai-service/chat/chat-session-store.js +21 -4
  166. package/dist/ai-service/chat/chat-session-store.js.map +1 -1
  167. package/dist/ai-service/chat/utils.d.ts +1 -2
  168. package/dist/ai-service/chat/utils.d.ts.map +1 -1
  169. package/dist/ai-service/chat/utils.js +0 -1
  170. package/dist/ai-service/chat/utils.js.map +1 -1
  171. package/dist/ai-service/checklist/persisted-checklist-store.d.ts +3 -3
  172. package/dist/ai-service/checklist/persisted-checklist-store.d.ts.map +1 -1
  173. package/dist/ai-service/checklist/persisted-checklist-store.js +4 -2
  174. package/dist/ai-service/checklist/persisted-checklist-store.js.map +1 -1
  175. package/dist/ai-service/checklist/remediation-findings-client.d.ts +1 -1
  176. package/dist/ai-service/context/app-context.d.ts +7 -39
  177. package/dist/ai-service/context/app-context.d.ts.map +1 -1
  178. package/dist/ai-service/context/app-context.js +29 -110
  179. package/dist/ai-service/context/app-context.js.map +1 -1
  180. package/dist/ai-service/context-clear-metrics.d.ts +11 -0
  181. package/dist/ai-service/context-clear-metrics.d.ts.map +1 -0
  182. package/dist/ai-service/context-clear-metrics.js +64 -0
  183. package/dist/ai-service/context-clear-metrics.js.map +1 -0
  184. package/dist/ai-service/context-download.js +2 -2
  185. package/dist/ai-service/context-download.js.map +1 -1
  186. package/dist/ai-service/edit-after-clark.d.ts +0 -5
  187. package/dist/ai-service/edit-after-clark.d.ts.map +1 -1
  188. package/dist/ai-service/edit-after-clark.js +1 -1
  189. package/dist/ai-service/edit-after-clark.js.map +1 -1
  190. package/dist/ai-service/features.d.ts +20 -4
  191. package/dist/ai-service/features.d.ts.map +1 -1
  192. package/dist/ai-service/features.js +20 -4
  193. package/dist/ai-service/features.js.map +1 -1
  194. package/dist/ai-service/filter-disabled-tools-for-migration.js +1 -1
  195. package/dist/ai-service/filter-disabled-tools-for-migration.js.map +1 -1
  196. package/dist/ai-service/index.d.ts +58 -0
  197. package/dist/ai-service/index.d.ts.map +1 -1
  198. package/dist/ai-service/index.js +186 -21
  199. package/dist/ai-service/index.js.map +1 -1
  200. package/dist/ai-service/integrations/store.d.ts +6 -0
  201. package/dist/ai-service/integrations/store.d.ts.map +1 -1
  202. package/dist/ai-service/integrations/store.js +101 -3
  203. package/dist/ai-service/integrations/store.js.map +1 -1
  204. package/dist/ai-service/judge/tools/playwright-action.d.ts +1 -1
  205. package/dist/ai-service/judge/tools/submit-feedback.d.ts +1 -1
  206. package/dist/ai-service/live-edit-events/forwarder.d.ts +1 -12
  207. package/dist/ai-service/live-edit-events/forwarder.d.ts.map +1 -1
  208. package/dist/ai-service/live-edit-events/forwarder.js +1 -7
  209. package/dist/ai-service/live-edit-events/forwarder.js.map +1 -1
  210. package/dist/ai-service/llm/context-v2/compaction/client-side.d.ts.map +1 -1
  211. package/dist/ai-service/llm/context-v2/compaction/client-side.js +15 -5
  212. package/dist/ai-service/llm/context-v2/compaction/client-side.js.map +1 -1
  213. package/dist/ai-service/llm/context-v2/compaction/compaction-strategy.d.ts +6 -0
  214. package/dist/ai-service/llm/context-v2/compaction/compaction-strategy.d.ts.map +1 -1
  215. package/dist/ai-service/llm/context-v2/compaction/server-side.d.ts.map +1 -1
  216. package/dist/ai-service/llm/context-v2/compaction/server-side.js +5 -1
  217. package/dist/ai-service/llm/context-v2/compaction/server-side.js.map +1 -1
  218. package/dist/ai-service/llm/context-v2/compactor.d.ts.map +1 -1
  219. package/dist/ai-service/llm/context-v2/compactor.js +34 -12
  220. package/dist/ai-service/llm/context-v2/compactor.js.map +1 -1
  221. package/dist/ai-service/llm/context-v2/context-management.d.ts.map +1 -1
  222. package/dist/ai-service/llm/context-v2/context-management.js +9 -0
  223. package/dist/ai-service/llm/context-v2/context-management.js.map +1 -1
  224. package/dist/ai-service/llm/context-v2/context-metrics.d.ts +4 -0
  225. package/dist/ai-service/llm/context-v2/context-metrics.d.ts.map +1 -1
  226. package/dist/ai-service/llm/context-v2/context-metrics.js +15 -3
  227. package/dist/ai-service/llm/context-v2/context-metrics.js.map +1 -1
  228. package/dist/ai-service/llm/context-v2/context.d.ts +36 -6
  229. package/dist/ai-service/llm/context-v2/context.d.ts.map +1 -1
  230. package/dist/ai-service/llm/context-v2/context.js +72 -28
  231. package/dist/ai-service/llm/context-v2/context.js.map +1 -1
  232. package/dist/ai-service/llm/context-v2/manager.d.ts +3 -3
  233. package/dist/ai-service/llm/context-v2/manager.d.ts.map +1 -1
  234. package/dist/ai-service/llm/context-v2/manager.js +13 -7
  235. package/dist/ai-service/llm/context-v2/manager.js.map +1 -1
  236. package/dist/ai-service/llm/context-v2/migrations/types.d.ts +2 -1
  237. package/dist/ai-service/llm/context-v2/migrations/types.d.ts.map +1 -1
  238. package/dist/ai-service/llm/context-v2/storage/event-types.d.ts +17 -17
  239. package/dist/ai-service/llm/context-v2/storage/event-types.d.ts.map +1 -1
  240. package/dist/ai-service/llm/context-v2/storage/event-types.js +3 -23
  241. package/dist/ai-service/llm/context-v2/storage/event-types.js.map +1 -1
  242. package/dist/ai-service/llm/context-v2/types.d.ts +9 -1
  243. package/dist/ai-service/llm/context-v2/types.d.ts.map +1 -1
  244. package/dist/ai-service/llm/context-v2/types.js.map +1 -1
  245. package/dist/ai-service/llm/context-v2/uniquify-filenames.d.ts +20 -4
  246. package/dist/ai-service/llm/context-v2/uniquify-filenames.d.ts.map +1 -1
  247. package/dist/ai-service/llm/context-v2/uniquify-filenames.js +128 -29
  248. package/dist/ai-service/llm/context-v2/uniquify-filenames.js.map +1 -1
  249. package/dist/ai-service/llm/error.d.ts +1 -1
  250. package/dist/ai-service/llm/error.d.ts.map +1 -1
  251. package/dist/ai-service/llm/error.js +57 -7
  252. package/dist/ai-service/llm/error.js.map +1 -1
  253. package/dist/ai-service/llm/stream/config.d.ts +3 -3
  254. package/dist/ai-service/llm/stream/config.d.ts.map +1 -1
  255. package/dist/ai-service/llm/stream/errors.d.ts +3 -0
  256. package/dist/ai-service/llm/stream/errors.d.ts.map +1 -1
  257. package/dist/ai-service/llm/stream/errors.js +72 -4
  258. package/dist/ai-service/llm/stream/errors.js.map +1 -1
  259. package/dist/ai-service/llm/stream/observers/workflow-usage.d.ts +0 -6
  260. package/dist/ai-service/llm/stream/observers/workflow-usage.d.ts.map +1 -1
  261. package/dist/ai-service/llm/stream/observers/workflow-usage.js +2 -2
  262. package/dist/ai-service/llm/stream/observers/workflow-usage.js.map +1 -1
  263. package/dist/ai-service/llm/stream/orchestrator.d.ts.map +1 -1
  264. package/dist/ai-service/llm/stream/orchestrator.js +6 -1
  265. package/dist/ai-service/llm/stream/orchestrator.js.map +1 -1
  266. package/dist/ai-service/managed-native-db-availability.d.ts +5 -2
  267. package/dist/ai-service/managed-native-db-availability.d.ts.map +1 -1
  268. package/dist/ai-service/managed-native-db-availability.js +5 -1
  269. package/dist/ai-service/managed-native-db-availability.js.map +1 -1
  270. package/dist/ai-service/mcp/embedded-playwright-mcp-server.js +2 -0
  271. package/dist/ai-service/mcp/embedded-playwright-mcp-server.js.map +1 -1
  272. package/dist/ai-service/migration-prompt-gating.d.ts +1 -0
  273. package/dist/ai-service/migration-prompt-gating.d.ts.map +1 -1
  274. package/dist/ai-service/migration-prompt-gating.js +3 -0
  275. package/dist/ai-service/migration-prompt-gating.js.map +1 -1
  276. package/dist/ai-service/migration-turns-log.d.ts +3 -2
  277. package/dist/ai-service/migration-turns-log.d.ts.map +1 -1
  278. package/dist/ai-service/migration-turns-log.js.map +1 -1
  279. package/dist/ai-service/policy-agent-metrics.d.ts +2 -4
  280. package/dist/ai-service/policy-agent-metrics.d.ts.map +1 -1
  281. package/dist/ai-service/policy-agent-metrics.js +3 -3
  282. package/dist/ai-service/policy-agent-metrics.js.map +1 -1
  283. package/dist/ai-service/policy-gate-remediation-usage.d.ts.map +1 -1
  284. package/dist/ai-service/policy-gate-remediation-usage.js +29 -19
  285. package/dist/ai-service/policy-gate-remediation-usage.js.map +1 -1
  286. package/dist/ai-service/prompt-builder-service/classifiers/prompt-interpret-task.js +1 -1
  287. package/dist/ai-service/prompt-builder-service/classifiers/prompt-interpret-task.js.map +1 -1
  288. package/dist/ai-service/recording/types.d.ts +6 -5
  289. package/dist/ai-service/recording/types.d.ts.map +1 -1
  290. package/dist/ai-service/skills/system/_registry.generated.d.ts.map +1 -1
  291. package/dist/ai-service/skills/system/_registry.generated.js +10 -0
  292. package/dist/ai-service/skills/system/_registry.generated.js.map +1 -1
  293. package/dist/ai-service/skills/system/app-database/skill.generated.d.ts +2 -0
  294. package/dist/ai-service/skills/system/app-database/skill.generated.d.ts.map +1 -0
  295. package/dist/ai-service/skills/system/app-database/skill.generated.js +79 -0
  296. package/dist/ai-service/skills/system/app-database/skill.generated.js.map +1 -0
  297. package/dist/ai-service/skills/system/resolve.d.ts +1 -1
  298. package/dist/ai-service/skills/system/resolve.d.ts.map +1 -1
  299. package/dist/ai-service/skills/system/resolve.js +25 -2
  300. package/dist/ai-service/skills/system/resolve.js.map +1 -1
  301. package/dist/ai-service/skills/system/spreadsheet-import/skill.generated.d.ts +1 -1
  302. package/dist/ai-service/skills/system/spreadsheet-import/skill.generated.d.ts.map +1 -1
  303. package/dist/ai-service/skills/system/spreadsheet-import/skill.generated.js +1 -1
  304. package/dist/ai-service/skills/system/superblocks-frontend/_variants/base-ui-plain.generated.d.ts +2 -0
  305. package/dist/ai-service/skills/system/superblocks-frontend/_variants/base-ui-plain.generated.d.ts.map +1 -0
  306. package/dist/ai-service/skills/system/superblocks-frontend/_variants/base-ui-plain.generated.js +5 -0
  307. package/dist/ai-service/skills/system/superblocks-frontend/_variants/base-ui-plain.generated.js.map +1 -0
  308. package/dist/ai-service/skills/system/superblocks-frontend/_variants/base-ui-registered.generated.d.ts +2 -0
  309. package/dist/ai-service/skills/system/superblocks-frontend/_variants/base-ui-registered.generated.d.ts.map +1 -0
  310. package/dist/ai-service/skills/system/superblocks-frontend/_variants/base-ui-registered.generated.js +5 -0
  311. package/dist/ai-service/skills/system/superblocks-frontend/_variants/base-ui-registered.generated.js.map +1 -0
  312. package/dist/ai-service/skills/system/superblocks-frontend/_variants/icons-lucide.generated.d.ts +2 -0
  313. package/dist/ai-service/skills/system/superblocks-frontend/_variants/icons-lucide.generated.d.ts.map +1 -0
  314. package/dist/ai-service/skills/system/superblocks-frontend/_variants/icons-lucide.generated.js +32 -0
  315. package/dist/ai-service/skills/system/superblocks-frontend/_variants/icons-lucide.generated.js.map +1 -0
  316. package/dist/ai-service/skills/system/superblocks-frontend/_variants/icons-wrapper.generated.d.ts +2 -0
  317. package/dist/ai-service/skills/system/superblocks-frontend/_variants/icons-wrapper.generated.d.ts.map +1 -0
  318. package/dist/ai-service/skills/system/superblocks-frontend/_variants/icons-wrapper.generated.js +34 -0
  319. package/dist/ai-service/skills/system/superblocks-frontend/_variants/icons-wrapper.generated.js.map +1 -0
  320. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.d.ts +1 -1
  321. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.d.ts.map +1 -1
  322. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.js +99 -68
  323. package/dist/ai-service/skills/system/superblocks-frontend/skill.generated.js.map +1 -1
  324. package/dist/ai-service/skills/system/superblocks-migration/skill.generated.d.ts +1 -1
  325. package/dist/ai-service/skills/system/superblocks-migration/skill.generated.d.ts.map +1 -1
  326. package/dist/ai-service/skills/system/superblocks-migration/skill.generated.js +4 -4
  327. package/dist/ai-service/skills/system/superblocks-sdk-api/skill.generated.d.ts +1 -1
  328. package/dist/ai-service/skills/system/superblocks-sdk-api/skill.generated.d.ts.map +1 -1
  329. package/dist/ai-service/skills/system/superblocks-sdk-api/skill.generated.js +7 -7
  330. package/dist/ai-service/skills/system/third-party-migration/skill.generated.d.ts +1 -1
  331. package/dist/ai-service/skills/system/third-party-migration/skill.generated.d.ts.map +1 -1
  332. package/dist/ai-service/skills/system/third-party-migration/skill.generated.js +1 -1
  333. package/dist/ai-service/state-machine/clark-fsm.d.ts +2 -3
  334. package/dist/ai-service/state-machine/clark-fsm.d.ts.map +1 -1
  335. package/dist/ai-service/state-machine/clark-fsm.js +1 -1
  336. package/dist/ai-service/state-machine/clark-fsm.js.map +1 -1
  337. package/dist/ai-service/state-machine/handlers/agent-planning.d.ts.map +1 -1
  338. package/dist/ai-service/state-machine/handlers/agent-planning.js +1 -4
  339. package/dist/ai-service/state-machine/handlers/agent-planning.js.map +1 -1
  340. package/dist/ai-service/state-machine/handlers/llm-generating.d.ts.map +1 -1
  341. package/dist/ai-service/state-machine/handlers/llm-generating.js +21 -12
  342. package/dist/ai-service/state-machine/handlers/llm-generating.js.map +1 -1
  343. package/dist/ai-service/state-machine/helpers/prompt-degradation-cause.d.ts +63 -0
  344. package/dist/ai-service/state-machine/helpers/prompt-degradation-cause.d.ts.map +1 -0
  345. package/dist/ai-service/state-machine/helpers/prompt-degradation-cause.js +96 -0
  346. package/dist/ai-service/state-machine/helpers/prompt-degradation-cause.js.map +1 -0
  347. package/dist/ai-service/state-machine/helpers/prompt-degradation-metrics.d.ts +12 -3
  348. package/dist/ai-service/state-machine/helpers/prompt-degradation-metrics.d.ts.map +1 -1
  349. package/dist/ai-service/state-machine/helpers/prompt-degradation-metrics.js +89 -17
  350. package/dist/ai-service/state-machine/helpers/prompt-degradation-metrics.js.map +1 -1
  351. package/dist/ai-service/state-machine/helpers/prompt-org-claims.d.ts +17 -0
  352. package/dist/ai-service/state-machine/helpers/prompt-org-claims.d.ts.map +1 -0
  353. package/dist/ai-service/state-machine/helpers/prompt-org-claims.js +50 -0
  354. package/dist/ai-service/state-machine/helpers/prompt-org-claims.js.map +1 -0
  355. package/dist/ai-service/state-machine/helpers/prompt-request-metrics.d.ts +7 -1
  356. package/dist/ai-service/state-machine/helpers/prompt-request-metrics.d.ts.map +1 -1
  357. package/dist/ai-service/state-machine/helpers/prompt-request-metrics.js +35 -5
  358. package/dist/ai-service/state-machine/helpers/prompt-request-metrics.js.map +1 -1
  359. package/dist/ai-service/state-machine/mocks.d.ts.map +1 -1
  360. package/dist/ai-service/state-machine/mocks.js +1 -4
  361. package/dist/ai-service/state-machine/mocks.js.map +1 -1
  362. package/dist/ai-service/tasks/peer-push.d.ts +2 -1
  363. package/dist/ai-service/tasks/peer-push.d.ts.map +1 -1
  364. package/dist/ai-service/test-utils/metrics-harness.d.ts +2 -1
  365. package/dist/ai-service/test-utils/metrics-harness.d.ts.map +1 -1
  366. package/dist/ai-service/util/llm-config-utils.d.ts +16 -5
  367. package/dist/ai-service/util/llm-config-utils.d.ts.map +1 -1
  368. package/dist/ai-service/util/llm-config-utils.js +25 -3
  369. package/dist/ai-service/util/llm-config-utils.js.map +1 -1
  370. package/dist/ai-service/util/parse-jwt.d.ts +5 -1
  371. package/dist/ai-service/util/parse-jwt.d.ts.map +1 -1
  372. package/dist/ai-service/util/parse-jwt.js +9 -1
  373. package/dist/ai-service/util/parse-jwt.js.map +1 -1
  374. package/dist/app-scope-source.d.ts +13 -0
  375. package/dist/app-scope-source.d.ts.map +1 -0
  376. package/dist/app-scope-source.js +68 -0
  377. package/dist/app-scope-source.js.map +1 -0
  378. package/dist/extract-api-integrations.d.ts +28 -6
  379. package/dist/extract-api-integrations.d.ts.map +1 -1
  380. package/dist/extract-api-integrations.js +166 -98
  381. package/dist/extract-api-integrations.js.map +1 -1
  382. package/dist/file-sync-vite-plugin.d.ts +1 -5
  383. package/dist/file-sync-vite-plugin.d.ts.map +1 -1
  384. package/dist/file-sync-vite-plugin.js +45 -71
  385. package/dist/file-sync-vite-plugin.js.map +1 -1
  386. package/dist/file-system-helpers.d.ts +3 -8
  387. package/dist/file-system-helpers.d.ts.map +1 -1
  388. package/dist/file-system-helpers.js +39 -10
  389. package/dist/file-system-helpers.js.map +1 -1
  390. package/dist/file-system-manager.d.ts +2 -9
  391. package/dist/file-system-manager.d.ts.map +1 -1
  392. package/dist/file-system-manager.js +50 -118
  393. package/dist/file-system-manager.js.map +1 -1
  394. package/dist/git-service/index.d.ts.map +1 -1
  395. package/dist/git-service/index.js +8 -0
  396. package/dist/git-service/index.js.map +1 -1
  397. package/dist/lock-service/index.d.ts +24 -0
  398. package/dist/lock-service/index.d.ts.map +1 -1
  399. package/dist/lock-service/index.js +74 -2
  400. package/dist/lock-service/index.js.map +1 -1
  401. package/dist/migration/integration-write-policy.d.ts +2 -1
  402. package/dist/migration/integration-write-policy.d.ts.map +1 -1
  403. package/dist/migration/integration-write-policy.js.map +1 -1
  404. package/dist/migration/restructure.d.ts +1 -1
  405. package/dist/migration/restructure.d.ts.map +1 -1
  406. package/dist/migration/restructure.js +1 -1
  407. package/dist/migration/restructure.js.map +1 -1
  408. package/dist/migration/translation-prompt.js +1 -1
  409. package/dist/migration-templates/app-fullstack/client/components/ui/accordion.tsx +0 -69
  410. package/dist/migration-templates/app-fullstack/client/components/ui/aspect-ratio.tsx +0 -31
  411. package/dist/migration-templates/app-fullstack/client/components/ui/avatar.tsx +0 -52
  412. package/dist/migration-templates/app-fullstack/client/components/ui/badge.tsx +5 -79
  413. package/dist/migration-templates/app-fullstack/client/components/ui/breadcrumb.tsx +14 -161
  414. package/dist/migration-templates/app-fullstack/client/components/ui/button.tsx +1 -53
  415. package/dist/migration-templates/app-fullstack/client/components/ui/calendar.tsx +4 -56
  416. package/dist/migration-templates/app-fullstack/client/components/ui/card.tsx +0 -34
  417. package/dist/migration-templates/app-fullstack/client/components/ui/chart.tsx +3 -51
  418. package/dist/migration-templates/app-fullstack/client/components/ui/checkbox.tsx +0 -42
  419. package/dist/migration-templates/app-fullstack/client/components/ui/dialog.tsx +0 -55
  420. package/dist/migration-templates/app-fullstack/client/components/ui/dropdown-menu.tsx +0 -29
  421. package/dist/migration-templates/app-fullstack/client/components/ui/hover-card.tsx +0 -64
  422. package/dist/migration-templates/app-fullstack/client/components/ui/input.tsx +0 -57
  423. package/dist/migration-templates/app-fullstack/client/components/ui/label.tsx +0 -30
  424. package/dist/migration-templates/app-fullstack/client/components/ui/navigation-menu.tsx +16 -172
  425. package/dist/migration-templates/app-fullstack/client/components/ui/pagination.tsx +0 -44
  426. package/dist/migration-templates/app-fullstack/client/components/ui/popover.tsx +0 -64
  427. package/dist/migration-templates/app-fullstack/client/components/ui/progress.tsx +0 -31
  428. package/dist/migration-templates/app-fullstack/client/components/ui/scroll-area.tsx +0 -30
  429. package/dist/migration-templates/app-fullstack/client/components/ui/select.tsx +0 -41
  430. package/dist/migration-templates/app-fullstack/client/components/ui/separator.tsx +0 -29
  431. package/dist/migration-templates/app-fullstack/client/components/ui/sheet.tsx +0 -64
  432. package/dist/migration-templates/app-fullstack/client/components/ui/sidebar.tsx +30 -265
  433. package/dist/migration-templates/app-fullstack/client/components/ui/slider.tsx +0 -45
  434. package/dist/migration-templates/app-fullstack/client/components/ui/switch.tsx +0 -41
  435. package/dist/migration-templates/app-fullstack/client/components/ui/table.tsx +0 -32
  436. package/dist/migration-templates/app-fullstack/client/components/ui/tabs.tsx +0 -23
  437. package/dist/migration-templates/app-fullstack/client/components/ui/textarea.tsx +0 -48
  438. package/dist/migration-templates/app-fullstack/client/components/ui/toggle-group.tsx +0 -81
  439. package/dist/migration-templates/app-fullstack/client/components/ui/toggle.tsx +0 -46
  440. package/dist/migration-templates/app-fullstack/client/components/ui/tooltip.tsx +0 -63
  441. package/dist/migration-templates/app-fullstack/eslint.config.js +7 -0
  442. package/dist/migration-templates/app-fullstack/package.json +0 -3
  443. package/dist/parsing/types.d.ts +3 -2
  444. package/dist/parsing/types.d.ts.map +1 -1
  445. package/dist/policy-gate-runner.d.ts +1 -1
  446. package/dist/policy-gate-runner.d.ts.map +1 -1
  447. package/dist/policy-gate-runner.js.map +1 -1
  448. package/dist/reconnect-guard-plugin.d.ts +0 -1
  449. package/dist/reconnect-guard-plugin.d.ts.map +1 -1
  450. package/dist/reconnect-guard-plugin.js +1 -1
  451. package/dist/reconnect-guard-plugin.js.map +1 -1
  452. package/dist/resolver.d.ts +1 -1
  453. package/dist/resolver.d.ts.map +1 -1
  454. package/dist/resolver.js +2 -2
  455. package/dist/resolver.js.map +1 -1
  456. package/dist/socket-manager.d.ts.map +1 -1
  457. package/dist/socket-manager.js +12 -2
  458. package/dist/socket-manager.js.map +1 -1
  459. package/dist/source-tracker.d.ts +5 -15
  460. package/dist/source-tracker.d.ts.map +1 -1
  461. package/dist/source-tracker.js +28 -66
  462. package/dist/source-tracker.js.map +1 -1
  463. package/dist/sync-service/checkpoint-result.d.ts +6 -5
  464. package/dist/sync-service/checkpoint-result.d.ts.map +1 -1
  465. package/dist/sync-service/checkpoint-result.js +9 -8
  466. package/dist/sync-service/checkpoint-result.js.map +1 -1
  467. package/dist/sync-service/index.d.ts +1 -0
  468. package/dist/sync-service/index.d.ts.map +1 -1
  469. package/dist/sync-service/index.js +8 -1
  470. package/dist/sync-service/index.js.map +1 -1
  471. package/dist/sync-service/snapshot/types.d.ts +3 -2
  472. package/dist/sync-service/snapshot/types.d.ts.map +1 -1
  473. package/dist/util/generation-wait-timeout-error.d.ts +12 -0
  474. package/dist/util/generation-wait-timeout-error.d.ts.map +1 -0
  475. package/dist/util/generation-wait-timeout-error.js +16 -0
  476. package/dist/util/generation-wait-timeout-error.js.map +1 -0
  477. package/package.json +9 -13
  478. package/dist/ai-service/agent/tools/apis/sdk-root-documentation.d.ts +0 -20
  479. package/dist/ai-service/agent/tools/apis/sdk-root-documentation.d.ts.map +0 -1
  480. package/dist/ai-service/agent/tools/apis/sdk-root-documentation.js +0 -284
  481. package/dist/ai-service/agent/tools/apis/sdk-root-documentation.js.map +0 -1
  482. package/dist/migration-templates/app-fullstack/client/components/hooks/use-active-page.ts +0 -73
  483. package/dist/migration-templates/app-fullstack/client/components/ui/area-chart.tsx +0 -312
  484. package/dist/migration-templates/app-fullstack/client/components/ui/bar-chart.tsx +0 -264
  485. package/dist/migration-templates/app-fullstack/client/components/ui/code-block-content.tsx +0 -66
  486. package/dist/migration-templates/app-fullstack/client/components/ui/date-range-picker.tsx +0 -255
  487. package/dist/migration-templates/app-fullstack/client/components/ui/file-dropzone.tsx +0 -312
  488. package/dist/migration-templates/app-fullstack/client/components/ui/file-input.tsx +0 -105
  489. package/dist/migration-templates/app-fullstack/client/components/ui/icon.tsx +0 -120
  490. package/dist/migration-templates/app-fullstack/client/components/ui/image.tsx +0 -56
  491. package/dist/migration-templates/app-fullstack/client/components/ui/line-chart.tsx +0 -264
  492. package/dist/migration-templates/app-fullstack/client/components/ui/link.tsx +0 -119
  493. package/dist/migration-templates/app-fullstack/client/components/ui/pie-chart.tsx +0 -181
  494. package/dist/migration-templates/app-fullstack/client/components/ui/radar-chart.tsx +0 -252
  495. package/dist/migration-templates/app-fullstack/client/components/ui/radial-chart.tsx +0 -250
  496. package/dist/refactor/entities.d.ts +0 -6
  497. package/dist/refactor/entities.d.ts.map +0 -1
  498. package/dist/refactor/entities.js +0 -65
  499. package/dist/refactor/entities.js.map +0 -1
  500. package/dist/refactor/javascript.d.ts +0 -15
  501. package/dist/refactor/javascript.d.ts.map +0 -1
  502. package/dist/refactor/javascript.js +0 -283
  503. package/dist/refactor/javascript.js.map +0 -1
  504. package/dist/rename-manager.d.ts +0 -28
  505. package/dist/rename-manager.d.ts.map +0 -1
  506. package/dist/rename-manager.js +0 -161
  507. package/dist/rename-manager.js.map +0 -1
@@ -0,0 +1,2 @@
1
+ export declare const content = "---\nname: app-database\ndescription: |\n Use when writing attached, pasted, sample, or existing development rows to an App Database, or when the user wants those rows in the published app.\nreadOnly: true\nmetadata:\n author: superblocks\n version: \"1.0\"\n---\n\n# Placing data in an App Database\n\n## Direct writes reach development only\n\n`testApi` and `executeRequestToIntegration` write to the development database used by edit mode and preview. They never write directly to the separate production database used by the published app.\n\nWhenever you write rows, paste the development database's `entityTag` and state which of these outcomes applies:\n\n- **Development only:** the published app will not receive these rows on the next publish.\n- **Development and production through a seed migration:** applying the migration writes the rows to development; the next publish applies the same migration to production.\n- **Production through the App Database import flow, when the system prompt says it is available:** the user imports the production data separately; publishing does not copy the development rows.\n\nDo not say that production is empty or that the published app cannot see the rows without naming the applicable outcome.\n\n## Decide the destination before the first write\n\nUse the destination the user already gave. A request for sample, demo, placeholder, or test rows means development only. A request for the published app or production means development and production; do not ask another destination question. Building an app from an attachment says to load its rows into development, but does not by itself choose production.\n\nIf the destination is still unknown, use `askMultiChoice` once per attachment or pasted data set:\n\n- **Development only:** load the development database now. The next publish will not copy these rows to production.\n- **Development and production:** for small data, add and apply a seed migration so development receives the rows now and production receives them on the next publish.\n\nFor large data, follow the **App Database production import availability** section in the system prompt. When production import is available, replace the second choice with:\n\n- **Development now, production through import:** load the development database now. The user will import production data separately through the App Database data import flow; publishing will not copy the development rows.\n\nWhen production import is unavailable, do not offer the Data tab or a dump workflow. Explain that production import is not available today and CSV import will be available when the production import surface is enabled. If the user has not already asked to load the rows, ask whether to load development now or leave the rows unloaded.\n\nEvery choice must name the database it changes and what happens on the next publish. Refer only to the development database used by edit mode and preview and the production database used by the published app. Do not invent staging databases or other deploy targets.\n\n## Development-only data\n\nLoad development-only rows at runtime with `testApi` or `executeRequestToIntegration`. Do not add them to a migration.\n\n## Small development-and-production data\n\nUse a seed migration only for small reference lists, lookup tables, or modest starting data that the published app should always have.\n\n1. Write the rows once, in a migration under the `migrationDirectory` configured by `provisionDatabase`.\n2. Call `startApplyDevDatabaseMigrations`. This is the only development write for those rows; do not also load them with `testApi` or `executeRequestToIntegration`.\n3. Say that the migration has populated development and that the next publish applies it to production.\n\nKeep the complete migration set, including schema and seed files, well below the server's 5 MiB total limit. The limit applies to the whole set, so splitting a large seed into several files does not help. When the total would approach the limit, follow the production import availability guidance instead; if import is unavailable, explain that the large data cannot be loaded into production today.\n\nMake seed SQL safe for both databases:\n\n- Prefer stable natural keys and explicit conflict handling.\n- Omit generated identity or serial columns when inserting new rows.\n- When copying rows that already exist in development, preserve the keys needed to make the development apply a no-op. After the inserts, advance every affected production sequence with `setval` based on the table's current `max(id)` so existing production rows cannot move the sequence backward.\n\n## Large production data\n\nNever put large data into seed migrations. Follow this order:\n\n1. Classify the data as large.\n2. Read the **App Database production import availability** section in the system prompt before any row-writing tool call.\n - When import is available, state: \"I will load these rows into development. Production requires a separate App Database import that you run; publishing will not copy the development rows.\"\n - When import is unavailable, send the user the complete disclosure from that system-prompt section verbatim immediately before the first row-writing tool call.\n Do not proceed after stating only that import is unavailable. A statement that you intend or need to give the disclosure does not satisfy the requirement.\n3. Load the attachment into development without another destination question when the request already says to load its rows, including a request to build an app from the attachment.\n\nWhen production import is available, Clark cannot run it. Tell the user to open the App Database integration's **Data** tab in Superblocks, select the data tag used by production, and import the CSV or PostgreSQL custom-format dump there.\n\nWhen loading development, state the current production-import availability and that publishing does not copy the development rows.\n";
2
+ //# sourceMappingURL=skill.generated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill.generated.d.ts","sourceRoot":"","sources":["../../../../../src/ai-service/skills/system/app-database/skill.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,u2LA2EnB,CAAC"}
@@ -0,0 +1,79 @@
1
+ // Auto-generated from src/ai-service/skills/system/app-database/SKILL.md
2
+ // Do not edit directly - edit the .md file instead
3
+ export const content = `---
4
+ name: app-database
5
+ description: |
6
+ Use when writing attached, pasted, sample, or existing development rows to an App Database, or when the user wants those rows in the published app.
7
+ readOnly: true
8
+ metadata:
9
+ author: superblocks
10
+ version: "1.0"
11
+ ---
12
+
13
+ # Placing data in an App Database
14
+
15
+ ## Direct writes reach development only
16
+
17
+ \`testApi\` and \`executeRequestToIntegration\` write to the development database used by edit mode and preview. They never write directly to the separate production database used by the published app.
18
+
19
+ Whenever you write rows, paste the development database's \`entityTag\` and state which of these outcomes applies:
20
+
21
+ - **Development only:** the published app will not receive these rows on the next publish.
22
+ - **Development and production through a seed migration:** applying the migration writes the rows to development; the next publish applies the same migration to production.
23
+ - **Production through the App Database import flow, when the system prompt says it is available:** the user imports the production data separately; publishing does not copy the development rows.
24
+
25
+ Do not say that production is empty or that the published app cannot see the rows without naming the applicable outcome.
26
+
27
+ ## Decide the destination before the first write
28
+
29
+ Use the destination the user already gave. A request for sample, demo, placeholder, or test rows means development only. A request for the published app or production means development and production; do not ask another destination question. Building an app from an attachment says to load its rows into development, but does not by itself choose production.
30
+
31
+ If the destination is still unknown, use \`askMultiChoice\` once per attachment or pasted data set:
32
+
33
+ - **Development only:** load the development database now. The next publish will not copy these rows to production.
34
+ - **Development and production:** for small data, add and apply a seed migration so development receives the rows now and production receives them on the next publish.
35
+
36
+ For large data, follow the **App Database production import availability** section in the system prompt. When production import is available, replace the second choice with:
37
+
38
+ - **Development now, production through import:** load the development database now. The user will import production data separately through the App Database data import flow; publishing will not copy the development rows.
39
+
40
+ When production import is unavailable, do not offer the Data tab or a dump workflow. Explain that production import is not available today and CSV import will be available when the production import surface is enabled. If the user has not already asked to load the rows, ask whether to load development now or leave the rows unloaded.
41
+
42
+ Every choice must name the database it changes and what happens on the next publish. Refer only to the development database used by edit mode and preview and the production database used by the published app. Do not invent staging databases or other deploy targets.
43
+
44
+ ## Development-only data
45
+
46
+ Load development-only rows at runtime with \`testApi\` or \`executeRequestToIntegration\`. Do not add them to a migration.
47
+
48
+ ## Small development-and-production data
49
+
50
+ Use a seed migration only for small reference lists, lookup tables, or modest starting data that the published app should always have.
51
+
52
+ 1. Write the rows once, in a migration under the \`migrationDirectory\` configured by \`provisionDatabase\`.
53
+ 2. Call \`startApplyDevDatabaseMigrations\`. This is the only development write for those rows; do not also load them with \`testApi\` or \`executeRequestToIntegration\`.
54
+ 3. Say that the migration has populated development and that the next publish applies it to production.
55
+
56
+ Keep the complete migration set, including schema and seed files, well below the server's 5 MiB total limit. The limit applies to the whole set, so splitting a large seed into several files does not help. When the total would approach the limit, follow the production import availability guidance instead; if import is unavailable, explain that the large data cannot be loaded into production today.
57
+
58
+ Make seed SQL safe for both databases:
59
+
60
+ - Prefer stable natural keys and explicit conflict handling.
61
+ - Omit generated identity or serial columns when inserting new rows.
62
+ - When copying rows that already exist in development, preserve the keys needed to make the development apply a no-op. After the inserts, advance every affected production sequence with \`setval\` based on the table's current \`max(id)\` so existing production rows cannot move the sequence backward.
63
+
64
+ ## Large production data
65
+
66
+ Never put large data into seed migrations. Follow this order:
67
+
68
+ 1. Classify the data as large.
69
+ 2. Read the **App Database production import availability** section in the system prompt before any row-writing tool call.
70
+ - When import is available, state: "I will load these rows into development. Production requires a separate App Database import that you run; publishing will not copy the development rows."
71
+ - When import is unavailable, send the user the complete disclosure from that system-prompt section verbatim immediately before the first row-writing tool call.
72
+ Do not proceed after stating only that import is unavailable. A statement that you intend or need to give the disclosure does not satisfy the requirement.
73
+ 3. Load the attachment into development without another destination question when the request already says to load its rows, including a request to build an app from the attachment.
74
+
75
+ When production import is available, Clark cannot run it. Tell the user to open the App Database integration's **Data** tab in Superblocks, select the data tag used by production, and import the CSV or PostgreSQL custom-format dump there.
76
+
77
+ When loading development, state the current production-import availability and that publishing does not copy the development rows.
78
+ `;
79
+ //# sourceMappingURL=skill.generated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill.generated.js","sourceRoot":"","sources":["../../../../../src/ai-service/skills/system/app-database/skill.generated.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,mDAAmD;AAEnD,MAAM,CAAC,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2EtB,CAAC"}
@@ -1,2 +1,2 @@
1
- export declare function resolveSystemSkills(sdkApiEnabled: boolean, skills?: Readonly<Record<string, string>>): Record<string, string>;
1
+ export declare function resolveSystemSkills(sdkApiEnabled: boolean, skills?: Readonly<Record<string, string>>, iconWrapper?: boolean): Record<string, string>;
2
2
  //# sourceMappingURL=resolve.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../../src/ai-service/skills/system/resolve.ts"],"names":[],"mappings":"AAgBA,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,OAAO,EACtB,MAAM,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAiB,GACvD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAoExB"}
1
+ {"version":3,"file":"resolve.d.ts","sourceRoot":"","sources":["../../../../src/ai-service/skills/system/resolve.ts"],"names":[],"mappings":"AAuBA,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,OAAO,EACtB,MAAM,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAiB,EACxD,WAAW,UAAO,GACjB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA6FxB"}
@@ -6,8 +6,14 @@ const LEGACY_FRONTEND_IMPORTS_PATH = "superblocks-frontend/_variants/api-imports
6
6
  const SDK_FRONTEND_IMPORTS_PATH = "superblocks-frontend/_variants/api-imports-sdk.md";
7
7
  const LEGACY_FRONTEND_API_NAMES_PATH = "superblocks-frontend/_variants/api-names-legacy.md";
8
8
  const SDK_FRONTEND_API_NAMES_PATH = "superblocks-frontend/_variants/api-names-sdk.md";
9
+ const FRONTEND_ICON_GUIDANCE_MARKER = "<!-- ICON_GUIDANCE -->";
10
+ const ICON_WRAPPER_PATH = "superblocks-frontend/_variants/icons-wrapper.md";
11
+ const ICON_LUCIDE_PATH = "superblocks-frontend/_variants/icons-lucide.md";
12
+ const FRONTEND_BASE_UI_GUIDANCE_MARKER = "<!-- BASE_UI_GUIDANCE -->";
13
+ const BASE_UI_REGISTERED_PATH = "superblocks-frontend/_variants/base-ui-registered.md";
14
+ const BASE_UI_PLAIN_PATH = "superblocks-frontend/_variants/base-ui-plain.md";
9
15
  const SDK_API_SKILL_PATH = "superblocks-sdk-api/SKILL.md";
10
- export function resolveSystemSkills(sdkApiEnabled, skills = SYSTEM_SKILLS) {
16
+ export function resolveSystemSkills(sdkApiEnabled, skills = SYSTEM_SKILLS, iconWrapper = true) {
11
17
  const frontendSkill = skills[FRONTEND_SKILL_PATH];
12
18
  const legacyImports = skills[LEGACY_FRONTEND_IMPORTS_PATH];
13
19
  const sdkImports = skills[SDK_FRONTEND_IMPORTS_PATH];
@@ -39,16 +45,33 @@ export function resolveSystemSkills(sdkApiEnabled, skills = SYSTEM_SKILLS) {
39
45
  if (apiNameMarkerCount !== 1) {
40
46
  throw new Error(`Expected one frontend API name guidance marker, found ${apiNameMarkerCount}`);
41
47
  }
48
+ const iconGuidance = skills[iconWrapper ? ICON_WRAPPER_PATH : ICON_LUCIDE_PATH];
49
+ if (frontendSkill.includes(FRONTEND_ICON_GUIDANCE_MARKER) &&
50
+ iconGuidance === undefined) {
51
+ throw new Error("Frontend icon guidance variant is missing");
52
+ }
53
+ // Apps that still have icon.tsx also still register their UI components.
54
+ const baseUiGuidance = skills[iconWrapper ? BASE_UI_REGISTERED_PATH : BASE_UI_PLAIN_PATH];
55
+ if (frontendSkill.includes(FRONTEND_BASE_UI_GUIDANCE_MARKER) &&
56
+ baseUiGuidance === undefined) {
57
+ throw new Error("Frontend base UI guidance variant is missing");
58
+ }
42
59
  const resolvedSkills = {
43
60
  ...skills,
44
61
  [FRONTEND_SKILL_PATH]: frontendSkill
45
62
  .replace(FRONTEND_IMPORT_GUIDANCE_MARKER, sdkApiEnabled ? sdkImports : legacyImports)
46
- .replace(FRONTEND_API_NAME_GUIDANCE_MARKER, sdkApiEnabled ? sdkApiNames : legacyApiNames),
63
+ .replace(FRONTEND_API_NAME_GUIDANCE_MARKER, sdkApiEnabled ? sdkApiNames : legacyApiNames)
64
+ .replace(FRONTEND_ICON_GUIDANCE_MARKER, iconGuidance ?? "")
65
+ .replace(FRONTEND_BASE_UI_GUIDANCE_MARKER, baseUiGuidance ?? ""),
47
66
  };
48
67
  delete resolvedSkills[LEGACY_FRONTEND_IMPORTS_PATH];
49
68
  delete resolvedSkills[SDK_FRONTEND_IMPORTS_PATH];
50
69
  delete resolvedSkills[LEGACY_FRONTEND_API_NAMES_PATH];
51
70
  delete resolvedSkills[SDK_FRONTEND_API_NAMES_PATH];
71
+ delete resolvedSkills[ICON_WRAPPER_PATH];
72
+ delete resolvedSkills[ICON_LUCIDE_PATH];
73
+ delete resolvedSkills[BASE_UI_REGISTERED_PATH];
74
+ delete resolvedSkills[BASE_UI_PLAIN_PATH];
52
75
  if (!sdkApiEnabled) {
53
76
  delete resolvedSkills[SDK_API_SKILL_PATH];
54
77
  }
@@ -1 +1 @@
1
- {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../../../src/ai-service/skills/system/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,MAAM,mBAAmB,GAAG,+BAA+B,CAAC;AAC5D,MAAM,+BAA+B,GACnC,wCAAwC,CAAC;AAC3C,MAAM,iCAAiC,GAAG,4BAA4B,CAAC;AACvE,MAAM,4BAA4B,GAChC,sDAAsD,CAAC;AACzD,MAAM,yBAAyB,GAC7B,mDAAmD,CAAC;AACtD,MAAM,8BAA8B,GAClC,oDAAoD,CAAC;AACvD,MAAM,2BAA2B,GAC/B,iDAAiD,CAAC;AACpD,MAAM,kBAAkB,GAAG,8BAA8B,CAAC;AAE1D,MAAM,UAAU,mBAAmB,CACjC,aAAsB,EACtB,SAA2C,aAAa;IAExD,MAAM,aAAa,GAAG,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAClD,MAAM,aAAa,GAAG,MAAM,CAAC,4BAA4B,CAAC,CAAC;IAC3D,MAAM,UAAU,GAAG,MAAM,CAAC,yBAAyB,CAAC,CAAC;IACrD,MAAM,cAAc,GAAG,MAAM,CAAC,8BAA8B,CAAC,CAAC;IAC9D,MAAM,WAAW,GAAG,MAAM,CAAC,2BAA2B,CAAC,CAAC;IAExD,IACE,aAAa,KAAK,SAAS;QAC3B,aAAa,KAAK,SAAS;QAC3B,UAAU,KAAK,SAAS;QACxB,cAAc,KAAK,SAAS;QAC5B,WAAW,KAAK,SAAS,EACzB,CAAC;QACD,MAAM,cAAc,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC;QACrC,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO,cAAc,CAAC,kBAAkB,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,cAAc,CAAC;IACxB,CAAC;IAED,IACE,aAAa,KAAK,SAAS;QAC3B,aAAa,KAAK,SAAS;QAC3B,UAAU,KAAK,SAAS;QACxB,cAAc,KAAK,SAAS;QAC5B,WAAW,KAAK,SAAS,EACzB,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,WAAW,GACf,aAAa,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IAClE,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,2DAA2D,WAAW,EAAE,CACzE,CAAC;IACJ,CAAC;IAED,MAAM,kBAAkB,GACtB,aAAa,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IACpE,IAAI,kBAAkB,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,yDAAyD,kBAAkB,EAAE,CAC9E,CAAC;IACJ,CAAC;IAED,MAAM,cAAc,GAA2B;QAC7C,GAAG,MAAM;QACT,CAAC,mBAAmB,CAAC,EAAE,aAAa;aACjC,OAAO,CACN,+BAA+B,EAC/B,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAC3C;aACA,OAAO,CACN,iCAAiC,EACjC,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,cAAc,CAC7C;KACJ,CAAC;IACF,OAAO,cAAc,CAAC,4BAA4B,CAAC,CAAC;IACpD,OAAO,cAAc,CAAC,yBAAyB,CAAC,CAAC;IACjD,OAAO,cAAc,CAAC,8BAA8B,CAAC,CAAC;IACtD,OAAO,cAAc,CAAC,2BAA2B,CAAC,CAAC;IACnD,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,cAAc,CAAC,kBAAkB,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,cAAc,CAAC;AACxB,CAAC"}
1
+ {"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../../../src/ai-service/skills/system/resolve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,MAAM,mBAAmB,GAAG,+BAA+B,CAAC;AAC5D,MAAM,+BAA+B,GACnC,wCAAwC,CAAC;AAC3C,MAAM,iCAAiC,GAAG,4BAA4B,CAAC;AACvE,MAAM,4BAA4B,GAChC,sDAAsD,CAAC;AACzD,MAAM,yBAAyB,GAC7B,mDAAmD,CAAC;AACtD,MAAM,8BAA8B,GAClC,oDAAoD,CAAC;AACvD,MAAM,2BAA2B,GAC/B,iDAAiD,CAAC;AACpD,MAAM,6BAA6B,GAAG,wBAAwB,CAAC;AAC/D,MAAM,iBAAiB,GAAG,iDAAiD,CAAC;AAC5E,MAAM,gBAAgB,GAAG,gDAAgD,CAAC;AAC1E,MAAM,gCAAgC,GAAG,2BAA2B,CAAC;AACrE,MAAM,uBAAuB,GAC3B,sDAAsD,CAAC;AACzD,MAAM,kBAAkB,GAAG,iDAAiD,CAAC;AAC7E,MAAM,kBAAkB,GAAG,8BAA8B,CAAC;AAE1D,MAAM,UAAU,mBAAmB,CACjC,aAAsB,EACtB,SAA2C,aAAa,EACxD,WAAW,GAAG,IAAI;IAElB,MAAM,aAAa,GAAG,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAClD,MAAM,aAAa,GAAG,MAAM,CAAC,4BAA4B,CAAC,CAAC;IAC3D,MAAM,UAAU,GAAG,MAAM,CAAC,yBAAyB,CAAC,CAAC;IACrD,MAAM,cAAc,GAAG,MAAM,CAAC,8BAA8B,CAAC,CAAC;IAC9D,MAAM,WAAW,GAAG,MAAM,CAAC,2BAA2B,CAAC,CAAC;IAExD,IACE,aAAa,KAAK,SAAS;QAC3B,aAAa,KAAK,SAAS;QAC3B,UAAU,KAAK,SAAS;QACxB,cAAc,KAAK,SAAS;QAC5B,WAAW,KAAK,SAAS,EACzB,CAAC;QACD,MAAM,cAAc,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC;QACrC,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO,cAAc,CAAC,kBAAkB,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,cAAc,CAAC;IACxB,CAAC;IAED,IACE,aAAa,KAAK,SAAS;QAC3B,aAAa,KAAK,SAAS;QAC3B,UAAU,KAAK,SAAS;QACxB,cAAc,KAAK,SAAS;QAC5B,WAAW,KAAK,SAAS,EACzB,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,WAAW,GACf,aAAa,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IAClE,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,2DAA2D,WAAW,EAAE,CACzE,CAAC;IACJ,CAAC;IAED,MAAM,kBAAkB,GACtB,aAAa,CAAC,KAAK,CAAC,iCAAiC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IACpE,IAAI,kBAAkB,KAAK,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,yDAAyD,kBAAkB,EAAE,CAC9E,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAChB,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC;IAC7D,IACE,aAAa,CAAC,QAAQ,CAAC,6BAA6B,CAAC;QACrD,YAAY,KAAK,SAAS,EAC1B,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/D,CAAC;IAED,yEAAyE;IACzE,MAAM,cAAc,GAClB,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC;IACrE,IACE,aAAa,CAAC,QAAQ,CAAC,gCAAgC,CAAC;QACxD,cAAc,KAAK,SAAS,EAC5B,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IAED,MAAM,cAAc,GAA2B;QAC7C,GAAG,MAAM;QACT,CAAC,mBAAmB,CAAC,EAAE,aAAa;aACjC,OAAO,CACN,+BAA+B,EAC/B,aAAa,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAC3C;aACA,OAAO,CACN,iCAAiC,EACjC,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,cAAc,CAC7C;aACA,OAAO,CAAC,6BAA6B,EAAE,YAAY,IAAI,EAAE,CAAC;aAC1D,OAAO,CAAC,gCAAgC,EAAE,cAAc,IAAI,EAAE,CAAC;KACnE,CAAC;IACF,OAAO,cAAc,CAAC,4BAA4B,CAAC,CAAC;IACpD,OAAO,cAAc,CAAC,yBAAyB,CAAC,CAAC;IACjD,OAAO,cAAc,CAAC,8BAA8B,CAAC,CAAC;IACtD,OAAO,cAAc,CAAC,2BAA2B,CAAC,CAAC;IACnD,OAAO,cAAc,CAAC,iBAAiB,CAAC,CAAC;IACzC,OAAO,cAAc,CAAC,gBAAgB,CAAC,CAAC;IACxC,OAAO,cAAc,CAAC,uBAAuB,CAAC,CAAC;IAC/C,OAAO,cAAc,CAAC,kBAAkB,CAAC,CAAC;IAC1C,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,cAAc,CAAC,kBAAkB,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,cAAc,CAAC;AACxB,CAAC"}
@@ -1,2 +1,2 @@
1
- export declare const content = "---\nname: spreadsheet-import\ndescription: |\n Build an app from an attached Excel workbook, or import one into the App Database.\n Load whenever an `.xlsx` attachment is part of the request \u2014 before designing a schema, before writing a migration, and before importing any rows.\n Explains the structure digest the converter writes beside the CSVs, and how to decide which workbook columns become stored database columns and which become derived app behavior.\nreadOnly: true\nmetadata:\n author: superblocks\n version: \"1.0\"\n---\n\n# Building an app from an Excel workbook\n\nAn `.xlsx` attachment never reaches you as a spreadsheet. Before `downloadAttachments` or `testApi` hands it over, the converter turns it into:\n\n- one CSV per sheet that holds data, already trimmed to the table, and\n- a `*-structure.json` digest describing every sheet in a few hundred tokens.\n\nGenerated SDK code must still parse CSV only. Never parse spreadsheet binary formats in sandbox code.\n\n## Design from the digest, not from the rows\n\nRead the digest first. It is the thing to design from; the CSVs beside it are rows to load, not reading material. It is design evidence, not executable code.\n\nAttaching a workbook is not by itself a request to load its rows. If the request was to inspect the file, design a schema, or build screens, do that and ask before importing. If the request was to build an app from the workbook, the data is part of it.\n\n## What the digest tells you\n\n**`role`** \u2014 a `data table` has a `csvFile` to import. Most sheets are data tables, including ones with several calculated columns beside their typed-in values. A `derived report` is the narrower case where the sheet stores nothing of its own: every column beside its labels is worked out from other cells. It has no CSV, so build it as a page or view over the imported tables, never as another table.\n\n**`headerRowNumber`, `banner`, `notes`, `footerAggregates`** \u2014 the CSVs are already trimmed to the table. A banner title, the rows above the header, a sentence written under the table, and a footer total are described here and excluded from the CSV, so load every CSV row as data and put the banner, the notes, and the totals in the UI instead.\n\n**`computed` and `coverage`** \u2014 a column with `computed` states one rule for the whole column, and `coverage` says how many of its cells follow that rule (`5000/5000` is a per-row rule). Treat those as derived behavior, not stored columns. A column with only an `example` computes something different per row, so read it as report logic rather than a rule to repeat.\n\n**`readsFrom`** \u2014 the sheets this sheet's formulas reference. Use it plus validated matching keys as relationship evidence. Do not invent foreign keys from column names alone.\n\n**`hidden`** \u2014 a sheet or column the workbook hid from its own readers, so usually working data rather than part of the app. Ask before surfacing it instead of importing it as an ordinary table or field.\n\n**`type` and `format`** \u2014 the workbook's own column types and number formats (`yyyy-mm-dd`, currency, `0%`). Use them for column types and UI display formats. A percentage is stored as a fraction, so `0.15` with format `0%` means 15 percent.\n\n**`errorCellCount`** \u2014 cells whose formulas evaluate to an Excel error (`#DIV/0!`, `#N/A`) are blank in the CSV and counted here. Import them as empty and tell the user which columns were affected. Never substitute a guessed value.\n\nA sheet whose only content is a column of sentences is a note to the reader, not a table. Say so and leave it out of the schema rather than importing the sentences as rows.\n\nThe workbook's colors, fonts, borders, and column widths are not reported and are not part of the app. Use the Superblocks design system.\n\n## Stored versus derived\n\nStore constant and input columns in the App Database. Treat columns whose body cells are consistently formulas as derived behavior implemented in SQL queries, SDK APIs, or UI code instead of stored database columns.\n\nA column that mixes formulas and constants stays stored unless the workbook clearly establishes one rule. Do not guess a formula for exceptional or manually overridden values.\n\nNever evaluate workbook formulas, either yourself or in generated SDK code. Use cached formula results only to understand examples and to verify that the app's derived behavior matches the workbook.\n\nIf a formula cannot be represented safely in SQL, an SDK API, or UI code, import its cached result as a snapshot, keep it non-editable by default, and explain that it will not recalculate.\n\n## Order of work\n\nWhen a workbook is attached to a sparse request, treat it as the app specification unless the user says otherwise, and infer useful tables and screens from sheet names, headers, values, and formulas.\n\nAfter deciding stored versus derived fields: provision and migrate the schema, import only the stored fields, then build functional screens and APIs over the imported tables. Verify representative derived values against the cached workbook results before reporting completion.\n";
1
+ export declare const content = "---\nname: spreadsheet-import\ndescription: |\n Build an app from an attached Excel workbook, or import one into the App Database.\n Load whenever an `.xlsx` attachment is part of the request \u2014 before designing a schema, before writing a migration, and before importing any rows.\n Explains the structure digest the converter writes beside the CSVs, and how to decide which workbook columns become stored database columns and which become derived app behavior.\nreadOnly: true\nmetadata:\n author: superblocks\n version: \"1.0\"\n---\n\n# Building an app from an Excel workbook\n\nAn `.xlsx` attachment never reaches you as a spreadsheet. Before `downloadAttachments` or `testApi` hands it over, the converter turns it into:\n\n- one CSV per sheet that holds data, already trimmed to the table, and\n- a `*-structure.json` digest describing every sheet in a few hundred tokens.\n\nGenerated SDK code must still parse CSV only. Never parse spreadsheet binary formats in sandbox code.\n\n## Design from the digest, not from the rows\n\nRead the digest first. It is the thing to design from; the CSVs beside it are rows to load, not reading material. It is design evidence, not executable code.\n\nAttaching a workbook is not by itself a request to load its rows. If the request was to inspect the file, design a schema, or build screens, do that and ask before importing. If the request was to build an app from the workbook, the data is part of it. That says to load the rows, but does not say they belong in production; use the App Database skill to decide their destination before the first write.\n\n## What the digest tells you\n\n**`role`** \u2014 a `data table` has a `csvFile` to import. Most sheets are data tables, including ones with several calculated columns beside their typed-in values. A `derived report` is the narrower case where the sheet stores nothing of its own: every column beside its labels is worked out from other cells. It has no CSV, so build it as a page or view over the imported tables, never as another table.\n\n**`headerRowNumber`, `banner`, `notes`, `footerAggregates`** \u2014 the CSVs are already trimmed to the table. A banner title, the rows above the header, a sentence written under the table, and a footer total are described here and excluded from the CSV, so load every CSV row as data and put the banner, the notes, and the totals in the UI instead.\n\n**`computed` and `coverage`** \u2014 a column with `computed` states one rule for the whole column, and `coverage` says how many of its cells follow that rule (`5000/5000` is a per-row rule). Treat those as derived behavior, not stored columns. A column with only an `example` computes something different per row, so read it as report logic rather than a rule to repeat.\n\n**`readsFrom`** \u2014 the sheets this sheet's formulas reference. Use it plus validated matching keys as relationship evidence. Do not invent foreign keys from column names alone.\n\n**`hidden`** \u2014 a sheet or column the workbook hid from its own readers, so usually working data rather than part of the app. Ask before surfacing it instead of importing it as an ordinary table or field.\n\n**`type` and `format`** \u2014 the workbook's own column types and number formats (`yyyy-mm-dd`, currency, `0%`). Use them for column types and UI display formats. A percentage is stored as a fraction, so `0.15` with format `0%` means 15 percent.\n\n**`errorCellCount`** \u2014 cells whose formulas evaluate to an Excel error (`#DIV/0!`, `#N/A`) are blank in the CSV and counted here. Import them as empty and tell the user which columns were affected. Never substitute a guessed value.\n\nA sheet whose only content is a column of sentences is a note to the reader, not a table. Say so and leave it out of the schema rather than importing the sentences as rows.\n\nThe workbook's colors, fonts, borders, and column widths are not reported and are not part of the app. Use the Superblocks design system.\n\n## Stored versus derived\n\nStore constant and input columns in the App Database. Treat columns whose body cells are consistently formulas as derived behavior implemented in SQL queries, SDK APIs, or UI code instead of stored database columns.\n\nA column that mixes formulas and constants stays stored unless the workbook clearly establishes one rule. Do not guess a formula for exceptional or manually overridden values.\n\nNever evaluate workbook formulas, either yourself or in generated SDK code. Use cached formula results only to understand examples and to verify that the app's derived behavior matches the workbook.\n\nIf a formula cannot be represented safely in SQL, an SDK API, or UI code, import its cached result as a snapshot, keep it non-editable by default, and explain that it will not recalculate.\n\n## Order of work\n\nWhen a workbook is attached to a sparse request, treat it as the app specification unless the user says otherwise, and infer useful tables and screens from sheet names, headers, values, and formulas.\n\nAfter deciding stored versus derived fields: provision and migrate the schema, import only the stored fields, then build functional screens and APIs over the imported tables. Verify representative derived values against the cached workbook results before reporting completion.\n";
2
2
  //# sourceMappingURL=skill.generated.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"skill.generated.d.ts","sourceRoot":"","sources":["../../../../../src/ai-service/skills/system/spreadsheet-import/skill.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,wiKA8DnB,CAAC"}
1
+ {"version":3,"file":"skill.generated.d.ts","sourceRoot":"","sources":["../../../../../src/ai-service/skills/system/spreadsheet-import/skill.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,+rKA8DnB,CAAC"}
@@ -25,7 +25,7 @@ Generated SDK code must still parse CSV only. Never parse spreadsheet binary for
25
25
 
26
26
  Read the digest first. It is the thing to design from; the CSVs beside it are rows to load, not reading material. It is design evidence, not executable code.
27
27
 
28
- Attaching a workbook is not by itself a request to load its rows. If the request was to inspect the file, design a schema, or build screens, do that and ask before importing. If the request was to build an app from the workbook, the data is part of it.
28
+ Attaching a workbook is not by itself a request to load its rows. If the request was to inspect the file, design a schema, or build screens, do that and ask before importing. If the request was to build an app from the workbook, the data is part of it. That says to load the rows, but does not say they belong in production; use the App Database skill to decide their destination before the first write.
29
29
 
30
30
  ## What the digest tells you
31
31
 
@@ -0,0 +1,2 @@
1
+ export declare const content = "Every file in `{{ui}}components/ui` is a plain shadcn component. Reuse one before writing your own. If a base UI component is missing (for example a combobox or a date range picker), add it to `{{ui}}components/ui` following the same shadcn patterns as the files already there: `cva` variants, `cn`, and the same design tokens. Put app-specific components (for example a `TodoItem`) in `{{ui}}components` instead.\n";
2
+ //# sourceMappingURL=base-ui-plain.generated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-ui-plain.generated.d.ts","sourceRoot":"","sources":["../../../../../../src/ai-service/skills/system/superblocks-frontend/_variants/base-ui-plain.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,oaACnB,CAAC"}
@@ -0,0 +1,5 @@
1
+ // Auto-generated from src/ai-service/skills/system/superblocks-frontend/_variants/base-ui-plain.md
2
+ // Do not edit directly - edit the .md file instead
3
+ export const content = `Every file in \`{{ui}}components/ui\` is a plain shadcn component. Reuse one before writing your own. If a base UI component is missing (for example a combobox or a date range picker), add it to \`{{ui}}components/ui\` following the same shadcn patterns as the files already there: \`cva\` variants, \`cn\`, and the same design tokens. Put app-specific components (for example a \`TodoItem\`) in \`{{ui}}components\` instead.
4
+ `;
5
+ //# sourceMappingURL=base-ui-plain.generated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-ui-plain.generated.js","sourceRoot":"","sources":["../../../../../../src/ai-service/skills/system/superblocks-frontend/_variants/base-ui-plain.generated.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,mDAAmD;AAEnD,MAAM,CAAC,MAAM,OAAO,GAAG;CACtB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const content = "Every file in `{{ui}}components/ui` is a shadcn component registered with the Superblocks editor through `registerComponent`, `Prop`, and `Section`. That registration powers the editor's properties panel, so keep it when you edit one of these files. Reuse an existing component before writing your own. If a base UI component is missing (for example a combobox or a date range picker), add it to `{{ui}}components/ui` and match the files already there. Put app-specific components (for example a `TodoItem`) in `{{ui}}components` instead.\n";
2
+ //# sourceMappingURL=base-ui-registered.generated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-ui-registered.generated.d.ts","sourceRoot":"","sources":["../../../../../../src/ai-service/skills/system/superblocks-frontend/_variants/base-ui-registered.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,iiBACnB,CAAC"}
@@ -0,0 +1,5 @@
1
+ // Auto-generated from src/ai-service/skills/system/superblocks-frontend/_variants/base-ui-registered.md
2
+ // Do not edit directly - edit the .md file instead
3
+ export const content = `Every file in \`{{ui}}components/ui\` is a shadcn component registered with the Superblocks editor through \`registerComponent\`, \`Prop\`, and \`Section\`. That registration powers the editor's properties panel, so keep it when you edit one of these files. Reuse an existing component before writing your own. If a base UI component is missing (for example a combobox or a date range picker), add it to \`{{ui}}components/ui\` and match the files already there. Put app-specific components (for example a \`TodoItem\`) in \`{{ui}}components\` instead.
4
+ `;
5
+ //# sourceMappingURL=base-ui-registered.generated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-ui-registered.generated.js","sourceRoot":"","sources":["../../../../../../src/ai-service/skills/system/superblocks-frontend/_variants/base-ui-registered.generated.ts"],"names":[],"mappings":"AAAA,wGAAwG;AACxG,mDAAmD;AAEnD,MAAM,CAAC,MAAM,OAAO,GAAG;CACtB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const content = "Use icons from the Lucide React library. Import each icon by name from `lucide-react`:\n\n```tsx\nimport { Heart, Plus } from \"lucide-react\";\n<Heart />;\n\nimport { Button } from \"@/components/ui/button\";\n<Button>\n <Plus /> Add Item\n</Button>;\n```\n\n**Always use icons rather than emojis unless explicitly requested.**\n\nUse current Lucide icon names, not stale aliases: use `House` instead of `Home`, `LifeBuoy` instead of `HelpCircle` or `CircleHelp`, and `ChartColumnBig` instead of `BarChart3`.\n\nWhen storing icons as data (arrays/objects), store the icon component itself and type it with `LucideIcon`:\n\n```tsx\nimport { House, Settings, type LucideIcon } from \"lucide-react\";\n\nconst navItems: { icon: LucideIcon; label: string }[] = [\n { icon: House, label: \"Home\" },\n { icon: Settings, label: \"Settings\" },\n];\n\nnavItems.map((item) => <item.icon className=\"size-4\" />);\n```\n";
2
+ //# sourceMappingURL=icons-lucide.generated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icons-lucide.generated.d.ts","sourceRoot":"","sources":["../../../../../../src/ai-service/skills/system/superblocks-frontend/_variants/icons-lucide.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,w5BA4BnB,CAAC"}
@@ -0,0 +1,32 @@
1
+ // Auto-generated from src/ai-service/skills/system/superblocks-frontend/_variants/icons-lucide.md
2
+ // Do not edit directly - edit the .md file instead
3
+ export const content = `Use icons from the Lucide React library. Import each icon by name from \`lucide-react\`:
4
+
5
+ \`\`\`tsx
6
+ import { Heart, Plus } from "lucide-react";
7
+ <Heart />;
8
+
9
+ import { Button } from "@/components/ui/button";
10
+ <Button>
11
+ <Plus /> Add Item
12
+ </Button>;
13
+ \`\`\`
14
+
15
+ **Always use icons rather than emojis unless explicitly requested.**
16
+
17
+ Use current Lucide icon names, not stale aliases: use \`House\` instead of \`Home\`, \`LifeBuoy\` instead of \`HelpCircle\` or \`CircleHelp\`, and \`ChartColumnBig\` instead of \`BarChart3\`.
18
+
19
+ When storing icons as data (arrays/objects), store the icon component itself and type it with \`LucideIcon\`:
20
+
21
+ \`\`\`tsx
22
+ import { House, Settings, type LucideIcon } from "lucide-react";
23
+
24
+ const navItems: { icon: LucideIcon; label: string }[] = [
25
+ { icon: House, label: "Home" },
26
+ { icon: Settings, label: "Settings" },
27
+ ];
28
+
29
+ navItems.map((item) => <item.icon className="size-4" />);
30
+ \`\`\`
31
+ `;
32
+ //# sourceMappingURL=icons-lucide.generated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icons-lucide.generated.js","sourceRoot":"","sources":["../../../../../../src/ai-service/skills/system/superblocks-frontend/_variants/icons-lucide.generated.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,mDAAmD;AAEnD,MAAM,CAAC,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BtB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const content = "Use icons from Lucide React library:\n\n```tsx\nimport { Icon } from \"@/components/ui/icon\";\n<Icon icon=\"heart\" />;\n\nimport { Button } from \"@/components/ui/button\";\n<Button>\n <Icon icon=\"plus\" /> Add Item\n</Button>;\n```\n\n**Always use icons rather than emojis unless explicitly requested.**\n\nUse current Lucide icon names, not stale aliases: use `house` instead of `home`, `life-buoy` instead of `help-circle` or `circle-help`, and `chart-column-big` instead of `bar-chart-3`.\n\nWhen storing icon names as data (arrays/objects), type them with `IconName`. **`IconName` is a TYPE-only export \u2014 you MUST import it with the `type` keyword.** A value import compiles but CRASHES the app at runtime (`does not provide an export named 'IconName'`) and leaves the preview blank, which blocks UI verification. If you hit that error, read `skills/system/common-import-issues/SKILL.md`.\n\n```tsx\n// \u2705 CORRECT \u2014 type-only import\nimport type { IconName } from \"lucide-react/dynamic\";\n\nconst navItems: { icon: IconName; label: string }[] = [\n { icon: \"house\", label: \"Home\" },\n { icon: \"settings\", label: \"Settings\" },\n];\n\n// \u274C WRONG \u2014 value import crashes the app at runtime and blanks the preview\nimport { IconName } from \"lucide-react/dynamic\";\n```\n";
2
+ //# sourceMappingURL=icons-wrapper.generated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icons-wrapper.generated.d.ts","sourceRoot":"","sources":["../../../../../../src/ai-service/skills/system/superblocks-frontend/_variants/icons-wrapper.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,qyCA8BnB,CAAC"}
@@ -0,0 +1,34 @@
1
+ // Auto-generated from src/ai-service/skills/system/superblocks-frontend/_variants/icons-wrapper.md
2
+ // Do not edit directly - edit the .md file instead
3
+ export const content = `Use icons from Lucide React library:
4
+
5
+ \`\`\`tsx
6
+ import { Icon } from "@/components/ui/icon";
7
+ <Icon icon="heart" />;
8
+
9
+ import { Button } from "@/components/ui/button";
10
+ <Button>
11
+ <Icon icon="plus" /> Add Item
12
+ </Button>;
13
+ \`\`\`
14
+
15
+ **Always use icons rather than emojis unless explicitly requested.**
16
+
17
+ Use current Lucide icon names, not stale aliases: use \`house\` instead of \`home\`, \`life-buoy\` instead of \`help-circle\` or \`circle-help\`, and \`chart-column-big\` instead of \`bar-chart-3\`.
18
+
19
+ When storing icon names as data (arrays/objects), type them with \`IconName\`. **\`IconName\` is a TYPE-only export — you MUST import it with the \`type\` keyword.** A value import compiles but CRASHES the app at runtime (\`does not provide an export named 'IconName'\`) and leaves the preview blank, which blocks UI verification. If you hit that error, read \`skills/system/common-import-issues/SKILL.md\`.
20
+
21
+ \`\`\`tsx
22
+ // ✅ CORRECT — type-only import
23
+ import type { IconName } from "lucide-react/dynamic";
24
+
25
+ const navItems: { icon: IconName; label: string }[] = [
26
+ { icon: "house", label: "Home" },
27
+ { icon: "settings", label: "Settings" },
28
+ ];
29
+
30
+ // ❌ WRONG — value import crashes the app at runtime and blanks the preview
31
+ import { IconName } from "lucide-react/dynamic";
32
+ \`\`\`
33
+ `;
34
+ //# sourceMappingURL=icons-wrapper.generated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icons-wrapper.generated.js","sourceRoot":"","sources":["../../../../../../src/ai-service/skills/system/superblocks-frontend/_variants/icons-wrapper.generated.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,mDAAmD;AAEnD,MAAM,CAAC,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BtB,CAAC"}
@@ -1,2 +1,2 @@
1
- export declare const content = "---\nname: superblocks-frontend\ndescription: |\n Build frontend UI using React, Tailwind CSS, and Superblocks components. Essential for connecting Superblocks UIs with APIs.\n Use when creating pages, components, handling user interactions, or working with the design system.\nreadOnly: true\nmetadata:\n author: superblocks\n version: \"1.0\"\n---\n\n# Superblocks Frontend Development\n\nThis skill covers building frontend UI for Superblocks applications using React, Tailwind CSS v4, and the Superblocks component library.\n\n## Platform Overview\n\nThis is a React-based web application platform. Use standard React patterns:\n\n- `useState`, `useEffect`, `useCallback`, `useMemo`\n- Event handlers and controlled components\n- JSX with Tailwind CSS classes\n\n## Using APIs from Frontend\n\n<!-- API_PRIMITIVE_IMPORT_GUIDANCE -->\n\n- **`useApiData`** \u2014 declarative reads with SWR caching (preferred for data loading)\n- **`useApi`** \u2014 imperative mutations (for event-driven actions)\n- **`executeApi`** \u2014 plain Promise outside React (utility functions, event handlers without hooks)\n\n**Data loading** \u2014 auto-fetches on mount and when inputs change (preferred for reads):\n\n```typescript\n// Auto-fetches when email or name changes. No useEffect or HMR guards needed.\n// IMPORTANT: Always use `fetching` to show a loading indicator during refetches.\nconst { data, loading, fetching, isError, error } = useApiData(\"GetUsers\", {\n email,\n name,\n});\n```\n\n**Mutations** \u2014 call `run()` manually (for event-driven actions like form submissions):\n\n```typescript\nimport { toast } from \"sonner\";\n\nconst { run: createOrder, loading } = useApi(\"CreateOrder\");\n\nconst handleSubmit = useCallback(async () => {\n try {\n await createOrder({ item, qty });\n } catch (error) {\n const message =\n error && typeof error === \"object\" && \"message\" in error\n ? String((error as { message: unknown }).message)\n : String(error);\n toast.error(\"Error creating order: \" + message);\n }\n}, [item, qty, createOrder]);\n```\n\n**Outside React** \u2014 call APIs as a plain Promise (utility functions, event handlers):\n\n```typescript\nconst result = await executeApi(\"GetUsers\", { email });\n```\n\n`useApiData` returns `{ data, loading, fetching, isError, isSuccess, isStale, error, status, fetchStatus, refetch, cancel }`. `data` persists across background refetches (stale-while-revalidate). `loading` is true on first fetch with no cached data. `fetching` is true during any fetch including background refetch. `status` is `\"pending\"` | `\"success\"` | `\"error\"`. `fetchStatus` is `\"idle\"` | `\"fetching\"`. `cancel()` is synchronous (returns `void`).\n\n`useApi` returns `{ run, cancel, reset, loading, data, error, status, variables }`. `data` persists across re-runs (shows previous result while loading). `error` holds the most recent failure, or `undefined` on success. `status` is `\"idle\"` | `\"pending\"` | `\"success\"` | `\"error\"`. `variables` holds the inputs from the most recent `run()`. `reset()` clears all state back to idle. HMR double-fetch prevention is built in \u2014 no `useRef` guard needed.\n\n### useApiData Options\n\n```typescript\nconst { data } = useApiData(\"GetUsers\", { email }, {\n enabled: true, // Skip fetching when false (conditional fetching)\n staleTime: 0, // Ms before cached data is considered stale (0 = always refetch)\n retry: 3, // Retry attempts on failure (false to disable)\n retryDelay: (n) => ..., // Custom delay strategy (default: exponential backoff, max 30s)\n refetchOnWindowFocus: false, // Refetch when tab regains focus\n refetchOnReconnect: true, // Refetch when network reconnects\n refetchInterval: false, // Polling interval in ms (false = disabled)\n structuralSharing: false, // Deep-compare to preserve object identity\n placeholderData: undefined, // Shown while the first fetch is in progress\n});\n```\n\n### Cache invalidation and optimistic updates\n\n**Same scope as the read**: After a mutation, prefer `refetch()` from the `useApiData` that loads the data (same component or wherever you can pass `refetch`).\n\n```typescript\nimport { toast } from \"sonner\";\n\nconst { data: orders, refetch } = useApiData(\"GetOrders\", { status });\nconst { run: createOrder } = useApi(\"CreateOrder\");\n\nconst handleSubmit = useCallback(async () => {\n try {\n await createOrder({ item, qty });\n await refetch();\n } catch (error) {\n const message =\n error && typeof error === \"object\" && \"message\" in error\n ? String((error as { message: unknown }).message)\n : String(error);\n toast.error(\"Error creating order: \" + message);\n }\n}, [item, qty, createOrder, refetch]);\n```\n\n**Cross-scope or broad invalidation**: Use `queryClient.invalidateQueries` when you cannot call that `refetch` (mutation in a different place than the `useApiData`, or you need every cached variant of an API to refetch). For **`queryClient.invalidateQueries`, `buildCacheKey`, `setQueryData`, and any other `queryClient` usage, always import `queryClient` from `@superblocksteam/library`**.\n\n```typescript\nimport { queryClient } from \"@superblocksteam/library\";\nimport { toast } from \"sonner\";\n\nconst { run: createOrder } = useApi(\"CreateOrder\");\n\nconst handleSubmit = useCallback(async () => {\n try {\n await createOrder({ item, qty });\n await queryClient.invalidateQueries(\"GetOrders\");\n } catch (error) {\n const message =\n error && typeof error === \"object\" && \"message\" in error\n ? String((error as { message: unknown }).message)\n : String(error);\n toast.error(\"Error creating order: \" + message);\n }\n}, [item, qty, createOrder]);\n```\n\n```typescript\n// Optimistic update: set cached data without refetching\nimport { queryClient } from \"@superblocksteam/library\";\n\nconst cacheKey = queryClient.buildCacheKey(\"GetOrders\", { status: \"active\" });\nqueryClient.setQueryData(cacheKey, (old) => ({\n ...old,\n orders: [...old.orders, newOrder],\n}));\n```\n\n### Critical API Rules\n\n<!-- API_NAME_GUIDANCE -->\n\n2. **Use `useApiData` for data loading, `useApi` for mutations**: For data fetching, use `useApiData(\"GetUsers\", { email })`. Only use `useApi` for event-driven actions (button clicks, form submissions). Use `executeApi` for API calls outside React components.\n3. **Use the hook's state fields**: Do NOT create separate `useState` for loading or response data \u2014 the hooks manage this for you\n4. **ALWAYS check API interfaces**: NEVER guess the response structure\n5. **Include all parameters**: Even optional ones should be passed as `null`\n6. **ALWAYS use try/catch**: In imperative mode, APIs throw errors when they fail - wrap ALL `run()` calls in try/catch blocks. In declarative mode, check the `error` field for failure details.\n\n### File Handling\n\n**CRITICAL: Files must be wrapped in `{ files: [...] }` format:**\n\n```tsx\n// \u2705 CORRECT - Frontend: wrap files in { files: [...] }\nconst response = await runUploadApi({ userFile: { files: selectedFiles } });\n\n// \u274C WRONG - backend cannot process unwrapped files\nconst response = await runUploadApi({ userFile: selectedFiles });\n```\n\n## Platform Hooks and Functions\n\nAvailable hooks and functions from `@superblocksteam/library`:\n\n### User and Group Context\n\n```typescript\nimport {\n useSuperblocksUser,\n useSuperblocksGroups,\n} from \"@superblocksteam/library\";\n\n// Get current user info\nconst user = useSuperblocksUser();\n// user.name, user.email, user.id, user.groups, user.username, user.metadata\n\n// Get organization groups\nconst groups = useSuperblocksGroups();\n```\n\n### Environments and Data Tags\n\nConcepts & terminology:\n\n- `Data tags` are labels for different data segments, such as `Staging`, `Production`, or `us-east`.\n- `Profiles` is the legacy term for the same concept. If a user asks about profiles, treat that as data tags.\n- `Environments` are `Edit`, `Preview`, and `Production`, and each data tag is allowed in one or more of those environments.\n- Clark operates in `Edit` mode, so the data tags visible in app context represent the tags available in `Edit`; do not assume this is the same set that will be available in `Preview` or `Production`.\n- When implementing app code, prefer `useSuperblocksDataTags()`, `dataTags`, and `setDataTag()`.\n\nThe `DataTag` type has these fields (it is an alias for `Profile` from `@superblocksteam/shared`):\n\n```typescript\ntype DataTag = {\n id: string;\n key: string;\n displayName: string; // human-readable label \u2014 use this for display, NOT \"name\"\n description: string;\n type: \"RESERVED\" | \"CUSTOM\";\n};\n```\n\nRelevant hooks and functions:\n\n```typescript\nimport { useSuperblocksDataTags, getAppMode } from \"@superblocksteam/library\";\nimport type { DataTag, DataTags } from \"@superblocksteam/library\";\n\n// Manage data tags\nconst { dataTags, setDataTag } = useSuperblocksDataTags();\n// dataTags.available: DataTag[] \u2014 all tags in the current environment\n// dataTags.selected: DataTag | undefined \u2014 currently active tag (may be undefined)\n// dataTags.default: DataTag \u2014 the default tag\n// setDataTag(dataTag.key) \u2014 switch the active tag by key\n\n// Read the current app mode when reasoning about environment-specific behavior\nconst appMode = getAppMode();\n```\n\nExample \u2014 data tag switcher using a Select:\n\n```tsx\nconst { dataTags, setDataTag } = useSuperblocksDataTags();\n\n<Select value={dataTags?.selected?.key} onValueChange={setDataTag}>\n {dataTags?.available.map((tag) => (\n <SelectItem key={tag.key} value={tag.key}>\n {tag.displayName}\n </SelectItem>\n ))}\n</Select>;\n```\n\n### Embedded Applications\n\n**For embedded applications**, see the `references/embedding.md` file for `useEmbedProperties`, `useEmbedEvent`, and `useEmitEmbedEvent` hooks.\n\n## Logging Out of Integrations\n\nWhen building a \"Log out\" or \"Sign out\" button for apps that use OAuth-like integrations, use `logoutIntegrations` from the library to clear OAuth tokens:\n\n```typescript\nimport { logoutIntegrations } from \"@superblocksteam/library\";\n\nconst handleLogout = async () => {\n await logoutIntegrations();\n // Optionally redirect or show confirmation\n};\n```\n\n## Application Architecture\n\n### {{ui}}App.tsx Layout Structure\n\n**For single-page applications:**\n\n- Put all content in `{{ui}}pages/<pageName>/index.tsx`\n- Use components to compose and build the page\n- Keep `{{ui}}App.tsx` minimal with just `<AppProvider>` and `<Outlet />`\n\n**For multi-page applications:**\n\n- Put shared navigation/layout in `{{ui}}App.tsx` (sidebars, headers, footers)\n- Always include `<Outlet />` for page content\n- Always include `<AppProvider />` wrapper\n- Individual pages focus on content, not layout\n\n```tsx\n// \u2705 CORRECT - Multi-page {{ui}}App.tsx layout\n<AppProvider>\n <div className=\"flex flex-row size-screen\">\n {/* Sidebar - persistent across all pages */}\n <div className=\"flex bg-sidebar border-r w-[250px]\">\n <Navigation />\n </div>\n\n {/* Main content area */}\n <div className=\"flex flex-1 h-full flex-col\">\n {/* Header - persistent across all pages */}\n <div className=\"flex bg-header border-b h-[60px]\">\n <Header />\n </div>\n\n {/* Page content area */}\n <div className=\"flex p-4 flex-1 overflow-auto\">\n <Outlet />\n </div>\n </div>\n </div>\n</AppProvider>\n```\n\n**PROTECTED: NEVER remove `<AppProvider>` or `<Outlet />` from {{ui}}App.tsx!**\n\n### Page Structure\n\n```tsx\n// \u2705 CORRECT - Page focuses on content only\n<div className=\"flex flex-col gap-4 size-screen overflow-auto\">\n <h1 className=\"text-3xl font-bold\">Dashboard Content</h1>\n <Card>{/* Page-specific content */}</Card>\n</div>\n```\n\n**IMPORTANT**: The first div on the page must have `overflow-auto` so the user's page can scroll.\n\n## Routing\n\nUse `react-router@7` in data mode. Standard patterns apply:\n\n- `useNavigate()` for programmatic navigation\n- `useParams()` for route parameters\n- `useSearchParams()` for query strings\n\n**If you add new pages or rename files, you MUST update the router.**\n\nFile edits change the live app immediately, so keep every intermediate state valid:\n\n- Create a page before adding its router reference.\n- Remove router references before deleting a page.\n- To rename a page, create the new page, repoint the router, then delete the old page.\n- Wait for each dependent tool call to return without an error before starting the next step. Batch only independent tool calls.\n\nKeep route elements statically discoverable by the Superblocks editor:\n\n- Prefer direct JSX such as `element: <Page />`.\n- Keep code splitting with top-level `lazy(() => import(\"./pages/Page\"))`\n bindings.\n- Keep page routes as `children` of the `App` layout route, and put one\n `<Suspense>` boundary around `<Outlet />` inside `<AppProvider>` in `{{ui}}App.tsx`.\n- A single-argument wrapper such as `element: withSuspense(Page)` is also\n discoverable when `Page` is directly imported or bound by top-level\n `React.lazy`.\n- Do not unwrap working routes that use this shape.\n- Do not use nested or multi-argument calls such as\n `element: withSuspense(makePage(Home))` or\n `element: withSuspense(Home, opts)`.\n- Do not wrap page JSX in another element, such as\n `element: <PageErrorBoundary><Page /></PageErrorBoundary>`.\n\n```tsx\nimport { App as AppProvider } from \"@superblocksteam/library\";\nimport { Suspense, lazy } from \"react\";\nimport { Outlet, createBrowserRouter } from \"react-router\";\n\nconst Page = lazy(() => import(\"./pages/Page\"));\n\nconst router = createBrowserRouter([\n {\n Component: App,\n children: [{ path: \"/page\", element: <Page /> }],\n },\n]);\n\nfunction App() {\n return (\n <AppProvider className=\"h-full w-full\">\n <Suspense fallback={null}>\n <Outlet />\n </Suspense>\n </AppProvider>\n );\n}\n```\n\n### Page names\n\nA new app ships with one empty page at `{{ui}}pages/Home`, bound as the `/` index route:\n\n```tsx\n{ path: \"/\", index: true, lazy: () => import(\"./pages/Home/index.js\").then((mod) => ({ Component: mod.default })) }\n```\n\n- **Single-page app**: build the landing content in `{{ui}}pages/Home` and keep it named `Home` unless the user asks for something else\n- **Multi-page app**: every page you add beyond the starter gets a descriptive PascalCase name for what it shows (`Dashboard`, `Inventory`, `TopBuilders`), and the starter page keeps `Home` as the app's landing page \u2014 rename it too if the app's landing route is really something else (e.g. `Overview`), keeping it bound to the `/` index route\n- Page names are user-visible in the editor's page switcher, so name them the way you'd label a nav item\n- Never name a page `Page1`, `Page2`, ...\n\n## Design System (Tailwind CSS v4)\n\n**All design tokens are defined in `{{ui}}index.css`**. Apps come with a professional black-and-white theme by default.\n\n### Semantic Tokens Rule\n\n**ALWAYS use semantic tokens** \u2014 NEVER raw Tailwind utilities:\n\n```tsx\n// \u2705 CORRECT\n<Card className=\"bg-background text-foreground border border-border\" />\n\n// \u274C WRONG\n<Card className=\"bg-white text-black border-gray-200\" />\n```\n\n### When to Modify {{ui}}index.css\n\nOnly modify when:\n\n- User explicitly requests branding/theme changes\n- Replicating a specific brand look (e.g., Yelp, Instacart)\n- Migrating an attached third-party app \u2014 see `skills/system/third-party-migration/SKILL.md` (its theme-port rules override the Modification Rules below \u2014 copy source tokens as-is, do not convert to OKLCH or trim the palette)\n- Feature requests (lists, filters, CRUD) do NOT require changes\n\n**Modification Rules:**\n\n- All colors must be in OKLCH format\n- Use semantic names (`--color-warning`, `--shadow-elevated`)\n- Do not remove or rename existing tokens\n- Limit color palette to 5 colors max\n- Avoid gradients unless explicitly requested\n- Minimal font sizes (3 max: body, section heading, main heading)\n\n### Component Variants\n\nUse or create variants instead of one-off styles:\n\n```tsx\n// \u274C WRONG - Hacky inline overrides\n<Button className=\"text-white border-white hover:bg-white\" />\n\n// \u2705 CORRECT - Use a variant\n<Button variant=\"secondary\" />\n```\n\n## Icons\n\nUse icons from Lucide React library:\n\n```tsx\nimport { Icon } from \"@/components/ui/icon\";\n<Icon icon=\"heart\" />;\n\nimport { Button } from \"@/components/ui/button\";\n<Button>\n <Icon icon=\"plus\" /> Add Item\n</Button>;\n```\n\n**Always use icons rather than emojis unless explicitly requested.**\n\nUse current Lucide icon names, not stale aliases: use `house` instead of `home`, `life-buoy` instead of `help-circle` or `circle-help`, and `chart-column-big` instead of `bar-chart-3`.\n\nWhen storing icon names as data (arrays/objects), type them with `IconName`. **`IconName` is a TYPE-only export \u2014 you MUST import it with the `type` keyword.** A value import compiles but CRASHES the app at runtime (`does not provide an export named 'IconName'`) and leaves the preview blank, which blocks UI verification. If you hit that error, read `skills/system/common-import-issues/SKILL.md`.\n\n```tsx\n// \u2705 CORRECT \u2014 type-only import\nimport type { IconName } from \"lucide-react/dynamic\";\n\nconst navItems: { icon: IconName; label: string }[] = [\n { icon: \"house\", label: \"Home\" },\n { icon: \"settings\", label: \"Settings\" },\n];\n\n// \u274C WRONG \u2014 value import crashes the app at runtime and blanks the preview\nimport { IconName } from \"lucide-react/dynamic\";\n```\n\n## Custom Components\n\n**CRITICAL: Component composition is MANDATORY. DO NOT create monolithic pages.**\n\n### The Composition Rule\n\nWhen building ANY feature:\n\n1. **First**, identify reusable parts (list items, cards, forms, filters, headers)\n2. **Then**, create separate component files\n3. **Finally**, compose them together in the page\n\n### When to Create Components\n\n- **Rendering any list** - Extract to a component\n- **Building cards/complex UI** - Each card type is a component\n- **Creating forms** - Form sections are components\n- **Adding filters/headers** - These are components\n- **Page has >50 lines JSX** - Break it down\n\n### Component Boundary Rules\n\n**Inside a custom component:**\n\n- \u2705 Use React hooks (useState, useReducer, useEffect, etc.)\n- \u2705 Use local React state\n- \u2705 Use internal helper components\n- \u2705 Use other registered components\n- \u274C CANNOT call APIs - must pass data into the component\n\n### Example: Proper Component Structure\n\n```tsx\n// {{ui}}components/ProductCard/index.tsx - Extract to component\nimport { Card } from \"@/components/ui/card\";\nimport { Button } from \"@/components/ui/button\";\n\ntype ProductCardProps = {\n product: {\n id: string;\n name: string;\n image: string;\n };\n};\n\nexport default function ProductCard(props: ProductCardProps) {\n return (\n <Card>\n <img src={props.product.image} />\n <h3 className=\"text-lg font-semibold\">{props.product.name}</h3>\n <Button>Add</Button>\n </Card>\n );\n}\n\n// {{ui}}pages/Products/index.tsx - Clean composition\nimport ProductCard from \"@/components/ProductCard\";\n\nconst ProductsPage = () => {\n const [products, setProducts] = useState([]);\n\n return (\n <div className=\"grid grid-cols-3 gap-4\">\n {products.map((p) => (\n <ProductCard key={p.id} product={p} />\n ))}\n </div>\n );\n};\n```\n\n## Visual Excellence\n\n**Prioritize visual excellence from the start:**\n\n1. **Design System Enhancement**: Start by enhancing `{{ui}}index.css` with app-specific colors that match the target aesthetic\n2. **Professional Layout Architecture**: Use sophisticated layouts with proper spacing, responsive design\n3. **Rich Interactive Components**: Leverage advanced components with proper variants and states\n4. **Visual Polish**: Add shadows, smooth transitions, skeleton loaders, hover effects, typography hierarchy\n5. **Real-World UI Patterns**: Create layouts that feel like professional applications\n\n**Make applications that look and feel like real, polished products - not basic wireframes.**\n\n### Loading States\n\nLoading behavior must differ based on whether data has already been fetched:\n\n**Initial load (no data yet):** Show skeleton shimmers that mirror the shape of the final content. Never show a blank screen or a generic spinner.\n\n**Refetch / background refresh (data already exists):** Keep showing the existing data. Use `fetching` from `useApiData` to apply a subtle visual indicator: light opacity (e.g. `opacity-70`) to signal a refresh is in progress, plus a non-blocking label such as a small inline spinner, a thin progress bar, or an \"Updating\u2026\" label. **Do not** use `pointer-events-none` or otherwise disable the content \u2014 it must remain fully interactive during refetch.\n\nUse `loading` and `fetching` from `useApiData` to distinguish these states:\n\n```tsx\nconst { data, loading, fetching, isError, error } = useApiData(\"GetOrders\", {\n status: statusFilter,\n search,\n});\n\n// Initial load \u2014 no data yet \u2192 show skeleton placeholder\nif (loading) {\n return <OrderTableSkeleton />;\n}\n\nif (isError) return <ErrorBanner error={error} />;\n\n// Refetch \u2014 data exists \u2192 subtle opacity + indicator, table stays interactive\nreturn (\n <div>\n {fetching && <div className=\"text-xs text-muted-foreground\">Updating\u2026</div>}\n <div className={fetching ? \"opacity-70\" : \"\"}>\n <OrderTable orders={data.orders} />\n </div>\n </div>\n);\n```\n\n```tsx\n// \u274C WRONG \u2014 pointer-events-none disables the table, making it feel broken\nreturn (\n <div className={fetching ? \"pointer-events-none\" : \"\"}>\n <OrderTable orders={data.orders} />\n </div>\n);\n\n// \u274C WRONG \u2014 no loading feedback when filters change\nif (loading) return <Skeleton />;\nreturn <OrderTable orders={data.orders} />;\n```\n\n#### Table Loading Rules\n\n- **Do not** use `pointer-events-none` on a table during loading \u2014 this disables interaction and makes the UI feel broken. During refetch, apply a subtle opacity (e.g. `opacity-70`) plus a non-blocking indicator (e.g., an \"Updating\u2026\" label or a thin progress bar). The table must remain clickable, sortable, and scrollable.\n- **Do not** replace a populated table with a full skeleton on refetch \u2014 this causes disorienting content flashes.\n- **Skeleton tables are only for initial load** (`loading` is true) when there is no data to display yet. Build them to match the real table's column structure (header + a few placeholder rows).\n\n### Efficient Loading Patterns\n\n1. **Always show loading indicators on refetch**: When inputs change (e.g. filters, search), show a non-blocking visual indicator while new data loads. Use `fetching` from `useApiData`.\n2. **Loading State Hierarchy**:\n - No data yet (`loading`) \u2192 Full skeleton placeholder\n - Has data, refetching (`fetching` && !`loading`) \u2192 Keep showing current data with a subtle visual indicator: light opacity (e.g. `opacity-70`) plus a non-blocking label (e.g. \"Updating\u2026\" text, thin progress bar, inline spinner). Do not use `pointer-events-none` \u2014 the content must remain fully interactive.\n - Error state (`isError`) \u2192 Show error with retry option, optionally keep stale data visible\n3. **Debounce Rapid Requests**: Prevent multiple API calls in short succession\n4. **Use useApiData for automatic refetching**: `useApiData` auto-refetches when inputs change and supports `staleTime`, `refetchOnWindowFocus`, `refetchOnReconnect`, `refetchInterval`, and `retry` options.\n\n## Performance Rules\n\n### 1. ALWAYS Paginate Tables and Lists\n\n**NEVER render more than 50 rows without pagination.** Always add client-side pagination:\n\n```tsx\nfunction PaginatedTable({ data }: { data: any[] }) {\n const PAGE_SIZE = 20;\n const [page, setPage] = useState(0);\n const totalPages = Math.ceil(data.length / PAGE_SIZE);\n const pageData = useMemo(\n () => data.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE),\n [data, page],\n );\n\n useEffect(() => {\n setPage(0);\n }, [data.length]);\n\n return (\n <>\n <Table>{/* render pageData rows */}</Table>\n <span>\n Page {page + 1} of {totalPages} ({data.length} total rows)\n </span>\n <Button\n onClick={() => setPage((p) => Math.max(0, p - 1))}\n disabled={page === 0}\n >\n Previous\n </Button>\n <Button\n onClick={() => setPage((p) => Math.min(totalPages - 1, p + 1))}\n disabled={page >= totalPages - 1}\n >\n Next\n </Button>\n </>\n );\n}\n```\n\nFor 200+ rows, prefer **server-side pagination**; use cursor/keyset pagination for high offsets instead of `LIMIT`/`OFFSET`.\n\nFor 500+ item lists where pagination doesn't fit the UX (chat messages, infinite scroll feeds, large dropdowns), use **virtualization** (`react-virtuoso` or `@tanstack/react-virtual`) to render only the items visible in the viewport.\n\n### 2. ALWAYS Debounce Input-Driven API Calls\n\n**NEVER call an API directly from onChange.** Debounce search/filter inputs with a 300ms timer:\n\n```tsx\nfunction DebouncedSearch({ onSearch }: { onSearch: (query: string) => void }) {\n const [localValue, setLocalValue] = useState(\"\");\n const timerRef = useRef<ReturnType<typeof setTimeout>>();\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n setLocalValue(e.target.value);\n clearTimeout(timerRef.current);\n timerRef.current = setTimeout(() => onSearch(e.target.value), 300);\n },\n [onSearch],\n );\n useEffect(() => () => clearTimeout(timerRef.current), []);\n\n return (\n <Input value={localValue} onChange={handleChange} placeholder=\"Search...\" />\n );\n}\n```\n\n### 3. Memoize Expensive Renders\n\nUse memoization (`memo()`, `useMemo`, `useCallback`) when it prevents measurable re-renders or expensive recomputation:\n\n```tsx\nconst OrderRow = memo(function OrderRow({\n order,\n onSelect,\n}: {\n order: Order;\n onSelect: (id: string) => void;\n}) {\n return (\n <TableRow onClick={() => onSelect(order.id)}>\n <TableCell>{order.id}</TableCell>...\n </TableRow>\n );\n});\n\nconst handleSelect = useCallback((id: string) => {\n setSelectedId(id);\n}, []);\n\nconst filtered = useMemo(\n () =>\n orders.filter((o) => o.status === status).sort((a, b) => b.total - a.total),\n [orders, status],\n);\n```\n\n### 4. ALWAYS Clean Up Side Effects\n\nClean up timers, event listeners, and subscriptions in `useEffect` return functions:\n\n```tsx\nuseEffect(() => {\n const handler = (e: KeyboardEvent) => {\n /* ... */\n };\n window.addEventListener(\"keydown\", handler);\n return () => window.removeEventListener(\"keydown\", handler);\n}, []);\n```\n\n### 5. Cancel In-Flight API Requests on Unmount\n\nFor search/filter patterns, prefer `useApiData` \u2014 it handles cancellation and cleanup automatically:\n\n```tsx\n// useApiData: auto-fetches and cancels on unmount/input change\nconst { data, fetching } = useApiData(\"SearchProducts\", { query });\n```\n\nFor imperative usage with manual cleanup, use the `cancel()` function:\n\n```tsx\nconst { run, cancel } = useApi(\"SearchProducts\");\n\nuseEffect(() => {\n if (!query) return;\n run({ query }).catch(console.error);\n return () => {\n cancel().catch(() => {});\n };\n}, [query, run, cancel]);\n```\n";
1
+ export declare const content = "---\nname: superblocks-frontend\ndescription: |\n Build frontend UI using React, Tailwind CSS, and Superblocks components. Essential for connecting Superblocks UIs with APIs.\n Use when creating pages, components, handling user interactions, or working with the design system.\nreadOnly: true\nmetadata:\n author: superblocks\n version: \"1.0\"\n---\n\n# Superblocks Frontend Development\n\nThis skill covers building frontend UI for Superblocks applications using React, Tailwind CSS v4, and the Superblocks component library.\n\n## Platform Overview\n\nThis is a React-based web application platform. Use standard React patterns:\n\n- `useState`, `useEffect`, `useCallback`, `useMemo`\n- Event handlers and controlled components\n- JSX with Tailwind CSS classes\n\n## Using APIs from Frontend\n\n<!-- API_PRIMITIVE_IMPORT_GUIDANCE -->\n\n- **`useApiData`** \u2014 declarative reads with SWR caching (preferred for data loading)\n- **`useApi`** \u2014 imperative mutations (for event-driven actions)\n- **`executeApi`** \u2014 plain Promise outside React (utility functions, event handlers without hooks)\n\n**Data loading** \u2014 auto-fetches on mount and when inputs change (preferred for reads):\n\n```typescript\n// Auto-fetches when email or name changes. No useEffect or HMR guards needed.\n// IMPORTANT: Always use `fetching` to show a loading indicator during refetches.\nconst { data, loading, fetching, isError, error } = useApiData(\"GetUsers\", {\n email,\n name,\n});\n```\n\n**Mutations** \u2014 call `run()` manually (for event-driven actions like form submissions):\n\n```typescript\nimport { toast } from \"sonner\";\n\nconst { run: createOrder, loading } = useApi(\"CreateOrder\");\n\nconst handleSubmit = useCallback(async () => {\n try {\n await createOrder({ item, qty });\n } catch (error) {\n const message =\n error && typeof error === \"object\" && \"message\" in error\n ? String((error as { message: unknown }).message)\n : String(error);\n toast.error(\"Error creating order: \" + message);\n }\n}, [item, qty, createOrder]);\n```\n\n**Outside React** \u2014 call APIs as a plain Promise (utility functions, event handlers):\n\n```typescript\nconst result = await executeApi(\"GetUsers\", { email });\n```\n\n`useApiData` returns `{ data, loading, fetching, isError, isSuccess, isStale, error, status, fetchStatus, refetch, cancel }`. `data` persists across background refetches (stale-while-revalidate). `loading` is true on first fetch with no cached data. `fetching` is true during any fetch including background refetch. `status` is `\"pending\"` | `\"success\"` | `\"error\"`. `fetchStatus` is `\"idle\"` | `\"fetching\"`. `cancel()` is synchronous (returns `void`).\n\n`useApi` returns `{ run, cancel, reset, loading, data, error, status, variables }`. `data` persists across re-runs (shows previous result while loading). `error` holds the most recent failure, or `undefined` on success. `status` is `\"idle\"` | `\"pending\"` | `\"success\"` | `\"error\"`. `variables` holds the inputs from the most recent `run()`. `reset()` clears all state back to idle. HMR double-fetch prevention is built in \u2014 no `useRef` guard needed.\n\n### useApiData Options\n\n```typescript\nconst { data } = useApiData(\"GetUsers\", { email }, {\n enabled: true, // Skip fetching when false (conditional fetching)\n staleTime: 0, // Ms before cached data is considered stale (0 = always refetch)\n retry: 3, // Retry attempts on failure (false to disable)\n retryDelay: (n) => ..., // Custom delay strategy (default: exponential backoff, max 30s)\n refetchOnWindowFocus: false, // Refetch when tab regains focus\n refetchOnReconnect: true, // Refetch when network reconnects\n refetchInterval: false, // Polling interval in ms (false = disabled)\n structuralSharing: false, // Deep-compare to preserve object identity\n placeholderData: undefined, // Shown while the first fetch is in progress\n});\n```\n\n### Cache invalidation and optimistic updates\n\n**Same scope as the read**: After a mutation, prefer `refetch()` from the `useApiData` that loads the data (same component or wherever you can pass `refetch`).\n\n```typescript\nimport { toast } from \"sonner\";\n\nconst { data: orders, refetch } = useApiData(\"GetOrders\", { status });\nconst { run: createOrder } = useApi(\"CreateOrder\");\n\nconst handleSubmit = useCallback(async () => {\n try {\n await createOrder({ item, qty });\n await refetch();\n } catch (error) {\n const message =\n error && typeof error === \"object\" && \"message\" in error\n ? String((error as { message: unknown }).message)\n : String(error);\n toast.error(\"Error creating order: \" + message);\n }\n}, [item, qty, createOrder, refetch]);\n```\n\n**Cross-scope or broad invalidation**: Use `queryClient.invalidateQueries` when you cannot call that `refetch` (mutation in a different place than the `useApiData`, or you need every cached variant of an API to refetch). For **`queryClient.invalidateQueries`, `buildCacheKey`, `setQueryData`, and any other `queryClient` usage, always import `queryClient` from `@superblocksteam/library`**.\n\n```typescript\nimport { toast } from \"sonner\";\n\nimport { queryClient } from \"@superblocksteam/library\";\n\nconst { run: createOrder } = useApi(\"CreateOrder\");\n\nconst handleSubmit = useCallback(async () => {\n try {\n await createOrder({ item, qty });\n await queryClient.invalidateQueries(\"GetOrders\");\n } catch (error) {\n const message =\n error && typeof error === \"object\" && \"message\" in error\n ? String((error as { message: unknown }).message)\n : String(error);\n toast.error(\"Error creating order: \" + message);\n }\n}, [item, qty, createOrder]);\n```\n\n#### Optimistic updates\n\nWhen a mutation changes rows the page is already showing (toggling a checkbox, editing a field inline, deleting or adding a list item), change the cached data first so the UI updates at once, run the mutation, then refetch. If the mutation fails, restore the previous data and show `toast.error`. Do not make the user wait for the write and the refetch before the row changes.\n\n```typescript\nimport { toast } from \"sonner\";\n\nimport { queryClient } from \"@superblocksteam/library\";\n\ntype OrdersData = { orders: Array<{ id: number; shipped: boolean }> };\n\nconst inputs = { status: \"active\" };\nconst { data, refetch } = useApiData(\"GetOrders\", inputs);\nconst { run: updateOrder } = useApi(\"UpdateOrder\");\n\nconst handleToggle = useCallback(\n async (id: number, shipped: boolean) => {\n const cacheKey = queryClient.buildCacheKey(\"GetOrders\", inputs);\n // Undefined when nothing is cached yet (first load, after an invalidation).\n const previous = queryClient.getQueryData<OrdersData>(cacheKey);\n if (previous) {\n queryClient.setQueryData<OrdersData>(cacheKey, {\n ...previous,\n orders: previous.orders.map((o) =>\n o.id === id ? { ...o, shipped } : o,\n ),\n });\n }\n try {\n await updateOrder({ id, shipped });\n await refetch();\n } catch (error) {\n // updateOrder throws the API error object ({ code, message }), not an Error.\n const message =\n error && typeof error === \"object\" && \"message\" in error\n ? String((error as { message: unknown }).message)\n : String(error);\n if (previous) {\n queryClient.setQueryData<OrdersData>(cacheKey, previous);\n }\n toast.error(\"Could not update order: \" + message);\n }\n },\n [updateOrder, refetch],\n);\n```\n\n- Read the cached data and skip the cache write when there is none, as above. Never read fields off the `old` argument of a `setQueryData` updater without a guard: it can be `undefined`, and a throw there happens outside the `try`, so the write never runs and no toast shows.\n- For deletes, filter the row out of the cached list; for inserts, append it with a temporary id of the same type as the column (for a numeric id, a negative number such as `-Date.now()`, not a string) and let the refetch replace it. Do not send writes for a row that still has the temporary id; disable its controls until the refetch lands.\n- Fire-and-confirm is fine when nothing on the page shows the change yet: a create dialog that closes on success, a one-shot action such as sending a message, or a settings save with no preview.\n\n### Critical API Rules\n\n<!-- API_NAME_GUIDANCE -->\n\n2. **Use `useApiData` for data loading, `useApi` for mutations**: For data fetching, use `useApiData(\"GetUsers\", { email })`. Only use `useApi` for event-driven actions (button clicks, form submissions). Use `executeApi` for API calls outside React components.\n3. **Use the hook's state fields**: Do NOT create separate `useState` for loading or response data \u2014 the hooks manage this for you\n4. **ALWAYS check API interfaces**: NEVER guess the response structure\n5. **Include all parameters**: Even optional ones should be passed as `null`\n6. **ALWAYS use try/catch**: In imperative mode, APIs throw errors when they fail - wrap ALL `run()` calls in try/catch blocks. In declarative mode, check the `error` field for failure details.\n\n### File Handling\n\n**CRITICAL: Files must be wrapped in `{ files: [...] }` format:**\n\n```tsx\n// \u2705 CORRECT - Frontend: wrap files in { files: [...] }\nconst response = await runUploadApi({ userFile: { files: selectedFiles } });\n\n// \u274C WRONG - backend cannot process unwrapped files\nconst response = await runUploadApi({ userFile: selectedFiles });\n```\n\n## Platform Hooks and Functions\n\nAvailable hooks and functions from `@superblocksteam/library`:\n\n### User and Group Context\n\n```typescript\nimport {\n useSuperblocksUser,\n useSuperblocksGroups,\n} from \"@superblocksteam/library\";\n\n// Get current user info\nconst user = useSuperblocksUser();\n// user.name, user.email, user.id, user.groups, user.username, user.metadata\n\n// Get organization groups\nconst groups = useSuperblocksGroups();\n```\n\n### Environments and Data Tags\n\nConcepts & terminology:\n\n- `Data tags` are labels for different data segments, such as `Staging`, `Production`, or `us-east`.\n- `Profiles` is the legacy term for the same concept. If a user asks about profiles, treat that as data tags.\n- `Environments` are `Edit`, `Preview`, and `Production`, and each data tag is allowed in one or more of those environments.\n- Clark operates in `Edit` mode, so the data tags visible in app context represent the tags available in `Edit`; do not assume this is the same set that will be available in `Preview` or `Production`.\n- When implementing app code, prefer `useSuperblocksDataTags()`, `dataTags`, and `setDataTag()`.\n\nThe `DataTag` type has these fields (it is an alias for `Profile` from `@superblocksteam/shared`):\n\n```typescript\ntype DataTag = {\n id: string;\n key: string;\n displayName: string; // human-readable label \u2014 use this for display, NOT \"name\"\n description: string;\n type: \"RESERVED\" | \"CUSTOM\";\n};\n```\n\nRelevant hooks and functions:\n\n```typescript\nimport { useSuperblocksDataTags, getAppMode } from \"@superblocksteam/library\";\nimport type { DataTag, DataTags } from \"@superblocksteam/library\";\n\n// Manage data tags\nconst { dataTags, setDataTag } = useSuperblocksDataTags();\n// dataTags.available: DataTag[] \u2014 all tags in the current environment\n// dataTags.selected: DataTag | undefined \u2014 currently active tag (may be undefined)\n// dataTags.default: DataTag \u2014 the default tag\n// setDataTag(dataTag.key) \u2014 switch the active tag by key\n\n// Read the current app mode when reasoning about environment-specific behavior\nconst appMode = getAppMode();\n```\n\nExample \u2014 data tag switcher using a Select:\n\n```tsx\nconst { dataTags, setDataTag } = useSuperblocksDataTags();\n\n<Select value={dataTags?.selected?.key} onValueChange={setDataTag}>\n <SelectTrigger>\n <SelectValue placeholder=\"Select a data tag\" />\n </SelectTrigger>\n <SelectContent>\n {dataTags?.available.map((tag) => (\n <SelectItem key={tag.key} value={tag.key}>\n {tag.displayName}\n </SelectItem>\n ))}\n </SelectContent>\n</Select>;\n```\n\n### Embedded Applications\n\n**For embedded applications**, see the `references/embedding.md` file for `useEmbedProperties`, `useEmbedEvent`, and `useEmitEmbedEvent` hooks.\n\n## Logging Out of Integrations\n\nWhen building a \"Log out\" or \"Sign out\" button for apps that use OAuth-like integrations, use `logoutIntegrations` from the library to clear OAuth tokens:\n\n```typescript\nimport { logoutIntegrations } from \"@superblocksteam/library\";\n\nconst handleLogout = async () => {\n await logoutIntegrations();\n // Optionally redirect or show confirmation\n};\n```\n\n## Application Architecture\n\n### {{ui}}App.tsx Layout Structure\n\n**For single-page applications:**\n\n- Put all content in `{{ui}}pages/<pageName>/index.tsx`\n- Use components to compose and build the page\n- Keep `{{ui}}App.tsx` minimal with `<AppProvider>`, `<Outlet />`, and `<Toaster />`\n\n**For multi-page applications:**\n\n- Put shared navigation/layout in `{{ui}}App.tsx` (sidebars, headers, footers)\n- Always include `<Outlet />` for page content\n- Always include `<AppProvider />` wrapper\n- Always include `<Toaster />` from `{{ui}}components/common/sonner` (sibling of `<AppProvider>`)\n- Individual pages focus on content, not layout\n\n```tsx\n// \u2705 CORRECT - Multi-page {{ui}}App.tsx layout\n<>\n <AppProvider>\n <div className=\"flex flex-row h-full w-full\">\n {/* Sidebar - persistent across all pages */}\n <div className=\"flex bg-sidebar border-r w-[250px]\">\n <Navigation />\n </div>\n\n {/* Main content area */}\n <div className=\"flex flex-1 h-full flex-col\">\n {/* Header - persistent across all pages */}\n <div className=\"flex bg-background border-b h-[60px]\">\n <Header />\n </div>\n\n {/* Page content area */}\n <div className=\"flex p-4 flex-1 overflow-auto\">\n <Outlet />\n </div>\n </div>\n </div>\n </AppProvider>\n <Toaster />\n</>\n```\n\n**PROTECTED: NEVER remove `<AppProvider>`, `<Outlet />`, or `<Toaster />` from {{ui}}App.tsx!**\n\n### Page Structure\n\n```tsx\n// \u2705 CORRECT - Page focuses on content only\n<div className=\"flex flex-col gap-4 h-full w-full overflow-auto\">\n <h1 className=\"text-3xl font-bold\">Dashboard Content</h1>\n <Card>{/* Page-specific content */}</Card>\n</div>\n```\n\n**IMPORTANT**: The first div on the page must have `overflow-auto` so the user's page can scroll.\n\n## Routing\n\nUse `react-router@7` in data mode. Standard patterns apply:\n\n- `useNavigate()` for programmatic navigation\n- `useParams()` for route parameters\n- `useSearchParams()` for query strings\n\n**If you add new pages or rename files, you MUST update the router.**\n\nFile edits change the live app immediately, so keep every intermediate state valid:\n\n- Create a page before adding its router reference.\n- Remove router references before deleting a page.\n- To rename a page, create the new page, repoint the router, then delete the old page.\n- Wait for each dependent tool call to return without an error before starting the next step. Batch only independent tool calls.\n\nKeep route elements statically discoverable by the Superblocks editor:\n\n- Prefer direct JSX such as `element: <Page />`.\n- Keep code splitting with top-level `lazy(() => import(\"./pages/Page\"))`\n bindings.\n- Keep page routes as `children` of the `App` layout route, and put one\n `<Suspense>` boundary around `<Outlet />` inside `<AppProvider>` in `{{ui}}App.tsx`.\n- A single-argument wrapper such as `element: withSuspense(Page)` is also\n discoverable when `Page` is directly imported or bound by top-level\n `React.lazy`.\n- Do not unwrap working routes that use this shape.\n- Do not use nested or multi-argument calls such as\n `element: withSuspense(makePage(Home))` or\n `element: withSuspense(Home, opts)`.\n- Do not wrap page JSX in another element, such as\n `element: <PageErrorBoundary><Page /></PageErrorBoundary>`.\n\n```tsx\nimport { Suspense, lazy } from \"react\";\nimport { Outlet, createBrowserRouter } from \"react-router\";\n\nimport { App as AppProvider } from \"@superblocksteam/library\";\n\nimport { Toaster } from \"./components/common/sonner\";\n\nconst Page = lazy(() => import(\"./pages/Page\"));\n\nconst router = createBrowserRouter([\n {\n Component: App,\n children: [{ path: \"/page\", element: <Page /> }],\n },\n]);\n\nfunction App() {\n return (\n <>\n <AppProvider className=\"h-full w-full\">\n <Suspense fallback={null}>\n <Outlet />\n </Suspense>\n </AppProvider>\n <Toaster />\n </>\n );\n}\n```\n\n### Page names\n\nA new app ships with one empty page at `{{ui}}pages/Home`, bound as the `/` index route:\n\n```tsx\n{ path: \"/\", index: true, lazy: () => import(\"./pages/Home/index.js\").then((mod) => ({ Component: mod.default })) }\n```\n\n- **Single-page app**: build the landing content in `{{ui}}pages/Home` and keep it named `Home` unless the user asks for something else\n- **Multi-page app**: every page you add beyond the starter gets a descriptive PascalCase name for what it shows (`Dashboard`, `Inventory`, `TopBuilders`), and the starter page keeps `Home` as the app's landing page \u2014 rename it too if the app's landing route is really something else (e.g. `Overview`), keeping it bound to the `/` index route\n- Page names are user-visible in the editor's page switcher, so name them the way you'd label a nav item\n- Never name a page `Page1`, `Page2`, ...\n\n## Design System (Tailwind CSS v4)\n\n**All design tokens are defined in `{{ui}}index.css`**. Apps come with a professional black-and-white theme by default.\n\n### Semantic Tokens Rule\n\n**ALWAYS use semantic tokens** \u2014 NEVER raw Tailwind utilities:\n\n```tsx\n// \u2705 CORRECT\n<Card className=\"bg-background text-foreground border border-border\" />\n\n// \u274C WRONG\n<Card className=\"bg-white text-black border-gray-200\" />\n```\n\n### When to Modify {{ui}}index.css\n\nOnly modify when:\n\n- User explicitly requests branding/theme changes\n- Replicating a specific brand look (e.g., Yelp, Instacart)\n- Migrating an attached third-party app \u2014 see `skills/system/third-party-migration/SKILL.md` (its theme-port rules override the Modification Rules below \u2014 copy source tokens as-is, do not convert to OKLCH or trim the palette)\n- Feature requests (lists, filters, CRUD) do NOT require changes\n\n**Modification Rules:**\n\n- All colors must be in OKLCH format\n- Use semantic names (`--color-warning`, `--shadow-elevated`)\n- Do not remove or rename existing tokens\n- Limit color palette to 5 colors max\n- Avoid gradients unless explicitly requested\n- Minimal font sizes (3 max: body, section heading, main heading)\n\n### Component Variants\n\nUse or create variants instead of one-off styles:\n\n```tsx\n// \u274C WRONG - Hacky inline overrides\n<Button className=\"text-white border-white hover:bg-white\" />\n\n// \u2705 CORRECT - Use a variant\n<Button variant=\"secondary\" />\n```\n\n### Base UI Components\n\n<!-- BASE_UI_GUIDANCE -->\n\n## Icons\n\n<!-- ICON_GUIDANCE -->\n\n## Custom Components\n\n**CRITICAL: Component composition is MANDATORY. DO NOT create monolithic pages.**\n\n### The Composition Rule\n\nWhen building ANY feature:\n\n1. **First**, identify reusable parts (list items, cards, forms, filters, headers)\n2. **Then**, create separate component files\n3. **Finally**, compose them together in the page\n\n### When to Create Components\n\n- **Rendering any list** - Extract to a component\n- **Building cards/complex UI** - Each card type is a component\n- **Creating forms** - Form sections are components\n- **Adding filters/headers** - These are components\n- **Page has >50 lines JSX** - Break it down\n\n### Component Boundary Rules\n\n**Inside a custom component:**\n\n- \u2705 Use React hooks (useState, useReducer, useEffect, etc.)\n- \u2705 Use local React state\n- \u2705 Use internal helper components\n- \u2705 Use other registered components\n- \u274C CANNOT call APIs - must pass data into the component\n\n### Example: Proper Component Structure\n\n```tsx\n// {{ui}}components/ProductCard/index.tsx - Extract to component\nimport { Button } from \"@/components/ui/button\";\nimport { Card } from \"@/components/ui/card\";\n\ntype ProductCardProps = {\n product: {\n id: string;\n name: string;\n image: string;\n };\n};\n\nexport default function ProductCard(props: ProductCardProps) {\n return (\n <Card>\n <img src={props.product.image} />\n <h3 className=\"text-lg font-semibold\">{props.product.name}</h3>\n <Button>Add</Button>\n </Card>\n );\n}\n\n// {{ui}}pages/Products/index.tsx - Clean composition\nimport ProductCard from \"@/components/ProductCard\";\n\nconst ProductsPage = () => {\n const [products, setProducts] = useState([]);\n\n return (\n <div className=\"grid grid-cols-3 gap-4\">\n {products.map((p) => (\n <ProductCard key={p.id} product={p} />\n ))}\n </div>\n );\n};\n```\n\n## Visual Excellence\n\n**Prioritize visual excellence from the start:**\n\n1. **Design System Enhancement**: Start by enhancing `{{ui}}index.css` with app-specific colors that match the target aesthetic\n2. **Professional Layout Architecture**: Use sophisticated layouts with proper spacing, responsive design\n3. **Rich Interactive Components**: Leverage advanced components with proper variants and states\n4. **Visual Polish**: Add shadows, smooth transitions, skeleton loaders, hover effects, typography hierarchy\n5. **Real-World UI Patterns**: Create layouts that feel like professional applications\n\n**Make applications that look and feel like real, polished products - not basic wireframes.**\n\n### Loading States\n\nLoading behavior must differ based on whether data has already been fetched:\n\n**Initial load (no data yet):** Show skeleton shimmers that mirror the shape of the final content. Never show a blank screen or a generic spinner.\n\n**Refetch / background refresh (data already exists):** Keep showing the existing data. Use `fetching` from `useApiData` to apply a subtle visual indicator: light opacity (e.g. `opacity-70`) to signal a refresh is in progress, plus a non-blocking label such as a small inline spinner, a thin progress bar, or an \"Updating\u2026\" label. **Do not** use `pointer-events-none` or otherwise disable the content \u2014 it must remain fully interactive during refetch.\n\nUse `loading` and `fetching` from `useApiData` to distinguish these states:\n\n```tsx\nconst { data, loading, fetching, isError, error } = useApiData(\"GetOrders\", {\n status: statusFilter,\n search,\n});\n\n// Initial load \u2014 no data yet \u2192 show skeleton placeholder\nif (loading) {\n return <OrderTableSkeleton />;\n}\n\nif (isError) return <ErrorBanner error={error} />;\n\n// Refetch \u2014 data exists \u2192 subtle opacity + indicator, table stays interactive\nreturn (\n <div>\n {fetching && <div className=\"text-xs text-muted-foreground\">Updating\u2026</div>}\n <div className={fetching ? \"opacity-70\" : \"\"}>\n <OrderTable orders={data.orders} />\n </div>\n </div>\n);\n```\n\n```tsx\n// \u274C WRONG \u2014 pointer-events-none disables the table, making it feel broken\nreturn (\n <div className={fetching ? \"pointer-events-none\" : \"\"}>\n <OrderTable orders={data.orders} />\n </div>\n);\n\n// \u274C WRONG \u2014 no loading feedback when filters change\nif (loading) return <Skeleton />;\nreturn <OrderTable orders={data.orders} />;\n```\n\n#### Table Loading Rules\n\n- **Do not** use `pointer-events-none` on a table during loading \u2014 this disables interaction and makes the UI feel broken. During refetch, apply a subtle opacity (e.g. `opacity-70`) plus a non-blocking indicator (e.g., an \"Updating\u2026\" label or a thin progress bar). The table must remain clickable, sortable, and scrollable.\n- **Do not** replace a populated table with a full skeleton on refetch \u2014 this causes disorienting content flashes.\n- **Skeleton tables are only for initial load** (`loading` is true) when there is no data to display yet. Build them to match the real table's column structure (header + a few placeholder rows).\n\n### Efficient Loading Patterns\n\n1. **Always show loading indicators on refetch**: When inputs change (e.g. filters, search), show a non-blocking visual indicator while new data loads. Use `fetching` from `useApiData`.\n2. **Loading State Hierarchy**:\n - No data yet (`loading`) \u2192 Full skeleton placeholder\n - Has data, refetching (`fetching` && !`loading`) \u2192 Keep showing current data with a subtle visual indicator: light opacity (e.g. `opacity-70`) plus a non-blocking label (e.g. \"Updating\u2026\" text, thin progress bar, inline spinner). Do not use `pointer-events-none` \u2014 the content must remain fully interactive.\n - Error state (`isError`) \u2192 Show error with retry option, optionally keep stale data visible\n3. **Debounce Rapid Requests**: Prevent multiple API calls in short succession\n4. **Use useApiData for automatic refetching**: `useApiData` auto-refetches when inputs change and supports `staleTime`, `refetchOnWindowFocus`, `refetchOnReconnect`, `refetchInterval`, and `retry` options.\n\n## Performance Rules\n\n### 1. ALWAYS Paginate Tables and Lists\n\n**NEVER render more than 50 rows without pagination.** Always add client-side pagination:\n\n```tsx\nfunction PaginatedTable({ data }: { data: any[] }) {\n const PAGE_SIZE = 20;\n const [page, setPage] = useState(0);\n const totalPages = Math.ceil(data.length / PAGE_SIZE);\n const pageData = useMemo(\n () => data.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE),\n [data, page],\n );\n\n useEffect(() => {\n setPage(0);\n }, [data.length]);\n\n return (\n <>\n <Table>{/* render pageData rows */}</Table>\n <span>\n Page {page + 1} of {totalPages} ({data.length} total rows)\n </span>\n <Button\n onClick={() => setPage((p) => Math.max(0, p - 1))}\n disabled={page === 0}\n >\n Previous\n </Button>\n <Button\n onClick={() => setPage((p) => Math.min(totalPages - 1, p + 1))}\n disabled={page >= totalPages - 1}\n >\n Next\n </Button>\n </>\n );\n}\n```\n\nFor 200+ rows, prefer **server-side pagination**; use cursor/keyset pagination for high offsets instead of `LIMIT`/`OFFSET`.\n\nFor 500+ item lists where pagination doesn't fit the UX (chat messages, infinite scroll feeds, large dropdowns), use **virtualization** (`react-virtuoso` or `@tanstack/react-virtual`) to render only the items visible in the viewport.\n\n### 2. ALWAYS Debounce Input-Driven API Calls\n\n**NEVER call an API directly from onChange.** Debounce search/filter inputs with a 300ms timer:\n\n```tsx\nfunction DebouncedSearch({ onSearch }: { onSearch: (query: string) => void }) {\n const [localValue, setLocalValue] = useState(\"\");\n const timerRef = useRef<ReturnType<typeof setTimeout>>();\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n setLocalValue(e.target.value);\n clearTimeout(timerRef.current);\n timerRef.current = setTimeout(() => onSearch(e.target.value), 300);\n },\n [onSearch],\n );\n useEffect(() => () => clearTimeout(timerRef.current), []);\n\n return (\n <Input value={localValue} onChange={handleChange} placeholder=\"Search...\" />\n );\n}\n```\n\n### 3. Memoize Expensive Renders\n\nUse memoization (`memo()`, `useMemo`, `useCallback`) when it prevents measurable re-renders or expensive recomputation:\n\n```tsx\nconst OrderRow = memo(function OrderRow({\n order,\n onSelect,\n}: {\n order: Order;\n onSelect: (id: string) => void;\n}) {\n return (\n <TableRow onClick={() => onSelect(order.id)}>\n <TableCell>{order.id}</TableCell>...\n </TableRow>\n );\n});\n\nconst handleSelect = useCallback((id: string) => {\n setSelectedId(id);\n}, []);\n\nconst filtered = useMemo(\n () =>\n orders.filter((o) => o.status === status).sort((a, b) => b.total - a.total),\n [orders, status],\n);\n```\n\n### 4. ALWAYS Clean Up Side Effects\n\nClean up timers, event listeners, and subscriptions in `useEffect` return functions:\n\n```tsx\nuseEffect(() => {\n const handler = (e: KeyboardEvent) => {\n /* ... */\n };\n window.addEventListener(\"keydown\", handler);\n return () => window.removeEventListener(\"keydown\", handler);\n}, []);\n```\n\n### 5. Cancel In-Flight API Requests on Unmount\n\nFor search/filter patterns, prefer `useApiData` \u2014 it handles cancellation and cleanup automatically:\n\n```tsx\n// useApiData: auto-fetches and cancels on unmount/input change\nconst { data, fetching } = useApiData(\"SearchProducts\", { query });\n```\n\nFor imperative usage with manual cleanup, use the `cancel()` function:\n\n```tsx\nconst { run, cancel } = useApi(\"SearchProducts\");\n\nuseEffect(() => {\n if (!query) return;\n run({ query }).catch(console.error);\n return () => {\n cancel().catch(() => {});\n };\n}, [query, run, cancel]);\n```\n";
2
2
  //# sourceMappingURL=skill.generated.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"skill.generated.d.ts","sourceRoot":"","sources":["../../../../../src/ai-service/skills/system/superblocks-frontend/skill.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,802BA2uBnB,CAAC"}
1
+ {"version":3,"file":"skill.generated.d.ts","sourceRoot":"","sources":["../../../../../src/ai-service/skills/system/superblocks-frontend/skill.generated.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,u25BA0wBnB,CAAC"}