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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Win
3
+ Copyright (c) 2026 itsw1n
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,187 +1,85 @@
1
- ```
2
- ██╗ ██╗ ██╗ ███╗ ██╗
3
- ██║ ██║ ███║ ████╗ ██║
4
- ██║ █╗ ██║ ╚██║ ██╔██╗██║
5
- ██║███╗██║ ██║ ██║╚████║
6
- ╚███╔███╔╝ ██║ ██║ ╚███║
7
- ╚══╝╚══╝ ╚═╝ ╚═╝ ╚══╝
8
-
9
- █▀█ █▀█ █▀█ █ █▀▀ █▀▀ ▀█▀
10
- █▀▀ █▀█ █ █ █ █▀ █ █
11
- █ █ █ █▄█ █▄█ █▄▄ █▄▄ █
12
- ```
13
-
14
- # Are you confused creating architecture rules, AGENTS.md, and folder structures from scratch every time you pick a new stack?
15
-
16
- > **Just answer what tech stack you want.** `create-win-project` turns your answers into a runnable, tested, agent-ready foundation — not an empty folder with a giant doc dump.
17
-
18
- Stop bikeshedding folders for 3 days. Stop pasting a 500-line `AGENTS.md` that burns tokens and still lets the agent hallucinate your stack. Your stack, your rules, already wired.
19
-
20
- [![CI](https://github.com/itsw1n/create-win-project/actions/workflows/ci.yml/badge.svg)](https://github.com/itsw1n/create-win-project/actions/workflows/ci.yml)
21
- [![Node](https://img.shields.io/badge/node-24_LTS-green)](https://nodejs.org)
22
- [![Manifest-driven](https://img.shields.io/badge/architecture-manifest--driven-blue)](./playbooks)
23
- [![Lean docs](https://img.shields.io/badge/AGENTS.md-lean%20%26%20lazy-9cf)](./playbooks)
24
- [![Interactive](https://img.shields.io/badge/cli-interactive-ff69b4)](./index.js)
25
-
26
- Next.js · React + Vite · Expo (React Native) · Spring Boot · Supabase · PostgreSQL · Tailwind · CSS Modules
27
-
28
- ---
1
+ <div align="center">
2
+ <img src="./public/logo.svg" alt="W1N Project logo" width="160">
3
+ <h1>W1N PROJECT</h1>
4
+ <p>Generate a tested, production-oriented web or mobile project with stack-specific code, CI, deployment artifacts, documentation, and agent guidance.</p>
5
+ </div>
29
6
 
30
- ## Why this exists
7
+ ## Start
31
8
 
32
- You were that dev who rebuilt the same foundation twice — Next.js App Router one week, Expo the next, Spring Boot after that. Same questions: where do components live? How does auth refresh? What goes in `AGENTS.md` without drowning the context window?
33
-
34
- **The fix:** answer a short, plain-language interview and get **two coordinated outputs**:
35
-
36
- 1. **A small runnable app** — real page/screen, strict TypeScript, ESLint, health endpoint, tests, and the exact config for your stack.
37
- 2. **A task-routed docs layer** — a tiny always-on `AGENTS.md` + a lazy `RULES.md` (`concern → playbook §`). The agent reads only what it touches, when it touches it.
38
-
39
- Manifests declare capabilities and package names. A tested compatibility profile owns every exact version, so generated stacks are declarative without letting version strings drift.
40
-
41
- ## Prerequisites — what you actually need to install
42
-
43
- ### Lane 1 — To run *this generator*
44
- **Node 24 LTS *OR* Docker — that's it.**
9
+ ```bash
10
+ npx create-win-project@latest
11
+ ```
45
12
 
46
- - No global `prettier`, `eslint`, or `typescript` they are generated inside your project (`package.json` devDeps + `.prettierrc` + `.editorconfig` + `eslint.config` via `lib/scaffold.js`).
47
- - Prefer zero host setup? Use Docker directly. Make is an optional convenience, never a prerequisite.
13
+ Answer product questions, choose whether dependencies should be installed, then follow the generated project README. Existing non-empty destinations are never overwritten.
14
+
15
+ ```text
16
+ my-project/
17
+ ├── application source and tests
18
+ ├── .github/workflows/ CI and security checks
19
+ ├── Dockerfile / eas.json production artifacts when applicable
20
+ ├── AGENTS.md small agent operating contract
21
+ ├── RULES.md task-to-playbook router
22
+ ├── CONTEXT.md product decisions and approved deviations
23
+ ├── playbooks/ selected stack guidance
24
+ └── create-win-project.profile.json
25
+ ```
48
26
 
49
- ### Lane 2 — To run *what it generates* (depends on your answers)
50
- | You picked | You need | What the generator includes |
51
- |---|---|---|
52
- | **Next.js** or **React + Vite** | Node 24 LTS → `npm install` → `npm run dev` | Page/entry, Vite/Next config, strict TS, ESLint, tests, `frontend/.env.example` |
53
- | **Expo** | Node 24 LTS + Expo Go app → `npm install` → `npx expo start` | Expo Router layout/screen, `app.json`, Jest, TS |
54
- | **Supabase** | Docker for the generated local Supabase stack | Pinned local CLI, migrations/RLS tests, platform-native clients; login/callback/secure lifecycle only when login is selected |
55
- | **Spring Boot / PostgreSQL** | JDK 21 + Docker for DB **only if you selected them** | Maven app, public health + fail-closed security, Flyway/PostgreSQL; server session or OIDC Resource Server when login is selected |
27
+ ## Supported stacks
56
28
 
57
- **Hybrid:** the exact commands for *your* stack live in the README inside `./your-project` and `docs/guides/setup.md` — no duplication here. If you didn't enable Docker/Make, no compose file is generated.
29
+ | Application | Backends and data |
30
+ |---|---|
31
+ | Next.js | None, Supabase, PostgreSQL, Spring Boot, Laravel, FastAPI |
32
+ | React + Vite | None, Supabase, Spring Boot, Laravel, FastAPI |
33
+ | Expo / React Native | None, Supabase, Spring Boot, Laravel, FastAPI |
34
+ | Laravel UI | Blade, Livewire, or Inertia React with Laravel |
35
+ | API only | Spring Boot, Laravel, or FastAPI |
58
36
 
59
- ## Quick start
37
+ Authentication follows the chosen stack and audience: Supabase Auth, server sessions, Sanctum SPA, or OIDC validation where supported.
60
38
 
61
- **Fastest (no clone required):**
39
+ ## Production-oriented by default
62
40
 
63
- ```bash
64
- npx create-win-project@latest
65
- ```
41
+ Version 2 generates stack-appropriate tests, CI and security checks, production builds, environment guidance, operations documentation, and cloud-neutral deployment artifacts. Web tests include Playwright; Expo uses Jest and React Native Testing Library.
66
42
 
67
- **From a clone with Node:**
43
+ Optional complexity remains requirement-driven:
68
44
 
69
- ```bash
70
- npm ci
71
- npm run doctor
72
- npm start
73
- ```
45
+ - Private object-storage uploads appear only when uploads are required.
46
+ - Durable queue conventions appear only when background jobs are required.
47
+ - Offline cache or synchronization appears only for mobile when selected.
48
+ - Development Docker and Make remain optional.
74
49
 
75
- **Docker-first (no host Node or Make needed):**
50
+ The baseline is not a substitute for product authorization, infrastructure sizing, compliance, secrets, monitoring, deployment approval, backups, or restore drills. See the [production contract](./docs/production-contract.md).
76
51
 
77
- ```bash
78
- git clone https://github.com/itsw1n/create-win-project && cd create-win-project
79
- docker compose build
80
- docker compose run --rm app
81
- ```
52
+ ## Agent-flexible defaults
82
53
 
83
- `docker compose run` creates a disposable CLI container and reuses the existing image. It does not rebuild an existing image unless you explicitly build again. On systems with Make, `make build` and `make generate` are shortcuts; `make run` remains an alias for `make generate`.
54
+ Agents may propose different architecture, providers, authentication, data boundaries, or major dependencies, but must obtain approval and record the decision in `CONTEXT.md` before changing them.
84
55
 
85
- <details>
86
- <summary>Prefer host Node?</summary>
56
+ Exact tested direct versions come from dated profiles. Package managers resolve transitive dependencies and create project-owned lockfiles. Existing projects can run a read-only comparison:
87
57
 
88
58
  ```bash
89
- npx create-win-project --install # install generated dependencies now
90
- npx create-win-project --no-install # generate files only
91
- npx create-win-project doctor # diagnose available tools
92
- # then follow the same interview
59
+ create-win-project upgrade-report .
93
60
  ```
94
61
 
95
- Requires Node 24 LTS. Still dockerizes the *generated* app only if you enabled it.
62
+ ## Documentation
96
63
 
97
- </details>
64
+ - [Documentation map](./docs/README.md)
65
+ - [Getting started](./docs/getting-started.md)
66
+ - [Production contract](./docs/production-contract.md)
67
+ - [Stacks and capabilities](./docs/capabilities.md)
68
+ - [Understanding a generated project](./docs/generated-project.md)
69
+ - [Compatibility and upgrades](./docs/compatibility.md)
70
+ - [Migrating from version 1](./docs/migration-v2.md)
98
71
 
99
- **Then run what you generated:**
72
+ ## Development
100
73
 
101
74
  ```bash
102
- # Next.js or Expo
103
- cd your-project
104
- npm install
105
- npm run dev
106
-
107
- # React + Vite (frontend workspace)
108
- cd your-project/frontend
109
- npm install
110
- npm run dev
111
- ```
112
-
113
- The generator asks whether to install dependencies. One local `npm install` provides Prettier, ESLint, TypeScript, and the selected test tools; global installs are neither required nor silently performed.
114
-
115
- > The generator **never overwrites a non-empty folder** — it stages to a temp dir and moves into place only on success.
116
-
117
- ## Features
118
-
119
- - **Three frontend families** — Next.js App Router, React + Vite, and Expo Router.
120
- - **Optional backends** — every frontend can choose no backend, Supabase, PostgreSQL where supported, or Spring Boot.
121
- - **Styling** — Tailwind CSS or CSS Modules (native-styles for Expo).
122
- - **Lean agent docs** — `AGENTS.md` (tiny, always on) + `RULES.md` (lazy index) generated per project.
123
- - **Stack-native profiles** — Small, Medium (recommended/default), and Large map to familiar architecture for each selected stack; Large defaults to a modular monolith, not microservices.
124
- - **Intent-based authentication** — choose Yes, Not yet, or No; the generator maps that intent to Supabase Auth, Spring server sessions, or external-provider OIDC validation as appropriate.
125
- - **Manifest-driven** — `*.manifest.json` drives compatibility, exact dependency requests, env prefixes (`NEXT_PUBLIC_`/`VITE_`/`EXPO_PUBLIC_`), conditional playbooks, and concern wiring.
126
- - **Tested compatibility profiles** — exact direct dependencies and runtime/container versions are resolved from one catalog; current and previous profiles are verified in CI.
127
- - **Optional concerns, never mandated** — validation/Zod, data-fetching, state, t3-env, URL state are advisory (`CONTEXT.md` only) not forced.
128
- - **Runnable foundations** — profile-specific feature slices, health endpoints, security headers, selected auth plumbing, Spring `ProblemDetail`, PostgreSQL Testcontainers, and risk-based tests.
129
- - **Safety + contracts** — destination-exists guard, manifest ↔ heading checks, and a generated-output matrix covering every pairing, architecture profile, and applicable auth model.
130
-
131
- ## What you get
132
-
133
- | File | Purpose |
134
- |------|---------|
135
- | `AGENTS.md` | Lean, always-loaded guidance for your coding agent. |
136
- | `RULES.md` | Lazy index — `concern → playbook §`. Read only what you touch. |
137
- | `CONTEXT.md` | Project context + any advisory "expected concerns". |
138
- | `playbooks/` | Curated rule playbooks (shipped lean). |
139
- | `package.json` | Generated from the selected stack's manifest. |
140
- | `create-win-project.profile.json` | Separately records compatibility, architecture, and authentication selections. |
141
- | `.env.example` | Generated from the stack's declared env vars (prefixes already applied). |
142
- | Framework source/config | A working page or screen, health endpoint where applicable, strict TypeScript, lint, tests, and build scripts. |
143
- | `Makefile` / `docker-compose.yml` / `.github/workflows` | Optional, interview-toggled. |
144
-
145
- ## How it works
146
-
147
- The generator never hardcodes the folder or playbook list. It loads `playbooks/**/*.manifest.json` and resolves a *stack*:
148
-
149
- ```jsonc
150
- // playbooks/stack/nextjs.manifest.json (excerpt)
151
- {
152
- "id": "nextjs",
153
- "kind": "frontend",
154
- "label": "Next.js",
155
- "appliesTo": { "backend": ["none", "supabase", "springboot", "postgres"] },
156
- "architectureProfiles": ["small", "medium", "large"],
157
- "playbooks": [
158
- "stack/nextjs/architecture.md",
159
- "stack/nextjs/structure.md",
160
- "stack/nextjs/runtime.md",
161
- "stack/nextjs/security.md",
162
- "stack/nextjs/testing.md"
163
- ],
164
- "deps": ["next", "react", "react-dom"],
165
- "concerns": [
166
- { "id": "validation", "required": false, "when": "runtime validation needed", "sections": ["Zod for Runtime Validation"] }
167
- ]
168
- }
75
+ git clone https://github.com/itsw1n/create-win-project
76
+ cd create-win-project
77
+ npm ci
78
+ npm test
169
79
  ```
170
80
 
171
- Versions are resolved from `compatibility/profiles.json`; manifests never own them. The current tested profile is the default, while `--profile=YYYY.MM` selects a retained profile explicitly. Add a policy-only concern with a manifest + playbook. A new executable stack also needs a focused `lib/scaffold.js` implementation and a contract test — this prevents docs from advertising code that doesn't exist.
172
-
173
- ## Topics
174
-
175
- Next.js · React · Spring Boot · Supabase · PostgreSQL · TypeScript · Tailwind CSS · CSS Modules · project scaffolding · project template · `AGENTS.md` · coding agents · manifest-driven · lean documentation · lazy rules index
176
-
177
- ## Contributing
178
-
179
- 1. Fork and create a feature branch.
180
- 2. `npm install` and `npm test` must stay green.
181
- 3. Add or update a `*.manifest.json` for new stacks/concerns; put package versions only in `compatibility/profiles.json`.
182
- 4. Run `npm run verify:generated -- --profile=2026.09 --case=nextjs-supabase --architecture=medium --authentication=yes` for a focused executable check. CI runs every supported combination, architecture/auth model, and retained profile.
183
- 5. Document migration work for major upgrades, then open a pull request.
81
+ See the [maintainer documentation](./docs/maintainers/contributing.md) before changing manifests, generated behavior, compatibility profiles, or CI.
184
82
 
185
83
  ## License
186
84
 
187
- MIT see [LICENSE](./LICENSE) if added, otherwise free to use and adapt.
85
+ Licensed under the [MIT License](./LICENSE). Copyright © 2026 itsw1n.
@@ -0,0 +1,95 @@
1
+ import fs from 'fs-extra'
2
+
3
+ const catalog = await fs.readJson(new URL('../library/tested-versions.json', import.meta.url))
4
+ const scope = process.argv.find((arg) => arg.startsWith('--scope='))?.split('=')[1] || 'smoke'
5
+ const selectedStack = process.argv.find((arg) => arg.startsWith('--stack='))?.split('=')[1]
6
+ if (!['none', 'smoke', 'stack', 'full'].includes(scope)) throw new Error('--scope must be none, smoke, stack, or full')
7
+ if (scope === 'stack' && !selectedStack) throw new Error('--stack is required for stack scope')
8
+
9
+ const cases = [
10
+ 'nextjs-none', 'nextjs-supabase', 'nextjs-springboot', 'nextjs-postgres', 'nextjs-laravel', 'nextjs-fastapi',
11
+ 'react-none', 'react-supabase', 'react-springboot', 'react-laravel', 'react-fastapi',
12
+ 'react-native-none', 'react-native-supabase', 'react-native-springboot', 'react-native-laravel', 'react-native-fastapi',
13
+ 'laravel-api', 'laravel-blade', 'laravel-livewire', 'laravel-inertia-react',
14
+ 'fastapi-api',
15
+ ]
16
+
17
+ function authChoices(caseName) {
18
+ const mobile = caseName.startsWith('react-native')
19
+ const nonBrowser = mobile || caseName === 'laravel-api' || caseName === 'fastapi-api'
20
+ const base = ['not-yet', 'none'].map((authentication) => ({ authentication, audience: nonBrowser ? 'multi-client' : 'website' }))
21
+ if (caseName.includes('supabase')) base.push({ authentication: 'yes', audience: mobile ? 'multi-client' : 'website' })
22
+ if (caseName.includes('springboot') || caseName.includes('laravel') || caseName.includes('fastapi')) {
23
+ if (caseName.startsWith('laravel-') && caseName !== 'laravel-api') base.push({ authentication: 'yes', audience: 'website' })
24
+ else if (mobile || caseName === 'laravel-api' || caseName === 'fastapi-api') base.push({ authentication: 'yes', audience: 'multi-client' })
25
+ else base.push({ authentication: 'yes', audience: 'website' }, { authentication: 'yes', audience: 'multi-client' })
26
+ }
27
+ return base
28
+ }
29
+
30
+ const current = catalog.defaultProfile
31
+ const allProfiles = Object.entries(catalog.profiles).flatMap(([profile, versions]) =>
32
+ cases.flatMap((caseName) => ['small', 'medium', 'large'].flatMap((architecture) =>
33
+ authChoices(caseName).map((auth) => ({ profile, case: caseName, architecture, ...auth, node: versions.runtimes.node, java: versions.runtimes.java, php: versions.runtimes.php })))),
34
+ )
35
+ const currentFull = allProfiles.filter((entry) => entry.profile === current)
36
+
37
+ const smokeSelections = [
38
+ ['nextjs-none', 'small', 'not-yet', 'website'],
39
+ ['nextjs-supabase', 'medium', 'yes', 'website'],
40
+ ['react-springboot', 'large', 'yes', 'website'],
41
+ ['react-native-supabase', 'medium', 'yes', 'multi-client'],
42
+ ['react-native-springboot', 'small', 'yes', 'multi-client'],
43
+ ['laravel-api', 'medium', 'not-yet', 'multi-client'],
44
+ ['laravel-blade', 'small', 'yes', 'website'],
45
+ ['laravel-livewire', 'medium', 'none', 'website'],
46
+ ['laravel-inertia-react', 'large', 'yes', 'website'],
47
+ ['react-laravel', 'medium', 'yes', 'website'],
48
+ ['nextjs-fastapi', 'small', 'none', 'website'],
49
+ ['nextjs-fastapi', 'medium', 'yes', 'website'],
50
+ ['fastapi-api', 'medium', 'not-yet', 'multi-client'],
51
+ ['fastapi-api', 'large', 'yes', 'multi-client'],
52
+ ]
53
+ const matchesSmoke = (entry) => smokeSelections.some(([caseName, architecture, authentication, audience]) =>
54
+ entry.case === caseName && entry.architecture === architecture && entry.authentication === authentication && entry.audience === audience)
55
+ const containerSelections = new Set([
56
+ 'nextjs-none:small:not-yet:website',
57
+ 'react-springboot:large:yes:website',
58
+ 'laravel-inertia-react:large:yes:website',
59
+ 'nextjs-fastapi:medium:yes:website',
60
+ ])
61
+ const verifiesContainers = (entry) => entry.profile === current && containerSelections.has(
62
+ `${entry.case}:${entry.architecture}:${entry.authentication}:${entry.audience}`,
63
+ )
64
+ const smoke = currentFull.filter(matchesSmoke)
65
+ // A complete run exhaustively verifies the current profile and keeps every
66
+ // retained profile alive through the same representative compatibility lanes.
67
+ const full = [...currentFull, ...allProfiles.filter((entry) => entry.profile !== current && matchesSmoke(entry))]
68
+
69
+ function belongsToStack(caseName, stackName) {
70
+ if (stackName === 'none') return caseName.endsWith('-none')
71
+ if (stackName === 'react') return caseName.startsWith('react-')
72
+ if (stackName === 'react-native') return caseName.startsWith('react-native-')
73
+ if (['nextjs'].includes(stackName)) return caseName.startsWith(`${stackName}-`)
74
+ return caseName.includes(stackName)
75
+ }
76
+
77
+ const stackCases = full.filter((entry) => belongsToStack(entry.case, selectedStack))
78
+ const stack = [...new Map([...stackCases, ...smoke].map((entry) => [JSON.stringify(entry), entry])).values()]
79
+ const selected = (scope === 'full' ? full : scope === 'stack' ? stack : smoke)
80
+ .map((entry) => ({
81
+ ...entry,
82
+ native: scope === 'smoke' || matchesSmoke(entry),
83
+ containers: verifiesContainers(entry),
84
+ }))
85
+ const shards = Array.from({ length: 4 }, (_, index) => ({ shard: index + 1, cases: [] }))
86
+ const containerCases = selected.filter((entry) => entry.containers)
87
+ const nativeCases = selected.filter((entry) => entry.native && !entry.containers)
88
+ const contractCases = selected.filter((entry) => !entry.native)
89
+ containerCases.forEach((entry, index) => shards[index % shards.length].cases.push(entry))
90
+ nativeCases.forEach((entry, index) => shards[index % shards.length].cases.push(entry))
91
+ contractCases.forEach((entry) => {
92
+ const shortest = shards.reduce((best, shard) => shard.cases.length < best.cases.length ? shard : best)
93
+ shortest.cases.push(entry)
94
+ })
95
+ process.stdout.write(JSON.stringify(shards.filter(({ cases }) => cases.length)))
@@ -3,7 +3,7 @@ import os from 'node:os'
3
3
  import path from 'node:path'
4
4
  import { spawnSync } from 'node:child_process'
5
5
  import { fileURLToPath } from 'node:url'
6
- import { generateProject } from '../lib/generator.js'
6
+ import { generateProject } from '../src/engine/create-project.js'
7
7
 
8
8
  const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
9
9
  const args = Object.fromEntries(process.argv.slice(2).map((arg) => {
@@ -25,6 +25,10 @@ const cases = {
25
25
  'react-native-springboot': { frontend: 'react-native', backend: 'springboot', packageName: 'com.example' },
26
26
  'react-native-none': { frontend: 'react-native', backend: 'none' },
27
27
  'react-native-laravel': { frontend: 'react-native', backend: 'laravel', applicationShape: 'mobile' },
28
+ 'react-native-fastapi': { frontend: 'react-native', backend: 'fastapi', applicationShape: 'mobile' },
29
+ 'nextjs-fastapi': { frontend: 'nextjs', backend: 'fastapi', styling: 'tailwind' },
30
+ 'react-fastapi': { frontend: 'react', backend: 'fastapi', styling: 'tailwind', applicationShape: 'separate' },
31
+ 'fastapi-api': { frontend: 'no-frontend', backend: 'fastapi', applicationShape: 'api' },
28
32
  'laravel-api': { frontend: 'no-frontend', backend: 'laravel', applicationShape: 'api' },
29
33
  'laravel-blade': { frontend: 'laravel-ui', backend: 'laravel', applicationShape: 'fullstack', laravelUi: 'blade', styling: 'tailwind' },
30
34
  'laravel-livewire': { frontend: 'laravel-ui', backend: 'laravel', applicationShape: 'fullstack', laravelUi: 'livewire', styling: 'tailwind' },
@@ -32,7 +36,7 @@ const cases = {
32
36
  }
33
37
 
34
38
  if (!args.profile || !cases[args.case]) {
35
- throw new Error(`Usage: node scripts/verify-generated.mjs --profile=<id> --case=<${Object.keys(cases).join('|')}> [--architecture=small|medium|large] [--authentication=yes|not-yet|none] [--auth-audience=website|multi-client]`)
39
+ throw new Error(`Usage: npm run verify:generated -- --profile=<id> --case=<${Object.keys(cases).join('|')}> [--architecture=small|medium|large] [--authentication=yes|not-yet|none] [--auth-audience=website|multi-client] [--native=true|false]`)
36
40
  }
37
41
 
38
42
  const architecture = args.architecture || 'medium'
@@ -67,7 +71,7 @@ try {
67
71
  authentication,
68
72
  authAudience,
69
73
  testing: selected.frontend === 'react-native' ? 'basic' : 'full',
70
- docker: selected.frontend !== 'react-native' || selected.backend === 'laravel',
74
+ docker: selected.frontend !== 'react-native' || ['laravel', 'fastapi'].includes(selected.backend),
71
75
  makefile: false,
72
76
  githubActions: true,
73
77
  expectedConcerns: [],
@@ -80,6 +84,9 @@ try {
80
84
  metadata.authentication.intent !== authentication || metadata.authentication.audience !== authAudience) {
81
85
  throw new Error('Generated profile metadata does not match the requested matrix entry')
82
86
  }
87
+ if (args.native === 'false') {
88
+ console.log(`Generated contract verified: ${projectName}`)
89
+ } else {
83
90
  const packageRoot = selected.frontend === 'react' ? path.join(projectRoot, 'frontend') : projectRoot
84
91
  const publicEnv = {
85
92
  NEXT_PUBLIC_SUPABASE_URL: 'https://example.supabase.co',
@@ -94,7 +101,7 @@ try {
94
101
  POSTGRES_USER: 'postgres',
95
102
  POSTGRES_PASSWORD: 'compatibility-test',
96
103
  POSTGRES_DB: projectName.replaceAll('-', '_'),
97
- DATABASE_URL: `postgresql://postgres:compatibility-test@localhost:5432/${projectName.replaceAll('-', '_')}`,
104
+ DATABASE_URL: `postgresql+asyncpg://postgres:compatibility-test@localhost:5432/${projectName.replaceAll('-', '_')}`,
98
105
  SPRING_PROFILES_ACTIVE: 'test',
99
106
  OIDC_ISSUER_URI: 'http://localhost:9090/realms/app',
100
107
  OIDC_AUDIENCE: 'api',
@@ -140,6 +147,22 @@ try {
140
147
  }
141
148
  }
142
149
 
150
+ if (selected.backend === 'fastapi') {
151
+ const backendRoot = selected.frontend === 'no-frontend' ? projectRoot : path.join(projectRoot, 'backend')
152
+ run('uv', ['sync'], backendRoot)
153
+ run('docker', ['compose', 'up', '-d', '--wait', 'db'], projectRoot, publicEnv)
154
+ try {
155
+ run('uv', ['run', 'ruff', 'check', '.'], backendRoot)
156
+ run('uv', ['run', 'ruff', 'format', '--check', '.'], backendRoot)
157
+ run('uv', ['run', 'mypy', '.'], backendRoot)
158
+ run('uv', ['run', 'pytest'], backendRoot, publicEnv)
159
+ run('uv', ['run', 'alembic', 'upgrade', 'head'], backendRoot, publicEnv)
160
+ run('uv', ['run', 'alembic', 'check'], backendRoot, publicEnv)
161
+ } finally {
162
+ run('docker', ['compose', 'down', '--volumes'], projectRoot, publicEnv)
163
+ }
164
+ }
165
+
143
166
  if (selected.frontend !== 'react-native') {
144
167
  run('docker', ['compose', 'config'], projectRoot, publicEnv)
145
168
  if (args.containers === 'true') {
@@ -147,6 +170,7 @@ try {
147
170
  if (selected.backend === 'postgres') run('docker', ['build', '-t', `${projectName}:compat`, '.'], projectRoot, publicEnv)
148
171
  }
149
172
  }
173
+ }
150
174
  } finally {
151
175
  process.chdir(root)
152
176
  await fs.remove(fixtureRoot)
@@ -1,18 +1,18 @@
1
1
  import fs from 'fs-extra'
2
2
  import path from 'node:path'
3
3
  import { fileURLToPath } from 'node:url'
4
- import { loadCatalog, resolveStack } from '../lib/catalog.js'
5
- import { buildRulesIndex } from '../lib/playbooks.js'
4
+ import { loadCatalog, resolveStack } from '../src/engine/load-library.js'
5
+ import { buildRulesIndex, resolvePlaybook } from '../src/engine/project-guidance.js'
6
6
 
7
7
  const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
8
- const playbooksDir = path.join(root, 'playbooks')
8
+ const playbooksDir = path.join(root, 'library')
9
9
  const catalog = await loadCatalog(playbooksDir)
10
10
  const errors = []
11
11
 
12
12
  for (const manifest of catalog.manifests) {
13
13
  for (const entry of manifest._playbooks || []) {
14
14
  const relative = typeof entry === 'string' ? entry : entry.file
15
- if (!await fs.pathExists(path.join(playbooksDir, relative))) {
15
+ if (!await fs.pathExists(await resolvePlaybook(playbooksDir, relative))) {
16
16
  errors.push(`${manifest.id}: missing playbook ${relative}`)
17
17
  }
18
18
  }
@@ -23,10 +23,10 @@ const authIntents = ['not-yet', 'none']
23
23
  for (const frontend of catalog.frontends) {
24
24
  for (const backend of frontend.appliesTo?.backend || []) {
25
25
  const authCases = [...authIntents]
26
- if (['supabase', 'springboot'].includes(backend)) authCases.push('yes')
26
+ if (['supabase', 'springboot', 'fastapi'].includes(backend)) authCases.push('yes')
27
27
  for (const architecture of architectures) {
28
28
  for (const authentication of authCases) {
29
- const audiences = authentication === 'yes' && backend === 'springboot'
29
+ const audiences = authentication === 'yes' && ['springboot', 'fastapi'].includes(backend)
30
30
  ? ['website', 'multi-client']
31
31
  : [frontend.platform === 'mobile' ? 'multi-client' : 'website']
32
32
  for (const authAudience of audiences) {
@@ -48,8 +48,12 @@ const markdownFiles = (await fs.readdir(playbooksDir, { recursive: true, withFil
48
48
  for (const file of markdownFiles) {
49
49
  const content = await fs.readFile(file, 'utf8')
50
50
  const relative = path.relative(playbooksDir, file)
51
- const limit = relative.startsWith('stack/') || relative.startsWith('platform/') || relative.startsWith('capabilities/') ? 250 : 650
51
+ const limit = 200
52
52
  if (content.split('\n').length > limit) errors.push(`${relative}: exceeds ${limit} lines`)
53
+ const headings = [...content.matchAll(/^#{1,2}\s+(.+)$/gm)].map((match) => match[1].trim().toLocaleLowerCase('en'))
54
+ for (const heading of new Set(headings.filter((value, index) => headings.indexOf(value) !== index))) {
55
+ errors.push(`${relative}: duplicate heading ${heading}`)
56
+ }
53
57
  for (const match of content.matchAll(/\[[^\]]*\]\(([^)]+)\)/g)) {
54
58
  const target = match[1].split('#')[0]
55
59
  if (!target || /^(?:https?:|mailto:)/.test(target)) continue
@@ -64,8 +68,14 @@ for (const directory of [path.join(root, 'README.md'), path.join(root, 'docs'),
64
68
  ? (await fs.readdir(directory, { recursive: true, withFileTypes: true })).filter((entry) => entry.isFile() && entry.name.endsWith('.md')).map((entry) => path.join(entry.parentPath, entry.name))
65
69
  : [directory]
66
70
  for (const file of files) {
71
+ if (path.relative(root, file).startsWith(`docs${path.sep}superpowers${path.sep}`)) continue
67
72
  const content = await fs.readFile(file, 'utf8')
68
73
  for (const stale of staleClaims) if (content.includes(stale)) errors.push(`${path.relative(root, file)}: stale reference ${stale}`)
74
+ for (const match of content.matchAll(/\[[^\]]*\]\(([^)]+)\)/g)) {
75
+ const target = match[1].split('#')[0]
76
+ if (!target || /^(?:https?:|mailto:)/.test(target)) continue
77
+ if (!await fs.pathExists(path.resolve(path.dirname(file), target))) errors.push(`${path.relative(root, file)}: broken link ${match[1]}`)
78
+ }
69
79
  }
70
80
  }
71
81
 
@@ -0,0 +1,15 @@
1
+ import { execFileSync } from 'node:child_process'
2
+
3
+ const [{ files = [] } = {}] = JSON.parse(execFileSync('npm', ['pack', '--dry-run', '--json'], { encoding: 'utf8' }))
4
+ const names = new Set(files.map(({ path }) => path))
5
+ const required = ['index.js', 'package.json', 'README.md', 'LICENSE', 'src/cli/main.js', 'library/tested-versions.json']
6
+ const missing = required.filter((name) => !names.has(name))
7
+ const forbidden = [...names].filter((name) => name.startsWith('.github/') || name.startsWith('tests/') || name.includes('.env'))
8
+
9
+ if (missing.length || forbidden.length) {
10
+ if (missing.length) console.error(`Missing package files: ${missing.join(', ')}`)
11
+ if (forbidden.length) console.error(`Forbidden package files: ${forbidden.join(', ')}`)
12
+ process.exitCode = 1
13
+ } else {
14
+ console.log(`Package contents verified (${names.size} files).`)
15
+ }
@@ -0,0 +1,29 @@
1
+ import { pathToFileURL } from 'node:url'
2
+ import impact from '../library/compatibility-impact.json' with { type: 'json' }
3
+
4
+ const DOCUMENTATION = /^(?:AGENTS\.md|README\.md|LICENSE|docs\/|\.github\/ISSUE_TEMPLATE\/|\.github\/PULL_REQUEST_TEMPLATE)/
5
+ const STACK_PATHS = Object.entries(impact.stacks)
6
+
7
+ export function classifyChanges(files) {
8
+ const changed = [...new Set(files.map((file) => file.trim()).filter(Boolean))]
9
+ if (!changed.length || changed.every((file) => DOCUMENTATION.test(file))) return { scope: 'none' }
10
+ if (changed.some((file) => impact.shared.some((prefix) => file.startsWith(prefix)))) return { scope: 'full' }
11
+
12
+ const stacks = new Set()
13
+ let hasOrdinaryChange = false
14
+ for (const file of changed) {
15
+ const matches = STACK_PATHS.filter(([, prefixes]) => prefixes.some((prefix) => file.startsWith(prefix))).map(([stack]) => stack)
16
+ if (matches.length) matches.forEach((stack) => stacks.add(stack))
17
+ else if (!DOCUMENTATION.test(file)) hasOrdinaryChange = true
18
+ }
19
+ if (!hasOrdinaryChange && stacks.size === 1) return { scope: 'stack', stack: [...stacks][0] }
20
+ return { scope: 'smoke' }
21
+ }
22
+
23
+ async function main() {
24
+ let input = ''
25
+ for await (const chunk of process.stdin) input += chunk
26
+ process.stdout.write(JSON.stringify(classifyChanges(input.split(/\r?\n/))))
27
+ }
28
+
29
+ if (import.meta.url === pathToFileURL(process.argv[1]).href) await main()
@@ -0,0 +1,21 @@
1
+ import { spawnSync } from 'node:child_process'
2
+
3
+ const cases = JSON.parse(process.env.COMPATIBILITY_CASES || '[]')
4
+ if (!Array.isArray(cases) || !cases.length) throw new Error('COMPATIBILITY_CASES must contain at least one case')
5
+
6
+ for (const testCase of cases) {
7
+ const args = [
8
+ 'checks/check-generated-project.js',
9
+ `--profile=${testCase.profile}`,
10
+ `--case=${testCase.case}`,
11
+ `--architecture=${testCase.architecture}`,
12
+ `--authentication=${testCase.authentication}`,
13
+ `--auth-audience=${testCase.audience}`,
14
+ `--native=${testCase.native !== false}`,
15
+ ]
16
+ if (testCase.native !== false && testCase.containers && process.env.VERIFY_CONTAINERS === 'true') args.push('--containers=true')
17
+ console.log(`::group::${testCase.profile} ${testCase.case} ${testCase.architecture} ${testCase.authentication}`)
18
+ const result = spawnSync(process.execPath, args, { stdio: 'inherit' })
19
+ console.log('::endgroup::')
20
+ if (result.status !== 0) process.exit(result.status ?? 1)
21
+ }
package/docs/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # Documentation
2
+
3
+ ## Using create-win-project
4
+
5
+ - [Getting started](getting-started.md) — install the CLI and create the first project.
6
+ - [Production contract](production-contract.md) — guarantees and team-owned work.
7
+ - [Stacks and capabilities](capabilities.md) — supported combinations and conditional features.
8
+ - [Generated project](generated-project.md) — files and next steps after generation.
9
+ - [Compatibility and upgrades](compatibility.md) — versions, lockfiles, support, and reports.
10
+ - [Version 1 migration](migration-v2.md) — adopt version 2 without overwriting a project.
11
+
12
+ ## Maintaining the generator
13
+
14
+ - [Architecture](maintainers/architecture.md)
15
+ - [Content model](maintainers/content-model.md)
16
+ - [CI strategy](maintainers/ci-strategy.md)
17
+ - [Dependency maintenance](maintainers/dependencies.md)
18
+ - [Contributing](maintainers/contributing.md)
19
+
20
+ The `library/` directory contains manifest-selected knowledge copied into generated projects for coding agents. It is not general user documentation.
@@ -0,0 +1,13 @@
1
+ # Stacks and capabilities
2
+
3
+ Frontends include Next.js, React with Vite, Expo/React Native, and Laravel Blade, Livewire, or Inertia React. Server/data boundaries include none, Supabase, PostgreSQL through Prisma, Spring Boot, Laravel, and FastAPI (Python with uv, async SQLAlchemy/PostgreSQL, and Alembic). Unsupported pairings fail before files are written.
4
+
5
+ Authentication is intent-based. The generator selects Supabase Auth, a server-managed browser session, Sanctum SPA, or OIDC validation according to stack and audience. FastAPI uses OIDC bearer validation only; it does not offer session authentication.
6
+
7
+ ## Conditional capabilities
8
+
9
+ - `--uploads=object-storage` adds private-upload authorization, limits, signature verification, generated names, quarantine/scanning, cleanup, and test expectations.
10
+ - `--background-jobs=queue` adds bounded retry, timeouts, idempotency, failed jobs, observability, and replay rules.
11
+ - `--offline=cache|sync` is mobile-only and defines ownership, keys, TTL, invalidation, privacy, reconnect states, and conflicts.
12
+
13
+ Shared server caching is not automatic. Add it only for a measured use case with recorded ownership, keys, TTL, invalidation, privacy, and multi-instance behavior.
@@ -0,0 +1,13 @@
1
+ # Compatibility profiles and upgrades
2
+
3
+ Dated current and previous profiles own exact direct npm, Composer, and Python versions plus tested runtime and container versions. Definitions request package names only. Package managers validate peers, resolve transitive dependencies, and generate project-owned lockfiles (`package-lock.json`, `composer.lock`, `uv.lock`).
4
+
5
+ Profiles are tested starting points, not permanent locks. Support and successor dates are recorded in `library/tested-versions.json`; promotion requires generated-project coverage and migration review.
6
+
7
+ Existing projects are never modified automatically:
8
+
9
+ ```bash
10
+ create-win-project upgrade-report .
11
+ ```
12
+
13
+ The report describes profile, runtime, security-contract, and migration differences without writing files. Review dependency release notes and resolved lockfile changes before upgrading.
@@ -0,0 +1,12 @@
1
+ # Understanding a generated project
2
+
3
+ Every project contains runnable source, tests, stack-specific CI and security workflows, environment examples, setup and operations guides, plus production Docker or EAS files where applicable.
4
+
5
+ `create-win-project.profile.json` records schema version 2, stack, dated profile, architecture, authentication, production guarantees, capabilities, and runtimes. It enables read-only comparisons without controlling future project changes.
6
+
7
+ - `AGENTS.md` is the small always-on operating and authority contract.
8
+ - `RULES.md` maps tasks to exact selected playbook sections.
9
+ - `playbooks/` contains only knowledge applicable to the stack.
10
+ - `CONTEXT.md` records product goals, decisions, capabilities, and approved deviations.
11
+
12
+ Application behavior, tests, and framework configuration remain the source of truth when prose drifts.