create-lumen 2.0.0-alpha.2 → 2.0.0-beta.2
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/README.md +24 -6
- package/bin/cli.js +2 -1
- package/package.json +2 -2
- package/schema/lumen.config.v2.json +29 -0
- package/src/agent-context.js +71 -47
- package/src/cleanup.js +19 -1
- package/src/cli-args.js +131 -5
- package/src/configure.js +8 -4
- package/src/dependencies.js +46 -13
- package/src/engine/capabilities.js +71 -1
- package/src/engine/composition.js +334 -17
- package/src/engine/providers/base-provider.js +4 -4
- package/src/env.js +24 -6
- package/src/main.js +38 -15
- package/src/manifest/emit.js +14 -1
- package/src/manifest/schema.js +30 -0
- package/src/prompts.js +227 -23
- package/templates/bases/next/app-router/js/.provenance.json +19 -0
- package/templates/bases/next/app-router/js/README.md +36 -0
- package/templates/bases/next/app-router/js/jsconfig.json +7 -0
- package/templates/bases/next/app-router/js/next.config.mjs +6 -0
- package/templates/bases/next/app-router/js/package.json +15 -0
- package/templates/bases/next/app-router/js/public/file.svg +1 -0
- package/templates/bases/next/app-router/js/public/globe.svg +1 -0
- package/templates/bases/next/app-router/js/public/next.svg +1 -0
- package/templates/bases/next/app-router/js/public/vercel.svg +1 -0
- package/templates/bases/next/app-router/js/public/window.svg +1 -0
- package/templates/bases/next/app-router/js/src/app/api/hello/route.js +5 -0
- package/templates/bases/next/app-router/js/src/app/blog/[slug]/page.js +33 -0
- package/templates/bases/next/app-router/js/src/app/favicon.ico +0 -0
- package/templates/bases/next/app-router/js/src/app/globals.css +49 -0
- package/templates/bases/next/app-router/js/src/app/layout.js +25 -0
- package/templates/bases/next/app-router/js/src/app/loading.js +15 -0
- package/templates/bases/next/app-router/js/src/app/not-found.js +29 -0
- package/templates/bases/next/app-router/js/src/app/page.js +69 -0
- package/templates/bases/next/app-router/js/src/app/page.module.css +150 -0
- package/templates/bases/next/app-router/ts/.provenance.json +19 -0
- package/templates/bases/next/app-router/ts/README.md +36 -0
- package/templates/bases/next/app-router/ts/next.config.ts +7 -0
- package/templates/bases/next/app-router/ts/package.json +21 -0
- package/templates/bases/next/app-router/ts/public/file.svg +1 -0
- package/templates/bases/next/app-router/ts/public/globe.svg +1 -0
- package/templates/bases/next/app-router/ts/public/next.svg +1 -0
- package/templates/bases/next/app-router/ts/public/vercel.svg +1 -0
- package/templates/bases/next/app-router/ts/public/window.svg +1 -0
- package/templates/bases/next/app-router/ts/src/app/api/hello/route.ts +5 -0
- package/templates/bases/next/app-router/ts/src/app/blog/[slug]/page.tsx +38 -0
- package/templates/bases/next/app-router/ts/src/app/favicon.ico +0 -0
- package/templates/bases/next/app-router/ts/src/app/globals.css +49 -0
- package/templates/bases/next/app-router/ts/src/app/layout.tsx +30 -0
- package/templates/bases/next/app-router/ts/src/app/loading.tsx +15 -0
- package/templates/bases/next/app-router/ts/src/app/not-found.tsx +29 -0
- package/templates/bases/next/app-router/ts/src/app/page.module.css +150 -0
- package/templates/bases/next/app-router/ts/src/app/page.tsx +69 -0
- package/templates/bases/next/app-router/ts/tsconfig.json +34 -0
- package/templates/bases/next/pages-router/js/.provenance.json +19 -0
- package/templates/bases/next/pages-router/js/README.md +40 -0
- package/templates/bases/next/pages-router/js/jsconfig.json +7 -0
- package/templates/bases/next/pages-router/js/next.config.mjs +7 -0
- package/templates/bases/next/pages-router/js/package.json +15 -0
- package/templates/bases/next/pages-router/js/public/favicon.ico +0 -0
- package/templates/bases/next/pages-router/js/public/file.svg +1 -0
- package/templates/bases/next/pages-router/js/public/globe.svg +1 -0
- package/templates/bases/next/pages-router/js/public/next.svg +1 -0
- package/templates/bases/next/pages-router/js/public/vercel.svg +1 -0
- package/templates/bases/next/pages-router/js/public/window.svg +1 -0
- package/templates/bases/next/pages-router/js/src/pages/404.js +35 -0
- package/templates/bases/next/pages-router/js/src/pages/500.js +35 -0
- package/templates/bases/next/pages-router/js/src/pages/_app.js +20 -0
- package/templates/bases/next/pages-router/js/src/pages/_document.js +15 -0
- package/templates/bases/next/pages-router/js/src/pages/api/hello.js +5 -0
- package/templates/bases/next/pages-router/js/src/pages/blog/[slug].js +41 -0
- package/templates/bases/next/pages-router/js/src/pages/index.js +77 -0
- package/templates/bases/next/pages-router/js/src/styles/Home.module.css +149 -0
- package/templates/bases/next/pages-router/js/src/styles/globals.css +42 -0
- package/templates/bases/next/pages-router/ts/.provenance.json +19 -0
- package/templates/bases/next/pages-router/ts/README.md +40 -0
- package/templates/bases/next/pages-router/ts/next.config.ts +8 -0
- package/templates/bases/next/pages-router/ts/package.json +21 -0
- package/templates/bases/next/pages-router/ts/public/favicon.ico +0 -0
- package/templates/bases/next/pages-router/ts/public/file.svg +1 -0
- package/templates/bases/next/pages-router/ts/public/globe.svg +1 -0
- package/templates/bases/next/pages-router/ts/public/next.svg +1 -0
- package/templates/bases/next/pages-router/ts/public/vercel.svg +1 -0
- package/templates/bases/next/pages-router/ts/public/window.svg +1 -0
- package/templates/bases/next/pages-router/ts/src/pages/404.tsx +35 -0
- package/templates/bases/next/pages-router/ts/src/pages/500.tsx +35 -0
- package/templates/bases/next/pages-router/ts/src/pages/_app.tsx +21 -0
- package/templates/bases/next/pages-router/ts/src/pages/_document.tsx +15 -0
- package/templates/bases/next/pages-router/ts/src/pages/api/hello.ts +13 -0
- package/templates/bases/next/pages-router/ts/src/pages/blog/[slug].tsx +46 -0
- package/templates/bases/next/pages-router/ts/src/pages/index.tsx +77 -0
- package/templates/bases/next/pages-router/ts/src/styles/Home.module.css +149 -0
- package/templates/bases/next/pages-router/ts/src/styles/globals.css +42 -0
- package/templates/bases/next/pages-router/ts/tsconfig.json +29 -0
- package/templates/conditional/database/prisma/db.js +14 -0
- package/templates/conditional/database/prisma/db.ts +16 -0
- package/templates/conditional/database/prisma/schema.prisma +9 -0
- package/templates/conditional/database/prisma/seed.js +16 -0
- package/templates/conditional/database/prisma/seed.ts +16 -0
- package/templates/conditional/docker/.dockerignore +10 -0
- package/templates/conditional/docker/Dockerfile +57 -0
- package/templates/conditional/docker/docker-compose.yml +35 -0
- package/templates/conditional/linting/biome/biome.json +31 -0
- package/templates/conditional/next/config/cloudflare/next.config.mjs +6 -0
- package/templates/conditional/next/config/cloudflare/next.config.ts +7 -0
- package/templates/conditional/next/config/node/next.config.mjs +6 -0
- package/templates/conditional/next/config/node/next.config.ts +7 -0
- package/templates/conditional/next/config/static/next.config.mjs +6 -0
- package/templates/conditional/next/config/static/next.config.ts +7 -0
- package/templates/conditional/next/config/turbopack/next.config.mjs +7 -0
- package/templates/conditional/next/config/turbopack/next.config.ts +8 -0
- package/templates/conditional/next/config/vercel/next.config.mjs +6 -0
- package/templates/conditional/next/config/vercel/next.config.ts +7 -0
- package/templates/conditional/next/config/webpack/next.config.mjs +9 -0
- package/templates/conditional/next/config/webpack/next.config.ts +10 -0
- package/templates/conditional/next/font/BUILD_SIZE_NOTE.md +17 -0
- package/templates/conditional/next/font/app-router/src/app/layout.js +25 -0
- package/templates/conditional/next/font/app-router/src/app/layout.tsx +30 -0
- package/templates/conditional/next/font/pages-router/src/pages/_app.js +20 -0
- package/templates/conditional/next/font/pages-router/src/pages/_app.tsx +21 -0
- package/templates/conditional/next/font/pages-router/src/pages/_document.js +18 -0
- package/templates/conditional/next/font/pages-router/src/pages/_document.tsx +18 -0
- package/templates/conditional/next/font/src/app/layout.js +25 -0
- package/templates/conditional/next/font/src/app/layout.tsx +30 -0
- package/templates/conditional/next/font/src/lib/fonts.js +11 -0
- package/templates/conditional/next/font/src/lib/fonts.ts +11 -0
- package/templates/conditional/next/font/src/pages/_app.js +20 -0
- package/templates/conditional/next/font/src/pages/_app.tsx +21 -0
- package/templates/conditional/next/font/src/pages/_document.js +18 -0
- package/templates/conditional/next/font/src/pages/_document.tsx +18 -0
- package/templates/conditional/next/image/BUILD_SIZE_NOTE.md +15 -0
- package/templates/conditional/next/image/app-router/src/components/ImageExample.jsx +34 -0
- package/templates/conditional/next/image/app-router/src/components/ImageExample.tsx +44 -0
- package/templates/conditional/next/image/pages-router/src/components/ImageExample.jsx +34 -0
- package/templates/conditional/next/image/pages-router/src/components/ImageExample.tsx +44 -0
- package/templates/conditional/next/image/src/components/ImageExample.jsx +44 -0
- package/templates/conditional/next/image/src/components/ImageExample.tsx +54 -0
- package/templates/conditional/next/image/src/components/image/index.js +1 -0
- package/templates/conditional/next/image/src/components/image/index.ts +2 -0
- package/templates/conditional/next/route-groups/js/src/app/(admin)/layout.jsx +23 -0
- package/templates/conditional/next/route-groups/js/src/app/(auth)/layout.jsx +17 -0
- package/templates/conditional/next/route-groups/js/src/app/(private)/layout.jsx +27 -0
- package/templates/conditional/next/route-groups/js/src/app/(public)/layout.jsx +28 -0
- package/templates/conditional/next/route-groups/js/src/app/(public)/page.jsx +38 -0
- package/templates/conditional/next/route-groups/js/src/middleware.js +48 -0
- package/templates/conditional/next/route-groups/ts/src/app/(admin)/layout.tsx +28 -0
- package/templates/conditional/next/route-groups/ts/src/app/(auth)/layout.tsx +22 -0
- package/templates/conditional/next/route-groups/ts/src/app/(private)/layout.tsx +32 -0
- package/templates/conditional/next/route-groups/ts/src/app/(public)/layout.tsx +33 -0
- package/templates/conditional/next/route-groups/ts/src/app/(public)/page.tsx +38 -0
- package/templates/conditional/next/route-groups/ts/src/middleware.ts +49 -0
- package/templates/conditional/next/server-components/README.md +13 -0
- package/templates/conditional/next/server-components/eslint.config.mjs +47 -0
- package/templates/conditional/next/server-components/rules/no-server-imports-in-client.js +89 -0
- package/templates/conditional/next/server-components/rules/no-server-imports-in-client.mjs +88 -0
- package/templates/conditional/next/server-components/src/components/boundaries/ClientCounter.jsx +48 -0
- package/templates/conditional/next/server-components/src/components/boundaries/ClientCounter.tsx +53 -0
- package/templates/conditional/next/server-components/src/components/boundaries/ServerCard.jsx +41 -0
- package/templates/conditional/next/server-components/src/components/boundaries/ServerCard.tsx +46 -0
- package/templates/conditional/next/server-components/src/components/boundaries/index.js +2 -0
- package/templates/conditional/next/server-components/src/components/boundaries/index.ts +4 -0
- package/templates/conditional/testing/jest/next/app-router/src/__tests__/page.test.jsx +10 -0
- package/templates/conditional/testing/jest/next/app-router/src/__tests__/page.test.tsx +10 -0
- package/templates/conditional/testing/jest/next/jest.config.mjs +20 -0
- package/templates/conditional/testing/jest/next/jest.config.ts +20 -0
- package/templates/conditional/testing/jest/next/pages-router/src/__tests__/index.test.jsx +10 -0
- package/templates/conditional/testing/jest/next/pages-router/src/__tests__/index.test.tsx +10 -0
- package/templates/conditional/testing/vitest/next/app-router/src/__tests__/page.test.jsx +11 -0
- package/templates/conditional/testing/vitest/next/app-router/src/__tests__/page.test.tsx +11 -0
- package/templates/conditional/testing/vitest/next/pages-router/src/__tests__/index.test.jsx +11 -0
- package/templates/conditional/testing/vitest/next/pages-router/src/__tests__/index.test.tsx +11 -0
- package/templates/conditional/testing/vitest/next/vitest.config.js +16 -0
- package/templates/conditional/testing/vitest/next/vitest.config.ts +16 -0
|
@@ -41,9 +41,13 @@ export const TOOLING_ORDER = [
|
|
|
41
41
|
"router",
|
|
42
42
|
"icons",
|
|
43
43
|
"api",
|
|
44
|
+
"database",
|
|
44
45
|
"testing",
|
|
45
46
|
"linter",
|
|
46
47
|
"feature-script",
|
|
48
|
+
"next-config",
|
|
49
|
+
"route-groups",
|
|
50
|
+
"docker",
|
|
47
51
|
"formatter",
|
|
48
52
|
];
|
|
49
53
|
|
|
@@ -149,6 +153,15 @@ export function resolveFragments(responses = {}, templatesDir) {
|
|
|
149
153
|
try { await fsp.rm(path.join(projectPath, "src", "App.jsx"), { force: true }); } catch {}
|
|
150
154
|
try { await fsp.rm(path.join(projectPath, "src", "index.css"), { force: true }); } catch {}
|
|
151
155
|
try { await fsp.rm(path.join(projectPath, "src", "App.css"), { force: true }); } catch {}
|
|
156
|
+
if (fw.name === "next") {
|
|
157
|
+
// Next cleanup: remove Vite defaults without touching app/
|
|
158
|
+
try { await fsp.rm(path.join(projectPath, "src", "main.tsx"), { force: true }); } catch {}
|
|
159
|
+
try { await fsp.rm(path.join(projectPath, "src", "main.jsx"), { force: true }); } catch {}
|
|
160
|
+
try { await fsp.rm(path.join(projectPath, "index.html"), { force: true }); } catch {}
|
|
161
|
+
try { await fsp.rm(path.join(projectPath, "vite.config.ts"), { force: true }); } catch {}
|
|
162
|
+
try { await fsp.rm(path.join(projectPath, "vite.config.js"), { force: true }); } catch {}
|
|
163
|
+
try { await fsp.rm(path.join(projectPath, "public", "vite.svg"), { force: true }); } catch {}
|
|
164
|
+
}
|
|
152
165
|
},
|
|
153
166
|
});
|
|
154
167
|
|
|
@@ -164,6 +177,19 @@ export function resolveFragments(responses = {}, templatesDir) {
|
|
|
164
177
|
sourceDir: archSrcDir,
|
|
165
178
|
targetDir: "src",
|
|
166
179
|
action: async ({ projectPath }) => {
|
|
180
|
+
if (fw.name === "next") {
|
|
181
|
+
if (fw.variant === "pages-router") {
|
|
182
|
+
try { await fsp.rm(path.join(projectPath, "src", "app"), { recursive: true, force: true }); } catch {}
|
|
183
|
+
} else {
|
|
184
|
+
// Next App Router: preserve file-system routes; purge React/Vite-specific application layer
|
|
185
|
+
try { await fsp.rm(path.join(projectPath, "src", "app", "App.tsx"), { force: true }); } catch {}
|
|
186
|
+
try { await fsp.rm(path.join(projectPath, "src", "app", "App.jsx"), { force: true }); } catch {}
|
|
187
|
+
try { await fsp.rm(path.join(projectPath, "src", "app", "hooks"), { recursive: true, force: true }); } catch {}
|
|
188
|
+
try { await fsp.rm(path.join(projectPath, "src", "app", "contexts"), { recursive: true, force: true }); } catch {}
|
|
189
|
+
try { await fsp.rm(path.join(projectPath, "src", "app", "providers"), { recursive: true, force: true }); } catch {}
|
|
190
|
+
}
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
167
193
|
// Copy architecture main entry
|
|
168
194
|
try {
|
|
169
195
|
await fsp.access(archMainSrc);
|
|
@@ -194,6 +220,16 @@ export function resolveFragments(responses = {}, templatesDir) {
|
|
|
194
220
|
id: `styling:${cssFramework}:components`,
|
|
195
221
|
sourceDir: componentStylesDir,
|
|
196
222
|
targetDir: "src",
|
|
223
|
+
action: async ({ projectPath }) => {
|
|
224
|
+
if (fw.name === "next") {
|
|
225
|
+
if (fw.variant === "pages-router") {
|
|
226
|
+
try { await fsp.rm(path.join(projectPath, "src", "app"), { recursive: true, force: true }); } catch {}
|
|
227
|
+
} else {
|
|
228
|
+
try { await fsp.rm(path.join(projectPath, "src", "app", "App.tsx"), { force: true }); } catch {}
|
|
229
|
+
try { await fsp.rm(path.join(projectPath, "src", "app", "App.jsx"), { force: true }); } catch {}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
},
|
|
197
233
|
});
|
|
198
234
|
}
|
|
199
235
|
|
|
@@ -210,6 +246,7 @@ export function resolveFragments(responses = {}, templatesDir) {
|
|
|
210
246
|
sourceDir: path.join(templatesDir, "conditional", "state", "redux", architecture, "src"),
|
|
211
247
|
targetDir: "src",
|
|
212
248
|
action: async ({ projectPath }) => {
|
|
249
|
+
if (fw.name === "next") return;
|
|
213
250
|
await updateMainWithProvider(projectPath, "redux", architecture, ext);
|
|
214
251
|
},
|
|
215
252
|
});
|
|
@@ -306,6 +343,106 @@ export function resolveFragments(responses = {}, templatesDir) {
|
|
|
306
343
|
id: `tooling:api:${responses.apiClient}`,
|
|
307
344
|
sourceDir: apiDir,
|
|
308
345
|
targetDir: "src",
|
|
346
|
+
action: async ({ projectPath }) => {
|
|
347
|
+
if (fw.name === "next") {
|
|
348
|
+
const configCandidates = [
|
|
349
|
+
path.join(projectPath, "src", "config", `axios.config.${extConfig}`),
|
|
350
|
+
path.join(projectPath, "src", "shared", "lib", "axios", `api.config.${extConfig}`),
|
|
351
|
+
path.join(projectPath, "src", "config", `api.config.${extConfig}`),
|
|
352
|
+
path.join(projectPath, "src", "shared", "api", `api.config.${extConfig}`),
|
|
353
|
+
];
|
|
354
|
+
for (const cfgPath of configCandidates) {
|
|
355
|
+
try {
|
|
356
|
+
let content = await fsp.readFile(cfgPath, "utf8");
|
|
357
|
+
if (content.includes("import.meta.env.VITE_API_URL")) {
|
|
358
|
+
content = content.replace(
|
|
359
|
+
/import\.meta\.env\.VITE_API_URL/g,
|
|
360
|
+
"process.env.NEXT_PUBLIC_API_URL"
|
|
361
|
+
);
|
|
362
|
+
await fsp.writeFile(cfgPath, content, "utf8");
|
|
363
|
+
}
|
|
364
|
+
} catch {}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// 5.45 Database (Prisma)
|
|
372
|
+
if (responses.database === "prisma") {
|
|
373
|
+
fragments.push({
|
|
374
|
+
scope: FRAGMENT_SCOPES.TOOLING,
|
|
375
|
+
category: "database",
|
|
376
|
+
id: "tooling:database:prisma",
|
|
377
|
+
scripts: {
|
|
378
|
+
"db:generate": "prisma generate",
|
|
379
|
+
"db:push": "prisma db push",
|
|
380
|
+
"db:seed": "prisma db seed",
|
|
381
|
+
"db:studio": "prisma studio",
|
|
382
|
+
},
|
|
383
|
+
action: async ({ projectPath, templatesDir: tDir }) => {
|
|
384
|
+
const prismaTplDir = path.join(tDir, "conditional", "database", "prisma");
|
|
385
|
+
const prismaDir = path.join(projectPath, "prisma");
|
|
386
|
+
await fsp.mkdir(prismaDir, { recursive: true });
|
|
387
|
+
|
|
388
|
+
// Copy schema.prisma
|
|
389
|
+
const schemaSrc = path.join(prismaTplDir, "schema.prisma");
|
|
390
|
+
const schemaDest = path.join(prismaDir, "schema.prisma");
|
|
391
|
+
try {
|
|
392
|
+
await fsp.access(schemaSrc);
|
|
393
|
+
await fsp.copyFile(schemaSrc, schemaDest);
|
|
394
|
+
} catch {}
|
|
395
|
+
|
|
396
|
+
// Copy seed file: prisma/seed.ts or prisma/seed.js
|
|
397
|
+
const seedFileName = language === "ts" ? "seed.ts" : "seed.js";
|
|
398
|
+
const seedSrc = path.join(prismaTplDir, seedFileName);
|
|
399
|
+
const seedDest = path.join(prismaDir, seedFileName);
|
|
400
|
+
try {
|
|
401
|
+
await fsp.access(seedSrc);
|
|
402
|
+
await fsp.copyFile(seedSrc, seedDest);
|
|
403
|
+
} catch {}
|
|
404
|
+
|
|
405
|
+
// Copy client singleton: src/lib/db.ts or src/lib/db.js
|
|
406
|
+
const libDir = path.join(projectPath, "src", "lib");
|
|
407
|
+
await fsp.mkdir(libDir, { recursive: true });
|
|
408
|
+
const dbFileName = language === "ts" ? "db.ts" : "db.js";
|
|
409
|
+
const dbSrc = path.join(prismaTplDir, dbFileName);
|
|
410
|
+
const dbDest = path.join(libDir, dbFileName);
|
|
411
|
+
try {
|
|
412
|
+
await fsp.access(dbSrc);
|
|
413
|
+
await fsp.copyFile(dbSrc, dbDest);
|
|
414
|
+
} catch {}
|
|
415
|
+
|
|
416
|
+
// Configure prisma.seed in package.json
|
|
417
|
+
const pkgPath = path.join(projectPath, "package.json");
|
|
418
|
+
try {
|
|
419
|
+
const content = await fsp.readFile(pkgPath, "utf8");
|
|
420
|
+
const pkg = JSON.parse(content);
|
|
421
|
+
pkg.prisma = {
|
|
422
|
+
seed: language === "ts" ? "tsx prisma/seed.ts" : "node prisma/seed.js",
|
|
423
|
+
};
|
|
424
|
+
await fsp.writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
425
|
+
} catch {}
|
|
426
|
+
|
|
427
|
+
// Append DATABASE_URL and DIRECT_URL to .env.example
|
|
428
|
+
const envExamplePath = path.join(projectPath, ".env.example");
|
|
429
|
+
const envLines = [
|
|
430
|
+
"",
|
|
431
|
+
"# Database (Prisma + PostgreSQL / Supabase)",
|
|
432
|
+
'DATABASE_URL="postgresql://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:5432/postgres"',
|
|
433
|
+
'DIRECT_URL="postgresql://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:5432/postgres"',
|
|
434
|
+
].join("\n");
|
|
435
|
+
try {
|
|
436
|
+
let envContent = "";
|
|
437
|
+
try {
|
|
438
|
+
envContent = await fsp.readFile(envExamplePath, "utf8");
|
|
439
|
+
} catch {}
|
|
440
|
+
if (!envContent.includes("DATABASE_URL")) {
|
|
441
|
+
envContent = envContent.trimEnd() + "\n" + envLines.trimStart() + "\n";
|
|
442
|
+
await writeFileRecursive(envExamplePath, envContent);
|
|
443
|
+
}
|
|
444
|
+
} catch {}
|
|
445
|
+
},
|
|
309
446
|
});
|
|
310
447
|
}
|
|
311
448
|
|
|
@@ -325,11 +462,26 @@ export function resolveFragments(responses = {}, templatesDir) {
|
|
|
325
462
|
scripts: testScripts,
|
|
326
463
|
action: async ({ projectPath }) => {
|
|
327
464
|
// Config file to project root
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
465
|
+
if (testingFramework === "jest" && fw.name === "next") {
|
|
466
|
+
const nextConfigExt = language === "ts" ? "ts" : "mjs";
|
|
467
|
+
const configSrc = path.join(testingDir, "next", `jest.config.${nextConfigExt}`);
|
|
468
|
+
try {
|
|
469
|
+
await fsp.access(configSrc);
|
|
470
|
+
await fsp.copyFile(configSrc, path.join(projectPath, `jest.config.${nextConfigExt}`));
|
|
471
|
+
} catch {}
|
|
472
|
+
} else if (testingFramework === "vitest" && fw.name === "next") {
|
|
473
|
+
const configSrc = path.join(testingDir, "next", `vitest.config.${extConfig}`);
|
|
474
|
+
try {
|
|
475
|
+
await fsp.access(configSrc);
|
|
476
|
+
await fsp.copyFile(configSrc, path.join(projectPath, `vitest.config.${extConfig}`));
|
|
477
|
+
} catch {}
|
|
478
|
+
} else {
|
|
479
|
+
const configSrc = path.join(testingDir, `${testingFramework}.config.${extConfig}`);
|
|
480
|
+
try {
|
|
481
|
+
await fsp.access(configSrc);
|
|
482
|
+
await fsp.copyFile(configSrc, path.join(projectPath, `${testingFramework}.config.${extConfig}`));
|
|
483
|
+
} catch {}
|
|
484
|
+
}
|
|
333
485
|
|
|
334
486
|
// Setup file to src/test/setup.*
|
|
335
487
|
const setupSrc = path.join(testingDir, `src/test/setup.${extConfig}`);
|
|
@@ -345,20 +497,39 @@ export function resolveFragments(responses = {}, templatesDir) {
|
|
|
345
497
|
await fsp.access(jestSetupSrc);
|
|
346
498
|
await fsp.copyFile(jestSetupSrc, path.join(projectPath, `jest.setup.${extConfig}`));
|
|
347
499
|
} catch {}
|
|
348
|
-
|
|
500
|
+
if (fw.name !== "next") {
|
|
501
|
+
const babelSrc = path.join(testingDir, "babel.config.cjs");
|
|
502
|
+
try {
|
|
503
|
+
await fsp.access(babelSrc);
|
|
504
|
+
await fsp.copyFile(babelSrc, path.join(projectPath, "babel.config.cjs"));
|
|
505
|
+
} catch {}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (fw.name === "next") {
|
|
510
|
+
const testFileName = fw.variant === "pages-router" ? "index" : "page";
|
|
511
|
+
const testSrc = path.join(
|
|
512
|
+
testingDir,
|
|
513
|
+
"next",
|
|
514
|
+
fw.variant,
|
|
515
|
+
"src",
|
|
516
|
+
"__tests__",
|
|
517
|
+
`${testFileName}.test.${ext}`
|
|
518
|
+
);
|
|
349
519
|
try {
|
|
350
|
-
await fsp.access(
|
|
351
|
-
|
|
520
|
+
await fsp.access(testSrc);
|
|
521
|
+
const testDest = path.join(projectPath, "src", "__tests__", `${testFileName}.test.${ext}`);
|
|
522
|
+
await writeFileRecursive(testDest, await fsp.readFile(testSrc, "utf8"));
|
|
523
|
+
} catch {}
|
|
524
|
+
} else {
|
|
525
|
+
// Architecture-scoped test file
|
|
526
|
+
const testSrc = path.join(testingDir, architecture, `src/__tests__/App.test.${ext}`);
|
|
527
|
+
try {
|
|
528
|
+
await fsp.access(testSrc);
|
|
529
|
+
const testDest = path.join(projectPath, "src", "__tests__", `App.test.${ext}`);
|
|
530
|
+
await writeFileRecursive(testDest, await fsp.readFile(testSrc, "utf8"));
|
|
352
531
|
} catch {}
|
|
353
532
|
}
|
|
354
|
-
|
|
355
|
-
// Architecture-scoped test file
|
|
356
|
-
const testSrc = path.join(testingDir, architecture, `src/__tests__/App.test.${ext}`);
|
|
357
|
-
try {
|
|
358
|
-
await fsp.access(testSrc);
|
|
359
|
-
const testDest = path.join(projectPath, "src", "__tests__", `App.test.${ext}`);
|
|
360
|
-
await writeFileRecursive(testDest, await fsp.readFile(testSrc, "utf8"));
|
|
361
|
-
} catch {}
|
|
362
533
|
},
|
|
363
534
|
});
|
|
364
535
|
}
|
|
@@ -406,6 +577,23 @@ export function resolveFragments(responses = {}, templatesDir) {
|
|
|
406
577
|
} catch {}
|
|
407
578
|
},
|
|
408
579
|
});
|
|
580
|
+
} else if (responses.linter === "biome") {
|
|
581
|
+
fragments.push({
|
|
582
|
+
scope: FRAGMENT_SCOPES.TOOLING,
|
|
583
|
+
category: "linter",
|
|
584
|
+
id: "tooling:linter:biome",
|
|
585
|
+
scripts: {
|
|
586
|
+
lint: "biome check .",
|
|
587
|
+
"lint:fix": "biome check --write .",
|
|
588
|
+
},
|
|
589
|
+
action: async ({ projectPath }) => {
|
|
590
|
+
const configSrc = path.join(templatesDir, "conditional", "linting", "biome", "biome.json");
|
|
591
|
+
try {
|
|
592
|
+
await fsp.access(configSrc);
|
|
593
|
+
await fsp.copyFile(configSrc, path.join(projectPath, "biome.json"));
|
|
594
|
+
} catch {}
|
|
595
|
+
},
|
|
596
|
+
});
|
|
409
597
|
}
|
|
410
598
|
|
|
411
599
|
// 5.7 Feature Script (Feature-based only)
|
|
@@ -437,7 +625,136 @@ export function resolveFragments(responses = {}, templatesDir) {
|
|
|
437
625
|
});
|
|
438
626
|
}
|
|
439
627
|
|
|
440
|
-
// 5.
|
|
628
|
+
// 5.75 Next.js Configuration & Adapter Overlays
|
|
629
|
+
if (fw.name === "next") {
|
|
630
|
+
const nextScripts = {};
|
|
631
|
+
if (responses.bundler === "turbopack") {
|
|
632
|
+
nextScripts.dev = "next dev --turbopack";
|
|
633
|
+
} else if (responses.bundler === "webpack") {
|
|
634
|
+
nextScripts.dev = "next dev --webpack";
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
fragments.push({
|
|
638
|
+
scope: FRAGMENT_SCOPES.TOOLING,
|
|
639
|
+
category: "next-config",
|
|
640
|
+
id: "tooling:next:config",
|
|
641
|
+
scripts: nextScripts,
|
|
642
|
+
action: async ({ projectPath }) => {
|
|
643
|
+
const configExt = language === "ts" ? "ts" : "mjs";
|
|
644
|
+
|
|
645
|
+
// Apply adapter overlay if specified and not "none"
|
|
646
|
+
if (responses.adapter && responses.adapter !== "none") {
|
|
647
|
+
const adapterConfigSrc = path.join(
|
|
648
|
+
templatesDir,
|
|
649
|
+
"conditional",
|
|
650
|
+
"next",
|
|
651
|
+
"config",
|
|
652
|
+
responses.adapter,
|
|
653
|
+
`next.config.${configExt}`
|
|
654
|
+
);
|
|
655
|
+
try {
|
|
656
|
+
await fsp.access(adapterConfigSrc);
|
|
657
|
+
await fsp.copyFile(adapterConfigSrc, path.join(projectPath, `next.config.${configExt}`));
|
|
658
|
+
} catch {}
|
|
659
|
+
} else if (responses.bundler === "turbopack" || responses.bundler === "webpack") {
|
|
660
|
+
// If no adapter specified, but bundler config overlay exists, apply bundler overlay
|
|
661
|
+
const bundlerConfigSrc = path.join(
|
|
662
|
+
templatesDir,
|
|
663
|
+
"conditional",
|
|
664
|
+
"next",
|
|
665
|
+
"config",
|
|
666
|
+
responses.bundler,
|
|
667
|
+
`next.config.${configExt}`
|
|
668
|
+
);
|
|
669
|
+
try {
|
|
670
|
+
await fsp.access(bundlerConfigSrc);
|
|
671
|
+
await fsp.copyFile(bundlerConfigSrc, path.join(projectPath, `next.config.${configExt}`));
|
|
672
|
+
} catch {}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// Apply reactCompiler flag to next.config if enabled
|
|
676
|
+
if (responses.reactCompiler === true) {
|
|
677
|
+
const targetConfigPath = path.join(projectPath, `next.config.${configExt}`);
|
|
678
|
+
try {
|
|
679
|
+
let content = await fsp.readFile(targetConfigPath, "utf8");
|
|
680
|
+
if (!content.includes("reactCompiler")) {
|
|
681
|
+
content = content.replace(
|
|
682
|
+
/const nextConfig(?::\s*NextConfig)?\s*=\s*\{/,
|
|
683
|
+
(match) => `${match}\n reactCompiler: true,`
|
|
684
|
+
);
|
|
685
|
+
await fsp.writeFile(targetConfigPath, content, "utf8");
|
|
686
|
+
}
|
|
687
|
+
} catch {}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Apply standalone output to next.config if docker is enabled
|
|
691
|
+
if (responses.docker === true) {
|
|
692
|
+
const targetConfigPath = path.join(projectPath, `next.config.${configExt}`);
|
|
693
|
+
try {
|
|
694
|
+
let content = await fsp.readFile(targetConfigPath, "utf8");
|
|
695
|
+
if (!content.includes('"standalone"') && !content.includes("'standalone'")) {
|
|
696
|
+
content = content.replace(
|
|
697
|
+
/const nextConfig(?::\s*NextConfig)?\s*=\s*\{/,
|
|
698
|
+
(match) => `${match}\n output: "standalone",`
|
|
699
|
+
);
|
|
700
|
+
await fsp.writeFile(targetConfigPath, content, "utf8");
|
|
701
|
+
}
|
|
702
|
+
} catch {}
|
|
703
|
+
}
|
|
704
|
+
},
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// 5.8 Route Groups Preset & Auth Proxy Template (#64)
|
|
709
|
+
if (responses.routeGroups === true && fw.name === "next" && fw.variant === "app-router") {
|
|
710
|
+
fragments.push({
|
|
711
|
+
scope: FRAGMENT_SCOPES.TOOLING,
|
|
712
|
+
category: "route-groups",
|
|
713
|
+
id: "tooling:next:route-groups",
|
|
714
|
+
sourceDir: path.join(templatesDir, "conditional", "next", "route-groups", language),
|
|
715
|
+
targetDir: "",
|
|
716
|
+
action: async ({ projectPath }) => {
|
|
717
|
+
// Remove colliding root page so it does not collide with (public)/page
|
|
718
|
+
try {
|
|
719
|
+
await fsp.rm(path.join(projectPath, "src", "app", `page.${ext}`), { force: true });
|
|
720
|
+
} catch {}
|
|
721
|
+
const oppositeExt = language === "ts" ? "jsx" : "tsx";
|
|
722
|
+
try {
|
|
723
|
+
await fsp.rm(path.join(projectPath, "src", "app", `page.${oppositeExt}`), { force: true });
|
|
724
|
+
} catch {}
|
|
725
|
+
// Also remove page.js / page.tsx in case base provider was named page.js
|
|
726
|
+
try {
|
|
727
|
+
await fsp.rm(path.join(projectPath, "src", "app", "page.js"), { force: true });
|
|
728
|
+
} catch {}
|
|
729
|
+
try {
|
|
730
|
+
await fsp.rm(path.join(projectPath, "src", "app", "page.tsx"), { force: true });
|
|
731
|
+
} catch {}
|
|
732
|
+
},
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// 5.9 Docker (Multi-stage Dockerfile and docker-compose for Next.js standalone)
|
|
737
|
+
if (responses.docker === true && hasCapability(fw, CAPABILITIES.DOCKER)) {
|
|
738
|
+
fragments.push({
|
|
739
|
+
scope: FRAGMENT_SCOPES.TOOLING,
|
|
740
|
+
category: "docker",
|
|
741
|
+
id: "tooling:docker",
|
|
742
|
+
action: async ({ projectPath }) => {
|
|
743
|
+
const dockerSrcDir = path.join(templatesDir, "conditional", "docker");
|
|
744
|
+
const filesToCopy = ["Dockerfile", ".dockerignore", "docker-compose.yml"];
|
|
745
|
+
for (const file of filesToCopy) {
|
|
746
|
+
const srcFile = path.join(dockerSrcDir, file);
|
|
747
|
+
const destFile = path.join(projectPath, file);
|
|
748
|
+
try {
|
|
749
|
+
await fsp.access(srcFile);
|
|
750
|
+
await fsp.copyFile(srcFile, destFile);
|
|
751
|
+
} catch {}
|
|
752
|
+
}
|
|
753
|
+
},
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// 5.10 Formatter
|
|
441
758
|
if (responses.formatter && responses.formatter !== "none") {
|
|
442
759
|
const isPrettier = responses.formatter === "prettier";
|
|
443
760
|
const configFile = isPrettier ? ".prettierrc" : ".oxfmtrc.json";
|
|
@@ -125,7 +125,7 @@ export class NextAppRouterProvider extends BaseProvider {
|
|
|
125
125
|
getPackageSet({ options = {} } = {}) {
|
|
126
126
|
const isTs = options.language === "ts";
|
|
127
127
|
const deps = {
|
|
128
|
-
next: "^
|
|
128
|
+
next: "^16.0.0",
|
|
129
129
|
react: "^19.0.0",
|
|
130
130
|
"react-dom": "^19.0.0",
|
|
131
131
|
};
|
|
@@ -134,7 +134,7 @@ export class NextAppRouterProvider extends BaseProvider {
|
|
|
134
134
|
devDeps.typescript = "^5.7.0";
|
|
135
135
|
devDeps["@types/react"] = "^19.0.0";
|
|
136
136
|
devDeps["@types/react-dom"] = "^19.0.0";
|
|
137
|
-
devDeps["@types/node"] = "^
|
|
137
|
+
devDeps["@types/node"] = "^22.0.0";
|
|
138
138
|
}
|
|
139
139
|
return {
|
|
140
140
|
dependencies: deps,
|
|
@@ -159,7 +159,7 @@ export class NextPagesRouterProvider extends BaseProvider {
|
|
|
159
159
|
getPackageSet({ options = {} } = {}) {
|
|
160
160
|
const isTs = options.language === "ts";
|
|
161
161
|
const deps = {
|
|
162
|
-
next: "^
|
|
162
|
+
next: "^16.0.0",
|
|
163
163
|
react: "^19.0.0",
|
|
164
164
|
"react-dom": "^19.0.0",
|
|
165
165
|
};
|
|
@@ -168,7 +168,7 @@ export class NextPagesRouterProvider extends BaseProvider {
|
|
|
168
168
|
devDeps.typescript = "^5.7.0";
|
|
169
169
|
devDeps["@types/react"] = "^19.0.0";
|
|
170
170
|
devDeps["@types/react-dom"] = "^19.0.0";
|
|
171
|
-
devDeps["@types/node"] = "^
|
|
171
|
+
devDeps["@types/node"] = "^22.0.0";
|
|
172
172
|
}
|
|
173
173
|
return {
|
|
174
174
|
dependencies: deps,
|
package/src/env.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import { promises as fsp } from "fs";
|
|
3
|
+
import { normalizeFramework } from "./engine/capabilities.js";
|
|
3
4
|
|
|
4
5
|
const ENV_GITIGNORE = `
|
|
5
6
|
# Environment variables
|
|
@@ -8,12 +9,29 @@ const ENV_GITIGNORE = `
|
|
|
8
9
|
!.env.example
|
|
9
10
|
`;
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
const NEXT_ENV_EXAMPLE = `# Copy this file to \`.env.local\` (or \`.env\`) and fill in your values.
|
|
13
|
+
# See https://nextjs.org/docs/app/building-your-application/configuring/environment-variables for details.
|
|
14
|
+
|
|
15
|
+
NEXT_PUBLIC_API_URL=https://api.example.com
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
export async function copyEnvExample(projectPath, templatesDir, responses = {}) {
|
|
19
|
+
const fw = normalizeFramework(
|
|
20
|
+
responses.framework || {
|
|
21
|
+
name: responses.frameworkName,
|
|
22
|
+
variant: responses.frameworkVariant,
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
if (fw.name === "next") {
|
|
27
|
+
await fsp.writeFile(path.join(projectPath, ".env.example"), NEXT_ENV_EXAMPLE, "utf8");
|
|
28
|
+
} else {
|
|
29
|
+
const src = path.join(templatesDir, ".env.example");
|
|
30
|
+
try {
|
|
31
|
+
await fsp.access(src);
|
|
32
|
+
await fsp.copyFile(src, path.join(projectPath, ".env.example"));
|
|
33
|
+
} catch {}
|
|
34
|
+
}
|
|
17
35
|
|
|
18
36
|
// Ensure the project gitignore excludes env files but keeps .env.example.
|
|
19
37
|
const gitignore = path.join(projectPath, ".gitignore");
|
package/src/main.js
CHANGED
|
@@ -12,7 +12,7 @@ import { injectArchitecture, injectConditionals, injectFormatter } from "@/injec
|
|
|
12
12
|
import { getPkgManager } from "@/utils/pkg-manager.js";
|
|
13
13
|
import { handleDevServer } from "@/utils/dev-server.js";
|
|
14
14
|
import { getUserInputs, onCancel } from "@/prompts.js";
|
|
15
|
-
import { runBaseInstall, runGitInit, runViteCreate } from "@/scaffold.js";
|
|
15
|
+
import { runBaseInstall, runGitInit, runViteCreate, scaffoldBase } from "@/scaffold.js";
|
|
16
16
|
import { installAllDeps } from "@/dependencies.js";
|
|
17
17
|
import { generateReadme } from "@/readme.js";
|
|
18
18
|
import { runProjectFormat } from "@/format.js";
|
|
@@ -20,6 +20,8 @@ import { emitManifest } from "@/manifest/emit.js";
|
|
|
20
20
|
import { emitAgentContext } from "@/agent-context.js";
|
|
21
21
|
|
|
22
22
|
import { parseCliArgs, loadManifestSource, manifestToResponses, PRESETS } from "@/cli-args.js";
|
|
23
|
+
import { getProvider } from "@/engine/providers/base-provider.js";
|
|
24
|
+
import { normalizeFramework } from "@/engine/capabilities.js";
|
|
23
25
|
|
|
24
26
|
const __filename = fileURLToPath(import.meta.url);
|
|
25
27
|
const __dirname = path.dirname(__filename);
|
|
@@ -48,6 +50,7 @@ export async function main(options = {}) {
|
|
|
48
50
|
projectName: nameArg,
|
|
49
51
|
manifest: manifestSource,
|
|
50
52
|
template: templateName,
|
|
53
|
+
framework: frameworkArg,
|
|
51
54
|
arch: archArg,
|
|
52
55
|
} = options;
|
|
53
56
|
const pkg = getPkgManager();
|
|
@@ -94,20 +97,40 @@ export async function main(options = {}) {
|
|
|
94
97
|
...preset,
|
|
95
98
|
};
|
|
96
99
|
} else {
|
|
97
|
-
responses = await getUserInputs(projectName, { quickSetup, initialArch: archArg });
|
|
100
|
+
responses = await getUserInputs(projectName, { quickSetup, initialArch: archArg, framework: frameworkArg });
|
|
98
101
|
}
|
|
99
102
|
|
|
100
103
|
const projectPath = path.resolve(CURRENT_DIR, projectName);
|
|
101
104
|
|
|
102
|
-
// 4. Scaffold
|
|
105
|
+
// 4. Scaffold the base project (provider-owned bases; Vite shells out, Next copies)
|
|
106
|
+
const fw = normalizeFramework(
|
|
107
|
+
responses.framework || { name: responses.frameworkName, variant: responses.frameworkVariant }
|
|
108
|
+
);
|
|
103
109
|
const createSpin = spinner();
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
if (fw.name === "next") {
|
|
111
|
+
createSpin.start("Creating your Next.js project...");
|
|
112
|
+
try {
|
|
113
|
+
const provider = getProvider(fw.name, fw.variant);
|
|
114
|
+
const based = await scaffoldBase(projectPath, provider, responses.language, TEMPLATES_DIR);
|
|
115
|
+
if (!based) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Next.js base template not found for variant "${fw.variant}" (${responses.language}).`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
createSpin.stop(chalk.green("Project created successfully!"));
|
|
121
|
+
} catch (err) {
|
|
122
|
+
createSpin.stop(chalk.red("Failed to create project."));
|
|
123
|
+
throw err;
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
createSpin.start("Creating your Vite + React project...");
|
|
127
|
+
try {
|
|
128
|
+
await runViteCreate(projectPath, projectName, pkg, responses.language);
|
|
129
|
+
createSpin.stop(chalk.green("Project created successfully!"));
|
|
130
|
+
} catch (err) {
|
|
131
|
+
createSpin.stop(chalk.red("Failed to create project."));
|
|
132
|
+
throw err;
|
|
133
|
+
}
|
|
111
134
|
}
|
|
112
135
|
|
|
113
136
|
// 5. Install base dependencies
|
|
@@ -234,7 +257,7 @@ export async function main(options = {}) {
|
|
|
234
257
|
await cleanupBoilerplate(projectPath);
|
|
235
258
|
|
|
236
259
|
// 12.5 Copy .env.example into the project
|
|
237
|
-
await copyEnvExample(projectPath, TEMPLATES_DIR);
|
|
260
|
+
await copyEnvExample(projectPath, TEMPLATES_DIR, responses);
|
|
238
261
|
|
|
239
262
|
// 12.6 Emit lumen.config.json (manifest v2) — byte-deterministic, Zod-validated
|
|
240
263
|
const manifestSpin = spinner();
|
|
@@ -247,15 +270,15 @@ export async function main(options = {}) {
|
|
|
247
270
|
throw err;
|
|
248
271
|
}
|
|
249
272
|
|
|
250
|
-
// 12.7 Emit .
|
|
273
|
+
// 12.7 Emit AGENTS.md at root (only when agentDocs is enabled)
|
|
251
274
|
if (responses.agentDocs === true || responses._manifestSource?.agentDocs === true) {
|
|
252
275
|
const agentSpin = spinner();
|
|
253
|
-
agentSpin.start("Writing .
|
|
276
|
+
agentSpin.start("Writing AGENTS.md...");
|
|
254
277
|
try {
|
|
255
278
|
await emitAgentContext(projectPath, responses._manifestSource ?? responses);
|
|
256
|
-
agentSpin.stop(chalk.green(".
|
|
279
|
+
agentSpin.stop(chalk.green("AGENTS.md written."));
|
|
257
280
|
} catch (err) {
|
|
258
|
-
agentSpin.stop(chalk.red("Failed to write .
|
|
281
|
+
agentSpin.stop(chalk.red("Failed to write AGENTS.md."));
|
|
259
282
|
throw err;
|
|
260
283
|
}
|
|
261
284
|
}
|
package/src/manifest/emit.js
CHANGED
|
@@ -64,7 +64,7 @@ export function buildManifest(responses, opts = {}) {
|
|
|
64
64
|
};
|
|
65
65
|
// Include optional axes only if present in responses (future overlays)
|
|
66
66
|
if (responses.bundler) framework.bundler = responses.bundler;
|
|
67
|
-
if (responses.adapter) framework.adapter = responses.adapter;
|
|
67
|
+
if (responses.adapter && responses.adapter !== "none") framework.adapter = responses.adapter;
|
|
68
68
|
|
|
69
69
|
const styling = {
|
|
70
70
|
engine: responses.cssFramework || "tailwind",
|
|
@@ -117,8 +117,18 @@ export function buildManifest(responses, opts = {}) {
|
|
|
117
117
|
};
|
|
118
118
|
|
|
119
119
|
// Optional cross-cutting flags — only include if explicitly set (keeps minimal manifest byte-stable)
|
|
120
|
+
if (responses.database && responses.database !== "none") {
|
|
121
|
+
manifest.database = {
|
|
122
|
+
orm: responses.database,
|
|
123
|
+
provider: "postgresql",
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (typeof responses.docker === "boolean") manifest.docker = responses.docker;
|
|
127
|
+
if (typeof responses.routeGroups === "boolean") manifest.routeGroups = responses.routeGroups;
|
|
120
128
|
if (typeof responses.reactCompiler === "boolean") manifest.reactCompiler = responses.reactCompiler;
|
|
129
|
+
if (typeof responses.routeGroups === "boolean") manifest.routeGroups = responses.routeGroups;
|
|
121
130
|
if (typeof responses.agentDocs === "boolean") manifest.agentDocs = responses.agentDocs;
|
|
131
|
+
if (typeof responses.docker === "boolean") manifest.docker = responses.docker;
|
|
122
132
|
|
|
123
133
|
// Enforce stable top-level key order for determinism (like formatter pass)
|
|
124
134
|
const ordered = {};
|
|
@@ -133,6 +143,9 @@ export function buildManifest(responses, opts = {}) {
|
|
|
133
143
|
"docs",
|
|
134
144
|
"paths",
|
|
135
145
|
"tooling",
|
|
146
|
+
"database",
|
|
147
|
+
"docker",
|
|
148
|
+
"routeGroups",
|
|
136
149
|
"reactCompiler",
|
|
137
150
|
"agentDocs",
|
|
138
151
|
];
|