Skip to content

feat(shopify-nav): AI add-product assistant, store scan & 10-language i18n#217

Merged
izzywdev merged 10 commits into
masterfrom
claude/shopify-navigator-product-upgrade-mwql3l
Jul 12, 2026
Merged

feat(shopify-nav): AI add-product assistant, store scan & 10-language i18n#217
izzywdev merged 10 commits into
masterfrom
claude/shopify-navigator-product-upgrade-mwql3l

Conversation

@izzywdev

Copy link
Copy Markdown
Owner

📋 Description

Upgrades the standalone Shopify Navigator PWA (single-file index.html, vanilla JS, no build) under a new self-contained shopify-nav/ directory. Previously the app only guided users through the Shopify Admin UI. This PR adds an interactive AI add-product assistant, a store profile & scan feature, and full 10-language localization. This is an independent unit — not wired into FuzeFront's Module Federation shell.

🔄 Type of Change

  • ✨ New feature (non-breaking change which adds functionality)
  • 📝 Documentation update

🧪 Testing

  • Manual testing
  • E2E (headless Chromium via Playwright, mocked Anthropic API)

Test Instructions

cd shopify-nav
python -m http.server 8080   # open http://localhost:8080, enter an Anthropic API key

Verified with automated browser tests (mocked API):

  • App loads with no console errors; first-run settings sheet opens.
  • Language switch across 10 languages flips dir (RTL for he/ar, LTR otherwise) and re-renders header/placeholder/examples; all 10 tables carry an identical 90-key set.
  • Normal guide search renders numbered steps + "stuck on this step" buttons.
  • Intent detection: an "add a product for me" query opens the wizard instead of a guide.
  • Full wizard: 4 tailored ideas (2 classic / 1 differentiating / 1 TikTok-trend badges) → find candidate (image + price) → No → feedback → new candidate loop → supplier pick → ready-to-copy English prompt + product image links + copy button.
  • Store scan handles the server-tool pause_turn → end_turn loop and persists the profile.

Test Configuration: Chromium (pre-installed), served over http.server, Anthropic API mocked.

🔧 Implementation Details

Changes Made

  • Frontend Changes (shopify-nav/index.html):
    • i18n layerI18N tables for he, en-US, ru, zh, fr, es, de, ar, en-GB, pt; getLang()/t()/applyLangDir(); browser-language fallback on first run; language picker in Settings; RTL/LTR handling. System prompts inject the selected output language dynamically.
    • Store profile + scan — Settings field for the store URL + "Scan now" button and last-scan status; scanStore() builds a structured profile (niche/style/colors/tone/categories) via Claude web_fetch/web_search, saved to localStorage; background re-scan every 10 min while the app is open.
    • Intent detectionSYSTEM_PROMPT now returns actionRequested; an add-product request routes to the wizard.
    • Product wizard — 4 ideas (2 classic, 1 differentiating, 1 TikTok trend) → free-text / link / image input → web-tool product match with yes/no + feedback loop → supplier app choice → a detailed English hand-off prompt (exact-product match, image-edit rules that never alter the product, ≤2-line title, ~10-line description + technical info) for a browser/computer-control AI (e.g. Claude Code / Manus), plus product image links.
    • Tool loopcallClaudeSmart() runs Sonnet 5 / Opus 4.8 with web_search_20260209/web_fetch_20260209 and a pause_turn continuation loop; smart flows auto-upgrade off Haiku (too weak for tool use).
    • Decorative header circles set pointer-events: none.
  • Backend Changes: none.
  • SDK Changes: none.

Code Quality

  • Self-review of code completed
  • No stray debugging statements left in code

Documentation

  • README updated (shopify-nav/README.md) — new features, tools-capable-model requirement, best-effort live data, and background-scan PWA limitation.

🚨 Breaking Changes

None. New, self-contained directory; existing guide behavior is preserved.

📸 Screenshots (if applicable)

