@fiodos/cli 0.1.31 → 0.1.33

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": "@fiodos/cli",
3
- "version": "0.1.31",
3
+ "version": "0.1.33",
4
4
  "description": "Fiodos CLI — analyzes your app's source code and generates the in-app voice-agent manifest, then wires the orb. Powers `npx @fiodos/cli analyze`.",
5
5
  "type": "commonjs",
6
6
  "bin": {
package/src/index.js CHANGED
@@ -102,6 +102,7 @@ const {
102
102
  const { wireReactNativeOrb, reportReactNativeWire, connectRnSession } = require('./wireReactNative');
103
103
  const {
104
104
  isShopifyThemeRoot, fetchStorefrontSnapshot, wireShopifyTheme, reportShopifyWire,
105
+ normalizeStoreDomain,
105
106
  } = require('./shopifyTheme');
106
107
  const { wireFlutterOrb, writeFlutterEnv, reportFlutterWire } = require('./wireFlutter');
107
108
  const { wireHandlers, reportHandlerResult } = require('./wireHandlers');
@@ -404,13 +405,22 @@ async function main() {
404
405
  // so the analysis personalizes routes/bubbles/examples to the REAL catalog
405
406
  // — and so evidence verification has concrete files to point at. Fail-open:
406
407
  // an unreachable store still gets the universal surface file.
408
+ // Hoisted out of the `if` so it also reaches analysisMeta below: persisting
409
+ // the store domain in the published meta (not just using it locally for the
410
+ // snapshot) is what lets the DASHBOARD reconstruct the exact `--platform
411
+ // shopify --store <domain>` re-analysis command from the BACKEND record —
412
+ // no dependency on this browser's localStorage remembering how the project
413
+ // was first set up. Without it, a project analyzed before this field existed
414
+ // (or from a different browser/machine) shows the generic web command,
415
+ // silently missing the flags a Shopify re-analysis needs.
416
+ let shopifyStoreDomain = '';
407
417
  if (platform === 'shopify') {
408
- const storeDomain = arg('--store', process.env.FYODOS_SHOPIFY_STORE || '');
409
- if (!storeDomain) {
418
+ shopifyStoreDomain = normalizeStoreDomain(arg('--store', process.env.FYODOS_SHOPIFY_STORE || ''));
419
+ if (!shopifyStoreDomain) {
410
420
  logUser('Tip: add --store your-store.com so the analysis reads your live catalog (collections & products).');
411
421
  }
412
422
  const snapshot = await withSpinner('Reading your store\u2019s public catalog', () =>
413
- fetchStorefrontSnapshot(storeDomain, { log: logDev }),
423
+ fetchStorefrontSnapshot(shopifyStoreDomain, { log: logDev }),
414
424
  );
415
425
  included.push(...snapshot.files);
416
426
  }
@@ -466,6 +476,13 @@ async function main() {
466
476
  ({ manifest, provenance } = routesOnlyManifest(appMeta, routes));
467
477
  logDev('[no-llm] static-only manifest (routes only, no actions)');
468
478
  logUser('Static analysis complete');
479
+ // Still record platform (+ store, when given) so the dashboard's
480
+ // re-analysis command stays correct even for a free/no-key smoke pass.
481
+ analysisMeta = {
482
+ ...analysisMeta,
483
+ platform,
484
+ ...(platform === 'shopify' && shopifyStoreDomain ? { store: shopifyStoreDomain } : {}),
485
+ };
469
486
  } else {
470
487
  // Hybrid AI-key model. DEFAULT: hosted analysis via the Fiodos backend
471
488
  // proxy (Fiodos's AI key, no key needed by the developer). With
@@ -552,6 +569,7 @@ async function main() {
552
569
  costUSD: Number(costUSD.toFixed(4)),
553
570
  droppedActions,
554
571
  droppedRoutes,
572
+ ...(platform === 'shopify' && shopifyStoreDomain ? { store: shopifyStoreDomain } : {}),
555
573
  };
556
574
  }
557
575
 
@@ -108,6 +108,10 @@ function universalSurfaceDoc(storeDomain) {
108
108
  universalRoutes: ['/', '/collections/all', '/search', '/cart'],
109
109
  universalActions: [
110
110
  { intent: 'search_products', handler: 'shopifySearchProducts', api: '{baseUrl}/search/suggest.json' },
111
+ // The search→cart bridge: suggest.json returns product ids/handles but
112
+ // NEVER variant ids, and cart/add.js only accepts variant ids. This
113
+ // endpoint (universal on every store) turns a handle into its variants.
114
+ { intent: 'get_product_details', handler: 'shopifyGetProductDetails', api: '{baseUrl}/products/{handle}.js' },
111
115
  { intent: 'add_to_cart', handler: 'shopifyAddToCart', api: '{baseUrl}/cart/add.js' },
112
116
  { intent: 'get_cart', handler: 'shopifyGetCart', api: '{baseUrl}/cart.js' },
113
117
  { intent: 'update_cart_item', handler: 'shopifyUpdateCartItem', api: '{baseUrl}/cart/change.js' },
@@ -194,11 +198,41 @@ async function fetchStorefrontSnapshot(storeRaw, { log = () => {} } = {}) {
194
198
  return { files, store, collections, products, warnings };
195
199
  }
196
200
 
201
+ /**
202
+ * The Liquid-rendered SCREEN CONTEXT lines shared by the CLI and dashboard
203
+ * snippets. One branch per storefront template the agent can act on, each
204
+ * exposing the visible items WITH their add-to-cart variantIds.
205
+ *
206
+ * Why every branch matters: with product-only context (the original version)
207
+ * the agent was BLIND on collection/search/cart pages — "add it to the cart"
208
+ * said next to a perfectly visible product degenerated into a text search
209
+ * with the user's phrasing, zero results, and an apology. The agent must be
210
+ * able to act on whatever the customer is LOOKING AT, on every page type.
211
+ */
212
+ function shopifyScreenContextLiquidLines() {
213
+ return [
214
+ " {% if template contains 'product' and product %}",
215
+ ' {% capture fyodos_screen_text %}The customer is viewing the product page of: {{ product.title }} (price {{ product.price | money | strip_html }}). Add-to-cart variantId: {{ product.selected_or_first_available_variant.id }}.{% endcapture %}',
216
+ " window.__fyodosScreen = { kind: 'product', text: {{ fyodos_screen_text | json }} };",
217
+ " {% elsif template contains 'collection' and collection %}",
218
+ ' {% capture fyodos_screen_text %}The customer is browsing the collection "{{ collection.title }}". Products visible on screen (title — price — add-to-cart variantId): {% for fyodos_p in collection.products limit: 16 %}{{ fyodos_p.title }} — {{ fyodos_p.price | money | strip_html }} — variantId {{ fyodos_p.selected_or_first_available_variant.id }}; {% endfor %}To add one to the cart, call add_to_cart with its variantId from this list.{% endcapture %}',
219
+ " window.__fyodosScreen = { kind: 'collection', text: {{ fyodos_screen_text | json }} };",
220
+ " {% elsif template contains 'search' and search.performed %}",
221
+ ' {% capture fyodos_screen_text %}The customer is viewing search results for "{{ search.terms }}". Products visible on screen (title — price — add-to-cart variantId): {% for fyodos_r in search.results limit: 12 %}{% if fyodos_r.object_type == \'product\' %}{{ fyodos_r.title }} — {{ fyodos_r.price | money | strip_html }} — variantId {{ fyodos_r.selected_or_first_available_variant.id }}; {% endif %}{% endfor %}To add one to the cart, call add_to_cart with its variantId from this list.{% endcapture %}',
222
+ " window.__fyodosScreen = { kind: 'search', text: {{ fyodos_screen_text | json }} };",
223
+ " {% elsif template contains 'cart' %}",
224
+ ' {% capture fyodos_screen_text %}The customer is viewing their cart: {{ cart.item_count }} item(s), total {{ cart.total_price | money | strip_html }}. Lines (title × quantity — variantId): {% for fyodos_i in cart.items %}{{ fyodos_i.product.title }} × {{ fyodos_i.quantity }} — variantId {{ fyodos_i.variant_id }}; {% endfor %}Use update_cart_item with a variantId from this list to change quantities.{% endcapture %}',
225
+ " window.__fyodosScreen = { kind: 'cart', text: {{ fyodos_screen_text | json }} };",
226
+ ' {% endif %}',
227
+ ];
228
+ }
229
+
197
230
  /**
198
231
  * The theme.liquid block: loads the embed, fetches the published manifest and
199
232
  * mounts the orb with API-wired registries. Mirrors the dashboard's
200
- * buildShopifySnippet, plus a Liquid-rendered PRODUCT screen context so the
201
- * agent knows the visible variant id (what add_to_cart needs by voice).
233
+ * buildShopifySnippet, plus a Liquid-rendered screen context (product,
234
+ * collection, search and cart pages) so the agent knows the visible items and
235
+ * their variant ids (what add_to_cart / update_cart_item need by voice).
202
236
  */
203
237
  function buildShopifySnippet({ apiKey, apiUrl }) {
204
238
  const key = apiKey || '<your-api-key>';
@@ -207,10 +241,7 @@ function buildShopifySnippet({ apiKey, apiUrl }) {
207
241
  ORB_START,
208
242
  `<script src="${FIODOS_EMBED_CDN_URL}" defer></script>`,
209
243
  '<script>',
210
- " {% if template contains 'product' and product %}",
211
- ' {% capture fyodos_screen_text %}The customer is viewing the product page of: {{ product.title }} (price {{ product.price | money | strip_html }}). Add-to-cart variantId: {{ product.selected_or_first_available_variant.id }}.{% endcapture %}',
212
- " window.__fyodosScreen = { kind: 'product', text: {{ fyodos_screen_text | json }} };",
213
- ' {% endif %}',
244
+ ...shopifyScreenContextLiquidLines(),
214
245
  " window.addEventListener('DOMContentLoaded', function () {",
215
246
  ` var FYODOS_API_KEY = '${key}';`,
216
247
  ` var FYODOS_API_URL = '${url}';`,
@@ -330,6 +361,7 @@ module.exports = {
330
361
  isShopifyThemeRoot,
331
362
  readThemeMeta,
332
363
  fetchStorefrontSnapshot,
364
+ shopifyScreenContextLiquidLines,
333
365
  buildShopifySnippet,
334
366
  wireShopifyTheme,
335
367
  reportShopifyWire,