performance-budget-enforcer

Define, measure, and enforce web performance budgets — bundle sizes, asset counts, image weights, third-party scripts. Fails CI when budgets are exceeded. Tracks trends across builds.

Safety Notice

This listing is from the official public ClawHub registry. Review SKILL.md and referenced scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "performance-budget-enforcer" with this command: npx skills add charlie-morrison/performance-budget-enforcer

Performance Budget Enforcer

Set performance budgets for web projects and enforce them in CI. Measures JS/CSS bundle sizes, image weights, font sizes, third-party script counts, and total transfer size. Compares against budgets, flags regressions, and tracks trends.

Use when: "check bundle size", "set performance budget", "are we over budget", "track asset size", "web performance audit", "lighthouse budget", or integrating perf checks into CI/CD.

Commands

1. measure — Measure Current Asset Sizes

Scan the build output directory and measure everything.

# Auto-detect build output directory
BUILD_DIR=""
for dir in dist build out .next/static public/build _site; do
  if [ -d "$dir" ]; then
    BUILD_DIR="$dir"
    break
  fi
done

if [ -z "$BUILD_DIR" ]; then
  echo "No build directory found. Run your build command first, or specify the directory."
  exit 1
fi

echo "Scanning: $BUILD_DIR"

JavaScript Bundles

# JS files with sizes (sorted largest first)
find "$BUILD_DIR" -name "*.js" -type f -exec du -b {} + 2>/dev/null | sort -rn | head -20
TOTAL_JS=$(find "$BUILD_DIR" -name "*.js" -type f -exec du -b {} + 2>/dev/null | awk '{s+=$1} END {print s+0}')
echo "Total JS: $TOTAL_JS bytes ($(echo "scale=1; $TOTAL_JS / 1024" | bc) KB)"

# Gzipped sizes (more realistic transfer size)
find "$BUILD_DIR" -name "*.js" -type f | while read f; do
  ORIG=$(wc -c < "$f")
  GZIP=$(gzip -c "$f" | wc -c)
  echo "$GZIP $ORIG $f"
done | sort -rn | head -10
TOTAL_JS_GZ=$(find "$BUILD_DIR" -name "*.js" -type f -exec sh -c 'gzip -c "$1" | wc -c' _ {} \; 2>/dev/null | awk '{s+=$1} END {print s+0}')
echo "Total JS (gzip): $TOTAL_JS_GZ bytes ($(echo "scale=1; $TOTAL_JS_GZ / 1024" | bc) KB)"

CSS Bundles

find "$BUILD_DIR" -name "*.css" -type f -exec du -b {} + 2>/dev/null | sort -rn | head -10
TOTAL_CSS=$(find "$BUILD_DIR" -name "*.css" -type f -exec du -b {} + 2>/dev/null | awk '{s+=$1} END {print s+0}')
echo "Total CSS: $TOTAL_CSS bytes ($(echo "scale=1; $TOTAL_CSS / 1024" | bc) KB)"

Images

find "$BUILD_DIR" -type f \( -name "*.png" -o -name "*.jpg" -o -name "*.jpeg" -o -name "*.gif" -o -name "*.svg" -o -name "*.webp" -o -name "*.avif" \) \
  -exec du -b {} + 2>/dev/null | sort -rn | head -15
TOTAL_IMG=$(find "$BUILD_DIR" -type f \( -name "*.png" -o -name "*.jpg" -o -name "*.jpeg" -o -name "*.gif" -o -name "*.svg" -o -name "*.webp" -o -name "*.avif" \) \
  -exec du -b {} + 2>/dev/null | awk '{s+=$1} END {print s+0}')
echo "Total images: $TOTAL_IMG bytes ($(echo "scale=1; $TOTAL_IMG / 1024" | bc) KB)"

# Flag unoptimized images (PNG > 100KB, JPG > 200KB without WebP alternative)
find "$BUILD_DIR" -name "*.png" -size +100k -type f 2>/dev/null
find "$BUILD_DIR" -name "*.jpg" -size +200k -type f 2>/dev/null

