create-lumen 2.0.0-alpha.0 → 2.0.0-alpha.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 +35 -12
- package/bin/cli.js +1 -0
- package/package.json +3 -2
- package/schema/fixtures/v2/invalid/architecture-preset-type-mismatch.json +35 -0
- package/schema/fixtures/v2/invalid/harness-empty-commands.json +37 -0
- package/schema/fixtures/v2/invalid/harness-missing-command.json +41 -0
- package/schema/fixtures/v2/invalid/invalid-validation-mode.json +34 -0
- package/schema/fixtures/v2/invalid/shadcn-without-tailwind.json +1 -1
- package/schema/fixtures/v2/valid/harness-full.json +44 -0
- package/schema/fixtures/v2/valid/react-vite-type-based.json +1 -1
- package/schema/fixtures/v2/valid/validation-none.json +41 -0
- package/schema/fixtures/v2/valid/validation-relaxed.json +41 -0
- package/schema/lumen.config.v2.json +54 -2
- package/skills/create-lumen/SKILL.md +110 -0
- package/skills/create-lumen/references/manifest-v2.md +9 -0
- package/src/agent-context.js +114 -0
- package/src/cli-args.js +65 -3
- package/src/configure.js +4 -0
- package/src/css.js +14 -30
- package/src/dependencies.js +28 -18
- package/src/engine/capabilities.js +174 -4
- package/src/engine/composition.js +644 -0
- package/src/engine/index.js +1 -0
- package/src/injector.js +294 -446
- package/src/main.js +20 -7
- package/src/manifest/emit.js +49 -3
- package/src/manifest/schema.js +65 -7
- package/src/prompts.js +459 -247
- package/src/readme.js +23 -2
- package/src/scaffold.js +17 -0
- package/templates/architectures/hybrid/main.jsx +10 -0
- package/templates/architectures/hybrid/main.tsx +10 -0
- package/templates/architectures/hybrid/src/App.jsx +20 -0
- package/templates/architectures/hybrid/src/App.tsx +20 -0
- package/templates/architectures/hybrid/src/shared/components/ui/Button.jsx +31 -0
- package/templates/architectures/hybrid/src/shared/components/ui/Button.tsx +36 -0
- package/templates/architectures/hybrid/src/shared/components/ui/index.js +1 -0
- package/templates/architectures/hybrid/src/shared/components/ui/index.ts +1 -0
- package/templates/architectures/hybrid/src/shared/hooks/useLocalStorage.jsx +28 -0
- package/templates/architectures/hybrid/src/shared/hooks/useLocalStorage.ts +28 -0
- package/templates/architectures/hybrid/src/shared/layouts/.gitkeep +0 -0
- package/templates/architectures/hybrid/src/shared/stores/.gitkeep +0 -0
- package/templates/architectures/hybrid/src/shared/styles/.gitkeep +0 -0
- package/templates/architectures/hybrid/src/shared/types/index.js +20 -0
- package/templates/architectures/hybrid/src/shared/types/index.ts +16 -0
- package/templates/architectures/hybrid/src/shared/utils/cn.jsx +3 -0
- package/templates/architectures/hybrid/src/shared/utils/cn.ts +5 -0
- package/templates/architectures/none/main.jsx +10 -0
- package/templates/architectures/none/main.tsx +10 -0
- package/templates/architectures/none/src/App.jsx +15 -0
- package/templates/architectures/none/src/App.tsx +15 -0
- package/templates/architectures/none/src/styles/.gitkeep +1 -0
- package/templates/architectures/none/src/types/index.js +20 -0
- package/templates/architectures/none/src/types/index.ts +16 -0
- package/templates/conditional/testing/jest/hybrid/src/__tests__/App.test.jsx +11 -0
- package/templates/conditional/testing/jest/hybrid/src/__tests__/App.test.tsx +11 -0
- package/templates/conditional/testing/jest/none/src/__tests__/App.test.jsx +11 -0
- package/templates/conditional/testing/jest/none/src/__tests__/App.test.tsx +11 -0
- package/templates/conditional/testing/vitest/hybrid/src/__tests__/App.test.jsx +10 -0
- package/templates/conditional/testing/vitest/hybrid/src/__tests__/App.test.tsx +10 -0
- package/templates/conditional/testing/vitest/none/src/__tests__/App.test.jsx +10 -0
- package/templates/conditional/testing/vitest/none/src/__tests__/App.test.tsx +10 -0
- package/templates/css/tailwind/src/globals.css +10 -0
- package/templates/css/tailwind/src/themes.css +86 -4
- package/templates/ui/shadcn/lib/utils.js +6 -0
- package/templates/ui/shadcn/lib/utils.ts +6 -0
- package/templates/ui/shadcn/primitives/Button.jsx +48 -0
- package/templates/ui/shadcn/primitives/Button.tsx +59 -0
- package/templates/ui/shadcn/primitives/Card.jsx +55 -0
- package/templates/ui/shadcn/primitives/Card.tsx +73 -0
- package/templates/ui/shadcn/primitives/index.js +9 -0
- package/templates/ui/shadcn/primitives/index.ts +9 -0
- package/templates/css/bootstrap/src/themes.css +0 -14
- /package/templates/{css/bootstrap/src/globals.css → architectures/hybrid/src/features/.gitkeep} +0 -0
package/src/main.js
CHANGED
|
@@ -17,6 +17,7 @@ import { installAllDeps } from "@/dependencies.js";
|
|
|
17
17
|
import { generateReadme } from "@/readme.js";
|
|
18
18
|
import { runProjectFormat } from "@/format.js";
|
|
19
19
|
import { emitManifest } from "@/manifest/emit.js";
|
|
20
|
+
import { emitAgentContext } from "@/agent-context.js";
|
|
20
21
|
|
|
21
22
|
import { parseCliArgs, loadManifestSource, manifestToResponses, PRESETS } from "@/cli-args.js";
|
|
22
23
|
|
|
@@ -47,6 +48,7 @@ export async function main(options = {}) {
|
|
|
47
48
|
projectName: nameArg,
|
|
48
49
|
manifest: manifestSource,
|
|
49
50
|
template: templateName,
|
|
51
|
+
arch: archArg,
|
|
50
52
|
} = options;
|
|
51
53
|
const pkg = getPkgManager();
|
|
52
54
|
|
|
@@ -92,7 +94,7 @@ export async function main(options = {}) {
|
|
|
92
94
|
...preset,
|
|
93
95
|
};
|
|
94
96
|
} else {
|
|
95
|
-
responses = await getUserInputs(projectName, { quickSetup });
|
|
97
|
+
responses = await getUserInputs(projectName, { quickSetup, initialArch: archArg });
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
const projectPath = path.resolve(CURRENT_DIR, projectName);
|
|
@@ -135,7 +137,7 @@ export async function main(options = {}) {
|
|
|
135
137
|
// 7. Inject architecture templates
|
|
136
138
|
const archSpin = spinner();
|
|
137
139
|
archSpin.start(
|
|
138
|
-
`Setting up ${responses.architecture
|
|
140
|
+
`Setting up ${responses.architecture} architecture...`
|
|
139
141
|
);
|
|
140
142
|
try {
|
|
141
143
|
await injectArchitecture(
|
|
@@ -193,11 +195,9 @@ export async function main(options = {}) {
|
|
|
193
195
|
// 10. CSS framework setup
|
|
194
196
|
const cssSpin = spinner();
|
|
195
197
|
const cssLabel =
|
|
196
|
-
responses.cssFramework === "
|
|
197
|
-
? "CSS reset"
|
|
198
|
-
: responses.cssFramework === "tailwind"
|
|
198
|
+
responses.cssFramework === "tailwind"
|
|
199
199
|
? "Tailwind CSS"
|
|
200
|
-
: "
|
|
200
|
+
: "CSS reset";
|
|
201
201
|
if (responses.cssFramework !== "none") {
|
|
202
202
|
cssSpin.start(`Setting up ${cssLabel}...`);
|
|
203
203
|
}
|
|
@@ -247,6 +247,19 @@ export async function main(options = {}) {
|
|
|
247
247
|
throw err;
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
+
// 12.7 Emit .lumen agent context dir (only when agentDocs is enabled)
|
|
251
|
+
if (responses.agentDocs === true || responses._manifestSource?.agentDocs === true) {
|
|
252
|
+
const agentSpin = spinner();
|
|
253
|
+
agentSpin.start("Writing .lumen agent context...");
|
|
254
|
+
try {
|
|
255
|
+
await emitAgentContext(projectPath, responses._manifestSource ?? responses);
|
|
256
|
+
agentSpin.stop(chalk.green(".lumen agent context written."));
|
|
257
|
+
} catch (err) {
|
|
258
|
+
agentSpin.stop(chalk.red("Failed to write .lumen agent context."));
|
|
259
|
+
throw err;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
250
263
|
// 13. Generate README.md + LICENSE (if requested)
|
|
251
264
|
if (responses.readme) {
|
|
252
265
|
const readmeSpin = spinner();
|
|
@@ -276,7 +289,7 @@ export async function main(options = {}) {
|
|
|
276
289
|
log.step(chalk.green("\nProject setup complete!"));
|
|
277
290
|
log.message(
|
|
278
291
|
chalk.gray(
|
|
279
|
-
` Architecture: ${responses.architecture
|
|
292
|
+
` Architecture: ${responses.architecture}`
|
|
280
293
|
)
|
|
281
294
|
);
|
|
282
295
|
|
package/src/manifest/emit.js
CHANGED
|
@@ -1,8 +1,43 @@
|
|
|
1
1
|
import { promises as fsp } from "fs";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import { parseManifest, SCHEMA_URL, MANIFEST_VERSION } from "./schema.js";
|
|
3
|
+
import { parseManifest, SCHEMA_URL, MANIFEST_VERSION, HARNESS_DEFAULT_COMMANDS } from "./schema.js";
|
|
4
4
|
import { resolvePaths } from "./paths.js";
|
|
5
5
|
|
|
6
|
+
const LINT_COMMANDS = {
|
|
7
|
+
eslint: "eslint .",
|
|
8
|
+
oxlint: "oxlint .",
|
|
9
|
+
biome: "biome check .",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const FORMAT_COMMANDS = {
|
|
13
|
+
prettier: "prettier --check .",
|
|
14
|
+
oxfmt: "oxfmt --check .",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Build default harness commands from tooling choices.
|
|
19
|
+
* Skips lint/format when tooling is "none"; skips types when language is not "ts".
|
|
20
|
+
* Test + build are always included so commands.length >= 1.
|
|
21
|
+
* @param {{ linter?: string, formatter?: string, language?: string }} tooling
|
|
22
|
+
* @returns {Array<{ name: string, command: string, required: boolean }>}
|
|
23
|
+
*/
|
|
24
|
+
export function defaultHarnessCommands(tooling = {}) {
|
|
25
|
+
const { linter = "none", formatter = "none", language = "ts" } = tooling;
|
|
26
|
+
const commands = [];
|
|
27
|
+
if (linter && linter !== "none" && LINT_COMMANDS[linter]) {
|
|
28
|
+
commands.push({ name: "lint", command: LINT_COMMANDS[linter], required: true });
|
|
29
|
+
}
|
|
30
|
+
if (formatter && formatter !== "none" && FORMAT_COMMANDS[formatter]) {
|
|
31
|
+
commands.push({ name: "format", command: FORMAT_COMMANDS[formatter], required: true });
|
|
32
|
+
}
|
|
33
|
+
if (language === "ts") {
|
|
34
|
+
commands.push({ name: "types", command: "tsc -b", required: true });
|
|
35
|
+
}
|
|
36
|
+
commands.push({ name: "test", command: "vitest run", required: true });
|
|
37
|
+
commands.push({ name: "build", command: "npm run build", required: true });
|
|
38
|
+
return commands.length > 0 ? commands : [...HARNESS_DEFAULT_COMMANDS];
|
|
39
|
+
}
|
|
40
|
+
|
|
6
41
|
/**
|
|
7
42
|
* Build an ordered manifest v2 object from scaffolder responses.
|
|
8
43
|
* Stable key ordering for byte-determinism.
|
|
@@ -35,9 +70,14 @@ export function buildManifest(responses, opts = {}) {
|
|
|
35
70
|
engine: responses.cssFramework || "tailwind",
|
|
36
71
|
};
|
|
37
72
|
|
|
38
|
-
const rawArch =
|
|
73
|
+
const rawArch =
|
|
74
|
+
responses.architecturePreset || responses.architecture || "feature-based";
|
|
75
|
+
const normalizedArch = rawArch === "component-based" ? "type-based" : rawArch;
|
|
39
76
|
const architecture = {
|
|
40
|
-
|
|
77
|
+
preset: normalizedArch,
|
|
78
|
+
type: normalizedArch,
|
|
79
|
+
validation:
|
|
80
|
+
responses.architectureValidation || responses.validation || "strict",
|
|
41
81
|
};
|
|
42
82
|
|
|
43
83
|
const ui = {
|
|
@@ -59,12 +99,17 @@ export function buildManifest(responses, opts = {}) {
|
|
|
59
99
|
formatter: responses.formatter || "none",
|
|
60
100
|
};
|
|
61
101
|
|
|
102
|
+
const harness = responses.harness ?? {
|
|
103
|
+
commands: defaultHarnessCommands(tooling),
|
|
104
|
+
};
|
|
105
|
+
|
|
62
106
|
const manifest = {
|
|
63
107
|
$schema: SCHEMA_URL,
|
|
64
108
|
manifestVersion: MANIFEST_VERSION,
|
|
65
109
|
framework,
|
|
66
110
|
styling,
|
|
67
111
|
architecture,
|
|
112
|
+
harness,
|
|
68
113
|
ui,
|
|
69
114
|
docs,
|
|
70
115
|
paths,
|
|
@@ -83,6 +128,7 @@ export function buildManifest(responses, opts = {}) {
|
|
|
83
128
|
"framework",
|
|
84
129
|
"styling",
|
|
85
130
|
"architecture",
|
|
131
|
+
"harness",
|
|
86
132
|
"ui",
|
|
87
133
|
"docs",
|
|
88
134
|
"paths",
|
package/src/manifest/schema.js
CHANGED
|
@@ -12,8 +12,12 @@ export const FRAMEWORK_VARIANTS = ["vite", "app-router", "pages-router"];
|
|
|
12
12
|
export const FRAMEWORK_BUNDLERS = ["turbopack", "webpack"];
|
|
13
13
|
export const FRAMEWORK_ADAPTERS = ["node", "vercel", "cloudflare", "static"];
|
|
14
14
|
|
|
15
|
-
export const STYLING_ENGINES = ["tailwind", "
|
|
16
|
-
export const
|
|
15
|
+
export const STYLING_ENGINES = ["tailwind", "none"];
|
|
16
|
+
export const ARCHITECTURE_PRESETS = ["feature-based", "type-based", "hybrid", "none"];
|
|
17
|
+
// Legacy alias: docs used architecture.type, canonical is architecture.preset.
|
|
18
|
+
// Keep both in sync; parseManifest normalizes to expose both keys.
|
|
19
|
+
export const ARCHITECTURE_TYPES = ARCHITECTURE_PRESETS;
|
|
20
|
+
export const ARCHITECTURE_VALIDATIONS = ["strict", "relaxed", "none"];
|
|
17
21
|
export const UI_KITS = ["shadcn", "none"];
|
|
18
22
|
export const DOCS_LANGUAGES = ["en", "es"];
|
|
19
23
|
export const TOOLING_LANGUAGES = ["ts", "js"];
|
|
@@ -40,7 +44,49 @@ const stylingSchema = z
|
|
|
40
44
|
|
|
41
45
|
const architectureSchema = z
|
|
42
46
|
.object({
|
|
43
|
-
type
|
|
47
|
+
// Canonical key per docs/manifest-v2.md; legacy `type` kept as alias.
|
|
48
|
+
preset: z.enum(ARCHITECTURE_PRESETS).optional(),
|
|
49
|
+
type: z.enum(ARCHITECTURE_TYPES).optional(),
|
|
50
|
+
validation: z.enum(ARCHITECTURE_VALIDATIONS).optional().default("strict"),
|
|
51
|
+
})
|
|
52
|
+
.strict()
|
|
53
|
+
.superRefine((val, ctx) => {
|
|
54
|
+
if (!val.preset && !val.type) {
|
|
55
|
+
ctx.addIssue({
|
|
56
|
+
code: "custom",
|
|
57
|
+
path: ["preset"],
|
|
58
|
+
message: 'architecture must define "preset" or legacy "type"',
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
if (val.preset && val.type && val.preset !== val.type) {
|
|
62
|
+
ctx.addIssue({
|
|
63
|
+
code: "custom",
|
|
64
|
+
path: ["preset"],
|
|
65
|
+
message: `architecture.preset "${val.preset}" must match architecture.type "${val.type}"`,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
export const HARNESS_DEFAULT_COMMANDS = [
|
|
71
|
+
{ name: "lint", command: "oxlint .", required: true },
|
|
72
|
+
{ name: "format", command: "oxfmt --check .", required: true },
|
|
73
|
+
{ name: "types", command: "tsc -b", required: true },
|
|
74
|
+
{ name: "test", command: "vitest run", required: true },
|
|
75
|
+
{ name: "build", command: "npm run build", required: true },
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
const harnessCommandSchema = z
|
|
79
|
+
.object({
|
|
80
|
+
name: z.string().min(1),
|
|
81
|
+
command: z.string().min(1),
|
|
82
|
+
required: z.boolean().optional().default(true),
|
|
83
|
+
description: z.string().optional(),
|
|
84
|
+
})
|
|
85
|
+
.strict();
|
|
86
|
+
|
|
87
|
+
const harnessSchema = z
|
|
88
|
+
.object({
|
|
89
|
+
commands: z.array(harnessCommandSchema).min(1),
|
|
44
90
|
})
|
|
45
91
|
.strict();
|
|
46
92
|
|
|
@@ -85,6 +131,7 @@ export const manifestSchemaV2 = z
|
|
|
85
131
|
framework: frameworkSchema,
|
|
86
132
|
styling: stylingSchema,
|
|
87
133
|
architecture: architectureSchema,
|
|
134
|
+
harness: harnessSchema.optional(),
|
|
88
135
|
ui: uiSchema,
|
|
89
136
|
docs: docsSchema,
|
|
90
137
|
paths: pathsSchema,
|
|
@@ -130,16 +177,17 @@ export const manifestSchemaV2 = z
|
|
|
130
177
|
});
|
|
131
178
|
}
|
|
132
179
|
|
|
133
|
-
// architecture scoping per framework (#34)
|
|
180
|
+
// architecture scoping per framework (#34) — canonical `preset`, legacy `type` fallback
|
|
181
|
+
const archValue = architecture.preset ?? architecture.type;
|
|
134
182
|
const allowedByFramework =
|
|
135
183
|
framework.name === "react"
|
|
136
184
|
? ["feature-based", "type-based", "none"]
|
|
137
185
|
: ["feature-based", "hybrid", "none"];
|
|
138
|
-
if (!allowedByFramework.includes(
|
|
186
|
+
if (!allowedByFramework.includes(archValue)) {
|
|
139
187
|
ctx.addIssue({
|
|
140
188
|
code: "custom",
|
|
141
189
|
path: ["architecture", "type"],
|
|
142
|
-
message: `architecture.type "${
|
|
190
|
+
message: `architecture.type "${archValue}" is not valid for framework "${framework.name}" (allowed: ${allowedByFramework.join(", ")})`,
|
|
143
191
|
});
|
|
144
192
|
}
|
|
145
193
|
|
|
@@ -205,7 +253,17 @@ export function parseManifest(raw) {
|
|
|
205
253
|
err.cause = result.error;
|
|
206
254
|
throw err;
|
|
207
255
|
}
|
|
208
|
-
|
|
256
|
+
// Normalize: always expose architecture.preset + architecture.type (synced)
|
|
257
|
+
// + architecture.validation default, for backward/forward compat.
|
|
258
|
+
const data = result.data;
|
|
259
|
+
const preset = data.architecture.preset ?? data.architecture.type;
|
|
260
|
+
const type = data.architecture.type ?? data.architecture.preset;
|
|
261
|
+
data.architecture = {
|
|
262
|
+
preset,
|
|
263
|
+
type,
|
|
264
|
+
validation: data.architecture.validation ?? "strict",
|
|
265
|
+
};
|
|
266
|
+
return data;
|
|
209
267
|
}
|
|
210
268
|
|
|
211
269
|
// Re-export type helper for JSDoc (z.infer)
|