create-bw-app 0.8.0 → 0.9.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/README.md CHANGED
@@ -34,7 +34,7 @@ npm create bw-app@latest
34
34
  - platform apps include BrightWeb auth, shell wiring, and optional module starter surfaces
35
35
  - site apps include Next.js, Tailwind CSS v4, and local component primitives
36
36
  - writes `package.json`, `next.config.ts`, `.gitignore`, and `README.md` for both templates
37
- - platform apps also write `.env.example`, generated config files, and module feature flags
37
+ - platform apps also write `.env.local`, `AGENTS.md`, `docs/ai/README.md`, and generated config files for brand and module state
38
38
  - supports repo-local `workspace:*` wiring and future published dependency wiring
39
39
 
40
40
  ## Workspace mode extras
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-bw-app",
3
3
  "private": false,
4
- "version": "0.8.0",
4
+ "version": "0.9.0",
5
5
  "type": "module",
6
6
  "bin": "bin/create-bw-app.mjs",
7
7
  "files": [
package/src/constants.mjs CHANGED
@@ -20,21 +20,18 @@ export const SELECTABLE_MODULES = [
20
20
  label: "CRM",
21
21
  packageName: "@brightweblabs/module-crm",
22
22
  templateFolder: "crm",
23
- envKey: "NEXT_PUBLIC_ENABLE_CRM",
24
23
  },
25
24
  {
26
25
  key: "projects",
27
26
  label: "Projects",
28
27
  packageName: "@brightweblabs/module-projects",
29
28
  templateFolder: "projects",
30
- envKey: "NEXT_PUBLIC_ENABLE_PROJECTS",
31
29
  },
32
30
  {
33
31
  key: "admin",
34
32
  label: "Admin",
35
33
  packageName: "@brightweblabs/module-admin",
36
34
  templateFolder: "admin",
37
- envKey: "NEXT_PUBLIC_ENABLE_ADMIN",
38
35
  },
39
36
  ];
40
37
 
package/src/generator.mjs CHANGED
@@ -256,13 +256,6 @@ async function getVersionMap(workspaceRoot) {
256
256
  return versionMap;
257
257
  }
258
258
 
259
- function createModuleFlags(selectedModules) {
260
- const selected = new Set(selectedModules);
261
- return Object.fromEntries(
262
- SELECTABLE_MODULES.map((moduleDefinition) => [moduleDefinition.key, selected.has(moduleDefinition.key)]),
263
- );
264
- }
265
-
266
259
  function createDerivedBrandValues(slug) {
267
260
  const projectName = titleizeSlug(slug);
268
261
 
@@ -276,7 +269,94 @@ function createDerivedBrandValues(slug) {
276
269
  };
277
270
  }
278
271
 
