@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,323 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Validate Migration Script
4
+
5
+ Validates SQL migration files against project conventions.
6
+
7
+ Usage:
8
+ python validate-migration.py --file PATH [--strict]
9
+ python validate-migration.py --path DIR [--strict]
10
+
11
+ Options:
12
+ --file PATH Single migration file to validate
13
+ --path DIR Directory of migrations to validate
14
+ --strict Exit with error if issues found
15
+ """
16
+
17
+ import os
18
+ import sys
19
+ import re
20
+ import argparse
21
+ from pathlib import Path
22
+ from typing import List, Dict, Tuple
23
+
24
+
25
+ class ValidationError:
26
+ """Represents a validation issue."""
27
+ def __init__(self, severity: str, message: str, line: int = None, context: str = None):
28
+ self.severity = severity # 'error', 'warning', 'info'
29
+ self.message = message
30
+ self.line = line
31
+ self.context = context
32
+
33
+
34
+ def validate_timestamptz(content: str, errors: List[ValidationError]) -> None:
35
+ """Check that all timestamps use TIMESTAMPTZ, not TIMESTAMP."""
36
+ lines = content.split('\n')
37
+ for i, line in enumerate(lines, 1):
38
+ # Skip comments
39
+ if line.strip().startswith('--'):
40
+ continue
41
+
42
+ # Check for plain TIMESTAMP (not TIMESTAMPTZ)
43
+ if re.search(r'\bTIMESTAMP\b(?!TZ)', line, re.IGNORECASE):
44
+ errors.append(ValidationError(
45
+ 'error',
46
+ 'Use TIMESTAMPTZ instead of TIMESTAMP',
47
+ i,
48
+ line.strip()[:60]
49
+ ))
50
+
51
+ # Check for CURRENT_TIMESTAMP
52
+ if 'CURRENT_TIMESTAMP' in line.upper():
53
+ errors.append(ValidationError(
54
+ 'warning',
55
+ 'Use now() instead of CURRENT_TIMESTAMP',
56
+ i,
57
+ line.strip()[:60]
58
+ ))
59
+
60
+
61
+ def validate_id_type(content: str, errors: List[ValidationError]) -> None:
62
+ """Check that IDs use TEXT, not UUID type."""
63
+ lines = content.split('\n')
64
+ for i, line in enumerate(lines, 1):
65
+ if line.strip().startswith('--'):
66
+ continue
67
+
68
+ # Check for UUID PRIMARY KEY (should be TEXT)
69
+ if re.search(r'\bid\s+UUID\s+PRIMARY\s+KEY', line, re.IGNORECASE):
70
+ errors.append(ValidationError(
71
+ 'error',
72
+ 'Use TEXT PRIMARY KEY, not UUID (Better Auth uses TEXT)',
73
+ i,
74
+ line.strip()[:60]
75
+ ))
76
+
77
+
78
+ def validate_field_ordering(content: str, errors: List[ValidationError]) -> None:
79
+ """Check that fields follow the required ordering."""
80
+ # Find CREATE TABLE blocks
81
+ create_pattern = r'CREATE\s+TABLE[^;]+;'
82
+ for match in re.finditer(create_pattern, content, re.IGNORECASE | re.DOTALL):
83
+ table_def = match.group()
84
+
85
+ # Extract field lines
86
+ lines = table_def.split('\n')
87
+ fields = []
88
+ for line in lines:
89
+ # Skip comments and empty lines
90
+ stripped = line.strip()
91
+ if not stripped or stripped.startswith('--') or stripped.startswith('CREATE') or stripped.startswith(')'):
92
+ continue
93
+ if stripped.startswith('CONSTRAINT'):
94
+ continue
95
+ fields.append(stripped)
96
+
97
+ if not fields:
98
+ continue
99
+
100
+ # Check first field is 'id'
101
+ if fields and not fields[0].strip().lower().startswith('id '):
102
+ errors.append(ValidationError(
103
+ 'warning',
104
+ 'First field should be "id" (primary key)',
105
+ context=fields[0][:50]
106
+ ))
107
+
108
+ # Check system fields are last
109
+ system_fields = ['createdAt', 'updatedAt', 'status']
110
+ found_system = False
111
+ for field in fields:
112
+ for sys_field in system_fields:
113
+ if f'"{sys_field}"' in field or field.strip().startswith(sys_field):
114
+ found_system = True
115
+ break
116
+ if found_system:
117
+ # After finding a system field, no business fields should appear
118
+ if not any(sf in field for sf in system_fields) and 'CONSTRAINT' not in field:
119
+ if re.match(r'^"\w+"\s+', field) and field.strip() != ')':
120
+ errors.append(ValidationError(
121
+ 'info',
122
+ 'System fields (createdAt, updatedAt) should be last',
123
+ context=field[:50]
124
+ ))
125
+
126
+
127
+ def validate_meta_fk_naming(content: str, errors: List[ValidationError]) -> None:
128
+ """Check that meta tables use "entityId" not entity-specific names."""
129
+ if '_metas' not in content.lower():
130
+ return
131
+
132
+ # Check for incorrect FK naming patterns
133
+ wrong_patterns = [
134
+ (r'"postId"\s+TEXT\s+NOT\s+NULL\s+REFERENCES', 'Use "entityId" instead of "postId"'),
135
+ (r'"userId"\s+TEXT\s+NOT\s+NULL\s+REFERENCES.*metas', 'Use "entityId" instead of "userId" for meta table'),
136
+ (r'"clientId"\s+TEXT\s+NOT\s+NULL\s+REFERENCES', 'Use "entityId" instead of "clientId"'),
137
+ (r'"taskId"\s+TEXT\s+NOT\s+NULL\s+REFERENCES', 'Use "entityId" instead of "taskId"'),
138
+ ]
139
+
140
+ for pattern, message in wrong_patterns:
141
+ if re.search(pattern, content, re.IGNORECASE):
142
+ errors.append(ValidationError(
143
+ 'error',
144
+ message
145
+ ))
146
+
147
+
148
+ def validate_child_fk_naming(content: str, errors: List[ValidationError]) -> None:
149
+ """Check that child tables use "parentId" not parent-specific names."""
150
+ # Skip if it's a meta table
151
+ if '_metas' in content.lower():
152
+ return
153
+
154
+ # Check for child table patterns (has FK but no userId directly)
155
+ lines = content.split('\n')
156
+ for i, line in enumerate(lines, 1):
157
+ if line.strip().startswith('--'):
158
+ continue
159
+
160
+ # Look for FK references in child tables that aren't "parentId"
161
+ if re.search(r'"(clientId|orderId|productId)"\s+TEXT\s+NOT\s+NULL\s+REFERENCES', line, re.IGNORECASE):
162
+ # Skip if it's the users table reference
163
+ if 'public."users"' in line:
164
+ continue
165
+ errors.append(ValidationError(
166
+ 'info',
167
+ 'Consider using "parentId" for child entity FK',
168
+ i,
169
+ line.strip()[:60]
170
+ ))
171
+
172
+
173
+ def validate_rls_enabled(content: str, errors: List[ValidationError]) -> None:
174
+ """Check that RLS is enabled for tables."""
175
+ # Find table names
176
+ table_pattern = r'CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?public\."(\w+)"'
177
+ tables = re.findall(table_pattern, content, re.IGNORECASE)
178
+
179
+ for table in tables:
180
+ # Check if RLS is enabled
181
+ rls_pattern = rf'ALTER\s+TABLE\s+public\."{table}"\s+ENABLE\s+ROW\s+LEVEL\s+SECURITY'
182
+ if not re.search(rls_pattern, content, re.IGNORECASE):
183
+ errors.append(ValidationError(
184
+ 'warning',
185
+ f'RLS not enabled for table "{table}"',
186
+ ))
187
+
188
+
189
+ def validate_cascade_usage(content: str, errors: List[ValidationError]) -> None:
190
+ """Check that main tables use DROP CASCADE."""
191
+ # Find DROP TABLE statements
192
+ drop_pattern = r'DROP\s+TABLE\s+IF\s+EXISTS\s+public\."(\w+)"'
193
+ for match in re.finditer(drop_pattern, content, re.IGNORECASE):
194
+ table = match.group(1)
195
+ full_match = match.group(0)
196
+
197
+ # Skip meta tables
198
+ if '_metas' in table.lower():
199
+ continue
200
+
201
+ # Check if CASCADE is used
202
+ line_end = content[match.end():match.end()+20]
203
+ if 'CASCADE' not in line_end.upper():
204
+ errors.append(ValidationError(
205
+ 'warning',
206
+ f'Use "DROP TABLE IF EXISTS ... CASCADE" for main table "{table}"',
207
+ ))
208
+
209
+
210
+ def validate_trigger_function(content: str, errors: List[ValidationError]) -> None:
211
+ """Check that triggers use existing Better Auth function."""
212
+ if 'set_updated_at' in content.lower():
213
+ # Check for function redefinition
214
+ if 'CREATE OR REPLACE FUNCTION' in content and 'set_updated_at' in content:
215
+ errors.append(ValidationError(
216
+ 'error',
217
+ 'Do not redefine set_updated_at() - use existing Better Auth function',
218
+ ))
219
+
220
+
221
+ def validate_file(file_path: Path) -> List[ValidationError]:
222
+ """Validate a single migration file."""
223
+ errors = []
224
+
225
+ try:
226
+ with open(file_path, 'r', encoding='utf-8') as f:
227
+ content = f.read()
228
+ except Exception as e:
229
+ errors.append(ValidationError('error', f'Could not read file: {e}'))
230
+ return errors
231
+
232
+ # Run all validations
233
+ validate_timestamptz(content, errors)
234
+ validate_id_type(content, errors)
235
+ validate_field_ordering(content, errors)
236
+ validate_meta_fk_naming(content, errors)
237
+ validate_child_fk_naming(content, errors)
238
+ validate_rls_enabled(content, errors)
239
+ validate_cascade_usage(content, errors)
240
+ validate_trigger_function(content, errors)
241
+
242
+ return errors
243
+
244
+
245
+ def main():
246
+ parser = argparse.ArgumentParser(description='Validate migration files')
247
+ parser.add_argument('--file', help='Single migration file to validate')
248
+ parser.add_argument('--path', help='Directory of migrations to validate')
249
+ parser.add_argument('--strict', action='store_true', help='Exit with error if issues found')
250
+
251
+ args = parser.parse_args()
252
+
253
+ if not args.file and not args.path:
254
+ args.path = 'core/migrations/'
255
+
256
+ print(f"\n{'=' * 60}")
257
+ print("MIGRATION VALIDATION")
258
+ print(f"{'=' * 60}")
259
+
260
+ files_to_check = []
261
+
262
+ if args.file:
263
+ files_to_check.append(Path(args.file))
264
+ elif args.path:
265
+ path = Path(args.path)
266
+ if path.is_dir():
267
+ files_to_check = list(path.glob('*.sql'))
268
+ else:
269
+ print(f"Error: Path not found: {args.path}")
270
+ return 1
271
+
272
+ print(f"Checking {len(files_to_check)} file(s)")
273
+ print(f"{'=' * 60}\n")
274
+
275
+ total_errors = 0
276
+ total_warnings = 0
277
+ files_with_issues = 0
278
+
279
+ for file_path in sorted(files_to_check):
280
+ errors = validate_file(file_path)
281
+
282
+ if errors:
283
+ files_with_issues += 1
284
+ print(f"\n{file_path.name}:")
285
+ print("-" * 40)
286
+
287
+ for error in errors:
288
+ if error.severity == 'error':
289
+ total_errors += 1
290
+ prefix = " ERROR"
291
+ elif error.severity == 'warning':
292
+ total_warnings += 1
293
+ prefix = " WARN "
294
+ else:
295
+ prefix = " INFO "
296
+
297
+ line_info = f" (line {error.line})" if error.line else ""
298
+ print(f"{prefix}: {error.message}{line_info}")
299
+ if error.context:
300
+ print(f" → {error.context}")
301
+
302
+ # Summary
303
+ print(f"\n{'=' * 60}")
304
+ print("SUMMARY")
305
+ print(f"{'=' * 60}")
306
+ print(f" Files checked: {len(files_to_check)}")
307
+ print(f" Files with issues: {files_with_issues}")
308
+ print(f" Errors: {total_errors}")
309
+ print(f" Warnings: {total_warnings}")
310
+ print(f"{'=' * 60}\n")
311
+
312
+ if total_errors == 0 and total_warnings == 0:
313
+ print("All migrations follow conventions.")
314
+ return 0
315
+ elif args.strict and total_errors > 0:
316
+ print("Strict mode: Exiting with error due to validation issues.")
317
+ return 1
318
+ else:
319
+ return 0
320
+
321
+
322
+ if __name__ == '__main__':
323
+ sys.exit(main())