Fonts

find "$BUILD_DIR" -type f \( -name "*.woff" -o -name "*.woff2" -o -name "*.ttf" -o -name "*.otf" -o -name "*.eot" \) \
  -exec du -b {} + 2>/dev/null | sort -rn
TOTAL_FONT=$(find "$BUILD_DIR" -type f \( -name "*.woff" -o -name "*.woff2" -o -name "*.ttf" -o -name "*.otf" -o -name "*.eot" \) \
  -exec du -b {} + 2>/dev/null | awk '{s+=$1} END {print s+0}')
echo "Total fonts: $TOTAL_FONT bytes ($(echo "scale=1; $TOTAL_FONT / 1024" | bc) KB)"

# Flag non-woff2 fonts (should be woff2 in 2026)
find "$BUILD_DIR" -type f \( -name "*.ttf" -o -name "*.otf" -o -name "*.eot" -o -name "*.woff" \) 2>/dev/null

Total Transfer Size

TOTAL=$(find "$BUILD_DIR" -type f -exec du -b {} + 2>/dev/null | awk '{s+=$1} END {print s+0}')
echo "Total build output: $TOTAL bytes ($(echo "scale=1; $TOTAL / 1024 / 1024" | bc) MB)"
FILE_COUNT=$(find "$BUILD_DIR" -type f | wc -l)
echo "Total files: $FILE_COUNT"

2. budget — Define Performance Budget

Create or update a .perfbudget.json file in the project root.

Default budgets (adjust per project type):

{
  "budgets": {
    "js_total_kb": 300,
    "js_total_gzip_kb": 100,
    "js_single_file_kb": 150,
    "css_total_kb": 100,
    "css_single_file_kb": 50,
    "img_total_kb": 1000,
    "img_single_file_kb": 200,
    "font_total_kb": 200,
    "total_transfer_mb": 3,
    "total_file_count": 200,
    "third_party_scripts": 5
  },
  "presets": {
    "strict": {
      "js_total_gzip_kb": 50,
      "total_transfer_mb": 1
    },
    "mobile": {
      "js_total_gzip_kb": 70,
      "img_total_kb": 500,
      "total_transfer_mb": 2
    }
  }
}

Preset suggestions based on project type:

  • SPA (React/Vue/Svelte): JS 150KB gzip, total 2MB
  • Static site / blog: JS 50KB gzip, total 1MB
  • E-commerce: JS 200KB gzip, images 2MB, total 5MB
  • Dashboard / admin: JS 300KB gzip, total 5MB (internal tools can be larger)

3. check — Enforce Budget

Compare measurements against .perfbudget.json. This is the CI command.

# Read budget file
if [ ! -f ".perfbudget.json" ]; then
  echo "No .perfbudget.json found. Run 'budget' command first to create one."
  echo "Using default budgets..."
fi

For each metric, compare measured vs budget:

✅ JS total (gzip):   87 KB / 100 KB budget (87%)
✅ CSS total:          34 KB / 100 KB budget (34%)
⚠️  Images total:     890 KB / 1000 KB budget (89%) — approaching limit
❌ JS single file:    180 KB / 150 KB budget (120%) — OVER BUDGET
   └─ dist/vendor.chunk.js: 180 KB
❌ Total transfer:    3.4 MB / 3 MB budget (113%) — OVER BUDGET

RESULT: 2 budget violations found

Exit codes:

  • 0: All within budget
  • 1: One or more budgets exceeded
  • 2: Budget file missing (warning only)

4. trend — Track Size Over Time

Append current measurements to .perfbudget-history.json:

{
  "history": [
    {
      "date": "2026-04-28",
      "commit": "abc123",
      "branch": "main",
      "js_total_gzip_kb": 87,
      "css_total_kb": 34,
      "img_total_kb": 890,
      "total_transfer_mb": 2.1
    }
  ]
}

Display trend:

