@nextsparkjs/ai-workflow 0.1.0-beta.100

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 (272) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +115 -0
  3. package/claude/_docs/workflows-optimizations.md +359 -0
  4. package/claude/agents/api-tester.md +634 -0
  5. package/claude/agents/architecture-supervisor.md +1351 -0
  6. package/claude/agents/backend-developer.md +997 -0
  7. package/claude/agents/backend-validator.md +417 -0
  8. package/claude/agents/bdd-docs-writer.md +737 -0
  9. package/claude/agents/block-developer.md +677 -0
  10. package/claude/agents/code-reviewer.md +1432 -0
  11. package/claude/agents/db-developer.md +721 -0
  12. package/claude/agents/db-validator.md +407 -0
  13. package/claude/agents/demo-video-generator.md +493 -0
  14. package/claude/agents/documentation-writer.md +1268 -0
  15. package/claude/agents/frontend-developer.md +1234 -0
  16. package/claude/agents/frontend-validator.md +777 -0
  17. package/claude/agents/functional-validator.md +630 -0
  18. package/claude/agents/mock-analyst.md +387 -0
  19. package/claude/agents/product-manager.md +963 -0
  20. package/claude/agents/qa-automation.md +1762 -0
  21. package/claude/agents/release-manager.md +634 -0
  22. package/claude/agents/selectors-translator.md +262 -0
  23. package/claude/agents/unit-test-writer.md +785 -0
  24. package/claude/agents/visual-comparator.md +329 -0
  25. package/claude/agents/workflow-maintainer.md +352 -0
  26. package/claude/commands/do/README.md +88 -0
  27. package/claude/commands/do/create-api.md +64 -0
  28. package/claude/commands/do/create-entity.md +66 -0
  29. package/claude/commands/do/create-migration.md +64 -0
  30. package/claude/commands/do/create-plugin.md +56 -0
  31. package/claude/commands/do/create-theme.md +70 -0
  32. package/claude/commands/do/mock-data.md +67 -0
  33. package/claude/commands/do/reset-db.md +71 -0
  34. package/claude/commands/do/setup-scheduled-action.md +75 -0
  35. package/claude/commands/do/sync-code-review.md +117 -0
  36. package/claude/commands/do/update-selectors.md +112 -0
  37. package/claude/commands/do/use-skills.md +90 -0
  38. package/claude/commands/do/validate-blocks.md +69 -0
  39. package/claude/commands/how-to/README.md +261 -0
  40. package/claude/commands/how-to/add-metadata.md +692 -0
  41. package/claude/commands/how-to/add-taxonomies.md +806 -0
  42. package/claude/commands/how-to/add-translations.md +571 -0
  43. package/claude/commands/how-to/create-api.md +577 -0
  44. package/claude/commands/how-to/create-block.md +575 -0
  45. package/claude/commands/how-to/create-child-entities.md +771 -0
  46. package/claude/commands/how-to/create-entity.md +597 -0
  47. package/claude/commands/how-to/create-migrations.md +605 -0
  48. package/claude/commands/how-to/create-plugin.md +654 -0
  49. package/claude/commands/how-to/customize-app.md +481 -0
  50. package/claude/commands/how-to/customize-dashboard.md +553 -0
  51. package/claude/commands/how-to/customize-theme.md +438 -0
  52. package/claude/commands/how-to/define-features-flows.md +632 -0
  53. package/claude/commands/how-to/deploy.md +507 -0
  54. package/claude/commands/how-to/handle-file-uploads.md +746 -0
  55. package/claude/commands/how-to/implement-search.md +1001 -0
  56. package/claude/commands/how-to/install-plugins.md +352 -0
  57. package/claude/commands/how-to/manage-test-coverage.md +984 -0
  58. package/claude/commands/how-to/run-tests.md +400 -0
  59. package/claude/commands/how-to/set-app-languages.md +601 -0
  60. package/claude/commands/how-to/set-plans-and-permissions.md +575 -0
  61. package/claude/commands/how-to/set-scheduled-actions.md +527 -0
  62. package/claude/commands/how-to/set-user-roles-and-permissions.md +550 -0
  63. package/claude/commands/how-to/setup-authentication.md +388 -0
  64. package/claude/commands/how-to/setup-claude-code.md +440 -0
  65. package/claude/commands/how-to/setup-database.md +274 -0
  66. package/claude/commands/how-to/setup-email-providers.md +598 -0
  67. package/claude/commands/how-to/setup-mobile-dev.md +627 -0
  68. package/claude/commands/how-to/start.md +500 -0
  69. package/claude/commands/how-to/use-devtools.md +639 -0
  70. package/claude/commands/how-to/use-superadmin.md +622 -0
  71. package/claude/commands/session/README.md +193 -0
  72. package/claude/commands/session/block-create.md +190 -0
  73. package/claude/commands/session/block-list.md +203 -0
  74. package/claude/commands/session/block-update.md +192 -0
  75. package/claude/commands/session/block-validate.md +218 -0
  76. package/claude/commands/session/changelog.md +115 -0
  77. package/claude/commands/session/close.md +225 -0
  78. package/claude/commands/session/commit.md +174 -0
  79. package/claude/commands/session/db-entity.md +206 -0
  80. package/claude/commands/session/db-fix.md +212 -0
  81. package/claude/commands/session/db-sample.md +206 -0
  82. package/claude/commands/session/demo.md +178 -0
  83. package/claude/commands/session/doc-bdd.md +207 -0
  84. package/claude/commands/session/doc-feature.md +218 -0
  85. package/claude/commands/session/doc-read.md +225 -0
  86. package/claude/commands/session/execute.md +204 -0
  87. package/claude/commands/session/explain.md +202 -0
  88. package/claude/commands/session/fix-bug.md +210 -0
  89. package/claude/commands/session/fix-build.md +182 -0
  90. package/claude/commands/session/fix-test.md +189 -0
  91. package/claude/commands/session/pending.md +232 -0
  92. package/claude/commands/session/refine.md +188 -0
  93. package/claude/commands/session/resume.md +192 -0
  94. package/claude/commands/session/review.md +192 -0
  95. package/claude/commands/session/scope-change.md +181 -0
  96. package/claude/commands/session/start-blocks.md +347 -0
  97. package/claude/commands/session/start.md +604 -0
  98. package/claude/commands/session/status.md +169 -0
  99. package/claude/commands/session/test-fix.md +221 -0
  100. package/claude/commands/session/test-run.md +203 -0
  101. package/claude/commands/session/test-write.md +242 -0
  102. package/claude/commands/session/validate.md +162 -0
  103. package/claude/config/context.json +40 -0
  104. package/claude/config/github.json +69 -0
  105. package/claude/config/github.schema.json +106 -0
  106. package/claude/config/team.json +46 -0
  107. package/claude/config/team.schema.json +106 -0
  108. package/claude/config/workspace.json +43 -0
  109. package/claude/config/workspace.schema.json +75 -0
  110. package/claude/skills/README.md +228 -0
  111. package/claude/skills/accessibility/SKILL.md +573 -0
  112. package/claude/skills/api-bypass-layers/SKILL.md +550 -0
  113. package/claude/skills/asana-integration/SKILL.md +499 -0
  114. package/claude/skills/better-auth/SKILL.md +666 -0
  115. package/claude/skills/billing-subscriptions/SKILL.md +660 -0
  116. package/claude/skills/block-decision-matrix/SKILL.md +359 -0
  117. package/claude/skills/clickup-integration/SKILL.md +434 -0
  118. package/claude/skills/core-theme-responsibilities/SKILL.md +485 -0
  119. package/claude/skills/create-plugin/SKILL.md +425 -0
  120. package/claude/skills/create-theme/SKILL.md +331 -0
  121. package/claude/skills/cypress-api/SKILL.md +511 -0
  122. package/claude/skills/cypress-api/scripts/generate-api-controller.py +329 -0
  123. package/claude/skills/cypress-api/scripts/generate-api-test.py +930 -0
  124. package/claude/skills/cypress-e2e/SKILL.md +526 -0
  125. package/claude/skills/cypress-e2e/scripts/extract-selectors.py +383 -0
  126. package/claude/skills/cypress-e2e/scripts/generate-uat-test.py +788 -0
  127. package/claude/skills/cypress-selectors/SKILL.md +309 -0
  128. package/claude/skills/cypress-selectors/scripts/extract-missing.py +243 -0
  129. package/claude/skills/cypress-selectors/scripts/generate-block-selectors.py +283 -0
  130. package/claude/skills/cypress-selectors/scripts/validate-selectors.py +145 -0
  131. package/claude/skills/database-migrations/SKILL.md +335 -0
  132. package/claude/skills/database-migrations/scripts/generate-sample-data.py +284 -0
  133. package/claude/skills/database-migrations/scripts/validate-migration.py +323 -0
  134. package/claude/skills/design-system/SKILL.md +682 -0
  135. package/claude/skills/documentation/SKILL.md +540 -0
  136. package/claude/skills/entity-api/SKILL.md +482 -0
  137. package/claude/skills/entity-system/SKILL.md +635 -0
  138. package/claude/skills/entity-system/scripts/generate-child-migration.py +298 -0
  139. package/claude/skills/entity-system/scripts/generate-metas-migration.py +233 -0
  140. package/claude/skills/entity-system/scripts/generate-migration.py +382 -0
  141. package/claude/skills/entity-system/scripts/generate-sample-data.py +418 -0
  142. package/claude/skills/entity-system/scripts/scaffold-entity.py +661 -0
  143. package/claude/skills/github/SKILL.md +467 -0
  144. package/claude/skills/i18n-nextintl/SKILL.md +302 -0
  145. package/claude/skills/i18n-nextintl/scripts/add-translation.py +243 -0
  146. package/claude/skills/i18n-nextintl/scripts/extract-hardcoded.py +246 -0
  147. package/claude/skills/i18n-nextintl/scripts/validate-translations.py +260 -0
  148. package/claude/skills/impact-analysis/SKILL.md +203 -0
  149. package/claude/skills/jest-unit/SKILL.md +306 -0
  150. package/claude/skills/jest-unit/references/component-testing.md +371 -0
  151. package/claude/skills/jest-unit/references/mocking-patterns.md +380 -0
  152. package/claude/skills/jest-unit/references/service-hook-testing.md +454 -0
  153. package/claude/skills/jira-integration/SKILL.md +539 -0
  154. package/claude/skills/media-library/SKILL.md +743 -0
  155. package/claude/skills/mock-analysis/SKILL.md +276 -0
  156. package/claude/skills/monorepo-architecture/SKILL.md +162 -0
  157. package/claude/skills/nextjs-api-development/SKILL.md +364 -0
  158. package/claude/skills/nextjs-api-development/scripts/generate-crud-tests.py +456 -0
  159. package/claude/skills/nextjs-api-development/scripts/scaffold-endpoint.py +481 -0
  160. package/claude/skills/nextjs-api-development/scripts/validate-api.py +283 -0
  161. package/claude/skills/notion-integration/SKILL.md +641 -0
  162. package/claude/skills/npm-development-workflow/SKILL.md +480 -0
  163. package/claude/skills/page-builder-blocks/SKILL.md +530 -0
  164. package/claude/skills/page-builder-blocks/scripts/scaffold-block.py +444 -0
  165. package/claude/skills/permissions-system/SKILL.md +619 -0
  166. package/claude/skills/plugins/SKILL.md +340 -0
  167. package/claude/skills/plugins/references/plugin-templates.md +414 -0
  168. package/claude/skills/plugins/references/plugin-testing.md +353 -0
  169. package/claude/skills/plugins/references/plugin-types.md +198 -0
  170. package/claude/skills/plugins/scripts/scaffold-plugin.py +443 -0
  171. package/claude/skills/pom-patterns/SKILL.md +452 -0
  172. package/claude/skills/pom-patterns/scripts/generate-pom.py +392 -0
  173. package/claude/skills/rate-limiting/SKILL.md +342 -0
  174. package/claude/skills/react-best-practices/AGENTS.md +2410 -0
  175. package/claude/skills/react-best-practices/README.md +123 -0
  176. package/claude/skills/react-best-practices/SKILL.md +125 -0
  177. package/claude/skills/react-best-practices/metadata.json +15 -0
  178. package/claude/skills/react-best-practices/rules/_sections.md +46 -0
  179. package/claude/skills/react-best-practices/rules/_template.md +28 -0
  180. package/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  181. package/claude/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
  182. package/claude/skills/react-best-practices/rules/async-api-routes.md +38 -0
  183. package/claude/skills/react-best-practices/rules/async-defer-await.md +80 -0
  184. package/claude/skills/react-best-practices/rules/async-dependencies.md +36 -0
  185. package/claude/skills/react-best-practices/rules/async-parallel.md +28 -0
  186. package/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
  187. package/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
  188. package/claude/skills/react-best-practices/rules/bundle-conditional.md +31 -0
  189. package/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
  190. package/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  191. package/claude/skills/react-best-practices/rules/bundle-preload.md +50 -0
  192. package/claude/skills/react-best-practices/rules/client-event-listeners.md +74 -0
  193. package/claude/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
  194. package/claude/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  195. package/claude/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
  196. package/claude/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
  197. package/claude/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
  198. package/claude/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  199. package/claude/skills/react-best-practices/rules/js-cache-storage.md +70 -0
  200. package/claude/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  201. package/claude/skills/react-best-practices/rules/js-early-exit.md +50 -0
  202. package/claude/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  203. package/claude/skills/react-best-practices/rules/js-index-maps.md +37 -0
  204. package/claude/skills/react-best-practices/rules/js-length-check-first.md +49 -0
  205. package/claude/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  206. package/claude/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  207. package/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  208. package/claude/skills/react-best-practices/rules/rendering-activity.md +26 -0
  209. package/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  210. package/claude/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
  211. package/claude/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  212. package/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  213. package/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  214. package/claude/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  215. package/claude/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  216. package/claude/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  217. package/claude/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  218. package/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  219. package/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  220. package/claude/skills/react-best-practices/rules/rerender-memo.md +44 -0
  221. package/claude/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  222. package/claude/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
  223. package/claude/skills/react-best-practices/rules/server-cache-lru.md +41 -0
  224. package/claude/skills/react-best-practices/rules/server-cache-react.md +76 -0
  225. package/claude/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
  226. package/claude/skills/react-best-practices/rules/server-serialization.md +38 -0
  227. package/claude/skills/react-patterns/SKILL.md +688 -0
  228. package/claude/skills/registry-system/SKILL.md +331 -0
  229. package/claude/skills/scheduled-actions/SKILL.md +671 -0
  230. package/claude/skills/scope-enforcement/SKILL.md +542 -0
  231. package/claude/skills/scope-enforcement/scripts/validate-scope.py +357 -0
  232. package/claude/skills/server-actions/SKILL.md +493 -0
  233. package/claude/skills/service-layer/SKILL.md +587 -0
  234. package/claude/skills/session-management/SKILL.md +266 -0
  235. package/claude/skills/session-management/scripts/create-session.py +166 -0
  236. package/claude/skills/session-management/scripts/iteration-close.sh +105 -0
  237. package/claude/skills/session-management/scripts/iteration-init.sh +180 -0
  238. package/claude/skills/session-management/scripts/session-archive.sh +87 -0
  239. package/claude/skills/session-management/scripts/session-close.sh +133 -0
  240. package/claude/skills/session-management/scripts/session-init.sh +225 -0
  241. package/claude/skills/session-management/scripts/session-list.sh +163 -0
  242. package/claude/skills/session-management/scripts/split-plan.sh +116 -0
  243. package/claude/skills/shadcn-components/SKILL.md +586 -0
  244. package/claude/skills/shadcn-theming/SKILL.md +446 -0
  245. package/claude/skills/suspense-loading/SKILL.md +280 -0
  246. package/claude/skills/tailwind-theming/SKILL.md +507 -0
  247. package/claude/skills/tanstack-query/SKILL.md +608 -0
  248. package/claude/skills/test-coverage/SKILL.md +239 -0
  249. package/claude/skills/web-design-guidelines/SKILL.md +39 -0
  250. package/claude/skills/zod-validation/SKILL.md +537 -0
  251. package/claude/templates/blocks/progress.md +86 -0
  252. package/claude/templates/iteration/changes.md +61 -0
  253. package/claude/templates/iteration/progress.md +55 -0
  254. package/claude/templates/log.md +31 -0
  255. package/claude/templates/story/context.md +77 -0
  256. package/claude/templates/story/pendings.md +37 -0
  257. package/claude/templates/story/plan.md +299 -0
  258. package/claude/templates/story/requirements.md +109 -0
  259. package/claude/templates/story/scope.json +10 -0
  260. package/claude/templates/story/tests.md +91 -0
  261. package/claude/templates/task/progress.md +58 -0
  262. package/claude/templates/task/requirements.md +54 -0
  263. package/claude/workflows/README.md +154 -0
  264. package/claude/workflows/blocks.md +614 -0
  265. package/claude/workflows/story.md +1207 -0
  266. package/claude/workflows/task.md +927 -0
  267. package/claude/workflows/tweak.md +527 -0
  268. package/cursor/.gitkeep +0 -0
  269. package/package.json +35 -0
  270. package/scripts/postinstall.mjs +198 -0
  271. package/scripts/setup.mjs +282 -0
  272. package/scripts/sync.mjs +209 -0
