epicmerch-mcp 1.3.20 → 1.3.21

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "epicmerch-mcp",
3
- "version": "1.3.20",
3
+ "version": "1.3.21",
4
4
  "description": "MCP server for EpicMerch — integrates e-commerce into Claude and ChatGPT",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -1,608 +1,123 @@
1
1
  ---
2
2
  name: epicmerch-storefront
3
- description: EpicMerch storefront scaffold — SDK + Login + ProductCard + ProductList + Cart + Checkout + OrderHistory + .env with auto-generated API key. The "do everything" route under /epicmerch, and also the home for single-slice integration (auth-only, products-only, or orders-only).
3
+ description: EpicMerch storefront scaffold — skeleton from the deterministic store_scaffold_full tool (SDK + Login + ProductCard + ProductList + Cart + Checkout + OrderHistory + .env), then a themed style pass and optional custom pages. The "do everything" route under /epicmerch, and the home for single-slice integration (auth-only, products-only, orders-only). Fast by design: the boilerplate is emitted by a tool (zero hand-writing), so generation is minutes, not an hour.
4
4
  ---
5
5
 
6
- # EpicMerch Full Storefront Scaffold
6
+ # EpicMerch Storefront Scaffold (tiered)
7
7
 
8
- You are integrating EpicMerch into this project. EpicMerch is a complete e-commerce backend that handles auth, inventory, orders, and payments. Follow every step below in order without asking for confirmation unless a step explicitly says to ask.
8
+ Build the storefront in tiers, fast: lay down the **standard skeleton from the
9
+ `store_scaffold_full` tool** (instant — do NOT hand-write those files), apply a
10
+ **theme delta**, and only LLM-generate genuinely custom pages (in parallel).
9
11
 
10
- ## Scope full storefront, or just one slice
12
+ **Idempotency rule (throughout):** before writing any file, check if it already
13
+ exists. If it does, SKIP it and tell the user "X already exists — keeping it".
14
+ Never overwrite a merchant's existing file.
11
15
 
12
- This skill scaffolds the **full** storefront by default. It also absorbs the former `/epicmerch-auth`, `/epicmerch-products`, and `/epicmerch-orders` skills if the merchant asked for only one slice, run just that subset. **Steps 0–3 always apply** (survey, detect framework, install SDK, write `src/lib/epicmerch.js`); then:
16
+ ## Phase 0Survey the project
13
17
 
14
- - **"add login / OTP / sign-in"** also Step 4 (Login.jsx).
15
- - **"add a product catalog / product list / search"** also Step 5 (+5b) (ProductCard + ProductList, including the keep-existing-card adapter path).
16
- - **"add a cart / checkout / order history"** also Step 6 (Cart + Checkout + OrderHistory).
17
- - **"set up everything" / unsure** the whole checklist.
18
+ - Read `package.json`. Determine the framework + env prefix:
19
+ - `react` or `vite` in depsframework `react`, env prefix `VITE_`
20
+ - `next` in depsframework `react`, env prefix `NEXT_PUBLIC_`
21
+ - `vue` in deps framework `react`, env prefix `VITE_`; tell the user "Vue isn't natively supported — installing React templates."
22
+ - none / no `package.json` → tell the user "No frontend project detected here — run me from inside a React/Vite/Next project." STOP.
23
+ - List which scaffold targets already exist (so Phase 1 can skip them):
24
+ `src/lib/epicmerch.js`, `src/components/{Login,ProductCard,ProductList,Cart,Checkout,OrderHistory}.jsx`, `.env`, `.env.example`.
25
+ - Briefly tell the user which exist (will be kept) vs which will be created.
18
26
 
19
- Step 7 (API key + `.env`) and Step 8 (next steps) run in every mode. The components and contracts are identical across modes — only which files you write changes.
27
+ ## Phase 1 Skeleton (instant, via the scaffold tool)
20
28
 
21
- **Idempotency rule (apply throughout):** Before writing any file in the scaffold steps below, check if it already exists. If it does, SKIP that file and tell the user "X already exists — keeping it". Never overwrite existing files. The merchant may already have a polished version of any of these components; preserving their work is more important than completing the scaffold.
29
+ Do NOT hand-write the standard components that is the slow path. Call the tool:
22
30
 
