create-win-project 1.3.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 +21 -0
- package/README.md +187 -0
- package/ci/expo.yml +28 -0
- package/ci/laravel.yml +49 -0
- package/ci/nextjs.yml +29 -0
- package/ci/springboot.yml +33 -0
- package/ci/vite.yml +36 -0
- package/compatibility/profiles.json +237 -0
- package/docs/ARCHITECTURE.md +118 -0
- package/docs/CONTENT_MODEL.md +36 -0
- package/docs/DEPENDENCY_MAINTENANCE.md +31 -0
- package/index.js +431 -0
- package/lib/application-shapes.js +95 -0
- package/lib/banner.js +45 -0
- package/lib/catalog.js +343 -0
- package/lib/compatibility.js +116 -0
- package/lib/constants.js +3 -0
- package/lib/doctor.js +45 -0
- package/lib/files.js +158 -0
- package/lib/generator.js +366 -0
- package/lib/interview.js +77 -0
- package/lib/laravel-scaffold.js +3 -0
- package/lib/playbooks.js +172 -0
- package/lib/project-location.js +18 -0
- package/lib/scaffold.js +467 -0
- package/lib/stacks/context.js +27 -0
- package/lib/stacks/contract.js +142 -0
- package/lib/stacks/index.js +8 -0
- package/lib/stacks/laravel/architecture.js +27 -0
- package/lib/stacks/laravel/auth/oidc.js +13 -0
- package/lib/stacks/laravel/auth/public.js +28 -0
- package/lib/stacks/laravel/auth/sanctum.js +14 -0
- package/lib/stacks/laravel/auth/session.js +17 -0
- package/lib/stacks/laravel/composer.js +50 -0
- package/lib/stacks/laravel/generate.js +362 -0
- package/lib/stacks/laravel/index.js +18 -0
- package/lib/stacks/laravel/ui/blade.js +15 -0
- package/lib/stacks/laravel/ui/index.js +29 -0
- package/lib/stacks/laravel/ui/inertia-react.js +35 -0
- package/lib/stacks/laravel/ui/livewire.js +39 -0
- package/lib/stacks/laravel/ui/shared.js +14 -0
- package/lib/stacks/registry.js +42 -0
- package/lib/stacks/shared/contributions.js +25 -0
- package/lib/template.js +62 -0
- package/package.json +49 -0
- package/playbooks/INDEX.md +58 -0
- package/playbooks/capabilities/auth/oidc-resource-server.md +15 -0
- package/playbooks/capabilities/auth/spring-session.md +15 -0
- package/playbooks/capabilities/ci/github-actions.manifest.json +10 -0
- package/playbooks/capabilities/ci/github-actions.md +61 -0
- package/playbooks/capabilities/docker/docker.manifest.json +11 -0
- package/playbooks/capabilities/docker/overview.md +55 -0
- package/playbooks/capabilities/flyway/environments.md +5 -0
- package/playbooks/capabilities/flyway/flyway.manifest.json +12 -0
- package/playbooks/capabilities/flyway/migrations.md +7 -0
- package/playbooks/capabilities/flyway/testing.md +5 -0
- package/playbooks/capabilities/laravel/database.md +8 -0
- package/playbooks/capabilities/laravel/migrations.md +15 -0
- package/playbooks/capabilities/laravel/observability.md +8 -0
- package/playbooks/capabilities/laravel/oidc-resource-server.md +10 -0
- package/playbooks/capabilities/laravel/queues.md +8 -0
- package/playbooks/capabilities/laravel/sanctum-spa.md +8 -0
- package/playbooks/capabilities/laravel/scheduler.md +7 -0
- package/playbooks/capabilities/laravel/session-auth.md +8 -0
- package/playbooks/capabilities/laravel/storage-uploads.md +7 -0
- package/playbooks/capabilities/postgresql/architecture.md +5 -0
- package/playbooks/capabilities/postgresql/migrations.md +5 -0
- package/playbooks/capabilities/postgresql/postgresql.manifest.json +15 -0
- package/playbooks/capabilities/postgresql/schema-design.md +7 -0
- package/playbooks/capabilities/postgresql/security.md +5 -0
- package/playbooks/capabilities/postgresql/testing.md +5 -0
- package/playbooks/capabilities/prisma/architecture.md +5 -0
- package/playbooks/capabilities/prisma/migrations.md +11 -0
- package/playbooks/capabilities/prisma/prisma.manifest.json +14 -0
- package/playbooks/capabilities/prisma/runtime.md +5 -0
- package/playbooks/capabilities/prisma/schema.md +5 -0
- package/playbooks/capabilities/prisma/testing.md +5 -0
- package/playbooks/capabilities/supabase/architecture.md +5 -0
- package/playbooks/capabilities/supabase/authentication.md +5 -0
- package/playbooks/capabilities/supabase/expo.md +16 -0
- package/playbooks/capabilities/supabase/migrations.md +7 -0
- package/playbooks/capabilities/supabase/nextjs.md +24 -0
- package/playbooks/capabilities/supabase/rls.md +7 -0
- package/playbooks/capabilities/supabase/supabase.manifest.json +32 -0
- package/playbooks/capabilities/supabase/testing.md +5 -0
- package/playbooks/capabilities/supabase/vite.md +14 -0
- package/playbooks/concerns/axios.md +123 -0
- package/playbooks/concerns/next-safe-action.md +66 -0
- package/playbooks/concerns/next-themes.md +87 -0
- package/playbooks/concerns/nuqs.md +59 -0
- package/playbooks/concerns/t3-env.md +48 -0
- package/playbooks/concerns/tanstack-query.md +145 -0
- package/playbooks/concerns/zod.md +174 -0
- package/playbooks/concerns/zustand.md +93 -0
- package/playbooks/devops/makefile.manifest.json +10 -0
- package/playbooks/devops/makefile.md +556 -0
- package/playbooks/devops/pr-template.manifest.json +10 -0
- package/playbooks/devops/pr-template.md +106 -0
- package/playbooks/platform/laravel-ui/blade/architecture.md +5 -0
- package/playbooks/platform/laravel-ui/blade/runtime.md +5 -0
- package/playbooks/platform/laravel-ui/blade/security.md +5 -0
- package/playbooks/platform/laravel-ui/blade/structure.md +5 -0
- package/playbooks/platform/laravel-ui/blade/testing.md +5 -0
- package/playbooks/platform/laravel-ui/inertia-react/architecture.md +5 -0
- package/playbooks/platform/laravel-ui/inertia-react/runtime.md +5 -0
- package/playbooks/platform/laravel-ui/inertia-react/security.md +5 -0
- package/playbooks/platform/laravel-ui/inertia-react/structure.md +5 -0
- package/playbooks/platform/laravel-ui/inertia-react/testing.md +5 -0
- package/playbooks/platform/laravel-ui/livewire/architecture.md +5 -0
- package/playbooks/platform/laravel-ui/livewire/runtime.md +5 -0
- package/playbooks/platform/laravel-ui/livewire/security.md +5 -0
- package/playbooks/platform/laravel-ui/livewire/structure.md +5 -0
- package/playbooks/platform/laravel-ui/livewire/testing.md +5 -0
- package/playbooks/platform/mobile.manifest.json +17 -0
- package/playbooks/platform/mobile.md +15 -0
- package/playbooks/platform/web.manifest.json +17 -0
- package/playbooks/platform/web.md +17 -0
- package/playbooks/stack/expo/architecture.md +27 -0
- package/playbooks/stack/expo/runtime.md +14 -0
- package/playbooks/stack/expo/security.md +18 -0
- package/playbooks/stack/expo/structure.md +26 -0
- package/playbooks/stack/expo/testing.md +13 -0
- package/playbooks/stack/laravel/architecture.md +34 -0
- package/playbooks/stack/laravel/runtime.md +27 -0
- package/playbooks/stack/laravel/security.md +23 -0
- package/playbooks/stack/laravel/structure.md +29 -0
- package/playbooks/stack/laravel/testing.md +22 -0
- package/playbooks/stack/laravel-ui.manifest.json +42 -0
- package/playbooks/stack/laravel.manifest.json +35 -0
- package/playbooks/stack/nextjs/architecture.md +62 -0
- package/playbooks/stack/nextjs/runtime.md +31 -0
- package/playbooks/stack/nextjs/security.md +32 -0
- package/playbooks/stack/nextjs/structure.md +50 -0
- package/playbooks/stack/nextjs/testing.md +20 -0
- package/playbooks/stack/nextjs.manifest.json +49 -0
- package/playbooks/stack/no-frontend.manifest.json +25 -0
- package/playbooks/stack/none.manifest.json +21 -0
- package/playbooks/stack/react-native.manifest.json +49 -0
- package/playbooks/stack/react-vite/architecture.md +33 -0
- package/playbooks/stack/react-vite/runtime.md +13 -0
- package/playbooks/stack/react-vite/security.md +16 -0
- package/playbooks/stack/react-vite/structure.md +23 -0
- package/playbooks/stack/react-vite/testing.md +12 -0
- package/playbooks/stack/react-vite.manifest.json +46 -0
- package/playbooks/stack/springboot/architecture.md +40 -0
- package/playbooks/stack/springboot/runtime.md +28 -0
- package/playbooks/stack/springboot/security.md +28 -0
- package/playbooks/stack/springboot/structure.md +30 -0
- package/playbooks/stack/springboot/testing.md +22 -0
- package/playbooks/stack/springboot.manifest.json +38 -0
- package/playbooks/styling/css-modules-extensions.md +267 -0
- package/playbooks/styling/css-modules.manifest.json +11 -0
- package/playbooks/styling/native-styles.manifest.json +9 -0
- package/playbooks/styling/native-styles.md +29 -0
- package/playbooks/styling/tailwind-extensions.md +182 -0
- package/playbooks/styling/tailwind.manifest.json +11 -0
- package/playbooks/universal/accessibility.manifest.json +11 -0
- package/playbooks/universal/accessibility.md +45 -0
- package/playbooks/universal/coding-rules.manifest.json +12 -0
- package/playbooks/universal/coding-rules.md +281 -0
- package/playbooks/universal/error-handling.manifest.json +10 -0
- package/playbooks/universal/error-handling.md +21 -0
- package/playbooks/universal/git-conventions.manifest.json +10 -0
- package/playbooks/universal/git-conventions.md +186 -0
- package/playbooks/universal/observability.manifest.json +10 -0
- package/playbooks/universal/observability.md +29 -0
- package/playbooks/universal/security.manifest.json +11 -0
- package/playbooks/universal/security.md +30 -0
- package/playbooks/universal/typescript.manifest.json +11 -0
- package/playbooks/universal/typescript.md +272 -0
- package/scripts/compatibility-matrix.mjs +48 -0
- package/scripts/validate-content.mjs +76 -0
- package/scripts/verify-generated.mjs +153 -0
- package/templates/agents/nextjs.md +47 -0
- package/templates/agents/react-native.md +48 -0
- package/templates/agents/react-vite.md +48 -0
- package/templates/docker/compose/postgres.yml +29 -0
- package/templates/docker/compose/springboot.yml +69 -0
- package/templates/docker/compose/supabase.yml +18 -0
- package/templates/docker/compose-prod/springboot.yml +64 -0
- package/templates/docker/dockerfile/laravel-inertia.dev.dockerfile +21 -0
- package/templates/docker/dockerfile/laravel.dev.dockerfile +12 -0
- package/templates/docker/dockerfile/laravel.prod.dockerfile +13 -0
- package/templates/docker/dockerfile/nextjs.dev.dockerfile +6 -0
- package/templates/docker/dockerfile/nextjs.prod.dockerfile +18 -0
- package/templates/docker/dockerfile/springboot.dev.dockerfile +5 -0
- package/templates/docker/dockerfile/springboot.prod.dockerfile +10 -0
- package/templates/docker/dockerfile/vite.dev.dockerfile +6 -0
- package/templates/docker/dockerfile/vite.prod.dockerfile +12 -0
- package/templates/gitignore/base.gitignore +32 -0
- package/templates/gitignore/nextjs.gitignore +40 -0
- package/templates/gitignore/react-native.gitignore +43 -0
- package/templates/gitignore/react-vite.gitignore +39 -0
- package/templates/makefile/frontend.mk +31 -0
- package/templates/makefile/laravel.mk +47 -0
- package/templates/makefile/postgres.mk +40 -0
- package/templates/makefile/springboot.mk +125 -0
- package/templates/makefile/supabase.mk +42 -0
package/lib/playbooks.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import fs from 'fs-extra'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Extract section headings from a playbook markdown file.
|
|
6
|
+
* Skips fenced code blocks so `#` inside code fences never pollute the index.
|
|
7
|
+
*/
|
|
8
|
+
export function extractSections(content) {
|
|
9
|
+
const sections = []
|
|
10
|
+
let inFence = false
|
|
11
|
+
let lineNo = 0
|
|
12
|
+
|
|
13
|
+
for (const line of content.split('\n')) {
|
|
14
|
+
lineNo++
|
|
15
|
+
if (/^\s*```/.test(line)) { inFence = !inFence; continue }
|
|
16
|
+
if (inFence) continue
|
|
17
|
+
|
|
18
|
+
const h1 = /^# (?!#)(.+)$/.exec(line)
|
|
19
|
+
const h2 = /^## (?!#)(.+)$/.exec(line)
|
|
20
|
+
if (!h1 && !h2) continue
|
|
21
|
+
|
|
22
|
+
const title = (h1 ? h1[1] : h2[1]).trim()
|
|
23
|
+
const numbered = /^(\d+)\.\s+(.+)$/.exec(title)
|
|
24
|
+
const ref = numbered ? `§ ${numbered[1]} ${numbered[2]}` : title
|
|
25
|
+
sections.push({ level: h1 ? 1 : 2, title, ref, line: lineNo })
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return sections
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Resolve a playbook relative path to an absolute path.
|
|
33
|
+
* Handles nested stack facets and concern playbooks transparently.
|
|
34
|
+
*/
|
|
35
|
+
export async function resolvePlaybook(playbooksDir, file) {
|
|
36
|
+
return path.join(playbooksDir, file)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Collect all unique playbook files referenced by this stack's concerns —
|
|
41
|
+
* including concern files (concerns/*.md) and stack files (stack/*.md).
|
|
42
|
+
*/
|
|
43
|
+
export function collectPlaybookFiles(stack) {
|
|
44
|
+
const files = new Set(stack.playbooks)
|
|
45
|
+
for (const c of stack.concerns) {
|
|
46
|
+
const pb = c.playbook || c._playbook
|
|
47
|
+
if (pb) files.add(pb)
|
|
48
|
+
}
|
|
49
|
+
return [...files]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Build the § ref rows for a single concern entry
|
|
53
|
+
async function concernRows(concern, playbooksDir) {
|
|
54
|
+
const playbookFile = concern.playbook || concern._playbook
|
|
55
|
+
if (!playbookFile) {
|
|
56
|
+
return [`| \`${concern.id}\` | <!-- MISSING playbook --> | — |`]
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const fullPath = await resolvePlaybook(playbooksDir, playbookFile)
|
|
60
|
+
let content
|
|
61
|
+
try {
|
|
62
|
+
content = await fs.readFile(fullPath, 'utf-8')
|
|
63
|
+
} catch {
|
|
64
|
+
return [`| \`${concern.id}\` | <!-- MISSING ${playbookFile} --> | — |`]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const sections = extractSections(content)
|
|
68
|
+
const wanted = concern.sections || []
|
|
69
|
+
|
|
70
|
+
if (!wanted.length) {
|
|
71
|
+
// No sections declared — just link the playbook
|
|
72
|
+
return [`| \`${concern.id}\` | \`playbooks/${playbookFile}\` | — |`]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const normalize = (title) => title
|
|
76
|
+
.replace(/^\d+\.\s+/, '')
|
|
77
|
+
.trim()
|
|
78
|
+
.toLocaleLowerCase('en')
|
|
79
|
+
const matched = sections.filter((section) => {
|
|
80
|
+
const actual = normalize(section.title)
|
|
81
|
+
return wanted.some((title) => {
|
|
82
|
+
const requested = normalize(title)
|
|
83
|
+
return actual === requested || actual.startsWith(`${requested} —`) || actual.startsWith(`${requested} (`)
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
if (!matched.length) {
|
|
87
|
+
return [`| \`${concern.id}\` | \`playbooks/${playbookFile}\` | (section not found) |`]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return matched.map((s) => `| \`${concern.id}\` | \`playbooks/${playbookFile}\` | ${s.ref} |`)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Build RULES.md — the lazy index that maps every concern to its playbook §.
|
|
95
|
+
* Agents read only the § they need, never the whole playbook.
|
|
96
|
+
*/
|
|
97
|
+
export async function buildRulesIndex(stack, catalog, playbooksDir) {
|
|
98
|
+
const lines = []
|
|
99
|
+
|
|
100
|
+
lines.push('# RULES.md')
|
|
101
|
+
lines.push('')
|
|
102
|
+
lines.push(`**Stack:** ${stack.label}`)
|
|
103
|
+
lines.push(`**Platform:** ${stack.platform}`)
|
|
104
|
+
lines.push('')
|
|
105
|
+
lines.push('> This file is a **lazy index** — `concern → playbook §`.')
|
|
106
|
+
lines.push('> Read only the § you need. Never load all playbooks eagerly.')
|
|
107
|
+
lines.push('> Detail lives in `playbooks/`. Concern files live in `playbooks/concerns/`.')
|
|
108
|
+
lines.push('')
|
|
109
|
+
lines.push('---')
|
|
110
|
+
lines.push('')
|
|
111
|
+
|
|
112
|
+
// Deduplicate concerns by id — required wins over optional
|
|
113
|
+
const seen = new Map()
|
|
114
|
+
for (const c of stack.concerns) {
|
|
115
|
+
if (!seen.has(c.id) || c.required) seen.set(c.id, c)
|
|
116
|
+
}
|
|
117
|
+
const concerns = [...seen.values()]
|
|
118
|
+
|
|
119
|
+
const required = concerns.filter((c) => c.required)
|
|
120
|
+
const optional = concerns.filter((c) => !c.required)
|
|
121
|
+
|
|
122
|
+
// ── Always-on Invariants ───────────────────────────────────────────────
|
|
123
|
+
lines.push('## Always-on Invariants')
|
|
124
|
+
lines.push('')
|
|
125
|
+
lines.push('| Concern | Playbook | Section |')
|
|
126
|
+
lines.push('|---------|----------|---------|')
|
|
127
|
+
for (const c of required) {
|
|
128
|
+
for (const row of await concernRows(c, playbooksDir)) lines.push(row)
|
|
129
|
+
}
|
|
130
|
+
lines.push('')
|
|
131
|
+
lines.push('---')
|
|
132
|
+
lines.push('')
|
|
133
|
+
|
|
134
|
+
// ── Optional Concerns ─────────────────────────────────────────────────
|
|
135
|
+
lines.push('## Optional Concerns')
|
|
136
|
+
lines.push('')
|
|
137
|
+
lines.push('| Concern | Playbook | Section | When |')
|
|
138
|
+
lines.push('|---------|----------|---------|------|')
|
|
139
|
+
for (const c of optional) {
|
|
140
|
+
const rows = await concernRows(c, playbooksDir)
|
|
141
|
+
for (const row of rows) {
|
|
142
|
+
lines.push(row.replace(/ \|$/, ` | ${c.when || '—'} |`))
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
lines.push('')
|
|
146
|
+
lines.push('---')
|
|
147
|
+
lines.push('')
|
|
148
|
+
lines.push('**How to use this file:**')
|
|
149
|
+
lines.push('1. Identify which concern your task touches.')
|
|
150
|
+
lines.push('2. Open only the listed playbook at the listed §.')
|
|
151
|
+
lines.push('3. Stop reading when the § ends.')
|
|
152
|
+
lines.push('4. Never read all playbooks eagerly — your context window is finite.')
|
|
153
|
+
lines.push('')
|
|
154
|
+
|
|
155
|
+
return `${lines.join('\n')}\n`
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Copy all stack playbooks AND concern playbooks into the generated project.
|
|
160
|
+
* Preserves the relative path so RULES.md refs stay valid.
|
|
161
|
+
*/
|
|
162
|
+
export async function copySelectedPlaybooks(playbooksDir, dest, stack) {
|
|
163
|
+
const allFiles = collectPlaybookFiles(stack)
|
|
164
|
+
for (const file of allFiles) {
|
|
165
|
+
const src = path.join(playbooksDir, file)
|
|
166
|
+
if (await fs.pathExists(src)) {
|
|
167
|
+
const dst = path.join(dest, 'playbooks', file)
|
|
168
|
+
await fs.ensureDir(path.dirname(dst))
|
|
169
|
+
await fs.copy(src, dst)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
|
|
3
|
+
export function isInsideDirectory(candidate, parent) {
|
|
4
|
+
const relative = path.relative(path.resolve(parent), path.resolve(candidate))
|
|
5
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function projectLocationNotice({ cwd, cliRoot, projectName }) {
|
|
9
|
+
if (!isInsideDirectory(cwd, cliRoot)) return null
|
|
10
|
+
|
|
11
|
+
const generatedPath = path.join(path.resolve(cwd), projectName)
|
|
12
|
+
const suggestedPath = path.join(path.dirname(path.resolve(cliRoot)), projectName)
|
|
13
|
+
return {
|
|
14
|
+
generatedPath,
|
|
15
|
+
suggestedPath,
|
|
16
|
+
message: `This project was created inside the create-win-project repository. Move or cut the generated folder to your normal projects folder outside this repository before you start committing application work.`,
|
|
17
|
+
}
|
|
18
|
+
}
|