Skill: tenshowinnovation/marketing-site-dev (version 0.1.0)

--- name: marketing-site-dev description: Opinionated end-to-end workflow for shipping a bilingual (中文/English) static company marketing site to Volcengine — Astro 6 + React 19 islands + Tailwind 4, deployed to TOS + CDN with HTTPS, force-redirect, HSTS, and HTTP/2, entirely via the `ve` CLI and Node SDKs. Use whenever the user mentions building, scaffolding, deploying, or updating a company / brand / product / 公司官网 / 营销网站 / 品牌站 / marketing site, ESPECIALLY when Volcengine (火山引擎), TOS, CDN, ICP 备案, or Chinese deployment is involved. Also trigger when the user provides a company brief with product list + brand assets and asks for a "single command deploy" or "production-ready landing page", or references files like `astro.config.mjs`, `deploy.config.mjs`, `scripts/cdn-setup.mjs`, `scripts/setup-bucket.mjs`. Prefer this skill over generic web-dev guidance whenever the target deployment is Volcengine + China mainland. license: MIT compatibility: Designed for Claude Code and compatible agents. Requires Node.js 22+, pnpm 10+, the Volcengine `ve` CLI (1.0.x+), and network access to npm + Volcengine OpenAPI. Domain must already be ICP-filed and ideally hosted on Volcengine DNS. macOS / Linux recommended. metadata: author: "北京腾秀创智技术有限公司 (Tenshow Innovation)" organization: tenshowinnovation.com version: "0.1.0" --- # Marketing Site Dev (Volcengine) End-to-end skill for building and shipping a bilingual static company marketing site to Volcengine. Two phases: 1. **Build** — Astro 6 + React 19 islands (mobile menu only) + Tailwind 4. Inline SVG everywhere. Single content file (`src/content/site.ts`) is the source of truth. 2. **Deploy** — TOS bucket + CDN + free DV cert + HTTPS hardening, all scripted in idempotent Node scripts via `ve` CLI and Volcengine OpenAPI (SigV4-signed via `volc-api.mjs` for the actions `ve` doesn't ship). **What success looks like:** the user runs ~6 commands end-to-end and gets `https://<their-domain>/` serving a production-quality bilingual site with HTTPS, force-redirect to HTTPS, HSTS, HTTP/2, edge caching, and automated cache invalidation. Every infrastructure step is in version control as an idempotent script. --- ## Is this skill the right fit? This is a **deliberately opinionated** workflow optimized for one specific scenario. Before going deep, check that scenario actually matches the user's situation. If it doesn't, surface the better alternative instead of forcing the user through a heavier path. **Use this skill when:** - The site is for a **Chinese company or a product targeting users in mainland China** — latency from mainland to non-Chinese CDNs is bad enough (200-400ms, often worse during peak hours) that visitors notice and bounce. - **ICP 备案 is required or already in hand** — distributing in China legally needs the filing tied to a domain hosted on a Chinese cloud. Volcengine bundles 备案 assistance and offers it for the domain you deploy here. - The user is already in the Volcengine ecosystem (other Volcengine services, existing TOS buckets, etc.) and consolidation is valuable. - The bilingual zh/en pattern is desired — the content layer (`site.ts`) is designed around it. **Use a simpler alternative when:** | Scenario | Recommended alternative | Why | |---|---|---| | **Personal site / portfolio / side project, no Chinese audience requirement** | **GitHub Pages** | Free, auto-HTTPS, deploys on `git push`, no infra to manage. Zero of the Volcengine landmines apply. | | **International-only audience (US/EU/SEA), no China presence planned** | **Vercel** (or Cloudflare Pages / Netlify) | Free tier, instant deploys from git, preview URLs per PR, automatic HTTPS, global edge network. Skip the whole `ve` CLI + ICP workflow entirely. | | **Site needs serverless functions (forms, auth, dynamic content)** | **Vercel** or **Cloudflare Pages** with their edge functions | This skill ships purely static — Volcengine has serverless products, but the workflow here doesn't cover them. | | **You want preview URLs per branch / PR-based review** | **Vercel** | Vercel's preview-URL UX is best-in-class. This skill produces one production deploy per push; no branch previews. | If the user's situation matches the "use a simpler alternative" column, **tell them so explicitly** before scaffolding anything. A 2-minute Vercel deploy is a better experience than a 30-minute Volcengine bootstrap when the user doesn't need what Volcengine provides. Don't sunk-cost them into the wrong stack because they invoked this skill. When in doubt, ask: "Will mainland-China users be a meaningful part of your audience, and do you have (or plan to file) ICP 备案 for the domain?" If both answers are no, point at Vercel / GitHub Pages and stop. --- ## Step 1: Capture intent (ALWAYS ASK FIRST) Don't write any code until you have these six inputs from the user. If they're missing from the prompt, ask via `AskUserQuestion`. Most teams will have everything except possibly the public-security number — that one is OK as a placeholder. | # | Input | Used for | Notes | |---|---|---|---| | 1 | **Company name** (Chinese + English) | `COMPANY.nameZh` / `COMPANY.nameEn` in `site.ts`; footer; SEO | | | 2 | **Domain** (e.g. `example.com`) | Everywhere — bucket binding, CDN, DNS, cert SAN | MUST already be ICP-filed and ideally hosted on Volcengine DNS. If hosted elsewhere, the user needs to manually add CNAME + TXT records. | | 3 | **Brand assets directory** (path to SVG logo files) | Inline `BrandMark.astro` extraction (last 3 paths of the short logo SVG) | Usually something like `assets/logo/svg/Tenshow_short1.svg` — short / icon-only variant. | | 4 | **Product list** | `PRODUCTS[]` in `site.ts` | Each product needs bilingual name + description, URL, accent hex color, icon enum. | | 5 | **ICP record number** | Footer | The MIIT-issued 备案号. | | 6 | **Working directory** | Project root | Should be greenfield (empty) or you'll have to coordinate with existing files. | Optionally: - **公安备案号** (Public Security Bureau filing number) — usually a placeholder at scaffold time. Flag it loudly in the README so the user replaces it before launch. - **Brand accent color** — extract from the logo SVG's hex values. If undecidable, default to whatever the dominant non-neutral color is. --- ## Step 2: Build the static site Full details in [references/phase-1-static-site.md](references/phase-1-static-site.md). The shortlist: 1. **Scaffold** — copy [`assets/templates/package.json`](assets/templates/package.json) and [`assets/templates/astro.config.mjs`](assets/templates/astro.config.mjs) into the project root. Update `name` in package.json and `site` in astro.config.mjs. 2. **Install** — `pnpm install`. 3. **Create the source tree** matching the layout in [phase-1-static-site.md](references/phase-1-static-site.md) (`src/content/site.ts`, `src/components/`, `src/layouts/BaseLayout.astro`, `src/pages/index.astro`, `src/pages/en/index.astro`, `src/styles/global.css`). 4. **Extract the brand mark** from the user's SVG — use [`assets/templates/BrandMark.astro`](assets/templates/BrandMark.astro) as a starting point. The last 3 `<path>` elements of the short-logo SVG are the mark; copy them verbatim and pick fills from the brand palette. 5. **Build the sections** — Hero (with the exact recipe in [phase-1-static-site.md](references/phase-1-static-site.md) — grid background + radial glow + gradient headline + status pill + 2 CTAs), About, Products, Focus, Updates, Contact, Footer. Header sticky, mobile menu = the only React island. 6. **Verify locally** — `pnpm dev` to develop, `pnpm build && pnpm preview` to sanity-check the static output before touching infra. ### Stack lock-in (don't deviate) Astro 6, React 19 (mobile menu only), Tailwind 4 via `@tailwindcss/vite`, pnpm 10, Node 22+, TypeScript strict, zero external images, zero UI libs, zero font services, system font stack. Rationale and full details in [phase-1-static-site.md](references/phase-1-static-site.md). --- ## Step 3: Set up deploy infrastructure Full details in [references/phase-2-volcengine-deploy.md](references/phase-2-volcengine-deploy.md). **Read [references/landmines.md](references/landmines.md) FIRST** — half your time in this phase is avoiding the landmines, not running commands. 1. **Copy scripts** — copy the entire [`assets/scripts/`](assets/scripts/) directory and [`assets/cdn-payloads/`](assets/cdn-payloads/) into the user's `scripts/` and `scripts/cdn-payloads/` respectively. Copy [`assets/templates/deploy.config.mjs`](assets/templates/deploy.config.mjs) to the project root and fill in `bucket`, `region`, `endpoint`, `cdnDomains`. 2. **Replace placeholders in payloads** — `{{DOMAIN}}`, `{{BUCKET}}`, `{{REGION}}` in `scripts/cdn-payloads/apex.json` and `www.json`. **`OriginHost` must be the domain itself, not empty** — the templates are correct out of the box; if you edit them, preserve `OriginHost`. See [landmines.md #3](references/landmines.md). 3. **Create `.env`** at project root (gitignored): ```env VOLC_ACCESS_KEY=AK... VOLC_SECRET_KEY=... ``` Volcengine AK/SK needs `TOSFullAccess`, `CDNFullAccess`, `DNSFullAccess`, `CertificateFullAccess` (or least-privilege equivalents). 4. **Verify `ve` is installed**: `ve version` — needs 1.0.x or later. If missing, install via Volcengine's official docs. --- ## Step 4: Execute the first deploy ```bash pnpm install # if not done yet pnpm build # verify local build works pnpm run setup:bucket # idempotent: bucket + website routing + custom-domain binding pnpm run deploy:upload # sync dist/ → bucket # at this point: https://<bucket>.tos-<region>.volces.com/index.html should 200 pnpm run setup:cdn # verification → CDN add → CNAME → cert → HTTPS hardening # wait 1-3 minutes for DNS propagation pnpm run cdn:refresh # purge edge cache so first-time hits don't return bucket-listing JSON ``` After this initial run, daily iteration becomes **one command**: ```bash pnpm run deploy && pnpm run cdn:refresh ``` ### Always use `pnpm run`, NOT bare `pnpm` `pnpm deploy` is a pnpm built-in workspace command — it'll error with `ERR_PNPM_CANNOT_DEPLOY` outside a workspace. Use **`pnpm run deploy`** and **`pnpm run cdn:refresh`**. Document this in the project's README. See [landmines.md #6](references/landmines.md). --- ## Step 5: Verify Run the curl + openssl checklist in [references/verification.md](references/verification.md). Expected: 5× HTTP 200 on key routes, 1× 301 on HTTP → HTTPS, valid cert with SAN covering apex + www, HTTP/2 + HSTS confirmed, cache-control headers matching `deploy.config.mjs` rules. If any check fails, [verification.md](references/verification.md) maps each failure mode to the root cause and the fix. --- ## Critical landmines you must know before executing Full enumeration in [landmines.md](references/landmines.md). The ones you'll hit if you skip reading: 1. **`HTTPS_PROXY` pollution** — TOS SDK + a local HTTP proxy = `Protocol "http:" not supported`. Every script in `assets/scripts/` already deletes proxy env vars at the top — preserve that preamble if you copy / adapt them. 2. **TOS SDK quirks** — `headBucket(string)` not `headBucket({bucket})`; `listObjectsType2` signature breaks when `continuationToken: undefined` is in the input; `putObject` defaults to private even on public-read buckets — always set `acl: 'public-read'`. 3. **TOS has no separate website endpoint hostname** — the same `<bucket>.tos-<region>.volces.com` returns either bucket-listing JSON or `index.html` depending on the `Host` header. You MUST (a) call `putBucketCustomDomain` per apex/www domain, AND (b) set `OriginHost` in the CDN `AddCdnDomain` payload to the user's domain (not empty). 4. **CDN ≠ DCDN for verification** — CDN (内容分发网络) wants TXT host `volccdnauth` and `cdn:CheckCdnDomain` (not in `ve` CLI — use `volc-api.mjs`). DCDN (全站加速) uses `_dnsauth` and `ve dcdn VerifyDomainOwnership`. Mixing them up = endless verification loop. 5. **TXT changes take ~10 min to propagate** at Volcengine's verifier. Get the value right on the first write; don't churn. 6. **`pnpm deploy` is a built-in.** Use `pnpm run deploy`. 7. **Free DV cert APPLICATION is console-only.** API issuance is blocked with permanent AK/SK (`OperationDenied.RequestFreeInstance`). But **listing already-issued certs works fine** — and free DV certs are often auto-issued at domain registration. Always `CertificateGetInstanceList` first; look in `Result.Instances[]` (NOT `InstanceList`, NOT `Data`) for one whose `San` covers both apex + www. --- ## Behavior rules - **Plan first; confirm before destructive ops.** Before `createBucket`, `AddCdnDomain`, `DeleteRecord` on existing TXT, or `BatchDeployCert`, summarize what's about to happen and ask the user for confirmation. - **Read existing state before writing.** Always `ListRecords` / `ListCdnDomains` / `CertificateGetInstanceList` first — free certs are often pre-issued, CDN domains may already exist, DNS records may already point at the right place. Idempotency is the whole point. - **When `ve` lacks an action, use `volc-api.mjs`** rather than telling the user to upgrade `ve` or use the console. The only true console-only path is free DV cert issuance ([landmines.md #4](references/landmines.md)). - **Don't churn DNS records.** Each TXT modification adds ~10 min cache. Get the host/value right the first time — `volccdnauth` for CDN, `_dnsauth` for DCDN, they're not interchangeable. - **Surface every config decision in `deploy.config.mjs`**, never hardcode in scripts. Bucket, region, endpoint, CDN domains, cache rules — all live in the config so the user can edit one file to change everything. - **`/usr/bin/curl` is more reliable than bare `curl`** when running checks after long commands in the same shell — bash PATH lookup occasionally goes weird. Same goes for tests in CI scripts. --- ## Bundled assets and references ### `assets/scripts/` (copy verbatim into the user's `scripts/`) - [`lib/volc-api.mjs`](assets/scripts/lib/volc-api.mjs) — SigV4 signer, zero deps. Use for any action missing from `ve` CLI. - [`setup-bucket.mjs`](assets/scripts/setup-bucket.mjs) — idempotent bucket + website + custom-domain binding. - [`deploy.mjs`](assets/scripts/deploy.mjs) — TOS sync uploader with per-prefix `Cache-Control`, orphan deletion. - [`cdn-setup.mjs`](assets/scripts/cdn-setup.mjs) — CDN orchestration: ownership verification → AddCdnDomain → CNAME → cert → HTTPS hardening. - [`cdn-refresh.mjs`](assets/scripts/cdn-refresh.mjs) — cache invalidation via `cdn:SubmitRefreshTask` (signed by `volc-api.mjs`, works in CI without `ve`). ### `assets/cdn-payloads/` (copy into `scripts/cdn-payloads/`) - [`apex.json`](assets/cdn-payloads/apex.json) and [`www.json`](assets/cdn-payloads/www.json) — `AddCdnDomain` payload templates with `{{DOMAIN}}`, `{{BUCKET}}`, `{{REGION}}` placeholders. `OriginHost` is set correctly out of the box — don't strip it. ### `assets/templates/` (copy into project root) - [`astro.config.mjs`](assets/templates/astro.config.mjs) — locked-in Astro config. Only `site` should change per project. - [`deploy.config.mjs`](assets/templates/deploy.config.mjs) — fill in `bucket`, `region`, `endpoint`, `cdnDomains`. - [`package.json`](assets/templates/package.json) — scaffolded with the right dependencies + scripts. Update `name`. - [`BrandMark.astro`](assets/templates/BrandMark.astro) — starting point for the brand mark component. Replace the three `<path>` elements with the user's logo paths. ### References (deep dives — read as needed) - [`references/phase-1-static-site.md`](references/phase-1-static-site.md) — full Phase 1 walkthrough: stack rationale, content layout, brand mark extraction, SEO/a11y rules, hero visual recipe, file structure. - [`references/phase-2-volcengine-deploy.md`](references/phase-2-volcengine-deploy.md) — full Phase 2 walkthrough: what `ve` can/can't do, the 5 scripts step-by-step, environment setup, the 12-step `cdn-setup.mjs` sequence with payload examples. - [`references/landmines.md`](references/landmines.md) — every known gotcha (proxy pollution, TOS SDK quirks, CDN vs DCDN, TXT propagation, pnpm-deploy collision, cert auto-issuance). Read once before executing Phase 2 for the first time. - [`references/verification.md`](references/verification.md) — post-deploy curl + openssl checklist with failure-mode mapping.


Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: clean | SkillSpector: suspicious (MEDIUM)