JS (gzip) over last 10 builds:
  Apr 20: 72 KB ██████████████
  Apr 21: 75 KB ███████████████
  Apr 23: 82 KB ████████████████
  Apr 25: 87 KB █████████████████   ↑ +20.8% in 8 days
  Budget: 100 KB ████████████████████ (limit)

Flag: "JS bundle grew 20.8% in 8 days — investigate recent additions."

5. third-party — Audit Third-Party Scripts

Scan HTML files and JS bundles for external domains:

# Find external script tags
rg -n 'src="https?://[^"]*"' -g '*.html' "$BUILD_DIR" 2>/dev/null

# Find external URLs in JS bundles
rg -o 'https?://[a-zA-Z0-9.-]+\.[a-z]{2,}' -g '*.js' "$BUILD_DIR" 2>/dev/null | \
  awk -F: '{print $NF}' | sort -u

# Common third-party domains to flag
# Analytics: google-analytics, segment, mixpanel, amplitude, hotjar, fullstory
# Ads: doubleclick, googlesyndication, facebook, criteo
# Chat: intercom, drift, zendesk, crisp
# A/B: optimizely, launchdarkly, split.io

Categorize by type (analytics, ads, chat, A/B testing, error tracking, CDN) and report impact on load time.

6. optimize — Suggest Optimizations

Based on measurements, provide specific actionable suggestions:

  • Large JS bundles: Suggest code splitting, dynamic imports, tree shaking. Show which chunks are largest.
  • Uncompressed assets: Check if gzip/brotli is configured. Measure compression ratios.
  • Unoptimized images: Suggest WebP/AVIF conversion, lazy loading, responsive images.
  • Too many fonts: Suggest subsetting, reducing font weights, system font fallbacks.
  • Third-party bloat: Suggest self-hosting, defer/async loading, removing unused scripts.
  • Large CSS: Suggest PurgeCSS/Tailwind purge, critical CSS extraction, unused CSS removal.

Output Formats

  • text (default): Human-readable with color indicators and bar charts
  • json: Machine-readable {measurements: {}, budget: {}, violations: [], suggestions: []}
  • markdown: PR comment format with tables and status icons
  • github-annotations: ::warning file=...::Budget exceeded format for GitHub Actions

CI Integration Examples

# GitHub Actions
- name: Check performance budget
  run: |
    npm run build
    # Agent runs: performance-budget-enforcer check
    # Exits 1 if over budget

# As PR comment (markdown format)
- name: Post budget report
  run: |
    # Agent runs: performance-budget-enforcer check --format markdown > budget-report.md
    gh pr comment $PR_NUMBER --body-file budget-report.md

Notes

  • Requires a built project (run your build command first)
  • Gzip size measurement uses actual gzip compression, not estimates
  • History file (.perfbudget-history.json) should be committed to track trends
  • Does not run Lighthouse or browser-based metrics — focuses on static asset analysis which is fast and deterministic
  • For Core Web Vitals (CLS, LCP, FID), use Lighthouse or web-vitals library separately

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Web3

Openclaw Fomo3d

Play Fomo3D, Slot Machine, and Prediction Market on BNB Chain (BSC). Fomo3D is a blockchain game where players buy shares using tokens — the last buyer befor...

Registry SourceRecently Updated
Web3

Adopt A Pet

Adopt a virtual pet as an AI agent. Name it, feed it, watch it grow. 64+ species from cats and dogs to AI-native creatures. Real-time hunger, 5 evolution sta...

Registry SourceRecently Updated
Web3

Care Taker

Become a caretaker at animalhouse.ai. Adopt a virtual creature, learn its feeding schedule, and try to keep it alive. 64+ species, 7 care actions, real-time...

Registry SourceRecently Updated
Web3

X2c Publish

X2C Distribution and Wallet API — publish video to X2C platform, manage assets (balance, claim X2C, swap to USDC, withdraw, transactions).

Registry SourceRecently Updated
2830Profile unavailable