Skip to main content
Run the CLI with npx @shoppexio/cli, or install it globally. Authenticate with a Shoppex API key that has the required theme scopes.
settings update validates the complete settings object. Fetch the current settings first, make a focused change, and keep a local copy for review. The CLI can also install the Shoppex MCP server configuration for supported AI clients. Run shoppex --help for the exact commands available in your installed version.

Editing an Easy theme locally

An Easy theme is a ThemeDocument, not a source tree. It is not one opaque file either. theme pull splits it into files you can read, diff, and commit. theme push puts them back.
A checkout looks like this:
Every authored file carries a $schema pointer into .shoppex/schema/. VS Code and other JSON language servers use it to complete block types, element types, and settings. They also flag mistakes while you type. The schemas come from the server on each pull, so they always match the contract that validates your push. pull requires an empty target directory. Add --force to overwrite matching files in a non-empty one. Pages deleted upstream are removed locally, and each removal is printed. theme.json carries a pageOrder array. Page aliases resolve in document page order, so the checkout records that order instead of inferring it from the directory listing. Reorder the entries to reorder the document. A page file you add locally is appended, and the push output names it. Move it inside pageOrder to place it somewhere else. pull also writes AGENTS.md, so Cursor, Claude Code, and Codex pick up the rules that the files do not show: which block types exist, that .shoppex/ is off limits, and that a push conflict means pull-and-reapply. A pull refreshes the guide it wrote itself. It never touches an AGENTS.md you wrote: it says so and leaves your file alone. push sends the reassembled document with the revision recorded at pull time. If the Builder saved the theme in the meantime, the push is rejected with a conflict. It does not overwrite that edit. Pull again, reconcile, then push. The server is the only validator. A contract violation comes back as a validation error that names the offending path, so the CLI keeps no second copy of the schema. push writes the draft. publish promotes exactly the revision the checkout recorded, so a Builder save in between is a conflict instead of a silent promotion of an edit this checkout never saw.

Which themes can be pushed

pull works for every Easy theme. push only works for themes whose document is authoritative, which means themes imported from a ThemeDocument. A theme installed from the catalog renders its document from Builder settings. The server refuses direct document writes to it, and pull says so up front. That checkout is still useful for reading, diffing, and backing up. Edit it in the Builder instead.

Live preview while you edit

theme dev watches the checkout and saves the draft after each change. It also serves a local page that frames the hosted preview and reloads it on every save.
It prints a http://localhost:<port> address. Open that address, not the preview URL, because the local page performs the reload. The preview renders on the server from the draft, so you see the real renderer, not a local approximation. A save that would store an identical document is skipped, so no-op writes do not spend a revision. If the Builder saves the same theme while dev runs, the next save conflicts and dev stops. Pull again, reconcile, then restart. A file that is not valid JSON yet is normal mid-edit. It is reported once and does not stop the loop. The API key needs themes.read for pull. It needs themes.write for push, dev, and publish. A key bound to one theme can use the equivalent theme-specific scopes.

Advanced storefronts

Advanced storefronts use merchant-owned Vite and React source instead of a ThemeDocument. The CLI can pull the current source, push a complete local project, build it, and publish the completed artifact.
pull requires an empty target directory. Add --force when you intentionally want to overwrite matching files in a non-empty directory. After a pull, the CLI writes .shoppex/storefront.json:
The revision is the optimistic concurrency guard. The files map records the last successful pull or push. On every pull, the CLI writes the full remote manifest and removes locally tracked files that were deleted remotely. It prints each removed path. --force still controls overwriting in a non-empty directory. pull downloads the whole revision as a single archive instead of one request per file, so even large storefronts arrive in one round trip. Every extracted file is verified against the manifest checksum, and the pull stops without writing anything if the archive and the manifest disagree. push sends every tracked file plus new local files. New files under node_modules, dist, .git, .turbo, or .shoppex, and .DS_Store files are ignored. A shoppex.theme.json at the storefront root is ignored too. The name is reserved for theme package metadata, and the API rejects it as source. Already tracked files are never ignored, so a tracked file such as dist/vendor.js remains part of the source tree.

Exporting a storefront package

export downloads the complete source tree of the current revision as a single ZIP. It adds one shoppex.theme.json package manifest, holding the theme metadata and the storefront’s content. It is the portable form of a storefront: back it up, hand it to another developer, or import it into another shop.
Inside a pulled storefront directory the theme id comes from .shoppex/storefront.json, so --theme is optional there. Without --out the CLI writes the server-provided filename into the current directory. It refuses to overwrite an existing file unless you pass --force, and never writes through a symlink. export needs only read access, so themes.read or the theme-specific read scope is enough. Pushes above about 10 MB automatically upload the complete source tree as a single ZIP archive. This transport switch requires no flags and keeps the same revision, deletion-confirmation, and build behavior as smaller pushes. If a tracked file is missing locally, push prints the complete deletion list and stops. Re-run with --delete to confirm those deletions. The full snapshot then removes them remotely. If the remote revision changed, pull the latest source before you push again. Pulling also applies remote deletions locally. By default, push starts a build and prints its job ID. Use --no-build to upload source without starting one. deploy checks the current revision’s build every five seconds, waits up to 600 seconds, and publishes only after the build reaches COMPLETED. Override the wait with --timeout <seconds>. Immediately before publishing, deploy re-checks that the server is still on the revision recorded in .shoppex/storefront.json. If another developer pushed, deploy stops and asks you to pull or push first. When a build fails, deploy and status print the failure kind and the reason recorded by the build. They also print what to do about it, plus the tail of the build log. The most common failure is lockfile_out_of_sync: your package.json and lockfile disagree, because a dependency changed without a matching bun install. Run bun install, then push again so the updated lockfile ships with your source. workspace_unavailable is ours, not yours: retry the build. The API key needs themes.read for list, pull, status, and the build polling in deploy. It needs themes.write for push, build enqueueing, and the publish step in deploy, so deploy needs both scopes. A key bound to one theme can use the equivalent theme-specific read and write scopes.