279
- function createEnvFileContent({ slug, brandValues, moduleFlags }) {
272
+ function createPlatformBrandConfigFile({ slug, brandValues }) {
273
+ return [
274
+ "export type StarterBrandConfig = {",
275
+ " companyName: string;",
276
+ " productName: string;",
277
+ " slug: string;",
278
+ " tagline: string;",
279
+ " contactEmail: string;",
280
+ " supportEmail: string;",
281
+ " primaryHex: string;",
282
+ "};",
283
+ "",
284
+ "export const starterBrandConfig: StarterBrandConfig = {",
285
+ ` companyName: ${JSON.stringify(brandValues.companyName)},`,
286
+ ` productName: ${JSON.stringify(brandValues.productName)},`,
287
+ ` slug: ${JSON.stringify(slug)},`,
288
+ ` tagline: ${JSON.stringify(brandValues.tagline)},`,
289
+ ` contactEmail: ${JSON.stringify(brandValues.contactEmail)},`,
290
+ ` supportEmail: ${JSON.stringify(brandValues.supportEmail)},`,
291
+ ` primaryHex: ${JSON.stringify(brandValues.primaryHex)},`,
292
+ "};",
293
+ "",
294
+ ].join("\n");
295
+ }
296
+
297
+ function createPlatformModulesConfigFile(selectedModules) {
298
+ const selected = new Set(selectedModules);
299
+
300
+ return [
301
+ 'export type StarterModuleKey = "core-auth" | "crm" | "projects" | "admin";',
302
+ "",
303
+ "export type StarterModuleConfig = {",
304
+ " key: StarterModuleKey;",
305
+ " label: string;",
306
+ " description: string;",
307
+ " enabled: boolean;",
308
+ " packageName: string;",
309
+ " playgroundHref?: string;",
310
+ ' placement: "core" | "primary" | "admin";',
311
+ "};",
312
+ "",
313
+ "export const starterModuleConfig: StarterModuleConfig[] = [",
314
+ " {",
315
+ ' key: "core-auth",',
316
+ ' label: "Core Auth",',
317
+ ' description: "Login, reset-password, callback URLs, and shared auth validation utilities.",',
318
+ " enabled: true,",
319
+ ' packageName: "@brightweblabs/core-auth",',
320
+ ' playgroundHref: "/playground/auth",',
321
+ ' placement: "core",',
322
+ " },",
323
+ " {",
324
+ ' key: "crm",',
325
+ ' label: "CRM",',
326
+ ' description: "Contacts, marketing audience, and CRM server/data layer.",',
327
+ ` enabled: ${String(selected.has("crm"))},`,
328
+ ' packageName: "@brightweblabs/module-crm",',
329
+ ' playgroundHref: "/playground/crm",',
330
+ ' placement: "primary",',
331
+ " },",
332
+ " {",
333
+ ' key: "projects",',
334
+ ' label: "Projects",',
335
+ ' description: "Project portfolio, detail routes, and work-management server logic.",',
336
+ ` enabled: ${String(selected.has("projects"))},`,
337
+ ' packageName: "@brightweblabs/module-projects",',
338
+ ' playgroundHref: "/playground/projects",',
339
+ ' placement: "primary",',
340
+ " },",
341
+ " {",
342
+ ' key: "admin",',
343
+ ' label: "Admin",',
344
+ ' description: "User role governance, admin tools, and access-control surfaces.",',
345
+ ` enabled: ${String(selected.has("admin"))},`,
346
+ ' packageName: "@brightweblabs/module-admin",',
347
+ ' playgroundHref: "/playground/admin",',
348
+ ' placement: "admin",',
349
+ " },",
350
+ "];",
351
+ "",
352
+ "export function getEnabledStarterModules() {",
353
+ " return starterModuleConfig.filter((moduleConfig) => moduleConfig.enabled);",
354
+ "}",
355
+ "",
356
+ ].join("\n");
357
+ }
358
+
359
+ function createEnvFileContent() {
280
360
  return [
281
361
  "NEXT_PUBLIC_APP_URL=http://localhost:3000",
282
362
  "NEXT_PUBLIC_SUPABASE_URL=",
@@ -284,18 +364,6 @@ function createEnvFileContent({ slug, brandValues, moduleFlags }) {
284
364
  "SUPABASE_SERVICE_ROLE_KEY=",
285
365
  "RESEND_API_KEY=",
286
366
  "",
287
- `NEXT_PUBLIC_CLIENT_COMPANY_NAME=${brandValues.companyName}`,
288
- `NEXT_PUBLIC_CLIENT_PRODUCT_NAME=${brandValues.productName}`,
289
- `NEXT_PUBLIC_CLIENT_SLUG=${slug}`,
290
- `NEXT_PUBLIC_CLIENT_TAGLINE=${brandValues.tagline}`,
291
- `NEXT_PUBLIC_CLIENT_CONTACT_EMAIL=${brandValues.contactEmail}`,
292
- `NEXT_PUBLIC_CLIENT_SUPPORT_EMAIL=${brandValues.supportEmail}`,
293
- `NEXT_PUBLIC_CLIENT_PRIMARY_HEX=${brandValues.primaryHex}`,
294
- "",
295
- `NEXT_PUBLIC_ENABLE_CRM=${String(moduleFlags.crm)}`,
296
- `NEXT_PUBLIC_ENABLE_PROJECTS=${String(moduleFlags.projects)}`,
297
- `NEXT_PUBLIC_ENABLE_ADMIN=${String(moduleFlags.admin)}`,
298
- "",
299
367
  ].join("\n");
300
368
  }
301
369
 
@@ -333,9 +401,8 @@ function createGitignore() {
333
401
  "yarn-error.log*",
334
402
  ".pnpm-debug.log*",
335
403
  "",
336
- "# env files (can opt-in for committing if needed)",
404
+ "# env files",
337
405
  ".env*",
338
- "!.env.example",
339
406
  "",
340
407
  "# vercel",
341
408
  ".vercel",
@@ -363,12 +430,12 @@ function createPlatformReadme({
363
430
 
364
431
  const localSteps = workspaceMode
365
432
  ? [
366
- "1. Review `.env.example`, then copy it to `.env.local` and fill in real service credentials.",
433
+ "1. Review `.env.local` and fill in real service credentials.",
367
434
  "2. Run `pnpm install` from the BrightWeb workspace root.",
368
435
  `3. Run \`pnpm --filter ${slug} dev\`.`,
369
436
  ]
370
437
  : [
371
- "1. Review `.env.example`, then copy it to `.env.local` and fill in real service credentials.",
438
+ "1. Review `.env.local` and fill in real service credentials.",
372
439
  `2. Run \`${packageManager} install\`.`,
373
440
  `3. Run \`${packageManager} dev\`.`,
374
441
  ];
@@ -871,7 +938,6 @@ async function scaffoldPlatformProject({
871
938
  answers,
872
939
  dbInstallPlan,
873
940
  }) {
874
- const moduleFlags = createModuleFlags(selectedModules);
875
941
  const brandValues = createDerivedBrandValues(answers.slug);
876
942
  const baseTemplateDir = path.join(TEMPLATE_ROOT, "base");
877
943
 
@@ -899,11 +965,16 @@ async function scaffoldPlatformProject({
899
965
  )}\n`,
900
966
  );
901
967
  await fs.writeFile(path.join(targetDir, "next.config.ts"), createNextConfig({ template: "platform", selectedModules }));
968
+ await fs.writeFile(
969
+ path.join(targetDir, "config", "brand.ts"),
970
+ createPlatformBrandConfigFile({ slug: answers.slug, brandValues }),
971
+ );
972
+ await fs.writeFile(path.join(targetDir, "config", "modules.ts"), createPlatformModulesConfigFile(selectedModules));
902
973
  await fs.writeFile(path.join(targetDir, "config", "shell.ts"), createShellConfig(selectedModules));
903
974
 
904
- const envFileContent = createEnvFileContent({ slug: answers.slug, brandValues, moduleFlags });
975
+ const envFileContent = createEnvFileContent();
905
976
 
906
- await fs.writeFile(path.join(targetDir, ".env.example"), envFileContent);
977
+ await fs.writeFile(path.join(targetDir, ".env.local"), envFileContent);
907
978
  await fs.writeFile(path.join(targetDir, ".gitignore"), createGitignore());
908
979
  await fs.writeFile(
909
980
  path.join(targetDir, "README.md"),
@@ -0,0 +1,26 @@
1
+ # AGENTS.md
2
+
3
+ This generated project is a BrightWeb platform starter. Use this file as the local entrypoint for AI agents working inside the app.
4
+
5
+ ## Start here
6
+
7
+ - `README.md`: local setup commands and starter routes.
8
+ - `docs/ai/README.md`: app-specific routing guide for agents.
9
+ - `config/brand.ts`: client identity, naming, and contact defaults.
10
+ - `config/modules.ts`: selected module set and runtime enablement.
11
+ - `config/client.ts`: starter-facing derived state used by the home page and setup surfaces.
12
+ - `.env.local`: runtime service values for local development.
13
+
14
+ ## Working rules
15
+
16
+ - Treat `/bootstrap`, `/preview/app-shell`, and `/playground/*` as starter validation surfaces. They are app-owned and can be removed after setup if links and references are cleaned up too.
17
+ - Check `config/modules.ts` before assuming CRM, Projects, or Admin routes exist.
18
+ - Prefer composing app-level routes and config before forking logic from `@brightweblabs/*` packages.
19
+ - Keep edits local to this app unless the change is intentionally shared across multiple BrightWeb projects.
20
+
21
+ ## First validation pass
22
+
23
+ 1. Run the local dev server from this project or workspace.
24
+ 2. Open `/`, `/bootstrap`, `/preview/app-shell`, and `/playground/auth`.
25
+ 3. If optional modules are enabled, open the matching `/playground/*` route for each one.
26
+ 4. Confirm `.env.local` contains real service values before debugging runtime behavior.
@@ -115,7 +115,7 @@ export default function HomePage() {
115
115
  <li>`config/brand.ts` for client identity and contact details.</li>
116
116
  <li>`config/modules.ts` for enabled platform modules.</li>
117
117
  <li>`config/env.ts` for infra requirements and readiness checks.</li>
118
- <li>`.env.example` for starter defaults; copy it to `.env.local` for per-client secrets and flags.</li>
118
+ <li>`.env.local` for per-client service credentials and local runtime overrides.</li>
119
119
  </ul>
120
120
  </div>
121
121
  </article>
@@ -69,7 +69,7 @@ export function getStarterBootstrapChecklist() {
69
69
  {
70
70
  label: "Create per-client environment variables",
71
71
  done: config.envReadiness.allReady,
72
- detail: "Copy `.env.example` to `.env.local` and fill the real values.",
72
+ detail: "Fill `.env.local` with the real service values for this client.",
73
73
  },
74
74
  ],
75
75
  };
@@ -9,13 +9,11 @@ export type StarterBrandConfig = {
9
9
  };
10
10
 
11
11
  export const starterBrandConfig: StarterBrandConfig = {
12
- companyName: process.env.NEXT_PUBLIC_CLIENT_COMPANY_NAME?.trim() || "Starter Client",
13
- productName: process.env.NEXT_PUBLIC_CLIENT_PRODUCT_NAME?.trim() || "Operations Platform",
14
- slug: process.env.NEXT_PUBLIC_CLIENT_SLUG?.trim() || "starter-client",
15
- tagline:
16
- process.env.NEXT_PUBLIC_CLIENT_TAGLINE?.trim()
17
- || "A configurable Brightweb starter app for shipping new client instances without rebuilding the platform.",
18
- contactEmail: process.env.NEXT_PUBLIC_CLIENT_CONTACT_EMAIL?.trim() || "hello@example.com",
19
- supportEmail: process.env.NEXT_PUBLIC_CLIENT_SUPPORT_EMAIL?.trim() || "support@example.com",
20
- primaryHex: process.env.NEXT_PUBLIC_CLIENT_PRIMARY_HEX?.trim() || "#1f7a45",
12
+ companyName: "Starter Client",
13
+ productName: "Operations Platform",
14
+ slug: "starter-client",
15
+ tagline: "A configurable Brightweb starter app for shipping new client instances without rebuilding the platform.",
16
+ contactEmail: "hello@example.com",
17
+ supportEmail: "support@example.com",
18
+ primaryHex: "#1f7a45",
21
19
  };
@@ -10,14 +10,6 @@ export type StarterModuleConfig = {
10
10
  placement: "core" | "primary" | "admin";
11
11
  };
12
12
 
13
- function envFlag(value: string | undefined, defaultValue: boolean) {
14
- if (typeof value !== "string") return defaultValue;
15
- const normalized = value.trim().toLowerCase();
16
- if (["1", "true", "yes", "on"].includes(normalized)) return true;
17
- if (["0", "false", "no", "off"].includes(normalized)) return false;
18
- return defaultValue;
19
- }
20
-
21
13
  export const starterModuleConfig: StarterModuleConfig[] = [
22
14
  {
23
15
  key: "core-auth",
@@ -32,7 +24,7 @@ export const starterModuleConfig: StarterModuleConfig[] = [
32
24
  key: "crm",
33
25
  label: "CRM",
34
26
  description: "Contacts, marketing audience, and CRM server/data layer.",
35
- enabled: envFlag(process.env.NEXT_PUBLIC_ENABLE_CRM, true),
27
+ enabled: true,
36
28
  packageName: "@brightweblabs/module-crm",
37
29
  playgroundHref: "/playground/crm",
38
30
  placement: "primary",
@@ -41,7 +33,7 @@ export const starterModuleConfig: StarterModuleConfig[] = [
41
33
  key: "projects",
42
34
  label: "Projects",
43
35
  description: "Project portfolio, detail routes, and work-management server logic.",
44
- enabled: envFlag(process.env.NEXT_PUBLIC_ENABLE_PROJECTS, true),
36
+ enabled: true,
45
37
  packageName: "@brightweblabs/module-projects",
46
38
  playgroundHref: "/playground/projects",
47
39
  placement: "primary",
@@ -50,7 +42,7 @@ export const starterModuleConfig: StarterModuleConfig[] = [
50
42
  key: "admin",
51
43
  label: "Admin",
52
44
  description: "User role governance, admin tools, and access-control surfaces.",
53
- enabled: envFlag(process.env.NEXT_PUBLIC_ENABLE_ADMIN, true),
45
+ enabled: true,
54
46
  packageName: "@brightweblabs/module-admin",
55
47
  playgroundHref: "/playground/admin",
56
48
  placement: "admin",
@@ -0,0 +1,44 @@
1
+ # Agent Guide
2
+
3
+ This file is the local routing guide for AI agents working inside a generated BrightWeb platform app.
4
+
5
+ It is intentionally app-scoped. It explains the generated project you are in, not the maintainer-only BrightWeb monorepo internals.
6
+
7
+ ## Project shape
8
+
9
+ This app is a normal Next.js App Router project with BrightWeb runtime wiring layered on top.
10
+
11
+ - `app/`: route tree, layouts, pages, starter previews, and playground routes.
12
+ - `config/`: generated app configuration for brand, env readiness, enabled modules, bootstrap content, and shell registration.
13
+ - `public/brand/`: starter logos used by the shell lockups.
14
+ - `.env.local`: local service configuration for Supabase, Resend, and runtime URLs.
15
+
16
+ ## Fast routing map
17
+
18
+ - `README.md`: first-run setup steps.
19
+ - `config/brand.ts`: client name, product name, support inboxes, and brand color.
20
+ - `config/modules.ts`: module metadata and enablement flags for CRM, Projects, and Admin.
21
+ - `config/client.ts`: aggregated state consumed by starter pages.
22
+ - `config/bootstrap.ts`: bootstrap checklist content for `/bootstrap`.
23
+ - `config/shell.ts`: app-shell registration and navigation wiring.
24
+ - `app/page.tsx`: starter landing page for the generated app.
25
+ - `app/bootstrap/page.tsx`: setup checklist surface.
26
+ - `app/preview/app-shell/page.tsx`: shell preview validation route.
27
+ - `app/playground/auth/page.tsx`: auth validation route.
28
+ - `app/playground/*`: optional module playgrounds when those modules were selected at scaffold time.
29
+
30
+ ## Editing strategy
31
+
32
+ - Change client identity first in `config/brand.ts`.
33
+ - Check module presence in `config/modules.ts` before editing or creating module-specific routes.
34
+ - Use `config/shell.ts` when navigation or toolbar behavior needs to change.
35
+ - Use `config/bootstrap.ts` and `config/client.ts` when the setup checklist or readiness messaging is wrong.
36
+ - Keep starter validation routes until the real product routes replace their purpose.
37
+
38
+ ## Validation checklist
39
+
40
+ 1. Confirm `.env.local` is populated with real values.
41
+ 2. Run the app locally.
42
+ 3. Validate `/`, `/bootstrap`, `/preview/app-shell`, and `/playground/auth`.
43
+ 4. Validate the playground route for each enabled optional module.
44
+ 5. If a starter route is removed, also remove any links or config references that still point to it.
@@ -0,0 +1,6 @@
1
+ export const dynamic = "force-dynamic";
2
+
3
+ export async function GET(request: Request) {
4
+ const { handleCrmContactsGetRequest } = await import("@brightweblabs/module-crm");
5
+ return handleCrmContactsGetRequest(request);
6
+ }
@@ -0,0 +1,6 @@
1
+ export const dynamic = "force-dynamic";
2
+
3
+ export async function GET(request: Request) {
4
+ const { handleCrmOrganizationsGetRequest } = await import("@brightweblabs/module-crm");
5
+ return handleCrmOrganizationsGetRequest(request);
6
+ }
@@ -0,0 +1,6 @@
1
+ export const dynamic = "force-dynamic";
2
+
3
+ export async function GET(request: Request) {
4
+ const { handleCrmOwnersGetRequest } = await import("@brightweblabs/module-crm");
5
+ return handleCrmOwnersGetRequest(request);
6
+ }
@@ -0,0 +1,6 @@
1
+ export const dynamic = "force-dynamic";
2
+
3
+ export async function GET(request: Request) {
4
+ const { handleCrmStatsGetRequest } = await import("@brightweblabs/module-crm");
5
+ return handleCrmStatsGetRequest(request);
6
+ }