@thorprovider/storefront 0.3.1 → 0.4.0-mvp.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 +321 -152
- package/VENDOR.md +69 -0
- package/assets/openspec/config.yaml +38 -0
- package/assets/templates/AGENTS.md +76 -0
- package/assets/templates/app/app/[[...slug]]/page.tsx +54 -2
- package/assets/templates/app/app/[[...slug]]/renderer.tsx +13 -2
- package/assets/templates/app/app/globals.css +5 -8
- package/assets/templates/app/app/layout.tsx +17 -7
- package/assets/templates/app/app/robots.ts +19 -0
- package/assets/templates/app/app/sitemap.ts +71 -0
- package/assets/templates/app/lib/__STOREFRONT__/actions.ts +612 -0
- package/assets/templates/app/lib/__STOREFRONT__/cart-state.ts +66 -0
- package/assets/templates/app/lib/__STOREFRONT__/catalog.ts +50 -57
- package/assets/templates/app/lib/__STOREFRONT__/commerce.ts +679 -0
- package/assets/templates/app/lib/__STOREFRONT__/composition.ts +26 -0
- package/assets/templates/app/lib/__STOREFRONT__/handlers.core.ts +197 -0
- package/assets/templates/app/lib/__STOREFRONT__/handlers.ts +5 -36
- package/assets/templates/app/lib/__STOREFRONT__/icon-map.ts +27 -0
- package/assets/templates/app/lib/__STOREFRONT__/mappers.ts +13 -11
- package/assets/templates/app/lib/__STOREFRONT__/registry.tsx +264 -51
- package/assets/templates/app/lib/__STOREFRONT__/runtime.ts +6 -3
- package/assets/templates/app/lib/__STOREFRONT__/schema.ts +136 -0
- package/assets/templates/app/lib/__STOREFRONT__/section-observation.ts +182 -0
- package/assets/templates/app/lib/__STOREFRONT__/section-sources.ts +130 -0
- package/assets/templates/app/lib/__STOREFRONT__/session.core.ts +470 -0
- package/assets/templates/app/lib/__STOREFRONT__/session.ts +169 -0
- package/assets/templates/app/lib/__STOREFRONT__/shell/content.ts +120 -0
- package/assets/templates/app/lib/__STOREFRONT__/shell/props.ts +38 -0
- package/assets/templates/app/lib/__STOREFRONT__/spec/index.ts +129 -10
- package/assets/templates/app/lib/__STOREFRONT__/spec/page-root.ts +5 -1
- package/assets/templates/app/lib/__STOREFRONT__/spec/seo.ts +191 -0
- package/assets/templates/app/lib/__STOREFRONT__/state.core.ts +257 -0
- package/assets/templates/app/lib/__STOREFRONT__/state.ts +7 -31
- package/assets/templates/app/lib/__STOREFRONT__/store-context.ts +161 -0
- package/assets/templates/app/lib/__STOREFRONT__/views/sections/cta.client.tsx +25 -0
- package/assets/templates/app/lib/__STOREFRONT__/views/sections/cta.tsx +16 -0
- package/assets/templates/app/lib/__STOREFRONT__/views/sections/faq.client.tsx +24 -0
- package/assets/templates/app/lib/__STOREFRONT__/views/sections/faq.tsx +5 -18
- package/assets/templates/app/lib/__STOREFRONT__/views/sections/index.ts +18 -0
- package/assets/templates/app/lib/__STOREFRONT__/views/sections/logoCloud.tsx +26 -14
- package/assets/templates/app/lib/__STOREFRONT__/views/sections/pricing.tsx +36 -18
- package/assets/templates/app/lib/__STOREFRONT__/views/sections/products.client.tsx +100 -0
- package/assets/templates/app/lib/__STOREFRONT__/views/sections/products.tsx +19 -0
- package/assets/templates/app/lib/__STOREFRONT__/views/sections/schema.cta.ts +19 -0
- package/assets/templates/app/lib/__STOREFRONT__/views/sections/schema.faq.ts +18 -0
- package/assets/templates/app/lib/__STOREFRONT__/views/sections/schema.products.ts +39 -0
- package/assets/templates/app/lib/__STOREFRONT__/views/sections/statistics.tsx +35 -14
- package/assets/templates/app/lib/__STOREFRONT__/views/sections/testimonials.tsx +31 -13
- package/assets/templates/app/lib/__STOREFRONT__/views/types.ts +5 -3
- package/assets/templates/features/checkout/handlers.ts +159 -0
- package/assets/templates/features/checkout/state.ts +61 -0
- package/assets/templates/features/customer-auth/handlers.ts +59 -0
- package/assets/templates/features/customer-auth/state.ts +45 -0
- package/assets/templates/features/recommendations/state.ts +11 -0
- package/assets/templates/views/account/content.ts +35 -0
- package/assets/templates/views/account/index.tsx +114 -0
- package/assets/templates/views/account/registry.tsx +439 -0
- package/assets/templates/views/account/schema.ts +53 -0
- package/assets/templates/views/cart/content.ts +11 -0
- package/assets/templates/views/cart/data.ts +23 -0
- package/assets/templates/views/cart/index.tsx +53 -0
- package/assets/templates/views/cart/props.ts +12 -0
- package/assets/templates/views/cart/registry.tsx +81 -0
- package/assets/templates/views/cart/schema.ts +52 -0
- package/assets/templates/views/category/assembly/assembly.json +29 -0
- package/assets/templates/views/category/assembly/index.skeleton.tsx +251 -0
- package/assets/templates/views/category/assembly/registry.skeleton.tsx +34 -0
- package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/binding.json +5 -0
- package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/catalog.part +4 -0
- package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/element.part +7 -0
- package/assets/templates/views/category/assembly/slots/breadcrumb/Breadcrumb/registry.part +18 -0
- package/assets/templates/views/category/assembly/slots/filter/CategoryFilter2/catalog.part +5 -0
- package/assets/templates/views/category/assembly/slots/filter/CategoryFilter2/element.part +34 -0
- package/assets/templates/views/category/assembly/slots/filter/CategoryFilter2/registry.part +71 -0
- package/assets/templates/views/category/content.ts +30 -0
- package/assets/templates/views/category/data.ts +12 -0
- package/assets/templates/views/category/index.tsx +299 -0
- package/assets/templates/views/category/props.ts +23 -0
- package/assets/templates/views/category/registry.tsx +122 -0
- package/assets/templates/views/category/schema.ts +91 -0
- package/assets/templates/views/checkout/content.ts +105 -0
- package/assets/templates/views/checkout/data.ts +23 -0
- package/assets/templates/views/checkout/index.tsx +111 -0
- package/assets/templates/views/checkout/props.ts +49 -0
- package/assets/templates/views/checkout/registry.tsx +217 -0
- package/assets/templates/views/checkout/schema.ts +229 -0
- package/assets/templates/views/home/assembly/assembly.json +33 -0
- package/assets/templates/views/home/assembly/index.skeleton.tsx +90 -0
- package/assets/templates/views/home/assembly/registry.skeleton.tsx +25 -0
- package/assets/templates/views/home/assembly/slots/benefits/Feature1/binding.json +5 -0
- package/assets/templates/views/home/assembly/slots/benefits/Feature1/catalog.part +4 -0
- package/assets/templates/views/home/assembly/slots/benefits/Feature1/element.part +4 -0
- package/assets/templates/views/home/assembly/slots/benefits/Feature1/registry.part +11 -0
- package/assets/templates/views/home/assembly/slots/benefits/Feature2/binding.json +5 -0
- package/assets/templates/views/home/assembly/slots/benefits/Feature2/catalog.part +4 -0
- package/assets/templates/views/home/assembly/slots/benefits/Feature2/element.part +4 -0
- package/assets/templates/views/home/assembly/slots/benefits/Feature2/registry.part +10 -0
- package/assets/templates/views/home/assembly/slots/categories/ProductCategory2/binding.json +5 -0
- package/assets/templates/views/home/assembly/slots/categories/ProductCategory2/catalog.part +4 -0
- package/assets/templates/views/home/assembly/slots/categories/ProductCategory2/element.part +5 -0
- package/assets/templates/views/home/assembly/slots/categories/ProductCategory2/registry.part +16 -0
- package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/binding.json +5 -0
- package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/catalog.part +4 -0
- package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/element.part +5 -0
- package/assets/templates/views/home/assembly/slots/hero/StorefrontHero1/registry.part +26 -0
- package/assets/templates/views/home/content.ts +70 -0
- package/assets/templates/views/home/data.ts +24 -0
- package/assets/templates/views/home/index.tsx +57 -85
- package/assets/templates/views/home/props.ts +22 -0
- package/assets/templates/views/home/registry.tsx +38 -22
- package/assets/templates/views/home/schema.ts +93 -0
- package/assets/templates/views/product/content.ts +25 -0
- package/assets/templates/views/product/data.ts +50 -0
- package/assets/templates/views/product/index.tsx +148 -0
- package/assets/templates/views/product/props.ts +27 -0
- package/assets/templates/views/product/registry.tsx +103 -0
- package/assets/templates/views/product/schema.ts +121 -0
- package/assets/templates/views/static/content.ts +99 -0
- package/assets/templates/views/static/index.tsx +60 -0
- package/assets/templates/views/static/registry.tsx +66 -0
- package/assets/templates/views/static/schema.ts +45 -0
- package/bin/storefront.js +2424 -148
- package/examples/merchandising/category-led.json +37 -0
- package/examples/merchandising/collection-led.json +37 -0
- package/package.json +15 -9
- package/recipes/archetypes.json +28 -14
- package/recipes/catalog.json +400 -0
- package/recipes/catalog.mjs +128 -0
- package/recipes/catalog.schema.json +64 -0
- package/recipes/feature.schema.json +89 -0
- package/recipes/features.json +162 -0
- package/recipes/manifest.schema.json +69 -0
- package/recipes/operations.schema.json +43 -0
- package/recipes/recipe.schema.json +11 -2
- package/recipes/recipes.json +76 -19
- package/recipes/sections.json +34 -5
- package/recipes/validate.mjs +838 -32
- package/scripts/candidate.mjs +236 -0
- package/scripts/composers.mjs +160 -0
- package/scripts/env-file.mjs +86 -0
- package/scripts/file-classes.mjs +121 -0
- package/scripts/logical-controls.mjs +61 -0
- package/scripts/manifest.mjs +298 -0
- package/scripts/migrate-english-routes.mjs +276 -0
- package/scripts/operation-registry.mjs +256 -0
- package/scripts/operations.mjs +720 -0
- package/scripts/ownership-conflict.mjs +356 -0
- package/scripts/pinning.mjs +573 -0
- package/scripts/plan-apply.mjs +987 -0
- package/scripts/preflight.mjs +51 -0
- package/scripts/reconcile.mjs +549 -0
- package/scripts/render-templates.mjs +338 -68
- package/scripts/schema-validate.mjs +132 -0
- package/scripts/section-compat.mjs +300 -0
- package/scripts/section-config.mjs +253 -0
- package/scripts/section-operations.mjs +508 -0
- package/scripts/starter.mjs +17 -0
- package/scripts/sync-assets.mjs +22 -15
- package/scripts/theme/color.mjs +185 -0
- package/scripts/theme/css-theme.mjs +233 -0
- package/scripts/theme/fonts.mjs +123 -0
- package/scripts/theme/import-export.mjs +125 -0
- package/scripts/theme/preset.mjs +93 -0
- package/scripts/theme/theme-plan.mjs +292 -0
- package/scripts/theme/tokens.mjs +137 -0
- package/scripts/view-assembly.mjs +337 -0
- package/scripts/view-selection.mjs +211 -0
- package/skills/sf-feature/SKILL.md +80 -0
- package/skills/sf-setup/SKILL.md +79 -0
- package/skills/sf-theme-gen/SKILL.md +100 -30
- package/skills/sf-theme-gen/references/shadcn-theming.md +87 -0
- package/skills/{shadcn-theming/references/templates → sf-theme-gen/references}/theme-setup.md +15 -19
- package/skills/{shadcn-theming → sf-theme-gen}/references/theming-guide.md +6 -11
- package/skills/sf-view-gen/SKILL.md +146 -93
- package/skills/sf-view-gen/assets/recipes/archetypes.json +28 -14
- package/skills/sf-view-gen/assets/recipes/catalog.json +400 -0
- package/skills/sf-view-gen/assets/recipes/catalog.schema.json +64 -0
- package/skills/sf-view-gen/assets/recipes/features.json +162 -0
- package/skills/sf-view-gen/assets/recipes/recipes.json +76 -19
- package/skills/sf-view-gen/assets/recipes/sections.json +34 -5
- package/skills/sf-view-gen/assets/templates/views/sections/cta.client.tsx +25 -0
- package/skills/sf-view-gen/assets/templates/views/sections/cta.tsx +16 -0
- package/skills/sf-view-gen/assets/templates/views/sections/faq.client.tsx +13 -9
- package/skills/sf-view-gen/assets/templates/views/sections/faq.tsx +2 -7
- package/skills/sf-view-gen/assets/templates/views/sections/logoCloud.client.tsx +12 -8
- package/skills/sf-view-gen/assets/templates/views/sections/logoCloud.tsx +2 -7
- package/skills/sf-view-gen/assets/templates/views/sections/pricing.client.tsx +14 -10
- package/skills/sf-view-gen/assets/templates/views/sections/pricing.tsx +2 -9
- package/skills/sf-view-gen/assets/templates/views/sections/products.client.tsx +100 -0
- package/skills/sf-view-gen/assets/templates/views/sections/products.tsx +19 -0
- package/skills/sf-view-gen/assets/templates/views/sections/schema.cta.ts +19 -0
- package/skills/sf-view-gen/assets/templates/views/sections/schema.faq.ts +18 -0
- package/skills/sf-view-gen/assets/templates/views/sections/schema.logoCloud.ts +19 -0
- package/skills/sf-view-gen/assets/templates/views/sections/schema.pricing.ts +26 -0
- package/skills/sf-view-gen/assets/templates/views/sections/schema.products.ts +39 -0
- package/skills/sf-view-gen/assets/templates/views/sections/schema.statistics.ts +25 -0
- package/skills/sf-view-gen/assets/templates/views/sections/schema.testimonials.ts +23 -0
- package/skills/sf-view-gen/assets/templates/views/sections/statistics.client.tsx +16 -8
- package/skills/sf-view-gen/assets/templates/views/sections/statistics.tsx +2 -7
- package/skills/sf-view-gen/assets/templates/views/sections/testimonials.client.tsx +13 -9
- package/skills/sf-view-gen/assets/templates/views/sections/testimonials.tsx +2 -5
- package/skills/{json-render-core/SKILL.md → sf-view-gen/references/json-render-core.md} +4 -27
- package/skills/{json-render-next/SKILL.md → sf-view-gen/references/json-render-next.md} +5 -8
- package/skills/{json-render-react/SKILL.md → sf-view-gen/references/json-render-react.md} +3 -5
- package/skills/{json-render-shadcn/SKILL.md → sf-view-gen/references/json-render-shadcn.md} +3 -5
- package/skills/sf-view-gen/references/views.md +58 -33
- package/skills/sf-view-gen/scripts/catalog.mjs +128 -0
- package/skills/sf-view-gen/scripts/schema-validate.mjs +132 -0
- package/skills/sf-view-gen/scripts/validate.mjs +838 -32
- package/skills/storefront-workflow/SKILL.md +179 -0
- package/skills/{storefront-best-practices/reference/design.md → storefront-workflow/references/design-consistency.md} +13 -29
- package/CHANGELOG.md +0 -61
- package/commands/sf-add-view.md +0 -26
- package/commands/sf-theme.md +0 -20
- package/commands/sf-view.md +0 -25
- package/skills/building-storefronts/SKILL.md +0 -178
- package/skills/building-storefronts/references/frontend-integration.md +0 -229
- package/skills/json-render-remotion/SKILL.md +0 -111
- package/skills/json-render-solid/SKILL.md +0 -204
- package/skills/nextjs-shadcn/SKILL.md +0 -367
- package/skills/nextjs-shadcn/references/architecture.md +0 -499
- package/skills/nextjs-shadcn/references/project-setup.md +0 -162
- package/skills/nextjs-shadcn/references/shadcn-platform.md +0 -261
- package/skills/nextjs-shadcn/references/sidebar.md +0 -274
- package/skills/nextjs-shadcn/references/styling.md +0 -555
- package/skills/sf-view-gen/assets/templates/views/category/index.tsx +0 -86
- package/skills/sf-view-gen/assets/templates/views/category/registry.tsx +0 -48
- package/skills/sf-view-gen/assets/templates/views/checkout/index.tsx +0 -192
- package/skills/sf-view-gen/assets/templates/views/checkout/registry.tsx +0 -45
- package/skills/sf-view-gen/assets/templates/views/product/index.tsx +0 -122
- package/skills/sf-view-gen/assets/templates/views/product/registry.tsx +0 -49
- package/skills/sf-view-gen/assets/templates/views/user-panel/index.tsx +0 -168
- package/skills/sf-view-gen/assets/templates/views/user-panel/registry.tsx +0 -49
- package/skills/shadcn-component-discovery/SKILL.md +0 -273
- package/skills/shadcn-component-discovery/references/registries.md +0 -226
- package/skills/shadcn-theming/SKILL.md +0 -104
- package/skills/storefront-best-practices/SKILL.md +0 -421
- package/skills/storefront-best-practices/reference/components/breadcrumbs.md +0 -123
- package/skills/storefront-best-practices/reference/components/cart-popup.md +0 -189
- package/skills/storefront-best-practices/reference/components/country-selector.md +0 -298
- package/skills/storefront-best-practices/reference/components/footer.md +0 -112
- package/skills/storefront-best-practices/reference/components/hero.md +0 -241
- package/skills/storefront-best-practices/reference/components/megamenu.md +0 -239
- package/skills/storefront-best-practices/reference/components/navbar.md +0 -397
- package/skills/storefront-best-practices/reference/components/popups.md +0 -221
- package/skills/storefront-best-practices/reference/components/product-card.md +0 -125
- package/skills/storefront-best-practices/reference/components/product-reviews.md +0 -217
- package/skills/storefront-best-practices/reference/components/product-slider.md +0 -174
- package/skills/storefront-best-practices/reference/components/search.md +0 -101
- package/skills/storefront-best-practices/reference/connecting-to-backend.md +0 -391
- package/skills/storefront-best-practices/reference/features/promotions.md +0 -307
- package/skills/storefront-best-practices/reference/features/wishlist.md +0 -230
- package/skills/storefront-best-practices/reference/layouts/account.md +0 -380
- package/skills/storefront-best-practices/reference/layouts/cart.md +0 -316
- package/skills/storefront-best-practices/reference/layouts/checkout.md +0 -486
- package/skills/storefront-best-practices/reference/layouts/home-page.md +0 -264
- package/skills/storefront-best-practices/reference/layouts/order-confirmation.md +0 -231
- package/skills/storefront-best-practices/reference/layouts/product-details.md +0 -527
- package/skills/storefront-best-practices/reference/layouts/product-listing.md +0 -520
- package/skills/storefront-best-practices/reference/layouts/static-pages.md +0 -356
- package/skills/storefront-best-practices/reference/medusa.md +0 -307
- /package/skills/{storefront-best-practices/reference → storefront-workflow/references}/mobile-responsiveness.md +0 -0
- /package/skills/{storefront-best-practices/reference → storefront-workflow/references}/seo.md +0 -0
package/README.md
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "@thorprovider/storefront"
|
|
3
|
-
purpose: "Deterministic storefront scaffolder and installable
|
|
4
|
-
lastReviewed: "2026-10-
|
|
3
|
+
purpose: "Deterministic storefront scaffolder and installable skill suite for Thor Commerce"
|
|
4
|
+
lastReviewed: "2026-10-14"
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# @thorprovider/storefront
|
|
8
8
|
|
|
9
|
-
Deterministic storefront scaffolder and installable
|
|
9
|
+
Deterministic storefront scaffolder and installable skill suite for Thor Commerce.
|
|
10
10
|
|
|
11
|
-
`npx @thorprovider/storefront create <dir>` scaffolds a complete, buildable storefront
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
`npx @thorprovider/storefront create <dir>` scaffolds a complete, buildable storefront,
|
|
12
|
+
installs its OpenCode skill suite into `<dir>/.opencode/skills/`, and writes a managed
|
|
13
|
+
root `AGENTS.md` — all from static templates, a recipe registry, and vendored skills,
|
|
14
|
+
with nothing else from the monorepo needed at generation time.
|
|
15
|
+
|
|
16
|
+
A generated storefront ships **no `/sf-*` commands**. Guidance lives in the root
|
|
17
|
+
`AGENTS.md`, which states the authority rules and routes an intent (add a view, change
|
|
18
|
+
the theme, add a feature, connect a backend) to the skill that owns the procedure: open
|
|
19
|
+
the project in OpenCode and ask in natural language. The `storefront` CLI stays the
|
|
20
|
+
structural authority.
|
|
16
21
|
|
|
17
22
|
This README is organised in two parts:
|
|
18
23
|
|
|
@@ -36,43 +41,50 @@ storefront it generates resolves its own dependencies from the registry.
|
|
|
36
41
|
| Package manager | Yarn with the `node-modules` linker (for generated projects) |
|
|
37
42
|
| OpenCode | a version that supports project skills in `.opencode/skills/` |
|
|
38
43
|
| OS | macOS, Linux, Windows (via WSL) |
|
|
39
|
-
| Network | access to the npm registry
|
|
44
|
+
| Network | access to the npm registry — only for the first scaffold per machine (repeat scaffolds can run offline, see [troubleshooting](#flaky-network-etimedout)) |
|
|
40
45
|
|
|
41
46
|
Generated projects use Yarn with the `node-modules` linker, not Plug'n'Play (PnP).
|
|
42
47
|
Yarn 4 defaults to PnP, which Next.js 16's Turbopack cannot resolve from, so the
|
|
43
48
|
scaffold writes a `.yarnrc.yml` setting `nodeLinker: node-modules` before installing
|
|
44
49
|
dependencies. PnP is not used. You do not need to configure this yourself.
|
|
45
50
|
|
|
51
|
+
The same `.yarnrc.yml` hardens installs for unreliable networks: `httpRetry: 10`
|
|
52
|
+
and `networkConcurrency: 8`. The first successful scaffold warms the global Yarn
|
|
53
|
+
cache (`~/.yarn/berry/cache`); repeat scaffolds then download no packages, and
|
|
54
|
+
`YARN_ENABLE_OFFLINE_MODE=1` drops even the metadata requests.
|
|
55
|
+
|
|
46
56
|
### Install
|
|
47
57
|
|
|
48
|
-
Run the scaffolder directly from npm — no global install required
|
|
49
|
-
`yarn dlx
|
|
58
|
+
Run the scaffolder directly from npm — no global install required; the equivalent is
|
|
59
|
+
`yarn dlx @thorprovider/storefront`:
|
|
50
60
|
|
|
51
61
|
```bash
|
|
52
62
|
npx @thorprovider/storefront create my-shop \
|
|
53
63
|
--site-name "My Shop" \
|
|
54
|
-
--backend-url http://localhost:9000 \
|
|
55
64
|
--preset maia
|
|
56
65
|
```
|
|
57
66
|
|
|
58
|
-
```bash
|
|
59
|
-
yarn dlx @thorprovider/storefront create my-shop --preset maia
|
|
60
|
-
```
|
|
61
|
-
|
|
62
67
|
The first positional argument is the target directory: it is created if missing, and
|
|
63
68
|
it is where `.opencode/`, the Next.js app, and `lib/<name>/` live. Run the command
|
|
64
69
|
from the parent directory you want the project created in.
|
|
65
70
|
|
|
66
71
|
In a terminal, `create` runs an interactive wizard for any input you omit (name,
|
|
67
|
-
site name,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
72
|
+
site name, preset — Maia is preselected); an empty prompt accepts the suggested
|
|
73
|
+
default (name from the target basename, site name from its Title Case). Without a
|
|
74
|
+
terminal every required input must be a flag — the command fails fast and writes
|
|
75
|
+
nothing. Brand color is never prompted: pass `--brand`, or change it later in natural
|
|
76
|
+
language (the theme skill runs `storefront theme`).
|
|
77
|
+
|
|
78
|
+
`create` is structural and offline: it scaffolds a storefront that runs in **mock
|
|
79
|
+
mode** with no backend. Connecting a real backend is a separate, re-runnable step —
|
|
80
|
+
run `storefront setup` from inside the project (see [Backend
|
|
81
|
+
configuration](#backend-configuration)).
|
|
71
82
|
|
|
72
83
|
Once the package is installed as a dependency, the shorter binary form is available:
|
|
73
84
|
|
|
74
85
|
```bash
|
|
75
|
-
storefront create my-shop --preset maia # scaffold + install the suite
|
|
86
|
+
storefront create my-shop --preset maia # scaffold + install the suite (mock mode)
|
|
87
|
+
storefront setup --publishable-key <key> --region-id <id> # connect a real backend
|
|
76
88
|
storefront update # refresh an existing install
|
|
77
89
|
storefront doctor # report the recorded version and status
|
|
78
90
|
```
|
|
@@ -87,7 +99,6 @@ project root that will hold `.opencode/`:
|
|
|
87
99
|
storefront create my-shop \
|
|
88
100
|
--name my-shop \
|
|
89
101
|
--site-name "My Shop" \
|
|
90
|
-
--backend-url http://localhost:9000 \
|
|
91
102
|
--preset maia \
|
|
92
103
|
[--brand orange]
|
|
93
104
|
```
|
|
@@ -96,25 +107,24 @@ storefront create my-shop \
|
|
|
96
107
|
| --- | --- | --- |
|
|
97
108
|
| `<dir>` (positional) | target project directory (created if missing) | required |
|
|
98
109
|
| `--name <name>` | kebab-case storefront name = `lib/<name>/` = `__STOREFRONT__` | prompted in the wizard |
|
|
99
|
-
| `--site-name <name>` | human site name (`__SITE_NAME__`) | `<name>` |
|
|
100
|
-
| `--backend-url <url>` | Medusa backend URL | `http://localhost:9000` |
|
|
110
|
+
| `--site-name <name>` | human site name (`__SITE_NAME__`) | Title Case of `<name>` (`my-shop` → `My Shop`) |
|
|
101
111
|
| `--preset <preset>` | visual preset: `maia`, `nova`, `vega`, `luma`, `lyra`, `mira` | prompted in the wizard (Maia preselected) |
|
|
102
|
-
| `--
|
|
112
|
+
| `--starter` | seed the full curated composition — every curated view and feature — instead of the minimal `home`+`cart` baseline | off |
|
|
113
|
+
| `--brand <color>` | named brand color (`orange`, `blue`, …) applied as a chromatic primary family (primary/foreground/ring/nav) over the preset tokens (never prompted) | none |
|
|
103
114
|
| `--force`, `-f` | overwrite files that already exist | off |
|
|
104
115
|
|
|
105
|
-
The pipeline runs: preconditions → Next.js base
|
|
106
|
-
|
|
107
|
-
placeholder substitution →
|
|
108
|
-
install into `.opencode/`.
|
|
116
|
+
The pipeline runs: preconditions → Next.js base (files only) → `.yarnrc.yml`
|
|
117
|
+
(`nodeLinker: node-modules`) → shadcn base with the preset → dependency install →
|
|
118
|
+
template copy + placeholder substitution → mock `.env.local` + theme → suite install.
|
|
109
119
|
|
|
110
|
-
Same inputs produce a byte-identical tree (verified by recursive `diff -r`).
|
|
111
|
-
`create` twice with different inputs is the only reason the output differs.
|
|
120
|
+
Same inputs produce a byte-identical tree (verified by recursive `diff -r`).
|
|
112
121
|
|
|
113
|
-
The suite install copies
|
|
114
|
-
skills
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
122
|
+
The suite install copies the shipped skills (own and vendored, flat) into
|
|
123
|
+
`<dir>/.opencode/skills/`, verifying generated copies against their source first. It
|
|
124
|
+
records the installed version in `.opencode/storefront.json` and is idempotent
|
|
125
|
+
(`--force` overwrites). `create` also writes the managed root `AGENTS.md` from the
|
|
126
|
+
derived template path, so `storefront sync` refreshes it and `storefront doctor`
|
|
127
|
+
reports drift.
|
|
118
128
|
|
|
119
129
|
### Run the storefront
|
|
120
130
|
|
|
@@ -125,31 +135,70 @@ cd my-shop
|
|
|
125
135
|
yarn dev
|
|
126
136
|
```
|
|
127
137
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
138
|
+
A fresh `create` seeds `home` + `cart` (home hero/benefits/category sections, a
|
|
139
|
+
navbar-owned cart sheet, a standalone `/cart` review route). Pass `--starter` to
|
|
140
|
+
seed the full curated composition instead — every curated view and feature, read
|
|
141
|
+
from the registry, held units excluded. Everything renders with only
|
|
142
|
+
`@thorprovider/blocks` (UI) and `@thorprovider/adapters` (data). Then grow it by asking
|
|
143
|
+
in natural language — the `AGENTS.md` router maps each intent to a skill that delegates
|
|
144
|
+
the structural write to the CLI:
|
|
131
145
|
|
|
132
146
|
```
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
147
|
+
add a product view # the view skill runs `storefront view add product`
|
|
148
|
+
add a testimonials section to home # the view skill composes the section, then syncs
|
|
149
|
+
change the theme to dark with a teal brand # the theme skill runs `storefront theme`
|
|
150
|
+
connect the backend to my Medusa store # the setup skill runs `storefront setup`
|
|
136
151
|
```
|
|
137
152
|
|
|
138
153
|
### Backend configuration
|
|
139
154
|
|
|
140
|
-
|
|
141
|
-
backend
|
|
155
|
+
`create` writes a git-ignored `.env.local` that starts the store in **mock mode** —
|
|
156
|
+
no backend, a generated cookie secret, the store identity — plus a committed
|
|
157
|
+
`.env.example` documenting every variable:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
# .env.local (git-ignored) — written by create
|
|
161
|
+
THOR_DATA_SOURCE=mock
|
|
162
|
+
THOR_STORE_ID=my-shop
|
|
163
|
+
THOR_SESSION_SECRET=<generated per install>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Mock mode is complete: views and the cart render from labelled demo data with no
|
|
167
|
+
configuration. To connect a real backend, run `storefront setup` from inside the project:
|
|
142
168
|
|
|
143
169
|
```bash
|
|
144
|
-
|
|
145
|
-
MEDUSA_REGION_ID=
|
|
146
|
-
MEDUSA_PUBLISHABLE_KEY=
|
|
170
|
+
storefront setup --publishable-key <key> --sales-channel-id <id> --region-id <region-id> [--backend-url <url>]
|
|
147
171
|
```
|
|
148
172
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
173
|
+
In a terminal, `setup` prompts for the backend URL, publishable key, sales channel id,
|
|
174
|
+
and region. It takes the sales channel id as an explicit input (Medusa admin → Settings
|
|
175
|
+
→ Sales Channels), derives the region's currency (`GET /store/regions`), then **upserts**
|
|
176
|
+
only the keys it manages — `THOR_STORE_ID`, `THOR_SESSION_SECRET`, and user additions are
|
|
177
|
+
untouched. It is re-runnable (rotate the key, change the channel or region, re-point the
|
|
178
|
+
backend). Without a publishable key it stays in mock mode; without a channel id it reports the missing input and stays in mock mode.
|
|
179
|
+
|
|
180
|
+
The store context is **explicit and validated** in real mode: `MEDUSA_SALES_CHANNEL_ID`
|
|
181
|
+
scopes the store, `MEDUSA_REGION_ID` supplies currency, countries, and shipping, and
|
|
182
|
+
`MEDUSA_CURRENCY_CODE` (ISO 4217) comes from the environment and is **never hardcoded**.
|
|
183
|
+
`THOR_SESSION_SECRET` is the host-only cookie signing secret (≥16 chars, 32 recommended).
|
|
184
|
+
|
|
185
|
+
> **Renamed:** the currency variable was `THOR_CURRENCY_CODE` and is now
|
|
186
|
+
> `MEDUSA_CURRENCY_CODE`. An existing store renames it in `.env.local`, or re-runs
|
|
187
|
+
> `storefront setup`, which writes it.
|
|
188
|
+
|
|
189
|
+
`THOR_DATA_SOURCE` selects the mode **explicitly** and is never inferred from a failure.
|
|
190
|
+
`mock` renders labelled demo data and resolves a fixture store context in code (channel
|
|
191
|
+
`mock-sales-channel`, region `mock-region-us`, currency `USD`), so no context variables
|
|
192
|
+
are required. `medusa` uses the real backend: a missing provider or invalid config is a
|
|
193
|
+
**visible error** — never a silent fallback to demo data. Run `storefront setup`, then
|
|
194
|
+
restart `yarn dev`.
|
|
195
|
+
|
|
196
|
+
The guest flow is backend-authoritative end to end: the signed host cookie binds a
|
|
197
|
+
persistent backend cart; the PDP adds the explicitly selected variant; the cart and
|
|
198
|
+
checkout render the backend's totals; checkout applies the one approved manual TEST
|
|
199
|
+
provider and completes the cart with its native discriminator. An ambiguous completion
|
|
200
|
+
is never retried — the shopper reconciles the committed cart, and the active cart is
|
|
201
|
+
cleared only after a confirmed order. No card data is collected at launch.
|
|
153
202
|
|
|
154
203
|
### Troubleshooting / Known Issues
|
|
155
204
|
|
|
@@ -157,90 +206,201 @@ Each entry below follows the same shape: **cause** → **symptom** → **remedy*
|
|
|
157
206
|
|
|
158
207
|
#### Ancestor `.opencode/`
|
|
159
208
|
|
|
160
|
-
- **Cause**:
|
|
161
|
-
|
|
162
|
-
- **
|
|
163
|
-
and `/sf-theme` run commands from the ancestor's `.opencode/` instead of the ones
|
|
164
|
-
this package installed, so you see stale or unrelated behaviour.
|
|
165
|
-
- **Remedy**: scaffold in a clean root with no ancestor `.opencode/` (for example a
|
|
166
|
-
fresh directory outside the monorepo), or remove/relocate the ancestor `.opencode/`
|
|
167
|
-
before opening the project.
|
|
209
|
+
- **Cause**: an ancestor directory already contains a `.opencode/`.
|
|
210
|
+
- **Symptom**: OpenCode resolves the wrong skill set or an ancestor's `AGENTS.md`, so you see stale or unrelated behaviour.
|
|
211
|
+
- **Remedy**: scaffold in a clean root with no ancestor `.opencode/`, or remove/relocate the ancestor `.opencode/` before opening the project.
|
|
168
212
|
|
|
169
213
|
#### Non-empty target directory
|
|
170
214
|
|
|
171
|
-
- **Cause**: `
|
|
172
|
-
|
|
173
|
-
- **
|
|
174
|
-
`lib/<name>/ already exists — refusing to overwrite`.
|
|
175
|
-
- **Remedy**: choose a different `--name`, or remove the existing `lib/<name>/`
|
|
176
|
-
directory. To extend an existing storefront instead of creating one, use
|
|
177
|
-
`/sf-add-view`. `create-next-app` also refuses to initialize a directory that is
|
|
178
|
-
already a Next.js project unless it can reuse it; run `create` in an empty directory.
|
|
215
|
+
- **Cause**: `lib/<name>/` already exists, or the root is already a Next.js project.
|
|
216
|
+
- **Symptom**: the run stops at the preconditions stage with `lib/<name>/ already exists — refusing to overwrite`.
|
|
217
|
+
- **Remedy**: choose a different `--name`, or remove the existing `lib/<name>/`. To extend an existing storefront, run the `storefront` CLI from inside it (or ask in natural language — see `AGENTS.md`); run `create` in an empty directory.
|
|
179
218
|
|
|
180
219
|
#### Missing or invalid `--preset`
|
|
181
220
|
|
|
182
|
-
- **Cause**: `--preset` was omitted (
|
|
183
|
-
|
|
184
|
-
- **
|
|
185
|
-
`--preset must be one of: maia, nova, vega, luma, lyra, mira`.
|
|
186
|
-
- **Remedy**: pass `--preset` with one of `maia`, `nova`, `vega`, `luma`, `lyra`,
|
|
187
|
-
`mira`. In a terminal the wizard preselects `maia`, so the preset is only required
|
|
188
|
-
on the command line for non-interactive runs.
|
|
221
|
+
- **Cause**: `--preset` was omitted (non-interactive) or is not a recognized preset.
|
|
222
|
+
- **Symptom**: the run stops at input validation with `--preset must be one of: maia, nova, vega, luma, lyra, mira`.
|
|
223
|
+
- **Remedy**: pass one of those presets. In a terminal the wizard preselects `maia`, so the flag is only required for non-interactive runs.
|
|
189
224
|
|
|
190
225
|
#### Yarn Plug'n'Play / linker
|
|
191
226
|
|
|
192
227
|
- **Cause**: a generated project installed with Yarn's default PnP linker.
|
|
193
|
-
- **Symptom**: Next.js 16's Turbopack fails to resolve the Next.js package
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
228
|
+
- **Symptom**: Next.js 16's Turbopack fails to resolve Next.js (`Could not find the Next.js package`).
|
|
229
|
+
- **Remedy**: `create` writes `.yarnrc.yml` with `nodeLinker: node-modules` before installing; if you set the project up manually, add that file and re-run `yarn install`. Do not use PnP.
|
|
230
|
+
|
|
231
|
+
#### Flaky network (ETIMEDOUT)
|
|
232
|
+
|
|
233
|
+
- **Cause**: an unstable connection stalls registry requests mid-transfer (`RequestError … AggregateError [ETIMEDOUT]`).
|
|
234
|
+
- **Remedy**: the scaffolded `.yarnrc.yml` sets `httpRetry: 10` and `networkConcurrency: 8`; that run warms the global cache (`~/.yarn/berry/cache`), which later scaffolds reuse. For offline re-scaffolding run `YARN_ENABLE_OFFLINE_MODE=1 storefront create <dir> …`; the first scaffold per machine still needs one successful online run.
|
|
199
235
|
|
|
200
236
|
#### Backend not configured
|
|
201
237
|
|
|
202
|
-
- **Cause**: `
|
|
203
|
-
|
|
204
|
-
- **
|
|
205
|
-
- **Remedy**: fill in the variables in [Backend configuration](#backend-configuration)
|
|
206
|
-
and restart `yarn dev`, or use the `mock` adapter data source during local
|
|
207
|
-
development.
|
|
238
|
+
- **Cause**: `THOR_DATA_SOURCE=medusa` but `MEDUSA_BASE_URL`/`MEDUSA_PUBLISHABLE_KEY` are missing, empty, or unreachable (for example, a store still in mock mode).
|
|
239
|
+
- **Symptom**: the storefront renders a visible error notice (`role="alert"`) instead of backend data; real-mode failures never degrade to a fake catalog.
|
|
240
|
+
- **Remedy**: run `storefront setup` from inside the project to write the real-mode variables (see [Backend configuration](#backend-configuration)), then restart `yarn dev`. To stay offline, leave `THOR_DATA_SOURCE=mock`.
|
|
208
241
|
|
|
209
242
|
---
|
|
210
243
|
|
|
211
244
|
## What gets installed
|
|
212
245
|
|
|
213
246
|
```
|
|
214
|
-
<project
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
shadcn-theming/
|
|
225
|
-
shadcn-component-discovery/
|
|
226
|
-
storefront-best-practices/
|
|
227
|
-
building-storefronts/
|
|
247
|
+
<project>/
|
|
248
|
+
AGENTS.md # managed (derived): RULE 0 rules + intent router
|
|
249
|
+
.opencode/
|
|
250
|
+
storefront.json # install record: version, pinning, owned asset BOM
|
|
251
|
+
skills/
|
|
252
|
+
storefront-workflow/ # four phases, CLI/manifest authority; references/ = design/mobile/SEO
|
|
253
|
+
sf-view-gen/ # view generation; references/ = json-render + views.md
|
|
254
|
+
sf-feature/ # feature bundles (feature-owned views + mounts)
|
|
255
|
+
sf-theme-gen/ # theme; references/ = theming guidance
|
|
256
|
+
sf-setup/ # connect/re-point a Medusa backend via `storefront setup`
|
|
228
257
|
```
|
|
229
258
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
259
|
+
The suite ships exactly these five operative skills plus their scoped references;
|
|
260
|
+
the retained generic knowledge is loaded explicitly by the owning skill and is not
|
|
261
|
+
separately installable. The workflow model is
|
|
262
|
+
[`skills/storefront-workflow/SKILL.md`](./skills/storefront-workflow/SKILL.md).
|
|
263
|
+
`create`/`update` also provision the pinned `@fission-ai/openspec` suite as a co-tenant (`openspec init --tools opencode` writes `openspec/`, the `openspec-*` skills, and the `opsx-*` commands, then the CLI seeds a storefront-aware `openspec/config.yaml`); the suite never installs over or removes those co-tenanted files. A non-trivial customization runs through the spec-driven loop surfaced as explore → plan → build → verify → inform, while a deterministic CLI change recorded in `storefront.config.json` needs no change and completion waits on `storefront sync` + `storefront doctor`.
|
|
233
264
|
|
|
234
|
-
##
|
|
265
|
+
## Workflow: bootstrap → compose → customize → verify
|
|
235
266
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
267
|
+
The project manifest (`storefront.config.json`) is the structural source of truth;
|
|
268
|
+
the `storefront` CLI is the only writer of derived files; the developer/agent edits
|
|
269
|
+
only user-owned files and the bounded external surface. Read the installed
|
|
270
|
+
`storefront-workflow` skill for the full model. Every command runs from inside the
|
|
271
|
+
project (no directory argument).
|
|
272
|
+
|
|
273
|
+
| Phase | Authority | Command |
|
|
274
|
+
| --- | --- | --- |
|
|
275
|
+
| **Bootstrap** | CLI only | `npx @thorprovider/storefront create <dir> --name <name> --preset <preset>` |
|
|
276
|
+
| **Compose** | CLI only (agents choose, CLI writes) | `view add\|remove`, `feature add\|remove`, `section add\|remove\|move`, `theme` |
|
|
277
|
+
| **Customize** | developer/agent | edit user-owned `content.ts`/`props.ts`/`data.ts`/`schema.ts`/`mappers.ts` |
|
|
278
|
+
| **Verify** | CLI + build | `npx @thorprovider/storefront sync`, `doctor`, `yarn build` |
|
|
279
|
+
|
|
280
|
+
**Ownership classes.** *Derived* files (`views/index.ts`, `views/registry.ts`,
|
|
281
|
+
`index.tsx`, `registry.tsx`, `catalog.ts`, `runtime.ts`, `spec/index.ts`,
|
|
282
|
+
`state.ts`, `handlers.ts`, `features/*/adapter.ts`) are CLI-regenerated and never
|
|
283
|
+
hand-edited. *User-owned* files (content, props, data, local schema, mappers)
|
|
284
|
+
survive `sync`. *External* files (`app/layout.tsx`, `app/globals.css`) expose a
|
|
285
|
+
**bounded** surface — theme tokens/foreground pairs, fonts, global identity — that
|
|
286
|
+
must preserve the CLI-managed wiring (stylesheet import, provider/mode markers,
|
|
287
|
+
block token contract); the CLI appends it idempotently.
|
|
288
|
+
|
|
289
|
+
**Curated composition.** The CLI registry (`storefront views --json`,
|
|
290
|
+
`features --json`, `recipes --json`) is the authority for what is installable.
|
|
291
|
+
Curated MVP views: `home`, `product`, `cart`, `category`, `checkout`, `account`,
|
|
292
|
+
`static`. Curated MVP features: `checkout` (owns the `checkout` view —
|
|
293
|
+
`view add checkout` is rejected, use `feature add checkout`), `customer-auth`
|
|
294
|
+
(owns `account`), `static-pages` (owns `static`), and the mount-only `benefits`,
|
|
295
|
+
`catalog-search`, `category-filters`, and `navigability`. `recommendations` is
|
|
296
|
+
held post-MVP and rejected before any write. `create` seeds `home` and `cart`;
|
|
297
|
+
`--starter` seeds every curated view and feature. Home editorial (`faq`, `cta`) and
|
|
298
|
+
data-bound `products` sections: see [Product sections](docs/product-sections.md).
|
|
299
|
+
|
|
300
|
+
**Cart lifecycle and checkout independence.** The cart review is a normal view,
|
|
301
|
+
not a shell element: `view add cart` installs it, `view remove cart` drops it
|
|
302
|
+
(which also hides the lateral's "View cart" action and leaves `/cart` unrouted;
|
|
303
|
+
the navbar icon still opens the compact lateral). `sync` never silently adds it
|
|
304
|
+
to an existing store — upgrading is an explicit `view add cart`. Cart and
|
|
305
|
+
checkout are independent: review works without checkout (its action is hidden),
|
|
306
|
+
and adding `checkout` never adds the cart view. The CLI derives
|
|
307
|
+
`cartViewAvailable`/`checkoutAvailable` from the installed views/features into
|
|
308
|
+
`composition.ts`, which the generated wiring reads.
|
|
309
|
+
|
|
310
|
+
**Prerequisites and pinning.** Node `>=20.9.0`, `node-modules` linker, and the
|
|
311
|
+
canonical Yarn pinned in the generated `package.json` as
|
|
312
|
+
`"packageManager": "yarn@4.5.1"` (Corepack resolves it; the scaffold runs every
|
|
313
|
+
install under that version). The generated project pins an exact compatible
|
|
314
|
+
candidate toolset and installs the `@thorprovider/*` packages from the npm
|
|
315
|
+
registry by default. `--packs <dir>` is an optional path for local candidates
|
|
316
|
+
(published or unpublished): it maps each dependency to a packed tarball via Yarn `resolutions`
|
|
317
|
+
(no workspace import), and the CLI verifies every tarball before writing
|
|
318
|
+
anything — the manifest `name`/`version` must match the expected toolset, and the
|
|
319
|
+
packed `@thorprovider/runtime` must export every value the generated host
|
|
320
|
+
`commerce.ts` imports; a stale or mismatched pack is refused with a named code
|
|
321
|
+
before any file is written. No package-manager safety policy is suppressed: the generated
|
|
322
|
+
`.yarnrc.yml` sets no `npmPreapprovedPackages`, and the pinned versions are
|
|
323
|
+
intended to be published past Yarn's age gate. `doctor` reports the observed linker/Node/Yarn
|
|
324
|
+
against the declared ranges and exits non-zero on a mismatch instead of a false
|
|
325
|
+
clean stamp.
|
|
326
|
+
|
|
327
|
+
### Configurable composition (E1)
|
|
328
|
+
|
|
329
|
+
The composition engine is a truthful semantic catalog plus a structured
|
|
330
|
+
`inspect → plan → apply` boundary. Run these inside the project:
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
storefront inspect --json
|
|
334
|
+
storefront plan --operations operations.json [--out plan.json] [--json]
|
|
335
|
+
storefront apply --plan plan.json [--json]
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
- **`inspect --json`** is read-only and deterministic: `formatVersion: 1` /
|
|
339
|
+
`catalogVersion: 1`, every slot/variant with `executable` or `held` + a reason, and a
|
|
340
|
+
capability catalog with `verified`/`unverified` state. A held or unverified unit is
|
|
341
|
+
never reported as available.
|
|
342
|
+
- **`plan`** validates the whole intended state and emits before/after fingerprints and
|
|
343
|
+
the exact derived-file diff. Without `--out` it writes nothing and prints the plan to
|
|
344
|
+
stdout; with `--out` it writes only a safe plan artifact. `--out` is refused
|
|
345
|
+
(`E_OUT_PATH_REFUSED`) for owned, derived, secret-bearing, or installation paths and
|
|
346
|
+
for the operation input.
|
|
347
|
+
- **`apply`** is the only composition mutation. It applies from the recorded
|
|
348
|
+
before-state, reports a **verified no-op** at the recorded after-state, and rejects
|
|
349
|
+
anything else as stale (`E_STALE_PLAN`); concurrent runs are serialized by a local
|
|
350
|
+
lock. A handled write failure rolls back every touched file. An abrupt process kill is
|
|
351
|
+
**not** crash-atomic — the manifest is written last and `sync` detects and repairs the
|
|
352
|
+
divergence.
|
|
353
|
+
|
|
354
|
+
The operations envelope is `{ "formatVersion": 1, "operations": [ ... ] }` (missing or
|
|
355
|
+
different `formatVersion` → `E_INVALID_ENVELOPE`). Operation names are
|
|
356
|
+
`view.add|remove|configure` and `feature.add|remove|configure`; `view add|remove` and
|
|
357
|
+
`feature add|remove` remain thin aliases over the same engine, and so do
|
|
358
|
+
`section add|remove|move`/`sections [<view>]` over `section.add|remove|reorder` (see [Product sections](docs/product-sections.md)).
|
|
359
|
+
|
|
360
|
+
**What E1 proves.** The only executable non-default swap is `home.benefits`
|
|
361
|
+
`Feature1 → Feature2` (identical `BenefitsItem[]` data shape, no bridge); every bridged
|
|
362
|
+
variant is `held`. A feature may declare a `config` and `mounts`; the bounded `benefits`
|
|
363
|
+
feature mounts at `home.benefits` with `{ "presentation": "compact" | "detailed" }`,
|
|
364
|
+
claims the slot exactly once, and preserves user-owned content on removal. Checkout
|
|
365
|
+
keeps its existing feature-owned route only — unproven checkout variants or route
|
|
366
|
+
options are rejected (`E_UNPROVEN_CHECKOUT_OPTION`). The category filter's action is
|
|
367
|
+
**held** (`unverified`, owner E2) and its control is omitted rather than exposed as a
|
|
368
|
+
no-op; catalog search/filters (E2) and composable sections (E3) are **not** implemented
|
|
369
|
+
here.
|
|
370
|
+
|
|
371
|
+
## Agent guidance and skills
|
|
372
|
+
|
|
373
|
+
A generated storefront ships a managed root `AGENTS.md` and no `/sf-*` commands.
|
|
374
|
+
`AGENTS.md` states the non-negotiable authority rules (RULE 0) and routes each intent
|
|
375
|
+
to the skill that owns the procedure; the skill then delegates the structural write to
|
|
376
|
+
the `storefront` CLI.
|
|
377
|
+
|
|
378
|
+
| Intent | Skill | CLI it drives |
|
|
379
|
+
| --- | --- | --- |
|
|
380
|
+
| Add or remove a view; compose a view's sections; edit user-owned content/props/data | `sf-view-gen` | `storefront view add\|remove`, `section …`, `views --json` |
|
|
381
|
+
| Change colors, theme mode, typography, radius, presets, or identity/SEO metadata | `sf-theme-gen` | `storefront theme` (`--brand`, `--primary`, `--accent`, `--radius`, `--font-sans`, `--set`, `--preset`, `--import`, `--export`, `--mode`) |
|
|
382
|
+
| Add or remove a feature bundle (e.g. `checkout`) | `sf-feature` | `storefront feature add\|remove` |
|
|
383
|
+
| Connect or re-point a Medusa backend | `sf-setup` | `storefront setup` |
|
|
384
|
+
| Edit a view's content/props/data, map props or data, reshape composition, or any change whose acceptance is not a CLI assertion (non-trivial) | `storefront-workflow` | the spec-driven loop, surfaced as **explore → plan → build → verify → inform** |
|
|
385
|
+
|
|
386
|
+
Scaffolding is `npx @thorprovider/storefront create <dir>`; connecting a real backend is
|
|
387
|
+
`storefront setup` from inside the project (see [Backend configuration](#backend-configuration)).
|
|
241
388
|
|
|
242
|
-
|
|
243
|
-
|
|
389
|
+
## Philosophy: determinism first; the LLM only where there is judgment
|
|
390
|
+
|
|
391
|
+
A storefront task is one of three kinds, and the tool follows the kind:
|
|
392
|
+
|
|
393
|
+
- **Deterministic** (output is a pure function of input) → a CLI subcommand with flags
|
|
394
|
+
(e.g. `storefront theme --brand orange --radius 0.75rem`). Repeatable, reviewable, no model in the loop.
|
|
395
|
+
- **Mixed** → split it: the script applies the deterministic part, the LLM decides the
|
|
396
|
+
judgment part (the `sf-theme-gen` skill chooses the brand color; `storefront theme --brand` applies it).
|
|
397
|
+
- **Pure judgment** → an `sf-*` skill (e.g. `sf-view-gen`): variant selection, copy, and
|
|
398
|
+
layout choices live here.
|
|
399
|
+
|
|
400
|
+
**Test criterion**: if you can assert the result without a model ("after this, the file
|
|
401
|
+
contains X"), it must be a script; if you cannot, it is skill work. LLM edits are
|
|
402
|
+
non-deterministic and expensive to review, so keep the judgment-heavy work where a
|
|
403
|
+
model adds value and make the rest a reliable script.
|
|
244
404
|
|
|
245
405
|
## Design guarantees
|
|
246
406
|
|
|
@@ -252,40 +412,60 @@ Scaffolding itself is `npx @thorprovider/storefront create <dir>` — there is n
|
|
|
252
412
|
installer copies anything.
|
|
253
413
|
- **Stack-constrained**: UI comes only from `@thorprovider/blocks`; data only from
|
|
254
414
|
`@thorprovider/adapters` and `@thorprovider/runtime/commerce`.
|
|
255
|
-
- **
|
|
256
|
-
|
|
257
|
-
so
|
|
415
|
+
- **Truthful output**: a versioned recipe/catalog exposes every slot and variant with an
|
|
416
|
+
`executable` or `held` status and a reason. Only executable variants render; a held
|
|
417
|
+
variant is rejected before any write, so a site is never presented with a choice that
|
|
418
|
+
silently does nothing. E1 ships a small proven set (`home.benefits` `Feature1`↔
|
|
419
|
+
`Feature2`); bridged variants and optional sections stay held.
|
|
258
420
|
|
|
259
421
|
## Generated project structure
|
|
260
422
|
|
|
261
|
-
Each view is a self-contained module.
|
|
262
|
-
|
|
423
|
+
Each view is a self-contained module. The CLI generates the barrels and wiring
|
|
424
|
+
from the manifest; shared files compose whatever the barrel exports and never name
|
|
425
|
+
a view. Only user-owned files (marked `*`) are hand-edited.
|
|
263
426
|
|
|
264
427
|
```
|
|
265
428
|
lib/<name>/
|
|
266
|
-
catalog.ts, registry.tsx, runtime.ts, handlers.ts, state.ts # shell; compose the barrel
|
|
267
|
-
mappers.ts
|
|
268
|
-
|
|
429
|
+
catalog.ts, registry.tsx, runtime.ts, handlers.ts, state.ts # derived shell; compose the barrel
|
|
430
|
+
mappers.ts * # user-owned: CardProduct -> block props
|
|
431
|
+
shell/
|
|
432
|
+
content.ts * # user-owned: wordmark accents + navbar note + footer newsletter
|
|
433
|
+
props.ts * # user-owned: shell element props
|
|
434
|
+
spec/index.ts, spec/page-root.ts, spec/types.ts # derived routes built from the barrel
|
|
269
435
|
views/
|
|
270
|
-
index.ts # barrel: export const views: ViewModule[] = [home, ...]
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
home/
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
436
|
+
index.ts # derived server barrel: export const views: ViewModule[] = [home, ...]
|
|
437
|
+
registry.ts # derived client barrel: viewRegistries (no server loaders)
|
|
438
|
+
types.ts # ViewModule / ViewRegistry contract
|
|
439
|
+
home/
|
|
440
|
+
index.tsx # derived thin wrapper (imports ./content, ./props, ./data)
|
|
441
|
+
registry.tsx # derived 'use client' component mappings
|
|
442
|
+
content.ts * # user-owned copy/data
|
|
443
|
+
props.ts * # user-owned element props
|
|
444
|
+
data.ts * # user-owned loader shaping
|
|
445
|
+
schema.ts * # user-owned local Zod mirror
|
|
446
|
+
product/ category/ checkout/ # same shape
|
|
278
447
|
```
|
|
279
448
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
and
|
|
283
|
-
|
|
449
|
+
The shell's derived `spec/index.ts` imports the user-owned `shell/props.ts`
|
|
450
|
+
(`...navbarShellProps`, `...footerShellProps`) and composes it **last**, so the brand
|
|
451
|
+
surface and footer copy are configured without touching derived files. Configure
|
|
452
|
+
`shell/content.ts` (`logoAccent`/`brandAccent`, `navbarClassName`/`footerClassName`,
|
|
453
|
+
`brandDescription`, `linkColumns`); defaults are neutral and link only to served routes
|
|
454
|
+
(`/`, `/categories`, `/checkout`), never dead `#` links. `newsletter` defaults to `false`
|
|
455
|
+
and the scaffold ships no submit callback, so a content-only newsletter renders no form
|
|
456
|
+
— never a fake form. `shippingNote` defaults to `''`: while it is empty (and there are
|
|
457
|
+
no utility links) the navbar renders no utility strip, so no placeholder note is shown.
|
|
458
|
+
|
|
459
|
+
A view's derived `index.tsx` imports the user-owned `content.ts`/`props.ts`/
|
|
460
|
+
`data.ts`, so a customization changes real spec/loader output and survives `sync`.
|
|
461
|
+
`npx @thorprovider/storefront view add <view>` chooses the intent/variant from the
|
|
462
|
+
registry, writes the derived files, and regenerates the barrels; the agent edits
|
|
463
|
+
only the user-owned files. The scaffold templates live at the package root:
|
|
284
464
|
|
|
285
465
|
```
|
|
286
|
-
assets/templates/app/ # shell (
|
|
287
|
-
assets/templates/views/home/ # home (
|
|
288
|
-
|
|
466
|
+
assets/templates/app/ # shell (generated by `create`)
|
|
467
|
+
assets/templates/views/home/ # home (generated by `create`)
|
|
468
|
+
assets/templates/views/<view>/ # the other views (generated by `storefront view add`)
|
|
289
469
|
```
|
|
290
470
|
|
|
291
471
|
## Development
|
|
@@ -301,24 +481,13 @@ node bin/storefront.js doctor <tmp> # report the recorded version
|
|
|
301
481
|
node scripts/render-templates.mjs <tmp> my-shop # re-render templates only (no install; fast loop)
|
|
302
482
|
```
|
|
303
483
|
|
|
304
|
-
`scripts/render-templates.mjs` is the same renderer
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
`
|
|
308
|
-
|
|
309
|
-
`
|
|
310
|
-
the
|
|
311
|
-
meaningful at these moments:
|
|
312
|
-
|
|
313
|
-
| Moment | Action |
|
|
314
|
-
| --- | --- |
|
|
315
|
-
| Day-to-day development | Nothing — use the local binary above |
|
|
316
|
-
| Changing `files`, moving assets, adding or removing directories | `npm pack --dry-run` to list what would ship |
|
|
317
|
-
| CI, before publishing | `yarn check-assets` + `npm pack --dry-run` as a guard |
|
|
318
|
-
| Publishing a release | `npm pack` / `yarn npm publish` — the real tarball |
|
|
319
|
-
|
|
320
|
-
A generated `.tgz` goes stale the moment the tree changes, so it is not a
|
|
321
|
-
development tool. Keep it out of the working tree.
|
|
484
|
+
`scripts/render-templates.mjs` is the same renderer `create` uses for stage 6; it copies
|
|
485
|
+
templates and substitutes placeholders without touching dependencies, so iterating on a
|
|
486
|
+
template costs seconds and no network. `npm pack` verifies a different thing — **what
|
|
487
|
+
actually ships to npm** (the `files` list, and that every asset travels inside its
|
|
488
|
+
skill). Use it only when `files`/assets change or before a release (`npm pack --dry-run`
|
|
489
|
+
to list; `yarn check-assets` + `npm pack --dry-run` in CI). A generated `.tgz` goes
|
|
490
|
+
stale the moment the tree changes, so keep it out of the working tree.
|
|
322
491
|
|
|
323
492
|
## Maintenance
|
|
324
493
|
|