@@ -0,0 +1,382 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Generate Migration Script
4
+
5
+ Generates SQL migration from entity configuration.
6
+
7
+ Usage:
8
+ python generate-migration.py --entity ENTITY_NAME [--theme THEME] [--type TYPE]
9
+
10
+ Options:
11
+ --entity ENTITY_NAME Name of the entity (kebab-case)
12
+ --theme THEME Theme name (default: from NEXT_PUBLIC_ACTIVE_THEME or 'default')
13
+ --type TYPE Migration type: table, metas, index, rls (default: table)
14
+ --output OUTPUT Output file (default: stdout)
15
+ --with-rls Include RLS policies
16
+ --with-indexes Include optimized indexes
17
+ --soft-delete Add soft delete columns
18
+ """
19
+
20
+ import os
21
+ import sys
22
+ import argparse
23
+ import json
24
+ from pathlib import Path
25
+ from datetime import datetime
26
+ from typing import Dict, List, Optional
27
+
28
+
29
+ def get_active_theme() -> str:
30
+ """Get active theme from environment or default."""
31
+ return os.environ.get('NEXT_PUBLIC_ACTIVE_THEME', 'default')
32
+
33
+
34
+ def to_snake_case(name: str) -> str:
35
+ """Convert kebab-case to snake_case."""
36
+ return name.replace('-', '_')
37
+
38
+
39
+ def to_camel_case(name: str) -> str:
40
+ """Convert kebab-case to camelCase."""
41
+ components = name.split('-')
42
+ return components[0] + ''.join(x.title() for x in components[1:])
43
+
44
+
45
+ # Field type to SQL type mapping
46
+ FIELD_TYPE_MAP = {
47
+ 'text': 'VARCHAR(255)',
48
+ 'textarea': 'TEXT',
49
+ 'number': 'DECIMAL',
50
+ 'boolean': 'BOOLEAN',
51
+ 'date': 'DATE',
52
+ 'datetime': 'TIMESTAMPTZ',
53
+ 'email': 'VARCHAR(255)',
54
+ 'url': 'TEXT',
55
+ 'json': 'JSONB',
56
+ 'select': 'VARCHAR(100)',
57
+ 'multiselect': 'TEXT[]',
58
+ 'tags': 'TEXT[]',
59
+ 'relation': 'UUID',
60
+ 'relation-multi': 'UUID[]',
61
+ 'reference': 'UUID',
62
+ 'user': 'UUID',
63
+ 'phone': 'VARCHAR(50)',
64
+ 'rating': 'INTEGER',
65
+ 'range': 'DECIMAL',
66
+ 'doublerange': 'DECIMAL[]',
67
+ 'markdown': 'TEXT',
68
+ 'richtext': 'TEXT',
69
+ 'code': 'TEXT',
70
+ 'timezone': 'VARCHAR(100)',
71
+ 'currency': 'VARCHAR(10)',
72
+ 'country': 'VARCHAR(10)',
73
+ 'address': 'JSONB',
74
+ 'file': 'JSONB',
75
+ 'image': 'JSONB',
76
+ 'video': 'JSONB',
77
+ 'audio': 'JSONB',
78
+ }
79
+
80
+
81
+ def parse_fields_file(fields_path: Path) -> List[Dict]:
82
+ """Parse TypeScript fields file to extract field definitions."""
83
+ fields = []
84
+
85
+ if not fields_path.exists():
86
+ print(f"Warning: Fields file not found: {fields_path}")
87
+ return fields
88
+
89
+ with open(fields_path, 'r', encoding='utf-8') as f:
90
+ content = f.read()
91
+
92
+ # Simple regex-based parser for field objects
93
+ import re
94
+
95
+ # Find field blocks
96
+ field_pattern = re.compile(r'\{[^}]*name:\s*[\'"](\w+)[\'"][^}]*type:\s*[\'"](\w+(?:-\w+)?)[\'"][^}]*required:\s*(true|false)[^}]*\}', re.DOTALL)
97
+
98
+ for match in field_pattern.finditer(content):
99
+ name = match.group(1)
100
+ field_type = match.group(2)
101
+ required = match.group(3) == 'true'
102
+
103
+ # Skip system fields
104
+ if name in ['id', 'createdAt', 'updatedAt', 'created_at', 'updated_at', 'userId', 'user_id']:
105
+ continue
106
+
107
+ fields.append({
108
+ 'name': name,
109
+ 'type': field_type,
110
+ 'required': required
111
+ })
112
+
113
+ # Try to extract options for select fields
114
+ if field_type == 'select':
115
+ options_pattern = re.compile(rf'name:\s*[\'"]{name}[\'"][^}}]*options:\s*\[(.*?)\]', re.DOTALL)
116
+ options_match = options_pattern.search(content)
117
+ if options_match:
118
+ options_str = options_match.group(1)
119
+ values = re.findall(r'value:\s*[\'"](\w+)[\'"]', options_str)
120
+ fields[-1]['options'] = values
121
+
122
+ return fields
123
+
124
+
125
+ def generate_column_sql(field: Dict) -> str:
126
+ """Generate SQL column definition from field."""
127
+ name = to_snake_case(field['name'])
128
+ field_type = field['type']
129
+ required = field['required']
130
+
131
+ sql_type = FIELD_TYPE_MAP.get(field_type, 'TEXT')
132
+
133
+ # Handle select fields with CHECK constraint
134
+ if field_type == 'select' and 'options' in field:
135
+ options = ', '.join(f"'{v}'" for v in field['options'])
136
+ sql_type = f"VARCHAR(100) CHECK ({name} IN ({options}))"
137
+
138
+ nullable = '' if required else ''
139
+ not_null = ' NOT NULL' if required else ''
140
+
141
+ return f' "{name}" {sql_type}{not_null}'
142
+
143
+
144
+ def generate_table_migration(entity_slug: str, fields: List[Dict], options: Dict) -> str:
145
+ """Generate CREATE TABLE migration."""
146
+ table_name = to_snake_case(entity_slug)
147
+ timestamp = datetime.now().isoformat()
148
+
149
+ # Build column definitions
150
+ columns = [
151
+ ' "id" UUID PRIMARY KEY DEFAULT gen_random_uuid()',
152
+ ' "user_id" UUID NOT NULL REFERENCES "user"(id) ON DELETE CASCADE',
153
+ ]
154
+
155
+ for field in fields:
156
+ columns.append(generate_column_sql(field))
157
+
158
+ # Timestamps
159
+ columns.extend([
160
+ ' "created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP',
161
+ ' "updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP',
162
+ ])
163
+
164
+ # Soft delete
165
+ if options.get('soft_delete'):
166
+ columns.extend([
167
+ ' "deleted_at" TIMESTAMPTZ',
168
+ ' "deleted_by" UUID REFERENCES "user"(id)',
169
+ ])
170
+
171
+ # Build SQL
172
+ sql = f'''-- Migration: Create {entity_slug} table
173
+ -- Description: Create {entity_slug} entity with fields from config
174
+ -- Generated: {timestamp}
175
+
176
+ -- Up Migration
177
+ BEGIN;
178
+
179
+ -- Create {table_name} table
180
+ CREATE TABLE "{table_name}" (
181
+ {','.join(columns)}
182
+ );
183
+ '''
184
+
185
+ # Indexes
186
+ if options.get('with_indexes', True):
187
+ sql += f'''
188
+ -- Indexes
189
+ CREATE INDEX "idx_{table_name}_user_id" ON "{table_name}" ("user_id");
190
+ CREATE INDEX "idx_{table_name}_created_at" ON "{table_name}" ("created_at" DESC);
191
+ '''
192
+ # Add indexes for searchable/sortable fields
193
+ for field in fields:
194
+ name = to_snake_case(field['name'])
195
+ if field['type'] in ['text', 'textarea']:
196
+ sql += f'CREATE INDEX "idx_{table_name}_{name}_search" ON "{table_name}" USING gin(to_tsvector(\'english\', {name}));\n'
197
+ elif field['type'] == 'select':
198
+ sql += f'CREATE INDEX "idx_{table_name}_{name}" ON "{table_name}" ("{name}");\n'
199
+
200
+ if options.get('soft_delete'):
201
+ sql += f'CREATE INDEX "idx_{table_name}_deleted_at" ON "{table_name}" ("deleted_at");\n'
202
+
203
+ # RLS
204
+ if options.get('with_rls', True):
205
+ sql += f'''
206
+ -- Enable Row Level Security
207
+ ALTER TABLE "{table_name}" ENABLE ROW LEVEL SECURITY;
208
+
209
+ -- RLS Policies
210
+ CREATE POLICY "{table_name}_owner_access" ON "{table_name}"
211
+ FOR ALL
212
+ USING (auth.uid() = user_id);
213
+
214
+ CREATE POLICY "{table_name}_service_role_access" ON "{table_name}"
215
+ FOR ALL TO service_role
216
+ USING (TRUE);
217
+ '''
218
+
219
+ sql += f'''
220
+ -- Trigger for updated_at
221
+ CREATE TRIGGER update_{table_name}_updated_at
222
+ BEFORE UPDATE ON "{table_name}"
223
+ FOR EACH ROW
224
+ EXECUTE FUNCTION update_updated_at_column();
225
+
226
+ COMMIT;
227
+
228
+ -- Down Migration (commented for safety)
229
+ -- BEGIN;
230
+ -- DROP TABLE IF EXISTS "{table_name}" CASCADE;
231
+ -- COMMIT;
232
+ '''
233
+
234
+ return sql
235
+
236
+
237
+ def generate_metas_migration(entity_slug: str) -> str:
238
+ """Generate metadata table migration."""
239
+ table_name = to_snake_case(entity_slug)
240
+ timestamp = datetime.now().isoformat()
241
+
242
+ return f'''-- Migration: Create {entity_slug}_metas table
243
+ -- Description: Create metadata table for {entity_slug} entity
244
+ -- Generated: {timestamp}
245
+
246
+ -- Up Migration
247
+ BEGIN;
248
+
249
+ -- Create {table_name}_metas table
250
+ CREATE TABLE "{table_name}_metas" (
251
+ "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
252
+ "entity_id" UUID NOT NULL REFERENCES "{table_name}"(id) ON DELETE CASCADE,
253
+ "key" VARCHAR(255) NOT NULL,
254
+ "value" JSONB,
255
+ "created_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
256
+ "updated_at" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
257
+ UNIQUE("entity_id", "key")
258
+ );
259
+
260
+ -- Indexes
261
+ CREATE INDEX "idx_{table_name}_metas_entity_id" ON "{table_name}_metas" ("entity_id");
262
+ CREATE INDEX "idx_{table_name}_metas_key" ON "{table_name}_metas" ("key");
263
+ CREATE INDEX "idx_{table_name}_metas_value" ON "{table_name}_metas" USING gin("value");
264
+
265
+ -- Enable Row Level Security
266
+ ALTER TABLE "{table_name}_metas" ENABLE ROW LEVEL SECURITY;
267
+
268
+ -- RLS Policies
269
+ CREATE POLICY "{table_name}_metas_owner_access" ON "{table_name}_metas"
270
+ FOR ALL
271
+ USING (
272
+ EXISTS (
273
+ SELECT 1 FROM "{table_name}"
274
+ WHERE "{table_name}".id = entity_id
275
+ AND "{table_name}".user_id = auth.uid()
276
+ )
277
+ );
278
+
279
+ CREATE POLICY "{table_name}_metas_service_role_access" ON "{table_name}_metas"
280
+ FOR ALL TO service_role
281
+ USING (TRUE);
282
+
283
+ -- Trigger for updated_at
284
+ CREATE TRIGGER update_{table_name}_metas_updated_at
285
+ BEFORE UPDATE ON "{table_name}_metas"
286
+ FOR EACH ROW
287
+ EXECUTE FUNCTION update_updated_at_column();
288
+
289
+ COMMIT;
290
+
291
+ -- Down Migration
292
+ -- DROP TABLE IF EXISTS "{table_name}_metas" CASCADE;
293
+ '''
294
+
295
+
296
+ def generate_index_migration(entity_slug: str, fields: List[Dict]) -> str:
297
+ """Generate index-only migration."""
298
+ table_name = to_snake_case(entity_slug)
299
+ timestamp = datetime.now().isoformat()
300
+
301
+ sql = f'''-- Migration: Add indexes to {entity_slug}
302
+ -- Generated: {timestamp}
303
+
304
+ BEGIN;
305
+
306
+ '''
307
+
308
+ for field in fields:
309
+ name = to_snake_case(field['name'])
310
+ if field['type'] in ['text', 'textarea']:
311
+ sql += f'CREATE INDEX IF NOT EXISTS "idx_{table_name}_{name}_search" ON "{table_name}" USING gin(to_tsvector(\'english\', {name}));\n'
312
+ elif field['type'] in ['select', 'boolean', 'date', 'datetime']:
313
+ sql += f'CREATE INDEX IF NOT EXISTS "idx_{table_name}_{name}" ON "{table_name}" ("{name}");\n'
314
+ elif field['type'] in ['relation', 'reference', 'user']:
315
+ sql += f'CREATE INDEX IF NOT EXISTS "idx_{table_name}_{name}" ON "{table_name}" ("{name}");\n'
316
+
317
+ sql += '\nCOMMIT;\n'
318
+
319
+ return sql
320
+
321
+
322
+ def main():
323
+ parser = argparse.ArgumentParser(description='Generate entity migration')
324
+ parser.add_argument('--entity', required=True, help='Entity name (kebab-case)')
325
+ parser.add_argument('--theme', default=None, help='Theme name')
326
+ parser.add_argument('--type', choices=['table', 'metas', 'index', 'rls'], default='table')
327
+ parser.add_argument('--output', help='Output file (default: stdout)')
328
+ parser.add_argument('--with-rls', action='store_true', default=True)
329
+ parser.add_argument('--with-indexes', action='store_true', default=True)
330
+ parser.add_argument('--soft-delete', action='store_true')
331
+
332
+ args = parser.parse_args()
333
+
334
+ theme = args.theme or get_active_theme()
335
+ entity_slug = args.entity.lower()
336
+
337
+ # Find fields file
338
+ fields_path = Path(f'contents/themes/{theme}/entities/{entity_slug}/{entity_slug}.fields.ts')
339
+
340
+ print(f"\nGenerating migration for: {entity_slug}")
341
+ print(f"Theme: {theme}")
342
+ print(f"Type: {args.type}")
343
+ print(f"Fields file: {fields_path}")
344
+
345
+ # Parse fields
346
+ fields = parse_fields_file(fields_path)
347
+ print(f"Found {len(fields)} fields")
348
+
349
+ # Generate migration
350
+ options = {
351
+ 'with_rls': args.with_rls,
352
+ 'with_indexes': args.with_indexes,
353
+ 'soft_delete': args.soft_delete,
354
+ }
355
+
356
+ if args.type == 'table':
357
+ sql = generate_table_migration(entity_slug, fields, options)
358
+ elif args.type == 'metas':
359
+ sql = generate_metas_migration(entity_slug)
360
+ elif args.type == 'index':
361
+ sql = generate_index_migration(entity_slug, fields)
362
+ else:
363
+ print(f"Unknown migration type: {args.type}")
364
+ return 1
365
+
366
+ # Output
367
+ if args.output:
368
+ with open(args.output, 'w', encoding='utf-8') as f:
369
+ f.write(sql)
370
+ print(f"\nMigration written to: {args.output}")
371
+ else:
372
+ print("\n" + "=" * 60)
373
+ print("GENERATED MIGRATION:")
374
+ print("=" * 60)
375
+ print(sql)
376
+ print("=" * 60)
377
+
378
+ return 0
379
+
380
+
381
+ if __name__ == '__main__':
382
+ sys.exit(main())