N/A — verified via automated headless-browser E2E; deploy the folder (Netlify Drop / python -m http.server) to view.

📝 Additional Notes

  • The assistant produces a copy-ready hand-off prompt for another AI to perform the upload — it does not connect to Shopify or upload products itself.
  • Live AliExpress price/image data is best-effort (AliExpress blocks automated access); background scanning only runs while the app is open (PWA limitation, esp. iOS).
  • The interactive smart flows require a tools-capable model (Sonnet 5 default or Opus 4.8); Haiku remains fine for the plain navigation guide.

🌐 Browser Compatibility

  • Chrome ✅
  • Mobile browsers ✅ (PWA-installable; primary target)

🔄 Backwards Compatibility

  • Fully backwards compatible

🤖 Generated with Claude Code

https://claude.ai/code/session_01JyM1WF7vQqrdKw9JdaXphi


Generated by Claude Code

…i18n

Upgrade the standalone Shopify Navigator PWA (single-file vanilla JS, no build)
under shopify-nav/.

- i18n: full UI in 10 languages (he, en-US, ru, zh, fr, es, de, ar, en-GB, pt)
  with RTL/LTR handling and dynamic AI-output language injection into the
  system prompts. Language picker in Settings; browser-language fallback on
  first run.
- Store profile + scan: Settings field for the store URL + "Scan now" button;
  scanStore() uses Claude web_fetch/web_search to build a structured store
  profile (niche/style/colors/tone/categories) saved to localStorage. Auto
  re-scan every 10 min while the app is open (PWA-limited; noted in UI).
- Intent detection: SYSTEM_PROMPT now emits actionRequested; an "add a product
  for me" style query opens the interactive product wizard instead of a guide.
- Product wizard: 4 tailored ideas (2 classic, 1 differentiating, 1 TikTok
  trend) -> free-text / link / image input -> web-tool product match with
  image+price and yes/no+feedback loop -> supplier app pick -> ready-to-copy
  detailed English prompt (title/description/image-edit rules) for a
  browser/computer-control AI, with product image links.
- Tool loop: callClaudeSmart() runs Sonnet 5 / Opus 4.8 with web_search/
  web_fetch and a pause_turn->end_turn loop; smart flows auto-upgrade off Haiku.
- Settings: adds Opus 4.8 model option; sw.js cache bumped to v5; README
  documents the new features, model requirement, and background-scan limit.
- Decorative header circles set pointer-events:none so they never eat taps.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JyM1WF7vQqrdKw9JdaXphi
@github-actions

Copy link
Copy Markdown
Contributor

Automated code review (gate-code-review)

Credit balance is too low

Report-only — this check never blocks merge.

…ns) & Android APK

Adds an optional self-hosted backend and a dedicated Android TWA build for the
standalone Shopify Navigator app.

Server (shopify-nav/server/server.js — zero-dependency Node >=18):
- Serves the PWA and proxies Claude at POST /api/message using a server-held
  ANTHROPIC_API_KEY, so end users need no key. Same-origin (the served app +
  the TWA) uses the server key; a client-supplied x-user-api-key always wins;
  foreign browser origins must bring their own key (403/401 otherwise).
- Real background store scans every 10 min (server-side, works with the app
  closed) via web_search/web_fetch with pause_turn handling; JSON persistence.
- Hardening: per-IP + global rate limits, model/tool allowlists, max_tokens
  clamp, payload caps, path-traversal-safe static serving, /.well-known route.

Deploy (shopify-nav/deploy/): Dockerfile (node:22-alpine), docker-compose with
Caddy auto-HTTPS (HTTP/1-3), Caddyfile, .env.example, and setup.sh for a fresh
Ubuntu VPS (Contabo).

Client (index.html/sw.js): auto-detects the server via /api/health and switches
to keyless mode (persisted so an offline relaunch stays usable); routes calls
and store scans through the server; settings shows a "connected" note and makes
the personal key optional; sw v6 never caches /api/ or cross-origin.

