@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,444 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Scaffold Block - Generate Page Builder Block Structure
4
+
5
+ Creates the 5 required files for a new page builder block:
6
+ - config.ts
7
+ - schema.ts
8
+ - fields.ts
9
+ - component.tsx
10
+ - index.ts
11
+
12
+ Usage:
13
+ python scaffold-block.py [--theme THEME] [--slug SLUG] [--name NAME]
14
+
15
+ Interactive mode if no arguments provided.
16
+ """
17
+
18
+ import argparse
19
+ import os
20
+ import re
21
+ import subprocess
22
+ import sys
23
+ from pathlib import Path
24
+
25
+ # Block categories
26
+ CATEGORIES = [
27
+ 'hero', 'features', 'cta', 'content', 'testimonials',
28
+ 'pricing', 'faq', 'stats', 'gallery', 'timeline',
29
+ 'contact', 'newsletter', 'team', 'portfolio', 'custom'
30
+ ]
31
+
32
+ # Common Lucide icons for blocks
33
+ COMMON_ICONS = [
34
+ 'LayoutTemplate', 'Grid', 'List', 'Star', 'Quote',
35
+ 'DollarSign', 'HelpCircle', 'BarChart', 'Image', 'Clock',
36
+ 'Mail', 'Users', 'Briefcase', 'Sparkles', 'Box'
37
+ ]
38
+
39
+
40
+ def to_kebab_case(s: str) -> str:
41
+ """Convert string to kebab-case."""
42
+ s = re.sub(r'([A-Z])', r'-\1', s)
43
+ s = re.sub(r'[\s_]+', '-', s)
44
+ return s.lower().strip('-')
45
+
46
+
47
+ def to_pascal_case(s: str) -> str:
48
+ """Convert string to PascalCase."""
49
+ return ''.join(word.capitalize() for word in re.split(r'[-_\s]+', s))
50
+
51
+
52
+ def to_camel_case(s: str) -> str:
53
+ """Convert string to camelCase."""
54
+ pascal = to_pascal_case(s)
55
+ return pascal[0].lower() + pascal[1:] if pascal else ''
56
+
57
+
58
+ def get_project_root() -> Path:
59
+ """Get project root directory."""
60
+ # Look for package.json or .env
61
+ current = Path.cwd()
62
+ while current != current.parent:
63
+ if (current / 'package.json').exists():
64
+ return current
65
+ current = current.parent
66
+ return Path.cwd()
67
+
68
+
69
+ def get_active_theme(project_root: Path) -> str:
70
+ """Get active theme from .env file."""
71
+ env_file = project_root / '.env'
72
+ if env_file.exists():
73
+ with open(env_file, 'r') as f:
74
+ for line in f:
75
+ if line.startswith('NEXT_PUBLIC_ACTIVE_THEME='):
76
+ return line.split('=', 1)[1].strip().strip('"\'')
77
+ return 'default'
78
+
79
+
80
+ def prompt_input(prompt: str, default: str = None, choices: list = None) -> str:
81
+ """Prompt user for input with optional default and choices."""
82
+ if choices:
83
+ print(f"\nAvailable options: {', '.join(choices)}")
84
+
85
+ if default:
86
+ result = input(f"{prompt} [{default}]: ").strip()
87
+ return result if result else default
88
+ else:
89
+ while True:
90
+ result = input(f"{prompt}: ").strip()
91
+ if result:
92
+ return result
93
+ print("This field is required.")
94
+
95
+
96
+ def validate_slug(slug: str, blocks_dir: Path) -> bool:
97
+ """Validate block slug."""
98
+ if not re.match(r'^[a-z][a-z0-9-]*$', slug):
99
+ print(f"Error: Slug must be kebab-case (lowercase letters, numbers, hyphens)")
100
+ return False
101
+
102
+ if (blocks_dir / slug).exists():
103
+ print(f"Error: Block '{slug}' already exists")
104
+ return False
105
+
106
+ return True
107
+
108
+
109
+ def generate_config(slug: str, name: str, description: str, category: str, icon: str, scope: list) -> str:
110
+ """Generate config.ts content."""
111
+ scope_str = str(scope).replace("'", '"')
112
+ return f'''import type {{ BlockConfig, BlockCategory }} from '@/core/types/blocks'
113
+
114
+ export const config: Omit<BlockConfig, 'schema' | 'fieldDefinitions' | 'Component' | 'examples'> = {{
115
+ slug: '{slug}',
116
+ name: '{name}',
117
+ description: '{description}',
118
+ category: '{category}' as BlockCategory,
119
+ icon: '{icon}',
120
+ thumbnail: '/theme/blocks/{slug}/thumbnail.png',
121
+ scope: {scope_str},
122
+ }}
123
+ '''
124
+
125
+
126
+ def generate_schema(slug: str) -> str:
127
+ """Generate schema.ts content."""
128
+ pascal = to_pascal_case(slug)
129
+ return f'''import {{ z }} from 'zod'
130
+ import {{ baseBlockSchema }} from '@/core/types/blocks'
131
+
132
+ // Extend baseBlockSchema with block-specific fields
133
+ // baseBlockSchema already includes: title, content, cta, backgroundColor, className, id
134
+ export const schema = baseBlockSchema.merge(z.object({{
135
+ // Add custom fields here
136
+ // Example: items: z.array(itemSchema).optional()
137
+ }}))
138
+
139
+ export type {pascal}Props = z.infer<typeof schema>
140
+ '''
141
+
142
+
143
+ def generate_fields(slug: str) -> str:
144
+ """Generate fields.ts content."""
145
+ return '''import type { FieldDefinition } from '@/core/types/blocks'
146
+ import {
147
+ baseContentFields,
148
+ baseDesignFields,
149
+ baseAdvancedFields,
150
+ } from '@/core/types/blocks'
151
+
152
+ // Custom content fields
153
+ const customContentFields: FieldDefinition[] = [
154
+ // Add custom content fields here
155
+ // Example:
156
+ // {
157
+ // name: 'subtitle',
158
+ // label: 'Subtitle',
159
+ // type: 'text',
160
+ // tab: 'content',
161
+ // },
162
+ ]
163
+
164
+ // Custom design fields
165
+ const customDesignFields: FieldDefinition[] = [
166
+ // Add custom design fields here
167
+ // Example:
168
+ // {
169
+ // name: 'layout',
170
+ // label: 'Layout',
171
+ // type: 'select',
172
+ // tab: 'design',
173
+ // options: [
174
+ // { label: 'Left', value: 'left' },
175
+ // { label: 'Right', value: 'right' },
176
+ // ],
177
+ // },
178
+ ]
179
+
180
+ // CRITICAL: Order matters - content -> design -> advanced
181
+ export const fieldDefinitions: FieldDefinition[] = [
182
+ ...baseContentFields,
183
+ ...customContentFields,
184
+ ...baseDesignFields,
185
+ ...customDesignFields,
186
+ ...baseAdvancedFields, // MUST be last
187
+ ]
188
+
189
+ // Compatibility alias
190
+ export const fields = fieldDefinitions
191
+ '''
192
+
193
+
194
+ def generate_component(slug: str, name: str) -> str:
195
+ """Generate component.tsx content."""
196
+ pascal = to_pascal_case(slug)
197
+ camel = to_camel_case(slug)
198
+ return f'''import {{ buildSectionClasses }} from '@/core/types/blocks'
199
+ import {{ sel }} from '../../lib/selectors'
200
+ import type {{ {pascal}Props }} from './schema'
201
+
202
+ export function {pascal}Block({{
203
+ // Base content props (from baseBlockSchema)
204
+ title,
205
+ content,
206
+ cta,
207
+ // Custom props
208
+ // Add destructured custom props here
209
+ // Base design props
210
+ backgroundColor,
211
+ // Base advanced props
212
+ className,
213
+ id,
214
+ }}: {pascal}Props) {{
215
+ // Use buildSectionClasses helper (NEVER hardcode colors)
216
+ const sectionClasses = buildSectionClasses('py-16 px-4 md:py-24', {{
217
+ backgroundColor,
218
+ className,
219
+ }})
220
+
221
+ return (
222
+ <section
223
+ id={{id}}
224
+ className={{sectionClasses}}
225
+ data-cy={{sel('blocks.{camel}.container')}}
226
+ >
227
+ <div className="container mx-auto">
228
+ {{title && (
229
+ <h2 className="text-3xl font-bold text-center mb-4">{{title}}</h2>
230
+ )}}
231
+ {{content && (
232
+ <div
233
+ className="text-center text-muted-foreground mb-8 max-w-2xl mx-auto"
234
+ dangerouslySetInnerHTML={{{{ __html: content }}}}
235
+ />
236
+ )}}
237
+
238
+ {{/* Add custom block content here */}}
239
+
240
+ {{cta?.text && cta?.link && (
241
+ <div className="mt-8 text-center">
242
+ <a
243
+ href={{cta.link}}
244
+ target={{cta.target || '_self'}}
245
+ className="inline-flex items-center px-6 py-3 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors"
246
+ data-cy={{sel('blocks.{camel}.cta')}}
247
+ >
248
+ {{cta.text}}
249
+ </a>
250
+ </div>
251
+ )}}
252
+ </div>
253
+ </section>
254
+ )
255
+ }}
256
+ '''
257
+
258
+
259
+ def generate_index(slug: str) -> str:
260
+ """Generate index.ts content."""
261
+ pascal = to_pascal_case(slug)
262
+ return f'''export {{ config }} from './config'
263
+ export {{ schema }} from './schema'
264
+ export {{ fields, fieldDefinitions }} from './fields'
265
+ export {{ {pascal}Block as Component }} from './component'
266
+
267
+ export type {{ {pascal}Props }} from './schema'
268
+ '''
269
+
270
+
271
+ def update_selectors(selectors_path: Path, slug: str) -> bool:
272
+ """Add block selectors to BLOCK_SELECTORS."""
273
+ camel = to_camel_case(slug)
274
+
275
+ if not selectors_path.exists():
276
+ print(f"Warning: Selectors file not found at {selectors_path}")
277
+ return False
278
+
279
+ with open(selectors_path, 'r') as f:
280
+ content = f.read()
281
+
282
+ # Check if already exists
283
+ if f"'{camel}':" in content or f'"{camel}":' in content:
284
+ print(f"Selectors for '{camel}' already exist")
285
+ return True
286
+
287
+ # Find BLOCK_SELECTORS and add entry
288
+ selector_entry = f''' {camel}: {{
289
+ container: 'block-{slug}',
290
+ }},'''
291
+
292
+ # Find the closing of BLOCK_SELECTORS object
293
+ match = re.search(r'(export const BLOCK_SELECTORS\s*=\s*\{[^}]*)(} as const)', content, re.DOTALL)
294
+ if match:
295
+ new_content = content[:match.end(1)] + '\n' + selector_entry + '\n' + match.group(2) + content[match.end():]
296
+ with open(selectors_path, 'w') as f:
297
+ f.write(new_content)
298
+ print(f"Added selectors for '{camel}' to BLOCK_SELECTORS")
299
+ return True
300
+ else:
301
+ print("Warning: Could not find BLOCK_SELECTORS in selectors file")
302
+ return False
303
+
304
+
305
+ def run_registry_build(project_root: Path) -> bool:
306
+ """Run registry build script."""
307
+ print("\nRebuilding block registry...")
308
+ try:
309
+ result = subprocess.run(
310
+ ['node', 'core/scripts/build/registry.mjs'],
311
+ cwd=project_root,
312
+ capture_output=True,
313
+ text=True
314
+ )
315
+ if result.returncode == 0:
316
+ print("Registry rebuilt successfully")
317
+ return True
318
+ else:
319
+ print(f"Registry build failed: {result.stderr}")
320
+ return False
321
+ except Exception as e:
322
+ print(f"Error running registry build: {e}")
323
+ return False
324
+
325
+
326
+ def verify_block_in_registry(project_root: Path, slug: str) -> bool:
327
+ """Verify block was added to registry."""
328
+ registry_path = project_root / 'core' / 'lib' / 'registries' / 'block-registry.ts'
329
+ if registry_path.exists():
330
+ with open(registry_path, 'r') as f:
331
+ content = f.read()
332
+ if f"'{slug}':" in content or f'"{slug}":' in content:
333
+ print(f"Block '{slug}' verified in BLOCK_REGISTRY")
334
+ return True
335
+ print(f"Warning: Block '{slug}' not found in BLOCK_REGISTRY")
336
+ return False
337
+
338
+
339
+ def main():
340
+ parser = argparse.ArgumentParser(description='Scaffold a new page builder block')
341
+ parser.add_argument('--theme', help='Theme name (default: from .env)')
342
+ parser.add_argument('--slug', help='Block slug (kebab-case)')
343
+ parser.add_argument('--name', help='Block display name')
344
+ parser.add_argument('--description', help='Block description')
345
+ parser.add_argument('--category', help='Block category', choices=CATEGORIES)
346
+ parser.add_argument('--icon', help='Lucide icon name')
347
+ parser.add_argument('--scope', help='Scope: pages, posts, or both', default='pages')
348
+ args = parser.parse_args()
349
+
350
+ project_root = get_project_root()
351
+ print(f"Project root: {project_root}")
352
+
353
+ # Check if all required args provided (non-interactive mode)
354
+ non_interactive = all([args.theme or get_active_theme(project_root), args.slug, args.name])
355
+
356
+ # Get theme
357
+ theme = args.theme or get_active_theme(project_root)
358
+ if not non_interactive:
359
+ theme = prompt_input("Theme", default=theme)
360
+
361
+ blocks_dir = project_root / 'contents' / 'themes' / theme / 'blocks'
362
+ if not blocks_dir.exists():
363
+ print(f"Error: Theme blocks directory not found: {blocks_dir}")
364
+ sys.exit(1)
365
+
366
+ # Get block metadata
367
+ if non_interactive:
368
+ slug = to_kebab_case(args.slug)
369
+ if not validate_slug(slug, blocks_dir):
370
+ sys.exit(1)
371
+ name = args.name
372
+ description = args.description or f"{name} block"
373
+ category = args.category or 'content'
374
+ icon = args.icon or 'Box'
375
+ scope_input = args.scope or 'pages'
376
+ else:
377
+ while True:
378
+ slug = args.slug or prompt_input("Block slug (kebab-case)")
379
+ slug = to_kebab_case(slug)
380
+ if validate_slug(slug, blocks_dir):
381
+ break
382
+ args.slug = None # Reset to prompt again
383
+
384
+ name = args.name or prompt_input("Display name", default=to_pascal_case(slug).replace('', ' ').strip())
385
+ description = args.description or prompt_input("Description", default=f"{name} block")
386
+
387
+ print(f"\nCategories: {', '.join(CATEGORIES)}")
388
+ category = args.category or prompt_input("Category", default='content', choices=CATEGORIES)
389
+
390
+ print(f"\nCommon icons: {', '.join(COMMON_ICONS)}")
391
+ icon = args.icon or prompt_input("Lucide icon", default='Box')
392
+
393
+ scope_input = args.scope or prompt_input("Scope (pages, posts, both)", default='pages')
394
+
395
+ if category not in CATEGORIES:
396
+ category = 'custom'
397
+ if scope_input == 'both':
398
+ scope = ['pages', 'posts']
399
+ else:
400
+ scope = [scope_input]
401
+
402
+ # Create block directory
403
+ block_dir = blocks_dir / slug
404
+ block_dir.mkdir(parents=True, exist_ok=True)
405
+ print(f"\nCreating block at: {block_dir}")
406
+
407
+ # Generate files
408
+ files = {
409
+ 'config.ts': generate_config(slug, name, description, category, icon, scope),
410
+ 'schema.ts': generate_schema(slug),
411
+ 'fields.ts': generate_fields(slug),
412
+ 'component.tsx': generate_component(slug, name),
413
+ 'index.ts': generate_index(slug),
414
+ }
415
+
416
+ for filename, content in files.items():
417
+ filepath = block_dir / filename
418
+ with open(filepath, 'w') as f:
419
+ f.write(content)
420
+ print(f" Created: {filename}")
421
+
422
+ # Update selectors
423
+ selectors_path = project_root / 'contents' / 'themes' / theme / 'lib' / 'selectors.ts'
424
+ update_selectors(selectors_path, slug)
425
+
426
+ # Run registry build
427
+ run_registry_build(project_root)
428
+
429
+ # Verify
430
+ verify_block_in_registry(project_root, slug)
431
+
432
+ print(f"\n{'='*50}")
433
+ print(f"Block '{slug}' created successfully!")
434
+ print(f"\nNext steps:")
435
+ print(f" 1. Edit schema.ts to add custom fields")
436
+ print(f" 2. Edit fields.ts to add field definitions")
437
+ print(f" 3. Edit component.tsx to implement rendering")
438
+ print(f" 4. Run: node core/scripts/build/registry.mjs")
439
+ print(f" 5. Test block in page builder")
440
+ print(f"{'='*50}")
441
+
442
+
443
+ if __name__ == '__main__':
444
+ main()