Claude Skills Guide

Claude Skills with Vercel Deployment Automation

Vercel’s deployment platform hooks into your Git workflow, but the gap between pushing code and knowing whether your deployment is production-ready still requires human judgment. Claude skills fill that gap: the tdd skill catches missing tests before deployment, frontend-design flags accessibility regressions, pdf generates release notes, and supermemory tracks deployment history for incident context. This guide covers Claude skills with Vercel deployment automation from Vercel API setup through full pre- and post-deployment pipelines.

What You Will Build

Prerequisites

Step 1: Get Your Vercel API Token

  1. Go to vercel.com/account/tokens
  2. Create a token with Full Account scope (or project-scoped for tighter security)
  3. Store it as VERCEL_TOKEN in your environment

Step 2: Set Up the Project

mkdir claude-vercel-automation && cd claude-vercel-automation
npm init -y
npm install @anthropic-ai/sdk dotenv node-fetch

Create .env:

VERCEL_TOKEN=your_vercel_token
VERCEL_TEAM_ID=your_team_id_or_empty
VERCEL_PROJECT_ID=your_project_id
ANTHROPIC_API_KEY=your_claude_api_key
GITHUB_TOKEN=your_github_token

Find your project ID in Vercel project settings under the General tab.

Step 3: Vercel API Client

require('dotenv').config();
const fetch = require('node-fetch');

const VERCEL_BASE = 'https://api.vercel.com';
const headers = {
  Authorization: `Bearer ${process.env.VERCEL_TOKEN}`,
  'Content-Type': 'application/json',
};
const teamParam = process.env.VERCEL_TEAM_ID
  ? `?teamId=${process.env.VERCEL_TEAM_ID}`
  : '';

async function getLatestDeployment(projectId) {
  const resp = await fetch(
    `${VERCEL_BASE}/v6/deployments${teamParam}&projectId=${projectId}&limit=1`,
    { headers }
  );
  const data = await resp.json();
  return data.deployments?.[0];
}

async function getDeploymentStatus(deploymentId) {
  const resp = await fetch(
    `${VERCEL_BASE}/v13/deployments/${deploymentId}${teamParam}`,
    { headers }
  );
  return resp.json();
}

async function cancelDeployment(deploymentId) {
  await fetch(
    `${VERCEL_BASE}/v12/deployments/${deploymentId}/cancel${teamParam}`,
    { method: 'PATCH', headers }
  );
}

Step 4: Pre-Deployment TDD Gate

Run the tdd skill on staged changes before Vercel deploys:

const Anthropic = require('@anthropic-ai/sdk');
const { execSync } = require('child_process');

const claude = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });

async function runTDDGate(diffContent) {
  const message = await claude.messages.create({
    model: 'claude-opus-4-6',
    max_tokens: 1024,
    system: `You are the TDD skill for Claude Code. You are a pre-deployment gate.
Analyze the provided diff for critical test coverage issues.
Return JSON: { "approved": true/false, "risk_level": "low|medium|high", "issues": [], "blocking_reason": "" }
Approve unless there are CRITICAL untested code paths in business logic.`,
    messages: [{
      role: 'user',
      content: `Review this diff for deployment approval:\n\n${diffContent.slice(0, 8000)}`,
    }],
  });
  
  try {
    return JSON.parse(message.content[0].text);
  } catch {
    return { approved: true, risk_level: 'unknown', issues: [], blocking_reason: '' };
  }
}

async function preDeploymentCheck() {
  console.log('Running pre-deployment TDD gate...');
  
  // Get diff from last merge commit
  const diff = execSync('git diff HEAD~1 -- "*.ts" "*.tsx" "*.js"').toString();
  
  if (!diff.trim()) {
    console.log('No code changes detected — skipping TDD gate');
    return { approved: true };
  }
  
  const result = await runTDDGate(diff);
  
  console.log(`TDD Gate: ${result.approved ? 'APPROVED' : 'BLOCKED'} (${result.risk_level} risk)`);
  
  if (!result.approved) {
    console.error('Deployment blocked by TDD gate:');
    console.error(result.blocking_reason);
    result.issues?.forEach(issue => console.error(`  - ${issue}`));
  }
  
  return result;
}

Step 5: Integrate with GitHub Actions for Vercel

Use the gate in your GitHub Actions workflow. This runs before Vercel’s automatic deployment:

# .github/workflows/deploy.yml
name: Deploy with Claude Gate

on:
  push:
    branches: [main]

jobs:
  claude-gate:
    runs-on: ubuntu-latest
    outputs:
      approved: ${{ steps.gate.outputs.approved }}

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Run Claude TDD gate
        id: gate
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          node scripts/pre-deploy-gate.js
          echo "approved=true" >> $GITHUB_OUTPUT
        continue-on-error: true

  deploy:
    needs: claude-gate
    if: needs.claude-gate.outputs.approved == 'true'
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Deploy to Vercel
        env:
          VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
          VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
          VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
        run: |
          npm install -g vercel
          vercel pull --yes --environment=production --token=$VERCEL_TOKEN
          vercel build --prod --token=$VERCEL_TOKEN
          vercel deploy --prebuilt --prod --token=$VERCEL_TOKEN