23
- ## Checklist
24
-
25
- - [ ] **Step 0: Survey the project**
26
-
27
- List which of the scaffold target files already exist in this project:
28
- - `src/lib/epicmerch.js`
29
- - `src/components/Login.jsx`
30
- - `src/components/ProductCard.jsx`
31
- - `src/components/ProductList.jsx`
32
- - `src/components/Cart.jsx`
33
- - `src/components/Checkout.jsx`
34
- - `src/components/OrderHistory.jsx`
35
- - `.env.example`
36
- - `.env`
37
-
38
- Briefly tell the user which already exist (those will be skipped) and which will be created. Then proceed.
39
-
40
- - [ ] **Step 1: Detect framework**
41
-
42
- Read `package.json`. Determine the framework:
43
- - If `"react"` or `"vite"` in dependencies → framework = `react`, envPrefix = `VITE_`
44
- - If `"next"` in dependencies → framework = `react`, envPrefix = `NEXT_PUBLIC_`
45
- - If `"vue"` in dependencies → framework = `react`, envPrefix = `VITE_`; tell the user: "Vue is not yet natively supported — installing React templates instead."
46
- - Otherwise → framework = `react`, envPrefix = `VITE_`
47
-
48
- Tell the user: "Detected [framework]. Installing EpicMerch SDK..."
49
-
50
- - [ ] **Step 2: Install the SDK**
51
-
52
- Check `package.json` first. If `"epicmerch"` is already in `dependencies`, skip the install and tell the user "epicmerch SDK already installed". Otherwise:
53
-
54
- Run: `npm install epicmerch`
55
-
56
- - [ ] **Step 3: Create SDK initializer**
57
-
58
- **If `src/lib/epicmerch.js` already exists, skip this step entirely** and tell the user "src/lib/epicmerch.js already exists — keeping it". Otherwise, write `src/lib/epicmerch.js` using the `envPrefix` detected in Step 1 to substitute the env var names:
59
-
60
- - For `VITE_` prefix (Vite / React):
61
- ```js
62
- import EpicMerch from 'epicmerch';
63
-
64
- export const store = new EpicMerch({
65
- apiKey: import.meta.env.VITE_API_KEY,
66
- ...(import.meta.env.VITE_API_URL && { baseUrl: import.meta.env.VITE_API_URL }),
67
- });
68
-
69
- // Restore the customer session on page load. Without this, OTP login
70
- // appears to "work" (token gets saved to localStorage) but the SDK
71
- // stays unauthenticated until something calls setCustomerToken — so
72
- // the merchant logs in, the page reloads, and they're logged out again.
73
- if (typeof window !== 'undefined') {
74
- try {
75
- const saved = localStorage.getItem('customerInfo');
76
- if (saved) {
77
- const parsed = JSON.parse(saved);
78
- if (parsed?.token) store.setCustomerToken(parsed.token);
79
- }
80
- } catch (_) { /* localStorage unavailable — silent fallback */ }
81
- }
82
- ```
83
-
84
- - For `NEXT_PUBLIC_` prefix (Next.js):
85
- ```js
86
- import EpicMerch from 'epicmerch';
87
-
88
- export const store = new EpicMerch({
89
- apiKey: process.env.NEXT_PUBLIC_API_KEY,
90
- ...(process.env.NEXT_PUBLIC_API_URL && { baseUrl: process.env.NEXT_PUBLIC_API_URL }),
91
- });
92
-
93
- // Restore customer session on page load (same as Vite — without this,
94
- // OTP login looks like it works but doesn't survive a page reload).
95
- // Guard the localStorage call so server-rendered pages don't crash.
96
- if (typeof window !== 'undefined') {
97
- try {
98
- const saved = localStorage.getItem('customerInfo');
99
- if (saved) {
100
- const parsed = JSON.parse(saved);
101
- if (parsed?.token) store.setCustomerToken(parsed.token);
102
- }
103
- } catch (_) { /* localStorage unavailable */ }
104
- }
105
- ```
106
-
107
- - [ ] **Step 4: Scaffold auth**
108
-
109
- **If `src/components/Login.jsx` already exists, skip this step** and tell the user "Login.jsx already exists — keeping it". Otherwise write `src/components/Login.jsx`:
110
-
111
- ```jsx
112
- import { useState } from 'react';
113
- import { store } from '../lib/epicmerch';
114
-
115
- export default function Login({ onLogin }) {
116
- const [phone, setPhone] = useState('');
117
- const [otp, setOtp] = useState('');
118
- const [step, setStep] = useState('phone');
119
- const [error, setError] = useState('');
120
-
121
- const sendOtp = async () => {
122
- setError('');
123
- try {
124
- await store.auth.sendOtp(phone, 'phone');
125
- setStep('otp');
126
- } catch (e) {
127
- // e.g. invalid number, or OTP_RATE_LIMIT_EXCEEDED (5/hour).
128
- setError(e?.message || 'Could not send OTP. Check the number and try again.');
129
- }
130
- };
131
-
132
- const verify = async () => {
133
- setError('');
134
- try {
135
- const result = await store.auth.verifyOtp(phone, otp, {});
136
- if (result.token) {
137
- // SDK already sets the token internally during verifyOtp; mirror to
138
- // localStorage so src/lib/epicmerch.js can restore on next page load.
139
- store.setCustomerToken(result.token);
140
- localStorage.setItem('customerInfo', JSON.stringify(result));
141
- onLogin(result);
142
- }
143
- } catch (e) {
144
- // e.g. "Invalid OTP" / "OTP expired" — let them retry.
145
- setError(e?.message || 'Invalid or expired OTP. Try again.');
146
- setOtp('');
147
- }
148
- };
149
-
150
- return (
151
- <div>
152
- {step === 'phone' ? (
153
- <>
154
- <input
155
- type="tel"
156
- value={phone}
157
- onChange={e => setPhone(e.target.value)}
158
- placeholder="+919876543210"
159
- autoComplete="tel"
160
- />
161
- <button onClick={sendOtp}>Send OTP</button>
162
- </>
163
- ) : (
164
- <>
165
- {/* EpicMerch OTPs are 4 digits. inputMode='numeric' pops the
166
- numeric keypad on mobile; autoComplete='one-time-code' lets
167
- iOS auto-fill from SMS without leaving the page. */}
168
- <input
169
- type="tel"
170
- inputMode="numeric"
171
- maxLength={4}
172
- autoComplete="one-time-code"
173
- value={otp}
174
- onChange={e => setOtp(e.target.value.replace(/\D/g, '').slice(0, 4))}
175
- placeholder="4-digit OTP"
176
- autoFocus
177
- />
178
- <button onClick={verify} disabled={otp.length !== 4}>Verify</button>
179
- </>
180
- )}
181
- {error && <p style={{ color: 'crimson' }}>{error}</p>}
182
- </div>
183
- );
184
- }
185
- ```
186
-
187
- - [ ] **Step 5: Scaffold product catalog**
188
-
189
- For each of the two files in this step, check existence individually. If a file already exists, skip writing it and tell the user "X already exists — keeping it". Otherwise write it.
190
-
191
- **Special case — `ProductCard.jsx` already exists:** don't just skip it. The merchant has a card UI that's probably showing placeholder data. KEEP their card, but make sure it gets fed REAL products: (1) read the existing card to note the field names it expects (`name`/`title`, `price`/`cost`, `image`/`images[0]`/`thumbnail`, single `product` prop vs destructured); (2) when you write/keep `ProductList.jsx` below, have it call `store.products.list()` and map each API product onto the shape the existing card expects (an adapter — see the ProductList note below). Tell the user "ProductCard.jsx already exists — keeping your design and wiring it to live product data."
192
-
193
- Write `src/components/ProductCard.jsx`:
194
-
195
- ```jsx
196
- import { useState } from 'react';
197
-
198
- export default function ProductCard({ product, onAddToCart }) {
199
- // ProductVariant rows are { variant: String, stock: Int } — note the field
200
- // is named `variant`, NOT `size`. The "variant" string is whatever the
201
- // merchant configured (typically a size like 'S'/'M'/'L', but could be
202
- // a color or material). Only show in-stock options — out-of-stock orders
203
- // are rejected with "INSUFFICIENT_STOCK: <variant>: need N, have 0".
204
- const availableVariants = (product.variants || []).filter(v => v.stock > 0);
205
- const [variant, setVariant] = useState(availableVariants[0]?.variant);
206
-
207
- // Products on sale return both `price` (original) and `salePrice` (current).
208
- // Always display the current price the customer will actually pay.
209
- const price = product.salePrice ?? product.price;
210
- const image = product.images?.[0] || product.image;
211
- const hasVariants = availableVariants.length > 0;
212
-
213
- return (
214
- <div className="product-card">
215
- <img src={image} alt={product.name} />
216
- <h3>{product.name}</h3>
217
- <p>₹{price}</p>
218
- {hasVariants && (
219
- <div className="variant-picker">
220
- {availableVariants.map(v => (
221
- <button
222
- key={v.variant}
223
- type="button"
224
- onClick={() => setVariant(v.variant)}
225
- className={variant === v.variant ? 'selected' : ''}
226
- >
227
- {v.variant}
228
- </button>
229
- ))}
230
- </div>
231
- )}
232
- <button
233
- onClick={() => onAddToCart(product._id, variant)}
234
- disabled={hasVariants && !variant}
235
- >
236
- Add to Cart
237
- </button>
238
- </div>
239
- );
240
- }
241
- ```
242
-
243
- Write `src/components/ProductList.jsx`:
244
-
245
- ```jsx
246
- import { useEffect, useState } from 'react';
247
- import { store } from '../lib/epicmerch';
248
- import ProductCard from './ProductCard';
249
-
250
- export default function ProductList() {
251
- const [products, setProducts] = useState([]);
252
- const [categories, setCategories] = useState([]);
253
- const [category, setCategory] = useState(undefined);
254
- const [query, setQuery] = useState('');
255
- // products.list/search return { products, page, pages, total } — track
256
- // page + pages for "Load more" so a catalog (or result set) larger than
257
- // `limit` isn't silently truncated to the first page.
258
- const [page, setPage] = useState(1);
259
- const [pages, setPages] = useState(1);
260
-
261
- useEffect(() => { store.categories.list().then(setCategories); }, []);
262
-
263
- useEffect(() => {
264
- const req = query.trim()
265
- ? store.products.search(query, { page, limit: 12 })
266
- : store.products.list({ type: category, page, limit: 12 });
267
- req.then(d => {
268
- setPages(d.pages ?? 1);
269
- // Page 1 replaces (new filter); later pages append (Load more).
270
- setProducts(prev => (page === 1 ? (d.products ?? d) : [...prev, ...(d.products ?? d)]));
271
- });
272
- }, [query, category, page]);
273
-
274
- // Reset to page 1 whenever the filter changes.
275
- const changeQuery = (q) => { setPage(1); setQuery(q); };
276
- const changeCategory = (c) => { setPage(1); setQuery(''); setCategory(c === 'All' ? undefined : c); };
277
-
278
- // `variant` is the size STRING (e.g. 'L'), per the SDK signature
279
- // store.cart.add(productId, qty, variant). Products without variants
280
- // pass undefined and the SDK accepts that.
281
- const addToCart = (productId, variant) => store.cart.add(productId, 1, variant);
282
-
283
- return (
284
- <div>
285
- <input
286
- placeholder="Search products..."
287
- value={query}
288
- onChange={e => changeQuery(e.target.value)}
289
- />
290
- <div>
291
- {categories.map(c => (
292
- <button key={c} onClick={() => changeCategory(c)}>{c}</button>
293
- ))}
294
- </div>
295
- <div className="product-grid">
296
- {products.map(p => <ProductCard key={p._id} product={p} onAddToCart={addToCart} />)}
297
- </div>
298
- {page < pages && (
299
- <button onClick={() => setPage(p => p + 1)}>Load more</button>
300
- )}
301
- </div>
302
- );
303
- }
304
- ```
305
-
306
- **If you KEPT an existing `ProductCard` (it already existed):** the API returns each product as `{ _id, name, price, salePrice, image, images, variants, ... }`. If the merchant's card reads different field names, don't change the card — adapt the data in the `.map()` so live products populate their existing design:
307
-
308
- ```jsx
309
- // e.g. existing card expects { title, cost, thumbnail }:
310
- {products.map(p => (
311
- <ProductCard
312
- key={p._id}
313
- product={{ ...p, title: p.name, cost: p.salePrice ?? p.price, thumbnail: p.images?.[0] || p.image }}
314
- onAddToCart={addToCart}
315
- />
316
- ))}
317
- ```
318
-
319
- Match the adapter keys to whatever the existing card actually reads (you noted that when you decided to keep it). The cards keep the merchant's look but show **real products fetched via `store.products.list()`**.
320
-
321
- - [ ] **Step 5b: Wire up an existing search bar (only if `ProductList.jsx` was skipped)**
322
-
323
- If you wrote a fresh `ProductList.jsx` in Step 5, skip this step — the canonical scaffold already has search wired.
324
-
325
- If `ProductList.jsx` was SKIPPED because it already existed, the merchant has their own UI. They may have a search bar somewhere — in a `Navbar`, a `Header`, a dedicated `SearchBar.jsx`, a search page, etc. — that isn't currently calling EpicMerch's search API. Detect and offer to wire it up.
326
-
327
- **Detection.** Look through `src/` for files containing any of these signals (case-insensitive):
328
- - `<input` whose `placeholder` mentions `search`
329
- - A state variable named `query`, `search`, `searchTerm`, `searchQuery`, or `keyword`
330
- - A handler named `onSearch`, `handleSearch`, `onQueryChange`, or similar
331
-
332
- For each match, check whether the file already calls `store.products.search(`. If yes, it's wired — skip silently.
333
-
334
- **For each unwired match:** show the merchant the file path and the snippet you found, then say:
335
-
336
- > "I noticed a search input in `<path>` that doesn't call EpicMerch's search yet. The one-line wire-up is:
337
- >
338
- > ```js
339
- > import { store } from '../lib/epicmerch'; // add at top if missing
340
- >
341
- > // inside the search handler / useEffect:
342
- > store.products.search(query).then(d => setProducts(d.products ?? d));
343
- > ```
344
- >
345
- > Want me to apply this to your component? (yes / no — I'll leave it alone if no.)"
346
-
347
- **WAIT for an explicit yes before editing.** If the merchant declines, move on without changing the file.
348
-
349
- If you find no search bar anywhere in `src/`, skip this step silently — don't tell the merchant "no search bar found", just continue to Step 6.
350
-
351
- - [ ] **Step 6: Scaffold cart + orders**
352
-
353
- For each of the three files in this step (Cart.jsx, Checkout.jsx, OrderHistory.jsx), check existence individually. If a file already exists, skip writing it and tell the user "X already exists — keeping it". Otherwise write it.
354
-
355
- Write `src/components/Cart.jsx`:
356
-
357
- ```jsx
358
- import { useEffect, useState } from 'react';
359
- import { store } from '../lib/epicmerch';
360
-
361
- export default function Cart({ onCheckout }) {
362
- // store.cart.get() returns { cart: [...] } — JUST one field. The SDK
363
- // README mentions cartCount/cartTotal but those aren't actually sent by
364
- // the server; compute them locally. Each line item is shaped
365
- // { product: { _id, name, price, originalPrice, salePrice, image, type },
366
- // qty, variant }. Note `product.price` on cart items is ALREADY the
367
- // effective price (sale if on sale, else regular).
368
- const [cart, setCart] = useState({ cart: [] });
369
-
370
- useEffect(() => { store.cart.get().then(setCart); }, []);
371
-
372
- const remove = async (productId, variant) => {
373
- await store.cart.remove(productId, variant);
374
- // Optimistic local update — match on BOTH productId AND variant so we
375
- // don't accidentally remove a different size of the same product.
376
- setCart(c => ({
377
- ...c,
378
- cart: (c.cart || []).filter(
379
- i => !(i.product._id === productId && i.variant === variant),
380
- ),
381
- }));
382
- };
383
-
384
- const items = cart.cart || [];
385
- const total = items.reduce((sum, i) => {
386
- const price = i.product.salePrice ?? i.product.price;
387
- return sum + price * i.qty;
388
- }, 0);
389
-
390
- return (
391
- <div>
392
- {items.map((item, idx) => {
393
- const price = item.product.salePrice ?? item.product.price;
394
- return (
395
- <div key={`${item.product._id}-${item.variant || 'novariant'}-${idx}`}>
396
- <span>
397
- {item.product.name} x{item.qty}
398
- {item.variant ? ` (${item.variant})` : ''}
399
- </span>
400
- <span>₹{price * item.qty}</span>
401
- <button onClick={() => remove(item.product._id, item.variant)}>Remove</button>
402
- </div>
403
- );
404
- })}
405
- <p>Total: ₹{total}</p>
406
- <button onClick={onCheckout}>Checkout</button>
407
- </div>
408
- );
409
- }
410
- ```
411
-
412
- Write `src/components/Checkout.jsx`:
413
-
414
- ```jsx
415
- import { useState } from 'react';
416
- import { store } from '../lib/epicmerch';
417
-
418
- // Razorpay's checkout.js is heavy — load it lazily on the first Place Order
419
- // click instead of blocking initial page render.
420
- const loadRazorpay = () =>
421
- new Promise((resolve) => {
422
- if (window.Razorpay) return resolve(true);
423
- const script = document.createElement('script');
424
- script.src = 'https://checkout.razorpay.com/v1/checkout.js';
425
- script.onload = () => resolve(true);
426
- script.onerror = () => resolve(false);
427
- document.body.appendChild(script);
428
- });
429
-
430
- export default function Checkout({ cart, onSuccess }) {
431
- const [address, setAddress] = useState({ street: '', city: '', postalCode: '', country: 'India' });
432
- const [error, setError] = useState('');
433
-
434
- const placeOrder = async () => {
435
- setError('');
436
- await loadRazorpay();
437
-
438
- // Map cart line items to the orderItems shape the API expects.
439
- // Critical fields per the SDK contract:
440
- // - `productId` — the product _id (same field name as cart.add,
441
- // magic-checkout-init, analytics.track; consistent
442
- // across every endpoint). Server still accepts the
443
- // legacy `product` field but `productId` is canonical.
444
- // - `name`, `image` — required, the server doesn't re-fetch them
445
- // - `variant` — the size STRING, must match what was added to cart
446
- // or the variant lookup returns undefined and you'll
447
- // see "INSUFFICIENT_STOCK: undefined: need 1, have 0"
448
- const items = cart.cart || [];
449
- const orderItems = items.map(i => ({
450
- productId: i.product._id,
451
- name: i.product.name,
452
- image: i.product.images?.[0] || i.product.image,
453
- qty: i.qty,
454
- price: i.product.salePrice ?? i.product.price,
455
- variant: i.variant,
456
- }));
457
- const total = orderItems.reduce((s, i) => s + i.price * i.qty, 0);
458
-
459
- // ONE call to /customer/orders is enough — the server creates the
460
- // EpicMerch order AND the Razorpay order in a single transaction and
461
- // returns both back. Do NOT separately call store.payment.getConfig()
462
- // or store.payment.createOrder() — orderResult already includes
463
- // razorpayKeyId, razorpayOrderId, amount, currency, merchantName.
464
- // Calling them again creates an orphan second Razorpay order AND can
465
- // trigger an idempotency-key collision against /customer/orders.
466
- let orderResult;
467
- try {
468
- orderResult = await store.orders.create({
469
- orderItems,
470
- shippingAddress: address,
471
- paymentMethod: 'Razorpay',
472
- totalPrice: total,
473
- });
474
- } catch (e) {
475
- // The API returns a structured error and the SDK throws it:
476
- // INSUFFICIENT_STOCK ("Halden 3-Seat Sofa: need 1, have 0"),
477
- // INVALID_ORDER_ITEM, PAYMENT_NOT_CONFIGURED, etc. Surface the message.
478
- setError(e?.message || 'Could not place the order. Please try again.');
479
- return;
480
- }
481
-
482
- // COD / fully-discounted orders return NO Razorpay session
483
- // (razorpayOrderId is null) — the order is already placed, nothing to pay.
484
- if (!orderResult.razorpayOrderId) {
485
- await store.cart.clear();
486
- onSuccess(orderResult.orderId);
487
- return;
488
- }
489
-
490
- const rzp = new window.Razorpay({
491
- key: orderResult.razorpayKeyId,
492
- order_id: orderResult.razorpayOrderId,
493
- amount: orderResult.amount, // already in paise
494
- currency: orderResult.currency,
495
- name: orderResult.merchantName,
496
- handler: async (response) => {
497
- await store.payment.verify({ ...response, orderId: orderResult.orderId });
498
- await store.cart.clear();
499
- onSuccess(orderResult.orderId);
500
- },
501
- });
502
- rzp.open();
503
- };
504
-
505
- return (
506
- <div>
507
- <input placeholder="Street" onChange={e => setAddress(a => ({ ...a, street: e.target.value }))} />
508
- <input placeholder="City" onChange={e => setAddress(a => ({ ...a, city: e.target.value }))} />
509
- <input placeholder="Postal Code" onChange={e => setAddress(a => ({ ...a, postalCode: e.target.value }))} />
510
- {error && <p style={{ color: 'crimson' }}>{error}</p>}
511
- <button onClick={placeOrder}>Place Order</button>
512
- </div>
513
- );
514
- }
515
31
  ```
516
-
517
- **If the merchant has a top-level `App.jsx` (or similar) that maintains a `refreshCartCount` or `handleAddToCart`, also update it:** `cart.get()` returns `{ cart: [...] }`, so `refreshCartCount` should read `c?.cart?.length` (NOT `c?.items?.length` and NOT `c?.cartCount` — that field doesn't come back from the server, despite what the SDK README says); and `handleAddToCart` should accept a `variant` arg and forward it: `store.cart.add(productId, 1, variant)`. Bonus: `store.cart.add` itself returns `{ message, cartCount }`, so you can update the badge from THAT response without a separate `cart.get()` round-trip.
518
-
519
- Do NOT add any Razorpay script tag to `index.html` — the script loads dynamically only when the user clicks Place Order.
520
-
521
- Write `src/components/OrderHistory.jsx`:
522
-
523
- ```jsx
524
- import { useEffect, useState } from 'react';
525
- import { store } from '../lib/epicmerch';
526
-
527
- export default function OrderHistory() {
528
- const [orders, setOrders] = useState([]);
529
-
530
- useEffect(() => { store.orders.list().then(setOrders); }, []);
531
-
532
- return (
533
- <div>
534
- <h2>Your Orders</h2>
535
- {orders.map(order => (
536
- <div key={order._id}>
537
- <span>Order #{order._id.slice(-6)}</span>
538
- <span> — ₹{order.totalPrice}</span>
539
- <span> — {order.status}</span>
540
- </div>
541
- ))}
542
- </div>
543
- );
544
- }
32
+ store_scaffold_full({ framework: "react" })
545
33
  ```
546
34
 
547
- - [ ] **Step 7: Generate API key via MCP + write `.env`**
548
-
549
- This step has two parts: write `.env.example` (the template), then generate a real API key and write a working `.env`.
550
-
551
- **Part A — `.env.example`:** If `.env.example` already exists, skip writing it and tell the user ".env.example already exists — keeping it". Otherwise write `.env.example`:
35
+ It returns `content[0].text` = a JSON string of `[{ path, content }]` for 8 files:
36
+ `src/lib/epicmerch.js`, `src/components/Login.jsx`, `ProductCard.jsx`,
37
+ `ProductList.jsx`, `Cart.jsx`, `Checkout.jsx`, `OrderHistory.jsx`, `.env.example`.
552
38
 
553
- ```
554
- VITE_API_KEY=your_epicmerch_api_key_here
555
- # VITE_API_URL is OPTIONAL — only set it for local development.
556
- # Production: leave it unset; the SDK defaults to https://api.epicmerch.in/api
557
- # Local dev: VITE_API_URL=http://localhost:5002/api
558
- ```
39
+ Parse it and **write each file whose path does NOT already exist** (Phase 0
40
+ skip-existing rule). Report `✓ wrote <n> files (kept <m> existing)`.
559
41
 
560
- Check `.gitignore` if `.env` is not listed, append `.env` to `.gitignore` before any `.env` file is written.
42
+ These files are contract-tested (cart.cart shape, `productId`, `v.variant`,
43
+ session-restore, COD guard, pagination) — they are correct by construction, which
44
+ is why we use the tool instead of writing them by hand.
561
45
 
562
- **Part B generate a real API key + write `.env`:**
46
+ > If the merchant only wants ONE slice (auth-only / products-only / orders-only),
47
+ > call the narrower tool instead: `store_scaffold_auth`, `store_scaffold_products`,
48
+ > or `store_scaffold_orders` (same `{framework}` arg, same `[{path,content}]`
49
+ > return). Phases 0, 2, 3, 5 still apply.
563
50
 
564
- First, read `.env` if it exists. If it already contains a `VITE_API_KEY=` line whose value is NOT the placeholder string `your_epicmerch_api_key_here` (i.e. there's already a real key), skip Part B entirely and tell the user "Existing API key in .env — keeping it".
51
+ ## Phase 2 API key + `.env`
565
52
 
566
- Otherwise, the merchant is authenticated via the EpicMerch MCP (this skill assumes the MCP is connected — that's how it was invoked). Generate a key by calling the MCP tool:
53
+ `store_scaffold_full` emits `.env.example`; now create the real `.env`:
567
54
 
568
- ```
569
- merchant_generate_api_key({ name: "Storefront" })
570
- ```
55
+ 1. If `.env` already has a real `VITE_API_KEY` (not the `your_epicmerch_api_key_here`
56
+ placeholder), keep it — tell the user "Existing API key in .env — keeping it."
57
+ 2. Otherwise call `merchant_generate_api_key({ name: "Storefront" })`, take the
58
+ returned key, and write `.env`:
59
+ - Production (default): just `VITE_API_KEY=<key>` (the SDK defaults to
60
+ `https://api.epicmerch.in/api`).
61
+ - Local dev (only if the merchant is clearly on a local server): also add
62
+ `VITE_API_URL=http://localhost:5001/api`.
63
+ - For Next.js, use `NEXT_PUBLIC_API_KEY` / `NEXT_PUBLIC_API_URL`.
64
+ 3. Ensure `.env` is gitignored — if `.gitignore` lacks `.env`, append it BEFORE
65
+ writing `.env`.
66
+ 4. Fallback: if `merchant_generate_api_key` is unavailable, tell the user to grab a
67
+ key from `https://epicmerch.in/dashboard → API Keys` and add it to `.env`. Do NOT
68
+ abort — continue to Phase 3.
571
69
 
572
- The response will include the new key. Extract it (the field is typically `key` or `apiKey` — check the response shape). Then write/update `.env`.
70
+ ## Phase 3 Theme delta (the only standard-tier LLM work)
573
71
 
574
- **Production (default).** Write only the key — the SDK already knows the prod URL:
72
+ Ask ONE vibe question:
575
73
 
576
- ```
577
- VITE_API_KEY=<the generated key from the MCP response>
578
- ```
74
+ > "Quick — what's the vibe of your store?
75
+ > 1) Editorial Funk (bold typography, big imagery, asymmetric layouts)
76
+ > 2) Minimal Mono (clean, monochrome, lots of whitespace)
77
+ > 3) Vibrant Pop (colourful, rounded corners, playful)
78
+ > 4) Skip — functional unstyled scaffolds, I'll polish later"
579
79
 
