create-win-project 1.4.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.
- package/LICENSE +1 -1
- package/README.md +54 -147
- package/checks/check-compatibility.js +37 -8
- package/checks/check-generated-project.js +27 -3
- package/checks/check-library.js +13 -3
- package/checks/check-package.js +15 -0
- package/checks/classify-changes.js +5 -15
- package/checks/run-compatibility-shard.js +21 -0
- package/docs/README.md +20 -0
- package/docs/capabilities.md +13 -0
- package/docs/compatibility.md +13 -0
- package/docs/generated-project.md +12 -0
- package/docs/getting-started.md +26 -0
- package/docs/{ARCHITECTURE.md → maintainers/architecture.md} +3 -3
- package/docs/maintainers/ci-strategy.md +46 -0
- package/docs/{CONTRIBUTING.md → maintainers/contributing.md} +7 -7
- package/docs/migration-v2.md +13 -0
- package/docs/production-contract.md +24 -0
- package/library/INDEX.md +1 -1
- package/library/compatibility-impact.json +14 -0
- package/library/development-tools/devops/makefile/commands.md +11 -0
- package/library/development-tools/devops/makefile/definition.json +2 -2
- package/library/development-tools/devops/makefile/docker.md +9 -0
- package/library/development-tools/devops/makefile/validation.md +5 -0
- package/library/features/sqlalchemy-alembic.md +17 -0
- package/library/optional-features/concerns/zod/errors.md +5 -0
- package/library/optional-features/concerns/zod/testing.md +5 -0
- package/library/optional-features/concerns/zod/validation.md +21 -0
- package/library/optional-features/styling/css-modules/definition.json +3 -2
- package/library/optional-features/styling/css-modules/responsive.md +5 -0
- package/library/optional-features/styling/css-modules/theme.md +7 -0
- package/library/stacks/expo/definition.json +2 -2
- package/library/stacks/fastapi/architecture.md +40 -0
- package/library/stacks/fastapi/definition.json +39 -0
- package/library/stacks/fastapi/runtime.md +25 -0
- package/library/stacks/fastapi/security.md +26 -0
- package/library/stacks/fastapi/structure.md +27 -0
- package/library/stacks/fastapi/testing.md +23 -0
- package/library/stacks/nextjs/definition.json +2 -2
- package/library/stacks/no-frontend/definition.json +1 -1
- package/library/stacks/react-vite/definition.json +2 -2
- package/library/tested-versions.json +42 -2
- package/library/universal/coding-rules/definition.json +3 -3
- package/library/universal/coding-rules/hygiene.md +9 -0
- package/library/universal/coding-rules/naming.md +17 -0
- package/library/universal/git-conventions/branches.md +5 -0
- package/library/universal/git-conventions/commits.md +7 -0
- package/library/universal/git-conventions/definition.json +4 -2
- package/library/universal/git-conventions/workflow.md +5 -0
- package/library/universal/typescript/boundaries.md +13 -0
- package/library/universal/typescript/definition.json +3 -3
- package/library/universal/typescript/errors.md +5 -0
- package/library/universal/typescript/patterns.md +7 -0
- package/package.json +4 -6
- package/src/cli/arguments.js +11 -0
- package/src/cli/main.js +18 -0
- package/src/cli/questions.js +16 -15
- package/src/cli/system-check.js +22 -2
- package/src/engine/load-library.js +3 -2
- package/src/engine/project-files.js +10 -1
- package/src/engine/project-guidance.js +2 -1
- package/src/engine/project-shapes.js +4 -4
- package/src/engine/render-templates.js +3 -0
- package/src/engine/tested-versions.js +21 -2
- package/src/engine/upgrade-report.js +20 -0
- package/src/stacks/available-stacks.js +2 -0
- package/src/stacks/backends/fastapi/ci.js +3 -0
- package/src/stacks/backends/fastapi/create-files.js +874 -0
- package/src/stacks/backends/fastapi/docker.js +75 -0
- package/src/stacks/backends/fastapi/environment.js +3 -0
- package/src/stacks/backends/fastapi/index.js +32 -0
- package/src/stacks/compose-files.js +19 -1
- package/src/stacks/create-project.js +128 -7
- package/src/stacks/frontends/nextjs/index.js +1 -1
- package/src/stacks/frontends/react-native/create-files.js +4 -1
- package/src/stacks/frontends/react-native/environment.js +1 -1
- package/src/stacks/frontends/react-native/index.js +1 -1
- package/src/stacks/frontends/react-vite/environment.js +1 -1
- package/src/stacks/frontends/react-vite/index.js +1 -1
- package/src/stacks/shared/capability-packs.js +31 -0
- package/src/stacks/shared/environment.js +12 -4
- package/src/stacks/shared/javascript-package.js +6 -0
- package/templates/ci/fastapi.yml +62 -0
- package/templates/docker/compose-prod/fastapi.yml +52 -0
- package/templates/docker/compose-prod/springboot.yml +19 -1
- package/templates/docker/dockerfile/fastapi.dev.dockerfile +9 -0
- package/templates/docker/dockerfile/fastapi.prod.dockerfile +11 -0
- package/templates/docker/dockerfile/nextjs.prod.dockerfile +1 -0
- package/templates/docker/dockerfile/springboot.prod.dockerfile +4 -1
- package/templates/docker/dockerfile/vite.prod.dockerfile +2 -1
- package/templates/makefile/fastapi.mk +97 -0
- package/library/development-tools/devops/makefile/makefile.md +0 -556
- package/library/optional-features/concerns/zod.md +0 -174
- package/library/optional-features/styling/css-modules/css-modules-extensions.md +0 -267
- package/library/universal/coding-rules/coding-rules.md +0 -281
- package/library/universal/git-conventions/git-conventions.md +0 -186
- package/library/universal/typescript/typescript.md +0 -272
- /package/docs/{CONTENT_MODEL.md → maintainers/content-model.md} +0 -0
- /package/docs/{DEPENDENCY_MAINTENANCE.md → maintainers/dependencies.md} +0 -0
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,178 +1,85 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<img src="./public/logo.svg" alt="W1N Project logo" width="160">
|
|
3
3
|
<h1>W1N PROJECT</h1>
|
|
4
|
-
<p>
|
|
4
|
+
<p>Generate a tested, production-oriented web or mobile project with stack-specific code, CI, deployment artifacts, documentation, and agent guidance.</p>
|
|
5
5
|
</div>
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Start
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
[](https://github.com/itsw1n/create-win-project/actions/workflows/ci.yml)
|
|
14
|
-
[](https://nodejs.org)
|
|
15
|
-
[](./library)
|
|
16
|
-
[](./playbooks)
|
|
17
|
-
[](./index.js)
|
|
18
|
-
|
|
19
|
-
Next.js · React + Vite · Expo (React Native) · Spring Boot · Supabase · PostgreSQL · Tailwind · CSS Modules
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## Why this exists
|
|
24
|
-
|
|
25
|
-
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?
|
|
26
|
-
|
|
27
|
-
**The fix:** answer a short, plain-language interview and get **two coordinated outputs**:
|
|
28
|
-
|
|
29
|
-
1. **A small runnable app** — real page/screen, strict TypeScript, ESLint, health endpoint, tests, and the exact config for your stack.
|
|
30
|
-
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.
|
|
31
|
-
|
|
32
|
-
Manifests declare capabilities and package names. A tested compatibility profile owns every exact version, so generated stacks are declarative without letting version strings drift.
|
|
33
|
-
|
|
34
|
-
## Prerequisites — what you actually need to install
|
|
35
|
-
|
|
36
|
-
### Lane 1 — To run *this generator*
|
|
37
|
-
**Node 24 LTS *OR* Docker — that's it.**
|
|
9
|
+
```bash
|
|
10
|
+
npx create-win-project@latest
|
|
11
|
+
```
|
|
38
12
|
|
|
39
|
-
|
|
40
|
-
|
|
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
|
+
```
|
|
41
26
|
|
|
42
|
-
|
|
43
|
-
| You picked | You need | What the generator includes |
|
|
44
|
-
|---|---|---|
|
|
45
|
-
| **Next.js** or **React + Vite** | Node 22.14+ with npm 11.19+ (tested on Node 24) → `npm install` → `npm run dev` | Page/entry, Vite/Next config, strict TS, ESLint, tests, `frontend/.env.example` |
|
|
46
|
-
| **Expo** | Node 22.14+ with npm 11.19+ (tested on Node 24) + Expo Go app → `npm install` → `npx expo start` | Expo Router layout/screen, `app.json`, Jest, TS |
|
|
47
|
-
| **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 |
|
|
48
|
-
| **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
|
|
49
28
|
|
|
50
|
-
|
|
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 |
|
|
51
36
|
|
|
52
|
-
|
|
37
|
+
Authentication follows the chosen stack and audience: Supabase Auth, server sessions, Sanctum SPA, or OIDC validation where supported.
|
|
53
38
|
|
|
54
|
-
|
|
39
|
+
## Production-oriented by default
|
|
55
40
|
|
|
56
|
-
|
|
57
|
-
npx create-win-project@latest
|
|
58
|
-
```
|
|
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.
|
|
59
42
|
|
|
60
|
-
|
|
43
|
+
Optional complexity remains requirement-driven:
|
|
61
44
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
```
|
|
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.
|
|
67
49
|
|
|
68
|
-
|
|
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).
|
|
69
51
|
|
|
70
|
-
|
|
71
|
-
git clone https://github.com/itsw1n/create-win-project && cd create-win-project
|
|
72
|
-
docker compose build
|
|
73
|
-
docker compose run --rm app
|
|
74
|
-
```
|
|
52
|
+
## Agent-flexible defaults
|
|
75
53
|
|
|
76
|
-
|
|
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.
|
|
77
55
|
|
|
78
|
-
|
|
79
|
-
<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:
|
|
80
57
|
|
|
81
58
|
```bash
|
|
82
|
-
|
|
83
|
-
npx create-win-project --no-install # generate files only
|
|
84
|
-
npx create-win-project doctor # diagnose available tools
|
|
85
|
-
# then follow the same interview
|
|
59
|
+
create-win-project upgrade-report .
|
|
86
60
|
```
|
|
87
61
|
|
|
88
|
-
|
|
62
|
+
## Documentation
|
|
89
63
|
|
|
90
|
-
|
|
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)
|
|
91
71
|
|
|
92
|
-
|
|
72
|
+
## Development
|
|
93
73
|
|
|
94
74
|
```bash
|
|
95
|
-
|
|
96
|
-
cd
|
|
97
|
-
npm
|
|
98
|
-
npm
|
|
99
|
-
|
|
100
|
-
# React + Vite (frontend workspace)
|
|
101
|
-
cd your-project/frontend
|
|
102
|
-
npm install
|
|
103
|
-
npm run dev
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
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.
|
|
107
|
-
|
|
108
|
-
Before automatic installation, the CLI checks the current Node, npm, PHP, and Composer versions needed by the selected stack. A mismatch never changes global tools: interactive users can create files without installing, view setup instructions, or cancel; noninteractive runs safely create files and skip installation. Node 22 is supported with npm 11.19 or newer. npm 10.9.8 and early npm 11 releases can crash while resolving current generated dependencies, so switch through Mise/NVM or update that managed Node installation before retrying.
|
|
109
|
-
|
|
110
|
-
> The generator **never overwrites a non-empty folder** — it stages to a temp dir and moves into place only on success.
|
|
111
|
-
|
|
112
|
-
## Features
|
|
113
|
-
|
|
114
|
-
- **Three frontend families** — Next.js App Router, React + Vite, and Expo Router.
|
|
115
|
-
- **Optional backends** — every frontend can choose no backend, Supabase, PostgreSQL where supported, or Spring Boot.
|
|
116
|
-
- **Styling** — Tailwind CSS or CSS Modules (native-styles for Expo).
|
|
117
|
-
- **Lean agent docs** — `AGENTS.md` (tiny, always on) + `RULES.md` (lazy index) generated per project.
|
|
118
|
-
- **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.
|
|
119
|
-
- **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.
|
|
120
|
-
- **Definition-driven** — `library/**/definition.json` drives compatibility, exact dependency requests, env prefixes (`NEXT_PUBLIC_`/`VITE_`/`EXPO_PUBLIC_`), conditional playbooks, and concern wiring.
|
|
121
|
-
- **Tested compatibility profiles** — exact direct dependencies and runtime/container versions are resolved from one catalog; current and previous profiles are verified in CI.
|
|
122
|
-
- **Optional concerns, never mandated** — validation/Zod, data-fetching, state, t3-env, URL state are advisory (`CONTEXT.md` only) not forced.
|
|
123
|
-
- **Runnable foundations** — profile-specific feature slices, health endpoints, security headers, selected auth plumbing, Spring `ProblemDetail`, PostgreSQL Testcontainers, and risk-based tests.
|
|
124
|
-
- **Safety + contracts** — destination-exists guard, definition ↔ heading checks, and a generated-output matrix covering every pairing, architecture profile, and applicable auth model.
|
|
125
|
-
|
|
126
|
-
## What you get
|
|
127
|
-
|
|
128
|
-
| File | Purpose |
|
|
129
|
-
|------|---------|
|
|
130
|
-
| `AGENTS.md` | Lean, always-loaded guidance for your coding agent. |
|
|
131
|
-
| `RULES.md` | Lazy index — `concern → playbook §`. Read only what you touch. |
|
|
132
|
-
| `CONTEXT.md` | Project context + any advisory "expected concerns". |
|
|
133
|
-
| `playbooks/` | Curated rule playbooks (shipped lean). |
|
|
134
|
-
| `package.json` | Generated from the selected stack's definition. |
|
|
135
|
-
| `create-win-project.profile.json` | Separately records compatibility, architecture, and authentication selections. |
|
|
136
|
-
| `.env.example` | Generated from the stack's declared env vars (prefixes already applied). |
|
|
137
|
-
| Framework source/config | A working page or screen, health endpoint where applicable, strict TypeScript, lint, tests, and build scripts. |
|
|
138
|
-
| `Makefile` / `docker-compose.yml` / `.github/workflows` | Optional, interview-toggled. |
|
|
139
|
-
|
|
140
|
-
## How it works
|
|
141
|
-
|
|
142
|
-
The generator never hardcodes the folder or playbook list. It loads `library/**/definition.json` and resolves a *stack*:
|
|
143
|
-
|
|
144
|
-
```jsonc
|
|
145
|
-
// library/stacks/nextjs/definition.json (excerpt)
|
|
146
|
-
{
|
|
147
|
-
"id": "nextjs",
|
|
148
|
-
"kind": "frontend",
|
|
149
|
-
"label": "Next.js",
|
|
150
|
-
"appliesTo": { "backend": ["none", "supabase", "springboot", "postgres"] },
|
|
151
|
-
"architectureProfiles": ["small", "medium", "large"],
|
|
152
|
-
"playbooks": [
|
|
153
|
-
"stack/nextjs/architecture.md",
|
|
154
|
-
"stack/nextjs/structure.md",
|
|
155
|
-
"stack/nextjs/runtime.md",
|
|
156
|
-
"stack/nextjs/security.md",
|
|
157
|
-
"stack/nextjs/testing.md"
|
|
158
|
-
],
|
|
159
|
-
"deps": ["next", "react", "react-dom"],
|
|
160
|
-
"concerns": [
|
|
161
|
-
{ "id": "validation", "required": false, "when": "runtime validation needed", "sections": ["Zod for Runtime Validation"] }
|
|
162
|
-
]
|
|
163
|
-
}
|
|
75
|
+
git clone https://github.com/itsw1n/create-win-project
|
|
76
|
+
cd create-win-project
|
|
77
|
+
npm ci
|
|
78
|
+
npm test
|
|
164
79
|
```
|
|
165
80
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
## Topics
|
|
169
|
-
|
|
170
|
-
Next.js · React · Spring Boot · Supabase · PostgreSQL · TypeScript · Tailwind CSS · CSS Modules · project scaffolding · project template · `AGENTS.md` · coding agents · definition-driven · lean documentation · lazy rules index
|
|
171
|
-
|
|
172
|
-
## Contributing
|
|
173
|
-
|
|
174
|
-
See [docs/CONTRIBUTING.md](./docs/CONTRIBUTING.md) — one workflow for every stack: quick start, 6-step vertical stack addition (`library/**` -> `src/stacks/<id>/` -> `available-stacks.js` -> tests/matrix -> verify), testing gates (`npm test`, `matrix:smoke` -> `dev`, `matrix:full` -> `main`), and version ownership (`library/tested-versions.json` only).
|
|
81
|
+
See the [maintainer documentation](./docs/maintainers/contributing.md) before changing manifests, generated behavior, compatibility profiles, or CI.
|
|
175
82
|
|
|
176
83
|
## License
|
|
177
84
|
|
|
178
|
-
|
|
85
|
+
Licensed under the [MIT License](./LICENSE). Copyright © 2026 itsw1n.
|
|
@@ -7,20 +7,21 @@ if (!['none', 'smoke', 'stack', 'full'].includes(scope)) throw new Error('--scop
|
|
|
7
7
|
if (scope === 'stack' && !selectedStack) throw new Error('--stack is required for stack scope')
|
|
8
8
|
|
|
9
9
|
const cases = [
|
|
10
|
-
'nextjs-none', 'nextjs-supabase', 'nextjs-springboot', 'nextjs-postgres', 'nextjs-laravel',
|
|
11
|
-
'react-none', 'react-supabase', 'react-springboot', 'react-laravel',
|
|
12
|
-
'react-native-none', 'react-native-supabase', 'react-native-springboot', 'react-native-laravel',
|
|
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
13
|
'laravel-api', 'laravel-blade', 'laravel-livewire', 'laravel-inertia-react',
|
|
14
|
+
'fastapi-api',
|
|
14
15
|
]
|
|
15
16
|
|
|
16
17
|
function authChoices(caseName) {
|
|
17
18
|
const mobile = caseName.startsWith('react-native')
|
|
18
|
-
const nonBrowser = mobile || caseName === 'laravel-api'
|
|
19
|
+
const nonBrowser = mobile || caseName === 'laravel-api' || caseName === 'fastapi-api'
|
|
19
20
|
const base = ['not-yet', 'none'].map((authentication) => ({ authentication, audience: nonBrowser ? 'multi-client' : 'website' }))
|
|
20
21
|
if (caseName.includes('supabase')) base.push({ authentication: 'yes', audience: mobile ? 'multi-client' : 'website' })
|
|
21
|
-
if (caseName.includes('springboot') || caseName.includes('laravel')) {
|
|
22
|
+
if (caseName.includes('springboot') || caseName.includes('laravel') || caseName.includes('fastapi')) {
|
|
22
23
|
if (caseName.startsWith('laravel-') && caseName !== 'laravel-api') base.push({ authentication: 'yes', audience: 'website' })
|
|
23
|
-
else if (mobile || caseName === 'laravel-api') base.push({ authentication: 'yes', audience: 'multi-client' })
|
|
24
|
+
else if (mobile || caseName === 'laravel-api' || caseName === 'fastapi-api') base.push({ authentication: 'yes', audience: 'multi-client' })
|
|
24
25
|
else base.push({ authentication: 'yes', audience: 'website' }, { authentication: 'yes', audience: 'multi-client' })
|
|
25
26
|
}
|
|
26
27
|
return base
|
|
@@ -44,9 +45,22 @@ const smokeSelections = [
|
|
|
44
45
|
['laravel-livewire', 'medium', 'none', 'website'],
|
|
45
46
|
['laravel-inertia-react', 'large', 'yes', 'website'],
|
|
46
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'],
|
|
47
52
|
]
|
|
48
53
|
const matchesSmoke = (entry) => smokeSelections.some(([caseName, architecture, authentication, audience]) =>
|
|
49
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
|
+
)
|
|
50
64
|
const smoke = currentFull.filter(matchesSmoke)
|
|
51
65
|
// A complete run exhaustively verifies the current profile and keeps every
|
|
52
66
|
// retained profile alive through the same representative compatibility lanes.
|
|
@@ -62,5 +76,20 @@ function belongsToStack(caseName, stackName) {
|
|
|
62
76
|
|
|
63
77
|
const stackCases = full.filter((entry) => belongsToStack(entry.case, selectedStack))
|
|
64
78
|
const stack = [...new Map([...stackCases, ...smoke].map((entry) => [JSON.stringify(entry), entry])).values()]
|
|
65
|
-
const selected = scope === 'full' ? full : scope === 'stack' ? stack : smoke
|
|
66
|
-
|
|
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)))
|
|
@@ -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: 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]`)
|
|
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' ||
|
|
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)
|
package/checks/check-library.js
CHANGED
|
@@ -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' &&
|
|
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 =
|
|
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
|
+
}
|
|
@@ -1,28 +1,18 @@
|
|
|
1
1
|
import { pathToFileURL } from 'node:url'
|
|
2
|
+
import impact from '../library/compatibility-impact.json' with { type: 'json' }
|
|
2
3
|
|
|
3
|
-
const DOCUMENTATION = /^(?:README\.md|LICENSE|docs\/|\.github\/ISSUE_TEMPLATE\/|\.github\/PULL_REQUEST_TEMPLATE)/
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
const STACK_PATHS = [
|
|
7
|
-
['nextjs', /^(?:src\/stacks\/frontends\/nextjs\/|library\/stacks\/nextjs\/|templates\/(?:agents|ci)\/nextjs\.)/],
|
|
8
|
-
['react', /^(?:src\/stacks\/frontends\/react-vite\/|library\/stacks\/react-vite\/|templates\/(?:agents|ci)\/(?:react-vite|vite)\.)/],
|
|
9
|
-
['react-native', /^(?:src\/stacks\/frontends\/react-native\/|library\/stacks\/expo\/|templates\/(?:agents|ci)\/(?:react-native|expo)\.)/],
|
|
10
|
-
['springboot', /^(?:src\/stacks\/backends\/springboot\/|library\/(?:stacks\/springboot|features\/(?:flyway|postgresql))\/|templates\/ci\/springboot\.)/],
|
|
11
|
-
['laravel', /^(?:src\/stacks\/backends\/laravel\/|library\/(?:stacks\/laravel|platforms\/laravel-ui|features\/laravel)\/|templates\/ci\/laravel\.)/],
|
|
12
|
-
['supabase', /^(?:src\/stacks\/backends\/supabase\/|library\/features\/supabase\/)/],
|
|
13
|
-
['postgres', /^(?:src\/stacks\/backends\/postgres\/|library\/features\/(?:postgresql|prisma)\/)/],
|
|
14
|
-
['none', /^src\/stacks\/backends\/none\//],
|
|
15
|
-
]
|
|
4
|
+
const DOCUMENTATION = /^(?:AGENTS\.md|README\.md|LICENSE|docs\/|\.github\/ISSUE_TEMPLATE\/|\.github\/PULL_REQUEST_TEMPLATE)/
|
|
5
|
+
const STACK_PATHS = Object.entries(impact.stacks)
|
|
16
6
|
|
|
17
7
|
export function classifyChanges(files) {
|
|
18
8
|
const changed = [...new Set(files.map((file) => file.trim()).filter(Boolean))]
|
|
19
9
|
if (!changed.length || changed.every((file) => DOCUMENTATION.test(file))) return { scope: 'none' }
|
|
20
|
-
if (changed.some((file) =>
|
|
10
|
+
if (changed.some((file) => impact.shared.some((prefix) => file.startsWith(prefix)))) return { scope: 'full' }
|
|
21
11
|
|
|
22
12
|
const stacks = new Set()
|
|
23
13
|
let hasOrdinaryChange = false
|
|
24
14
|
for (const file of changed) {
|
|
25
|
-
const matches = STACK_PATHS.filter(([,
|
|
15
|
+
const matches = STACK_PATHS.filter(([, prefixes]) => prefixes.some((prefix) => file.startsWith(prefix))).map(([stack]) => stack)
|
|
26
16
|
if (matches.length) matches.forEach((stack) => stacks.add(stack))
|
|
27
17
|
else if (!DOCUMENTATION.test(file)) hasOrdinaryChange = true
|
|
28
18
|
}
|
|
@@ -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.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
Run the generator with Node.js 24 LTS or Docker. Generated projects list their own runtime requirements in `docs/guides/setup.md`.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx create-win-project@latest
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
The interview asks for application shape, framework, backend/data boundary, architecture, authentication, and applicable capabilities. It can install dependencies and create lockfiles immediately.
|
|
10
|
+
|
|
11
|
+
For development from a clone:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm ci
|
|
15
|
+
npm run doctor
|
|
16
|
+
npm start
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
For a containerized generator:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
docker compose build
|
|
23
|
+
docker compose run --rm app
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
After generation, enter the new directory and follow its README. The CLI stages files before moving them into place and refuses to overwrite a non-empty destination.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Architecture
|
|
1
|
+
# Architecture
|
|
2
2
|
|
|
3
3
|
`create-win-project` composes a validated project specification into two coordinated outputs:
|
|
4
4
|
|
|
@@ -124,7 +124,7 @@ Exactly one profile is `current` and one is `previous`. The current profile is t
|
|
|
124
124
|
| `RULES.md` | Generated concern-to-playbook section router. |
|
|
125
125
|
| `playbooks/` | Reusable standards, recipes, rationale, and stack guidance. |
|
|
126
126
|
| `docs/` | Documentation for the generator (ARCHITECTURE, CONTRIBUTING, etc.), not generic framework teaching. |
|
|
127
|
-
| `docs/
|
|
127
|
+
| `docs/maintainers/contributing.md` | How to add a stack and run checks — the workflow, not the design. |
|
|
128
128
|
|
|
129
129
|
Manifest section names are checked against Markdown headings. Numbered headings are normalized for matching, and generated contract tests reject unresolved `RULES.md` entries.
|
|
130
130
|
|
|
@@ -147,7 +147,7 @@ Canonical Markdown code examples should progressively move into extracted fixtur
|
|
|
147
147
|
|
|
148
148
|
## Extension workflow
|
|
149
149
|
|
|
150
|
-
When adding a stack or capability (see `docs/
|
|
150
|
+
When adding a stack or capability (see `docs/maintainers/contributing.md` for the full checklist):
|
|
151
151
|
|
|
152
152
|
1. Add its `library/**/definition.json` (names only, no versions), playbooks, and `library/tested-versions.json` entry remains version-free.
|
|
153
153
|
2. Create one new `src/stacks/<frontends|backends>/<id>/` directory with `index.js` (`defineStackAdapter`), `create-files.js`, `dependencies.js`/`environment.js` where needed, and `auth/`+`ui/` subfolders for Laravel-style stacks.
|