vercel-deploy
Use when deploying an existing Vercel project to production non-interactively.
How do I install this agent skill?
npx skills add https://github.com/fellowship-dev/dogfooded-skills --skill vercel-deployIs this agent skill safe to install?
- Gen Agent Trust Hubpass
The skill facilitates the automated deployment of an existing Vercel project using the official Vercel CLI and API. It includes stages for environment preflight, project linking via file creation, deployment execution, and API polling for status. All operations target trusted or well-known services.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
What does this agent skill do?
Purpose
Deploy an existing Vercel project to production from a local directory. Designed to never hang, never create new projects, and never assume implicit state.
Required Secrets
Must be available as environment variables (from crew secret store):
VERCEL_TOKEN— Access token with deploy permissions (this is the only real secret)
Required Inputs
All inputs are passed as KEY=value pairs in the skill invocation or task description. Parse them from the args — never read these from environment variables.
DEPLOY_DIR— Path to the deployable directory (e.g.,./ui, repo root)PROD_DOMAIN— Expected production domain (e.g.,pylot.fellowship.dev)VERCEL_ORG_ID— Organization/team ID (starts withteam_)VERCEL_PROJECT_ID— Project ID (starts withprj_)
These values come from the repo playbook (GET /admin/playbooks/<repo>), not from secrets. The operator reads the playbook and passes them when invoking this skill. Deploy only through this skill's CLI flow — never rely on Vercel's Git auto-deploy integration; it bypasses the playbook-sourced inputs above and the checks in Forbidden Actions.
Optional Inputs
DEPLOY_BRANCH— Git branch to deploy from. Defaults tomainif not specified. Always checks out and pulls before deploying.DEPLOY_AUTHOR— Git name of a verified Vercel team member (e.g.,maxfindel). If provided along withDEPLOY_EMAIL, Stage 00 will fix the commit author before deploying. Only needed when Vercel enforces team membership on the commit author.DEPLOY_EMAIL— Git email matching the author. Required ifDEPLOY_AUTHORis set.
Forbidden Actions
- NEVER create a new Vercel project. If it doesn't exist, STOP and report failure.
- NEVER assume project auto-links. Always verify or write
.vercel/project.jsonexplicitly. - NEVER connect a git repo to the Vercel project. All deploys are CLI pushes, not git-triggered.
- NEVER run
vercelwithout--tokenand--yes. The CLI hangs forever without them. - NEVER run
vercel link. It prompts interactively. Write.vercel/project.jsondirectly. - NEVER run
vercel project createor equivalent. Separate procedure exists for that.
Stage Overview
| Stage | Name | Purpose |
|---|---|---|
| 00 | author-fix | Fix commit author if DEPLOY_AUTHOR is set (optional, skipped otherwise) |
| 01 | preflight | Verify secrets, tools, compute deploy context |
| 02 | link | Ensure .vercel/project.json exists; verify project via API |
| 03 | deploy | Run vercel deploy --prod non-interactively |
| 04 | poll | Poll Vercel API until deployment reaches READY or ERROR |
| 05 | verify | Confirm production domain is reachable, emit outcome |
Stage 00 — Author Fix (conditional)
Skip this stage if DEPLOY_AUTHOR is not provided. Not all Vercel projects enforce team membership on commit authors.
When Vercel does enforce it (TEAM_ACCESS_REQUIRED / seatBlock), bot accounts (e.g. fry-lobster) will block CLI deploys. The fix is an empty commit with a team member as author.
# Only run if DEPLOY_AUTHOR is set in the skill params
if [ -n "$DEPLOY_AUTHOR" ] && [ -n "$DEPLOY_EMAIL" ]; then
HEAD_AUTHOR=$(git log -1 --format='%an')
if [ "$HEAD_AUTHOR" != "$DEPLOY_AUTHOR" ]; then
echo "[vercel-deploy] HEAD author '$HEAD_AUTHOR' is not a Vercel team member."
echo "[vercel-deploy] Creating empty commit with author '$DEPLOY_AUTHOR' to unblock deploy."
git commit --allow-empty \
--author="$DEPLOY_AUTHOR <$DEPLOY_EMAIL>" \
-m "chore: vercel deploy author fix (empty commit)"
git push origin HEAD
fi
else
echo "[vercel-deploy] Stage 00 skipped — no DEPLOY_AUTHOR configured"
fi
Stage 01 — Preflight
Verify all secrets and inputs exist, tools are installed, and checkout the deploy branch.
# Verify required secret (from crew secret store — the only real secret)
[ -z "$VERCEL_TOKEN" ] && { echo "[vercel-deploy] MISSING secret: VERCEL_TOKEN"; exit 1; }
# Verify required inputs (parsed from skill args / task description — from the repo playbook)
[ -z "$DEPLOY_DIR" ] && { echo "[vercel-deploy] MISSING input: DEPLOY_DIR — check the repo playbook"; exit 1; }
[ -z "$PROD_DOMAIN" ] && { echo "[vercel-deploy] MISSING input: PROD_DOMAIN — check the repo playbook"; exit 1; }
[ -z "$VERCEL_ORG_ID" ] && { echo "[vercel-deploy] MISSING input: VERCEL_ORG_ID — check the repo playbook"; exit 1; }
[ -z "$VERCEL_PROJECT_ID" ] && { echo "[vercel-deploy] MISSING input: VERCEL_PROJECT_ID — check the repo playbook"; exit 1; }
# Optional: warn if author-fix params are incomplete (both or neither)
if [ -n "$DEPLOY_AUTHOR" ] && [ -z "$DEPLOY_EMAIL" ]; then
echo "[vercel-deploy] WARNING: DEPLOY_AUTHOR set but DEPLOY_EMAIL missing — Stage 00 will be skipped"
fi
# Verify vercel CLI is available
command -v vercel >/dev/null 2>&1 || command -v npx >/dev/null 2>&1 || {
echo "[vercel-deploy] vercel CLI not available — install with: npm i -g vercel"
exit 1
}
# Verify deploy directory exists
[ -d "$DEPLOY_DIR" ] || { echo "[vercel-deploy] DEPLOY_DIR not found: $DEPLOY_DIR"; exit 1; }
# Checkout and pull deploy branch
DEPLOY_BRANCH="${DEPLOY_BRANCH:-main}"
git checkout "$DEPLOY_BRANCH" && git pull origin "$DEPLOY_BRANCH" || {
echo "[vercel-deploy] Failed to checkout/pull $DEPLOY_BRANCH"
exit 1
}
# Save state for downstream stages
cat > /tmp/vercel-deploy-ctx.env <<EOF
DEPLOY_BRANCH=$DEPLOY_BRANCH
DEPLOY_DIR=$DEPLOY_DIR
PROD_DOMAIN=$PROD_DOMAIN
EOF
echo "[vercel-deploy] Stage 01 complete — preflight passed, branch: $DEPLOY_BRANCH"
Stage 02 — Link
Write .vercel/project.json directly (never use vercel link), then verify the project exists via API.
source /tmp/vercel-deploy-ctx.env
# Write project link file directly
mkdir -p "$DEPLOY_DIR/.vercel"
cat > "$DEPLOY_DIR/.vercel/project.json" <<EOF
{
"orgId": "$VERCEL_ORG_ID",
"projectId": "$VERCEL_PROJECT_ID"
}
EOF
# Verify project exists via Vercel API
PROJECT_JSON=$(curl -sf \
-H "Authorization: Bearer $VERCEL_TOKEN" \
"https://api.vercel.com/v9/projects/$VERCEL_PROJECT_ID?teamId=$VERCEL_ORG_ID")
[ -z "$PROJECT_JSON" ] && {
echo "[vercel-deploy] Stage 02 failed: project $VERCEL_PROJECT_ID not found — STOP, do not create"
exit 1
}
PROJECT_NAME=$(echo "$PROJECT_JSON" | python3 -c \
"import sys,json; d=json.load(sys.stdin); print(d.get('name','unknown'))" 2>/dev/null || echo "unknown")
echo "PROJECT_NAME=$PROJECT_NAME" >> /tmp/vercel-deploy-ctx.env
echo "[vercel-deploy] Stage 02 complete — linked to project '$PROJECT_NAME' ($VERCEL_PROJECT_ID)"
Stage 03 — Deploy
Run vercel deploy --prod non-interactively. Must use --token and --yes — without them the CLI hangs.
source /tmp/vercel-deploy-ctx.env
cd "$DEPLOY_DIR"
DEPLOY_OUTPUT=$(npx vercel deploy --prod --token="$VERCEL_TOKEN" --yes 2>&1)
DEPLOY_EXIT=$?
echo "$DEPLOY_OUTPUT"
if [ $DEPLOY_EXIT -ne 0 ]; then
echo "[vercel-deploy] Stage 03 failed: vercel deploy exited $DEPLOY_EXIT"
exit 1
fi
# Extract deployment URL (last https:// line in output)
DEPLOY_URL=$(echo "$DEPLOY_OUTPUT" | grep -E '^https://' | tail -1)
[ -z "$DEPLOY_URL" ] && {
echo "[vercel-deploy] Stage 03 failed: no deployment URL found in vercel output"
exit 1
}
echo "DEPLOY_URL=$DEPLOY_URL" >> /tmp/vercel-deploy-ctx.env
echo "[vercel-deploy] Stage 03 complete — deployment URL: $DEPLOY_URL"
Stage 04 — Poll
Poll the Vercel API until the deployment reaches READY or ERROR. Timeout after 120 seconds.
source /tmp/vercel-deploy-ctx.env
MAX_WAIT=120
ELAPSED=0
DEPLOY_HOST=$(echo "$DEPLOY_URL" | sed 's|https://||')
while [ $ELAPSED -lt $MAX_WAIT ]; do
DEPLOY_STATE=$(curl -sf \
-H "Authorization: Bearer $VERCEL_TOKEN" \
"https://api.vercel.com/v13/deployments?url=${DEPLOY_HOST}&teamId=$VERCEL_ORG_ID&limit=1" \
| python3 -c \
"import sys,json; d=json.load(sys.stdin); deps=d.get('deployments',[]); print(deps[0].get('state','UNKNOWN') if deps else 'NOT_FOUND')" \
2>/dev/null || echo "API_ERROR")
echo "[vercel-deploy] Deploy state: $DEPLOY_STATE (${ELAPSED}s elapsed)"
case "$DEPLOY_STATE" in
READY)
echo "[vercel-deploy] Stage 04 complete — deployment READY"
break
;;
ERROR|CANCELED)
echo "[vercel-deploy] Stage 04 failed: deployment reached terminal state $DEPLOY_STATE"
exit 1
;;
*)
sleep 10
ELAPSED=$((ELAPSED + 10))
;;
esac
done
[ $ELAPSED -ge $MAX_WAIT ] && {
echo "[vercel-deploy] Stage 04 failed: timed out after ${MAX_WAIT}s — last state: $DEPLOY_STATE"
exit 1
}
Stage 05 — Verify
Confirm the production domain is reachable (HTTP 200, 301, or 302), then emit outcome.
source /tmp/vercel-deploy-ctx.env
VERIFY_URL="https://$PROD_DOMAIN"
HTTP_STATUS=$(curl -sf -o /dev/null -w "%{http_code}" --max-time 15 "$VERIFY_URL" 2>/dev/null || echo "000")
echo "[vercel-deploy] $VERIFY_URL → HTTP $HTTP_STATUS"
case "$HTTP_STATUS" in
200|301|302)
echo "[vercel-deploy] Stage 05 complete — production domain verified"
;;
*)
echo "[vercel-deploy] Stage 05 failed: $VERIFY_URL returned HTTP $HTTP_STATUS"
exit 1
;;
esac
After the verification command, emit the matching resolved marker as your final full assistant line (not from Bash and not inside a fence):
- Verified: [pylot:$PYLOT_OUTCOME_NONCE] outcome="vercel-deploy succeeded" project=$PROJECT_NAME domain=$PROD_DOMAIN deploy_url=$DEPLOY_URL status=success
- Failed: [pylot:$PYLOT_OUTCOME_NONCE] outcome="vercel-deploy failed at stage 05: $PROD_DOMAIN returned HTTP $HTTP_STATUS" status=failed
Execution Model
- Follow stages in order. Each stage's checkpoint must pass before proceeding.
- Do not skip stages. Every checkpoint is verified.
- Fail fast. If any checkpoint fails, stop and report with
status=failed. - State is passed between stages via
/tmp/vercel-deploy-ctx.env.
Critical Rules
- Sequential execution only. Stage N's checkpoint must pass before starting stage N+1.
- No skipping. Every stage runs, every checkpoint is verified.
- Fail fast. If any checkpoint fails, stop and report.
- Every
vercelornpx vercelinvocation MUST include--token="$VERCEL_TOKEN" --yes.
Error Handling
If any stage fails:
- Print which stage failed and the exact error
- Include the Vercel API response body if available
- Emit:
[pylot:$PYLOT_OUTCOME_NONCE] outcome="vercel-deploy failed at stage <NN>: <reason>" status=failed - Do NOT retry automatically
Outcome
On success, emit:
[pylot:$PYLOT_OUTCOME_NONCE] outcome="vercel-deploy succeeded" project=$PROJECT_NAME domain=$PROD_DOMAIN deploy_url=$DEPLOY_URL status=success
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/fellowship-dev/dogfooded-skills/vercel-deploy">View vercel-deploy on skillZs</a>