580
- **Local development.** Only if the merchant is clearly running against a local EpicMerch server (e.g. they explicitly said so, or their existing `.env` already had a `localhost` URL, or the MCP was wired with `EPICMERCH_API_URL=http://localhost:...`), ALSO write the override:
80
+ - Options 1–3: do a BOUNDED Tailwind pass add 5–10 well-chosen utility classes
81
+ per scaffolded component (typography, spacing, colour) matching the vibe. This is
82
+ a class edit, NOT a rewrite — do not regenerate the components.
83
+ - Option 4: skip the pass.
84
+ - Either way, confirm `src/lib/epicmerch.js` contains the **session-restore block**
85
+ (`localStorage.getItem('customerInfo') → setCustomerToken`). The tool includes it
86
+ by default; if a pre-existing file was kept and lacks it, add it manually — without
87
+ it, OTP login silently doesn't survive a page reload (the #1 storefront bug).
88
+ - Recap `✓ Storefront scaffolded + styled (<vibe>)` or `✓ ... (unstyled)`.
581
89
 
582
- ```
583
- VITE_API_KEY=<the generated key from the MCP response>
584
- VITE_API_URL=http://localhost:5002/api
585
- ```
90
+ ## Phase 4 — Custom pages (opt-in, parallel)
586
91
 
