create-cartwright 0.1.0 → 2.0.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.
- package/README.md +21 -2
- package/dist/approve.d.ts +2 -0
- package/dist/approve.js +16 -0
- package/dist/approve.js.map +1 -0
- package/dist/approve.test.d.ts +1 -0
- package/dist/approve.test.js +23 -0
- package/dist/approve.test.js.map +1 -0
- package/dist/brief.d.ts +26 -0
- package/dist/brief.js +25 -0
- package/dist/brief.js.map +1 -0
- package/dist/brief.test.d.ts +1 -0
- package/dist/brief.test.js +23 -0
- package/dist/brief.test.js.map +1 -0
- package/dist/generate/index.d.ts +4 -0
- package/dist/generate/index.js +42 -0
- package/dist/generate/index.js.map +1 -0
- package/dist/generate/index.test.d.ts +1 -0
- package/dist/generate/index.test.js +47 -0
- package/dist/generate/index.test.js.map +1 -0
- package/dist/index.js +122 -82
- package/dist/index.js.map +1 -1
- package/dist/inject.d.ts +2 -0
- package/dist/inject.js +21 -0
- package/dist/inject.js.map +1 -0
- package/dist/inject.test.d.ts +1 -0
- package/dist/inject.test.js +37 -0
- package/dist/inject.test.js.map +1 -0
- package/dist/interview.d.ts +8 -0
- package/dist/interview.js +73 -0
- package/dist/interview.js.map +1 -0
- package/dist/interview.test.d.ts +1 -0
- package/dist/interview.test.js +50 -0
- package/dist/interview.test.js.map +1 -0
- package/dist/key-step.d.ts +12 -0
- package/dist/key-step.js +22 -0
- package/dist/key-step.js.map +1 -0
- package/dist/key-step.test.d.ts +1 -0
- package/dist/key-step.test.js +56 -0
- package/dist/key-step.test.js.map +1 -0
- package/dist/llm.d.ts +2 -0
- package/dist/llm.js +51 -0
- package/dist/llm.js.map +1 -0
- package/dist/llm.test.d.ts +1 -0
- package/dist/llm.test.js +32 -0
- package/dist/llm.test.js.map +1 -0
- package/dist/scaffold.d.ts +48 -0
- package/dist/scaffold.js +171 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/scaffold.test.d.ts +1 -0
- package/dist/scaffold.test.js +109 -0
- package/dist/scaffold.test.js.map +1 -0
- package/package.json +5 -3
package/dist/scaffold.js
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
import { randomBytes } from "node:crypto";
|
|
5
|
+
import pc from "picocolors";
|
|
6
|
+
export const TEMPLATE_SLUGS = [
|
|
7
|
+
"website-corporate",
|
|
8
|
+
"coffee",
|
|
9
|
+
"sunglasses",
|
|
10
|
+
"agent-marketplace",
|
|
11
|
+
"generic",
|
|
12
|
+
];
|
|
13
|
+
export const TEMPLATE_DEFAULTS = {
|
|
14
|
+
"website-corporate": {
|
|
15
|
+
mode: "website",
|
|
16
|
+
features: {
|
|
17
|
+
webshop: false,
|
|
18
|
+
acp: false,
|
|
19
|
+
a2a: false,
|
|
20
|
+
adminAgenticDashboard: false,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
coffee: {
|
|
24
|
+
mode: "webshop",
|
|
25
|
+
features: {
|
|
26
|
+
webshop: true,
|
|
27
|
+
acp: false,
|
|
28
|
+
a2a: false,
|
|
29
|
+
adminAgenticDashboard: false,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
sunglasses: {
|
|
33
|
+
mode: "webshop",
|
|
34
|
+
features: {
|
|
35
|
+
webshop: true,
|
|
36
|
+
acp: false,
|
|
37
|
+
a2a: false,
|
|
38
|
+
adminAgenticDashboard: false,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
"agent-marketplace": {
|
|
42
|
+
mode: "agent-marketplace",
|
|
43
|
+
features: {
|
|
44
|
+
webshop: false,
|
|
45
|
+
acp: true,
|
|
46
|
+
a2a: true,
|
|
47
|
+
adminAgenticDashboard: true,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
generic: {
|
|
51
|
+
mode: "webshop",
|
|
52
|
+
features: {
|
|
53
|
+
webshop: true,
|
|
54
|
+
acp: false,
|
|
55
|
+
a2a: false,
|
|
56
|
+
adminAgenticDashboard: false,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
export function isTemplateSlug(value) {
|
|
61
|
+
return typeof value === "string" && TEMPLATE_SLUGS.includes(value);
|
|
62
|
+
}
|
|
63
|
+
export function detectPackageManager() {
|
|
64
|
+
const ua = process.env.npm_config_user_agent ?? "";
|
|
65
|
+
if (ua.startsWith("pnpm"))
|
|
66
|
+
return "pnpm";
|
|
67
|
+
if (ua.startsWith("yarn"))
|
|
68
|
+
return "yarn";
|
|
69
|
+
if (ua.startsWith("bun"))
|
|
70
|
+
return "bun";
|
|
71
|
+
return "npm";
|
|
72
|
+
}
|
|
73
|
+
export function generateAuthSecret() {
|
|
74
|
+
return randomBytes(32).toString("hex");
|
|
75
|
+
}
|
|
76
|
+
export function patchEnvLocal(targetDir, authSecret) {
|
|
77
|
+
const envExamplePath = join(targetDir, ".env.example");
|
|
78
|
+
if (!existsSync(envExamplePath))
|
|
79
|
+
return;
|
|
80
|
+
const example = readFileSync(envExamplePath, "utf8");
|
|
81
|
+
const patched = example.replace(/^AUTH_SECRET=.*/m, `AUTH_SECRET="${authSecret}"`);
|
|
82
|
+
writeFileSync(join(targetDir, ".env.local"), patched);
|
|
83
|
+
}
|
|
84
|
+
export function titleCase(projectName) {
|
|
85
|
+
return projectName
|
|
86
|
+
.split("-")
|
|
87
|
+
.map((s) => s.charAt(0).toUpperCase() + s.slice(1))
|
|
88
|
+
.join(" ");
|
|
89
|
+
}
|
|
90
|
+
export function patchBrandConfigContent(original, projectName) {
|
|
91
|
+
return original
|
|
92
|
+
.replace(/storeName:\s*"[^"]*"/, `storeName: "${titleCase(projectName)}"`)
|
|
93
|
+
.replace(/storeSlug:\s*"[^"]*"/, `storeSlug: "${projectName}"`);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Apply the per-template defaults to a brand.config.ts source string.
|
|
97
|
+
* Patches:
|
|
98
|
+
* - industryTemplate: "<slug>"
|
|
99
|
+
* - mode: "<mode>"
|
|
100
|
+
* - features.webshop, features.acp, features.a2a, features.adminAgenticDashboard
|
|
101
|
+
*
|
|
102
|
+
* The replacements are regex-anchored to the exact field shape produced by
|
|
103
|
+
* the upstream brand.config.ts. If that shape ever changes the regexes here
|
|
104
|
+
* will silently no-op (no replacement) — the corresponding unit test catches
|
|
105
|
+
* that case.
|
|
106
|
+
*/
|
|
107
|
+
export function patchBrandConfigForTemplate(original, template) {
|
|
108
|
+
const defaults = TEMPLATE_DEFAULTS[template];
|
|
109
|
+
let out = original;
|
|
110
|
+
// industryTemplate: "xxx" (single-line)
|
|
111
|
+
out = out.replace(/industryTemplate:\s*"[^"]*"/, `industryTemplate: "${template}"`);
|
|
112
|
+
// mode: "xxx" as "website" | "webshop" | "agent-marketplace"
|
|
113
|
+
// We replace just the literal value inside the leading quotes, preserving
|
|
114
|
+
// the `as ...` type assertion if present.
|
|
115
|
+
out = out.replace(/mode:\s*"(?:website|webshop|agent-marketplace)"/, `mode: "${defaults.mode}"`);
|
|
116
|
+
// features.webshop|acp|a2a|adminAgenticDashboard: <bool>
|
|
117
|
+
for (const [key, value] of Object.entries(defaults.features)) {
|
|
118
|
+
const re = new RegExp(`(${key}:\\s*)(?:true|false)`, "g");
|
|
119
|
+
out = out.replace(re, `$1${value}`);
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
export function tryGitInit(targetDir) {
|
|
124
|
+
try {
|
|
125
|
+
execSync("git init -q && git add -A && git commit -q -m 'feat: initial commit from create-cartwright'", { cwd: targetDir, stdio: "ignore" });
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
export function tryInstall(targetDir, pm) {
|
|
133
|
+
try {
|
|
134
|
+
// Remove conflicting lockfiles from the template before installing
|
|
135
|
+
const lockfiles = ["package-lock.json", "pnpm-lock.yaml", "yarn.lock", "bun.lockb"];
|
|
136
|
+
for (const lockfile of lockfiles) {
|
|
137
|
+
const lockPath = join(targetDir, lockfile);
|
|
138
|
+
if (existsSync(lockPath)) {
|
|
139
|
+
unlinkSync(lockPath);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
execSync(`${pm} install`, { cwd: targetDir, stdio: "ignore" });
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
export function databaseNote(db) {
|
|
150
|
+
switch (db) {
|
|
151
|
+
case "turso":
|
|
152
|
+
return [
|
|
153
|
+
pc.bold("Turso setup (production):"),
|
|
154
|
+
" turso db create my-shop-db",
|
|
155
|
+
" turso db tokens create my-shop-db",
|
|
156
|
+
pc.dim(" Set TURSO_DATABASE_URL + TURSO_AUTH_TOKEN in Vercel."),
|
|
157
|
+
].join("\n");
|
|
158
|
+
case "postgres":
|
|
159
|
+
return [
|
|
160
|
+
pc.bold("Postgres setup:"),
|
|
161
|
+
" Update DATABASE_URL in .env.local to your Postgres URL.",
|
|
162
|
+
pc.dim(" Prisma schema currently uses 'sqlite' provider — switch to 'postgresql' in prisma/schema.prisma and run a fresh migration."),
|
|
163
|
+
].join("\n");
|
|
164
|
+
case "sqlite":
|
|
165
|
+
return [
|
|
166
|
+
pc.bold("SQLite (local only):"),
|
|
167
|
+
" No extra setup. dev.db will be created on first migration.",
|
|
168
|
+
].join("\n");
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=scaffold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { titleCase, patchBrandConfigContent, patchBrandConfigForTemplate, isTemplateSlug, TEMPLATE_SLUGS, TEMPLATE_DEFAULTS, } from "./scaffold";
|
|
3
|
+
describe("scaffold helpers", () => {
|
|
4
|
+
it("titleCase laver projektnavn til storeName", () => {
|
|
5
|
+
expect(titleCase("nord-kaffe")).toBe("Nord Kaffe");
|
|
6
|
+
});
|
|
7
|
+
it("patchBrandConfigContent erstatter storeName og storeSlug", () => {
|
|
8
|
+
const input = `{ storeName: "Demo", storeSlug: "demo" }`;
|
|
9
|
+
const out = patchBrandConfigContent(input, "min-shop");
|
|
10
|
+
expect(out).toContain(`storeName: "Min Shop"`);
|
|
11
|
+
expect(out).toContain(`storeSlug: "min-shop"`);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
// Sample brand.config.ts fragments that mirror the actual shape produced
|
|
15
|
+
// by cartwright-private after Phase 4. Keeping these here (vs. importing
|
|
16
|
+
// the real file) lets the CLI tests stay independent of the template repo.
|
|
17
|
+
const BRAND_FRAGMENT = `
|
|
18
|
+
industryTemplate: "saas",
|
|
19
|
+
mode: "website" as "website" | "webshop" | "agent-marketplace",
|
|
20
|
+
ecommerceEnabled: false,
|
|
21
|
+
features: {
|
|
22
|
+
webshop: false,
|
|
23
|
+
acp: false,
|
|
24
|
+
a2a: false,
|
|
25
|
+
adminAgenticDashboard: false,
|
|
26
|
+
tryOn: false,
|
|
27
|
+
aiStylist: true,
|
|
28
|
+
},
|
|
29
|
+
`;
|
|
30
|
+
describe("isTemplateSlug", () => {
|
|
31
|
+
for (const slug of TEMPLATE_SLUGS) {
|
|
32
|
+
it(`recognises "${slug}"`, () => {
|
|
33
|
+
expect(isTemplateSlug(slug)).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
it("rejects unknown slugs", () => {
|
|
37
|
+
expect(isTemplateSlug("not-a-template")).toBe(false);
|
|
38
|
+
expect(isTemplateSlug("")).toBe(false);
|
|
39
|
+
expect(isTemplateSlug(undefined)).toBe(false);
|
|
40
|
+
expect(isTemplateSlug(42)).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe("patchBrandConfigForTemplate", () => {
|
|
44
|
+
it("agent-marketplace flips A2A+ACP+dashboard on, webshop off", () => {
|
|
45
|
+
const out = patchBrandConfigForTemplate(BRAND_FRAGMENT, "agent-marketplace");
|
|
46
|
+
expect(out).toContain(`industryTemplate: "agent-marketplace"`);
|
|
47
|
+
expect(out).toContain(`mode: "agent-marketplace"`);
|
|
48
|
+
expect(out).toContain(`webshop: false`);
|
|
49
|
+
expect(out).toContain(`acp: true`);
|
|
50
|
+
expect(out).toContain(`a2a: true`);
|
|
51
|
+
expect(out).toContain(`adminAgenticDashboard: true`);
|
|
52
|
+
});
|
|
53
|
+
it("coffee flips webshop on, A2A off", () => {
|
|
54
|
+
const out = patchBrandConfigForTemplate(BRAND_FRAGMENT, "coffee");
|
|
55
|
+
expect(out).toContain(`industryTemplate: "coffee"`);
|
|
56
|
+
expect(out).toContain(`mode: "webshop"`);
|
|
57
|
+
expect(out).toContain(`webshop: true`);
|
|
58
|
+
expect(out).toContain(`a2a: false`);
|
|
59
|
+
});
|
|
60
|
+
it("sunglasses → webshop mode, legacy template name in industryTemplate", () => {
|
|
61
|
+
const out = patchBrandConfigForTemplate(BRAND_FRAGMENT, "sunglasses");
|
|
62
|
+
expect(out).toContain(`industryTemplate: "sunglasses"`);
|
|
63
|
+
expect(out).toContain(`mode: "webshop"`);
|
|
64
|
+
expect(out).toContain(`webshop: true`);
|
|
65
|
+
});
|
|
66
|
+
it("website-corporate → website mode, all A2A/webshop off", () => {
|
|
67
|
+
const out = patchBrandConfigForTemplate(BRAND_FRAGMENT, "website-corporate");
|
|
68
|
+
expect(out).toContain(`industryTemplate: "website-corporate"`);
|
|
69
|
+
expect(out).toContain(`mode: "website"`);
|
|
70
|
+
expect(out).toContain(`webshop: false`);
|
|
71
|
+
expect(out).toContain(`a2a: false`);
|
|
72
|
+
expect(out).toContain(`adminAgenticDashboard: false`);
|
|
73
|
+
});
|
|
74
|
+
it("generic preserves webshop mode with no A2A flags on", () => {
|
|
75
|
+
const out = patchBrandConfigForTemplate(BRAND_FRAGMENT, "generic");
|
|
76
|
+
expect(out).toContain(`industryTemplate: "generic"`);
|
|
77
|
+
expect(out).toContain(`mode: "webshop"`);
|
|
78
|
+
expect(out).toContain(`webshop: true`);
|
|
79
|
+
expect(out).toContain(`a2a: false`);
|
|
80
|
+
});
|
|
81
|
+
it("does not touch unrelated feature flags (tryOn, aiStylist)", () => {
|
|
82
|
+
const out = patchBrandConfigForTemplate(BRAND_FRAGMENT, "coffee");
|
|
83
|
+
expect(out).toContain(`tryOn: false`);
|
|
84
|
+
expect(out).toContain(`aiStylist: true`);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe("TEMPLATE_DEFAULTS — invariants", () => {
|
|
88
|
+
it("every template has all four mode-features defined", () => {
|
|
89
|
+
for (const slug of TEMPLATE_SLUGS) {
|
|
90
|
+
const d = TEMPLATE_DEFAULTS[slug];
|
|
91
|
+
expect(typeof d.features.webshop).toBe("boolean");
|
|
92
|
+
expect(typeof d.features.acp).toBe("boolean");
|
|
93
|
+
expect(typeof d.features.a2a).toBe("boolean");
|
|
94
|
+
expect(typeof d.features.adminAgenticDashboard).toBe("boolean");
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
it("agent-marketplace is the only template that turns a2a on", () => {
|
|
98
|
+
for (const slug of TEMPLATE_SLUGS) {
|
|
99
|
+
const a2aOn = TEMPLATE_DEFAULTS[slug].features.a2a;
|
|
100
|
+
if (slug === "agent-marketplace") {
|
|
101
|
+
expect(a2aOn).toBe(true);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
expect(a2aOn).toBe(false);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
//# sourceMappingURL=scaffold.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.test.js","sourceRoot":"","sources":["../src/scaffold.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-cartwright",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Scaffolder for AI-first webshops powered by Cartwright",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cartwright",
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
"dev": "tsx src/index.ts",
|
|
27
27
|
"typecheck": "tsc --noEmit",
|
|
28
28
|
"lint": "eslint src --max-warnings=0",
|
|
29
|
-
"test": "
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest",
|
|
30
31
|
"prepublishOnly": "pnpm build"
|
|
31
32
|
},
|
|
32
33
|
"dependencies": {
|
|
@@ -39,7 +40,8 @@
|
|
|
39
40
|
"@types/node": "^22.10.0",
|
|
40
41
|
"eslint": "^9.0.0",
|
|
41
42
|
"tsx": "^4.19.0",
|
|
42
|
-
"typescript": "^5.7.0"
|
|
43
|
+
"typescript": "^5.7.0",
|
|
44
|
+
"vitest": "^4.1.7"
|
|
43
45
|
},
|
|
44
46
|
"engines": {
|
|
45
47
|
"node": ">=22"
|