Step 6: Post-Deployment Frontend Design Check

After deployment, validate the live site with the frontend-design skill:

async function postDeploymentCheck(deploymentUrl) {
  // Fetch the deployed page HTML
  const resp = await fetch(deploymentUrl);
  const html = await resp.text();
  
  // Extract visible text and structure (simplified)
  const textContent = html
    .replace(/<script[\s\S]*?<\/script>/gi, '')
    .replace(/<style[\s\S]*?<\/style>/gi, '')
    .replace(/<[^>]+>/g, ' ')
    .replace(/\s+/g, ' ')
    .trim()
    .slice(0, 4000);
  
  const message = await claude.messages.create({
    model: 'claude-opus-4-6',
    max_tokens: 1024,
    system: `You are the frontend-design skill for Claude Code. 
Review this deployed page for: missing meta tags, poor heading structure, 
obvious accessibility issues, and broken content. Return JSON:
{ "passed": true/false, "issues": [], "warnings": [] }`,
    messages: [{
      role: 'user',
      content: `Check this deployed page:\nURL: ${deploymentUrl}\n\nContent:\n${textContent}`,
    }],
  });
  
  try {
    return JSON.parse(message.content[0].text);
  } catch {
    return { passed: true, issues: [], warnings: [] };
  }
}

Step 7: Generate Changelog with PDF Skill

async function generateChangelog(fromCommit, toCommit = 'HEAD') {
  const { execSync } = require('child_process');
  
  const commits = execSync(
    `git log ${fromCommit}..${toCommit} --pretty=format:"%h %s" --no-merges`
  ).toString();
  
  const message = await claude.messages.create({
    model: 'claude-opus-4-6',
    max_tokens: 1024,
    system: `You are the PDF processing skill for Claude Code. 
Generate a user-facing changelog from commit messages. 
Group by: Features, Bug Fixes, Performance, Other.
Write for end users, not developers. Skip internal refactors.`,
    messages: [{
      role: 'user',
      content: `Generate changelog from these commits:\n\n${commits}`,
    }],
  });
  
  return message.content[0].text;
}

Step 8: Store Deployment History with Supermemory

Track deployments for incident response:

async function recordDeployment(deployment, gateResult, checkResult) {
  const context = `
Deployment ${deployment.url}
Commit: ${deployment.meta?.githubCommitSha?.slice(0, 8)}
Time: ${new Date().toISOString()}
TDD Gate: ${gateResult.approved ? 'PASSED' : 'BLOCKED'} (${gateResult.risk_level})
Post-Check: ${checkResult.passed ? 'PASSED' : 'FAILED'}
Issues: ${[...gateResult.issues || [], ...checkResult.issues || []].join(', ') || 'None'}
  `.trim();
  
  await claude.messages.create({
    model: 'claude-opus-4-6',
    max_tokens: 256,
    system: `You are the supermemory skill for Claude Code. Acknowledge storage of deployment context.`,
    messages: [{ role: 'user', content: `Store this deployment record:\n\n${context}` }],
  });
  
  // In production, also write to a database
  console.log('Deployment recorded in supermemory');
}

Step 9: Full Pipeline Script

async function fullDeploymentPipeline() {
  const projectId = process.env.VERCEL_PROJECT_ID;
  
  // 1. Pre-deployment gate
  const gateResult = await preDeploymentCheck();
  if (!gateResult.approved) {
    process.exit(1); // Block deployment in CI
  }
  
  // 2. Deployment happens via Vercel (triggered by git push)
  // Wait for it to complete
  console.log('Waiting for Vercel deployment...');
  await new Promise(r => setTimeout(r, 30000)); // In practice, poll the API
  
  // 3. Get latest deployment
  const deployment = await getLatestDeployment(projectId);
  
  // 4. Post-deployment check
  const checkResult = await postDeploymentCheck(`https://${deployment.url}`);
  console.log(`Post-deployment: ${checkResult.passed ? 'PASSED' : 'ISSUES FOUND'}`);
  
  // 5. Generate changelog
  const changelog = await generateChangelog('HEAD~5');
  console.log('Changelog generated');
  
  // 6. Record in supermemory
  await recordDeployment(deployment, gateResult, checkResult);
  
  return { gateResult, checkResult, changelog };
}

fullDeploymentPipeline().catch(console.error);

Conclusion

Claude skills with Vercel deployment automation creates an intelligent deployment pipeline that catches issues before they reach production. The tdd skill acts as a quality gate, frontend-design validates the live result, pdf generates stakeholder-ready changelogs, and supermemory builds deployment history for incident response. The GitHub Actions integration in Step 5 is the practical starting point — add post-deployment checks once your gate is running reliably.


Built by theluckystrike — More at zovo.one