Skip to content

Commit 77cfdb0

Browse files
authored
feat(plugin): add storefront standards for coding agents (#387)
* feat(plugin): add storefront standards for coding agents * feat(plugin): define cart provider contract * chore: ignore generated zip archives * docs: strengthen storefront skill guidance * feat(plugin): build-shop with docs/src reference
1 parent d6429bd commit 77cfdb0

32 files changed

Lines changed: 1105 additions & 388 deletions

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ out/
2020
# production
2121
build
2222

23+
# generated archives
24+
/*.zip
25+
2326
# misc
2427
.DS_Store
2528
*.pem

apps/docs/AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ npx plugins add Shopify/shopify-ai-toolkit --scope project --yes
1616

1717
- `vercel-shop` provides the canonical storefront skills that the docs site renders and references.
1818
- `vercel-plugin` provides generic Vercel and Next.js skills.
19-
- `shopify-ai-toolkit` provides Shopify-aware tooling and schema access.
19+
- `shopify-ai-toolkit` is authoritative for current Shopify documentation, API schemas, operation validation, and store execution.
2020

2121
## Geistdocs architecture rules
2222

apps/docs/content/docs/getting-started/extending-with-agents.mdx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Vercel Shop is built for agentic development. Instead of copy-pasting snippets f
1111
This works because the template ships with structured context that agents read automatically:
1212

1313
- **`AGENTS.md`** - architecture rules and conventions. Tells the agent things like "every cart mutation must call `invalidateCartCache()`" or "components in `ui/` must not import domain types."
14-
- **Project-scoped plugins** - `vercel-shop`, `vercel-plugin`, and `shopify-ai-toolkit` provide skills, platform guidance, and live Shopify schema tooling.
14+
- **Project-scoped plugins** - `vercel-shop`, `vercel-plugin`, and `shopify-ai-toolkit` provide template conventions, platform guidance, and authoritative Shopify documentation and validation.
1515

1616
For best results, use an agent that reads `AGENTS.md` and supports skills and project-scoped plugins - for example [Claude Code](https://claude.ai/code), [Cursor](https://cursor.sh), or [Codex](https://openai.com/index/introducing-codex/). Claude Code has the deepest integration via the plugin install flow; Cursor and Codex lean more on the repo context and docs.
1717

@@ -21,7 +21,7 @@ After [creating your project](/docs/getting-started), `create-vercel-shop` insta
2121

2222
- `vercel-shop` for initialization and storefront skills plus drift and upgrade-planning commands
2323
- `vercel-plugin` for generic Vercel and Next.js guidance
24-
- `shopify-ai-toolkit` for Shopify-aware tooling and schema access
24+
- `shopify-ai-toolkit` for current Shopify documentation, schemas, operation validation, and store execution
2525

2626
If you already have a project and only want the agent setup:
2727

@@ -93,6 +93,9 @@ You can also follow any skill manually from the docs, or inspect the source in t
9393
### Available skills
9494

9595
<Cards>
96+
<Card title="Build with Vercel Shop" href="/docs/skills/build-shop">
97+
Source-backed storefront architecture and commerce behavior.
98+
</Card>
9699
<Card title="Enable Shopify Markets" href="/docs/skills/enable-shopify-markets">
97100
Multi-locale and multi-currency support.
98101
</Card>
@@ -146,6 +149,24 @@ Make the accordion closed by default on mobile
146149

147150
The agent retains context from the previous exchange.
148151

152+
### Use a browser feedback loop
153+
154+
Storefront changes should be checked in a real browser, not only by reading diffs. v0 has this feedback loop built in. For agents without built-in browser control, install the standalone [`agent-browser`](https://github.com/vercel-labs/agent-browser) CLI:
155+
156+
```bash
157+
npm install -g agent-browser
158+
agent-browser install
159+
```
160+
161+
Then ask the agent to start the dev server, open the affected route, capture a snapshot and screenshot, interact through the changed commerce flow, and repeat until the browser result matches the intended behavior.
162+
163+
```bash
164+
agent-browser open http://localhost:3000/products/example
165+
agent-browser wait --load networkidle
166+
agent-browser snapshot
167+
agent-browser screenshot --full
168+
```
169+
149170
### When something breaks, name the failure
150171

151172
Specific error messages and likely files beat "fix it":

apps/docs/content/docs/reference/storefront-api.mdx

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ Output lands in `lib/shopify/types/generated/`, which is **gitignored**. Nothing
7272

7373
Because codegen resolves interpolations of other `#graphql` consts by name, every fragment in `lib/shopify/fragments.ts` is also tagged `#graphql ... as const`. A raw selection snippet that isn't a valid standalone document (it can't be `#graphql`-tagged) must be inlined into each operation rather than interpolated.
7474

75-
Always verify fields against the live Storefront API schema with `shopify-ai-toolkit` or [`/vercel-shop:shopify-graphql-reference`](/docs/skills/shopify-graphql-reference) when adding or modifying queries - don't guess field names.
75+
Always use Shopify AI Toolkit to search current Shopify documentation and validate the final operation. Then use [`/vercel-shop:shopify-graphql-reference`](/docs/skills/shopify-graphql-reference) for Vercel Shop integration conventions. The Vercel Shop skill is not a schema source.
7676

7777
## Fragments
7878

@@ -131,14 +131,23 @@ export async function getProduct({
131131

132132
Key elements:
133133

134-
1. **`"use cache"`** - uses the local Next.js cache for stable, low-cardinality reads
134+
1. **`"use cache"`** - includes the stable product body coherently in the PDP's prerendered shell
135135
2. **`cacheLife("max")`** - caches indefinitely until manually revalidated
136136
3. **`cacheTag(...)`** - assigns tags for granular invalidation via `revalidateTag()`
137137
4. **Transform** - convert the Shopify response to a domain type before returning. Components never import Shopify types directly.
138138

139139
## Caching
140140

141-
Read operations use `"use cache"` or `"use cache: remote"` with `cacheLife("max")` and one or more cache tags. Stable, low-cardinality reads (product detail, single collection) can use plain `"use cache"` so they inline into the static shell; high-cardinality operations that depend on search, filters, sort, or cursors use `"use cache: remote"`, which stores entries in Vercel's shared Runtime Cache so they persist across serverless instances and between requests. Tags follow a hierarchy:
141+
Read operations choose cache behavior from their render role:
142+
143+
| Render role | Treatment |
144+
| ------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
145+
| Public identity/body that belongs coherently in the prerendered shell | Plain `"use cache"` |
146+
| Public results resolved after request inputs such as filters, search, or cursor | `"use cache: remote"` when shared Runtime Cache is justified |
147+
| Cart, session, authorization, or Customer Account data | Request-scoped or private; never public remote cache |
148+
| Mutation | No read-cache directive; invalidate affected state after success |
149+
150+
Cached public operations use the established `cacheLife` and tags. Combined with webhook-driven invalidation, those tags refresh the intended shell or runtime entry when Shopify changes. Tags follow a hierarchy:
142151

143152
| Tag pattern | Scope |
144153
| -------------------------- | ------------------------------------ |
@@ -206,20 +215,19 @@ Domain types are defined in `lib/types.ts` and are provider-agnostic. Components
206215

207216
## Key files and tools
208217

209-
| Resource | Purpose |
210-
| --------------------------------------- | ------------------------------------------------------------------------------------ |
211-
| `lib/shopify/storefront.ts` | Shared `@shopify/storefront-api-client` instance + `customFetchApi` |
212-
| `lib/shopify/errors.ts` | `assertStorefrontOk` response contract + cart error helpers |
213-
| `lib/shopify/customer-account.ts` | `customerAccountFetch` for the separate Customer Account API |
214-
| `.graphqlrc.ts` | Codegen config (schema source, documents, output dir) |
215-
| `lib/shopify/types/generated/` | Generated (gitignored) Storefront types + schema snapshot; codegen validation output |
216-
| `lib/shopify/fragments.ts` | Reusable `#graphql` fragment definitions |
217-
| `lib/shopify/operations/products.ts` | Product queries: single, batch, search, recommendations |
218-
| `lib/shopify/operations/cart.ts` | Cart queries and mutations |
219-
| `lib/shopify/operations/collections.ts` | Collection queries |
220-
| `lib/shopify/operations/search.ts` | Predictive search |
221-
| `lib/shopify/operations/menu.ts` | Navigation menu queries |
222-
| `lib/shopify/transforms/product.ts` | Shopify product → domain type |
223-
| `lib/shopify/transforms/cart.ts` | Shopify cart → domain type |
224-
| `lib/cart/server.ts` | `invalidateCartCache()` for mutation cache busting |
225-
| `shopify-ai-toolkit` | Live Storefront and Customer Account schema inspection |
218+
| Resource | Purpose |
219+
| --------------------------------------- | --------------------------------------------------------------------- |
220+
| `lib/shopify/storefront.ts` | Shared `@shopify/storefront-api-client` instance + `customFetchApi` |
221+
| `lib/shopify/errors.ts` | `assertStorefrontOk` response contract + cart error helpers |
222+
| `lib/shopify/customer-account.ts` | `customerAccountFetch` for the separate Customer Account API |
223+
| `.graphqlrc.ts` | Codegen config (schema source, documents, output dir) |
224+
| `lib/shopify/fragments.ts` | Reusable `#graphql` fragment definitions |
225+
| `lib/shopify/operations/products.ts` | Product queries: single, batch, search, recommendations |
226+
| `lib/shopify/operations/cart.ts` | Cart queries and mutations |
227+
| `lib/shopify/operations/collections.ts` | Collection queries |
228+
| `lib/shopify/operations/search.ts` | Predictive search |
229+
| `lib/shopify/operations/menu.ts` | Navigation menu queries |
230+
| `lib/shopify/transforms/product.ts` | Shopify product → domain type |
231+
| `lib/shopify/transforms/cart.ts` | Shopify cart → domain type |
232+
| `lib/cart/server.ts` | `invalidateCartCache()` for mutation cache busting |
233+
| `shopify-ai-toolkit` | Authoritative Shopify docs, schemas, operation design, and validation |

apps/docs/content/docs/reference/troubleshooting.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ images: {
4545

4646
This usually means the query references a field, argument, or enum value that doesn't exist on the live Shopify API version your store is using.
4747

48-
Re-check the operation with `shopify-ai-toolkit` or the [`shopify-graphql-reference` skill](/docs/skills/shopify-graphql-reference) and confirm each field against the live schema before changing the query.
48+
Re-check and validate the operation with Shopify AI Toolkit first. Then use the [`shopify-graphql-reference` skill](/docs/skills/shopify-graphql-reference) for the template integration and cache role.
4949

5050
If the Shopify plugin is missing, reinstall it:
5151

apps/docs/content/docs/shopify/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Menu items can link to collections, products, pages, or custom URLs.
3838

3939
## Webhooks and cache invalidation
4040

41-
All read operations are cached with `"use cache: remote"` and `cacheLife("max")`. Without webhooks, changes in Shopify won't appear until the cache expires. To get near-instant updates, configure Shopify webhooks to hit the template's invalidation endpoint.
41+
Public reads use either plain `"use cache"` for content that belongs coherently in a prerendered shell or `"use cache: remote"` for reusable request-time results. Both use established cache tags. Without webhooks, Shopify changes may remain cached until the configured lifetime expires, so configure Shopify webhooks to hit the template's invalidation endpoint.
4242

4343
### Webhook endpoint
4444

apps/docs/content/docs/shopify/writing-shopify-queries.mdx

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ prerequisites:
88

99
This guide walks through adding a new Shopify Storefront API operation from scratch. We'll fetch a shop's privacy policy as a concrete example.
1010

11-
If you're using a coding agent, run `/vercel-shop:shopify-graphql-reference` in Claude Code or ask the agent to use the `shopify-graphql-reference` skill first. The skill is the reference source for Shopify GraphQL work and uses `shopify-ai-toolkit` for live schema checks while keeping the agent anchored to the template guardrails.
11+
If you're using a coding agent, use Shopify AI Toolkit's Storefront GraphQL skill first so it searches current Shopify documentation and validates the operation. Then run `/vercel-shop:shopify-graphql-reference` to integrate that validated operation with the template's files, domain types, cache role, and invalidation.
1212

1313
## 1. Check the schema
1414

15-
Use `shopify-ai-toolkit`, Shopify's GraphQL docs, or your preferred GraphQL explorer to confirm the field exists before writing the query. For our example, the `QueryRoot` type has a `shop` field with a `privacyPolicy` property that returns a `ShopPolicy`:
15+
Use Shopify AI Toolkit to confirm the operation against the intended Storefront API version before writing template code. For this example, validate the `shop.privacyPolicy` selection and its return fields rather than relying on this guide as API reference:
1616

1717
```graphql
1818
type ShopPolicy {
@@ -86,15 +86,14 @@ Codegen parses every `#graphql`-tagged query and **fails the command if a field,
8686
Create a new file in `lib/shopify/operations/` or add to an existing one. The function calls `storefront.request`, asserts the response is OK, applies caching, and returns the result:
8787

8888
```ts
89-
import { cacheLife, cacheTag } from "next/cache";
89+
import { cacheLife } from "next/cache";
9090
import { defaultLocale, getCountryCode, getLanguageCode } from "@/lib/i18n";
9191
import { assertStorefrontOk } from "../errors";
9292
import { storefront } from "../storefront";
9393

9494
export async function getPrivacyPolicy(locale: string = defaultLocale) {
9595
"use cache";
96-
cacheLife("max");
97-
cacheTag("policies");
96+
cacheLife("days");
9897

9998
const response = await storefront.request<ShopifyPrivacyPolicyResponse>(PRIVACY_POLICY_QUERY, {
10099
variables: {
@@ -113,12 +112,12 @@ Key points:
113112

114113
- **`storefront`** is the shared `@shopify/storefront-api-client` instance from `lib/shopify/storefront.ts`. Pass your response type as the generic; the query is the first argument and variables go in the options object.
115114
- **`assertStorefrontOk(response, name)`** throws on a transport/GraphQL failure (no `data`), warns on partial errors, and narrows `response.data` to non-null.
116-
- **`"use cache"`** uses the local Next.js cache for stable, low-cardinality reads
117-
- **`cacheLife("max")`** caches until you manually invalidate
118-
- **`cacheTag("policies")`** lets you invalidate with `revalidateTag("policies")` later
119-
- Use `"use cache: remote"` instead for high-cardinality reads that depend on search, filters, sort, or cursors
115+
- **`"use cache"`** allows this stable policy body to become part of the page's prerendered shell.
116+
- **`cacheLife("days")`** refreshes content that has no matching webhook invalidation path.
120117
- Always pass `locale` and extract `country`/`language` from it
121118

119+
Cache behavior follows the consuming route, not a universal default. Use plain `"use cache"` for public identity/body content that must be coherent in the static shell. Use `"use cache: remote"` for reusable public results resolved after request inputs such as filters, search, or cursors. Keep cart, session, and Customer Account data request-scoped or private. Use `cacheLife("max")` only when a webhook, mutation, or deliberate manual path invalidates every associated tag.
120+
122121
## 6. Add a transform (if needed)
123122

124123
If the Shopify response shape differs from how your components want to consume the data, add a transform in `lib/shopify/transforms/`. This keeps Shopify-specific types out of your UI:
@@ -195,10 +194,11 @@ Expose mutations to the client through server actions in `components/cart/action
195194
- [ ] Query string starts with `#graphql` and the declaration ends with `as const`
196195
- [ ] Query/mutation is **named** so codegen can parse it
197196
- [ ] Document is static — dynamic parts passed as variables, not interpolated into the query string
197+
- [ ] Validated the final operation with Shopify AI Toolkit
198198
- [ ] `pnpm --filter template codegen` passes (validates fields against the live schema); generated types are gitignored and regenerate at dev/build
199199
- [ ] `@inContext` directive included if data is locale-sensitive
200200
- [ ] `storefront.request<ResponseType>(QUERY, { variables })` followed by `assertStorefrontOk(response, name)`
201-
- [ ] `"use cache"` or `"use cache: remote"`, `cacheLife`, and `cacheTag` set for read operations
201+
- [ ] Cache directive chosen from the route's render role, with `cacheLife` and `cacheTag` where applicable
202202
- [ ] Transform added if the Shopify response shape needs mapping
203203
- [ ] Components import domain types from `lib/types`, not Shopify response types
204204
- [ ] Cart mutations call `invalidateCartCache()`

0 commit comments

Comments
 (0)