create-bw-app 0.12.0 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +6 -6
  2. package/package.json +1 -1
  3. package/src/add.mjs +2 -1
  4. package/src/app-manifest.mjs +1 -0
  5. package/src/constants.mjs +7 -10
  6. package/src/generator.mjs +64 -56
  7. package/src/remove.mjs +2 -0
  8. package/src/update.mjs +3 -0
  9. package/template/base/AGENTS.md +16 -20
  10. package/template/base/app/globals.css +5 -569
  11. package/template/base/app/theme.css +5 -0
  12. package/template/base/config/modules.ts +3 -5
  13. package/template/base/docs/ai/README.md +15 -41
  14. package/template/base/docs/ai/examples.md +13 -34
  15. package/template/base/public/brand/logo-dark.svg +2 -2
  16. package/template/base/public/brand/logo-light.svg +2 -2
  17. package/template/modules/admin/app/admin/users/page.tsx +2 -0
  18. package/template/modules/admin/app/api/admin/users/roles/route.ts +1 -6
  19. package/template/modules/admin/app/api/admin/users/route.ts +1 -6
  20. package/template/modules/crm/app/api/crm/contacts/route.ts +4 -18
  21. package/template/modules/crm/app/api/crm/organizations/route.ts +1 -8
  22. package/template/modules/crm/app/api/crm/owners/route.ts +1 -8
  23. package/template/modules/crm/app/api/crm/report/route.ts +1 -0
  24. package/template/modules/crm/app/api/crm/stats/route.ts +1 -8
  25. package/template/modules/crm/app/api/crm/timeline/route.ts +1 -8
  26. package/template/modules/crm/app/crm/report/page.tsx +5 -0
  27. package/template/site/base/AGENTS.md +6 -19
  28. package/template/site/base/docs/ai/README.md +6 -33
  29. package/template/site/base/docs/ai/examples.md +6 -28
  30. package/template/base/app/bootstrap/page.tsx +0 -75
  31. package/template/base/app/page.tsx +0 -145
  32. package/template/base/app/playground/auth/page.tsx +0 -5
  33. package/template/base/app/playground/layout.tsx +0 -41
  34. package/template/base/app/preview/app-shell/page.tsx +0 -11
  35. package/template/base/components/app-shell-preview.tsx +0 -185
  36. package/template/base/components/auth-playground.tsx +0 -112
  37. package/template/base/config/bootstrap.ts +0 -131
  38. package/template/base/config/client.ts +0 -18
  39. package/template/base/config/env.ts +0 -100
  40. package/template/base/lib/email/resend-base.ts +0 -13
  41. package/template/modules/admin/app/playground/admin/page.tsx +0 -102
  42. package/template/modules/crm/app/api/crm/_shared/create-module-route-handler.ts +0 -13
  43. package/template/modules/projects/app/playground/projects/page.tsx +0 -93
  44. package/template/site/base/app/page.tsx +0 -165
  45. package/template/site/base/components/ui/badge.tsx +0 -28
  46. package/template/site/base/components/ui/button.tsx +0 -52
  47. package/template/site/base/components/ui/card.tsx +0 -28
  48. package/template/site/base/components.json +0 -17
  49. package/template/site/base/lib/utils.ts +0 -6
@@ -1,572 +1,8 @@
1
1
  @import "tailwindcss";
2
2
  @import "@brightweblabs/theme/css";
3
+ @import "./theme.css";
3
4
 
