Manages deployment of Poke-Singles to SiteGround, including environment handling and asset uploads.

Install

mkdir -p .claude/skills/deploy-dalso182 && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/19024" && unzip -o skill.zip -d .claude/skills/deploy-dalso182 && rm skill.zip

Installs to .claude/skills/deploy-dalso182

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Deployment, environments, and hosting for Poke-Singles on SiteGround. Use this whenever you deal with shipping the app or its assets: `npm run deploy:dev`/`deploy:prod`, the `scripts/deploy.mjs` SFTP uploader and its flags, the auto-generated SPA-fallback `.htaccess`, the two-tier (local/dev/prod) environment model and `environment*.ts` files, the deploy safety guard that blocks the live OpenCart root, self-hosting card images (`images:fetch`/`images:upload`), and the no-Node SiteGround constraint (SPA-only, SSG later). Trigger this for anything about where builds go, how `.env.local` creds work, or why a deploy is refused. Read this before running or modifying any deploy/hosting command.
697 charsno explicit “when” triggerlonger than Claude Code's old 250-char listing cap (fine on current versions)
Intermediate

Key capabilities

  • Deploy Poke-Singles app to dev or prod environments
  • Upload `dist/poke-singles/browser/` via SFTP
  • Generate SPA-fallback `.htaccess` files
  • Manage environment configurations for local, dev, and prod
  • Self-host card images

How it works

The skill uses `npm run deploy` commands to build the app and then `scripts/deploy.mjs` to upload the build output via SFTP, managing environment variables and generating `.htaccess` files.

Inputs & outputs

You give it
A deployment command like `npm run deploy:dev` or `npm run deploy:prod`
You get back
The Poke-Singles app deployed to the specified environment, or card images uploaded to the host

When to use deploy

  • Deploy app to production
  • Upload assets to SiteGround
  • Switch deployment environment

About this skill

Deploy, environments & hosting (SiteGround)

SiteGround Shared/Cloud runs Apache + PHP with no Node, so the app ships as a client-rendered SPA. (SSG via ng add @angular/ssr + getPrerenderParams() is feasible later — deferred until product-page SEO matters; only Dedicated Servers support true SSR.)

Deploy commands

npm run deploy:dev    # ng build --configuration=dev  + SFTP upload to dev.poke-singles.com
npm run deploy:prod   # ng build --configuration=production + SFTP upload to poke-singles.com

scripts/deploy.mjs reads .env.local (gitignored — copy from .env.local.example) for the SiteGround SFTP creds and uploads dist/poke-singles/browser/. Prod uses the unprefixed env vars (DEPLOY_HOST, DEPLOY_USER, …); dev uses the DEV_-prefixed counterparts. Auth is SSH key (preferred) or password.

