
Before And After
- 3 installs
- Updated February 15, 2026
- vijaykpatel/favorite_skills_and_plugins
This is a copy of before-and-after by vercel-labs - installs and ranking accrue to the original listing.
Helps with ai & agent building tasks.
About
before-and-after is a Claude Code skill for ai & agent building. It helps developers move faster with AI-assisted coding.
- before-and-after
- AI & Agent Building
- AI-coding skill
Before And After by the numbers
- 3 all-time installs (skills.sh)
- Data as of Jul 7, 2026 (Skillselion catalog sync)
npx skills add https://github.com/vijaykpatel/favorite_skills_and_plugins --skill before-and-afterAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 3 |
|---|---|
| Last updated | February 15, 2026 |
| Repository | vijaykpatel/favorite_skills_and_plugins ↗ |
What it does
Helps with ai & agent building tasks.
Files
Before-After Screenshot Skill
Package: @vercel/before-and-afterNever use before-and-after (wrong package).Agent Behavior Rules
DO NOT:
- Switch git branches, stash changes, start dev servers, or assume what "before" is
- Use
--fullunless user explicitly asks for full page / full scroll capture
DO:
- Use
--markdownwhen user wants PR integration or markdown output - Use
--mobile/--tabletif user mentions phone, mobile, tablet, responsive, etc. - Assume current state is After
- If user provides only one URL or says "PR screenshots" without URLs, ASK: "What URL should I use for the 'before' state? (production URL, preview deployment, or another local port)"
Execution Order (MUST follow)
1. Pre-flight — which before-and-after || npm install -g @vercel/before-and-after 2. Protection check — if .vercel.app URL: curl -s -o /dev/null -w "%{http_code}" "<url>" (401/403 = protected) 3. Capture — before-and-after "<before-url>" "<after-url>" 4. Upload — ./scripts/upload-and-copy.sh <before.png> <after.png> --markdown 5. PR integration — optionally gh pr edit to append markdown
Never skip steps 1-2.
Quick Reference
# Basic usage
before-and-after <before-url> <after-url>
# With selector
before-and-after url1 url2 ".hero-section"
# Different selectors for each
before-and-after url1 url2 ".old-card" ".new-card"
# Viewports
before-and-after url1 url2 --mobile # 375x812
before-and-after url1 url2 --tablet # 768x1024
before-and-after url1 url2 --full # full scroll
# From existing images
before-and-after before.png after.png --markdown
# Via npx (use full package name!)
npx @vercel/before-and-after url1 url2| Flag | Description |
|---|---|
-m, --mobile | Mobile viewport (375x812) |
-t, --tablet | Tablet viewport (768x1024) |
--size <WxH> | Custom viewport |
-f, --full | Full scrollable page |
-s, --selector | CSS selector to capture |
-o, --output | Output directory (default: ~/Downloads) |
--markdown | Upload images & output markdown table |
--upload-url <url> | Custom upload endpoint (default: 0x0.st) |
Image Upload
# Default (0x0.st - no signup needed)
./scripts/upload-and-copy.sh before.png after.png --markdown
# GitHub Gist
IMAGE_ADAPTER=gist ./scripts/upload-and-copy.sh before.png after.png --markdownVercel Deployment Protection
If .vercel.app URL returns 401/403:
1. Check Vercel CLI: which vercel && vercel whoami 2. If available: vercel inspect <url> to get bypass token 3. If not: Tell user to provide bypass token, take manual screenshots, or disable protection
PR Integration
# Check for gh CLI
which gh
# Get current PR
gh pr view --json number,body
# Append screenshots to PR body
gh pr edit <number> --body "<existing-body>
## Before and After
<generated-markdown>"If no gh CLI: output markdown and tell user to paste manually.
Error Reference
| Error | Fix |
|---|---|
command not found | npm install -g @vercel/before-and-after |
could not determine executable | Use npx @vercel/before-and-after (full name) |
| 401/403 on .vercel.app | See Vercel protection section |
| Element not found | Verify selector exists on page |
#!/bin/bash
# 0x0.st adapter - Free, no-signup file hosting
# https://0x0.st
#
# Usage: ./0x0st.sh <file>
# Output: URL to uploaded file (stdout)
#
# Notes:
# - Files expire after 365 days (or sooner for larger files)
# - Max file size: 512 MiB
# - No authentication required
set -e
FILE="$1"
if [[ -z "$FILE" ]]; then
echo "Usage: $0 <file>" >&2
exit 1
fi
if [[ ! -f "$FILE" ]]; then
echo "Error: File not found: $FILE" >&2
exit 1
fi
# Upload to 0x0.st - returns the URL directly
URL=$(curl -s -A "before-after-cli/1.0" -F "file=@$FILE" https://0x0.st)
# Validate we got a URL back
if [[ ! "$URL" =~ ^https?:// ]]; then
echo "Error: Upload failed. Response: $URL" >&2
exit 1
fi
echo "$URL"
#!/bin/bash
# Custom blob storage adapter - For self-hosted or custom endpoints
#
# Usage: ./blob.sh <file>
# Output: URL to uploaded file (stdout)
#
# Environment:
# BLOB_UPLOAD_URL Required. URL endpoint for uploading images
#
# Notes:
# - Expects endpoint to accept multipart form upload with "file" field
# - Expects response to be either:
# - Plain text URL
# - JSON with "url" field
set -e
FILE="$1"
if [[ -z "$FILE" ]]; then
echo "Usage: $0 <file>" >&2
exit 1
fi
if [[ ! -f "$FILE" ]]; then
echo "Error: File not found: $FILE" >&2
exit 1
fi
if [[ -z "$BLOB_UPLOAD_URL" ]]; then
echo "Error: BLOB_UPLOAD_URL environment variable not set" >&2
echo "" >&2
echo "Set it with:" >&2
echo " export BLOB_UPLOAD_URL='https://your-blob-service.com/upload'" >&2
exit 1
fi
# Upload file
RESPONSE=$(curl -s -X POST -F "file=@$FILE" "$BLOB_UPLOAD_URL")
# Try to extract URL from JSON response
URL=$(echo "$RESPONSE" | grep -o '"url"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/"url"[[:space:]]*:[[:space:]]*"//' | sed 's/"$//' || true)
# Fallback: maybe the response is just the URL
if [[ -z "$URL" ]]; then
URL="$RESPONSE"
fi
# Validate we got a URL back
if [[ ! "$URL" =~ ^https?:// ]]; then
echo "Error: Upload failed. Response: $RESPONSE" >&2
exit 1
fi
echo "$URL"
#!/bin/bash
# GitHub Gist adapter - Upload images via gh CLI
#
# Usage: ./gist.sh <file>
# Output: Raw URL to uploaded file (stdout)
#
# Requirements:
# - gh CLI installed and authenticated
#
# Notes:
# - Creates a public gist for each upload
# - Files persist indefinitely
# - Requires GitHub authentication
set -e
FILE="$1"
if [[ -z "$FILE" ]]; then
echo "Usage: $0 <file>" >&2
exit 1
fi
if [[ ! -f "$FILE" ]]; then
echo "Error: File not found: $FILE" >&2
exit 1
fi
# Check gh is available
if ! command -v gh &> /dev/null; then
echo "Error: gh CLI not found. Install from https://cli.github.com" >&2
exit 1
fi
# Create gist and capture output
GIST_OUTPUT=$(gh gist create "$FILE" --public 2>&1)
# Extract the gist URL from output
GIST_URL=$(echo "$GIST_OUTPUT" | grep -o 'https://gist.github.com/[^ ]*' | head -1)
if [[ -z "$GIST_URL" ]]; then
echo "Error: Failed to create gist. Output: $GIST_OUTPUT" >&2
exit 1
fi
# Convert to raw URL
# Format: https://gist.github.com/user/id -> https://gist.githubusercontent.com/user/id/raw/filename
GIST_ID=$(echo "$GIST_URL" | sed 's|.*/||')
FILENAME=$(basename "$FILE")
# Get the raw URL via gh api
RAW_URL=$(gh api "gists/$GIST_ID" --jq ".files[\"$FILENAME\"].raw_url")
if [[ -z "$RAW_URL" ]]; then
echo "Error: Could not get raw URL for gist" >&2
exit 1
fi
echo "$RAW_URL"
#!/bin/bash
# capture.sh - Core screenshot capture using agent-browser
# Usage: ./capture.sh <url> <output.png> [--full] [--element <selector>]
#
# Options:
# --full Full page screenshot (viewport 1500x1000)
# --element <sel> Element screenshot (auto-sized to element bounds)
#
# Examples:
# ./capture.sh "http://localhost:3000" ~/Downloads/screenshot.png --full
# ./capture.sh "http://localhost:3000" ~/Downloads/button.png --element ".btn-primary"
set -e
URL="$1"
OUTPUT="$2"
shift 2
# Default to full page
MODE="full"
SELECTOR=""
# Parse options
while [[ $# -gt 0 ]]; do
case $1 in
--full)
MODE="full"
shift
;;
--element)
MODE="element"
SELECTOR="$2"
shift 2
;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done
if [[ -z "$URL" || -z "$OUTPUT" ]]; then
echo "Usage: $0 <url> <output.png> [--full] [--element <selector>]"
exit 1
fi
# Ensure output directory exists
mkdir -p "$(dirname "$OUTPUT")"
echo "Opening: $URL"
agent-browser:open "$URL"
echo "Waiting for page load..."
agent-browser:wait --load networkidle
if [[ "$MODE" == "element" && -n "$SELECTOR" ]]; then
echo "Capturing element: $SELECTOR"
agent-browser:screenshot --selector "$SELECTOR" --output "$OUTPUT"
else
echo "Capturing full page (1500x1000)"
agent-browser:screenshot --viewport 1500x1000 --output "$OUTPUT"
fi
echo "Screenshot saved: $OUTPUT"
#!/bin/bash
# upload-and-copy.sh - Upload images via pluggable storage adapters
# Usage: ./upload-and-copy.sh <before.png> <after.png> [--markdown]
#
# Options:
# --markdown Generate PR markdown table and copy to clipboard
#
# Environment:
# IMAGE_ADAPTER Storage adapter to use (default: 0x0st)
# Available: 0x0st, gist, blob
#
# Adapter-specific environment variables:
# blob: BLOB_UPLOAD_URL - Custom upload endpoint
#
# Examples:
# ./upload-and-copy.sh before.png after.png
# ./upload-and-copy.sh before.png after.png --markdown
# IMAGE_ADAPTER=gist ./upload-and-copy.sh before.png after.png --markdown
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ADAPTERS_DIR="$SCRIPT_DIR/adapters"
# Default adapter
IMAGE_ADAPTER="${IMAGE_ADAPTER:-0x0st}"
BEFORE_FILE="$1"
AFTER_FILE="$2"
shift 2 2>/dev/null || true
MARKDOWN_MODE=false
# Parse options
while [[ $# -gt 0 ]]; do
case $1 in
--markdown)
MARKDOWN_MODE=true
shift
;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done
# Validate inputs
if [[ -z "$BEFORE_FILE" || -z "$AFTER_FILE" ]]; then
echo "Usage: $0 <before.png> <after.png> [--markdown]"
echo ""
echo "Environment:"
echo " IMAGE_ADAPTER Storage adapter (default: 0x0st)"
echo " Available: 0x0st, gist, blob"
exit 1
fi
if [[ ! -f "$BEFORE_FILE" ]]; then
echo "Error: Before file not found: $BEFORE_FILE"
exit 1
fi
if [[ ! -f "$AFTER_FILE" ]]; then
echo "Error: After file not found: $AFTER_FILE"
exit 1
fi
# Validate adapter exists
ADAPTER_SCRIPT="$ADAPTERS_DIR/$IMAGE_ADAPTER.sh"
if [[ ! -f "$ADAPTER_SCRIPT" ]]; then
echo "Error: Unknown adapter: $IMAGE_ADAPTER"
echo ""
echo "Available adapters:"
for adapter in "$ADAPTERS_DIR"/*.sh; do
name=$(basename "$adapter" .sh)
echo " - $name"
done
exit 1
fi
# Upload function using adapter
upload_file() {
local file="$1"
local filename=$(basename "$file")
echo "Uploading: $filename (via $IMAGE_ADAPTER)" >&2
# Call the adapter script
"$ADAPTER_SCRIPT" "$file"
}
# Upload both files
echo "=== Uploading Screenshots ==="
BEFORE_URL=$(upload_file "$BEFORE_FILE")
AFTER_URL=$(upload_file "$AFTER_FILE")
echo ""
echo "Before URL: $BEFORE_URL"
echo "After URL: $AFTER_URL"
echo ""
if [[ "$MARKDOWN_MODE" == "true" ]]; then
# Generate PR markdown table (centered alignment for GitHub)
MARKDOWN="| Before | After |
|:------:|:-----:|
|  |  |"
echo "=== PR Markdown Table ==="
echo "$MARKDOWN"
echo ""
# Copy to clipboard (macOS)
if command -v pbcopy &> /dev/null; then
echo "$MARKDOWN" | pbcopy
echo "Markdown table copied to clipboard!"
elif command -v xclip &> /dev/null; then
echo "$MARKDOWN" | xclip -selection clipboard
echo "Markdown table copied to clipboard!"
else
echo "(clipboard copy not available - install pbcopy or xclip)"
fi
else
# Copy URLs to clipboard
URLS="Before: $BEFORE_URL
After: $AFTER_URL"
if command -v pbcopy &> /dev/null; then
echo "$URLS" | pbcopy
echo "URLs copied to clipboard!"
elif command -v xclip &> /dev/null; then
echo "$URLS" | xclip -selection clipboard
echo "URLs copied to clipboard!"
else
echo "(clipboard copy not available - install pbcopy or xclip)"
fi
fi