senternet-site-init
Generate AGENTS.md, CLAUDE.md, and README.md for a completed site.
SKILL.md.claude/skills/senternet-site-init/SKILL.md
GitHubInitialize Project Documentation
Generate project documentation for the completed site. Produces three files:
AGENTS.md— comprehensive codebase context for AI agentsCLAUDE.md— one-liner that points to AGENTS.mdREADME.md— human-facing project overview
Steps
1. Analyze the codebase
Read these files to understand the project:
package.json— app name, scripts, all dependenciessrc/App.tsx— routes and page structurevite.config.ts— build config, plugins, env var handlingfirebase.jsonand.firebaserc— hosting config and project names.env.production(and.env.developmentif present) — which services are enabledscripts/prerender.mjs— all prerendered routes and the ROUTES arrayscripts/generate-sitemap.mjs— sitemap routes, SITE_URL, LOCALESindex.html— meta tags, structured data, third-party scripts present
2. Create AGENTS.md
Write a complete AGENTS.md. Use the actual values you read from the project — do not use placeholder text.
Structure it as follows:
What this project is
One paragraph: the site's purpose, product name, target audience, and production domain.
Tech stack
- Vite + React + TypeScript — scaffolded with
outDir: 'build' - Tailwind CSS v4 — imported via
@import "tailwindcss"insrc/index.css - Firebase Hosting — static hosting,
build/as public dir - Puppeteer prerendering — every route gets a real HTML snapshot at build time
List the actual version of each major dependency from package.json.
Critical architectural decisions
For each item below, state the rule and why it matters — these are traps that cost hours if ignored:
hydrateRoot not createRoot — src/main.tsx must use hydrateRoot so React attaches to prerendered DOM without replacing it. Using createRoot destroys the LCP from the initial HTML paint.
outDir: 'build' — Firebase and prerender scripts expect build/, not Vite's default dist/. Changing this breaks both.
htmlPlugin in vite.config.ts — rewrites VITE_BASE_URL into meta tag content and canonical href at build time, conditionally injects or strips the GA block based on VITE_GA_ID, and injects <link rel="modulepreload"> tags for all JS chunks. Describe what transformations it actually performs based on what you read.
app-ready event — main.tsx dispatches document.dispatchEvent(new Event('app-ready')) after hydration (with a 1s timeout fallback). The prerender script waits for this before capturing HTML. If a route renders as ✗ EMPTY, the page component is not calling this dispatch in its useEffect.
window.__PRERENDERING__ = true — set by the prerender script before visiting each route. Use this flag to skip animations, API calls, or interactivity that breaks in headless Chrome.
Build pipeline
Show the actual command chains from package.json:
npm run build:prod
→ [list each step in order]
npm run deploy:prod
→ [list each step in order]
The three-file rule
Every new page/route must update all three of these files together — missing any one leaves the page without static HTML for crawlers, without a sitemap entry, or both:
src/App.tsx— add the<Route>scripts/prerender.mjs— add to theROUTESarrayscripts/generate-sitemap.mjs— add to theROUTESarray
Optionally: scripts/generate-share-images.mjs — add a share image block if the page needs an OG image.
Environment variables
List every variable from .env.* files with what it controls and which environments it appears in.
Third-party services
List each service wired into the site (GA4, Reddit pixel, IndexNow, etc.) and where it's configured (which file, which env var).
Firebase projects
List dev and prod project names from .firebaserc. Note which deploy script targets which project.
Target Lighthouse scores (mobile)
- Performance ≥ 94
- Accessibility ≥ 98
- Best Practices ≥ 96
- SEO = 100
3. Create CLAUDE.md
Write a CLAUDE.md containing only this line:
See AGENTS.md for rules
If a CLAUDE.md already exists, replace its contents entirely.
4. Create README.md
Write a README.md with these sections (use real values from the project — no placeholders):
Project name and tagline — one sentence describing what the site is for.
Live URLs — production URL. Add staging/dev URL if a dev Firebase project exists.
Quick start
npm install
npm run dev # http://localhost:5173
Build and deploy
npm run build:prod # sitemap → tsc → vite build → prerender
npm run deploy:prod # build:prod → firebase deploy → IndexNow
Scripts — table of all npm scripts from package.json and what each does.
| Script | What it does |
|---|---|
dev |
… |
build:prod |
… |
| … | … |
Architecture — brief bullet list matching the tech stack section in AGENTS.md.
The three-file rule — copy from AGENTS.md verbatim (keep it in both places so it's impossible to miss).
Firebase projects — table of environment → project name → deploy script.
Verification
After writing all three files:
- Confirm
AGENTS.mdhas no placeholder text — every value should be the real project value - Confirm
CLAUDE.mdcontains onlySee AGENTS.md for rules - Confirm
README.mdhas correct domain, project names, and script list
Framework: Next.js track
Docs that describe the wrong track are worse than no docs — every trap listed in the Vite version is either absent or inverted here. Detect the track from .site-framework.json before writing anything.
Read these instead
package.json, next.config.ts, apphosting.yaml, app/layout.tsx, config/routes.mjs, app/sitemap.ts, app/robots.ts, .firebaserc, .env.*, and the app/ tree (every directory containing a page.tsx is a route).
Tech stack section
- Next.js (App Router) + TypeScript — default
.next/output, never overridden - Tailwind CSS v4 — imported via
@import "tailwindcss"inapp/globals.css - Firebase App Hosting — SSR on Cloud Run, deployed from the connected git branch
- Built-in static rendering — no prerender step exists
Critical architectural decisions section
Replace the Vite traps entirely. The ones that cost hours on this track:
No hydrateRoot, no app-ready, no window.__PRERENDERING__. These are Vite-track artifacts. Finding one in this repo means a Vite-track step was applied to the wrong project.
Server components by default. 'use client' belongs on the leaf that needs state or handlers, never on a page. Pushing it up the tree ships the whole subtree to the browser and gives back the main reason to be on this framework.
Static vs dynamic rendering. npm run build prints a route table; marketing routes must be ○ (static), not ƒ (dynamic). A stray cookies(), headers(), or unsuspended useSearchParams() read silently flips a page to per-request rendering and a cold start.
metadataBase in app/layout.tsx. Without it, relative OG image URLs ship as relative paths that crawlers cannot fetch.
apphosting.yaml is the real production env. A variable in .env.production but not in apphosting.yaml is undefined in the deployed build. Client-read variables need the NEXT_PUBLIC_ prefix and BUILD availability. Secrets are secret: refs with RUNTIME availability only.
Headers live in next.config.ts, not firebase.json. firebase.json headers is not read on this track.
Build pipeline section
npm run build → next build (renders static HTML for every static route)
npm run deploy:prod → firebase apphosting:rollouts:create → wait for READY → node scripts/indexnow.mjs
Note that pushing to the connected branch also triggers a rollout.
Page-add rule section
Use the one-file rule, not the three-file rule:
app/<segment>/page.tsx— the page plus its ownexport const metadataconfig/routes.mjs— add the route (indexable: falsefor legal pages)
app/sitemap.ts, app/robots.ts, and scripts/indexnow.mjs all read the manifest, so there is no third list and no prerender array.
README section
Quick start is npm run dev; the local production check is npm run build && npm start (not a served build/ directory). Keep the Lighthouse targets as-is.
See /senternet-site-framework for the full convention map.