587
- When in doubt, default to the production form (no `VITE_API_URL` line). Don't ask the merchant — guess from context.
92
+ Ask: *"Want any custom pages beyond the standard store (e.g. an About page, a
93
+ lookbook, a size guide, a blog)? Name them, or say 'no' to finish."*
588
94
 
589
- **Fallback if MCP isn't available:** If the `merchant_generate_api_key` tool fails or isn't connected, tell the user: "MCP tool unavailable. Generate a key manually at https://epicmerch.in/dashboard API Keys and add it to .env." Do NOT fail the whole flow — continue to Step 8.
95
+ - "no"skip to Phase 5.
96
+ - Otherwise build a manifest: one entry per custom file (path + a one-line spec).
97
+ Then generate them — **in parallel if your client supports subagents** (e.g.
98
+ Claude Code's Task/Agent tool: dispatch one agent per file in a single batch);
99
+ otherwise generate them **sequentially** (still far faster than the old flow,
100
+ since the whole skeleton was free). Each generation gets: the SDK usage (import
101
+ `store` from `src/lib/epicmerch.js`; products via `store.products.list()`; the
102
+ cart/checkout contracts), the chosen vibe, and that one file's spec.
103
+ - After the custom files exist, do ONE small assembly pass: wire routes/nav so the
104
+ new pages are reachable. Preserve existing routing.
590
105
 
591
- - [ ] **Step 8: Tell the user what to do next**
106
+ ## Phase 5 Verify (the Definition-of-Done gate)
592
107
 
593
- Print a summary using the actual state of the project:
108
+ Run `/epicmerch-verify` (Deep mode) the full product→cart→order pipeline against
109
+ the live API; on pass it stamps server-side verification. Do not tell the merchant
110
+ "your store is set up" until this is green. If it fails, route to `/epicmerch-debug`,
111
+ fix, and re-run.
594
112
 
595
- - List the files you CREATED in this run (skip the ones that were already present).
596
- - If you skipped any, briefly mention them as "kept as-is".
597
- - If Step 5b wired up a search bar in an existing component, mention which file you edited and that it now calls `store.products.search()`.
598
- - If the API key was generated automatically, mention that ".env now has a working VITE_API_KEY — your storefront should load real product data on next refresh."
599
- - Then output the next-steps block exactly:
113
+ ## Wrap-up
600
114
 
601
- ```
602
- Next steps:
603
- 1. (API key auto-generated and saved to .env — if not, get one at https://epicmerch.in/dashboard → API Keys)
604
- 2. Restart your dev server (npm run dev) to pick up the .env changes
605
- 3. Import the new components into your App.jsx where you need them
115
+ Print what was created (skeleton files written vs kept, theme applied, any custom
116
+ pages, verify result), then:
606
117
 
607
- Need help with payments, analytics, or notifications? Ask me!
118
+ ```
119
+ Next:
120
+ 1. Restart your dev server (npm run dev) to pick up .env.
121
+ 2. Import the components where you need them.
122
+ Need payments, analytics, or a Buy Now button? Just ask.
608
123
  ```
@@ -1,239 +1,242 @@
1
- ---
2
- name: epicmerch
3
- description: EpicMerch umbrella — guided 6-step onboarding wizard for new merchants, driven by a deterministic state machine (merchant_onboarding_state) that re-derives progress from live state and CANNOT report "complete" until a real end-to-end order test (/epicmerch-verify Deep mode) has stamped verification server-side. Also an intent router for merchants who already know what they want (scaffold a storefront, set up payments, add a Buy Now button, manage a store, migrate from Shopify, verify the store). Verification is the non-negotiable, structurally-enforced definition of done.
4
- ---
5
-
6
- # EpicMerch — the umbrella
7
-
8
- The **one entry point** for everything EpicMerch. Merchants don't need to memorise 10+ slash commands; they say what they want (or say nothing) and you figure out the right flow.
9
-
10
- ## Mode detection
11
-
12
- Two modes. Pick one based on what the merchant said:
13
-
14
- | What the merchant said when invoking `/epicmerch` | Mode |
15
- |---|---|
16
- | Nothing specific. Just `/epicmerch` with no extra words. | **Wizard mode** (Step A below) |
17
- | "help me start", "I'm new", "set up everything", "what's next", "where do I begin" | **Wizard mode** |
18
- | "set up Razorpay", "add a Buy Now button", "migrate Shopify", "scaffold a storefront", "show me my products" — anything with a clear specific intent | **Intent mode** (Step B below) |
19
-
20
- If ambiguous, ask ONE disambiguating question: *"Want me to walk you through a full setup wizard, or is there one specific thing you want to do?"*
21
-
22
- ---
23
-
24
- ## DEFINITION OF DONE — read this before you finish (applies to BOTH modes)
25
-
26
- **`/epicmerch` is not complete until a live end-to-end test has run and passed.**
27
-
28
- This holds no matter how the build happened — the wizard's scaffold step, an intent-mode `/epicmerch-storefront`, OR a fully custom build you designed through brainstorming (e.g. a themed multi-page store). However you got here, the LAST thing you do before telling the merchant "your store is set up" is run **`/epicmerch-verify` (Deep mode)** — the full product→cart→order pipeline; it automatically falls back to Smoke mode if the server's test-login isn't configured.
29
-
30
- Common trap: a design-heavy request ("build me a saree store") pulls you into scope/theme/spec brainstorming and a custom build, and the flow ends at "looks good!" without ever testing against the live API. **Don't let that happen.** After the storefront files are written and the `.env` has a real API key, run the e2e/verify gate. A store that was never tested against the live API is not done — that's exactly how the cart-shape / productId / out-of-stock bugs reached real merchants.
31
-
32
- If you realize you're wrapping up `/epicmerch` and haven't run the test → run it now.
33
-
34
- In **Wizard mode** this is enforced for you: `merchant_onboarding_state` will not return `complete` until `/epicmerch-verify` (Deep mode) has stamped verification server-side, so you structurally can't wrap up early. In **Intent mode** and custom/brainstormed builds there's no such gate — that's where this reminder matters most.
35
-
36
- ---
37
-
38
- ## Step A — Wizard mode (the harness loop)
39
-
40
- Wizard mode is a **deterministic loop driven by the `merchant_onboarding_state` tool** — not prose you interpret. The tool re-derives what's done, what's next, and whether you're complete from **live server state on every call**, and it enforces the gate: you **cannot** reach `complete` until a real `/epicmerch-verify` (Deep mode) run has stamped verification server-side. That is what makes `/epicmerch` un-breakable — the control flow lives in code + a hard gate, so "done but never verified" is structurally unreachable. You bring the creative work (the conversation, the product shape, the styling); the tool owns order, idempotency, and the gate.
41
-
42
- Open with one sentence:
43
-
44
- > "I'll set up your EpicMerch store. I'll check what's already done, do the next thing, and finish with a live end-to-end test — that test is the only thing that marks the setup complete."
45
-
46
- ### The loop
47
-
48
- Repeat until the tool returns `complete: true`:
49
-
50
- 1. Check the local project: does `src/lib/epicmerch.js` exist? (`true`/`false` → `storefrontPresent`).
51
- 2. Call `merchant_onboarding_state({ storefrontPresent })`.
52
- 3. If `complete === true` → print the **wrap-up recap** (below) and STOP. You're done.
53
- 4. Else → read `nextStep` and perform **that one step's action** (see *Step actions* below). The `steps[nextStep].action` string is a short reminder; the full expansion is below. Do exactly ONE step, recap it in one line (`✓ <what happened>`), then go back to 1.
54
-
55
- **Rules that keep the harness honest:**
56
- - Never skip ahead, never decide completion yourself, never print "your store is set up" before the tool returns `complete: true`.
57
- - The loop is inherently idempotent — every turn re-reads live state, so re-running after a partial setup just resumes where you left off.
58
- - If you catch yourself about to wrap up without `complete`, call the tool again — it will name the real gap.
59
-
60
- ### Step actions (the expansion of each `nextStep`)
61
-
62
- #### `account` — authenticate
63
- The tool couldn't read your settings (not connected / 401). Tell the merchant:
64
-
65
- > "Your CLI isn't connected. Run `npx epicmerch-mcp setup` (or `npx epicmerch-mcp login` if MCP is already installed), then ask me again."
66
-
67
- STOP the loop. Resume (re-call the tool) once they confirm they're connected.
68
-
69
- #### `apiKey` — generate a storefront key
70
-
71
- > "You don't have an API key yet. I'll generate one called 'Storefront' — that's what the SDK uses to talk to your store."
72
-
73
- Call `merchant_generate_api_key({ name: 'Storefront' })`. Recap `✓ API key generated.` and re-loop.
74
-
75
- #### `payment` — configure a processor
76
- Ask **one** question:
77
-
78
- > "Where do most of your customers buy from?
79
- > 1) India (Razorpay — recommended)
80
- > 2) Outside India (Stripe)"
81
-
82
- - "India" / "1" → run `/epicmerch-payments` (it takes the Razorpay path)
83
- - "outside" / "global" / "2" → run `/epicmerch-payments` (it takes the Stripe path)
84
-
85
- `/epicmerch-payments` configures the chosen processor **and** the checkout type — the tool's `payment` step needs both before it counts as done. Re-loop; the tool confirms `payment.done`.
86
-
87
- #### `products` — fill the catalog
88
- The catalog is empty (the tool counts an empty catalog as not-done, so this step keeps surfacing until at least one product exists). Ask:
89
-
90
- > "Your catalog is empty. Want me to:
91
- > 1) Migrate your existing Shopify store
92
- > 2) Add your first product right now
93
- > 3) Add a quick sample product so we can finish, and you replace it later"
94
-
95
- - "Shopify" / "1" → run `/epicmerch-migrate`
96
- - "first product" / "2" → walk through it with `merchant_create_product`. **Do NOT just ask for name + price** — products that ship without variants and images break the storefront UX (empty size picker, broken image). Collect the FULL shape in ONE round of questions:
97
- - **Name + Category** (`name`, `type`)
98
- - **Description** (`description`)
99
- - **Pricing** — list price (`price`), plus on-sale fields if applicable (`salePrice`, `discountPercent`). *"If this product is on sale, tell me the sale price and discount %. Otherwise say 'no sale'."*
100
- - **Variants** (`variants`) — array of `{ variant: 'S', stock: 5 }` entries. *"What sizes / colors / options does this come in, and how many of each in stock? If just one option, say 'no variants'."* When passed, the server derives total stock from `sum(variant.stock)` and ignores the top-level `stock`.
101
- - **Images** (`images`) — array of gallery URLs. Optionally `image` for an explicit primary; if omitted the server uses `images[0]`.
102
- Pass everything to `merchant_create_product` in ONE call — never create-then-update.
103
- - "sample" / "3" → create one reasonable sample product (full shape, a couple of variants, a placeholder image) so the loop can progress. Tell the merchant it's a placeholder they can edit or delete later.
104
-
105
- There is no hard "skip" here: `complete` requires a non-empty catalog. If the merchant truly wants to stop, be honest that onboarding stays incomplete until a product exists.
106
-
107
- #### `storefront` — scaffold into the project
108
- `storefrontPresent` is false. First check the project: does `package.json` exist with React / Vite / Next in dependencies?
109
-
110
- - **Not a frontend project / no `package.json`**: tell the merchant *"No frontend project detected here. Run `/epicmerch` from inside a React/Vite/Next project to scaffold the storefront."* `storefrontPresent` stays false, so the loop keeps flagging `storefront` — be honest that `complete` needs the storefront scaffolded (they can finish from a project later; a chat-only setup won't reach `complete`).
111
- - **Project detected, no SDK file**: do the two-part sub-flow:
112
-
113
- **4a — Visual style (before scaffolding).** Ask ONE question so the components match their vibe:
114
-
115
- > "Quick — what's the vibe of your store?
116
- > 1) Editorial Funk (bold typography, big imagery, asymmetric layouts)
117
- > 2) Minimal Mono (clean, monochrome, lots of whitespace)
118
- > 3) Vibrant Pop (colourful, rounded corners, playful)
119
- > 4) Skip — functional unstyled scaffolds, I'll polish later"
120
-
121
- Remember the answer as `visualStyle`.
122
-
123
- **4b — Scaffold + style.** Run `/epicmerch-storefront`. After it finishes:
124
- - If `visualStyle` skip, do a quick styling pass: 5-10 well-chosen Tailwind utility classes per component (typography, spacing, colour) matching `visualStyle`. Don't overdo it.
125
- - Confirm `src/lib/epicmerch.js` contains the **session-restore block** (the `localStorage.getItem('customerInfo') setCustomerToken` snippet). Without it, OTP login appears to work but doesn't survive a page reload — the #1 silent storefront bug. The 1.3.4+ scaffold includes it by default; add it manually if missing.
126
- - Recap `✓ Storefront scaffolded + styled (<visualStyle>)` (or `unstyled`).
127
-
128
- **Optional add-on (not gated):** once the storefront exists, you may offer a 1-click Buy Now button *"Want a 1-click 'Buy Now' button? Razorpay's Magic Checkout lets customers buy without a cart screen."* → `/epicmerch-magic-checkout` (it verifies Razorpay first). This is optional and does NOT affect `complete`; the merchant can add it anytime by saying *"add a Buy Now button"*. Then re-loop.
129
-
130
- #### `verified` — the gate (the ONLY step that flips `complete`)
131
- This is the un-fakeable half of the harness. Run **`/epicmerch-verify` (Deep mode)** automatically:
132
-
133
- > "Last step a live end-to-end test to confirm everything actually works."
134
-
135
- It gets a customer session, creates a throwaway test product, adds it to a cart, creates a real order (exercising stock reservation + Razorpay order creation, no charge), verifies every response shape, then cleans up (cancels the order, deletes the product). This proves the WHOLE pipeline, not just read paths. It gets its customer token via the server's env-gated test phone (`store_auth_send_otp` + `store_auth_verify_otp` against `E2E_TEST_PHONE`), so it needs no SMS and no manual login.
136
-
137
- - **On pass:** `/epicmerch-verify` (Deep mode) calls **`merchant_mark_verified`**, which stamps `lastVerifiedAt` server-side. That is the ONLY thing that flips the tool's `verified` step to done. Re-loop — `merchant_onboarding_state` will now return `complete: true`.
138
- - **On any failure:** do NOT stamp verified. Route to `/epicmerch-debug` automatically its four-check playbook (OTP not persisting, products not loading, INSUFFICIENT_STOCK on checkout, cart 401) fixes it without further merchant intervention. Then re-run `/epicmerch-verify` (Deep mode).
139
- - **If the e2e token step fails** because the server lacks `E2E_TEST_PHONE`/`E2E_TEST_OTP` (you'll see a normal SMS channel instead of `e2e-test`, or a 401 on verify), fall back to `/epicmerch-verify` (read-path + payload checks, no token). **Important:** the fallback does NOT stamp `lastVerifiedAt`, so the `verified` step stays not-done and the tool will NOT report `complete`. Be honest: *"Ran the read-path + payload checks. To mark the store verified end-to-end (and finish onboarding), your EpicMerch operator needs to set `E2E_TEST_PHONE`/`E2E_TEST_OTP` on the server so the automated order test can run."*
140
-
141
- ### Wizard wrap-up
142
-
143
- Print this one-block recap **only when `merchant_onboarding_state` has returned `complete: true`** (i.e. the `verified` step is done — never before):
144
-
145
- ```
146
- ━━━ Your store is set up ━━━
147
- ✓ Account connected as <email>
148
- ✓ API key: <name>
149
- Payments: <Razorpay/Stripe>
150
- Catalog: <N> products
151
- Storefront: scaffolded into <project> (or "skipped — no project")
152
- Buy Now button: skipped (or ✓ if they added it)
153
- End-to-end test: product → cart → order → cleanup all passed
154
- (verified livelastVerifiedAt stamped server-side)
155
-
156
- Next: restart your dev server (`npm run dev`) to pick up `.env`. Your
157
- store works end-to-end. Ask me about analytics, notifications, or
158
- abandoned-cart messages whenever you're ready.
159
- ```
160
-
161
- ---
162
-
163
- ## Step B — Intent mode (router)
164
-
165
- The merchant said something specific. Match against the table below, pick the closest, hand off. If multiple match, ask ONE disambiguating question.
166
-
167
- | The merchant says … | Route to |
168
- |---|---|
169
- | "set up EpicMerch", "scaffold a storefront", "build me a store", "integrate ecommerce" (and the project has no `src/lib/epicmerch.js`) | `/epicmerch-storefront` |
170
- | "add login / OTP / sign in" | `/epicmerch-storefront` (auth slice) |
171
- | "add a product catalog", "show products on my site", "product list with search" | `/epicmerch-storefront` (products slice) |
172
- | "add a cart", "build checkout", "order history page", "multi-product checkout" | `/epicmerch-storefront` (orders slice) |
173
- | "add Buy Now button", "1-click checkout", "impulse buy", "Razorpay Magic Checkout" | `/epicmerch-magic-checkout` |
174
- | "set up payments", "accept cards", "configure checkout", "set up Razorpay", "set up Stripe", "India / international payments" | `/epicmerch-payments` |
175
- | "connect my store to Claude / Cursor / Codex", "log in to EpicMerch", "install the MCP" | `/epicmerch-setup` |
176
- | "show me my store / products / orders / sales", "manage my store from chat" | `/epicmerch-merchant` |
177
- | "migrate from Shopify", "import my Shopify catalog" | `/epicmerch-migrate` |
178
- | "my login isn't sticking", "products aren't loading", "checkout fails", "insufficient stock undefined", "something's broken" | `/epicmerch-debug` |
179
- | "test everything", "does checkout actually work", "full end-to-end test", "run a real order test" | `/epicmerch-verify` (Deep mode) |
180
-
181
- ### Hand-off (or inline)
182
-
183
- **In Claude Code / Claude Desktop / Cursor / VS Code Claude extension:**
184
-
185
- Tell the merchant which slash command you're invoking, then proceed:
186
-
187
- > "Sounds like you want X — running `/epicmerch-Y` now."
188
-
189
- The slash-command file is loaded from `~/.claude/commands/epicmerch-Y.md`; follow its steps.
190
-
191
- **In Codex CLI (or any client without slash commands):**
192
-
193
- Fetch the matching skill from the API and follow it:
194
-
195
- ```
196
- Fetch https://api.epicmerch.in/api/skills/epicmerch-<slug>.md and execute every step against the current project, using the MCP tools available in this session.
197
- ```
198
-
199
- If the fetch fails, fall back to the high-level intent using the MCP tools you have (e.g. for Razorpay: `merchant_configure_razorpay({keyId, keySecret})` + `merchant_set_checkout_type({type:'epicmerch'})`).
200
-
201
- ### After the sub-flow
202
-
203
- Briefly recap and ask if anything else is needed. One short sentence, not three follow-ups.
204
-
205
- **If the sub-flow built or changed the storefront** (`/epicmerch-storefront`, `/epicmerch-magic-checkout`, or a custom build), run the **Definition of Done** gate above before recapping — `/epicmerch-verify` (Deep mode). Don't say "done" on anything that touched checkout without testing it live. (Pure config sub-flows like payment setup don't need the storefront verify, but a quick `merchant_diagnose` confirms they took.)
206
-
207
- Examples:
208
-
209
- > "Razorpay is now live on your store. Want a Buy Now button too?" *(routes to `/epicmerch-magic-checkout`)*
210
-
211
- > "Storefront scaffolded and end-to-end tested (product → cart → order all pass). Restart your dev server (`npm run dev`) to load `.env`. Need anything else — payments, Shopify migration, analytics?"
212
-
213
- ---
214
-
215
- ## Step C — Tool fallback (no skill match)
216
-
217
- If the intent doesn't match any row in the table AND it's not a "start me up" wizard cue, fall through to MCP tools directly:
218
-
219
- - "How many products do I have?" → `merchant_list_products`
220
- - "What's missing from my setup?" `merchant_diagnose`
221
- - "Generate a new API key called X" → `merchant_generate_api_key`
222
- - "Send a notification to all customers" → `merchant_send_notification`
223
-
224
- Only escalate to a sub-skill if a multi-step recipe is needed (scaffolding files, configuring payment processors, multi-stage migrations).
225
-
226
- ---
227
-
228
- ## Style
229
-
230
- - **One question at a time.** Never stack three.
231
- - **Read intent from context.** React + no `src/lib/epicmerch.js` → don't ask, just say "I'll scaffold the storefront" and start.
232
- - **Confirm destructive operations.** Anything that deletes data or sends bulk notifications needs a yes-or-no first.
233
- - **Skill files are guidance, not gospel.** If a step fails (server error, etc.), surface the actual error and ask the merchant rather than blindly retrying.
234
- - **Lead with numbers.** "You have 42 products and ₹5,231 revenue this week" beats "let me tell you about your store."
235
-
236
- ## What this skill does NOT do
237
-
238
- - Does not collect email/password in chat. Auth always goes through the browser OAuth flow via `/epicmerch-setup` (which runs `npx epicmerch-mcp login`).
239
- - Does not bypass per-skill safety checks. E.g. `/epicmerch-magic-checkout` won't scaffold a Buy Now button before verifying Razorpay is configured — the umbrella respects that and the wizard's step 2 ensures payments are configured before step 5 even asks.
1
+ ---
2
+ name: epicmerch
3
+ description: EpicMerch umbrella — guided 6-step onboarding wizard for new merchants, driven by a deterministic state machine (merchant_onboarding_state) that re-derives progress from live state and CANNOT report "complete" until a real end-to-end order test (/epicmerch-verify Deep mode) has stamped verification server-side. Also an intent router for merchants who already know what they want (scaffold a storefront, set up payments, add a Buy Now button, manage a store, migrate from Shopify, verify the store). Verification is the non-negotiable, structurally-enforced definition of done.
4
+ ---
5
+
6
+ # EpicMerch — the umbrella
7
+
8
+ The **one entry point** for everything EpicMerch. Merchants don't need to memorise 10+ slash commands; they say what they want (or say nothing) and you figure out the right flow.
9
+
10
+ ## Mode detection
11
+
12
+ Two modes. Pick one based on what the merchant said:
13
+
14
+ | What the merchant said when invoking `/epicmerch` | Mode |
15
+ |---|---|
16
+ | Nothing specific. Just `/epicmerch` with no extra words. | **Wizard mode** (Step A below) |
17
+ | "help me start", "I'm new", "set up everything", "what's next", "where do I begin" | **Wizard mode** |
18
+ | "set up Razorpay", "add a Buy Now button", "migrate Shopify", "scaffold a storefront", "show me my products" — anything with a clear specific intent | **Intent mode** (Step B below) |
19
+
20
+ If ambiguous, ask ONE disambiguating question: *"Want me to walk you through a full setup wizard, or is there one specific thing you want to do?"*
21
+
22
+ ---
23
+
24
+ ## DEFINITION OF DONE — read this before you finish (applies to BOTH modes)
25
+
26
+ **`/epicmerch` is not complete until a live end-to-end test has run and passed.**
27
+
28
+ This holds no matter how the build happened — the wizard's scaffold step, an intent-mode `/epicmerch-storefront`, OR a fully custom build you designed through brainstorming (e.g. a themed multi-page store). However you got here, the LAST thing you do before telling the merchant "your store is set up" is run **`/epicmerch-verify` (Deep mode)** — the full product→cart→order pipeline; it automatically falls back to Smoke mode if the server's test-login isn't configured.
29
+
30
+ Common trap: a design-heavy request ("build me a saree store") pulls you into scope/theme/spec brainstorming and a custom build, and the flow ends at "looks good!" without ever testing against the live API. **Don't let that happen.** After the storefront files are written and the `.env` has a real API key, run the e2e/verify gate. A store that was never tested against the live API is not done — that's exactly how the cart-shape / productId / out-of-stock bugs reached real merchants.
31
+
32
+ If you realize you're wrapping up `/epicmerch` and haven't run the test → run it now.
33
+
34
+ In **Wizard mode** this is enforced for you: `merchant_onboarding_state` will not return `complete` until `/epicmerch-verify` (Deep mode) has stamped verification server-side, so you structurally can't wrap up early. In **Intent mode** and custom/brainstormed builds there's no such gate — that's where this reminder matters most.
35
+
36
+ ---
37
+
38
+ ## Step A — Wizard mode (the harness loop)
39
+
40
+ Wizard mode is a **deterministic loop driven by the `merchant_onboarding_state` tool** — not prose you interpret. The tool re-derives what's done, what's next, and whether you're complete from **live server state on every call**, and it enforces the gate: you **cannot** reach `complete` until a real `/epicmerch-verify` (Deep mode) run has stamped verification server-side. That is what makes `/epicmerch` un-breakable — the control flow lives in code + a hard gate, so "done but never verified" is structurally unreachable. You bring the creative work (the conversation, the product shape, the styling); the tool owns order, idempotency, and the gate.
41
+
42
+ Open with one sentence:
43
+
44
+ > "I'll set up your EpicMerch store. I'll check what's already done, do the next thing, and finish with a live end-to-end test — that test is the only thing that marks the setup complete."
45
+
46
+ ### The loop
47
+
48
+ Repeat until the tool returns `complete: true`:
49
+
50
+ 1. Check the local project: does `src/lib/epicmerch.js` exist? (`true`/`false` → `storefrontPresent`).
51
+ 2. Call `merchant_onboarding_state({ storefrontPresent })`.
52
+ 3. If `complete === true` → print the **wrap-up recap** (below) and STOP. You're done.
53
+ 4. Else → read `nextStep` and perform **that one step's action** (see *Step actions* below). The `steps[nextStep].action` string is a short reminder; the full expansion is below. Do exactly ONE step, recap it in one line (`✓ <what happened>`), then go back to 1.
54
+
55
+ **Rules that keep the harness honest:**
56
+ - Never skip ahead, never decide completion yourself, never print "your store is set up" before the tool returns `complete: true`.
57
+ - The loop is inherently idempotent — every turn re-reads live state, so re-running after a partial setup just resumes where you left off.
58
+ - If you catch yourself about to wrap up without `complete`, call the tool again — it will name the real gap.
59
+
60
+ ### Step actions (the expansion of each `nextStep`)
61
+
62
+ #### `account` — authenticate
63
+ The tool couldn't read your settings (not connected / 401). Tell the merchant:
64
+
65
+ > "Your CLI isn't connected. Run `npx epicmerch-mcp setup` (or `npx epicmerch-mcp login` if MCP is already installed), then ask me again."
66
+
67
+ STOP the loop. Resume (re-call the tool) once they confirm they're connected.
68
+
69
+ #### `apiKey` — generate a storefront key
70
+
71
+ > "You don't have an API key yet. I'll generate one called 'Storefront' — that's what the SDK uses to talk to your store."
72
+
73
+ Call `merchant_generate_api_key({ name: 'Storefront' })`. Recap `✓ API key generated.` and re-loop.
74
+
75
+ #### `payment` — configure a processor
76
+ Ask **one** question:
77
+
78
+ > "Where do most of your customers buy from?
79
+ > 1) India (Razorpay — recommended)
80
+ > 2) Outside India (Stripe)"
81
+
82
+ - "India" / "1" → run `/epicmerch-payments` (it takes the Razorpay path)
83
+ - "outside" / "global" / "2" → run `/epicmerch-payments` (it takes the Stripe path)
84
+
85
+ `/epicmerch-payments` configures the chosen processor **and** the checkout type — the tool's `payment` step needs both before it counts as done. Re-loop; the tool confirms `payment.done`.
86
+
87
+ #### `products` — fill the catalog
88
+ The catalog is empty (the tool counts an empty catalog as not-done, so this step keeps surfacing until at least one product exists). Ask:
89
+
90
+ > "Your catalog is empty. Want me to:
91
+ > 1) Migrate your existing Shopify store
92
+ > 2) Add your first product right now
93
+ > 3) Add a quick sample product so we can finish, and you replace it later"
94
+
95
+ - "Shopify" / "1" → run `/epicmerch-migrate`
96
+ - "first product" / "2" → walk through it with `merchant_create_product`. **Do NOT just ask for name + price** — products that ship without variants and images break the storefront UX (empty size picker, broken image). Collect the FULL shape in ONE round of questions:
97
+ - **Name + Category** (`name`, `type`)
98
+ - **Description** (`description`)
99
+ - **Pricing** — list price (`price`), plus on-sale fields if applicable (`salePrice`, `discountPercent`). *"If this product is on sale, tell me the sale price and discount %. Otherwise say 'no sale'."*
100
+ - **Variants** (`variants`) — array of `{ variant: 'S', stock: 5 }` entries. *"What sizes / colors / options does this come in, and how many of each in stock? If just one option, say 'no variants'."* When passed, the server derives total stock from `sum(variant.stock)` and ignores the top-level `stock`.
101
+ - **Images** (`images`) — array of gallery URLs. Optionally `image` for an explicit primary; if omitted the server uses `images[0]`.
102
+ Pass everything to `merchant_create_product` in ONE call — never create-then-update.
103
+ - "sample" / "3" → create one reasonable sample product (full shape, a couple of variants, a placeholder image) so the loop can progress. Tell the merchant it's a placeholder they can edit or delete later.
104
+
105
+ There is no hard "skip" here: `complete` requires a non-empty catalog. If the merchant truly wants to stop, be honest that onboarding stays incomplete until a product exists.
106
+
107
+ #### `storefront` — scaffold into the project
108
+ `storefrontPresent` is false. First check the project: does `package.json` exist with React / Vite / Next in dependencies?
109
+
110
+ - **Not a frontend project / no `package.json`**: tell the merchant *"No frontend project detected here. Run `/epicmerch` from inside a React/Vite/Next project to scaffold the storefront."* `storefrontPresent` stays false, so the loop keeps flagging `storefront` — be honest that `complete` needs the storefront scaffolded (they can finish from a project later; a chat-only setup won't reach `complete`).
111
+ - **Project detected, no SDK file**: do the two-part sub-flow:
112
+
113
+ **4a — Visual style (before scaffolding).** Ask ONE question so the components match their vibe:
114
+
115
+ > "Quick — what's the vibe of your store?
116
+ > 1) Editorial Funk (bold typography, big imagery, asymmetric layouts)
117
+ > 2) Minimal Mono (clean, monochrome, lots of whitespace)
118
+ > 3) Vibrant Pop (colourful, rounded corners, playful)
119
+ > 4) Skip — functional unstyled scaffolds, I'll polish later"
120
+
121
+ Remember the answer as `visualStyle`.
122
+
123
+ **4b — Scaffold + style.** Run `/epicmerch-storefront`, passing along the chosen
124
+ `visualStyle`. It runs the tiered flow: it lays the standard skeleton from the
125
+ `store_scaffold_full` tool (instant no hand-writing), applies the matching Tailwind
126
+ theme pass (or leaves it unstyled on "skip"), confirms the session-restore block, and
127
+ ends on the `/epicmerch-verify` gate. You don't style files here — the leaf skill owns
128
+ the theme pass. Recap what it reports (`✓ Storefront scaffolded + styled (<visualStyle>)`
129
+ or `unstyled`).
130
+
131
+ **Optional add-on (not gated):** once the storefront exists, you may offer a 1-click Buy Now button — *"Want a 1-click 'Buy Now' button? Razorpay's Magic Checkout lets customers buy without a cart screen."* `/epicmerch-magic-checkout` (it verifies Razorpay first). This is optional and does NOT affect `complete`; the merchant can add it anytime by saying *"add a Buy Now button"*. Then re-loop.
132
+
133
+ #### `verified`the gate (the ONLY step that flips `complete`)
134
+ This is the un-fakeable half of the harness. Run **`/epicmerch-verify` (Deep mode)** automatically:
135
+
136
+ > "Last step — a live end-to-end test to confirm everything actually works."
137
+
138
+ It gets a customer session, creates a throwaway test product, adds it to a cart, creates a real order (exercising stock reservation + Razorpay order creation, no charge), verifies every response shape, then cleans up (cancels the order, deletes the product). This proves the WHOLE pipeline, not just read paths. It gets its customer token via the server's env-gated test phone (`store_auth_send_otp` + `store_auth_verify_otp` against `E2E_TEST_PHONE`), so it needs no SMS and no manual login.
139
+
140
+ - **On pass:** `/epicmerch-verify` (Deep mode) calls **`merchant_mark_verified`**, which stamps `lastVerifiedAt` server-side. That is the ONLY thing that flips the tool's `verified` step to done. Re-loop — `merchant_onboarding_state` will now return `complete: true`.
141
+ - **On any failure:** do NOT stamp verified. Route to `/epicmerch-debug` automatically — its four-check playbook (OTP not persisting, products not loading, INSUFFICIENT_STOCK on checkout, cart 401) fixes it without further merchant intervention. Then re-run `/epicmerch-verify` (Deep mode).
142
+ - **If the e2e token step fails** because the server lacks `E2E_TEST_PHONE`/`E2E_TEST_OTP` (you'll see a normal SMS channel instead of `e2e-test`, or a 401 on verify), fall back to `/epicmerch-verify` (read-path + payload checks, no token). **Important:** the fallback does NOT stamp `lastVerifiedAt`, so the `verified` step stays not-done and the tool will NOT report `complete`. Be honest: *"Ran the read-path + payload checks. To mark the store verified end-to-end (and finish onboarding), your EpicMerch operator needs to set `E2E_TEST_PHONE`/`E2E_TEST_OTP` on the server so the automated order test can run."*
143
+
144
+ ### Wizard wrap-up
145
+
146
+ Print this one-block recap **only when `merchant_onboarding_state` has returned `complete: true`** (i.e. the `verified` step is done never before):
147
+
148
+ ```
149
+ ━━━ Your store is set up ━━━
150
+ Account connected as <email>
151
+ API key: <name>
152
+ Payments: <Razorpay/Stripe>
153
+ Catalog: <N> products
154
+ ✓ Storefront: scaffolded into <project> (or "skippedno project")
155
+ ✗ Buy Now button: skipped (or ✓ if they added it)
156
+ End-to-end test: product cart order cleanup all passed
157
+ (verified live lastVerifiedAt stamped server-side)
158
+
159
+ Next: restart your dev server (`npm run dev`) to pick up `.env`. Your
160
+ store works end-to-end. Ask me about analytics, notifications, or
161
+ abandoned-cart messages whenever you're ready.
162
+ ```
163
+
164
+ ---
165
+
166
+ ## Step B — Intent mode (router)
167
+
168
+ The merchant said something specific. Match against the table below, pick the closest, hand off. If multiple match, ask ONE disambiguating question.
169
+
170
+ | The merchant says | Route to |
171
+ |---|---|
172
+ | "set up EpicMerch", "scaffold a storefront", "build me a store", "integrate ecommerce" (and the project has no `src/lib/epicmerch.js`) | `/epicmerch-storefront` |
173
+ | "add login / OTP / sign in" | `/epicmerch-storefront` (auth slice) |
174
+ | "add a product catalog", "show products on my site", "product list with search" | `/epicmerch-storefront` (products slice) |
175
+ | "add a cart", "build checkout", "order history page", "multi-product checkout" | `/epicmerch-storefront` (orders slice) |
176
+ | "add Buy Now button", "1-click checkout", "impulse buy", "Razorpay Magic Checkout" | `/epicmerch-magic-checkout` |
177
+ | "set up payments", "accept cards", "configure checkout", "set up Razorpay", "set up Stripe", "India / international payments" | `/epicmerch-payments` |
178
+ | "connect my store to Claude / Cursor / Codex", "log in to EpicMerch", "install the MCP" | `/epicmerch-setup` |
179
+ | "show me my store / products / orders / sales", "manage my store from chat" | `/epicmerch-merchant` |
180
+ | "migrate from Shopify", "import my Shopify catalog" | `/epicmerch-migrate` |
181
+ | "my login isn't sticking", "products aren't loading", "checkout fails", "insufficient stock undefined", "something's broken" | `/epicmerch-debug` |
182
+ | "test everything", "does checkout actually work", "full end-to-end test", "run a real order test" | `/epicmerch-verify` (Deep mode) |
183
+
184
+ ### Hand-off (or inline)
185
+
186
+ **In Claude Code / Claude Desktop / Cursor / VS Code Claude extension:**
187
+
188
+ Tell the merchant which slash command you're invoking, then proceed:
189
+
190
+ > "Sounds like you want X — running `/epicmerch-Y` now."
191
+
192
+ The slash-command file is loaded from `~/.claude/commands/epicmerch-Y.md`; follow its steps.
193
+
194
+ **In Codex CLI (or any client without slash commands):**
195
+
196
+ Fetch the matching skill from the API and follow it:
197
+
198
+ ```
199
+ Fetch https://api.epicmerch.in/api/skills/epicmerch-<slug>.md and execute every step against the current project, using the MCP tools available in this session.
200
+ ```
201
+
202
+ If the fetch fails, fall back to the high-level intent using the MCP tools you have (e.g. for Razorpay: `merchant_configure_razorpay({keyId, keySecret})` + `merchant_set_checkout_type({type:'epicmerch'})`).
203
+
204
+ ### After the sub-flow
205
+
206
+ Briefly recap and ask if anything else is needed. One short sentence, not three follow-ups.
207
+
208
+ **If the sub-flow built or changed the storefront** (`/epicmerch-storefront`, `/epicmerch-magic-checkout`, or a custom build), run the **Definition of Done** gate above before recapping — `/epicmerch-verify` (Deep mode). Don't say "done" on anything that touched checkout without testing it live. (Pure config sub-flows like payment setup don't need the storefront verify, but a quick `merchant_diagnose` confirms they took.)
209
+
210
+ Examples:
211
+
212
+ > "Razorpay is now live on your store. Want a Buy Now button too?" *(routes to `/epicmerch-magic-checkout`)*
213
+
214
+ > "Storefront scaffolded and end-to-end tested (product → cart → order all pass). Restart your dev server (`npm run dev`) to load `.env`. Need anything else — payments, Shopify migration, analytics?"
215
+
216
+ ---
217
+
218
+ ## Step C — Tool fallback (no skill match)
219
+
220
+ If the intent doesn't match any row in the table AND it's not a "start me up" wizard cue, fall through to MCP tools directly:
221
+
222
+ - "How many products do I have?" → `merchant_list_products`
223
+ - "What's missing from my setup?" → `merchant_diagnose`
224
+ - "Generate a new API key called X" `merchant_generate_api_key`
225
+ - "Send a notification to all customers" → `merchant_send_notification`
226
+
227
+ Only escalate to a sub-skill if a multi-step recipe is needed (scaffolding files, configuring payment processors, multi-stage migrations).
228
+
229
+ ---
230
+
231
+ ## Style
232
+
233
+ - **One question at a time.** Never stack three.
234
+ - **Read intent from context.** React + no `src/lib/epicmerch.js` don't ask, just say "I'll scaffold the storefront" and start.
235
+ - **Confirm destructive operations.** Anything that deletes data or sends bulk notifications needs a yes-or-no first.
236
+ - **Skill files are guidance, not gospel.** If a step fails (server error, etc.), surface the actual error and ask the merchant rather than blindly retrying.
237
+ - **Lead with numbers.** "You have 42 products and ₹5,231 revenue this week" beats "let me tell you about your store."
238
+
239
+ ## What this skill does NOT do
240
+
241
+ - Does not collect email/password in chat. Auth always goes through the browser OAuth flow via `/epicmerch-setup` (which runs `npx epicmerch-mcp login`).
242
+ - Does not bypass per-skill safety checks. E.g. `/epicmerch-magic-checkout` won't scaffold a Buy Now button before verifying Razorpay is configured — the umbrella respects that and the wizard's step 2 ensures payments are configured before step 5 even asks.