Flags:

  • --only=code|assets|all (default all) — code skips the assets/ subtree (useful once the ~5k product images stabilize so iterations don't re-upload them).
  • --skip-build — re-upload an existing dist/.
  • --dry-run — list files without uploading (run this first).

.htaccess (auto-written)

The script writes the .htaccess at the upload root automatically — no manual file management. Since the cutover it carries three rule blocks (see HTACCESS_BODY in scripts/deploy.mjs): a www→apex 301 (host-conditioned, inert on dev.), a legacy-OpenCart 301 (index.php/, query dropped — a fuller per-route 301 map is a tracked follow-up), and the SPA fallback so deep links (/products/<slug>) reload to index.html.

SEO artifacts in the pipeline (added 2026-08-01)

code/all deploys copy two root PHP endpoints into the upload root and stamp their shared per-env og-config.php (writePhpEndpoints() in deploy.mjs, values parsed from the env-matching environment*.ts):

  • server/og-render.php — the generated .htaccess routes /products/<slug> hard loads through it so WhatsApp/FB previews get per-product OG tags. Fail-open.
  • server/sitemap.php/sitemap.xml rewrites here; the sitemap is built live from the catalog (6 h cache). There is no deploy-time sitemap generation, on purpose — deploys ship code, content freshness comes from Supabase.

public/robots.txt ships as a normal static asset. --only=assets touches none of this. Details, URL mapping, caches, and the in-app SeoServicedocs/architecture/seo.md.

Static no-JS pages in public/ (Google OAuth verification)

The SPA-fallback rewrite has !-f/!-d conditions, so any physical file in the build wins over the SPA. public/** is copied verbatim to the dist root (angular.json assets), which is how public/info/politica-de-privacidad/index.html and public/info/terminos-de-servicio/index.html ship: static Spanish snapshots of those two static_pages rows, added 2026-07-30 because Google's OAuth-verification checker doesn't run JS and flagged the SPA-rendered privacy URL as "unresponsive". Direct/hard loads of those two URLs get the static file (Apache mod_dir 301s to the trailing-slash form first); in-app navigation still renders the Angular /info/:slug page from the DB. Sync caveat: editing either page in /admin/pages must be mirrored into the matching public/info/**/index.html — each file carries a header comment saying so.

Deploy safety guard (do not weaken casually)

Post-cutover (2026-07-22) the live root at poke-singles.com/public_html is this app, so deploy:prod targets it on purpose with real creds in .env.local. The BLOCKED_REMOTE_PATHS regex array in scripts/deploy.mjs now protects public_html_opencart — the renamed OpenCart site, kept as the rollback/backup until decommission (rollback = swap the two folder renames back over SSH). Never deploy into it.

Two-tier environment model (post 2026-07 prod promotion)

TierFrontendSupabaseSelected by
Localnpm start (localhost:4242)fdscdinfpmvswinpasdg (dev-poke-singles, free org)environment.ts
Devdev.poke-singles.com (Basic-auth)fdscdinfpmvswinpasdg (dev)ng build --configuration=dev (no file replacement)
Prodpoke-singles.com (live since 2026-07-22, maintenance mode during the restock window)dhslfridsjdmhwzrgebv (the original project, promoted to prod; Pro org)ng build --configuration=production (fileReplacementsenvironment.prod.ts)

Both environment files carry real values now. dhslfridsjdmhwzrgebv = PROD — never treat it as dev. The CLI stays linked to the dev project (db:push:dev); prod migration pushes go through npm run db:push:prod (a wrapper requiring SUPABASE_PROD_DB_URL in .env.local, deliberate by design). new.poke-singles.com (the old staging site) is being retired; proxy.conf.mjs targets dev.poke-singles.com for localhost images (switched 2026-07-14 — the stale new. PHP gate rejected dev tokens). The free-tier dev project auto-pauses after ~1 week idle — resume it from the dashboard if the dev site/e2e suddenly can't reach Supabase.

Promotion flow (dev → prod, post-launch)

Gate every promotion with npm run preflight (unit tests + Playwright e2e vs dev + prod build). Then by change type:

  1. Frontend-only: preflight → deploy:dev → spot-check dev.poke-singles.com → deploy:prod.
  2. DB change: migration → db:push:devdb:types:dev → code → preflight → verify on dev → db:push:prod BEFORE deploy:prod (additive changes are safe for the old bundle in between; breaking renames land migration+code together — see the rename-sequencing rule).
  3. Edge functions: functions:dev → exercise the flow on dev → functions:prod.
  4. Dashboard config (auth URLs, secrets, templates, Vault): no git trail — change on dev, verify, mirror on prod. The out-of-migrations config checklist lives in docs/architecture/environments-and-deploy.md.

Self-hosting card images

The store can serve its own card art instead of hotlinking the TCGdex CDN. Layout on the host: card-images/<serie>/<set>/<localId>.webp (e.g. card-images/swsh/swsh3/136.webp). Reference images by relative path (/card-images/...) so the same catalog rows resolve on every domain (dev., the prod root, localhost via proxy). The tree lives on the dev. and prod docroots (the full ~2 GB tree was server-side copied dev→prod at cutover — cp -a over SSH, not re-uploaded from local; incremental set additions since then go up per-set with images:upload --env=prod --sets=<id> — see the scanless-set runbook below). upload-images.mjs stamps auth-config.php per --env from the matching environment file so each site's PHP gate validates against its own Supabase project.

node scripts/fetch-card-images.mjs --dry-run   # set/card counts + size estimate, downloads nothing
npm run images:fetch                            # download English sets → ./card-images/ (gitignored, ~1.5–2.5 GB, resumable)
npm run images:upload                           # tar + SSH-extract to <remote>/card-images (reuses .env.local creds)
npm run images:upload:endpoints                 # push server/*.php only (no image tree)

./card-images/ lives outside dist/, so a normal deploy:* can never sweep it up. The PHP picker endpoints that live alongside the images → admin skill.

Admin-uploaded images — the host is the only source

Not every /card-images/ file is re-fetchable. Photos uploaded through the admin image picker (accessories/sealed under accesorios/*, sellado/*, plus OpenCart-era *_in_1000x1000.jpg promo scans in sv/svp, me/mep, ex/exu, swsh/swshp, mc/*) exist only on the host docrootsimages:fetch cannot recreate them, and they're usually absent from the local ./card-images tree. So a "prod catalog → dev" data sync leaves dev with broken images for exactly these files: copy them prod host → local tree → dev host over SFTP (done 2026-07-30 for 144 files; keeping the local tree complete means future images:upload runs carry them). Treat the prod docroot as the canonical image store.

SiteGround negative-cache gotcha: the host's NGINX cache stores per-URL responses, so an image URL requested while the file was missing keeps answering 403/404 after the upload. Diagnose with a query-string bypass (?v=2 → 200 = it's the cache); fix via Site Tools → Speed → Caching → Flush Cache, or wait out the TTL. Also chmod-check SFTP-written files (dirs 755 / files 644) — ssh2-sftp-client writes don't go through the tar path that normalizes modes.

Scanless sets — the pokemontcg.io fallback

Some cards have no scan on TCGdex (card.image is null). images:fetch can still get them from images.pokemontcg.io (PNG → webp via sharp, same <serie>/<set>/<localId>.webp path). Two mechanisms in fetch-card-images.mjs, chosen by how the two sources' numbering relates:

  • PTCGIO_FALLBACK_SETS (tcgdexSetId → pokemontcgSetId) — pokemontcg.io numbers the cards the same as the TCGdex localId, so the URL is a formula: <ptcgioSet>/<localId>_hires.png. Covers the five SWSH gallery subsets (swsh9.5tg, swsh11.5tg, swsh12.5tg, swsh12.5gg, swsh4.5sv) and Shining Legends (sm3.5sm35, added 2026-08-10) — fully scanless — and base Celebrations (cel25), which is scanned on TCGdex except localId 25 (Mew). The fallback fires per-card, so a mostly-scanned set with one gap costs exactly one fetch.
  • PTCGIO_CARD_MAP (tcgdexSetId → { ptcgioSet, cards: { localId → "<stem>" } }) — pokemontcg.io numbers them differently, so there's no formula; the URL is <ptcgioSet>/<stem>_hires.png. Covers Celebrations Classic Collection (TCGdex cel25cc → pokemontcg.io cel25c): localIds are clean CC001…CC025, but pokemontcg.io reuses each card's original number, disambiguates duplicates with a letter suffix (four "15"s → 15_A…15_D), and spells some

Content truncated.

When not to use it

  • When the deployment target is not SiteGround
  • When the app is not a client-rendered SPA
  • When attempting to deploy to the live OpenCart root without explicit modification

Limitations

  • SiteGround runs Apache + PHP with no Node, requiring a client-rendered SPA.
  • Deployment to the live OpenCart root is blocked by a safety guard.
  • Card images are self-hosted and managed separately from the main app deployment.

How it compares

This skill provides a specialized deployment process tailored for SiteGround's no-Node environment, including specific SFTP handling, `.htaccess` generation, and safety guards, unlike generic deployment scripts.

Compared to similar skills

deploy side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
deploy (this skill)01moReviewIntermediate
build-macos-apps709moReviewIntermediate
cloudflare-manager2510moReviewIntermediate
railway-cli-management99moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry