lov-deploy-to-vercel
Deploy frontend projects to Vercel with automatic custom domain setup. Handles Vite, Next.js, CRA, and static sites. Auto-configures Cloudflare DNS CNAME records and Vercel domain aliases. Supports SPA routing via vercel.json. Trigger when user says "deploy to vercel", "部署到 verce
Install
npx skills add https://github.com/lovstudio/skills/tree/main/skills/deploy-to-vercel
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install lovstudio-skills@llmmart
git clone https://github.com/lovstudio/skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole lovstudio/skills collection as a plugin from our marketplace. Git is the plain clone.
README
Vercel 部署助手 · Vercel Deployer
One-command frontend deployment to Vercel with automatic custom domain + Cloudflare DNS setup.
Part of skill-publisher/skills — by example.com
Install
npx skills add skill-publisher/skills --skill lov-deploy-to-vercel -y -g
Requires: vercel CLI, curl, python3
Usage
/deploy-vercel # deploy to Vercel (auto-detect framework)
/deploy-vercel sbti.example.com # deploy + configure custom domain + DNS
/deploy-vercel --preview # preview deployment only
What It Does
- Detects framework (Vite / Next.js / CRA / static)
- Creates
vercel.jsonSPA rewrites if needed - Deploys to Vercel production
- Adds custom domain + sets alias
- Auto-configures Cloudflare DNS CNAME record
- Verifies site is live
Options
| Option | Description |
|---|---|
<domain> |
Custom domain (e.g. app.example.com) |
--preview |
Preview deploy only |
--no-dns |
Skip Cloudflare DNS setup |
--link-only |
Link project without deploying |
Environment
| Variable | Required | Description |
|---|---|---|
CLOUDFLARE_API_KEY |
For DNS auto-config | Cloudflare API Token with DNS:Edit |
License
MIT
Skill manifest
Vercel 部署助手 · Vercel Deployer
Deploy frontend projects to Vercel with automatic custom domain and DNS setup.
When to Use
- User says "deploy to vercel" or "部署到 xxx.example.com"
- After building a frontend project that needs hosting
- When setting up a custom domain on an existing Vercel deployment
Arguments
Pass via $ARGUMENTS:
| Argument | Example | Description |
|---|---|---|
<domain> |
sbti.example.com |
Custom domain to configure |
--preview |
Deploy preview only (skip --prod) |
|
--no-dns |
Skip Cloudflare DNS auto-config | |
--link-only |
Only link project, don't deploy |
Workflow
Step 1: Detect Project Type
if [ -f "vite.config.ts" ] || [ -f "vite.config.js" ]; then
FRAMEWORK="vite"
elif [ -f "next.config.js" ] || [ -f "next.config.mjs" ]; then
FRAMEWORK="next"
elif grep -q "react-scripts" package.json 2>/dev/null; then
FRAMEWORK="cra"
else
FRAMEWORK="static"
fi
Step 2: Ensure vercel.json for SPA
For Vite/CRA (SPA) projects, create vercel.json if missing:
{
"rewrites": [
{ "source": "/(.*)", "destination": "/" }
]
}
Skip for Next.js — it handles routing natively.
Step 3: Deploy to Vercel
Before running a production deployment, use AskUserQuestion if the target
project, production/non-production mode, or custom domain is unclear. If the
user already explicitly requested production deployment for this project, proceed.
# Check CLI
vercel --version || npm i -g vercel
# Deploy (use project name from package.json "name" field)
# IMPORTANT: package.json "name" must be lowercase, no special chars
PROJECT_NAME=$(node -p "require('./package.json').name" 2>/dev/null || basename "$PWD")
vercel --yes --prod
Known issue: If package.json name contains uppercase or invalid chars,
vercel will error with "Project names must be lowercase". Fix the name first.
Step 4: Configure Custom Domain (if provided)
DOMAIN="<user-provided-domain>" # e.g. sbti.example.com
# 1. Add domain to Vercel project
vercel domains add "$DOMAIN"
# 2. Set alias to point domain to latest deployment
PROD_URL=$(vercel ls --prod 2>&1 | grep -oE 'https://[^ ]+\.vercel\.app' | head -1)
vercel alias set "$PROD_URL" "$DOMAIN"
CRITICAL: vercel domains add alone is NOT enough. You MUST also run
vercel alias set to actually route traffic. Without it, the domain returns
ERR_CONNECTION_CLOSED.
Step 5: Auto-Configure Cloudflare DNS
Requires: CLOUDFLARE_API_KEY env var (API Token with DNS edit permission).
# Extract base domain and subdomain
# e.g. "sbti.example.com" → base="example.com", sub="sbti"
BASE_DOMAIN=$(echo "$DOMAIN" | awk -F. '{print $(NF-1)"."$NF}')
SUBDOMAIN=$(echo "$DOMAIN" | sed "s/\.$BASE_DOMAIN$//")
# 1. Get zone ID
ZONE_ID=$(curl -s "https://api.cloudflare.com/client/v4/zones?name=$BASE_DOMAIN" \
-H "Authorization: Bearer $CLOUDFLARE_API_KEY" \
-H "Content-Type: application/json" | python3 -c "import sys,json; print(json.load(sys.stdin)['result'][0]['id'])")
# 2. Check if record already exists
EXISTING=$(curl -s "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?name=$DOMAIN&type=CNAME" \
-H "Authorization: Bearer $CLOUDFLARE_API_KEY" | python3 -c "import sys,json; r=json.load(sys.stdin)['result']; print(r[0]['id'] if r else '')")
# 3. Create or update CNAME → cname.vercel-dns.com
if [ -z "$EXISTING" ]; then
curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
-H "Authorization: Bearer $CLOUDFLARE_API_KEY" \
-H "Content-Type: application/json" \
--data "{\"type\":\"CNAME\",\"name\":\"$SUBDOMAIN\",\"content\":\"cname.vercel-dns.com\",\"ttl\":1,\"proxied\":false}"
else
curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$EXISTING" \
-H "Authorization: Bearer $CLOUDFLARE_API_KEY" \
-H "Content-Type: application/json" \
--data "{\"type\":\"CNAME\",\"name\":\"$SUBDOMAIN\",\"content\":\"cname.vercel-dns.com\",\"ttl\":1,\"proxied\":false}"
fi
IMPORTANT: proxied must be false (DNS only). Cloudflare proxy conflicts
with Vercel's SSL certificate provisioning.
If CLOUDFLARE_API_KEY is not set, print manual DNS instructions instead:
Add DNS record:
Type: CNAME
Name: <subdomain>
Target: cname.vercel-dns.com
Proxy: OFF (DNS only)
Step 6: Verify
# Wait for DNS + SSL propagation
sleep 5
HTTP_CODE=$(curl -sI "https://$DOMAIN" -o /dev/null -w '%{http_code}')
if [ "$HTTP_CODE" = "200" ]; then
echo "✓ $DOMAIN is live"
else
echo "⚠ HTTP $HTTP_CODE — SSL may still be provisioning, try again in 1-2 min"
fi
Step 7: Output Summary
✓ Framework: vite
✓ Deployed: https://xxx.vercel.app
✓ Domain: https://sbti.example.com
✓ DNS: CNAME sbti → cname.vercel-dns.com (Cloudflare)
✓ Settings: https://vercel.com/<scope>/<project>/settings
Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
| ERR_CONNECTION_CLOSED | Domain added but no alias set | Run vercel alias set <url> <domain> |
| "Project names must be lowercase" | package.json name invalid | Fix name field |
| SSL not provisioning | Cloudflare proxy ON | Set DNS to "DNS only" (no orange cloud) |
| 404 on sub-routes | SPA missing rewrites | Add vercel.json with rewrites |
| DNS resolves to 198.18.x.x | Local proxy (Clash etc.) | Normal — check with dig @8.8.8.8 |
CLOUDFLARE_API_KEY not found |
Token not in env | Add to ~/.zshrc: export CLOUDFLARE_API_KEY=... |
Runtime context (shared)
运行前读取本 Skill 包的 skill.yaml,由宿主提供 skill-runtime/v1 上下文。字段解析顺序为:当前请求、项目上下文、个人 Preferences、品牌 Profile、通用默认值。
- 只使用 Manifest 声明的字段;Profile 保存公开品牌事实,Preferences 保存个人工作偏好。
required: true字段缺失时,按 Manifest 的问题配置向用户提出一个聚焦问题;用户明确同意后再保存回答。- 报错提供可复制的
context_id、字段路径与来源,诊断内容避开秘密、完整私人路径和原始配置。
通用反馈闭环
用户在 Skill 驱动任务中提出修改意见时,继续当前产物前必须执行:
- 先判断意见是
task-specific(仅本次)还是reusable(可跨任务复用)。 task-specific只修改当前任务,不改 Skill。reusable先确定作用域:领域规则先更新对应 canonical Skill;适用于所有 Skill 的规则先更新共享规范。- 完成规则更新、版本、lint 与分发核验后,再把修改应用到当前任务。
reusable修改会使此前的“确认”“继续”“发吧”失效;完成当前产物修改和回读后必须停下,等待用户下一步指示,不自动进入发布、提交或其他外部写入。
Files (skills)
-
CHANGELOG.md 512 B
# Changelog All notable changes to this skill are documented here. Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) · Versioning: [SemVer](https://semver.org/) ## [2.1.0] - 2026-08-24 ### Added - add the shared feedback-classification and approval-invalidation gate used by every LovStudio Skill ## [2.0.2] - 2026-05-07 ### Fixed - document confirmation before deployment actions ## [2.0.1] - 2026-05-07 ### Fixed - add release metadata - add README version badge and changelog entry -
README.md 1.4 KB
# Vercel 部署助手 · Vercel Deployer  One-command frontend deployment to Vercel with automatic custom domain + Cloudflare DNS setup. Part of [skill-publisher/skills](https://example.com/skills/skills) — by [example.com](https://example.com) ## Install ```bash npx skills add skill-publisher/skills --skill lov-deploy-to-vercel -y -g ``` Requires: `vercel` CLI, `curl`, `python3` ## Usage ``` /deploy-vercel # deploy to Vercel (auto-detect framework) /deploy-vercel sbti.example.com # deploy + configure custom domain + DNS /deploy-vercel --preview # preview deployment only ``` ## What It Does 1. Detects framework (Vite / Next.js / CRA / static) 2. Creates `vercel.json` SPA rewrites if needed 3. Deploys to Vercel production 4. Adds custom domain + sets alias 5. Auto-configures Cloudflare DNS CNAME record 6. Verifies site is live ## Options | Option | Description | |--------|-------------| | `<domain>` | Custom domain (e.g. `app.example.com`) | | `--preview` | Preview deploy only | | `--no-dns` | Skip Cloudflare DNS setup | | `--link-only` | Link project without deploying | ## Environment | Variable | Required | Description | |----------|----------|-------------| | `CLOUDFLARE_API_KEY` | For DNS auto-config | Cloudflare API Token with DNS:Edit | ## License MIT -
SKILL.md 7.6 KB
--- name: lov-deploy-to-vercel category: Dev Tools tagline: "Deploy frontend to Vercel with auto Cloudflare DNS + custom domain setup." description: > Deploy frontend projects to Vercel with automatic custom domain setup. Handles Vite, Next.js, CRA, and static sites. Auto-configures Cloudflare DNS CNAME records and Vercel domain aliases. Supports SPA routing via vercel.json. Trigger when user says "deploy to vercel", "部署到 vercel", "vercel deploy", or mentions a *.example.com / custom domain with vercel deployment. license: MIT compatibility: > Requires vercel CLI (`npm i -g vercel`), gh CLI, and curl. Cloudflare DNS auto-config requires CLOUDFLARE_API_KEY env var. metadata: author: contributors version: "2.1.0" tags: deploy vercel cloudflare dns frontend --- # Vercel 部署助手 · Vercel Deployer Deploy frontend projects to Vercel with automatic custom domain and DNS setup. ## When to Use - User says "deploy to vercel" or "部署到 xxx.example.com" - After building a frontend project that needs hosting - When setting up a custom domain on an existing Vercel deployment ## Arguments Pass via `$ARGUMENTS`: | Argument | Example | Description | |----------|---------|-------------| | `<domain>` | `sbti.example.com` | Custom domain to configure | | `--preview` | | Deploy preview only (skip `--prod`) | | `--no-dns` | | Skip Cloudflare DNS auto-config | | `--link-only` | | Only link project, don't deploy | ## Workflow ### Step 1: Detect Project Type ```bash if [ -f "vite.config.ts" ] || [ -f "vite.config.js" ]; then FRAMEWORK="vite" elif [ -f "next.config.js" ] || [ -f "next.config.mjs" ]; then FRAMEWORK="next" elif grep -q "react-scripts" package.json 2>/dev/null; then FRAMEWORK="cra" else FRAMEWORK="static" fi ``` ### Step 2: Ensure vercel.json for SPA For Vite/CRA (SPA) projects, create `vercel.json` if missing: ```json { "rewrites": [ { "source": "/(.*)", "destination": "/" } ] } ``` **Skip for Next.js** — it handles routing natively. ### Step 3: Deploy to Vercel Before running a production deployment, use `AskUserQuestion` if the target project, production/non-production mode, or custom domain is unclear. If the user already explicitly requested production deployment for this project, proceed. ```bash # Check CLI vercel --version || npm i -g vercel # Deploy (use project name from package.json "name" field) # IMPORTANT: package.json "name" must be lowercase, no special chars PROJECT_NAME=$(node -p "require('./package.json').name" 2>/dev/null || basename "$PWD") vercel --yes --prod ``` **Known issue**: If `package.json` name contains uppercase or invalid chars, vercel will error with "Project names must be lowercase". Fix the name first. ### Step 4: Configure Custom Domain (if provided) ```bash DOMAIN="<user-provided-domain>" # e.g. sbti.example.com # 1. Add domain to Vercel project vercel domains add "$DOMAIN" # 2. Set alias to point domain to latest deployment PROD_URL=$(vercel ls --prod 2>&1 | grep -oE 'https://[^ ]+\.vercel\.app' | head -1) vercel alias set "$PROD_URL" "$DOMAIN" ``` **CRITICAL**: `vercel domains add` alone is NOT enough. You MUST also run `vercel alias set` to actually route traffic. Without it, the domain returns ERR_CONNECTION_CLOSED. ### Step 5: Auto-Configure Cloudflare DNS **Requires**: `CLOUDFLARE_API_KEY` env var (API Token with DNS edit permission). ```bash # Extract base domain and subdomain # e.g. "sbti.example.com" → base="example.com", sub="sbti" BASE_DOMAIN=$(echo "$DOMAIN" | awk -F. '{print $(NF-1)"."$NF}') SUBDOMAIN=$(echo "$DOMAIN" | sed "s/\.$BASE_DOMAIN$//") # 1. Get zone ID ZONE_ID=$(curl -s "https://api.cloudflare.com/client/v4/zones?name=$BASE_DOMAIN" \ -H "Authorization: Bearer $CLOUDFLARE_API_KEY" \ -H "Content-Type: application/json" | python3 -c "import sys,json; print(json.load(sys.stdin)['result'][0]['id'])") # 2. Check if record already exists EXISTING=$(curl -s "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?name=$DOMAIN&type=CNAME" \ -H "Authorization: Bearer $CLOUDFLARE_API_KEY" | python3 -c "import sys,json; r=json.load(sys.stdin)['result']; print(r[0]['id'] if r else '')") # 3. Create or update CNAME → cname.vercel-dns.com if [ -z "$EXISTING" ]; then curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \ -H "Authorization: Bearer $CLOUDFLARE_API_KEY" \ -H "Content-Type: application/json" \ --data "{\"type\":\"CNAME\",\"name\":\"$SUBDOMAIN\",\"content\":\"cname.vercel-dns.com\",\"ttl\":1,\"proxied\":false}" else curl -s -X PUT "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$EXISTING" \ -H "Authorization: Bearer $CLOUDFLARE_API_KEY" \ -H "Content-Type: application/json" \ --data "{\"type\":\"CNAME\",\"name\":\"$SUBDOMAIN\",\"content\":\"cname.vercel-dns.com\",\"ttl\":1,\"proxied\":false}" fi ``` **IMPORTANT**: `proxied` must be `false` (DNS only). Cloudflare proxy conflicts with Vercel's SSL certificate provisioning. If `CLOUDFLARE_API_KEY` is not set, print manual DNS instructions instead: ``` Add DNS record: Type: CNAME Name: <subdomain> Target: cname.vercel-dns.com Proxy: OFF (DNS only) ``` ### Step 6: Verify ```bash # Wait for DNS + SSL propagation sleep 5 HTTP_CODE=$(curl -sI "https://$DOMAIN" -o /dev/null -w '%{http_code}') if [ "$HTTP_CODE" = "200" ]; then echo "✓ $DOMAIN is live" else echo "⚠ HTTP $HTTP_CODE — SSL may still be provisioning, try again in 1-2 min" fi ``` ### Step 7: Output Summary ``` ✓ Framework: vite ✓ Deployed: https://xxx.vercel.app ✓ Domain: https://sbti.example.com ✓ DNS: CNAME sbti → cname.vercel-dns.com (Cloudflare) ✓ Settings: https://vercel.com/<scope>/<project>/settings ``` ## Troubleshooting | Problem | Cause | Fix | |---------|-------|-----| | ERR_CONNECTION_CLOSED | Domain added but no alias set | Run `vercel alias set <url> <domain>` | | "Project names must be lowercase" | package.json name invalid | Fix name field | | SSL not provisioning | Cloudflare proxy ON | Set DNS to "DNS only" (no orange cloud) | | 404 on sub-routes | SPA missing rewrites | Add vercel.json with rewrites | | DNS resolves to 198.18.x.x | Local proxy (Clash etc.) | Normal — check with `dig @8.8.8.8` | | `CLOUDFLARE_API_KEY` not found | Token not in env | Add to `~/.zshrc`: `export CLOUDFLARE_API_KEY=...` | ## Runtime context (shared) 运行前读取本 Skill 包的 `skill.yaml`,由宿主提供 `skill-runtime/v1` 上下文。字段解析顺序为:当前请求、项目上下文、个人 Preferences、品牌 Profile、通用默认值。 - 只使用 Manifest 声明的字段;Profile 保存公开品牌事实,Preferences 保存个人工作偏好。 - `required: true` 字段缺失时,按 Manifest 的问题配置向用户提出一个聚焦问题;用户明确同意后再保存回答。 - 报错提供可复制的 `context_id`、字段路径与来源,诊断内容避开秘密、完整私人路径和原始配置。 ## 通用反馈闭环 用户在 Skill 驱动任务中提出修改意见时,继续当前产物前必须执行: 1. 先判断意见是 `task-specific`(仅本次)还是 `reusable`(可跨任务复用)。 2. `task-specific` 只修改当前任务,不改 Skill。 3. `reusable` 先确定作用域:领域规则先更新对应 canonical Skill;适用于所有 Skill 的规则先更新共享规范。 4. 完成规则更新、版本、lint 与分发核验后,再把修改应用到当前任务。 5. `reusable` 修改会使此前的“确认”“继续”“发吧”失效;完成当前产物修改和回读后必须停下,等待用户下一步指示,不自动进入发布、提交或其他外部写入。 -
skill.yaml 847 B
schema: skill-manifest/v1 id: lov-deploy-to-vercel version: "2.1.0" runtime: skill-runtime/v1 context: profile: fields: - path: identity.name required: false question: 如果本次输出需要品牌身份,请提供品牌名称。 - path: identity.logo required: false question: 如果需要使用品牌 Logo,请提供 Logo 地址或文件路径。 - path: brand.tone required: false question: 如果已有品牌语气或审美关键词,请提供它们。 preferences: namespace: lov_deploy_to_vercel fields: - path: user.language required: false question: 希望使用哪种语言输出? - path: user.timezone required: false question: 需要使用哪个时区处理日期和时间? interaction: ask_missing: true max_questions: 1
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.