create-lumen 1.2.0 → 2.0.0-alpha.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 (146) hide show
  1. package/README.md +71 -9
  2. package/bin/cli.js +18 -4
  3. package/package.json +15 -4
  4. package/schema/fixtures/index.js +62 -0
  5. package/schema/fixtures/v2/invalid/architecture-preset-type-mismatch.json +35 -0
  6. package/schema/fixtures/v2/invalid/harness-empty-commands.json +37 -0
  7. package/schema/fixtures/v2/invalid/harness-missing-command.json +41 -0
  8. package/schema/fixtures/v2/invalid/invalid-manifest-version.json +33 -0
  9. package/schema/fixtures/v2/invalid/invalid-validation-mode.json +34 -0
  10. package/schema/fixtures/v2/invalid/malformed-paths.json +33 -0
  11. package/schema/fixtures/v2/invalid/missing-required-fields.json +17 -0
  12. package/schema/fixtures/v2/invalid/next-type-based.json +33 -0
  13. package/schema/fixtures/v2/invalid/next-vite.json +33 -0
  14. package/schema/fixtures/v2/invalid/react-bundler.json +34 -0
  15. package/schema/fixtures/v2/invalid/react-hybrid.json +33 -0
  16. package/schema/fixtures/v2/invalid/react-variant-mismatch.json +33 -0
  17. package/schema/fixtures/v2/invalid/shadcn-without-tailwind.json +33 -0
  18. package/schema/fixtures/v2/invalid/v1-flat-config.json +11 -0
  19. package/schema/fixtures/v2/valid/full-kitchen-sink.json +35 -0
  20. package/schema/fixtures/v2/valid/harness-full.json +44 -0
  21. package/schema/fixtures/v2/valid/next-app-router-feature-based.json +35 -0
  22. package/schema/fixtures/v2/valid/next-app-router-hybrid.json +35 -0
  23. package/schema/fixtures/v2/valid/next-app-router-none.json +33 -0
  24. package/schema/fixtures/v2/valid/next-pages-router-feature-based.json +35 -0
  25. package/schema/fixtures/v2/valid/next-pages-router-hybrid.json +35 -0
  26. package/schema/fixtures/v2/valid/next-pages-router-none.json +33 -0
  27. package/schema/fixtures/v2/valid/react-vite-feature-based.json +33 -0
  28. package/schema/fixtures/v2/valid/react-vite-none.json +33 -0
  29. package/schema/fixtures/v2/valid/react-vite-type-based.json +33 -0
  30. package/schema/fixtures/v2/valid/validation-none.json +41 -0
  31. package/schema/fixtures/v2/valid/validation-relaxed.json +41 -0
  32. package/schema/lumen.config.v2.json +269 -0
  33. package/skills/create-lumen/SKILL.md +110 -0
  34. package/skills/create-lumen/references/manifest-v2.md +9 -0
  35. package/src/agent-context.js +114 -0
  36. package/src/cli-args.js +294 -0
  37. package/src/css.js +20 -9
  38. package/src/dependencies.js +12 -6
  39. package/src/engine/capabilities.js +218 -0
  40. package/src/engine/index.js +2 -0
  41. package/src/engine/providers/base-provider.js +219 -0
  42. package/src/injector.js +26 -13
  43. package/src/main.js +66 -10
  44. package/src/manifest/emit.js +166 -0
  45. package/src/manifest/index.js +11 -0
  46. package/src/manifest/paths.js +128 -0
  47. package/src/manifest/schema.js +270 -0
  48. package/src/prompts.js +26 -6
  49. package/templates/conditional/linting/eslint/next/eslint.config.mjs +16 -0
  50. package/templates/css/tailwind/postcss.config.mjs +5 -0
  51. /package/templates/architectures/{component-based → type-based}/main.jsx +0 -0
  52. /package/templates/architectures/{component-based → type-based}/main.tsx +0 -0
  53. /package/templates/architectures/{component-based → type-based}/src/App.jsx +0 -0
  54. /package/templates/architectures/{component-based → type-based}/src/App.tsx +0 -0
  55. /package/templates/architectures/{component-based → type-based}/src/components/common/Button.jsx +0 -0
  56. /package/templates/architectures/{component-based → type-based}/src/components/common/Button.tsx +0 -0
  57. /package/templates/architectures/{component-based → type-based}/src/components/common/index.js +0 -0
  58. /package/templates/architectures/{component-based → type-based}/src/components/common/index.ts +0 -0
  59. /package/templates/architectures/{component-based → type-based}/src/components/form/.gitkeep +0 -0
  60. /package/templates/architectures/{component-based → type-based}/src/config/.gitkeep +0 -0
  61. /package/templates/architectures/{component-based → type-based}/src/hooks/useApp.js +0 -0
  62. /package/templates/architectures/{component-based → type-based}/src/hooks/useApp.ts +0 -0
  63. /package/templates/architectures/{component-based → type-based}/src/hooks/useLocalStorage.jsx +0 -0
  64. /package/templates/architectures/{component-based → type-based}/src/hooks/useLocalStorage.ts +0 -0
  65. /package/templates/architectures/{component-based → type-based}/src/layouts/MainLayout.jsx +0 -0
  66. /package/templates/architectures/{component-based → type-based}/src/layouts/MainLayout.tsx +0 -0
  67. /package/templates/architectures/{component-based → type-based}/src/pages/Home/Home.jsx +0 -0
  68. /package/templates/architectures/{component-based → type-based}/src/pages/Home/Home.tsx +0 -0
  69. /package/templates/architectures/{component-based → type-based}/src/pages/Home/index.js +0 -0
  70. /package/templates/architectures/{component-based → type-based}/src/pages/Home/index.ts +0 -0
  71. /package/templates/architectures/{component-based → type-based}/src/providers/.gitkeep +0 -0
  72. /package/templates/architectures/{component-based → type-based}/src/providers/AppProvider.jsx +0 -0
  73. /package/templates/architectures/{component-based → type-based}/src/providers/AppProvider.tsx +0 -0
  74. /package/templates/architectures/{component-based → type-based}/src/providers/appcontext.js +0 -0
  75. /package/templates/architectures/{component-based → type-based}/src/providers/appcontext.ts +0 -0
  76. /package/templates/architectures/{component-based → type-based}/src/services/.gitkeep +0 -0
  77. /package/templates/architectures/{component-based → type-based}/src/store/.gitkeep +0 -0
  78. /package/templates/architectures/{component-based → type-based}/src/styles/.gitkeep +0 -0
  79. /package/templates/architectures/{component-based → type-based}/src/types/index.js +0 -0
  80. /package/templates/architectures/{component-based → type-based}/src/types/index.ts +0 -0
  81. /package/templates/architectures/{component-based → type-based}/src/utils/cn.jsx +0 -0
  82. /package/templates/architectures/{component-based → type-based}/src/utils/cn.ts +0 -0
  83. /package/templates/conditional/axios/{component-based → type-based}/src/config/axios.config.js +0 -0
  84. /package/templates/conditional/axios/{component-based → type-based}/src/config/axios.config.ts +0 -0
  85. /package/templates/conditional/axios/{component-based → type-based}/src/services/axios.client.js +0 -0
  86. /package/templates/conditional/axios/{component-based → type-based}/src/services/axios.client.ts +0 -0
  87. /package/templates/conditional/axios/{component-based → type-based}/src/services/user.service.js +0 -0
  88. /package/templates/conditional/axios/{component-based → type-based}/src/services/user.service.ts +0 -0
  89. /package/templates/conditional/fetch/{component-based → type-based}/src/config/api.config.js +0 -0
  90. /package/templates/conditional/fetch/{component-based → type-based}/src/config/api.config.ts +0 -0
  91. /package/templates/conditional/fetch/{component-based → type-based}/src/services/api.client.js +0 -0
  92. /package/templates/conditional/fetch/{component-based → type-based}/src/services/api.client.ts +0 -0
  93. /package/templates/conditional/fetch/{component-based → type-based}/src/services/user.service.js +0 -0
  94. /package/templates/conditional/fetch/{component-based → type-based}/src/services/user.service.ts +0 -0
  95. /package/templates/conditional/icons/huge/{component-based → type-based}/bootstrap/src/pages/Home/Home.jsx +0 -0
  96. /package/templates/conditional/icons/huge/{component-based → type-based}/bootstrap/src/pages/Home/Home.tsx +0 -0
  97. /package/templates/conditional/icons/huge/{component-based → type-based}/src/pages/Home/Home.jsx +0 -0
  98. /package/templates/conditional/icons/huge/{component-based → type-based}/src/pages/Home/Home.tsx +0 -0
  99. /package/templates/conditional/icons/huge/{component-based → type-based}/tailwind/src/pages/Home/Home.jsx +0 -0
  100. /package/templates/conditional/icons/huge/{component-based → type-based}/tailwind/src/pages/Home/Home.tsx +0 -0
  101. /package/templates/conditional/icons/lucide/{component-based → type-based}/bootstrap/src/pages/Home/Home.jsx +0 -0
  102. /package/templates/conditional/icons/lucide/{component-based → type-based}/bootstrap/src/pages/Home/Home.tsx +0 -0
  103. /package/templates/conditional/icons/lucide/{component-based → type-based}/src/pages/Home/Home.jsx +0 -0
  104. /package/templates/conditional/icons/lucide/{component-based → type-based}/src/pages/Home/Home.tsx +0 -0
  105. /package/templates/conditional/icons/lucide/{component-based → type-based}/tailwind/src/pages/Home/Home.jsx +0 -0
  106. /package/templates/conditional/icons/lucide/{component-based → type-based}/tailwind/src/pages/Home/Home.tsx +0 -0
  107. /package/templates/conditional/router/{component-based → type-based}/bootstrap/src/layouts/MainLayout.jsx +0 -0
  108. /package/templates/conditional/router/{component-based → type-based}/bootstrap/src/layouts/MainLayout.tsx +0 -0
  109. /package/templates/conditional/router/{component-based → type-based}/src/App.jsx +0 -0
  110. /package/templates/conditional/router/{component-based → type-based}/src/App.tsx +0 -0
  111. /package/templates/conditional/router/{component-based → type-based}/src/layouts/MainLayout.jsx +0 -0
  112. /package/templates/conditional/router/{component-based → type-based}/src/layouts/MainLayout.tsx +0 -0
  113. /package/templates/conditional/router/{component-based → type-based}/src/router/guards/AuthGuard.jsx +0 -0
  114. /package/templates/conditional/router/{component-based → type-based}/src/router/guards/AuthGuard.tsx +0 -0
  115. /package/templates/conditional/router/{component-based → type-based}/src/router/guards/GuestGuard.jsx +0 -0
  116. /package/templates/conditional/router/{component-based → type-based}/src/router/guards/GuestGuard.tsx +0 -0
  117. /package/templates/conditional/router/{component-based → type-based}/src/router/index.jsx +0 -0
  118. /package/templates/conditional/router/{component-based → type-based}/src/router/index.tsx +0 -0
  119. /package/templates/conditional/router/{component-based → type-based}/tailwind/src/layouts/MainLayout.jsx +0 -0
  120. /package/templates/conditional/router/{component-based → type-based}/tailwind/src/layouts/MainLayout.tsx +0 -0
  121. /package/templates/conditional/state/redux/{component-based → type-based}/src/pages/Home/counterSlice.jsx +0 -0
  122. /package/templates/conditional/state/redux/{component-based → type-based}/src/pages/Home/counterSlice.ts +0 -0
  123. /package/templates/conditional/state/redux/{component-based → type-based}/src/providers/StoreProvider.jsx +0 -0
  124. /package/templates/conditional/state/redux/{component-based → type-based}/src/providers/StoreProvider.tsx +0 -0
  125. /package/templates/conditional/state/redux/{component-based → type-based}/src/store/hooks.js +0 -0
  126. /package/templates/conditional/state/redux/{component-based → type-based}/src/store/hooks.ts +0 -0
  127. /package/templates/conditional/state/redux/{component-based → type-based}/src/store/index.js +0 -0
  128. /package/templates/conditional/state/redux/{component-based → type-based}/src/store/index.ts +0 -0
  129. /package/templates/conditional/state/zustand/{component-based → type-based}/src/store/useCounterStore.jsx +0 -0
  130. /package/templates/conditional/state/zustand/{component-based → type-based}/src/store/useCounterStore.ts +0 -0
  131. /package/templates/conditional/testing/jest/{component-based → type-based}/src/__tests__/App.test.jsx +0 -0
  132. /package/templates/conditional/testing/jest/{component-based → type-based}/src/__tests__/App.test.tsx +0 -0
  133. /package/templates/conditional/testing/vitest/{component-based → type-based}/src/__tests__/App.test.jsx +0 -0
  134. /package/templates/conditional/testing/vitest/{component-based → type-based}/src/__tests__/App.test.tsx +0 -0
  135. /package/templates/css/component-styles/bootstrap/{component-based → type-based}/src/components/common/Button.jsx +0 -0
  136. /package/templates/css/component-styles/bootstrap/{component-based → type-based}/src/components/common/Button.tsx +0 -0
  137. /package/templates/css/component-styles/bootstrap/{component-based → type-based}/src/layouts/MainLayout.jsx +0 -0
  138. /package/templates/css/component-styles/bootstrap/{component-based → type-based}/src/layouts/MainLayout.tsx +0 -0
  139. /package/templates/css/component-styles/bootstrap/{component-based → type-based}/src/pages/Home/Home.jsx +0 -0
  140. /package/templates/css/component-styles/bootstrap/{component-based → type-based}/src/pages/Home/Home.tsx +0 -0
  141. /package/templates/css/component-styles/tailwind/{component-based → type-based}/src/components/common/Button.jsx +0 -0
  142. /package/templates/css/component-styles/tailwind/{component-based → type-based}/src/components/common/Button.tsx +0 -0
  143. /package/templates/css/component-styles/tailwind/{component-based → type-based}/src/layouts/MainLayout.jsx +0 -0
  144. /package/templates/css/component-styles/tailwind/{component-based → type-based}/src/layouts/MainLayout.tsx +0 -0
  145. /package/templates/css/component-styles/tailwind/{component-based → type-based}/src/pages/Home/Home.jsx +0 -0
  146. /package/templates/css/component-styles/tailwind/{component-based → type-based}/src/pages/Home/Home.tsx +0 -0
