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
- Very specific: line 10
Read-only sweep -> per-site report (ok / attention / action) -> confirm -> apply -> verify.
- Very specific: line 33
Every check runs against all 4 sites and appears in the report - clean ones as ok, never silently omitted.
- Very specific: line 39
Check: the shared source first - grep -n headers ~/Development/sources/pivoshenko.ui/ui/next/config.ts - then per site, grep -n headers …
- Very specific: line 56
Check per site: grep '@vercel/analytics\|@vercel/speed-insights' <repo>/site/package.json (dep present?) and grep -n …
- Very specific: line 63
Check: read each <repo>/site/vercel.json, diff the 4 against each other.
- Very specific: line 70
Check: grep -A2 '"engines"' <repo>/site/package.json.
- Very specific: line 76
Check: grep -rn 'next/image' <repo>/site (used?).
- Very specific: line 82
Check: find <repo>/site/app -name route.ts (ls with ` misses nested routes without globstar).
- Very specific: line 88
Check: grep -n runtime <repo>/site/app/icon.tsx <repo>/site/app/opengraph-image.tsx.
- Very specific: line 95
Check: ls <repo>/site/.env*.
- Very specific: line 101
Check: mcp__vercel__get_project domains per site vs the table above.
- Very specific: line 107
Check: grep -n 'functions\|crons' <repo>/site/vercel.json.
- Very specific: line 121
Speed Insights / Analytics coverage: - add the dep, import the component in app/layout.tsx, pass via afterShell to <SiteLayout>.
- Very specific: line 123
Cache header on a dynamic route: - add Cache-Control with stale-while-revalidate.
- Distinctive: line 136
No force-pushes, no env var changes without explicit ask
≈ “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.
Files
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/.
| Project | Repo | Domain |
pivoshenko.dev | pivoshenko.dev | pivoshenko.dev, www.pivoshenko.dev |
pivoshenko.startpage | pivoshenko.startpage | startpage.pivoshenko.dev |
pivoshenko.wallpapers | pivoshenko.wallpapers | wallpapers.pivoshenko.dev |
pivoshenko.ai | pivoshenko.ai | ai.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.tsandls <repo>/site/middleware.ts - Optimal:
headers()defined once inbaseNextConfig, inherited by all 4 - A site may narrow the shared set in its own
next.config.ts, but only by awaitingbaseNextConfig.headers?.()and filtering - that keeps one source of truth and makes the exception visible.pivoshenko.startpagedoes this forX-Frame-Options(it's meant to be framed). A site that redefines headers from scratch, or sets them invercel.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.comon another). Blanket CSP inbaseNextConfigbreaks 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-Securityheader is churn at best and a shortermax-ageat worst
2. Analytics + Speed Insights
- Check per site:
grep '@vercel/analytics\|@vercel/speed-insights' <repo>/site/package.json(dep present?) andgrep -n 'Analytics\|SpeedInsights' <repo>/site/app/layout.tsx(actually rendered?) - Optimal: both deps present and both components rendered.
<Analytics />comes free from<SiteLayout>inpivoshenko.ui;<SpeedInsights />is passed per site viaafterShell - 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_projectper 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)
nodeVersionis not avercel.jsonkey. Adding it does nothing - the version-controlled lever isengines.node. See check 4
4. Node Version
- Check:
grep -A2 '"engines"' <repo>/site/package.json;mcp__vercel__get_projectfor the dashboard's Node version - Optimal:
engines.nodepins the same major the dashboard runs, in source. Vercel readsengines.nodeand 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/imagewith the pipeline on, orunoptimized: trueas a stated deliberate trade-off. Remote sources ->remotePatternsmust list them, else the build fails at runtime unoptimized: trueon 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(lswith**misses nested routes without globstar); for each,grep -n 'Cache-Control\|revalidate'; note which routes usegenerateStaticParams - Optimal: every dynamic route (RSS, API, OG) sets an explicit
Cache-Control; static routes need nothing. A fully static site here isok, 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 forThe 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 tonodejsand confirm the routes go static in the build output. Handlers re-export frompivoshenko.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_projectfor dashboard env vars - Optimal: no secrets in
vercel.json, no committed.env, and everyprocess.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_projectdomains per site vs the table above; confirm the primary resolves andwww.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.tshandlers + thefunctionsblock);mcp__vercel__get_deploymenton the latest deployment only to confirm - Optimal: for static sites, none - SSG plus edge routes needs no
functionsblock. If a site has grown functions, its cost profile changed -> note it and hand off tovercel-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()inpivoshenko.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 ownnext.config.tsrather than opting out - Speed Insights / Analytics coverage - add the dep, import the component in
app/layout.tsx, pass viaafterShellto<SiteLayout>. Mirror whichever site already has it - Node pin - tighten
engines.nodeto 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 addnodeVersiontovercel.json - Cache header on a dynamic route - add
Cache-Controlwithstale-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) ormcp__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 - neverok pivoshenko.uichanges 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
In this repository21 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- blog-writeSkill
- cloudflare-hygieneSkill
- git-branch-cleanupSkill
- git-branch-createSkill
- git-branch-syncSkill
- git-commitSkill
- git-issue-createSkill
- git-issue-startSkill
- git-issue-triageSkill
- git-pr-createSkill
- herdr-dispatchSkill
- humanizeSkill
- macos-cleanupSkill
- macos-maintenanceSkill
- obsidian-markdownSkill
- spec-applySkill
- spec-archiveSkill
- spec-exploreSkill
- spec-proposeSkill
- spec-verifySkill
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
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.