Android APK: shopify-nav/android/twa-manifest.json + .well-known/assetlinks.json
(host shopify-nav.fuzefront.com, package com.fuzefront.shopifynav, pinned
fingerprint) and .github/workflows/shopify-nav-apk.yml (Bubblewrap build/sign,
gated on the SHOPIFY_NAV_* keystore secrets, Release on master / artifact on PR).

Hardened after an adversarial multi-agent review (30 confirmed findings fixed):
cross-origin scan-endpoint key-burn (origin check + global limit), X-Forwarded-For
spoofing (trusted-hop parsing), unbounded scan queue/backlog, shutdown data loss
(sync flush), pause_turn/partial-scan handling, Number() max_tokens clamp,
malformed-percent-encoding 400, blocklist-after-normalize traversal, upstream
body-read timeout, serverKeyed vs serverMode gating, offline keyless relaunch,
sw /api/ path-prefix + cross-origin caching, clearable personal key, setup.sh
sed-injection/EOF-abort/compose-plugin, HTTP/3 UDP port, compose env passthrough,
CI secret-completeness gate, keystore-path gitignore, and APK version-code default.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JyM1WF7vQqrdKw9JdaXphi
Comment thread .github/workflows/shopify-nav-apk.yml Fixed
Comment thread .github/workflows/shopify-nav-apk.yml Fixed
Comment thread .github/workflows/shopify-nav-apk.yml Fixed
Comment thread .github/workflows/shopify-nav-apk.yml Fixed
Comment thread .github/workflows/shopify-nav-apk.yml Fixed
Comment thread shopify-nav/server/server.js Fixed
// Resolve to an absolute path FIRST (collapses any ../ and %2f-decoded
// traversal), then derive the first path segment for the blocklist — checking
// the raw segment before normalization let "a%2f..%2f.env" slip past it.
const abs = path.normalize(path.join(STATIC_DIR, rel));
}

