create-win-project 1.3.0 → 2.0.0

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 (289) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +58 -160
  3. package/checks/check-compatibility.js +95 -0
  4. package/{scripts/verify-generated.mjs → checks/check-generated-project.js} +28 -4
  5. package/{scripts/validate-content.mjs → checks/check-library.js} +17 -7
  6. package/checks/check-package.js +15 -0
  7. package/checks/classify-changes.js +29 -0
  8. package/checks/run-compatibility-shard.js +21 -0
  9. package/docs/README.md +20 -0
  10. package/docs/capabilities.md +13 -0
  11. package/docs/compatibility.md +13 -0
  12. package/docs/generated-project.md +12 -0
  13. package/docs/getting-started.md +26 -0
  14. package/docs/maintainers/architecture.md +161 -0
  15. package/docs/maintainers/ci-strategy.md +46 -0
  16. package/docs/{CONTENT_MODEL.md → maintainers/content-model.md} +3 -3
  17. package/docs/maintainers/contributing.md +154 -0
  18. package/docs/{DEPENDENCY_MAINTENANCE.md → maintainers/dependencies.md} +1 -1
  19. package/docs/migration-v2.md +13 -0
  20. package/docs/production-contract.md +24 -0
  21. package/index.js +2 -429
  22. package/{playbooks → library}/INDEX.md +5 -5
  23. package/library/compatibility-impact.json +14 -0
  24. package/{playbooks/capabilities/ci/github-actions.manifest.json → library/development-tools/ci/definition.json} +1 -0
  25. package/library/development-tools/devops/makefile/commands.md +11 -0
  26. package/library/development-tools/devops/makefile/definition.json +11 -0
  27. package/library/development-tools/devops/makefile/docker.md +9 -0
  28. package/library/development-tools/devops/makefile/validation.md +5 -0
  29. package/{playbooks/devops/pr-template.manifest.json → library/development-tools/devops/pr-template/definition.json} +1 -0
  30. package/library/features/sqlalchemy-alembic.md +17 -0
  31. package/library/optional-features/concerns/zod/errors.md +5 -0
  32. package/library/optional-features/concerns/zod/testing.md +5 -0
  33. package/library/optional-features/concerns/zod/validation.md +21 -0
  34. package/library/optional-features/styling/css-modules/definition.json +12 -0
  35. package/library/optional-features/styling/css-modules/responsive.md +5 -0
  36. package/library/optional-features/styling/css-modules/theme.md +7 -0
  37. package/{playbooks/platform/mobile.manifest.json → library/platforms/mobile/definition.json} +1 -0
  38. package/{playbooks/platform/web.manifest.json → library/platforms/web/definition.json} +1 -0
  39. package/{playbooks/stack/react-native.manifest.json → library/stacks/expo/definition.json} +2 -2
  40. package/library/stacks/fastapi/architecture.md +40 -0
  41. package/library/stacks/fastapi/definition.json +39 -0
  42. package/library/stacks/fastapi/runtime.md +25 -0
  43. package/library/stacks/fastapi/security.md +26 -0
  44. package/library/stacks/fastapi/structure.md +27 -0
  45. package/library/stacks/fastapi/testing.md +23 -0
  46. package/{playbooks/stack/nextjs.manifest.json → library/stacks/nextjs/definition.json} +2 -2
  47. package/{playbooks/stack/no-frontend.manifest.json → library/stacks/no-frontend/definition.json} +1 -1
  48. package/{playbooks/stack/react-vite.manifest.json → library/stacks/react-vite/definition.json} +2 -2
  49. package/{compatibility/profiles.json → library/tested-versions.json} +46 -2
  50. package/{playbooks/universal/accessibility.manifest.json → library/universal/accessibility/definition.json} +1 -1
  51. package/library/universal/coding-rules/definition.json +13 -0
  52. package/library/universal/coding-rules/hygiene.md +9 -0
  53. package/library/universal/coding-rules/naming.md +17 -0
  54. package/{playbooks/universal/error-handling.manifest.json → library/universal/error-handling/definition.json} +1 -0
  55. package/library/universal/git-conventions/branches.md +5 -0
  56. package/library/universal/git-conventions/commits.md +7 -0
  57. package/library/universal/git-conventions/definition.json +13 -0
  58. package/library/universal/git-conventions/workflow.md +5 -0
  59. package/{playbooks/universal/observability.manifest.json → library/universal/observability/definition.json} +1 -0
  60. package/{playbooks/universal/security.manifest.json → library/universal/security/definition.json} +1 -0
  61. package/library/universal/typescript/boundaries.md +13 -0
  62. package/library/universal/typescript/definition.json +12 -0
  63. package/library/universal/typescript/errors.md +5 -0
  64. package/library/universal/typescript/patterns.md +7 -0
  65. package/package.json +14 -15
  66. package/public/logo.svg +19 -0
  67. package/src/cli/arguments.js +63 -0
  68. package/src/cli/display.js +220 -0
  69. package/src/cli/main.js +203 -0
  70. package/src/cli/navigation.js +156 -0
  71. package/src/cli/questions.js +188 -0
  72. package/src/cli/system-check.js +158 -0
  73. package/src/engine/create-project.js +10 -0
  74. package/src/engine/install-dependencies.js +13 -0
  75. package/{lib/catalog.js → src/engine/load-library.js} +8 -8
  76. package/{lib/files.js → src/engine/project-files.js} +10 -1
  77. package/{lib/playbooks.js → src/engine/project-guidance.js} +26 -3
  78. package/{lib/application-shapes.js → src/engine/project-shapes.js} +4 -4
  79. package/{lib/template.js → src/engine/render-templates.js} +11 -0
  80. package/{lib/compatibility.js → src/engine/tested-versions.js} +21 -2
  81. package/src/engine/upgrade-report.js +20 -0
  82. package/src/engine/write-files.js +34 -0
  83. package/{lib/stacks/registry.js → src/stacks/available-stacks.js} +25 -1
  84. package/src/stacks/backends/README.md +5 -0
  85. package/src/stacks/backends/fastapi/ci.js +3 -0
  86. package/src/stacks/backends/fastapi/create-files.js +874 -0
  87. package/src/stacks/backends/fastapi/docker.js +75 -0
  88. package/src/stacks/backends/fastapi/environment.js +3 -0
  89. package/src/stacks/backends/fastapi/index.js +32 -0
  90. package/{lib/stacks → src/stacks/backends}/laravel/architecture.js +1 -0
  91. package/{lib/stacks → src/stacks/backends}/laravel/auth/oidc.js +1 -0
  92. package/{lib/stacks → src/stacks/backends}/laravel/auth/public.js +1 -0
  93. package/{lib/stacks → src/stacks/backends}/laravel/auth/sanctum.js +1 -0
  94. package/{lib/stacks → src/stacks/backends}/laravel/auth/session.js +1 -0
  95. package/src/stacks/backends/laravel/ci.js +3 -0
  96. package/{lib/stacks → src/stacks/backends}/laravel/composer.js +1 -1
  97. package/{lib/stacks/laravel/generate.js → src/stacks/backends/laravel/create-files.js} +1 -0
  98. package/src/stacks/backends/laravel/docker.js +14 -0
  99. package/src/stacks/backends/laravel/environment.js +5 -0
  100. package/{lib/stacks → src/stacks/backends}/laravel/index.js +11 -3
  101. package/{lib/stacks → src/stacks/backends}/laravel/ui/blade.js +1 -0
  102. package/{lib/stacks → src/stacks/backends}/laravel/ui/index.js +1 -0
  103. package/{lib/stacks → src/stacks/backends}/laravel/ui/inertia-react.js +4 -2
  104. package/{lib/stacks → src/stacks/backends}/laravel/ui/livewire.js +1 -0
  105. package/{lib/stacks → src/stacks/backends}/laravel/ui/shared.js +1 -0
  106. package/src/stacks/backends/none/index.js +23 -0
  107. package/src/stacks/backends/postgres/ci.js +3 -0
  108. package/src/stacks/backends/postgres/create-files.js +21 -0
  109. package/src/stacks/backends/postgres/docker.js +3 -0
  110. package/src/stacks/backends/postgres/environment.js +3 -0
  111. package/src/stacks/backends/postgres/index.js +30 -0
  112. package/src/stacks/backends/springboot/ci.js +3 -0
  113. package/src/stacks/backends/springboot/create-files.js +91 -0
  114. package/src/stacks/backends/springboot/docker.js +3 -0
  115. package/src/stacks/backends/springboot/environment.js +3 -0
  116. package/src/stacks/backends/springboot/index.js +31 -0
  117. package/src/stacks/backends/supabase/ci.js +3 -0
  118. package/src/stacks/backends/supabase/create-files.js +52 -0
  119. package/src/stacks/backends/supabase/docker.js +3 -0
  120. package/src/stacks/backends/supabase/environment.js +3 -0
  121. package/src/stacks/backends/supabase/index.js +31 -0
  122. package/src/stacks/backends/supabase/native.js +14 -0
  123. package/src/stacks/compose-files.js +259 -0
  124. package/{lib/generator.js → src/stacks/create-project.js} +206 -57
  125. package/src/stacks/frontends/README.md +4 -0
  126. package/src/stacks/frontends/nextjs/ci.js +3 -0
  127. package/src/stacks/frontends/nextjs/create-files.js +46 -0
  128. package/src/stacks/frontends/nextjs/dependencies.js +5 -0
  129. package/src/stacks/frontends/nextjs/docker.js +3 -0
  130. package/src/stacks/frontends/nextjs/environment.js +7 -0
  131. package/src/stacks/frontends/nextjs/index.js +35 -0
  132. package/src/stacks/frontends/react-native/ci.js +3 -0
  133. package/src/stacks/frontends/react-native/create-files.js +28 -0
  134. package/src/stacks/frontends/react-native/dependencies.js +5 -0
  135. package/src/stacks/frontends/react-native/docker.js +5 -0
  136. package/src/stacks/frontends/react-native/environment.js +7 -0
  137. package/src/stacks/frontends/react-native/index.js +30 -0
  138. package/src/stacks/frontends/react-vite/ci.js +3 -0
  139. package/src/stacks/frontends/react-vite/create-files.js +27 -0
  140. package/src/stacks/frontends/react-vite/dependencies.js +5 -0
  141. package/src/stacks/frontends/react-vite/docker.js +3 -0
  142. package/src/stacks/frontends/react-vite/environment.js +8 -0
  143. package/src/stacks/frontends/react-vite/index.js +32 -0
  144. package/src/stacks/shared/capability-packs.js +31 -0
  145. package/{lib → src}/stacks/shared/contributions.js +1 -1
  146. package/src/stacks/shared/environment.js +38 -0
  147. package/src/stacks/shared/javascript-package.js +117 -0
  148. package/src/stacks/shared/testing-files.js +16 -0
  149. package/templates/ci/fastapi.yml +62 -0
  150. package/templates/docker/compose/postgres.yml +1 -1
  151. package/templates/docker/compose/springboot.yml +3 -3
  152. package/templates/docker/compose/supabase.yml +1 -1
  153. package/templates/docker/compose-prod/fastapi.yml +52 -0
  154. package/templates/docker/compose-prod/springboot.yml +20 -2
  155. package/templates/docker/dockerfile/fastapi.dev.dockerfile +9 -0
  156. package/templates/docker/dockerfile/fastapi.prod.dockerfile +11 -0
  157. package/templates/docker/dockerfile/nextjs.prod.dockerfile +1 -0
  158. package/templates/docker/dockerfile/springboot.prod.dockerfile +4 -1
  159. package/templates/docker/dockerfile/vite.prod.dockerfile +2 -1
  160. package/templates/makefile/fastapi.mk +97 -0
  161. package/docs/ARCHITECTURE.md +0 -118
  162. package/lib/banner.js +0 -45
  163. package/lib/constants.js +0 -3
  164. package/lib/doctor.js +0 -45
  165. package/lib/interview.js +0 -77
  166. package/lib/laravel-scaffold.js +0 -3
  167. package/lib/scaffold.js +0 -467
  168. package/lib/stacks/index.js +0 -8
  169. package/playbooks/concerns/zod.md +0 -174
  170. package/playbooks/devops/makefile.manifest.json +0 -10
  171. package/playbooks/devops/makefile.md +0 -556
  172. package/playbooks/styling/css-modules-extensions.md +0 -267
  173. package/playbooks/styling/css-modules.manifest.json +0 -11
  174. package/playbooks/universal/coding-rules.manifest.json +0 -12
  175. package/playbooks/universal/coding-rules.md +0 -281
  176. package/playbooks/universal/git-conventions.manifest.json +0 -10
  177. package/playbooks/universal/git-conventions.md +0 -186
  178. package/playbooks/universal/typescript.manifest.json +0 -11
  179. package/playbooks/universal/typescript.md +0 -272
  180. package/scripts/compatibility-matrix.mjs +0 -48
  181. /package/{playbooks/capabilities → library/development-tools}/ci/github-actions.md +0 -0
  182. /package/{playbooks/devops → library/development-tools/devops/pr-template}/pr-template.md +0 -0
  183. /package/{playbooks/capabilities/docker/docker.manifest.json → library/development-tools/docker/definition.json} +0 -0
  184. /package/{playbooks/capabilities → library/development-tools}/docker/overview.md +0 -0
  185. /package/{playbooks/capabilities → library/features}/auth/oidc-resource-server.md +0 -0
  186. /package/{playbooks/capabilities → library/features}/auth/spring-session.md +0 -0
  187. /package/{playbooks/capabilities/flyway/flyway.manifest.json → library/features/flyway/definition.json} +0 -0
  188. /package/{playbooks/capabilities → library/features}/flyway/environments.md +0 -0
  189. /package/{playbooks/capabilities → library/features}/flyway/migrations.md +0 -0
  190. /package/{playbooks/capabilities → library/features}/flyway/testing.md +0 -0
  191. /package/{playbooks/capabilities → library/features}/laravel/database.md +0 -0
  192. /package/{playbooks/capabilities → library/features}/laravel/migrations.md +0 -0
  193. /package/{playbooks/capabilities → library/features}/laravel/observability.md +0 -0
  194. /package/{playbooks/capabilities → library/features}/laravel/oidc-resource-server.md +0 -0
  195. /package/{playbooks/capabilities → library/features}/laravel/queues.md +0 -0
  196. /package/{playbooks/capabilities → library/features}/laravel/sanctum-spa.md +0 -0
  197. /package/{playbooks/capabilities → library/features}/laravel/scheduler.md +0 -0
  198. /package/{playbooks/capabilities → library/features}/laravel/session-auth.md +0 -0
  199. /package/{playbooks/capabilities → library/features}/laravel/storage-uploads.md +0 -0
  200. /package/{playbooks/capabilities → library/features}/postgresql/architecture.md +0 -0
  201. /package/{playbooks/capabilities/postgresql/postgresql.manifest.json → library/features/postgresql/definition.json} +0 -0
  202. /package/{playbooks/capabilities → library/features}/postgresql/migrations.md +0 -0
  203. /package/{playbooks/capabilities → library/features}/postgresql/schema-design.md +0 -0
  204. /package/{playbooks/capabilities → library/features}/postgresql/security.md +0 -0
  205. /package/{playbooks/capabilities → library/features}/postgresql/testing.md +0 -0
  206. /package/{playbooks/capabilities → library/features}/prisma/architecture.md +0 -0
  207. /package/{playbooks/capabilities/prisma/prisma.manifest.json → library/features/prisma/definition.json} +0 -0
  208. /package/{playbooks/capabilities → library/features}/prisma/migrations.md +0 -0
  209. /package/{playbooks/capabilities → library/features}/prisma/runtime.md +0 -0
  210. /package/{playbooks/capabilities → library/features}/prisma/schema.md +0 -0
  211. /package/{playbooks/capabilities → library/features}/prisma/testing.md +0 -0
  212. /package/{playbooks/capabilities → library/features}/supabase/architecture.md +0 -0
  213. /package/{playbooks/capabilities → library/features}/supabase/authentication.md +0 -0
  214. /package/{playbooks/capabilities/supabase/supabase.manifest.json → library/features/supabase/definition.json} +0 -0
  215. /package/{playbooks/capabilities → library/features}/supabase/expo.md +0 -0
  216. /package/{playbooks/capabilities → library/features}/supabase/migrations.md +0 -0
  217. /package/{playbooks/capabilities → library/features}/supabase/nextjs.md +0 -0
  218. /package/{playbooks/capabilities → library/features}/supabase/rls.md +0 -0
  219. /package/{playbooks/capabilities → library/features}/supabase/testing.md +0 -0
  220. /package/{playbooks/capabilities → library/features}/supabase/vite.md +0 -0
  221. /package/{playbooks → library/optional-features}/concerns/axios.md +0 -0
  222. /package/{playbooks → library/optional-features}/concerns/next-safe-action.md +0 -0
  223. /package/{playbooks → library/optional-features}/concerns/next-themes.md +0 -0
  224. /package/{playbooks → library/optional-features}/concerns/nuqs.md +0 -0
  225. /package/{playbooks → library/optional-features}/concerns/t3-env.md +0 -0
  226. /package/{playbooks → library/optional-features}/concerns/tanstack-query.md +0 -0
  227. /package/{playbooks → library/optional-features}/concerns/zustand.md +0 -0
  228. /package/{playbooks/styling/native-styles.manifest.json → library/optional-features/styling/native-styles/definition.json} +0 -0
  229. /package/{playbooks/styling → library/optional-features/styling/native-styles}/native-styles.md +0 -0
  230. /package/{playbooks/styling/tailwind.manifest.json → library/optional-features/styling/tailwind/definition.json} +0 -0
  231. /package/{playbooks/styling → library/optional-features/styling/tailwind}/tailwind-extensions.md +0 -0
  232. /package/{playbooks/platform → library/platforms}/laravel-ui/blade/architecture.md +0 -0
  233. /package/{playbooks/platform → library/platforms}/laravel-ui/blade/runtime.md +0 -0
  234. /package/{playbooks/platform → library/platforms}/laravel-ui/blade/security.md +0 -0
  235. /package/{playbooks/platform → library/platforms}/laravel-ui/blade/structure.md +0 -0
  236. /package/{playbooks/platform → library/platforms}/laravel-ui/blade/testing.md +0 -0
  237. /package/{playbooks/stack/laravel-ui.manifest.json → library/platforms/laravel-ui/definition.json} +0 -0
  238. /package/{playbooks/platform → library/platforms}/laravel-ui/inertia-react/architecture.md +0 -0
  239. /package/{playbooks/platform → library/platforms}/laravel-ui/inertia-react/runtime.md +0 -0
  240. /package/{playbooks/platform → library/platforms}/laravel-ui/inertia-react/security.md +0 -0
  241. /package/{playbooks/platform → library/platforms}/laravel-ui/inertia-react/structure.md +0 -0
  242. /package/{playbooks/platform → library/platforms}/laravel-ui/inertia-react/testing.md +0 -0
  243. /package/{playbooks/platform → library/platforms}/laravel-ui/livewire/architecture.md +0 -0
  244. /package/{playbooks/platform → library/platforms}/laravel-ui/livewire/runtime.md +0 -0
  245. /package/{playbooks/platform → library/platforms}/laravel-ui/livewire/security.md +0 -0
  246. /package/{playbooks/platform → library/platforms}/laravel-ui/livewire/structure.md +0 -0
  247. /package/{playbooks/platform → library/platforms}/laravel-ui/livewire/testing.md +0 -0
  248. /package/{playbooks/platform → library/platforms/mobile}/mobile.md +0 -0
  249. /package/{playbooks/platform → library/platforms/web}/web.md +0 -0
  250. /package/{playbooks/stack → library/stacks}/expo/architecture.md +0 -0
  251. /package/{playbooks/stack → library/stacks}/expo/runtime.md +0 -0
  252. /package/{playbooks/stack → library/stacks}/expo/security.md +0 -0
  253. /package/{playbooks/stack → library/stacks}/expo/structure.md +0 -0
  254. /package/{playbooks/stack → library/stacks}/expo/testing.md +0 -0
  255. /package/{playbooks/stack → library/stacks}/laravel/architecture.md +0 -0
  256. /package/{playbooks/stack/laravel.manifest.json → library/stacks/laravel/definition.json} +0 -0
  257. /package/{playbooks/stack → library/stacks}/laravel/runtime.md +0 -0
  258. /package/{playbooks/stack → library/stacks}/laravel/security.md +0 -0
  259. /package/{playbooks/stack → library/stacks}/laravel/structure.md +0 -0
  260. /package/{playbooks/stack → library/stacks}/laravel/testing.md +0 -0
  261. /package/{playbooks/stack → library/stacks}/nextjs/architecture.md +0 -0
  262. /package/{playbooks/stack → library/stacks}/nextjs/runtime.md +0 -0
  263. /package/{playbooks/stack → library/stacks}/nextjs/security.md +0 -0
  264. /package/{playbooks/stack → library/stacks}/nextjs/structure.md +0 -0
  265. /package/{playbooks/stack → library/stacks}/nextjs/testing.md +0 -0
  266. /package/{playbooks/stack/none.manifest.json → library/stacks/none/definition.json} +0 -0
  267. /package/{playbooks/stack → library/stacks}/react-vite/architecture.md +0 -0
  268. /package/{playbooks/stack → library/stacks}/react-vite/runtime.md +0 -0
  269. /package/{playbooks/stack → library/stacks}/react-vite/security.md +0 -0
  270. /package/{playbooks/stack → library/stacks}/react-vite/structure.md +0 -0
  271. /package/{playbooks/stack → library/stacks}/react-vite/testing.md +0 -0
  272. /package/{playbooks/stack → library/stacks}/springboot/architecture.md +0 -0
  273. /package/{playbooks/stack/springboot.manifest.json → library/stacks/springboot/definition.json} +0 -0
  274. /package/{playbooks/stack → library/stacks}/springboot/runtime.md +0 -0
  275. /package/{playbooks/stack → library/stacks}/springboot/security.md +0 -0
  276. /package/{playbooks/stack → library/stacks}/springboot/structure.md +0 -0
  277. /package/{playbooks/stack → library/stacks}/springboot/testing.md +0 -0
  278. /package/{playbooks/universal → library/universal/accessibility}/accessibility.md +0 -0
  279. /package/{playbooks/universal → library/universal/error-handling}/error-handling.md +0 -0
  280. /package/{playbooks/universal → library/universal/observability}/observability.md +0 -0
  281. /package/{playbooks/universal → library/universal/security}/security.md +0 -0
  282. /package/{lib → src/engine}/project-location.js +0 -0
  283. /package/{lib → src}/stacks/context.js +0 -0
  284. /package/{lib/stacks/contract.js → src/stacks/rules.js} +0 -0
  285. /package/{ci → templates/ci}/expo.yml +0 -0
  286. /package/{ci → templates/ci}/laravel.yml +0 -0
  287. /package/{ci → templates/ci}/nextjs.yml +0 -0
  288. /package/{ci → templates/ci}/springboot.yml +0 -0
  289. /package/{ci → templates/ci}/vite.yml +0 -0
package/index.js CHANGED
@@ -1,431 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import inquirer from 'inquirer'
3
- import chalk from 'chalk'
4
- import ora from 'ora'
5
- import path from 'path'
6
- import { fileURLToPath } from 'url'
7
- import { spawnSync } from 'node:child_process'
8
- import { generateProject } from './lib/generator.js'
9
- import { loadCompatibility } from './lib/compatibility.js'
10
- import { printDoctor } from './lib/doctor.js'
11
- import { configurationDecisionChoices, promptWithBack } from './lib/interview.js'
12
- import { projectLocationNotice } from './lib/project-location.js'
13
- import { w1nBanner } from './lib/banner.js'
14
- import {
15
- loadCatalog, resolveStack,
16
- stylingChoicesFor, architectureChoicesFor,
17
- } from './lib/catalog.js'
18
- import {
19
- APPLICATION_SHAPES,
20
- applicationShapeChoices,
21
- backendChoicesForShape,
22
- frontendChoicesForShape,
23
- } from './lib/application-shapes.js'
24
- import { laravelUiPromptContribution, laravelUis } from './lib/stacks/laravel/ui/index.js'
2
+ import { runCli } from './src/cli/main.js'
25
3
 
26
- const __dirname = path.dirname(fileURLToPath(import.meta.url))
27
- const cliArgs = process.argv.slice(2)
28
- const profileArg = cliArgs.find((arg) => arg.startsWith('--profile='))?.split('=')[1]
29
- const shapeArg = cliArgs.find((arg) => arg.startsWith('--shape='))?.split('=')[1]
30
- const frontendValue = cliArgs.find((arg) => arg.startsWith('--frontend='))?.split('=')[1]
31
- const backendArg = cliArgs.find((arg) => arg.startsWith('--backend='))?.split('=')[1]
32
- const frontendAliases = { vite: 'react', expo: 'react-native', none: 'no-frontend' }
33
- const frontendArg = frontendAliases[frontendValue] || frontendValue
34
- const architectureArg = cliArgs.find((arg) => arg.startsWith('--architecture='))?.split('=')[1]
35
- const authenticationArg = cliArgs.find((arg) => arg.startsWith('--authentication='))?.split('=')[1]
36
- const authAudienceArg = cliArgs.find((arg) => arg.startsWith('--auth-audience='))?.split('=')[1]
37
- const laravelUiArg = cliArgs.find((arg) => arg.startsWith('--laravel-ui='))?.split('=')[1]
38
- if (shapeArg && !APPLICATION_SHAPES[shapeArg]) {
39
- throw new Error('--shape must be fullstack, separate, api, mobile, or frontend')
40
- }
41
- if (architectureArg && !['small', 'medium', 'large'].includes(architectureArg)) {
42
- throw new Error('--architecture must be small, medium, or large')
43
- }
44
- if (authenticationArg && !['yes', 'not-yet', 'none'].includes(authenticationArg)) {
45
- throw new Error('--authentication must be yes, not-yet, or none')
46
- }
47
- if (authAudienceArg && !['website', 'multi-client'].includes(authAudienceArg)) {
48
- throw new Error('--auth-audience must be website or multi-client')
49
- }
50
- if (laravelUiArg && !laravelUis.some((ui) => ui.id === laravelUiArg)) {
51
- throw new Error('--laravel-ui must be blade, livewire, or inertia-react')
52
- }
53
- const wantsInstall = cliArgs.includes('--install')
54
- const skipsInstall = cliArgs.includes('--no-install')
55
- if (wantsInstall && skipsInstall) throw new Error('Use either --install or --no-install, not both')
56
- const { profile } = await loadCompatibility(
57
- path.join(__dirname, 'compatibility/profiles.json'),
58
- profileArg,
59
- )
60
- if (cliArgs[0] === 'doctor' || cliArgs.includes('--doctor')) {
61
- printDoctor(profile)
62
- process.exit(0)
63
- }
64
- const catalog = await loadCatalog(path.join(__dirname, 'playbooks'), profile)
65
-
66
- // ─── Banner ──────────────────────────────────────────────────────────────────
67
-
68
- console.log('')
69
- console.log(w1nBanner())
70
- console.log('')
71
- console.log(chalk.gray(' Production-ready project scaffolding'))
72
- console.log(chalk.gray(` Compatibility profile: ${profile.id} (${profile.status})`))
73
- console.log('')
74
-
75
- // ─── Interview ───────────────────────────────────────────────────────────────
76
-
77
- const questions = [
78
- {
79
- type: 'list',
80
- name: 'applicationShape',
81
- message: 'What kind of application are you building?',
82
- choices: applicationShapeChoices(),
83
- default: 'fullstack',
84
- when: () => !shapeArg,
85
- },
86
- // ── Always-on ──────────────────────────────────────────────────────────
87
- {
88
- type: 'input',
89
- name: 'projectName',
90
- message: 'Project name?',
91
- default: 'my-project',
92
- validate: (v) => {
93
- if (!v.trim()) return 'Project name is required'
94
- if (!/^[a-z0-9-]+$/.test(v)) return 'Use lowercase letters, numbers, and hyphens only'
95
- return true
96
- },
97
- },
98
- {
99
- type: 'input',
100
- name: 'projectDescription',
101
- message: 'One-line description?',
102
- default: 'A new application',
103
- },
104
-
105
- // ── Stack selection (catalog-driven) ───────────────────────────────────
106
- {
107
- type: 'list',
108
- name: 'frontend',
109
- message: 'Which application framework or frontend?',
110
- choices: (a) => frontendChoicesForShape(shapeArg || a.applicationShape, catalog),
111
- when: () => !frontendArg,
112
- },
113
- {
114
- type: 'list',
115
- name: 'backend',
116
- message: 'Which backend or data service?',
117
- choices: (a) => backendChoicesForShape(shapeArg || a.applicationShape, a.frontend, catalog),
118
- when: () => !backendArg,
119
- },
120
- ...laravelUiPromptContribution(laravelUiArg).questions,
121
-
122
- // ── Styling: only shown when frontend has >1 option (catalog-driven) ───
123
- {
124
- type: 'list',
125
- name: 'styling',
126
- message: 'Styling approach?',
127
- choices: (a) => stylingChoicesFor(catalog, a.frontend),
128
- when: (a) => stylingChoicesFor(catalog, a.frontend).length > 1,
129
- },
130
-
131
- // ── One architecture profile, interpreted natively by every stack ──────
132
- {
133
- type: 'list',
134
- name: 'architecture',
135
- message: 'Architecture? (Medium is recommended for most long-term applications)',
136
- choices: (a) => {
137
- const supported = architectureChoicesFor(catalog, a.frontend, a.backend)
138
- return [
139
- { name: 'Medium (Recommended) — clear feature, service, and data boundaries', value: 'medium' },
140
- { name: 'Small — fewer layers for prototypes and simple applications', value: 'small' },
141
- { name: 'Large — enforced boundaries for complex domains and larger teams', value: 'large' },
142
- ].filter((choice) => supported.includes(choice.value))
143
- },
144
- default: 'medium',
145
- when: () => !architectureArg,
146
- },
147
-
148
- // ── Authentication intent, expressed without protocol jargon ───────────
149
- {
150
- type: 'list',
151
- name: 'authentication',
152
- message: 'Does your application need user login?',
153
- choices: (a) => {
154
- const choices = []
155
- if (a.backend === 'supabase' || a.backend === 'springboot' || a.backend === 'laravel') {
156
- choices.push({ name: 'Yes — generate authentication appropriate for this stack', value: 'yes' })
157
- }
158
- choices.push(
159
- { name: 'Not yet (Recommended) — add guidance without pretending login exists', value: 'not-yet' },
160
- { name: 'No — this application is intentionally public and has no user accounts', value: 'none' },
161
- )
162
- return choices
163
- },
164
- default: 'not-yet',
165
- when: () => !authenticationArg,
166
- },
167
- {
168
- type: 'list',
169
- name: 'authAudience',
170
- message: 'Where will users access the application?',
171
- choices: [
172
- { name: 'Website only — use a secure server-managed browser session', value: 'website' },
173
- { name: 'Website and mobile — use a trusted identity provider for every client', value: 'multi-client' },
174
- ],
175
- default: 'website',
176
- when: (a) => ['springboot', 'laravel'].includes(a.backend) && a.frontend !== 'laravel-ui' && (authenticationArg || a.authentication) === 'yes' && !authAudienceArg,
177
- },
178
-
179
- // ── Testing ────────────────────────────────────────────────────────────
180
- {
181
- type: 'list',
182
- name: 'testing',
183
- message: 'Testing setup?',
184
- choices: (a) => {
185
- const fe = catalog.byId[a.frontend]
186
- if (fe?.platform === 'mobile') {
187
- return [
188
- { name: 'Basic (Jest + React Native Testing Library)', value: 'basic' },
189
- { name: 'None', value: 'none' },
190
- ]
191
- }
192
- return [
193
- { name: 'Full (Vitest + React Testing Library + Playwright)', value: 'full' },
194
- { name: 'Basic (Vitest + React Testing Library)', value: 'basic' },
195
- { name: 'None', value: 'none' },
196
- ]
197
- },
198
- default: (a) => {
199
- const fe = catalog.byId[a.frontend]
200
- return fe?.platform === 'mobile' ? 'basic' : 'full'
201
- },
202
- },
203
-
204
- // ── DevOps extras ──────────────────────────────────────────────────────
205
- {
206
- type: 'confirm',
207
- name: 'docker',
208
- message: 'Include Docker?',
209
- default: true,
210
- when: (a) => {
211
- const fe = catalog.byId[a.frontend]
212
- const be = catalog.byId[a.backend]
213
- return (be?.needsDocker ?? fe?.needsDocker ?? false)
214
- },
215
- },
216
- {
217
- type: 'confirm',
218
- name: 'makefile',
219
- message: 'Include Makefile?',
220
- default: true,
221
- when: (a) => {
222
- // Makefile is useful for web stacks; less relevant for bare mobile
223
- const fe = catalog.byId[a.frontend]
224
- return fe?.platform !== 'mobile'
225
- },
226
- },
227
- {
228
- type: 'confirm',
229
- name: 'githubActions',
230
- message: 'Include GitHub Actions CI?',
231
- default: true,
232
- },
233
- {
234
- type: 'confirm',
235
- name: 'installDependencies',
236
- message: 'Install project dependencies and create the lockfile now?',
237
- default: true,
238
- when: () => !wantsInstall && !skipsInstall,
239
- },
240
-
241
- // ── Spring Boot specific ───────────────────────────────────────────────
242
- {
243
- type: 'input',
244
- name: 'packageName',
245
- message: 'Java package name? (e.g. com.yourname)',
246
- default: 'com.app',
247
- when: (a) => a.backend === 'springboot',
248
- validate: (v) => {
249
- if (!v.trim()) return 'Package name is required'
250
- if (!/^[a-z]+(\.[a-z]+)+$/.test(v)) return 'Use format: com.yourname'
251
- return true
252
- },
253
- },
254
-
255
- // ── Optional concerns (filtered to current stack) ──────────────────────
256
- {
257
- type: 'checkbox',
258
- name: 'expectedConcerns',
259
- message: 'Expected optional concerns? (advisory only — all stay available)',
260
- choices: (a) => {
261
- const stack = resolveStack({ ...a, styling: a.styling || catalog.byId[a.frontend]?.stylingOptions?.[0] }, catalog)
262
- const opts = new Set()
263
- for (const c of stack.concerns) if (!c.required) opts.add(c.id)
264
- return [...opts].map((id) => ({ name: id, value: id }))
265
- },
266
- },
267
- ]
268
- let answers = {
269
- applicationShape: shapeArg,
270
- frontend: frontendArg,
271
- backend: backendArg,
272
- }
273
- let stack
274
- while (true) {
275
- answers = await promptWithBack(inquirer, questions, answers)
276
- answers.compatibilityProfile = profile.id
277
- answers.applicationShape = shapeArg || answers.applicationShape
278
- answers.architecture = architectureArg || answers.architecture || 'medium'
279
- answers.authentication = authenticationArg || answers.authentication || 'not-yet'
280
- answers.authAudience = authAudienceArg || answers.authAudience || (catalog.byId[answers.frontend]?.platform === 'mobile' ? 'multi-client' : 'website')
281
- answers.laravelUi = laravelUiArg || answers.laravelUi || (answers.frontend === 'laravel-ui' ? 'blade' : undefined)
282
- if (wantsInstall) answers.installDependencies = true
283
- if (skipsInstall) answers.installDependencies = false
284
-
285
- // ── Auto-resolve docker for stacks that need it ───────────────────────────────
286
- if (resolveStack({ ...answers, styling: answers.styling || catalog.byId[answers.frontend]?.stylingOptions?.[0] || 'tailwind' }, catalog).needsDocker) {
287
- answers.docker = answers.docker ?? true
288
- }
289
-
290
- stack = resolveStack({ ...answers, styling: answers.styling || catalog.byId[answers.frontend]?.stylingOptions?.[0] || 'tailwind' }, catalog)
291
-
292
- // ─── Confirm ─────────────────────────────────────────────────────────────────
293
-
294
- console.log('')
295
- console.log(chalk.bold(' Summary'))
296
- console.log(chalk.gray(' ───────────────────────────'))
297
- console.log(` ${chalk.cyan('Name:')} ${answers.projectName}`)
298
- console.log(` ${chalk.cyan('Stack:')} ${stack.label}`)
299
- console.log(` ${chalk.cyan('Shape:')} ${APPLICATION_SHAPES[stack.applicationShape].label}`)
300
- console.log(` ${chalk.cyan('Platform:')} ${stack.platform}`)
301
- if (stack.styleId) {
302
- console.log(` ${chalk.cyan('Styling:')} ${catalog.byId[stack.styleId]?.label || stack.styleId}`)
303
- }
304
- if (answers.laravelUi) console.log(` ${chalk.cyan('Laravel UI:')} ${answers.laravelUi}`)
305
- console.log(` ${chalk.cyan('Architecture:')} ${stack.architecture[0].toUpperCase()}${stack.architecture.slice(1)}`)
306
- console.log(` ${chalk.cyan('Authentication:')} ${stack.authentication}`)
307
- console.log(` ${chalk.cyan('Testing:')} ${answers.testing}`)
308
- if (stack.platform !== 'mobile') {
309
- console.log(` ${chalk.cyan('Docker:')} ${answers.docker ? 'yes' : 'no'}`)
310
- console.log(` ${chalk.cyan('Makefile:')} ${answers.makefile ? 'yes' : 'no'}`)
311
- }
312
- console.log(` ${chalk.cyan('CI/CD:')} ${answers.githubActions ? 'yes' : 'no'}`)
313
- console.log(` ${chalk.cyan('Install deps:')} ${answers.installDependencies ? 'yes' : 'no'}`)
314
- if (answers.packageName) {
315
- console.log(` ${chalk.cyan('Package:')} ${answers.packageName}`)
316
- }
317
- if (stack.constraints.length) {
318
- console.log('')
319
- console.log(chalk.gray(' Key constraints:'))
320
- for (const rule of stack.constraints.slice(0, 3)) {
321
- console.log(chalk.gray(` • ${rule}`))
322
- }
323
- }
324
- console.log('')
325
-
326
- const { decision } = await inquirer.prompt([{
327
- type: 'list', name: 'decision', message: 'Ready?', choices: configurationDecisionChoices(),
328
- }])
329
-
330
- if (decision === 'back') {
331
- continue
332
- }
333
- if (decision === 'cancel') {
334
- console.log(chalk.yellow('\n Cancelled.\n'))
335
- process.exit(0)
336
- }
337
- break
338
- }
339
-
340
- // ─── Generate ────────────────────────────────────────────────────────────────
341
-
342
- console.log('')
343
- const spinner = ora('Scaffolding project...').start()
344
-
345
- try {
346
- await generateProject(answers, __dirname)
347
- spinner.succeed(chalk.green('Project created!'))
348
- const locationNotice = projectLocationNotice({ cwd: process.cwd(), cliRoot: __dirname, projectName: answers.projectName })
349
- if (locationNotice) {
350
- console.log('')
351
- console.log(chalk.yellow.bold(' Project location note'))
352
- console.log(chalk.yellow(` ${locationNotice.message}`))
353
- console.log(chalk.gray(` Created at: ${locationNotice.generatedPath}`))
354
- console.log(chalk.gray(` Suggested destination: ${locationNotice.suggestedPath}`))
355
- console.log(chalk.gray(` Linux/macOS: mv "${locationNotice.generatedPath}" "${locationNotice.suggestedPath}"`))
356
- console.log(chalk.gray(` Windows: cut the generated folder in File Explorer and paste it into your projects folder.`))
357
- }
358
- if (answers.installDependencies) {
359
- const projectRoot = path.join(process.cwd(), answers.projectName)
360
- const steps = []
361
- if (stack.backendKey === 'laravel') {
362
- const laravelRoot = ['laravel-ui', 'no-frontend'].includes(stack.frontendKey) ? projectRoot : path.join(projectRoot, 'backend')
363
- steps.push({ command: process.platform === 'win32' ? 'composer.bat' : 'composer', args: ['install'], cwd: laravelRoot, retry: `cd ${path.relative(process.cwd(), laravelRoot)} && composer install` })
364
- }
365
- const needsNpm = stack.frontendKey !== 'no-frontend' && (stack.frontendKey !== 'laravel-ui' || answers.laravelUi === 'inertia-react')
366
- if (needsNpm) {
367
- const npmRoot = stack.frontendKey === 'react' ? path.join(projectRoot, 'frontend') : projectRoot
368
- steps.push({ command: process.platform === 'win32' ? 'npm.cmd' : 'npm', args: ['install'], cwd: npmRoot, retry: `cd ${path.relative(process.cwd(), npmRoot)} && npm install` })
369
- }
370
- const installSpinner = ora('Installing exact dependencies and creating lockfiles...').start()
371
- const failed = steps.find((step) => spawnSync(step.command, step.args, { cwd: step.cwd, stdio: 'inherit', shell: false }).status !== 0)
372
- if (failed) {
373
- installSpinner.warn(chalk.yellow('Project created, but dependency installation did not finish.'))
374
- console.log(chalk.yellow(` Retry with: ${failed.retry}`))
375
- } else {
376
- installSpinner.succeed(chalk.green('Dependencies installed and lockfiles created.'))
377
- }
378
- }
379
- console.log('')
380
- console.log(chalk.bold(` Next steps:`))
381
- console.log(chalk.gray(` cd ${answers.projectName}`))
382
-
383
- if (stack.backendKey === 'laravel') {
384
- const laravelDir = ['laravel-ui', 'no-frontend'].includes(stack.frontendKey) ? '' : 'backend/'
385
- console.log(chalk.gray(` cp ${laravelDir}.env.example ${laravelDir}.env`))
386
- if (answers.makefile) {
387
- console.log(chalk.gray(` make setup # first run only`))
388
- console.log(chalk.gray(` make run # later runs; never rebuilds`))
389
- } else {
390
- console.log(chalk.gray(` docker compose build`))
391
- console.log(chalk.gray(` docker compose up -d`))
392
- }
393
- } else if (stack.frontendKey === 'react') {
394
- if (stack.backendKey === 'springboot') {
395
- console.log(chalk.gray(` cp .env.example .env # Docker/backend values`))
396
- }
397
- console.log(chalk.gray(` cd frontend`))
398
- console.log(chalk.gray(` cp .env.example .env`))
399
- if (!answers.installDependencies) console.log(chalk.gray(` npm install`))
400
- } else {
401
- console.log(chalk.gray(` cp .env.example ${stack.isMobile ? '.env' : '.env.local'}`))
402
- if (!answers.installDependencies) console.log(chalk.gray(` npm install`))
403
- }
404
-
405
- if (stack.backendKey === 'laravel') {
406
- // Laravel commands were printed above because its root differs by application shape.
407
- } else if (stack.isMobile) {
408
- console.log(chalk.gray(` npx expo start`))
409
- } else if (answers.makefile) {
410
- if (stack.frontendKey === 'react') {
411
- console.log(chalk.gray(` cd ..`))
412
- }
413
- console.log(chalk.gray(` make dev`))
414
- } else if (stack.backendKey === 'supabase') {
415
- console.log(chalk.gray(` npm run supabase:start`))
416
- console.log(chalk.gray(` npm run dev`))
417
- } else if (stack.frontendKey === 'react') {
418
- console.log(chalk.gray(` npm run dev`))
419
- } else {
420
- console.log(chalk.gray(` docker compose up -d db`))
421
- console.log(chalk.gray(` npm run dev`))
422
- }
423
-
424
- console.log('')
425
- console.log(chalk.cyan(` Read RULES.md before starting — it maps every playbook for this stack.`))
426
- console.log('')
427
- } catch (err) {
428
- spinner.fail(chalk.red('Failed to generate project'))
429
- console.error(err)
430
- process.exit(1)
431
- }
4
+ await runCli()
@@ -1,6 +1,6 @@
1
1
  # Playbook Index
2
2
 
3
- Playbooks are task-routed standards used by generated projects. Their co-located manifests decide when a playbook applies and which headings appear in generated `RULES.md`.
3
+ Guidance files are task-routed standards used by generated projects. Their co-located definitions decide when guidance applies and which headings appear in generated `RULES.md`.
4
4
 
5
5
  ## Content groups
6
6
 
@@ -17,7 +17,7 @@ Playbooks are task-routed standards used by generated projects. Their co-located
17
17
  ## Composition
18
18
 
19
19
  ```text
20
- required universal + selected platform manifests
20
+ required universal + selected platform definitions
21
21
  + selected frontend
22
22
  + selected backend/database/migration
23
23
  + selected styling
@@ -37,10 +37,10 @@ Expo uses React Native `StyleSheet` by default. Web projects can select Tailwind
37
37
 
38
38
  ## Manifest contract
39
39
 
40
- A stack manifest must declare all three architecture profiles. Any manifest may declare:
40
+ A stack definition must declare all three architecture profiles. Any definition may declare:
41
41
 
42
42
  - `id`, `kind`, `label`, and compatibility;
43
- - dependency names, scripts, templates, and conditional playbooks (versions live only in `compatibility/profiles.json`);
43
+ - dependency names, scripts, templates, and conditional playbooks (versions live only in `library/tested-versions.json`);
44
44
  - semantic environment names and client-visible names;
45
45
  - stack constraints;
46
46
  - required and optional concerns, target playbook, headings, and applicability.
@@ -55,4 +55,4 @@ Manifests compose capabilities; `lib/scaffold.js` owns their executable minimum.
55
55
  - `playbooks/`: only selected reusable guidance.
56
56
  - `docs/`: setup, API, architecture, auth, and deployment for the generated product.
57
57
 
58
- See `docs/CONTENT_MODEL.md` for authoring and deduplication rules and `docs/ARCHITECTURE.md` for the generation pipeline.
58
+ See `docs/maintainers/content-model.md` for authoring and deduplication rules and `docs/maintainers/architecture.md` for the generation pipeline.
@@ -0,0 +1,14 @@
1
+ {
2
+ "stacks": {
3
+ "nextjs": ["src/stacks/frontends/nextjs/", "library/stacks/nextjs/", "templates/agents/nextjs.", "templates/ci/nextjs."],
4
+ "react": ["src/stacks/frontends/react-vite/", "library/stacks/react-vite/", "templates/agents/react-vite.", "templates/ci/vite."],
5
+ "react-native": ["src/stacks/frontends/react-native/", "library/stacks/expo/", "templates/agents/react-native.", "templates/ci/expo."],
6
+ "springboot": ["src/stacks/backends/springboot/", "library/stacks/springboot/", "library/features/flyway/", "templates/ci/springboot."],
7
+ "fastapi": ["src/stacks/backends/fastapi/", "library/stacks/fastapi/", "library/features/sqlalchemy-alembic.md", "templates/ci/fastapi."],
8
+ "laravel": ["src/stacks/backends/laravel/", "library/stacks/laravel/", "library/features/laravel", "templates/ci/laravel."],
9
+ "supabase": ["src/stacks/backends/supabase/", "library/features/supabase/"],
10
+ "postgres": ["src/stacks/backends/postgres/", "library/features/postgresql/", "library/features/prisma/"],
11
+ "none": ["src/stacks/backends/none/"]
12
+ },
13
+ "shared": ["library/tested-versions.json", "lib/", "src/engine/", "src/stacks/shared/", "templates/docker/", "templates/gitignore/", "templates/makefile/", "checks/check-compatibility.js", "checks/check-generated-project.js"]
14
+ }
@@ -2,6 +2,7 @@
2
2
  "id": "github-actions",
3
3
  "kind": "devops",
4
4
  "label": "GitHub Actions CI",
5
+ "file": "capabilities/ci/github-actions.md",
5
6
  "required": false,
6
7
  "folders": [".github/workflows"],
7
8
  "concerns": [
@@ -0,0 +1,11 @@
1
+ # Make commands
2
+
3
+ ## Core Rules
4
+
5
+ - Targets wrap documented project commands; they do not hide different behavior.
6
+ - Declare non-file targets with `.PHONY`.
7
+ - Use tabs for recipes and `$(VARIABLE)` for configurable values.
8
+ - Keep the default target safe and read-only; `help` or `check` is preferred.
9
+ - Fail on command errors and preserve the underlying exit status.
10
+
11
+ Use small targets such as `install`, `dev`, `lint`, `test`, `build`, and `check`. Compose them instead of duplicating recipes.
@@ -0,0 +1,11 @@
1
+ {
2
+ "id": "makefile",
3
+ "kind": "devops",
4
+ "label": "Makefile",
5
+ "playbooks": ["capabilities/devops/makefile/commands.md", "capabilities/devops/makefile/docker.md", "capabilities/devops/makefile/validation.md"],
6
+ "required": false,
7
+ "folders": [],
8
+ "concerns": [
9
+ { "id": "makefile", "required": true, "playbook": "capabilities/devops/makefile/commands.md", "sections": ["Core Rules"] }
10
+ ]
11
+ }
@@ -0,0 +1,9 @@
1
+ # Make and Docker
2
+
3
+ ## Docker Targets
4
+
5
+ - Keep image building separate from container startup.
6
+ - `build` may run `docker compose build`; `run` must not rebuild implicitly.
7
+ - Use the generated Compose file as the source of service names and profiles.
8
+ - Never interpolate secrets into command output.
9
+ - Destructive volume or database targets require explicit names and confirmation.
@@ -0,0 +1,5 @@
1
+ # Make validation
2
+
3
+ ## Validation Targets
4
+
5
+ `check` should compose the same lint, typecheck, test, and build commands used by CI. Keep focused targets independently runnable, and avoid suppressing failures with leading `-`, `|| true`, or unconditional success messages.
@@ -2,6 +2,7 @@
2
2
  "id": "pr-template",
3
3
  "kind": "devops",
4
4
  "label": "PR Template",
5
+ "file": "devops/pr-template.md",
5
6
  "required": false,
6
7
  "folders": [],
7
8
  "concerns": [
@@ -0,0 +1,17 @@
1
+ # SQLAlchemy and Alembic
2
+
3
+ ## Session Ownership
4
+
5
+ Use SQLAlchemy 2 async sessions with `asyncpg`. Services own session and transaction
6
+ boundaries spanning multiple writes or read-modify-write behavior. Routers never manage
7
+ sessions directly; they receive a session dependency from shared core infrastructure.
8
+
9
+ Database unique/check/foreign-key constraints are the final concurrency boundary; map
10
+ expected constraint conflicts to stable application errors.
11
+
12
+ ## Migrations
13
+
14
+ Alembic owns all schema changes. Generate revisions explicitly and apply them with
15
+ `alembic upgrade head` from the backend directory. Application startup must not
16
+ automatically migrate production databases. Verify migration configuration and upgrade
17
+ checks in CI before container builds.
@@ -0,0 +1,5 @@
1
+ # Zod validation errors
2
+
3
+ ## Safe Validation Errors
4
+
5
+ Convert issues to stable field/code structures at the boundary. Do not expose internal paths, secrets, provider payloads, or raw exception text. Log diagnostic context server-side with request correlation and privacy filtering.
@@ -0,0 +1,5 @@
1
+ # Testing Zod schemas
2
+
3
+ ## Schema Tests
4
+
5
+ Test valid boundaries, missing required values, wrong types, limits, unknown keys, and security-sensitive malformed input. Prefer small representative fixtures and assert stable error codes rather than complete library messages.
@@ -0,0 +1,21 @@
1
+ # Runtime validation with Zod
2
+
3
+ ## Agent Quick Reference
4
+
5
+ Parse untrusted values once at their owning boundary, use the inferred type internally, and return safe structured errors.
6
+
7
+ ## Schema First, Always
8
+
9
+ Define the runtime contract before consuming external data. Use strict schemas for requests, environment variables, persisted data, and provider responses. Apply coercion only when the transport contract explicitly permits it.
10
+
11
+ ## Zod Schema Placement
12
+
13
+ Place a schema at the boundary that owns the contract. Feature schemas stay with the feature; shared protocol schemas live in a dependency-neutral contract module. Export inferred types rather than duplicating shapes.
14
+
15
+ ## Web Form (React Hook Form)
16
+
17
+ Use the schema resolver at submission boundaries, show field errors accessibly, and validate again on the server. Client validation improves feedback but never establishes trust.
18
+
19
+ ## React Native Form (React Hook Form + Controller)
20
+
21
+ Wrap controlled native inputs with `Controller`, preserve accessible labels and errors, and validate the same transport contract again at the trusted server boundary.
@@ -0,0 +1,12 @@
1
+ {
2
+ "id": "css-modules",
3
+ "kind": "styling",
4
+ "label": "CSS Modules",
5
+ "playbooks": ["styling/css-modules/theme.md", "styling/css-modules/responsive.md"],
6
+ "required": false,
7
+ "folders": [],
8
+ "concerns": [
9
+ { "id": "styling-theme", "required": true, "playbook": "styling/css-modules/theme.md", "sections": ["Dark Mode with CSS Variables"] },
10
+ { "id": "styling-responsive", "required": true, "playbook": "styling/css-modules/responsive.md", "sections": ["Responsive in Module File"] }
11
+ ]
12
+ }
@@ -0,0 +1,5 @@
1
+ # CSS Modules responsive layout
2
+
3
+ ## Responsive in Module File
4
+
5
+ Keep component breakpoints beside the component styles. Start with the narrow layout, add only measured breakpoints, and prefer container-aware layout when the component is reusable. Avoid global selectors and viewport assumptions inside modules.
@@ -0,0 +1,7 @@
1
+ # CSS Modules theming
2
+
3
+ ## Dark Mode with CSS Variables
4
+
5
+ Define semantic color and spacing variables at the application theme boundary. Switch variable values through the selected theme mechanism; components consume semantic tokens and must not duplicate light/dark selectors.
6
+
7
+ Respect system preference by default, preserve explicit user choice, and maintain accessible contrast in every supported theme.
@@ -2,6 +2,7 @@
2
2
  "id": "mobile-platform",
3
3
  "kind": "platform",
4
4
  "label": "Mobile Platform",
5
+ "file": "platform/mobile.md",
5
6
  "appliesTo": { "platform": ["mobile"] },
6
7
  "deps": [],
7
8
  "devDeps": [],
@@ -2,6 +2,7 @@
2
2
  "id": "web-platform",
3
3
  "kind": "platform",
4
4
  "label": "Web Platform",
5
+ "file": "platform/web.md",
5
6
  "appliesTo": { "platform": ["web"] },
6
7
  "deps": [],
7
8
  "devDeps": [],