agentleFS
pivoshenkopivoshenko/pivoshenko.ai/skills/vercel-hygiene/SKILL.md

vercel-hygiene

Skill6 starsChanged 44 days agoMITView on GitHub

Audit and harden the 4 pivoshenko brand sites on Vercel (pivoshenko.dev, pivoshenko.startpage, pivoshenko.wallpapers, pivoshenko.ai), team `pivoshenko`. Read-only sweep -> per-site report (ok/attention/action) -> confirmed fixes. Emphasizes security headers and analytics coverage. Use when the user says "audit vercel", "check vercel hygiene", "harden the sites", "vercel security", "check analytics coverage", "vercel health check", or wants a periodic once-over of the Vercel setup. Delegates perf/cost -> `vercel-optimize`, CLI ops and deployments -> `vercel-cli` (or the `mcp__vercel__deploy_to_vercel` tool).

  • Reads credentials
  • Installs packages

We split this skill into the rules it gives an agent

15 rules. About 0 are common, 1 is distinctive, and 14 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 14

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  • could be automatic: a setting could enforce it
  1. Very specific:

    Read-only sweep -> per-site report (ok / attention / action) -> confirm -> apply -> verify.

    line 10
  2. Very specific:

    Every check runs against all 4 sites and appears in the report - clean ones as ok, never silently omitted.

    line 33
  3. Very specific:

    Check: the shared source first - grep -n headers ~/Development/sources/pivoshenko.ui/ui/next/config.ts - then per site, grep -n headers …

    line 39
  4. Very specific:

    Check per site: grep '@vercel/analytics\|@vercel/speed-insights' <repo>/site/package.json (dep present?) and grep -n …

    line 56
  5. Very specific:

    Check: read each <repo>/site/vercel.json, diff the 4 against each other.

    line 63
  6. Very specific:

    Check: grep -A2 '"engines"' <repo>/site/package.json.

    line 70
  7. Very specific:

    Check: grep -rn 'next/image' <repo>/site (used?).

    line 76
  8. Very specific:

    Check: find <repo>/site/app -name route.ts (ls with ` misses nested routes without globstar).

    line 82
  9. Very specific:

    Check: grep -n runtime <repo>/site/app/icon.tsx <repo>/site/app/opengraph-image.tsx.

    line 88
  10. Very specific:

    Check: ls <repo>/site/.env*.

    line 95
  11. Very specific:

    Check: mcp__vercel__get_project domains per site vs the table above.

    line 101
  12. Very specific:

    Check: grep -n 'functions\|crons' <repo>/site/vercel.json.

    line 107
  13. Very specific:

    Speed Insights / Analytics coverage: - add the dep, import the component in app/layout.tsx, pass via afterShell to <SiteLayout>.

    line 121
  14. Very specific:

    Cache header on a dynamic route: - add Cache-Control with stale-while-revalidate.

    line 123
  15. Distinctive:

    No force-pushes, no env var changes without explicit ask

    line 136

≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Vercel Hygiene

Read-only sweep -> per-site report (ok / attention / action) -> confirm -> apply -> verify. Use the Vercel MCP (mcp__vercel__*) for dashboard state; read on-disk files for code-side checks.

Run every check; never report a finding this file predicts. Each check below states the command and the optimal value - the verdict comes from the run, not from here. Baked-in findings are how an audit skill rots: the fix lands, the skill keeps reporting the gap, and the next sweep raises phantom actions against work already done.

Sites

Team pivoshenko. Local repos are siblings under ~/Development/sources/; each site's app lives in <repo>/site/.

ProjectRepoDomain
pivoshenko.devpivoshenko.devpivoshenko.dev, www.pivoshenko.dev
pivoshenko.startpagepivoshenko.startpagestartpage.pivoshenko.dev
pivoshenko.wallpaperspivoshenko.wallpaperswallpapers.pivoshenko.dev
pivoshenko.aipivoshenko.aiai.pivoshenko.dev

Shared foundation: pivoshenko.ui (git-tag-pinned per site). Config and chrome live there - baseNextConfig, <SiteLayout>, tokens - so a fix there propagates to all 4 on the next tag bump. Prefer shared fixes over per-site duplication.

Mind the two path shapes: sites import pivoshenko.ui/next/config, but on disk that file is pivoshenko.ui/ui/next/config.ts (the package export strips the leading ui/). Grep the disk path; a grep of the import path finds nothing and reads as "no shared headers" when the opposite is true.

Repo missing locally -> run the MCP-side checks anyway, mark the code-side ones skipped (no local repo), and name them. Never infer a code-side verdict from the dashboard.

Sweep (Read-Only)

Every check runs against all 4 sites and appears in the report - clean ones as ok, never silently omitted; omission reads as "not checked". Lead with security headers and analytics coverage.

First, mcp__vercel__list_projects on team pivoshenko: any project not in the Sites table = attention, name it - an unlisted project is exactly the drift this sweep exists to catch.

1. Security Headers

  • Check: the shared source first - grep -n headers ~/Development/sources/pivoshenko.ui/ui/next/config.ts - then per site, grep -n headers <repo>/site/vercel.json <repo>/site/next.config.ts and ls <repo>/site/middleware.ts
  • Optimal: headers() defined once in baseNextConfig, inherited by all 4
  • A site may narrow the shared set in its own next.config.ts, but only by awaiting baseNextConfig.headers?.() and filtering - that keeps one source of truth and makes the exception visible. pivoshenko.startpage does this for X-Frame-Options (it's meant to be framed). A site that redefines headers from scratch, or sets them in vercel.json, = attention: it silently stops inheriting later additions
  • Absent from the shared config -> action, one fix for all 4. Minimum set:

    X-Content-Type-Options: nosniff
    X-Frame-Options: DENY
    Referrer-Policy: strict-origin-when-cross-origin
    Permissions-Policy: camera=(), microphone=(), geolocation=()
  • CSP stays out of the shared config until validated per site - the sites don't share an origin allowlist (a fonts CDN on one, raw.githubusercontent.com on another). Blanket CSP in baseNextConfig breaks whichever site fetches something the others don't
  • HSTS is applied by Vercel on production domains - check, don't duplicate. A hand-rolled Strict-Transport-Security header is churn at best and a shorter max-age at worst

2. Analytics + Speed Insights

  • Check per site: grep '@vercel/analytics\|@vercel/speed-insights' <repo>/site/package.json (dep present?) and grep -n 'Analytics\|SpeedInsights' <repo>/site/app/layout.tsx (actually rendered?)
  • Optimal: both deps present and both components rendered. <Analytics /> comes free from <SiteLayout> in pivoshenko.ui; <SpeedInsights /> is passed per site via afterShell
  • Dep without a render = action, not ok - an installed package that nothing mounts collects nothing. Check both halves separately and report them separately
  • Missing on some sites but not others -> the covered site is the template; mirror its wiring exactly rather than inventing a second pattern

3. vercel.json Contract

  • Check: read each <repo>/site/vercel.json, diff the 4 against each other; mcp__vercel__get_project per site to confirm the dashboard agrees on framework / build / install / output
  • Optimal: all 4 identical - $schema, framework: nextjs, buildCommand: pnpm build, installCommand: pnpm install --frozen-lockfile, outputDirectory: .next. Any site that differs without a stated reason = attention
  • Dashboard value ≠ file value = attention (silent drift: the dashboard wins on some fields and the repo stops being the source of truth)
  • nodeVersion is not a vercel.json key. Adding it does nothing - the version-controlled lever is engines.node. See check 4

4. Node Version

  • Check: grep -A2 '"engines"' <repo>/site/package.json; mcp__vercel__get_project for the dashboard's Node version
  • Optimal: engines.node pins the same major the dashboard runs, in source. Vercel reads engines.node and it overrides the dashboard setting, so the repo is where the pin belongs
  • Open lower bound (>=N) while the dashboard runs a newer major = attention: it builds fine today and silently floats on the next platform bump

5. Image Optimization

  • Check: grep -rn 'next/image' <repo>/site (used?); grep -n 'images' <repo>/site/next.config.ts (unoptimized? remotePatterns?)
  • Optimal: next/image with the pipeline on, or unoptimized: true as a stated deliberate trade-off. Remote sources -> remotePatterns must list them, else the build fails at runtime
  • unoptimized: true on an image-heavy site = attention, not action: it skips WebP and responsive sizing, which is a real cost but a defensible call for a gallery of large static files. Revisit only if that site's LCP is actually suffering - check Speed Insights before proposing it

6. Caching / ISR

  • Check: find <repo>/site/app -name route.ts (ls with ** misses nested routes without globstar); for each, grep -n 'Cache-Control\|revalidate' ; note which routes use generateStaticParams
  • Optimal: every dynamic route (RSS, API, OG) sets an explicit Cache-Control; static routes need nothing. A fully static site here is ok, not a gap
  • Dynamic route with no cache header = action (every request hits a function that could have been cached)

7. Runtime on OG + Icon Routes

  • Check: grep -n runtime <repo>/site/app/icon.tsx <repo>/site/app/opengraph-image.tsx; watch the build output for The Edge Runtime is deprecated
  • Optimal: nodejs - the deprecation warning in the build output is the check, trust it over any version claim here. Why -> as of Next 16, export const runtime = 'edge' is deprecated and forces those routes dynamic (Using edge runtime on a page currently disables static generation); an icon and an OG image are exactly the routes you want prerendered
  • Still on edge = action: switch to nodejs and confirm the routes go static in the build output. Handlers re-export from pivoshenko.ui, so this is one shared fix plus a tag bump, not four
  • Re-check after any ui tag bump - shared config drifts silently and nothing in the site repo changes to signal it

8. Env Var Hygiene

  • Check: ls <repo>/site/.env*; grep -rn 'process.env\.' <repo>/site --include=*.ts --include=*.tsx; grep -rn 'env' <repo>/site/vercel.json; mcp__vercel__get_project for dashboard env vars
  • Optimal: no secrets in vercel.json, no committed .env, and every process.env.* read in source has a matching dashboard var
  • Source reads a var the dashboard doesn't define = action (undefined at build, usually a silent empty string rather than a crash). Dashboard defines a var no source reads = attention (dead config, or a leftover from a removed integration)

9. Domains / Redirects

  • Check: mcp__vercel__get_project domains per site vs the table above; confirm the primary resolves and www. redirects to apex where it exists
  • Optimal: exactly the domains listed, no stale preview or one-off domains left assigned
  • Unrecognized domain on a project = attention, name it rather than removing - it may be an intentional alias

10. Functions / Crons

  • Check: grep -n 'functions\|crons' <repo>/site/vercel.json; derive function presence from the repo (find <repo>/site/app -name route.ts handlers + the functions block); mcp__vercel__get_deployment on the latest deployment only to confirm
  • Optimal: for static sites, none - SSG plus edge routes needs no functions block. If a site has grown functions, its cost profile changed -> note it and hand off to vercel-optimize

Report Format

One row per site x check, verdicts from the run -> references/report-format.md.

Collapse to all 4 only when all four genuinely share a verdict - a collapsed row that hides one differing site is a missed finding. Lead with: "N ok, N attention, N actions available." Then AskUserQuestion multiSelect over the action items only.

Actions (Each Confirmed; Side Effects Stated First)

Actions are derived from what the sweep found, not from a fixed list. The recurring ones:

  • Security headers - add or extend headers() in pivoshenko.ui/ui/next/config.ts, then bump the pinned ui tag per site. Additive, no runtime change; takes effect on next deploy. Validate CSP per site before adding it. A site that needs less than the shared set filters it in its own next.config.ts rather than opting out
  • Speed Insights / Analytics coverage - add the dep, import the component in app/layout.tsx, pass via afterShell to <SiteLayout>. Mirror whichever site already has it
  • Node pin - tighten engines.node to the running major in <repo>/site/package.json. Locks the major in source; a later platform default bump then needs a deliberate edit. Don't add nodeVersion to vercel.json
  • Cache header on a dynamic route - add Cache-Control with stale-while-revalidate. Stale content for the max-age window - confirm the freshness budget for that route first

Delegation

  • Cost analysis, bundle size, perf budgets -> vercel-optimize
  • CLI ops (vercel env, vercel domains, vercel link) -> vercel-cli
  • Triggering/managing deployments -> vercel-cli (CLI) or mcp__vercel__deploy_to_vercel (MCP)

Rules

  • Sweep = read-only. Change state only via confirmed actions, one site at a time -> verify -> next
  • Report what the check returned, never what this file expects. A check that couldn't run is skipped + why - never ok
  • pivoshenko.ui changes hit all 4 sites - prefer shared fixes there over per-site duplication, and re-run the sweep after a tag bump
  • No force-pushes, no env var changes without explicit ask
  • Side effects before confirm, always
  • Clean sweep -> "all healthy", stop. Don't invent actions
  • Security headers + analytics coverage surface first - they're the checks where a gap costs the most and shows the least

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.