4
- :root {
5
- color-scheme: light;
6
- --font-sans: "IBM Plex Sans", "Segoe UI", sans-serif;
7
- --font-display: Georgia, "Times New Roman", serif;
8
- /* Raw brand tokens: customize these first per client. */
9
- --brand-primary: #266946;
10
- --brand-primary-rgb: 38, 105, 70;
11
- --brand-secondary-rgb: 126, 170, 112;
12
- --brand-highlight-rgb: 189, 140, 89;
13
- --brand-positive-rgb: 113, 196, 139;
14
- /* Semantic theme tokens consumed by the UI. */
15
- --background: #f3efe5;
16
- --foreground: #18241d;
17
- --muted-foreground: #5a665c;
18
- --border: rgba(24, 36, 29, 0.12);
19
- --panel: rgba(255, 255, 255, 0.82);
20
- --panel-strong: #ffffff;
21
- --accent: var(--brand-primary);
22
- --accent-soft: rgba(var(--brand-primary-rgb), 0.12);
23
- --danger: #b43f3f;
24
- --ink-soft: #eef1e8;
25
- --ring: rgba(var(--brand-primary-rgb), 0.28);
26
- --surface-glow-cool: rgba(var(--brand-secondary-rgb), 0.18);
27
- --surface-glow-warm: rgba(var(--brand-highlight-rgb), 0.12);
28
- --accent-glow-strong: rgba(var(--brand-primary-rgb), 0.18);
29
- --accent-glow-mid: rgba(var(--brand-primary-rgb), 0.14);
30
- --accent-glow-soft: rgba(var(--brand-primary-rgb), 0.08);
31
- --positive-glow-strong: rgba(var(--brand-positive-rgb), 0.5);
32
- --positive-glow-soft: rgba(var(--brand-positive-rgb), 0.14);
33
- --positive-glow-subtle: rgba(var(--brand-positive-rgb), 0.15);
34
-
35
- /* Compatibility aliases so both starters expose the same token surface. */
36
- --bg: var(--background);
37
- --text: var(--foreground);
38
- --muted: var(--muted-foreground);
39
- --line: var(--border);
40
- }
41
-
42
- * {
43
- box-sizing: border-box;
44
- }
45
-
46
- html,
47
- body {
48
- margin: 0;
49
- padding: 0;
50
- background:
51
- radial-gradient(circle at 15% 10%, var(--surface-glow-cool), transparent 22%),
52
- radial-gradient(circle at 85% 12%, var(--surface-glow-warm), transparent 18%),
53
- linear-gradient(180deg, #faf8f1 0%, var(--bg) 48%, #e8e0cf 100%);
54
- color: var(--text);
55
- font-family: var(--font-sans), sans-serif;
56
- }
57
-
58
- a {
59
- color: inherit;
60
- text-decoration: none;
61
- }
62
-
63
- button,
64
- input {
65
- font: inherit;
66
- }
67
-
68
- .shell {
69
- min-height: 100vh;
70
- padding: 32px 20px 56px;
71
- }
72
-
73
- .frame {
74
- margin: 0 auto;
75
- max-width: 1120px;
76
- }
77
-
78
- .starter-home .frame {
79
- position: relative;
80
- }
81
-
82
- .hero {
83
- display: grid;
84
- gap: 16px;
85
- margin-bottom: 28px;
86
- }
87
-
88
- .starter-hero {
89
- display: grid;
90
- grid-template-columns: minmax(0, 1.25fr) minmax(280px, 0.75fr);
91
- gap: 20px;
92
- align-items: stretch;
93
- margin-bottom: 28px;
94
- }
95
-
96
- .starter-hero-copy {
97
- position: relative;
98
- display: grid;
99
- gap: 16px;
100
- padding: 30px 0 8px;
101
- }
102
-
103
- .starter-hero-copy::before {
104
- content: "";
105
- position: absolute;
106
- inset: 0 0 auto 0;
107
- height: 1px;
108
- background: linear-gradient(90deg, rgba(24, 36, 29, 0.35), transparent);
109
- }
110
-
111
- .starter-hero-card {
112
- position: relative;
113
- overflow: hidden;
114
- background:
115
- linear-gradient(180deg, rgba(255, 255, 255, 0.86), rgba(247, 244, 235, 0.84)),
116
- radial-gradient(circle at top right, var(--accent-glow-strong), transparent 45%);
117
- }
118
-
119
- .starter-hero-card::after {
120
- content: "";
121
- position: absolute;
122
- inset: auto -36px -36px auto;
123
- width: 150px;
124
- height: 150px;
125
- border-radius: 999px;
126
- background: var(--accent-glow-soft);
127
- }
128
-
129
- .starter-stat-grid {
130
- display: grid;
131
- grid-template-columns: repeat(2, minmax(0, 1fr));
132
- gap: 16px;
133
- margin-top: 18px;
134
- }
135
-
136
- .stat-number {
137
- display: block;
138
- font-family: var(--font-display), serif;
139
- font-size: clamp(2rem, 4vw, 3rem);
140
- line-height: 1;
141
- letter-spacing: -0.04em;
142
- }
143
-
144
- .starter-hero-note {
145
- margin-top: 24px;
146
- padding-top: 16px;
147
- border-top: 1px solid var(--line);
148
- }
149
-
150
- .eyebrow {
151
- display: inline-flex;
152
- width: fit-content;
153
- padding: 6px 10px;
154
- border-radius: 999px;
155
- background: var(--accent-soft);
156
- color: var(--accent);
157
- font-size: 12px;
158
- font-weight: 700;
159
- letter-spacing: 0.04em;
160
- text-transform: uppercase;
161
- }
162
-
163
- .title {
164
- margin: 0;
165
- font-family: var(--font-display), serif;
166
- font-size: clamp(2.4rem, 5.5vw, 4.8rem);
167
- line-height: 0.92;
168
- letter-spacing: -0.04em;
169
- }
170
-
171
- .lead {
172
- max-width: 760px;
173
- margin: 0;
174
- color: var(--muted);
175
- font-size: 18px;
176
- line-height: 1.6;
177
- }
178
-
179
- .grid {
180
- display: grid;
181
- gap: 18px;
182
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
183
- }
184
-
185
- .starter-signal-grid {
186
- grid-template-columns: repeat(2, minmax(0, 1fr));
187
- }
188
-
189
- .panel {
190
- border: 1px solid var(--line);
191
- border-radius: 24px;
192
- background: var(--panel);
193
- backdrop-filter: blur(10px);
194
- box-shadow: 0 18px 50px rgba(23, 34, 22, 0.08);
195
- }
196
-
197
- .panel-inner {
198
- padding: 22px;
199
- }
200
-
201
- .preview-glass-card {
202
- background:
203
- linear-gradient(180deg, rgba(255, 255, 255, 0.84), rgba(246, 244, 236, 0.76)),
204
- radial-gradient(circle at top right, var(--accent-glow-soft), transparent 45%);
205
- }
206
-
207
- .panel h2,
208
- .panel h3,
209
- .panel p {
210
- margin-top: 0;
211
- }
212
-
213
- .panel p:last-child {
214
- margin-bottom: 0;
215
- }
216
-
217
- .muted {
218
- color: var(--muted);
219
- }
220
-
221
- .list {
222
- display: grid;
223
- gap: 10px;
224
- margin: 0;
225
- padding: 0;
226
- list-style: none;
227
- }
228
-
229
- .list li {
230
- padding: 12px 14px;
231
- border-radius: 16px;
232
- background: rgba(255, 255, 255, 0.78);
233
- border: 1px solid rgba(23, 34, 22, 0.08);
234
- }
235
-
236
- .actions {
237
- display: flex;
238
- flex-wrap: wrap;
239
- gap: 12px;
240
- margin-top: 10px;
241
- }
242
-
243
- .action {
244
- display: inline-flex;
245
- align-items: center;
246
- justify-content: center;
247
- padding: 12px 16px;
248
- border-radius: 14px;
249
- background: var(--text);
250
- color: white;
251
- font-weight: 600;
252
- }
253
-
254
- .action.secondary {
255
- background: white;
256
- color: var(--text);
257
- border: 1px solid var(--line);
258
- }
259
-
260
- .inline-link {
261
- display: inline-flex;
262
- margin-top: 12px;
263
- color: var(--accent);
264
- font-weight: 700;
265
- }
266
-
267
- .package-name {
268
- margin-top: 12px;
269
- color: var(--muted);
270
- font-size: 13px;
271
- font-family: "IBM Plex Mono", "SFMono-Regular", monospace;
272
- }
273
-
274
- .check-row {
275
- display: flex;
276
- gap: 12px;
277
- align-items: flex-start;
278
- }
279
-
280
- .check-dot {
281
- width: 12px;
282
- height: 12px;
283
- margin-top: 5px;
284
- border-radius: 999px;
285
- flex: 0 0 auto;
286
- }
287
-
288
- .check-dot.done {
289
- background: var(--accent);
290
- }
291
-
292
- .check-dot.pending {
293
- background: #c99925;
294
- }
295
-
296
- .inline-detail {
297
- margin-top: 6px;
298
- margin-bottom: 0;
299
- }
300
-
301
- .playground-layout {
302
- display: grid;
303
- gap: 18px;
304
- grid-template-columns: 280px 1fr;
305
- align-items: start;
306
- }
307
-
308
- .shell-preview-page {
309
- padding-top: 22px;
310
- }
311
-
312
- .app-preview-shell {
313
- display: grid;
314
- grid-template-columns: 320px minmax(0, 1fr);
315
- gap: 20px;
316
- align-items: start;
317
- }
318
-
319
- .app-preview-sidebar {
320
- position: sticky;
321
- top: 24px;
322
- min-height: calc(100vh - 72px);
323
- border: 1px solid var(--line);
324
- border-radius: 28px;
325
- background:
326
- linear-gradient(180deg, rgba(255, 255, 255, 0.88), rgba(241, 237, 226, 0.84)),
327
- radial-gradient(circle at top, var(--accent-soft), transparent 34%);
328
- backdrop-filter: blur(16px);
329
- box-shadow: 0 22px 60px rgba(24, 36, 29, 0.1);
330
- }
331
-
332
- .app-preview-main {
333
- display: grid;
334
- gap: 18px;
335
- }
336
-
337
- .app-preview-header {
338
- display: flex;
339
- justify-content: space-between;
340
- gap: 20px;
341
- align-items: flex-start;
342
- padding: 24px 26px;
343
- border: 1px solid var(--line);
344
- border-radius: 28px;
345
- background:
346
- linear-gradient(180deg, rgba(255, 255, 255, 0.88), rgba(246, 243, 235, 0.86)),
347
- radial-gradient(circle at right top, var(--accent-glow-mid), transparent 35%);
348
- backdrop-filter: blur(16px);
349
- }
350
-
351
- .app-preview-header-copy h1 {
352
- margin-bottom: 8px;
353
- font-family: Georgia, "Times New Roman", serif;
354
- font-size: clamp(2rem, 4vw, 3rem);
355
- line-height: 0.96;
356
- letter-spacing: -0.04em;
357
- }
358
-
359
- .app-preview-header-actions {
360
- display: flex;
361
- gap: 10px;
362
- align-items: center;
363
- }
364
-
365
- .app-preview-mobile-nav {
366
- display: none;
367
- }
368
-
369
- .app-preview-content {
370
- display: grid;
371
- }
372
-
373
- .app-preview-stage {
374
- display: grid;
375
- gap: 18px;
376
- }
377
-
378
- .preview-card-icon {
379
- display: inline-flex;
380
- margin-bottom: 16px;
381
- width: 38px;
382
- height: 38px;
383
- align-items: center;
384
- justify-content: center;
385
- border-radius: 14px;
386
- background: var(--ink-soft);
387
- color: var(--accent);
388
- }
389
-
390
- .preview-stage-panel {
391
- background:
392
- linear-gradient(180deg, rgba(23, 37, 29, 0.96), rgba(18, 30, 24, 0.95)),
393
- radial-gradient(circle at top left, var(--positive-glow-subtle), transparent 34%);
394
- color: #f4f1e8;
395
- border-color: rgba(255, 255, 255, 0.1);
396
- }
397
-
398
- .preview-stage-panel .muted {
399
- color: rgba(244, 241, 232, 0.72);
400
- }
401
-
402
- .preview-stage-head {
403
- display: flex;
404
- justify-content: space-between;
405
- gap: 16px;
406
- align-items: flex-start;
407
- margin-bottom: 24px;
408
- }
409
-
410
- .preview-stage-head h2 {
411
- margin-bottom: 0;
412
- font-size: 1.75rem;
413
- }
414
-
415
- .nav-chip {
416
- border: 1px solid rgba(255, 255, 255, 0.12);
417
- background: rgba(255, 255, 255, 0.04);
418
- color: #f4f1e8;
419
- border-radius: 999px;
420
- padding: 10px 14px;
421
- font-size: 12px;
422
- font-weight: 700;
423
- transition: 160ms ease;
424
- }
425
-
426
- .nav-chip.active,
427
- .nav-chip:hover {
428
- border-color: var(--positive-glow-strong);
429
- background: var(--positive-glow-soft);
430
- }
431
-
432
- .preview-surface-grid {
433
- display: grid;
434
- gap: 14px;
435
- grid-template-columns: repeat(3, minmax(0, 1fr));
436
- }
437
-
438
- .preview-surface-card {
439
- display: grid;
440
- gap: 6px;
441
- padding: 16px;
442
- border-radius: 20px;
443
- background: rgba(255, 255, 255, 0.06);
444
- border: 1px solid rgba(255, 255, 255, 0.08);
445
- }
446
-
447
- .preview-label {
448
- margin: 0;
449
- color: rgba(244, 241, 232, 0.62);
450
- font-size: 11px;
451
- font-weight: 700;
452
- letter-spacing: 0.08em;
453
- text-transform: uppercase;
454
- }
455
-
456
- .sidebar {
457
- position: sticky;
458
- top: 20px;
459
- }
460
-
461
- .nav-list {
462
- display: grid;
463
- gap: 8px;
464
- }
465
-
466
- .nav-item {
467
- display: block;
468
- padding: 12px 14px;
469
- border-radius: 14px;
470
- border: 1px solid transparent;
471
- color: var(--muted);
472
- }
473
-
474
- .nav-item.active,
475
- .nav-item:hover {
476
- background: white;
477
- border-color: var(--line);
478
- color: var(--text);
479
- }
480
-
481
- .stack {
482
- display: grid;
483
- gap: 18px;
484
- }
485
-
486
- .form-grid {
487
- display: grid;
488
- gap: 14px;
489
- }
490
-
491
- .field {
492
- display: grid;
493
- gap: 8px;
494
- }
495
-
496
- .field label {
497
- font-size: 14px;
498
- font-weight: 600;
499
- }
500
-
501
- .field input {
502
- width: 100%;
503
- padding: 13px 14px;
504
- border-radius: 14px;
505
- border: 1px solid var(--line);
506
- background: white;
507
- }
508
-
509
- .result-box,
510
- .code-box {
511
- padding: 16px;
512
- border-radius: 16px;
513
- background: #fff;
514
- border: 1px solid var(--line);
515
- overflow: auto;
516
- }
517
-
518
- .status {
519
- display: inline-flex;
520
- align-items: center;
521
- gap: 8px;
522
- padding: 8px 12px;
523
- border-radius: 999px;
524
- font-size: 13px;
525
- font-weight: 700;
526
- }
527
-
528
- .status.ok {
529
- background: rgba(var(--brand-primary-rgb), 0.14);
530
- color: var(--accent);
531
- }
532
-
533
- .status.warn {
534
- background: rgba(180, 127, 34, 0.14);
535
- color: #8d5f00;
536
- }
537
-
538
- .status.error {
539
- background: rgba(180, 63, 63, 0.12);
540
- color: var(--danger);
541
- }
542
-
543
- @media (max-width: 900px) {
544
- .starter-hero,
545
- .starter-signal-grid,
546
- .app-preview-shell,
547
- .preview-surface-grid {
548
- grid-template-columns: 1fr;
549
- }
550
-
551
- .playground-layout {
552
- grid-template-columns: 1fr;
553
- }
554
-
555
- .sidebar {
556
- position: static;
557
- }
558
-
559
- .app-preview-sidebar {
560
- display: none;
561
- }
562
-
563
- .app-preview-mobile-nav {
564
- display: block;
565
- }
566
-
567
- .app-preview-header,
568
- .preview-stage-head {
569
- grid-template-columns: 1fr;
570
- display: grid;
571
- }
572
- }
5
+ /* BRIGHTWEB_SOURCES_START */
6
+ @source "../node_modules/@brightweblabs/ui/src";
7
+ @source "../node_modules/@brightweblabs/app-shell/src";
8
+ /* BRIGHTWEB_SOURCES_END */
@@ -0,0 +1,5 @@
1
+ /* App-owned theme overrides belong here so managed source scanning and shared
2
+ starter styles in globals.css can be upgraded safely. */
3
+ :root {
4
+ /* Example: --brand-accent: your-client-color; */
5
+ }
@@ -6,7 +6,7 @@ export type StarterModuleConfig = {
6
6
  description: string;
7
7
  enabled: boolean;
8
8
  packageName: string;
9
- playgroundHref?: string;
9
+ routeHref?: string;
10
10
  placement: "core" | "primary" | "admin";
11
11
  };
12
12
 
@@ -17,7 +17,6 @@ export const starterModuleConfig: StarterModuleConfig[] = [
17
17
  description: "Login, reset-password, callback URLs, and shared auth validation utilities.",
18
18
  enabled: true,
19
19
  packageName: "@brightweblabs/core-auth",
20
- playgroundHref: "/playground/auth",
21
20
  placement: "core",
22
21
  },
23
22
  {
@@ -26,7 +25,7 @@ export const starterModuleConfig: StarterModuleConfig[] = [
26
25
  description: "Contacts and CRM server/data layer, with marketing-adjacent operational data stored in Supabase.",
27
26
  enabled: true,
28
27
  packageName: "@brightweblabs/module-crm",
29
- playgroundHref: "/crm",
28
+ routeHref: "/crm",
30
29
  placement: "primary",
31
30
  },
32
31
  {
@@ -35,7 +34,6 @@ export const starterModuleConfig: StarterModuleConfig[] = [
35
34
  description: "Project portfolio, detail routes, and work-management server logic.",
36
35
  enabled: true,
37
36
  packageName: "@brightweblabs/module-projects",
38
- playgroundHref: "/playground/projects",
39
37
  placement: "primary",
40
38
  },
41
39
  {
@@ -44,7 +42,7 @@ export const starterModuleConfig: StarterModuleConfig[] = [
44
42
  description: "User role governance, admin tools, and access-control surfaces.",
45
43
  enabled: true,
46
44
  packageName: "@brightweblabs/module-admin",
47
- playgroundHref: "/playground/admin",
45
+ routeHref: "/admin/users",
48
46
  placement: "admin",
49
47
  },
50
48
  ];
@@ -1,52 +1,26 @@
1
1
  # Agent Guide
2
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.
3
+ This generated project follows BrightWeb's thin-app contract: environment values, settings, theme overrides, shell wiring, assets, migrations, docs, and direct package mounts only.
6
4
 
7
5
  ## Project shape
8
6
 
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
- - `components/`: local React components used by starter routes and app-owned product work.
13
- - `config/`: generated app configuration for brand, env readiness, enabled modules, bootstrap content, and shell registration.
14
- - `public/brand/`: starter logos used by the shell lockups.
15
- - `.env.local`: local service configuration for Supabase, Resend, and runtime URLs.
16
-
17
- ## Fast routing map
18
-
19
- - `docs/ai/app-context.json`: machine-readable summary of this app's template, starter routes, and first-read files.
20
- - `docs/ai/examples.md`: common setup and customization workflows.
21
- - `README.md`: first-run setup steps.
22
- - `components/`: local app component layer for starter surfaces and future product UI.
23
- - `config/brand.ts`: client identity, product naming, and contact inboxes.
24
- - `config/modules.ts`: module metadata and enablement flags for CRM, Projects, and Admin.
25
- - `config/client.ts`: aggregated state consumed by starter pages.
26
- - `config/bootstrap.ts`: bootstrap checklist content for `/bootstrap`.
27
- - `config/shell.ts`: managed app-shell registration and navigation wiring.
28
- - `config/shell.overrides.ts`: app-owned shell registration customizations preserved across updates.
29
- - `app/globals.css`: color tokens (raw brand + semantic mappings), typography, and global surface styling.
30
- - `app/page.tsx`: starter landing page for the generated app.
31
- - `app/bootstrap/page.tsx`: setup checklist surface.
32
- - `app/preview/app-shell/page.tsx`: shell preview validation route.
33
- - `app/playground/auth/page.tsx`: auth validation route.
34
- - `app/playground/*`: optional module playgrounds when those modules were selected at scaffold time.
7
+ - `app/`: root layout plus direct package mounts.
8
+ - `config/`: brand, module, and shell settings.
9
+ - `public/brand/`: shell brand assets.
10
+ - `supabase/`: selected module migrations.
11
+ - `.env.local`: local service configuration.
35
12
 
36
13
  ## Editing strategy
37
14
 
38
- - Change client identity first in `config/brand.ts`.
39
- - Change colors and theme tokens in `app/globals.css`.
40
- - Check module presence in `config/modules.ts` before editing or creating module-specific routes.
41
- - Add app-specific UI in `components/` before forking shared package code.
42
- - Use `config/shell.overrides.ts` when navigation or toolbar behavior needs to change.
43
- - Use `config/bootstrap.ts` and `config/client.ts` when the setup checklist or readiness messaging is wrong.
44
- - Keep starter validation routes until the real product routes replace their purpose.
15
+ - Change identity in `config/brand.ts` and visual tokens in `app/theme.css`.
16
+ - Use `config/shell.overrides.ts` for app-specific navigation or toolbar changes.
17
+ - Keep route files declarative: direct package re-exports, or one import plus one component return when Next.js requires a no-props page wrapper.
18
+ - Move any new UI, data access, state, or helpers to the owning package before mounting it.
19
+ - Projects intentionally contributes no route until its package ships a mountable UI surface.
45
20
 
46
21
  ## Validation checklist
47
22
 
48
- 1. Confirm `.env.local` is populated with real values.
49
- 2. Run the app locally.
50
- 3. Validate `/`, `/bootstrap`, `/preview/app-shell`, and `/playground/auth`.
51
- 4. Validate the playground route for each enabled optional module.
52
- 5. If a starter route is removed, also remove any links or config references that still point to it.
23
+ 1. Confirm `.env.local` is populated.
24
+ 2. Run the app build or type check.
25
+ 3. Validate each route listed in `docs/ai/app-context.json`.
26
+ 4. Confirm new route files contain no JSX, function bodies, fetches, or state.