/* ---------------------------------- routing --------------------------------- */
const server = http.createServer(async (req, res) => {
@github-actions

Copy link
Copy Markdown
Contributor

Automated code review (gate-code-review)

Looking at the visible portions of the diff (the workflow YAML and shell scripts — most of index.html's functional JS is truncated and not reviewable).

  • .github/workflows/shopify-nav-apk.yml:159 — In the "Locate signed APK" step, APK=$(find . -name "*.apk" | grep -v unsigned | head -1) runs under GitHub Actions' default bash -eo pipefail. When find produces more output than the pipe buffer holds after head/grep exit early, the upstream commands die with SIGPIPE (141); pipefail propagates that non-zero code and set -e fails the step even though an APK was found. This should use a trailing || true on the pipeline (as the fallback line does not) or avoid the head short-circuit.

(That is the only defensible runtime-correctness concern in the reviewable code; the two find | head pipelines are the risk. The rest of the reviewable changes — the version-code node scripts, setup.sh, and generate-keystore.sh — are correct, and the app's core JS is truncated out of the diff so I can't assess it.)

Report-only — this check never blocks merge.

claude added 5 commits July 12, 2026 17:59
- Pin all GitHub Actions in shopify-nav-apk.yml to full commit SHAs
  (checkout/setup-java/setup-node/upload-artifact/action-gh-release) to close
  the Semgrep mutable-action-tag supply-chain findings.
- Locate-APK step: use `find -print -quit` instead of `find | head`, which
  could SIGPIPE find under `set -o pipefail` and fail the step spuriously.
- server.js: build the upstream-error object without Object.assign (the one
  site carrying upstream data) to clear the insecure-object-assign finding;
  the value is used for internal flow control, never merged into a response.
- Annotate the two architectural false positives with justified nosemgrep:
  the static path.join is containment-checked against STATIC_DIR on the next
  line, and http.createServer is intentional (Caddy terminates TLS in front).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JyM1WF7vQqrdKw9JdaXphi
Repoint the TWA host, PWA/icon/manifest URLs, deploy defaults, and docs from
shopify-nav.fuzefront.com to nav.velogearpremium.com. Signing keystore and
SHA-256 fingerprint are unchanged (fingerprint binds the key, not the domain);
assetlinks.json is domain-agnostic and is served from the new host.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JyM1WF7vQqrdKw9JdaXphi
Google Play requires an Android App Bundle, not an APK. Add a bundleRelease
step that jarsigns the .aab with the existing upload keystore, and attach both
the .apk (direct install) and .aab (Play upload) to the artifact and Release.
Release notes document the Play App Signing SHA-256 -> assetlinks.json step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JyM1WF7vQqrdKw9JdaXphi
Add shopify-nav/privacy.html (accurate to the app's actual data handling),
copy it into the server image, and serve it at a clean /privacy URL (plus
/privacy.html). Google Play requires a privacy policy URL for the listing;
this hosts it on the app's own domain (https://nav.velogearpremium.com/privacy).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JyM1WF7vQqrdKw9JdaXphi
@izzywdev
izzywdev marked this pull request as ready for review July 12, 2026 21:39
@github-actions
github-actions Bot enabled auto-merge (squash) July 12, 2026 21:39
@github-actions

Copy link
Copy Markdown
Contributor

Automated code review (gate-code-review)

Reviewing the diff for runtime-correctness issues. Most of the reviewable surface is CI/shell/Docker config; the main app JS in index.html is truncated to i18n tables (no logic shown), so I focused on the workflow and scripts.

  • .github/workflows/shopify-nav-apk.yml:154jarsigner -verify "$AAB" runs without -strict, so it exits 0 even for an unsigned AAB and only prints "jar is unsigned". If the preceding jarsigner ... sign produced no valid signature, this verification silently passes (swallowed error), and the unsigned .aab is then attached to the GitHub Release. The APK path correctly uses apksigner verify (which does fail on unsigned), so the AAB path is the weak one.

Everything else checks out: the have == 'true' secret-gate correctly prevents building/publishing an unsigned APK, the find -print -quit avoids the SIGPIPE-under-pipefail hazard, the version-code default falls back to $RUN_NUMBER, and the twa-manifest version write happens before the scaffold round-trip so appVersionCode is preserved.

Report-only — this check never blocks merge.

@github-actions

Copy link
Copy Markdown
Contributor

Automated code review (gate-code-review)

Reviewing the fully-visible diff (workflow, shell scripts) plus the truncated index.html JS read from disk. One high-confidence runtime bug:

  • .github/workflows/shopify-nav-apk.yml:178if ! jarsigner -verify -strict "$AAB" | grep -q "jar verified" will spuriously abort the build for a correctly signed AAB. The release/upload keystore made by generate-keystore.sh is self-signed, so jarsigner -verify -strict treats the unvalidated cert chain as an error and exits non-zero (code 4) even though signatures verify and it still prints jar verified.. GitHub Actions runs steps with bash -eo pipefail, so pipefail propagates jarsigner's non-zero exit through the pipe; the if ! then fires the error branch and exit 1s — failing every self-signed (i.e. normal) AAB build. The grep -q "jar verified" match is defeated by the exit-code propagation.

Secondary (lower confidence, manual-input only):

  • .github/workflows/shopify-nav-apk.yml:88 — on workflow_dispatch with a non-numeric version_code, parseInt(code, 10) yields NaN and JSON.stringify writes "appVersionCode": null into twa-manifest.json, feeding an invalid/absent version code into the Gradle build.

Report-only — this check never blocks merge.

@izzywdev
izzywdev merged commit 2df57e4 into master Jul 12, 2026
45 checks passed
@izzywdev
izzywdev deleted the claude/shopify-navigator-product-upgrade-mwql3l branch July 12, 2026 22:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants