@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,661 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Scaffold Entity Script
4
+
5
+ Creates the complete file structure for a new entity.
6
+
7
+ Usage:
8
+ python scaffold-entity.py --entity ENTITY_NAME [--theme THEME]
9
+
10
+ Options:
11
+ --entity ENTITY_NAME Name of the entity in kebab-case (e.g., blog-posts)
12
+ --theme THEME Theme name (default: from NEXT_PUBLIC_ACTIVE_THEME or 'default')
13
+ --with-metas Include metadata table migration
14
+ --with-builder Enable page builder for this entity
15
+ """
16
+
17
+ import os
18
+ import re
19
+ import sys
20
+ import argparse
21
+ from pathlib import Path
22
+ from datetime import datetime
23
+
24
+
25
+ def to_camel_case(name: str) -> str:
26
+ """Convert kebab-case to camelCase."""
27
+ components = name.split('-')
28
+ return components[0] + ''.join(x.title() for x in components[1:])
29
+
30
+
31
+ def to_pascal_case(name: str) -> str:
32
+ """Convert kebab-case to PascalCase."""
33
+ return ''.join(x.title() for x in name.split('-'))
34
+
35
+
36
+ def to_snake_case(name: str) -> str:
37
+ """Convert kebab-case to snake_case."""
38
+ return name.replace('-', '_')
39
+
40
+
41
+ def to_singular(name: str) -> str:
42
+ """Convert plural to singular (simple heuristic)."""
43
+ if name.endswith('ies'):
44
+ return name[:-3] + 'y'
45
+ elif name.endswith('es') and name[-3] in 'sxz':
46
+ return name[:-2]
47
+ elif name.endswith('s') and not name.endswith('ss'):
48
+ return name[:-1]
49
+ return name
50
+
51
+
52
+ def get_active_theme() -> str:
53
+ """Get active theme from environment or default."""
54
+ return os.environ.get('NEXT_PUBLIC_ACTIVE_THEME', 'default')
55
+
56
+
57
+ def generate_config_file(entity_slug: str, singular: str, pascal: str) -> str:
58
+ """Generate entity config TypeScript file."""
59
+ return f'''/**
60
+ * {pascal} Entity Configuration
61
+ *
62
+ * Config-driven entity following the 5-section structure.
63
+ * All table names, API paths, and metadata are derived from slug.
64
+ */
65
+
66
+ import {{ FileText }} from 'lucide-react'
67
+ import type {{ EntityConfig }} from '@/core/lib/entities/types'
68
+ import {{ {to_camel_case(entity_slug)}Fields }} from './{entity_slug}.fields'
69
+
70
+ export const {to_camel_case(singular)}EntityConfig: EntityConfig = {{
71
+ // ==========================================
72
+ // 1. BASIC IDENTIFICATION
73
+ // ==========================================
74
+ slug: '{entity_slug}',
75
+ enabled: true,
76
+ names: {{
77
+ singular: '{singular}',
78
+ plural: '{pascal}'
79
+ }},
80
+ icon: FileText,
81
+
82
+ // ==========================================
83
+ // 2. ACCESS AND SCOPE CONFIGURATION
84
+ // ==========================================
85
+ access: {{
86
+ public: false,
87
+ api: true,
88
+ metadata: false,
89
+ shared: false
90
+ }},
91
+
92
+ // ==========================================
93
+ // 3. UI/UX FEATURES
94
+ // ==========================================
95
+ ui: {{
96
+ dashboard: {{
97
+ showInMenu: true,
98
+ showInTopbar: true,
99
+ filters: [
100
+ // {{ field: 'status', type: 'multiSelect' }},
101
+ ],
102
+ }},
103
+ public: {{
104
+ hasArchivePage: false,
105
+ hasSinglePage: false
106
+ }},
107
+ features: {{
108
+ searchable: true,
109
+ sortable: true,
110
+ filterable: true,
111
+ bulkOperations: true,
112
+ importExport: false
113
+ }}
114
+ }},
115
+
116
+ // ==========================================
117
+ // 4. PERMISSIONS SYSTEM
118
+ // ==========================================
119
+ // Permissions are centralized in permissions.config.ts
120
+ // See: contents/themes/{{theme}}/permissions.config.ts
121
+
122
+ // ==========================================
123
+ // 5. INTERNATIONALIZATION
124
+ // ==========================================
125
+ i18n: {{
126
+ fallbackLocale: 'en',
127
+ loaders: {{
128
+ es: () => import('./messages/es.json'),
129
+ en: () => import('./messages/en.json')
130
+ }}
131
+ }},
132
+
133
+ // ==========================================
134
+ // FIELDS (imported from separate file)
135
+ // ==========================================
136
+ fields: {to_camel_case(entity_slug)}Fields,
137
+ }}
138
+ '''
139
+
140
+
141
+ def generate_fields_file(entity_slug: str, singular: str, pascal: str) -> str:
142
+ """Generate entity fields TypeScript file."""
143
+ return f'''/**
144
+ * {pascal} Entity Fields Configuration
145
+ *
146
+ * Field definitions separated from main config.
147
+ * Contains all field definitions for the {entity_slug} entity.
148
+ */
149
+
150
+ import type {{ EntityField }} from '@/core/lib/entities/types'
151
+
152
+ export const {to_camel_case(entity_slug)}Fields: EntityField[] = [
153
+ {{
154
+ name: 'title',
155
+ type: 'text',
156
+ required: true,
157
+ display: {{
158
+ label: 'Title',
159
+ description: '{pascal} title',
160
+ placeholder: 'Enter title...',
161
+ showInList: true,
162
+ showInDetail: true,
163
+ showInForm: true,
164
+ order: 1,
165
+ columnWidth: 12,
166
+ }},
167
+ api: {{
168
+ readOnly: false,
169
+ searchable: true,
170
+ sortable: true,
171
+ }},
172
+ }},
173
+ {{
174
+ name: 'description',
175
+ type: 'textarea',
176
+ required: false,
177
+ display: {{
178
+ label: 'Description',
179
+ description: 'Detailed description',
180
+ placeholder: 'Enter description...',
181
+ showInList: false,
182
+ showInDetail: true,
183
+ showInForm: true,
184
+ order: 2,
185
+ columnWidth: 12,
186
+ }},
187
+ api: {{
188
+ readOnly: false,
189
+ searchable: true,
190
+ sortable: false,
191
+ }},
192
+ }},
193
+ {{
194
+ name: 'status',
195
+ type: 'select',
196
+ required: false,
197
+ defaultValue: 'draft',
198
+ options: [
199
+ {{ value: 'draft', label: 'Draft' }},
200
+ {{ value: 'active', label: 'Active' }},
201
+ {{ value: 'archived', label: 'Archived' }},
202
+ ],
203
+ display: {{
204
+ label: 'Status',
205
+ description: 'Current status',
206
+ placeholder: 'Select status...',
207
+ showInList: true,
208
+ showInDetail: true,
209
+ showInForm: true,
210
+ order: 3,
211
+ columnWidth: 6,
212
+ }},
213
+ api: {{
214
+ readOnly: false,
215
+ searchable: false,
216
+ sortable: true,
217
+ }},
218
+ }},
219
+ // NOTE: id, createdAt, updatedAt are implicit system fields
220
+ // They are automatically included by the API and frontend
221
+ // Do NOT declare them here - see core/lib/entities/system-fields.ts
222
+ ]
223
+ '''
224
+
225
+
226
+ def generate_types_file(entity_slug: str, singular: str, pascal: str) -> str:
227
+ """Generate entity types TypeScript file."""
228
+ return f'''/**
229
+ * {pascal} Types
230
+ *
231
+ * Type definitions for the {pascal} entity.
232
+ *
233
+ * @module {pascal}Types
234
+ */
235
+
236
+ /**
237
+ * {pascal} status values
238
+ */
239
+ export type {pascal}Status = 'draft' | 'active' | 'archived'
240
+
241
+ /**
242
+ * {pascal} entity
243
+ */
244
+ export interface {pascal} {{
245
+ id: string
246
+ userId: string
247
+ teamId: string
248
+ title: string
249
+ description?: string
250
+ status: {pascal}Status
251
+ createdAt: string
252
+ updatedAt: string
253
+ }}
254
+
255
+ /**
256
+ * Input for creating a {singular}
257
+ */
258
+ export interface Create{pascal}Input {{
259
+ title: string
260
+ description?: string
261
+ status?: {pascal}Status
262
+ }}
263
+
264
+ /**
265
+ * Input for updating a {singular}
266
+ */
267
+ export interface Update{pascal}Input {{
268
+ title?: string
269
+ description?: string
270
+ status?: {pascal}Status
271
+ }}
272
+ '''
273
+
274
+
275
+ def generate_service_file(entity_slug: str, singular: str, pascal: str) -> str:
276
+ """Generate entity service TypeScript file."""
277
+ table_name = entity_slug.replace('-', '_')
278
+ camel = to_camel_case(entity_slug)
279
+ return f'''/**
280
+ * {pascal} Service
281
+ *
282
+ * Provides data access methods for {entity_slug}.
283
+ * Extends BaseEntityService for standard CRUD operations.
284
+ *
285
+ * @module {pascal}Service
286
+ */
287
+
288
+ import {{ BaseEntityService }} from '@/core/lib/services/base-entity.service'
289
+ import type {{ {pascal}, Create{pascal}Input, Update{pascal}Input }} from './{entity_slug}.types'
290
+
291
+ class {pascal}ServiceClass extends BaseEntityService<{pascal}, Create{pascal}Input, Update{pascal}Input> {{
292
+ constructor() {{
293
+ super({{
294
+ tableName: '{table_name}',
295
+ fields: ['title', 'description', 'status'],
296
+ searchableFields: ['title'],
297
+ defaultOrderBy: 'createdAt',
298
+ defaultOrderDir: 'desc',
299
+ }})
300
+ }}
301
+
302
+ // ============================================
303
+ // CUSTOM METHODS
304
+ // ============================================
305
+ // Add entity-specific methods here. Examples:
306
+ //
307
+ // async getByStatus(userId: string, status: string): Promise<{pascal}[]> {{
308
+ // return this.query(userId, {{ where: {{ status }} }})
309
+ // }}
310
+ //
311
+ // async getRecent(userId: string, limit: number = 5): Promise<{pascal}[]> {{
312
+ // return this.query(userId, {{ limit, orderBy: 'createdAt', orderDir: 'desc' }})
313
+ // }}
314
+ }}
315
+
316
+ // Export singleton instance
317
+ export const {camel}Service = new {pascal}ServiceClass()
318
+
319
+ // Export class for testing
320
+ export {{ {pascal}ServiceClass }}
321
+ '''
322
+
323
+
324
+ def generate_messages_en(entity_slug: str, singular: str, pascal: str) -> str:
325
+ """Generate English messages JSON."""
326
+ return f'''{{
327
+ "title": "{pascal}",
328
+ "description": "Manage your {entity_slug}",
329
+ "fields": {{
330
+ "title": {{
331
+ "label": "Title",
332
+ "placeholder": "Enter title...",
333
+ "description": "{pascal} title"
334
+ }},
335
+ "description": {{
336
+ "label": "Description",
337
+ "placeholder": "Enter description...",
338
+ "description": "Detailed description"
339
+ }},
340
+ "status": {{
341
+ "label": "Status",
342
+ "placeholder": "Select status...",
343
+ "options": {{
344
+ "draft": "Draft",
345
+ "active": "Active",
346
+ "archived": "Archived"
347
+ }}
348
+ }}
349
+ }},
350
+ "actions": {{
351
+ "create": "Create {singular.title()}",
352
+ "edit": "Edit {singular.title()}",
353
+ "delete": "Delete {singular.title()}",
354
+ "view": "View {singular.title()}"
355
+ }},
356
+ "messages": {{
357
+ "createSuccess": "{singular.title()} created successfully",
358
+ "updateSuccess": "{singular.title()} updated successfully",
359
+ "deleteSuccess": "{singular.title()} deleted successfully",
360
+ "deleteConfirm": "Are you sure you want to delete this {singular}?"
361
+ }}
362
+ }}
363
+ '''
364
+
365
+
366
+ def generate_messages_es(entity_slug: str, singular: str, pascal: str) -> str:
367
+ """Generate Spanish messages JSON."""
368
+ return f'''{{
369
+ "title": "{pascal}",
370
+ "description": "Gestiona tus {entity_slug}",
371
+ "fields": {{
372
+ "title": {{
373
+ "label": "Titulo",
374
+ "placeholder": "Ingrese titulo...",
375
+ "description": "Titulo del {singular}"
376
+ }},
377
+ "description": {{
378
+ "label": "Descripcion",
379
+ "placeholder": "Ingrese descripcion...",
380
+ "description": "Descripcion detallada"
381
+ }},
382
+ "status": {{
383
+ "label": "Estado",
384
+ "placeholder": "Seleccione estado...",
385
+ "options": {{
386
+ "draft": "Borrador",
387
+ "active": "Activo",
388
+ "archived": "Archivado"
389
+ }}
390
+ }}
391
+ }},
392
+ "actions": {{
393
+ "create": "Crear {singular}",
394
+ "edit": "Editar {singular}",
395
+ "delete": "Eliminar {singular}",
396
+ "view": "Ver {singular}"
397
+ }},
398
+ "messages": {{
399
+ "createSuccess": "{singular.title()} creado exitosamente",
400
+ "updateSuccess": "{singular.title()} actualizado exitosamente",
401
+ "deleteSuccess": "{singular.title()} eliminado exitosamente",
402
+ "deleteConfirm": "Esta seguro que desea eliminar este {singular}?"
403
+ }}
404
+ }}
405
+ '''
406
+
407
+
408
+ def generate_migration(entity_slug: str, singular: str, pascal: str) -> str:
409
+ """Generate migration SQL file matching project conventions."""
410
+ table_name = to_snake_case(entity_slug)
411
+ date_str = datetime.now().strftime('%Y-%m-%d')
412
+
413
+ return f'''-- Migration: 001_{table_name}_table.sql
414
+ -- Description: {pascal} (table, indexes, RLS)
415
+ -- Date: {date_str}
416
+
417
+ -- ============================================
418
+ -- TABLE
419
+ -- ============================================
420
+ CREATE TABLE IF NOT EXISTS public."{table_name}" (
421
+ -- Primary Key
422
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
423
+
424
+ -- Relational Fields
425
+ "userId" TEXT NOT NULL REFERENCES public."users"(id) ON DELETE CASCADE,
426
+ "teamId" TEXT NOT NULL REFERENCES public."teams"(id) ON DELETE CASCADE,
427
+
428
+ -- Entity-specific fields
429
+ title TEXT NOT NULL,
430
+ description TEXT,
431
+ status TEXT NOT NULL DEFAULT 'draft',
432
+
433
+ -- System fields
434
+ "createdAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
435
+ "updatedAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
436
+
437
+ -- Constraints
438
+ CONSTRAINT {table_name}_status_check CHECK (status IN ('draft', 'active', 'archived'))
439
+ );
440
+
441
+ COMMENT ON TABLE public."{table_name}" IS '{pascal} table with team isolation via RLS';
442
+ COMMENT ON COLUMN public."{table_name}"."userId" IS 'Owner user id';
443
+ COMMENT ON COLUMN public."{table_name}"."teamId" IS 'Team context for isolation';
444
+ COMMENT ON COLUMN public."{table_name}".title IS '{singular.title()} title';
445
+ COMMENT ON COLUMN public."{table_name}".description IS 'Detailed description';
446
+ COMMENT ON COLUMN public."{table_name}".status IS 'Status: draft, active, archived';
447
+
448
+ -- ============================================
449
+ -- TRIGGER updatedAt
450
+ -- ============================================
451
+ DROP TRIGGER IF EXISTS {table_name}_set_updated_at ON public."{table_name}";
452
+ CREATE TRIGGER {table_name}_set_updated_at
453
+ BEFORE UPDATE ON public."{table_name}"
454
+ FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
455
+
456
+ -- ============================================
457
+ -- INDEXES
458
+ -- ============================================
459
+ CREATE INDEX IF NOT EXISTS idx_{table_name}_user_id ON public."{table_name}"("userId");
460
+ CREATE INDEX IF NOT EXISTS idx_{table_name}_team_id ON public."{table_name}"("teamId");
461
+ CREATE INDEX IF NOT EXISTS idx_{table_name}_user_team ON public."{table_name}"("userId", "teamId");
462
+ CREATE INDEX IF NOT EXISTS idx_{table_name}_status ON public."{table_name}"(status);
463
+ CREATE INDEX IF NOT EXISTS idx_{table_name}_created_at ON public."{table_name}"("createdAt" DESC);
464
+ CREATE INDEX IF NOT EXISTS idx_{table_name}_title_search ON public."{table_name}" USING GIN(to_tsvector('english', title));
465
+
466
+ -- ============================================
467
+ -- RLS
468
+ -- ============================================
469
+ ALTER TABLE public."{table_name}" ENABLE ROW LEVEL SECURITY;
470
+
471
+ -- Cleanup existing policies
472
+ DROP POLICY IF EXISTS "{pascal} team can do all" ON public."{table_name}";
473
+
474
+ -- ============================
475
+ -- RLS: TEAM ISOLATION
476
+ -- ============================
477
+ -- RLS verifies team membership
478
+ -- The access.shared (user isolation) logic is handled at APP LEVEL
479
+ CREATE POLICY "{pascal} team can do all"
480
+ ON public."{table_name}"
481
+ FOR ALL TO authenticated
482
+ USING (
483
+ -- Superadmin bypass
484
+ public.is_superadmin()
485
+ OR
486
+ -- Team isolation: user must be member of the team
487
+ "teamId" = ANY(public.get_user_team_ids())
488
+ )
489
+ WITH CHECK (
490
+ public.is_superadmin()
491
+ OR
492
+ "teamId" = ANY(public.get_user_team_ids())
493
+ );
494
+ '''
495
+
496
+
497
+ def generate_metas_migration(entity_slug: str, singular: str, pascal: str) -> str:
498
+ """Generate metas table migration SQL file matching project conventions."""
499
+ table_name = to_snake_case(entity_slug)
500
+ date_str = datetime.now().strftime('%Y-%m-%d')
501
+
502
+ return f'''-- Migration: 002_{table_name}_metas.sql
503
+ -- Description: {pascal} metas (table, indexes, RLS)
504
+ -- Date: {date_str}
505
+
506
+ -- ============================================
507
+ -- TABLE
508
+ -- ============================================
509
+ CREATE TABLE IF NOT EXISTS public."{table_name}_metas" (
510
+ id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
511
+ "entityId" TEXT NOT NULL REFERENCES public."{table_name}"(id) ON DELETE CASCADE,
512
+ "metaKey" TEXT NOT NULL,
513
+ "metaValue" JSONB NOT NULL DEFAULT '{{}}'::jsonb,
514
+ "dataType" TEXT DEFAULT 'json',
515
+ "isPublic" BOOLEAN NOT NULL DEFAULT false,
516
+ "isSearchable" BOOLEAN NOT NULL DEFAULT false,
517
+ "createdAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
518
+ "updatedAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
519
+ CONSTRAINT {table_name}_metas_unique_key UNIQUE ("entityId", "metaKey")
520
+ );
521
+
522
+ COMMENT ON TABLE public."{table_name}_metas" IS 'Key-value metadata for {table_name}';
523
+ COMMENT ON COLUMN public."{table_name}_metas"."entityId" IS 'Reference to parent {singular}';
524
+ COMMENT ON COLUMN public."{table_name}_metas"."metaKey" IS 'Metadata key identifier';
525
+ COMMENT ON COLUMN public."{table_name}_metas"."metaValue" IS 'Metadata value in JSONB format';
526
+ COMMENT ON COLUMN public."{table_name}_metas"."dataType" IS 'Type hint: json, string, number, boolean';
527
+ COMMENT ON COLUMN public."{table_name}_metas"."isPublic" IS 'Whether this metadata is publicly readable';
528
+ COMMENT ON COLUMN public."{table_name}_metas"."isSearchable" IS 'Whether this metadata is searchable';
529
+
530
+ -- ============================================
531
+ -- TRIGGER updatedAt
532
+ -- ============================================
533
+ DROP TRIGGER IF EXISTS {table_name}_metas_set_updated_at ON public."{table_name}_metas";
534
+ CREATE TRIGGER {table_name}_metas_set_updated_at
535
+ BEFORE UPDATE ON public."{table_name}_metas"
536
+ FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
537
+
538
+ -- ============================================
539
+ -- INDEXES
540
+ -- ============================================
541
+ CREATE INDEX IF NOT EXISTS idx_{table_name}_metas_entity_id ON public."{table_name}_metas"("entityId");
542
+ CREATE INDEX IF NOT EXISTS idx_{table_name}_metas_key ON public."{table_name}_metas"("metaKey");
543
+ CREATE INDEX IF NOT EXISTS idx_{table_name}_metas_is_public ON public."{table_name}_metas"("isPublic") WHERE "isPublic" = true;
544
+ CREATE INDEX IF NOT EXISTS idx_{table_name}_metas_searchable ON public."{table_name}_metas"("isSearchable") WHERE "isSearchable" = true;
545
+ CREATE INDEX IF NOT EXISTS idx_{table_name}_metas_value_gin ON public."{table_name}_metas" USING GIN ("metaValue");
546
+ CREATE INDEX IF NOT EXISTS idx_{table_name}_metas_value_ops ON public."{table_name}_metas" USING GIN ("metaValue" jsonb_path_ops);
547
+
548
+ -- ============================================
549
+ -- RLS
550
+ -- ============================================
551
+ ALTER TABLE public."{table_name}_metas" ENABLE ROW LEVEL SECURITY;
552
+
553
+ -- Cleanup existing policies
554
+ DROP POLICY IF EXISTS "{pascal} metas team can do all" ON public."{table_name}_metas";
555
+
556
+ -- ============================
557
+ -- RLS: TEAM ISOLATION VIA PARENT
558
+ -- ============================
559
+ -- Inherits isolation from parent via teamId
560
+ CREATE POLICY "{pascal} metas team can do all"
561
+ ON public."{table_name}_metas"
562
+ FOR ALL TO authenticated
563
+ USING (
564
+ -- Superadmin bypass
565
+ public.is_superadmin()
566
+ OR
567
+ -- Team isolation via parent
568
+ EXISTS (
569
+ SELECT 1 FROM public."{table_name}" t
570
+ WHERE t.id = "entityId"
571
+ AND t."teamId" = ANY(public.get_user_team_ids())
572
+ )
573
+ )
574
+ WITH CHECK (
575
+ public.is_superadmin()
576
+ OR
577
+ EXISTS (
578
+ SELECT 1 FROM public."{table_name}" t
579
+ WHERE t.id = "entityId"
580
+ AND t."teamId" = ANY(public.get_user_team_ids())
581
+ )
582
+ );
583
+ '''
584
+
585
+
586
+ def main():
587
+ parser = argparse.ArgumentParser(description='Scaffold a new entity')
588
+ parser.add_argument('--entity', required=True, help='Entity name in kebab-case (plural)')
589
+ parser.add_argument('--theme', default=None, help='Theme name')
590
+ parser.add_argument('--with-metas', action='store_true', help='Include metadata table')
591
+ parser.add_argument('--with-builder', action='store_true', help='Enable page builder')
592
+ parser.add_argument('--dry-run', action='store_true', help='Show what would be created')
593
+
594
+ args = parser.parse_args()
595
+
596
+ theme = args.theme or get_active_theme()
597
+ entity_slug = args.entity.lower()
598
+ singular = to_singular(entity_slug)
599
+ pascal = to_pascal_case(entity_slug)
600
+
601
+ # Base path
602
+ base_path = Path(f'contents/themes/{theme}/entities/{entity_slug}')
603
+
604
+ print(f"\n{'=' * 60}")
605
+ print(f"SCAFFOLDING ENTITY: {entity_slug}")
606
+ print(f"{'=' * 60}")
607
+ print(f"Theme: {theme}")
608
+ print(f"Singular: {singular}")
609
+ print(f"Pascal: {pascal}")
610
+ print(f"Base path: {base_path}")
611
+ print(f"{'=' * 60}\n")
612
+
613
+ # Files to create
614
+ files = {
615
+ f'{entity_slug}.config.ts': generate_config_file(entity_slug, singular, pascal),
616
+ f'{entity_slug}.fields.ts': generate_fields_file(entity_slug, singular, pascal),
617
+ f'{entity_slug}.types.ts': generate_types_file(entity_slug, singular, pascal),
618
+ f'{entity_slug}.service.ts': generate_service_file(entity_slug, singular, pascal),
619
+ 'messages/en.json': generate_messages_en(entity_slug, singular, pascal),
620
+ 'messages/es.json': generate_messages_es(entity_slug, singular, pascal),
621
+ f'migrations/001_{to_snake_case(entity_slug)}_table.sql': generate_migration(entity_slug, singular, pascal),
622
+ }
623
+
624
+ # Add metas migration if requested
625
+ if args.with_metas:
626
+ files[f'migrations/002_{to_snake_case(entity_slug)}_metas.sql'] = generate_metas_migration(entity_slug, singular, pascal)
627
+
628
+ if args.dry_run:
629
+ print("DRY RUN - Files that would be created:\n")
630
+ for file_path in files.keys():
631
+ print(f" {base_path / file_path}")
632
+ print("\nRun without --dry-run to create files.")
633
+ return 0
634
+
635
+ # Create directories and files
636
+ for file_path, content in files.items():
637
+ full_path = base_path / file_path
638
+ full_path.parent.mkdir(parents=True, exist_ok=True)
639
+
640
+ if full_path.exists():
641
+ print(f" SKIP (exists): {full_path}")
642
+ else:
643
+ with open(full_path, 'w', encoding='utf-8') as f:
644
+ f.write(content)
645
+ print(f" CREATED: {full_path}")
646
+
647
+ print(f"\n{'=' * 60}")
648
+ print("NEXT STEPS:")
649
+ print("=" * 60)
650
+ print(f"1. Review and customize {entity_slug}.config.ts")
651
+ print(f"2. Add/modify fields in {entity_slug}.fields.ts")
652
+ print(f"3. Update translations in messages/en.json and es.json")
653
+ print(f"4. Run migration: pnpm db:migrate")
654
+ print(f"5. Regenerate registry: node core/scripts/build/registry.mjs")
655
+ print("=" * 60 + "\n")
656
+
657
+ return 0
658
+
659
+
660
+ if __name__ == '__main__':
661
+ sys.exit(main())