@@ -0,0 +1,269 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://lumen-sh.github.io/create-lumen/schema/lumen.config.v2.json",
4
+ "title": "Lumen manifest v2",
5
+ "description": "Schema for lumen.config.json v2 - emitted by create-lumen, consumed by lumen-cli. See docs/manifest-v2.md",
6
+ "type": "object",
7
+ "properties": {
8
+ "$schema": {
9
+ "default": "https://lumen-sh.github.io/create-lumen/schema/lumen.config.v2.json",
10
+ "type": "string",
11
+ "format": "uri"
12
+ },
13
+ "manifestVersion": {
14
+ "type": "number",
15
+ "const": 2
16
+ },
17
+ "framework": {
18
+ "type": "object",
19
+ "properties": {
20
+ "name": {
21
+ "type": "string",
22
+ "enum": [
23
+ "react",
24
+ "next"
25
+ ]
26
+ },
27
+ "variant": {
28
+ "type": "string",
29
+ "enum": [
30
+ "vite",
31
+ "app-router",
32
+ "pages-router"
33
+ ]
34
+ },
35
+ "bundler": {
36
+ "type": "string",
37
+ "enum": [
38
+ "turbopack",
39
+ "webpack"
40
+ ]
41
+ },
42
+ "adapter": {
43
+ "type": "string",
44
+ "enum": [
45
+ "node",
46
+ "vercel",
47
+ "cloudflare",
48
+ "static"
49
+ ]
50
+ }
51
+ },
52
+ "required": [
53
+ "name",
54
+ "variant"
55
+ ],
56
+ "additionalProperties": false
57
+ },
58
+ "styling": {
59
+ "type": "object",
60
+ "properties": {
61
+ "engine": {
62
+ "type": "string",
63
+ "enum": [
64
+ "tailwind",
65
+ "bootstrap",
66
+ "none"
67
+ ]
68
+ }
69
+ },
70
+ "required": [
71
+ "engine"
72
+ ],
73
+ "additionalProperties": false
74
+ },
75
+ "architecture": {
76
+ "type": "object",
77
+ "properties": {
78
+ "preset": {
79
+ "type": "string",
80
+ "enum": [
81
+ "feature-based",
82
+ "type-based",
83
+ "hybrid",
84
+ "none"
85
+ ]
86
+ },
87
+ "type": {
88
+ "type": "string",
89
+ "enum": [
90
+ "feature-based",
91
+ "type-based",
92
+ "hybrid",
93
+ "none"
94
+ ]
95
+ },
96
+ "validation": {
97
+ "default": "strict",
98
+ "type": "string",
99
+ "enum": [
100
+ "strict",
101
+ "relaxed",
102
+ "none"
103
+ ]
104
+ }
105
+ },
106
+ "additionalProperties": false
107
+ },
108
+ "harness": {
109
+ "type": "object",
110
+ "properties": {
111
+ "commands": {
112
+ "minItems": 1,
113
+ "type": "array",
114
+ "items": {
115
+ "type": "object",
116
+ "properties": {
117
+ "name": {
118
+ "type": "string",
119
+ "minLength": 1
120
+ },
121
+ "command": {
122
+ "type": "string",
123
+ "minLength": 1
124
+ },
125
+ "required": {
126
+ "default": true,
127
+ "type": "boolean"
128
+ },
129
+ "description": {
130
+ "type": "string"
131
+ }
132
+ },
133
+ "required": [
134
+ "name",
135
+ "command"
136
+ ],
137
+ "additionalProperties": false
138
+ }
139
+ }
140
+ },
141
+ "required": [
142
+ "commands"
143
+ ],
144
+ "additionalProperties": false
145
+ },
146
+ "ui": {
147
+ "type": "object",
148
+ "properties": {
149
+ "kit": {
150
+ "type": "string",
151
+ "enum": [
152
+ "shadcn",
153
+ "none"
154
+ ]
155
+ }
156
+ },
157
+ "required": [
158
+ "kit"
159
+ ],
160
+ "additionalProperties": false
161
+ },
162
+ "docs": {
163
+ "type": "object",
164
+ "properties": {
165
+ "language": {
166
+ "type": "string",
167
+ "enum": [
168
+ "en",
169
+ "es"
170
+ ]
171
+ }
172
+ },
173
+ "required": [
174
+ "language"
175
+ ],
176
+ "additionalProperties": false
177
+ },
178
+ "paths": {
179
+ "type": "object",
180
+ "properties": {
181
+ "features": {
182
+ "type": "string",
183
+ "minLength": 1
184
+ },
185
+ "components": {
186
+ "type": "string",
187
+ "minLength": 1
188
+ },
189
+ "services": {
190
+ "type": "string",
191
+ "minLength": 1
192
+ },
193
+ "hooks": {
194
+ "type": "string",
195
+ "minLength": 1
196
+ },
197
+ "pages": {
198
+ "type": "string",
199
+ "minLength": 1
200
+ },
201
+ "ui": {
202
+ "type": "string",
203
+ "minLength": 1
204
+ }
205
+ },
206
+ "required": [
207
+ "features",
208
+ "components",
209
+ "services",
210
+ "hooks",
211
+ "pages",
212
+ "ui"
213
+ ],
214
+ "additionalProperties": false
215
+ },
216
+ "tooling": {
217
+ "type": "object",
218
+ "properties": {
219
+ "language": {
220
+ "type": "string",
221
+ "enum": [
222
+ "ts",
223
+ "js"
224
+ ]
225
+ },
226
+ "linter": {
227
+ "type": "string",
228
+ "enum": [
229
+ "eslint",
230
+ "oxlint",
231
+ "biome",
232
+ "none"
233
+ ]
234
+ },
235
+ "formatter": {
236
+ "type": "string",
237
+ "enum": [
238
+ "prettier",
239
+ "oxfmt",
240
+ "none"
241
+ ]
242
+ }
243
+ },
244
+ "required": [
245
+ "language",
246
+ "linter",
247
+ "formatter"
248
+ ],
249
+ "additionalProperties": false
250
+ },
251
+ "reactCompiler": {
252
+ "type": "boolean"
253
+ },
254
+ "agentDocs": {
255
+ "type": "boolean"
256
+ }
257
+ },
258
+ "required": [
259
+ "manifestVersion",
260
+ "framework",
261
+ "styling",
262
+ "architecture",
263
+ "ui",
264
+ "docs",
265
+ "paths",
266
+ "tooling"
267
+ ],
268
+ "additionalProperties": false
269
+ }
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: create-lumen
3
+ description: Scaffold and extend a lumen v2 project (manifestVersion 2). Read lumen.config.json, resolve generator paths via manifest.paths, respect feature barrels, @/* alias, harness commands, and validation modes.
4
+ ---
5
+
6
+ # create-lumen
7
+
8
+ ## When to use
9
+
10
+ Use this skill when the working directory contains (or should contain) a
11
+ `lumen.config.json` with `manifestVersion: 2`: scaffolding follow-ups,
12
+ `lumen g` generation, `lumen doctor` / `lumen harness` diagnostics.
13
+
14
+ Do NOT guess layout. Every target directory comes from the manifest.
15
+
16
+ ## 1. Read `lumen.config.json`
17
+
18
+ ```bash
19
+ cat lumen.config.json
20
+ ```
21
+
22
+ Required top-level keys: `manifestVersion: 2`, `framework`, `styling`,
23
+ `architecture`, `ui`, `docs`, `paths`, `tooling`, `harness`.
24
+ Source of truth: `src/manifest/schema.js` (`parseManifest`, Zod strict).
25
+ Full spec: `docs/contracts/manifest-v2-contract.md`, `docs/manifest-v2.md`.
26
+
27
+ If `manifestVersion` is missing / `framework` is a string → **v1 manifest**:
28
+ fail fast (exit 1), generate nothing, point to
29
+ `docs/migration/v1-to-v2.md`.
30
+
31
+ ## 2. Resolve paths via `manifest.paths` (never hardcode)
32
+
33
+ Generators MUST do `<root>/<manifest.paths.<key>>`. POSIX `/` separators.
34
+
35
+ | Framework | Preset | features | components | services | hooks | pages | ui |
36
+ |---|---|---|---|---|---|---|---|
37
+ | React+Vite | `feature-based` | `src/features` | `src/shared/components` | `src/shared/services` | `src/shared/hooks` | `src/app/router` | `src/shared/components/ui` |
38
+ | React+Vite | `type-based` | `src/features` | `src/components` | `src/services` | `src/hooks` | `src/pages` | `src/ui` |
39
+ | Next App | `feature-based`/`hybrid` | `src/features` | `src/shared/components` | `src/shared/services` | `src/shared/hooks` | `app` | `src/shared/components/ui` |
40
+ | Next Pages | `feature-based` | `src/features` | `src/shared/components` | `src/shared/services` | `src/shared/hooks` | `pages` | `src/shared/components/ui` |
41
+ | Any | `none` | `src` | `src` (`components/` for Next) | `src/services` | `src/hooks` | per framework | `src…/ui` |
42
+
43
+ Resolver: `src/manifest/paths.js` (`resolvePaths`). Fixtures:
44
+ `schema/fixtures/v2/valid/react-vite-feature-based.json`,
45
+ `full-kitchen-sink.json`.
46
+
47
+ ## 3. Feature structure + barrels
48
+
49
+ New feature at `<paths.features>/<name>/` (e.g. `src/features/billing`):
50
+
51
+ ```bash
52
+ lumen g feature billing
53
+ lumen g component billing/InvoiceCard --feature billing
54
+ lumen g hook useBilling --feature billing
55
+ lumen g service billing-api --feature billing
56
+ ```
57
+
58
+ Rules:
59
+
60
+ - Private subfolders: `components/ hooks/ layouts/ pages/ services/ store/ types/`.
61
+ - Public barrel required: `<feature>/index.ts` (or `.js` when
62
+ `tooling.language === "js"`). External code imports only the barrel:
63
+ `import { InvoiceCard } from "@/features/billing"`.
64
+ - Extensions: `.tsx`/`.jsx` for JSX components, `.ts`/`.js` for pure logic,
65
+ per `tooling.language`.
66
+
67
+ ## 4. Path alias `@/*`
68
+
69
+ `@/*` → `<root>/src/*` (guaranteed by `tsconfig` + Vite/Next config).
70
+ Feature-based: shared imports via `@/shared/...`.
71
+ Type-based: `@/components/...`, `@/hooks/...`, `@/services/...`.
72
+
73
+ ```ts
74
+ import { Button } from "@/shared/components/ui/button";
75
+ import { useBilling } from "@/features/billing";
76
+ ```
77
+
78
+ ## 5. `harness.commands` execution
79
+
80
+ `lumen harness` runs each entry in order; `required: true` (default) fails
81
+ the run on error, `required: false` warns and continues.
82
+
83
+ ```json
84
+ { "name": "lint", "command": "oxlint .", "required": true }
85
+ ```
86
+
87
+ Defaults: `lint (oxlint .)`, `format (oxfmt --check .)`, `types (tsc -b)`,
88
+ `test (vitest run)`, `build (npm run build)`.
89
+
90
+ ## 6. `architecture.validation` modes
91
+
92
+ | Mode | Behavior |
93
+ |---|---|
94
+ | `strict` | Enforce barrels, encapsulation, path rules; fail on violation. |
95
+ | `relaxed` | Warn on violation, still generate. |
96
+ | `none` | Skip architecture checks. |
97
+
98
+ Note: `architecture.preset` is canonical; legacy `type` is accepted as an
99
+ alias (`parseManifest` normalizes both). Valid presets: React →
100
+ `feature-based|type-based|none`; Next → `feature-based|hybrid|none`.
101
+
102
+ ## 7. Common errors — reject, don't patch around
103
+
104
+ - **v1 rejection:** flat config (`css`, string `framework`/`architecture`) →
105
+ exit 1 + migration pointer. Never partially generate.
106
+ - **`shadcn` → `tailwind`:** `ui.kit: "shadcn"` requires
107
+ `styling.engine: "tailwind"` (v4 CSS-first, no `tailwind.config.js`).
108
+ - **`bundler`/`adapter` Next-only:** `framework.bundler` (`turbopack|webpack`)
109
+ and `adapter` (`node|vercel|cloudflare|static`) are prohibited on `react`.
110
+ `react` variant MUST be `vite`; `next` MUST be `app-router|pages-router`.
@@ -0,0 +1,9 @@
1
+ # manifest-v2 pointer
2
+
3
+ Canonical spec lives in `docs/` — do not duplicate it here.
4
+
5
+ - Contract: `docs/contracts/manifest-v2-contract.md`
6
+ - Shape / v1→v2 diff: `docs/manifest-v2.md`
7
+ - Zod source of truth: `src/manifest/schema.js`
8
+ - Path resolver: `src/manifest/paths.js`
9
+ - Valid fixtures: `schema/fixtures/v2/valid/`
@@ -0,0 +1,114 @@
1
+ import { promises as fsp } from "node:fs";
2
+ import path from "node:path";
3
+ import { buildManifest } from "./manifest/emit.js";
4
+
5
+ const VALIDATION_NOTES = {
6
+ strict: "strict: architecture boundaries are enforced — do not import across features except via barrels or shared paths; violations must be fixed.",
7
+ relaxed: "relaxed: architecture boundaries are advisory — prefer barrels and shared paths, but cross-imports only warn.",
8
+ none: "none: no architecture validation — paths are still the canonical locations.",
9
+ };
10
+
11
+ function resolveManifest(manifestOrResponses) {
12
+ if (manifestOrResponses && typeof manifestOrResponses.manifestVersion !== "undefined") {
13
+ return manifestOrResponses;
14
+ }
15
+ if (manifestOrResponses && manifestOrResponses._manifestSource) {
16
+ return manifestOrResponses._manifestSource;
17
+ }
18
+ return buildManifest(manifestOrResponses);
19
+ }
20
+
21
+ /**
22
+ * Render the short agent conventions markdown from a manifest.
23
+ * Deterministic: derived only from stable manifest fields.
24
+ * @param {object} manifest - validated manifest v2
25
+ * @returns {string}
26
+ */
27
+ export function renderConventions(manifest) {
28
+ const preset = manifest.architecture?.preset ?? manifest.architecture?.type ?? "feature-based";
29
+ const validation = manifest.architecture?.validation ?? "strict";
30
+ const paths = manifest.paths ?? {};
31
+ const harness = manifest.harness ?? { commands: [] };
32
+ const barrelFile = manifest.tooling?.language === "js" ? "index.js" : "index.ts";
33
+
34
+ const pathLines = Object.entries(paths)
35
+ .map(([key, value]) => `- ${key}: \`${value}\``)
36
+ .join("\n");
37
+
38
+ const harnessLines = (harness.commands ?? [])
39
+ .map((c) => `- \`${c.name}\`: \`${c.command}\` (${c.required === false ? "optional" : "required"})`)
40
+ .join("\n");
41
+
42
+ const harnessSummary = (harness.commands ?? []).map((c) => `\`${c.command}\``).join(", ");
43
+
44
+ return (
45
+ `# Lumen Agent Conventions\n` +
46
+ `\n` +
47
+ `Generated from \`lumen.config.json\`. Resolve every file location via \`paths.*\` in \`.lumen/architecture.json\` (preset: \`${preset}\`, validation: \`${validation}\`).\n` +
48
+ `\n` +
49
+ `## Rules\n` +
50
+ `\n` +
51
+ `- Resolve all paths via \`paths.*\` — never hardcode alternative \`src/...\` locations.\n` +
52
+ `- Feature barrel: every \`src/features/<feature>/\` must re-export its public API via \`${barrelFile}\`.\n` +
53
+ `- Shared code: import shared utilities via \`@/shared\` (plus the \`@/...\` aliases from tsconfig/jsconfig).\n` +
54
+ `- Harness: before finishing, run each command in \`.lumen/project.json\` \`harness.commands\` in order (${harnessSummary}).\n` +
55
+ `- Validation mode \`${validation}\`: ${VALIDATION_NOTES[validation] ?? VALIDATION_NOTES.strict}\n` +
56
+ `\n` +
57
+ `## Paths\n` +
58
+ `\n` +
59
+ `${pathLines}\n` +
60
+ `\n` +
61
+ `## Harness\n` +
62
+ `\n` +
63
+ `${harnessLines}\n`
64
+ );
65
+ }
66
+
67
+ /**
68
+ * Build the deterministic agent-context payload from a validated manifest v2.
69
+ * No timestamps — output must be byte-stable.
70
+ * @param {object} manifest - validated manifest v2
71
+ * @returns {{ project: object, architecture: object, conventionsMd: string }}
72
+ */
73
+ export function buildAgentContext(manifest) {
74
+ const project = {
75
+ manifestVersion: manifest.manifestVersion,
76
+ framework: manifest.framework,
77
+ styling: manifest.styling,
78
+ tooling: manifest.tooling,
79
+ harness: manifest.harness,
80
+ docs: manifest.docs,
81
+ };
82
+ const architecture = {
83
+ preset: manifest.architecture?.preset ?? manifest.architecture?.type,
84
+ validation: manifest.architecture?.validation ?? "strict",
85
+ paths: manifest.paths,
86
+ };
87
+ const conventionsMd = renderConventions(manifest);
88
+ return { project, architecture, conventionsMd };
89
+ }
90
+
91
+ function writeJson(filePath, value) {
92
+ return fsp.writeFile(filePath, JSON.stringify(value, null, 2) + "\n", "utf8");
93
+ }
94
+
95
+ /**
96
+ * Emit the .lumen agent context dir into a scaffolded project.
97
+ * Accepts either a validated manifest v2 or scaffolder responses
98
+ * (uses responses._manifestSource when present, else buildManifest).
99
+ * Writes .lumen/project.json, .lumen/architecture.json, .lumen/conventions.md
100
+ * with 2-space indent + trailing newline. Byte-deterministic (no timestamps).
101
+ * @param {string} projectPath - absolute path to scaffolded project
102
+ * @param {object} manifestOrResponses
103
+ * @returns {Promise<string>} absolute path to the .lumen dir
104
+ */
105
+ export async function emitAgentContext(projectPath, manifestOrResponses) {
106
+ const manifest = resolveManifest(manifestOrResponses);
107
+ const { project, architecture, conventionsMd } = buildAgentContext(manifest);
108
+ const dir = path.join(projectPath, ".lumen");
109
+ await fsp.mkdir(dir, { recursive: true });
110
+ await writeJson(path.join(dir, "project.json"), project);
111
+ await writeJson(path.join(dir, "architecture.json"), architecture);
112
+ await fsp.writeFile(path.join(dir, "conventions.md"), conventionsMd, "utf8");
113
+ return dir;
114
+ }