create-ollie-shop 1.0.0 → 1.3.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/.turbo/turbo-build.log +3 -3
- package/CHANGELOG.md +18 -0
- package/dist/index.js +52 -0
- package/dist/template/.claude/build-component.mjs +82 -0
- package/dist/template/.claude/registry.json +32 -0
- package/dist/template/.mcp.json +8 -0
- package/dist/template/_package.json +2 -1
- package/package.json +3 -1
- package/src/create-project.ts +76 -0
- package/template/.claude/build-component.mjs +82 -0
- package/template/.claude/registry.json +32 -0
- package/template/.mcp.json +8 -0
- package/template/_package.json +2 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> create-ollie-shop@1.
|
|
2
|
+
> create-ollie-shop@1.3.0 build /home/runner/work/ollie-shop/ollie-shop/packages/create-ollie-shop
|
|
3
3
|
> tsup
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -9,5 +9,5 @@
|
|
|
9
9
|
[34mCLI[39m Target: es2022
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mESM[39m Build start
|
|
12
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
13
|
-
[32mESM[39m ⚡️ Build success in
|
|
12
|
+
[32mESM[39m [1mdist/index.js [22m[32m7.76 KB[39m
|
|
13
|
+
[32mESM[39m ⚡️ Build success in 281ms
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# create-ollie-shop
|
|
2
2
|
|
|
3
|
+
## 1.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- bca53ae: Install Ollie agent skills into the project on scaffolding. After copying the template, `create-ollie-shop` downloads the latest release tag of the public skill mirror at `github.com/ollie-shop/skills`, extracts `skills/ollie-shop/` into the new project's `.claude/skills/ollie-shop/` directory, and stamps `.claude/skills/ollie-shop/.version` with the installed version. The install path matches the default location of `npx skills add ollie-shop/skills` so customers see the same `.claude/skills/ollie-shop/` regardless of which onboarding path they choose. The stale `template/.claude/skills/` and `template/.claude/agents/` are removed in favor of the live download.
|
|
8
|
+
|
|
9
|
+
## 1.2.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- bac681e: Scaffold an agentic Studio workflow into new projects: an `ollie_checkout_studio` agent plus `capability.design_reference` and `capability.studio_harness` skills, a server-less build helper (`.claude/build-component.mjs`), and a `chrome-devtools` MCP config. This lets an agent ingest a visual reference (a live site, Figma, or screenshot), build the component independently with esbuild, and inject its source into the store's real checkout via the hosted studio harness at `admin.ollie.shop/studio-harness.html` (same-site, so no cookie handling) — iterating from screenshots, then deploying.
|
|
14
|
+
|
|
15
|
+
## 1.1.0
|
|
16
|
+
|
|
17
|
+
### Minor Changes
|
|
18
|
+
|
|
19
|
+
- 3292be9: feat: add create custom component claude prompt to create-ollie-project cli
|
|
20
|
+
|
|
3
21
|
## 1.0.0
|
|
4
22
|
|
|
5
23
|
### Major Changes
|
package/dist/index.js
CHANGED
|
@@ -10,13 +10,21 @@ import { z } from "zod";
|
|
|
10
10
|
// src/create-project.ts
|
|
11
11
|
import { exec } from "child_process";
|
|
12
12
|
import fs from "fs/promises";
|
|
13
|
+
import os from "os";
|
|
13
14
|
import path from "path";
|
|
15
|
+
import { Readable } from "stream";
|
|
16
|
+
import { pipeline } from "stream/promises";
|
|
14
17
|
import { fileURLToPath } from "url";
|
|
15
18
|
import { promisify } from "util";
|
|
16
19
|
import fsExtra from "fs-extra";
|
|
17
20
|
import slugify from "slugify";
|
|
21
|
+
import * as tar from "tar";
|
|
18
22
|
var execAsync = promisify(exec);
|
|
19
23
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
24
|
+
var SKILLS_MIRROR_OWNER = "ollie-shop";
|
|
25
|
+
var SKILLS_MIRROR_REPO = "skills";
|
|
26
|
+
var SKILLS_API_BASE = `https://api.github.com/repos/${SKILLS_MIRROR_OWNER}/${SKILLS_MIRROR_REPO}`;
|
|
27
|
+
var SKILLS_TARBALL_BASE = `https://codeload.github.com/${SKILLS_MIRROR_OWNER}/${SKILLS_MIRROR_REPO}/tar.gz`;
|
|
20
28
|
async function copyWithTemplate(from, to, variables) {
|
|
21
29
|
const dirname = path.dirname(to);
|
|
22
30
|
await fsExtra.ensureDir(dirname);
|
|
@@ -66,6 +74,48 @@ async function copyTemplateDir(templateDir, targetDir, variables) {
|
|
|
66
74
|
}
|
|
67
75
|
}
|
|
68
76
|
}
|
|
77
|
+
async function fetchLatestSkillsTag() {
|
|
78
|
+
const res = await fetch(`${SKILLS_API_BASE}/tags`);
|
|
79
|
+
if (!res.ok) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`Failed to list tags from ${SKILLS_MIRROR_OWNER}/${SKILLS_MIRROR_REPO}: ${res.status} ${res.statusText}`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
const tags = await res.json();
|
|
85
|
+
if (tags.length === 0) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
`No release tags published on ${SKILLS_MIRROR_OWNER}/${SKILLS_MIRROR_REPO} yet.`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
return tags[0].name;
|
|
91
|
+
}
|
|
92
|
+
async function installSkills(projectPath) {
|
|
93
|
+
const tag = await fetchLatestSkillsTag();
|
|
94
|
+
const version = tag.startsWith("v") ? tag.slice(1) : tag;
|
|
95
|
+
const tarballUrl = `${SKILLS_TARBALL_BASE}/refs/tags/${tag}`;
|
|
96
|
+
const res = await fetch(tarballUrl);
|
|
97
|
+
if (!res.ok || !res.body) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Failed to download ${tarballUrl}: ${res.status} ${res.statusText}`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "ollie-skills-"));
|
|
103
|
+
try {
|
|
104
|
+
await pipeline(
|
|
105
|
+
Readable.fromWeb(res.body),
|
|
106
|
+
tar.x({ cwd: tempDir })
|
|
107
|
+
);
|
|
108
|
+
const entries = await fs.readdir(tempDir);
|
|
109
|
+
const archiveRoot = path.join(tempDir, entries[0]);
|
|
110
|
+
const skillSrc = path.join(archiveRoot, "skills", "ollie-shop");
|
|
111
|
+
const skillDest = path.join(projectPath, ".claude", "skills", "ollie-shop");
|
|
112
|
+
await fsExtra.copy(skillSrc, skillDest);
|
|
113
|
+
await fs.writeFile(path.join(skillDest, ".version"), `${version}
|
|
114
|
+
`);
|
|
115
|
+
} finally {
|
|
116
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
69
119
|
async function createProject(projectPath, options) {
|
|
70
120
|
const { storeId, versionId } = options;
|
|
71
121
|
const projectName = slugify(path.basename(projectPath), { lower: true });
|
|
@@ -89,6 +139,8 @@ async function createProject(projectPath, options) {
|
|
|
89
139
|
ollieConfig.versionId = versionId;
|
|
90
140
|
await fs.writeFile(ollieConfigPath, JSON.stringify(ollieConfig, null, 2));
|
|
91
141
|
}
|
|
142
|
+
console.log("\u{1F9E0} Installing Ollie skills...");
|
|
143
|
+
await installSkills(projectPath);
|
|
92
144
|
console.log("\u{1F527} Initializing git repository...");
|
|
93
145
|
try {
|
|
94
146
|
await execAsync("git init", { cwd: projectPath });
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Server-less, one-shot build for a single checkout component.
|
|
2
|
+
//
|
|
3
|
+
// This is the canonical build the agent runs in the studio loop — no dev server,
|
|
4
|
+
// no websocket. It mirrors the platform build (the options below match
|
|
5
|
+
// @ollie-shop/cli's createBuildContext) so that what you PREVIEW equals what you
|
|
6
|
+
// DEPLOY. Keep these options in sync with the CLI if the platform build changes.
|
|
7
|
+
//
|
|
8
|
+
// Usage: node .claude/build-component.mjs <ComponentName>
|
|
9
|
+
// Output: JSON on stdout -> { name, id, slot, source, css, errors }
|
|
10
|
+
// - source: bundled CJS (sets module.exports.default; the checkout runtime
|
|
11
|
+
// mounts it via new Function). CSS-module class maps are baked in.
|
|
12
|
+
// - css: bundled stylesheet TEXT (inject via the gateway as a <style>).
|
|
13
|
+
// The agent then posts { id, slot, source, css } through the studio message
|
|
14
|
+
// gateway — text only; URLs/blobs can't cross the iframe origin boundary.
|
|
15
|
+
|
|
16
|
+
import { readFile } from "node:fs/promises";
|
|
17
|
+
import os from "node:os";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import { build } from "esbuild";
|
|
20
|
+
|
|
21
|
+
const name = process.argv[2];
|
|
22
|
+
if (!name) {
|
|
23
|
+
process.stdout.write(
|
|
24
|
+
JSON.stringify({
|
|
25
|
+
errors: ["usage: node .claude/build-component.mjs <ComponentName>"],
|
|
26
|
+
}),
|
|
27
|
+
);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const cwd = process.cwd();
|
|
32
|
+
const dir = path.join(cwd, "components", name);
|
|
33
|
+
|
|
34
|
+
// meta.json carries the component's linked id (null until `ollieshop component
|
|
35
|
+
// create`) and its target slot.
|
|
36
|
+
let meta = {};
|
|
37
|
+
try {
|
|
38
|
+
meta = JSON.parse(await readFile(path.join(dir, "meta.json"), "utf8"));
|
|
39
|
+
} catch {
|
|
40
|
+
// no meta.json yet — fall back to a studio-* id and a null slot
|
|
41
|
+
}
|
|
42
|
+
const id = meta.id ?? `studio-${name}`; // studio merges by slot; a stable id is enough for preview
|
|
43
|
+
const slot = meta.slot ?? null;
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const result = await build({
|
|
47
|
+
entryPoints: { [`${name}/index`]: path.join(dir, "index.tsx") },
|
|
48
|
+
bundle: true,
|
|
49
|
+
write: false,
|
|
50
|
+
// outdir is required so CSS imports get an output path; nothing is written to disk.
|
|
51
|
+
outdir: path.join(os.tmpdir(), "ollie-build-out"),
|
|
52
|
+
format: "cjs", // runtime mounts module.exports.default
|
|
53
|
+
platform: "browser",
|
|
54
|
+
target: "es2020",
|
|
55
|
+
jsx: "automatic",
|
|
56
|
+
// provided by the checkout runtime via the require() shim — never bundle these
|
|
57
|
+
external: ["react", "react-dom", "next", "@ollie-shop/sdk", "next-intl"],
|
|
58
|
+
loader: {
|
|
59
|
+
".tsx": "tsx",
|
|
60
|
+
".ts": "ts",
|
|
61
|
+
".js": "js",
|
|
62
|
+
".jsx": "jsx",
|
|
63
|
+
".css": "css",
|
|
64
|
+
},
|
|
65
|
+
logLevel: "silent",
|
|
66
|
+
});
|
|
67
|
+
const source =
|
|
68
|
+
result.outputFiles.find((f) => f.path.endsWith(".js"))?.text ?? "";
|
|
69
|
+
const css =
|
|
70
|
+
result.outputFiles.find((f) => f.path.endsWith(".css"))?.text ?? "";
|
|
71
|
+
process.stdout.write(
|
|
72
|
+
JSON.stringify({ name, id, slot, source, css, errors: [] }),
|
|
73
|
+
);
|
|
74
|
+
} catch (e) {
|
|
75
|
+
const errors = e.errors?.map(
|
|
76
|
+
(x) => `${x.location?.file ?? ""}:${x.location?.line ?? ""} ${x.text}`,
|
|
77
|
+
) ?? [e.message];
|
|
78
|
+
process.stdout.write(
|
|
79
|
+
JSON.stringify({ name, id, slot, source: "", css: "", errors }),
|
|
80
|
+
);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.1.0",
|
|
3
|
+
"_maintenance": "Ollie Checkout Skill Runtime Registry. Defines the deterministic loading order for all skills. To add a new skill: create the .md file in skills/, add it here in the correct section, and reference it in the agent file.",
|
|
4
|
+
"_skillDescriptions": {
|
|
5
|
+
"core.identity": "Agent persona, priorities and communication style",
|
|
6
|
+
"core.rules": "Agent behavior rules and component code rules",
|
|
7
|
+
"core.constraints": "SDK usage rules, domain isolation, and hard limits for components",
|
|
8
|
+
"agent.components": "4-step workflow for component creation (inputs, questions, output, quality)",
|
|
9
|
+
"capability.react": "React/TypeScript standards: functional components, hooks, naming, prop patterns",
|
|
10
|
+
"capability.sdk": "Full @ollie-shop/sdk reference: hooks, types, action types, integration pattern",
|
|
11
|
+
"capability.component_architecture": "Folder structure, CSS naming convention, image handling, README template",
|
|
12
|
+
"capability.design_reference": "Ingest a live site / Figma / screenshot via the browser, extract structure + tokens, produce a build spec",
|
|
13
|
+
"capability.studio_harness": "Build a component server-less (esbuild) and inject its source into the real checkout via the hosted studio harness at admin.ollie.shop (chrome-devtools MCP), then screenshot and iterate"
|
|
14
|
+
},
|
|
15
|
+
"core": ["core.identity", "core.rules", "core.constraints"],
|
|
16
|
+
"agents": {
|
|
17
|
+
"components": [
|
|
18
|
+
"agent.components",
|
|
19
|
+
"capability.react",
|
|
20
|
+
"capability.sdk",
|
|
21
|
+
"capability.component_architecture"
|
|
22
|
+
],
|
|
23
|
+
"checkout_studio": [
|
|
24
|
+
"agent.components",
|
|
25
|
+
"capability.react",
|
|
26
|
+
"capability.sdk",
|
|
27
|
+
"capability.component_architecture",
|
|
28
|
+
"capability.design_reference",
|
|
29
|
+
"capability.studio_harness"
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
},
|
|
9
9
|
"devDependencies": {
|
|
10
10
|
"@ollie-shop/cli": "latest",
|
|
11
|
-
"@ollie-shop/sdk": "
|
|
11
|
+
"@ollie-shop/sdk": "^1",
|
|
12
12
|
"@types/node": "^22.14.0",
|
|
13
13
|
"@types/react": "^19.2.7",
|
|
14
|
+
"esbuild": "^0.25.0",
|
|
14
15
|
"react": "^19.0.0",
|
|
15
16
|
"typescript": "^5.7.3"
|
|
16
17
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-ollie-shop",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"devDependencies": {
|
|
11
11
|
"@types/fs-extra": "^11.0.4",
|
|
12
12
|
"@types/node": "^22.14.0",
|
|
13
|
+
"@types/tar": "^6.1.13",
|
|
13
14
|
"tsup": "^8.4.0",
|
|
14
15
|
"typescript": "^5.7.3",
|
|
15
16
|
"vitest": "^3.0.4"
|
|
@@ -18,6 +19,7 @@
|
|
|
18
19
|
"fs-extra": "^11.3.0",
|
|
19
20
|
"meow": "^13.2.0",
|
|
20
21
|
"slugify": "^1.6.6",
|
|
22
|
+
"tar": "^7.4.3",
|
|
21
23
|
"zod": "^3.24.2"
|
|
22
24
|
},
|
|
23
25
|
"publishConfig": {
|
package/src/create-project.ts
CHANGED
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
import { exec } from "node:child_process";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
3
4
|
import path from "node:path";
|
|
5
|
+
import { Readable } from "node:stream";
|
|
6
|
+
import { pipeline } from "node:stream/promises";
|
|
4
7
|
import { fileURLToPath } from "node:url";
|
|
5
8
|
import { promisify } from "node:util";
|
|
6
9
|
import fsExtra from "fs-extra";
|
|
7
10
|
import slugify from "slugify";
|
|
11
|
+
import * as tar from "tar";
|
|
8
12
|
|
|
9
13
|
const execAsync = promisify(exec);
|
|
10
14
|
|
|
11
15
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
16
|
|
|
17
|
+
const SKILLS_MIRROR_OWNER = "ollie-shop";
|
|
18
|
+
const SKILLS_MIRROR_REPO = "skills";
|
|
19
|
+
const SKILLS_API_BASE = `https://api.github.com/repos/${SKILLS_MIRROR_OWNER}/${SKILLS_MIRROR_REPO}`;
|
|
20
|
+
const SKILLS_TARBALL_BASE = `https://codeload.github.com/${SKILLS_MIRROR_OWNER}/${SKILLS_MIRROR_REPO}/tar.gz`;
|
|
21
|
+
|
|
13
22
|
interface CreateProjectOptions {
|
|
14
23
|
storeId: string;
|
|
15
24
|
versionId?: string;
|
|
@@ -101,6 +110,68 @@ async function copyTemplateDir(
|
|
|
101
110
|
}
|
|
102
111
|
}
|
|
103
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Ask the public mirror at github.com/ollie-shop/skills for its newest tag.
|
|
115
|
+
* Tags are created by the sync workflow on each release (vX.Y.Z); the GitHub
|
|
116
|
+
* API returns them ordered by creation date descending, so the first entry is
|
|
117
|
+
* the latest release.
|
|
118
|
+
*/
|
|
119
|
+
async function fetchLatestSkillsTag(): Promise<string> {
|
|
120
|
+
const res = await fetch(`${SKILLS_API_BASE}/tags`);
|
|
121
|
+
if (!res.ok) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`Failed to list tags from ${SKILLS_MIRROR_OWNER}/${SKILLS_MIRROR_REPO}: ${res.status} ${res.statusText}`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
const tags = (await res.json()) as Array<{ name: string }>;
|
|
127
|
+
if (tags.length === 0) {
|
|
128
|
+
throw new Error(
|
|
129
|
+
`No release tags published on ${SKILLS_MIRROR_OWNER}/${SKILLS_MIRROR_REPO} yet.`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
return tags[0].name;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Download the ollie-shop skill from the public mirror and install it into
|
|
137
|
+
* the new project's `.claude/skills/ollie-shop/` directory (the default
|
|
138
|
+
* install location the vercel-labs/skills CLI uses). The installed version
|
|
139
|
+
* is recorded in `.claude/skills/ollie-shop/.version` so future updates can
|
|
140
|
+
* detect drift.
|
|
141
|
+
*/
|
|
142
|
+
async function installSkills(projectPath: string): Promise<void> {
|
|
143
|
+
const tag = await fetchLatestSkillsTag();
|
|
144
|
+
const version = tag.startsWith("v") ? tag.slice(1) : tag;
|
|
145
|
+
|
|
146
|
+
const tarballUrl = `${SKILLS_TARBALL_BASE}/refs/tags/${tag}`;
|
|
147
|
+
const res = await fetch(tarballUrl);
|
|
148
|
+
if (!res.ok || !res.body) {
|
|
149
|
+
throw new Error(
|
|
150
|
+
`Failed to download ${tarballUrl}: ${res.status} ${res.statusText}`,
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "ollie-skills-"));
|
|
155
|
+
try {
|
|
156
|
+
// Extract the full tarball; the archive wraps everything in a single
|
|
157
|
+
// top-level directory like `ollie-shop-skills-<sha>/`.
|
|
158
|
+
await pipeline(
|
|
159
|
+
Readable.fromWeb(res.body as never),
|
|
160
|
+
tar.x({ cwd: tempDir }),
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
const entries = await fs.readdir(tempDir);
|
|
164
|
+
const archiveRoot = path.join(tempDir, entries[0]);
|
|
165
|
+
const skillSrc = path.join(archiveRoot, "skills", "ollie-shop");
|
|
166
|
+
const skillDest = path.join(projectPath, ".claude", "skills", "ollie-shop");
|
|
167
|
+
|
|
168
|
+
await fsExtra.copy(skillSrc, skillDest);
|
|
169
|
+
await fs.writeFile(path.join(skillDest, ".version"), `${version}\n`);
|
|
170
|
+
} finally {
|
|
171
|
+
await fs.rm(tempDir, { recursive: true, force: true });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
104
175
|
/**
|
|
105
176
|
* Create a new Ollie Shop project
|
|
106
177
|
*/
|
|
@@ -144,6 +215,11 @@ export async function createProject(
|
|
|
144
215
|
await fs.writeFile(ollieConfigPath, JSON.stringify(ollieConfig, null, 2));
|
|
145
216
|
}
|
|
146
217
|
|
|
218
|
+
// Install Ollie skills from the public mirror into .claude/skills/ollie-shop/
|
|
219
|
+
// (the default location vercel-labs/skills CLI uses for `npx skills add`)
|
|
220
|
+
console.log("🧠 Installing Ollie skills...");
|
|
221
|
+
await installSkills(projectPath);
|
|
222
|
+
|
|
147
223
|
// Initialize git
|
|
148
224
|
console.log("🔧 Initializing git repository...");
|
|
149
225
|
try {
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Server-less, one-shot build for a single checkout component.
|
|
2
|
+
//
|
|
3
|
+
// This is the canonical build the agent runs in the studio loop — no dev server,
|
|
4
|
+
// no websocket. It mirrors the platform build (the options below match
|
|
5
|
+
// @ollie-shop/cli's createBuildContext) so that what you PREVIEW equals what you
|
|
6
|
+
// DEPLOY. Keep these options in sync with the CLI if the platform build changes.
|
|
7
|
+
//
|
|
8
|
+
// Usage: node .claude/build-component.mjs <ComponentName>
|
|
9
|
+
// Output: JSON on stdout -> { name, id, slot, source, css, errors }
|
|
10
|
+
// - source: bundled CJS (sets module.exports.default; the checkout runtime
|
|
11
|
+
// mounts it via new Function). CSS-module class maps are baked in.
|
|
12
|
+
// - css: bundled stylesheet TEXT (inject via the gateway as a <style>).
|
|
13
|
+
// The agent then posts { id, slot, source, css } through the studio message
|
|
14
|
+
// gateway — text only; URLs/blobs can't cross the iframe origin boundary.
|
|
15
|
+
|
|
16
|
+
import { readFile } from "node:fs/promises";
|
|
17
|
+
import os from "node:os";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
import { build } from "esbuild";
|
|
20
|
+
|
|
21
|
+
const name = process.argv[2];
|
|
22
|
+
if (!name) {
|
|
23
|
+
process.stdout.write(
|
|
24
|
+
JSON.stringify({
|
|
25
|
+
errors: ["usage: node .claude/build-component.mjs <ComponentName>"],
|
|
26
|
+
}),
|
|
27
|
+
);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const cwd = process.cwd();
|
|
32
|
+
const dir = path.join(cwd, "components", name);
|
|
33
|
+
|
|
34
|
+
// meta.json carries the component's linked id (null until `ollieshop component
|
|
35
|
+
// create`) and its target slot.
|
|
36
|
+
let meta = {};
|
|
37
|
+
try {
|
|
38
|
+
meta = JSON.parse(await readFile(path.join(dir, "meta.json"), "utf8"));
|
|
39
|
+
} catch {
|
|
40
|
+
// no meta.json yet — fall back to a studio-* id and a null slot
|
|
41
|
+
}
|
|
42
|
+
const id = meta.id ?? `studio-${name}`; // studio merges by slot; a stable id is enough for preview
|
|
43
|
+
const slot = meta.slot ?? null;
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const result = await build({
|
|
47
|
+
entryPoints: { [`${name}/index`]: path.join(dir, "index.tsx") },
|
|
48
|
+
bundle: true,
|
|
49
|
+
write: false,
|
|
50
|
+
// outdir is required so CSS imports get an output path; nothing is written to disk.
|
|
51
|
+
outdir: path.join(os.tmpdir(), "ollie-build-out"),
|
|
52
|
+
format: "cjs", // runtime mounts module.exports.default
|
|
53
|
+
platform: "browser",
|
|
54
|
+
target: "es2020",
|
|
55
|
+
jsx: "automatic",
|
|
56
|
+
// provided by the checkout runtime via the require() shim — never bundle these
|
|
57
|
+
external: ["react", "react-dom", "next", "@ollie-shop/sdk", "next-intl"],
|
|
58
|
+
loader: {
|
|
59
|
+
".tsx": "tsx",
|
|
60
|
+
".ts": "ts",
|
|
61
|
+
".js": "js",
|
|
62
|
+
".jsx": "jsx",
|
|
63
|
+
".css": "css",
|
|
64
|
+
},
|
|
65
|
+
logLevel: "silent",
|
|
66
|
+
});
|
|
67
|
+
const source =
|
|
68
|
+
result.outputFiles.find((f) => f.path.endsWith(".js"))?.text ?? "";
|
|
69
|
+
const css =
|
|
70
|
+
result.outputFiles.find((f) => f.path.endsWith(".css"))?.text ?? "";
|
|
71
|
+
process.stdout.write(
|
|
72
|
+
JSON.stringify({ name, id, slot, source, css, errors: [] }),
|
|
73
|
+
);
|
|
74
|
+
} catch (e) {
|
|
75
|
+
const errors = e.errors?.map(
|
|
76
|
+
(x) => `${x.location?.file ?? ""}:${x.location?.line ?? ""} ${x.text}`,
|
|
77
|
+
) ?? [e.message];
|
|
78
|
+
process.stdout.write(
|
|
79
|
+
JSON.stringify({ name, id, slot, source: "", css: "", errors }),
|
|
80
|
+
);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.1.0",
|
|
3
|
+
"_maintenance": "Ollie Checkout Skill Runtime Registry. Defines the deterministic loading order for all skills. To add a new skill: create the .md file in skills/, add it here in the correct section, and reference it in the agent file.",
|
|
4
|
+
"_skillDescriptions": {
|
|
5
|
+
"core.identity": "Agent persona, priorities and communication style",
|
|
6
|
+
"core.rules": "Agent behavior rules and component code rules",
|
|
7
|
+
"core.constraints": "SDK usage rules, domain isolation, and hard limits for components",
|
|
8
|
+
"agent.components": "4-step workflow for component creation (inputs, questions, output, quality)",
|
|
9
|
+
"capability.react": "React/TypeScript standards: functional components, hooks, naming, prop patterns",
|
|
10
|
+
"capability.sdk": "Full @ollie-shop/sdk reference: hooks, types, action types, integration pattern",
|
|
11
|
+
"capability.component_architecture": "Folder structure, CSS naming convention, image handling, README template",
|
|
12
|
+
"capability.design_reference": "Ingest a live site / Figma / screenshot via the browser, extract structure + tokens, produce a build spec",
|
|
13
|
+
"capability.studio_harness": "Build a component server-less (esbuild) and inject its source into the real checkout via the hosted studio harness at admin.ollie.shop (chrome-devtools MCP), then screenshot and iterate"
|
|
14
|
+
},
|
|
15
|
+
"core": ["core.identity", "core.rules", "core.constraints"],
|
|
16
|
+
"agents": {
|
|
17
|
+
"components": [
|
|
18
|
+
"agent.components",
|
|
19
|
+
"capability.react",
|
|
20
|
+
"capability.sdk",
|
|
21
|
+
"capability.component_architecture"
|
|
22
|
+
],
|
|
23
|
+
"checkout_studio": [
|
|
24
|
+
"agent.components",
|
|
25
|
+
"capability.react",
|
|
26
|
+
"capability.sdk",
|
|
27
|
+
"capability.component_architecture",
|
|
28
|
+
"capability.design_reference",
|
|
29
|
+
"capability.studio_harness"
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
}
|
package/template/_package.json
CHANGED
|
@@ -8,9 +8,10 @@
|
|
|
8
8
|
},
|
|
9
9
|
"devDependencies": {
|
|
10
10
|
"@ollie-shop/cli": "latest",
|
|
11
|
-
"@ollie-shop/sdk": "
|
|
11
|
+
"@ollie-shop/sdk": "^1",
|
|
12
12
|
"@types/node": "^22.14.0",
|
|
13
13
|
"@types/react": "^19.2.7",
|
|
14
|
+
"esbuild": "^0.25.0",
|
|
14
15
|
"react": "^19.0.0",
|
|
15
16
|
"typescript": "^5.7.3"
|
|
16
17
|
}
|