new-website
Skill9 starsChanged 30 days agoMITView on GitHub
Orchestrates building a NEW website end-to-end, from insights to launch-ready and handoff-ready. Self-contained: it runs the stack decision interview, sequences the website-* skills + existing marketing skills in order, and scaffolds the project from its own templates/ (Astro starter overlay + full QA test suite + GDPR privacy draft + owner docs + permission allowlist), copying the bundled skill set into the project's skills dir (full copy list in §3) so the repo is self-contained for a third party. Default stack Astro → GitHub → Cloudflare Pages. Use at the very start of any new site. Trigger phrases: "new website", "start a new site", "scaffold a website", "spin up a site", "build a new website", "set up a new web project", "website starter", "which stack for this site".
- Reads credentials
- Deletes or force-pushes
- Commits and pushes
1 other project uses a version of this.
Adopted by
Most recently taken up by
- karero/website-builderword for word, first seen Oct 14, 2026
Other versions1 project uses a version of this.
Word for word · 1
- karero/website-builder★ 9100% same
Edited · 0
None found.
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository41 other files it gives its agents.
- skills/new-website/templates/AGENTS.mdAGENTS.md
- skills/new-website/templates/CLAUDE.mdCLAUDE.md
- ai-seoSkill
- astro-i18n-setupSkill
- business-listings-setupSkill
- copywritingSkill
- customer-researchSkill
- double-knuthSkill
- facts-checkSkill
- imageSkill
- independent-reviewSkill
- internal-link-auditSkill
- keystatic-setupSkill
- og-imagesSkill
- outgoing-link-auditSkill
- schema-markupSkill
- search-console-insightsSkill
- search-console-setupSkill
- seo-auditSkill
- seo-repositionSkill
- site-architectureSkill
- website-brand-kitSkill
- website-contact-formSkill
- website-content-guideSkill
- website-design-systemSkill
- website-hero-carouselSkill
- website-launch-checkSkill
- website-motionSkill
- website-new-pageSkill
- website-permissionsSkill
- website-positioning-checkSkill
- website-positioningSkill
- website-qaSkill
- website-reviewSkill
- website-scorecardSkill
- website-seo-geoSkill
- website-storySkill
- website-taglineSkill
- website-team-setupSkill
- website-testimonialsSkill
- wordpress-inspectorSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
18 rules. About 0 are common, 9 are distinctive, and 9 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈9
Distinctive · estimate
This author's own choices. Worth a look.
9
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 105
Default house stack: Astro (static) → GitHub → Cloudflare Pages.
- Distinctive: line 201
Ask whether the owner already runs a Plausible server.
- Distinctive: line 258
Prefer cookieless tools over Google Analytics, which would pull in a cookie-consent banner — for German-market sites the statute behind …
- Distinctive: line 371
Ask once, as a plain message in the chat (not a pick-list box: on a first run the owner may not see one and think you have stopped), with …
- Distinctive: line 384
Ask exactly once per build.
- Very specific: line 488
Scaffold + overlay: run the scaffold without questions, so it never waits for a key, then put back step 0's .gitignore, which the scaffold …
- Very specific: line 536
Use their own approval systems instead (Codex: AGENTS.md + Codex rules/config.
- Distinctive: line 686
Tell the owner about the generator tag, once,: after this first green run, unless the project README already has a "generator tag:" line.
- Distinctive: line 736
Read it in full before the FIRST deploy, when a brand-new page goes live, and when a live URL looks stale.
- Very specific: line 793
No analytics: the analytics paragraphs deleted, USE_ANALYTICS = false as shipped.
- Very specific: line 817
Two-stage: create the live branch (git checkout -b production && git push -u origin production && git checkout main — end back on main), …
- Very specific: line 850
Search Console (Domain property + DNS TXT) and Bing (import from GSC), sitemap-index.xml submitted to both, and IndexNow on (Cloudflare …
- Distinctive: line 916
Ask only when all three hold.
- Very specific: line 954
Look at the footer in light and in dark, and with the site's own theme switch if it has one, and pick the badge that reads well in each.
- Distinctive: line 969
Never add the credit unasked, never ask a second time, and never give the star for the user: it is their own click on their own account.
- Very specific: line 975
Do not rebuild what existing skills cover — GEO depth → ai-seo.
- Very specific: line 978
After any code/copy edit, re-run website-qa.
- Very specific: line 980
Run website-review (the Double-Knuth two-pass audit) at the end of a build (pre-launch) and after adding/editing a page — it's the …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 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
New website — orchestrator
The entry point for every new site you build. It decides the stack,
sequences the work, and scaffolds a self-contained, handoff-ready repo from
this skill's templates/. Concrete content/SEO/design/QA work is delegated to seven
sibling skills + existing global skills.
Everything needed is bundled here:
templates/astro/— the Astro starter overlay (Base.astro SEO+theme spine,config.ts, theme tokens,astro.config,tests/suite, CI, preview-noindex function, headers, llms.txt, GDPR privacy page draft). ItsREADME.mdis the assembly manual.templates/SETUP.md— accounts (GitHub + Cloudflare) + tools + the bootstrap.templates/PUBLISHING.md— plain-English "how to publish" for the owner (commit/push/branchexplained + step-by-step per publish model), plus the assistant-facing deploy-time guardrails (§4).templates/AGENTS.md+templates/CLAUDE.md— the working rules every assistant (Codex, Claude Code) follows in the repo: fetch the latest state first, pull request instead of a direct push, when a merge is allowed, never invent facts, the new-page checklist.CLAUDE.mdis one line (@AGENTS.md), so both tools read the same rules.templates/.gitignore,templates/claude/settings.json,templates/claude/hooks/git-stand.mjs— git ignore, the permission allowlist, and the Claude Code hook that fetches from GitHub at session start (and again after 2 hours) and reports what is new, soAGENTS.md§1 happens even when the assistant skips it. The settings register the hook: copy both, or every session start fails to find the script.templates/positioning.md,templates/content-guide.md,templates/brand.md— the per-site docs.references/WEBSITE_ARCHITECTURE.md(bundled with this skill) — the Cloudflare tier 1/2/3 decision tree + limits (the tiers are also summarized in §1, question 2).
Sibling skills (run in order, each usable on its own):
website-positioning · website-content-guide · website-seo-geo ·
website-design-system · website-testimonials · website-qa ·
website-review. website-positioning-check is an optional, read-only one-screen
diagnostic for a site that already has pages — run it after the build, or whenever the
offer starts to feel blurred; it is not a pipeline gate. website-launch-check is the launch
readiness check: it runs the site's tests first, checks only what they cannot, and writes a
dated report with a verdict to docs/launch/ (a baseline at step 5a, the verdict at 10c, an
optional live re-check at 12a). Plus outgoing-link-audit — the
pre-launch / monthly external-link
liveness sweep (only relevant once the site links out); internal-link-audit — the
internal-linking sweep that finds orphaned + thin pages and suggests where to cross-link
(run after adding a batch of pages); website-permissions — install + safely extend the
repo's permission allowlist (fewer prompts, same guardrails); search-console-setup
— post-launch GSC + Bing registration + IndexNow; and website-motion — an OPTIONAL
polish layer (stat count-up + section-heading reveal) for sites with a long scrolling
homepage, plus the reduced-motion contract and the a11y-gate change that keep motion
from silently hiding content. website-motion is never run by default: ask for it.
website-story is an OPTIONAL story layer (the home page told as the customer's story:
STORY.md + a seven-section home page map); it is offered once at §2a, right after
positioning, and never runs unasked. website-scorecard is an OPTIONAL public scorecard
(a section that shows what the site's own test gate checked, dated, plus the owner's
Lighthouse scores); it runs only when the owner asks for it. website-tagline is an
OPTIONAL skill for the short line under the logo (it starts from the purpose in
POSITIONING.md, shows the owner six to eight lines and lets them pick one →
TAGLINE.md + SITE.tagline); it too runs only when the owner asks for it.
website-brand-kit is OPTIONAL help with colours and a logo for an owner who is not a
designer (three prompts for the design tool she likes, then a checked install of the
palette and the seven icon files); the owner is asked once at §2b, right after
positioning and the story, and it never runs unasked.
website-team-setup is the other on-demand skill — run it once when a second person
joins the repo (invite collaborators, repo settings, prove CI triggers, block direct
pushes to main, connect Cloudflare Pages without the known traps, set the rights level
in AGENTS.md); a single owner never needs it.
A private repo gets 2 000 free CI minutes a month on GitHub Free (about 400 runs of the kit's
ci.yml); past that, jobs stop with a message that reads like a failed payment. Tell the
owner to check Budgets and alerts for an Actions budget (website-team-setup §3b has
the steps).
1. Decision interview (answer before any code)
Conduct the interview — and ALL user-facing guidance throughout this pipeline (explanations, the §3a link-sweep question, the §4 preview-vs-live announcements, error walk-throughs) — in the language the user writes in. A German owner gets the whole journey in German; the skill files themselves stay English. The verbatim message templates below are content specs, not required English wording.
The folder check, this interview and the §2a and §2b questions: read and list, don't
run commands of your own. A command this interview names, such as the analytics check,
runs once the site exists. To see what a folder holds, list it with your file tools; to
read a section of another skill, open that skill's SKILL.md and read it. A shell command
can show the owner a red error for an ordinary answer: ls astro.config.* fails in an
empty folder because nothing matches, and grep or awk can end with a failure when they
find nothing. Where reading a file or listing a folder is possible only through a shell
command, use one that succeeds on every ordinary answer: ls -A for the folder (never a
pattern such as astro.config.*), and cat on the whole SKILL.md. Ask your questions
as plain chat messages, not in a pick-list box, which a new owner may not see. The
owner's first minutes should show no red and no frozen screen.
Default house stack: Astro (static) → GitHub → Cloudflare Pages. Six questions
decide everything downstream — ask them in order, offer the examples so a
non-expert can answer, and record the answers in the project README.md.
- Pages + content types — what does the sitemap look like?
Decides: plain pages vs Content Collections.
- A handful of fixed pages (home, services, about, contact, imprint — ≤ ~15)
→ plain
.astropages. Example: a small-business brochure site. - Anything that repeats with the same shape — blog posts, events, case
studies, team members → Astro Content Collections (one schema, one
template, n entries) + the
Baselayout. Example: an event archive where every event has date/speaker/location. - Mixing both is normal: flat pages + one collection.
- A handful of fixed pages (home, services, about, contact, imprint — ≤ ~15)
→ plain
- Any dynamic/backend behaviour — what must a server actually do?
Decides: the Cloudflare tier. Pick the LOWEST tier that fits — going higher
is the classic mistake. Tier tree + limits:
references/WEBSITE_ARCHITECTURE.md.- Visitors only read; contact is an email link, or a contact form that
opens the visitor's own mail program → Tier 1 static (~90% of sites).
A contact form: run
website-contact-form, our suggestion (no server, no account, no setting). Photos that rotate by themselves, with dots: runwebsite-hero-carousel(optional, on request; one strong photo is often better). - Exactly one small server task — site search, hiding a third-party API key, one live widget (e.g. a next-event box fed by an API), a form that must send through a server → Tier 2 (one Pages Function or server island). No skill builds such a form.
- State per user — accounts/login, a database, checkout, user-generated content → Tier 3 (SSR + D1). Rare; challenge the requirement first.
- Visitors only read; contact is an email link, or a contact form that
opens the visitor's own mail program → Tier 1 static (~90% of sites).
A contact form: run
- Who edits content after launch?
Decides: CMS or not.
- The owner/Claude editing files in git → no CMS (default; markdown + redeploy is the workflow).
- A non-technical person (client, co-organizer writing posts) →
Keystatic (git-based, no separate backend). Can be added later —
don't install it speculatively. When chosen, run
keystatic-setup(local mode; the GitHub cloud-mode upgrade is documented there).
- One language or several?
Decides: i18n routing from day 1 or never.
- One language (German-only counts) → skip i18n entirely.
- Two+ at launch (e.g. a DE+EN consultancy site) → run
astro-i18n-setup(Astro i18n: clean default locale + prefixed others, self-referencing hreflang +x-default, sitemap alternates, language switcher, per-locale test harness). - Multilingual, but building one language first? Fine — keep the default locale
unprefixed from day 1, ship/test the primary language, then run
astro-i18n-setupto add the second locale when translations are ready. Because the default stays at/, that's additive, not a breaking retrofit (skill → Phased rollout). - "Maybe English someday" → treat as one language now; the unprefixed default keeps a
future second locale additive. The only costly change is later moving the default
off
/(e.g./→/en/).
4a. What language should the content actually be written in?
Decides: the copy language — a separate decision from Q4's routing question above.
Answering "one language" in Q4 does not mean English. The starter defaults to
English (templates/astro/src/config.ts ships locale: 'en', and the placeholder
homepage copy is English prose) — say so explicitly if you want German, or another
language, instead. German-only is a completely valid, common answer here, not
just "English, obviously, unless multilingual."
- Analytics — will anyone act on the numbers? (optional)
- Just "is anyone visiting?" → Google Search Console (free, no script, no consent
banner) is enough for traffic from Google search; it does not show which other
sites send visitors, and Cloudflare's docs list no referrer view for the Free plan.
Cloudflare also counts the domain's traffic at its own servers, with nothing added
to the pages: https://dash.cloudflare.com → your domain → Analytics →
Traffic. On the Free plan that is requests, bandwidth, unique visitors and up
to five top countries for at least the last 30 days, bots included; page views, and
filtering by referrer, need a paid plan
(https://developers.cloudflare.com/analytics/account-and-zone-analytics/zone-analytics/,
checked October 2026). It counts requests and visitors, not events such as an
enquiry, so it cannot give the enquiry count. Cloudflare's separate "Web Analytics"
cannot either: its FAQ answers custom events with "Not yet"
(https://developers.cloudflare.com/web-analytics/faq/, checked October 2026). It is
also a script in the visitor's browser, so keep it off
(
docs/CLOUDFLARE-CHECKLIST.md, section 3, "Cloudflare Web Analytics: Off"). The starter ships with its own analytics off (USE_ANALYTICS = falseinsrc/config.ts, no analytics host inpublic/_headers), so leave both as they are and delete the analytics paragraphs of section 3 of the privacy page. - "Which sources bring enquiries?" or other real decisions from traffic data
(campaigns, content strategy) → Plausible (cookieless; whether it needs a
consent banner: the last point of this question). In its installer, untick the
optional measurements (outbound links, file downloads, form submissions)
unless you keep the privacy page's slot sentence for each one you leave ticked.
Plausible's menus hide the pages the owner needs, so give each one as a link,
with
scriptHostand the site's domain (as added in Plausible) filled in: the installer and the script id at<scriptHost>/<domain>/installation, the goals at<scriptHost>/<domain>/settings/goals, a new site at<scriptHost>/sites/new. Seen on a self-hosted Community Edition instance on 2026-10-16: the owner added the goal on that goals page; the other two sent a signed-out visitor to the login page, where an unknown path gives 404. On Plausible Cloud (scriptHosthttps://plausible.io) the same paths are expected, not checked. If a link gives 404, say so, and find the page in Plausible's own menus with the owner. Ask whether the owner already runs a Plausible server. If not, the usual case, recommend the paid cloud version (from €9/mo — https://plausible.io/#pricing), so the owner runs no server; point at a self-hosted instance only if one already exists. The enquiry count (the "Email Tap" paragraph below) needs Plausible: Cloudflare's counts have no events. Switch it on, since the starter ships with analytics off: insrc/config.tssetUSE_ANALYTICS = trueand fill inscriptHostandscriptId, then add the same host toscript-srcandconnect-srcinpublic/_headers(the comments in both files say how). Keep the privacy page's analytics paragraphs and fill their slots. Runbash tests/check_analytics_gate.shonce to check the builds and the headers (a site's CI does not run it). Without the switch the live site counts nothing, and nothing says so. The enquiry count comes with it. Each click on an email address, and each press of the contact form's button that passes its checks, sends one "Email Tap" event (with the page and the referrer, as every event; no address, nothing typed). On the site's goals page in Plausible (<scriptHost>/<domain>/settings/goals, under Settings > Goals; open the address directly, owners find the page hard to reach from the dashboard), add a goal of type "Custom event" named exactlyEmail Tapbefore launch: Plausible's documentation says an event shows only once its goal exists and earlier ones are not counted. Once a test tap has reached Plausible, the goals page offersEmail Tapto add in one click. That rules out a typo in the name (seen on a self-hosted instance). To see a test tap, choose Today or Realtime: Plausible's "Last 7, 28, 30 and 91 days" views end yesterday (checked on self-hosted version 3.2.1; the cloud version may differ). On a site just added to Plausible, a tap in its first minute or so may not be kept; tap again a few minutes later. Filtered by that goal, the dashboard shows which sources and pages bring enquiries. It counts visitors who asked to write an email; whether their mail program opened, and whether they sent the mail, the site never learns. Script or server mode. With analytics switched on, Plausible by default runs as a script in the browser (ANALYTICS.mode: 'script'). Offer server mode ('server') to an owner who wants no analytics code on the visitor's device: the site's Cloudflare Function sends each page view and email tap to Plausible itself, so no script loads and nothing is read from or written to the device. It loses scroll depth, engaged time, the optional measurements and the exclusion of the owner's own visits (plausible_ignore), the contact form's button is not counted (only email links are), and a visitor can no longer opt out by blocking a script. Its numbers differ from the script's: it counts visitors who block scripts, but not a page a browser prefetched or prerendered before the visitor opened it, and not visitors whose browser sends no Fetch Metadata headers (Safari before 16.4, some in-app browsers); a tap on the email link shows those visitors a page with the address written as "name [at] domain" instead of opening their mail program. It forwards the visitor's IP to Plausible, as the script's request carries it; EDPB Guidelines 2/2023 (paras 54–56) say IP-based tracking can itself fall under the ePrivacy rule, so it is a stronger position, not a way out of a review. It needs two Production variables in Cloudflare (PLAUSIBLE_HOST,PLAUSIBLE_DOMAIN).PLAUSIBLE_DOMAINmust equal the site's name in Plausible, or Plausible answers 202 and drops every event without an error. The Function counts nothing on a*.pages.devaddress, so test on a custom domain. The starter README, step 4, has the setup and a list of what to check when nothing arrives; an existing site also takes the site-owned files that step names, whichwhats-newdoes not report, or it keeps counting with the script too. It works with self-hosted Plausible (tried on Community Edition 3.2.1) and, per Plausible's documentation, with the cloud version (not tried there with a real site). Counting uses the Functions quota the site already uses (free plan: 100,000 requests a day); past it, with "Fail open" (what the deploy guide sets; Cloudflare names no default, so check it), Cloudflare serves the site without the Function until midnight UTC, so counting stops and email links do not open the mail program until then ("Fail closed" shows an error page for the whole site instead;references/CLOUDFLARE_FIRST_DEPLOY.md). - Prefer cookieless tools over Google Analytics, which would pull in a cookie-consent banner — for German-market sites the statute behind that banner is § 25 TDDDG (formerly TTDSG). Third-party additions put the banner-free position at risk by different routes: a YouTube iframe triggers § 25 itself (device storage/readout), while remote Google Fonts change the answer via the GDPR instead (unconsented IP disclosure — LG München I, 3 O 17493/20; self-host fonts, as this starter does). Any analytics script is gated to the production branch only (server mode: the Production variables, on the live domain only). The privacy page must match whatever is chosen here, script or server mode included.
- When the site needs no consent banner. Tell the owner plainly: a site built
with this kit needs no consent banner in the EU, Germany, Switzerland or California
as long as it runs no analytics script: nothing reaches the visitor's browser that
the page itself does not need. For the starter's own
analytics that means leaving
USE_ANALYTICS = falseinsrc/config.ts, as it ships (the first point of this question; the other things that break the no-banner case are listed below); runbash tests/check_analytics_gate.shonce to check that no build writes the script (a site's CI does not run it). Plausible runs as a small script in the visitor's browser. With it, running without a banner is a defensible position in the EU, Germany, Switzerland and California, not a settled one; to be sure, ask a lawyer. Server mode runs no script, but it is not banner-free by definition either: the visitor's IP still reaches Plausible through the site's Function, and EDPB Guidelines 2/2023 (paras 54–56) say IP-based tracking can fall under the same rule; the same headers say what changes in server mode. For the EU and Germany, the headers ofprivacy.astroand_datenschutz.astroset out the sources on both sides and ask you to record who accepted the position. In Switzerland and California the position rests on the privacy page informing visitors. Switzerland (Art. 45c FMG) asks that visitors are told about the processing and its purpose, and that they may refuse it; California's CalOPPA asks the page to say whether third parties may collect personal information across websites through the site, and how the site answers "Do Not Track" if it collects such information itself. Section 3 ofprivacy.astrosays both;_datenschutz.astrosays the Swiss part. California's CCPA applies only above its thresholds (USD 26,625,000 annual gross revenue from 1 January 2025, adjusted every two years; or buying, selling or sharing the personal information of 100,000 or more California consumers or households; or half the revenue from selling or sharing it). A site without a banner still needs its privacy page, an imprint in Germany and Austria, and a processing contract with Cloudflare, and with Plausible if it runs on Plausible Cloud. What breaks the no-banner case: any Cloudflare feature that section 3 ofdocs/CLOUDFLARE-CHECKLIST.md(in the Croftweaver folder) asks you to switch off, the script monitor among them; a YouTube embed; remote Google Fonts; another analytics tool. A theme toggle that stores the visitor's choice needs a look too (the same headers say why). Not legal advice.
- Just "is anyone visiting?" → Google Search Console (free, no script, no consent
banner) is enough for traffic from Google search; it does not show which other
sites send visitors, and Cloudflare's docs list no referrer view for the Free plan.
Cloudflare also counts the domain's traffic at its own servers, with nothing added
to the pages: https://dash.cloudflare.com → your domain → Analytics →
Traffic. On the Free plan that is requests, bandwidth, unique visitors and up
to five top countries for at least the last 30 days, bots included; page views, and
filtering by referrer, need a paid plan
(https://developers.cloudflare.com/analytics/account-and-zone-analytics/zone-analytics/,
checked October 2026). It counts requests and visitors, not events such as an
enquiry, so it cannot give the enquiry count. Cloudflare's separate "Web Analytics"
cannot either: its FAQ answers custom events with "Not yet"
(https://developers.cloudflare.com/web-analytics/faq/, checked October 2026). It is
also a script in the visitor's browser, so keep it off
(
- Domain + how do changes go live? (publish model)
Decides: the publish workflow — a safety-vs-simplicity trade-off. Offer both, explain
them, let the owner choose; recommend two-stage. Record the choice in the README.
- New domain or subdomain of an existing site? (Affects canonical URLs and whether existing SEO authority carries over.) DNS moves to Cloudflare.
- Two-stage — recommended (default).
mainis an unlisted, noindexed preview (*.pages.dev— noindexed, but public-by-URL, not access-controlled);productionis the live site. The owner pushes tomain, checks the preview link, then runsnpm run shipto publish. A mistake never reaches the live domain. Scaffold adds: theproductionbranch, the_middleware.tsnoindex guard, and theshipscript. - Single-stage — simplest. One branch —
mainis live, so every push goes straight to the public site. Fewer moving parts, no safety net. Pick this only for a low-stakes site whose owner is fine with "save = instantly public." Note: the_middleware.tsnoindex guard de-indexes every*.pages.devhost, so a single-stage site is invisible to Google until a custom domain is attached — say this when recommending it. - Don't assume git fluency. Whichever they choose, hand them
templates/PUBLISHING.md(plain-Englishcommit/push/branch+ the exact step-by-step to publish) and walk the first publish through with them.
Plain hand-written HTML (static .html files, no build step) is a legacy anti-pattern — use Astro.
2. The pipeline (the order to use the skills)
| # | Step | Skill / source |
| 1 | Insights: ICP, voice-of-customer, competitor scan | customer-research |
| 2 | Positioning: what you offer, for whom, market category → POSITIONING.md (Dunford) | website-positioning |
| 2a | Optional: the home page as the customer's story → STORY.md (offered once, §2a; positioning stays the source of truth) | website-story |
| 2b | Optional: colours and a logo (asked once, §2b; her choices are made here, the install waits for the green baseline of §3 step 5) | website-brand-kit |
| 3 | Tone of Voice, EEAT, page inventory → CONTENT_GUIDE.md + BRAND.md (its palette and logo come from step 2b, where the owner brought or chose them) | website-content-guide |
| 4 | Pages, clean URLs, nav, internal links | site-architecture |
| 5 | Decision interview + scaffold the repo | this skill §1, §3 |
| 5a | Baseline launch check on the green scaffold (after the brand-kit intake, where there is one): the settings in docs/launch/config.json and the first punch list, "Baseline, not a launch verdict" | website-launch-check |
| 6 | Build: mobile graphics, dark mode, theme tokens | website-design-system |
| 6 | Build: head metadata within limits, schema, llms.txt | website-seo-geo (+ schema-markup, ai-seo) |
| 6 | Build: per-page OG share cards (npm run og → 1200×630 ≤300 KB, tested) | og-images |
| 6 | Build: testimonials / Review schema from one data file (if the site has quotes) | website-testimonials |
| 7 | QA: a11y (light+dark) / seo / navigation / anchors / orphans / images / tone / positioning / placeholders / email / links | website-qa |
| 8 | Performance: Lighthouse / PageSpeed for FCP & LCP | website-qa perf section (+ seo-audit) |
| 9 | Double-Knuth review (correctness + cross-file consistency) | website-review |
| 10 | Outgoing-link liveness sweep — only if the site links out (see §3a) | outgoing-link-audit |
| 10b | Internal-link sweep — orphaned + thin pages (run if the site grew past a handful of pages) | internal-link-audit |
| 10c | Launch check: the verdict before launch (🟢 / 🟡 / 🔴), and for a site that replaces an older one the current-vs-new comparison with its redirect map; §4 starts after 🟢, or a 🟡 the owner accepts | website-launch-check |
| 11 | Launch & handoff: schema/sitemap/robots, deploy, hand over | this skill §4 |
| 12 | Post-launch: register with Google Search Console + Bing, submit sitemap, enable IndexNow | search-console-setup |
| 12a | Optional, once the production build is Active: the live re-check (HTTPS, the 404 status, speed on the live domain as website-qa §2 measures it, the new site's row of the comparison) | website-launch-check |
| 13 | Post-launch: claim Google Business Profile + Bing Places, verify sameAs profiles resolve — only if the site is a claimable entity (see §4a) | business-listings-setup |
| 14 | Post-launch: ask once for a "Built with Croftweaver" credit in the footer — a request, never a condition (see §4b) | this skill §4b |
Work out positioning (step 2) before any content or SEO — it decides what the copy is even trying to say. Build the Content Guide (step 3) before writing page copy; positioning and voice are inputs to everything downstream, not an afterthought.
The build is test-driven, not test-after: the suite is green from commit 1,
and steps 6–7 run as a loop per page (red → green → commit, see §3 step 5 and
website-qa §1b). Step 7 in the table is the final full-suite gate, not the
first time tests run.
2a. Story layer — offer once, right after positioning
website-story is optional and the owner has usually never heard of it, so explain
before asking. Once POSITIONING.md is filled (step 2) and before the content guide
(step 3):
- Open
website-story/SKILL.md(in the folder next to this skill's folder) with your file tools (§1), read "The offer" and say it in the language the owner writes in (it explains the idea in plain words: the visitor as the hero, the brand as the guide, a one-liner, a three-step plan, one repeated call to action, positioning unchanged). Do not paraphrase it into jargon. - Ask once, as a plain message in the chat (not a pick-list box: on a first run the owner may not see one and think you have stopped), with the two answers written out: Yes, build the home page as a story / No, standard home page (default). Wait for the reply.
- Yes → run
website-storynow, before step 3, soCONTENT_GUIDE.md's home row andcopywritingreadSTORY.md. - No, or no answer → say nothing more about it. The skill still travels with the repo (§3 step 3), so it can be run later whenever the owner asks.
- Record the answer either way ("home page: story-led, see STORY.md" or "home page: standard, story layer declined") with the decision-interview answers when §3 writes the project README, so a later session knows the offer was made and does not ask again.
Ask exactly once per build. Never run it unasked.
2b. Colours and logo — ask once, after positioning and the story
website-brand-kit is optional. It is a plain question, not a pitch. Once
POSITIONING.md is filled (and §2a is answered) and before the content guide (step 3):
- Open
website-brand-kit/SKILL.md(in the folder next to this skill's folder) with your file tools (§1), read "The question" and ask it, as it stands there, in the language the owner writes in, as a plain message in the chat (not a pick-list box, as in §2a), and wait for the reply. - She has a part and wants it used → note the path of her logo file, which must be an SVG, or her colours as hex values. No prompt is written for that part. Her own colours still go through the skill's check (its §3 step 3) now.
- She wants help with a part → run now, before step 3, the steps before §6 that
website-brand-kit§0's table names for her answer, and only those (help with colours alone does not start the logo steps, and the other way round), so the palette exists whenBRAND.md's "Brand in one line" is written. These steps are a conversation: nothing is written into a project yet (there is none). - She lacks a part and wants no help with it → say nothing more about it. The site keeps the starter's colours and the scaffold's placeholder icons for that part. The skill still travels with the repo (§3 step 3).
- Record the answers either way with the decision-interview answers (what she has, what she wanted help with, the path of her own logo file or of her package, and, where she took the help, the palette and the variation she picked), so a later session knows the question was asked and does not ask again. Until §3 step 4 writes them into the project they live in this conversation: do that step in the same session, or write them into a note in the folder you are working in.
- The install comes later: §3 step 5, on the green baseline.
Ask exactly once per build. Never run it unasked.
3. Scaffold the project (handoff-ready)
Prerequisites (one-time). Walk the user through templates/SETUP.md if needed —
Node/git/gh/wrangler + image tools, a private GitHub account, a Cloudflare
account. The accounts are a human action the agent cannot do. When the owner sets up
GitHub, tell them plainly that the account and the website in it are theirs, and recommend
two-factor sign-in with a passkey to keep others out (SETUP.md §1 has the link); for
Cloudflare, which decides whether the site is online, the same with a security key or the
device's fingerprint or face unlock. Recommend, never require: GitHub makes it mandatory only
for some accounts, and Cloudflare leaves it optional unless an account's administrator
enforces it for members.
Assemble the project at <site>/ so it travels without any global setup. <site> is a new
folder inside the owner's websites folder, which is the folder this session was opened in
(never that folder itself). Before step 0, unless the owner has already confirmed it in this
session, look at the folder you are in. If it is hidden (a part of its path starts with a
dot, such as ~/.gemini/…) or is a tool's own scratch or temp folder, don't ask about it:
the owner could not find the site there. Ask them to make a folder they can see, for
example Websites in their Documents folder, and to tell you its name. Otherwise say which
folder you are in and ask whether it is their websites folder. If it is not, or it already
holds a site (an astro.config.* or index.html file) or is the toolkit folder (it has
skills/new-website/SKILL.md), ask which folder is their websites folder instead. A folder
the owner names themselves counts as their websites folder; if it is hidden or a tool's
scratch or temp folder, say they may not find the site there again, and ask once whether to
use it anyway. Find this out
by listing the folder with your file tools (§1). Start step 0 from the websites folder,
and once <site> exists, tell the owner its full path (the output of pwd inside it):
- Git first, before any code:
# Resolve where the suite is installed (the cp SOURCE). Per tool: # ~/.claude/skills = Claude Code · ~/.agents/skills = Codex · ~/.gemini/config/skills = Antigravity # Honour an explicit $SKILLS_ROOT; else auto-detect. First the folder this very skill was # loaded from: in the first candidate of the loop below, Claude Code puts this skill's # own folder where the name in braces stands, which also finds the suite when it is # installed as a plugin and so sits in none of the three folders above. Claude Code # replaces the name only where nothing follows it inside the braces, so the line before # the loop reaches every assistant as written: where the name was left alone, it gives # the shell an empty value for it instead of an unset variable, the candidate reads # "/..", and the loop passes over it. Then Claude Code → Antigravity → Codex # (Claude is the default/primary), and last the suite installed as a Codex plugin, in # Codex's folder (CODEX_HOME or ~/.codex, written as the shell names it after cd, as # SKILLS_ROOT is, so the two compare). An update leaves one version there; should there # be more, the last in name order is taken (0.9.0 sorts after 0.33.0). It is found with # `find`, because a pattern that matches nothing stops zsh with an error, and a failed # search leaves it empty, so the candidate reads "/.." as well. On a machine with # more than one installed, set it yourself — e.g. `export SKILLS_ROOT=~/.agents/skills` (Codex) or # `~/.gemini/config/skills` (Antigravity); Antigravity workspace installs use # `export SKILLS_ROOT="$PWD/.agents/skills"`. codex_home="$(CDPATH= cd -- "${CODEX_HOME:-$HOME/.codex}" 2>/dev/null && pwd)" || codex_home="${CODEX_HOME:-$HOME/.codex}" if [ -z "${SKILLS_ROOT:-}" ]; then SKILLS_ROOT="$HOME/.claude/skills" : "${CLAUDE_SKILL_DIR:=}" codex_plugin="$(find "$codex_home/plugins/cache/croftweaver/croftweaver" -mindepth 2 -maxdepth 2 -type d -name skills 2>/dev/null | sort | tail -n 1)" || codex_plugin= for d in "${CLAUDE_SKILL_DIR}/.." "$HOME/.claude/skills" "$HOME/.gemini/config/skills" "$HOME/.agents/skills" "${codex_plugin:-/..}"; do [ "$d" != /.. ] && [ -d "$d/new-website" ] && SKILLS_ROOT="$(cd "$d" && pwd)" && break done fi # Where bundled skills go IN the generated project (the cp DESTINATION). Claude default # (.claude/skills). Codex and Antigravity read repo-scoped skills from .agents/skills, so # derive that when the suite was installed through either of them. Force it with: # export PROJECT_SKILLS_DIR=.agents/skills if [ -z "${PROJECT_SKILLS_DIR:-}" ]; then case "$SKILLS_ROOT" in "$HOME/.agents/skills"*|"$HOME/.gemini/config/skills"*|*/.agents/skills*|"$codex_home"/plugins/*) PROJECT_SKILLS_DIR=".agents/skills" ;; *) PROJECT_SKILLS_DIR=".claude/skills" ;; esac fi mkdir <site> && cd <site> && git init cp "$SKILLS_ROOT"/new-website/templates/.gitignore . git add .gitignore && git commit -m "chore: init repo with .gitignore" - Scaffold + overlay: run the scaffold without questions, so it never waits for a key,
then put back step 0's
.gitignore, which the scaffold replaces with its own (that one lacks the lines keeping.env.*,.dev.varsand sign-in files out of git):
(The firstnpm create --yes astro@latest . -- --template minimal --no-install --no-git --yes --skip-houston git checkout -- .gitignore--yesanswers npm's own "Ok to proceed?", the second create-astro's questions; checked with npm 11.20.0 and create-astro 5.3.1, run fresh with no keyboard input.minimalis Empty, with TypeScript strict.git checkoutrestores the file step 0 committed. Nothing is installed yet:templates/astro/README.mdstep 3 runsnpm installafter the overlay.) Then copy thetemplates/astro/overlay (src/,tests/,public/,functions/,scripts/,.github/,.nvmrc,.gitattributes,CROFTWEAVER-LICENSE.md(the licence of these copied files; it stays in the site's top folder), root configs — seetemplates/astro/README.mdfor exact steps and npm deps). Set the real domain inastro.config.mjs(site:),src/config.ts(url),public/robots.txt(theSitemap:line) andpublic/llms.txt(everyhttps://example.comlink); the firstnpm testfails whilerobots.txtorllms.txtstill namesexample.com. SetSITE.localeinsrc/config.tsto match the interview's Q4a content-language answer (andlanginBase.astrotoo, if not runningastro-i18n-setup).'de'flips the footer's privacy link to/datenschutz— do the privacy→datenschutz swap in the SAME step (rename the in-repo draftsrc/pages/_datenschutz.astroper its header: page file,tests/_helpers.tsPAGES,public/llms.txt,OWN_CARD_EXEMPT,POSITIONING_EXEMPT+UNFILLED_UNTIL_LAUNCHin the specs) or every intermediatenpm testfails navigation on the footer link. The overlay shipslocale: 'en', and leaving that default in place for a German-content (or other non-English) site is exactly the silent-default bug Q4a exists to catch. This starter needs Node ≥22.23.4, and Node's current security fixes are in 22.23.4, 24.22.1 and 26.12.1 (one per line). The overlay's.nvmrcpins 24 for local + Cloudflare Pages builds. Ifnpm installprintsEBADENGINEfor the site's own package (thename@versionin the site'spackage.json, whatever the owner named it) andnode, stop and have the owner update Node (SETUP.md) before going on; a warning for another package is not this. - Permissions + working rules. Copy the setup guide into the project (all tools), then — Claude Code
only — copy the allowlist so routine
npm/astro/playwright/git statuscalls don't prompt (a commit still asks; it blocksrm -rf, force pushes,wrangler … delete,gh repo delete: Claude Code refuses them, and the owner runs one by hand if needed):
Codex / Antigravity: skip thecp "$SKILLS_ROOT"/new-website/templates/SETUP.md . # all tools — receiving party can set up too cp "$SKILLS_ROOT"/new-website/templates/PUBLISHING.md . # owner "how to publish" + assistant deploy guardrails cp "$SKILLS_ROOT"/new-website/templates/AGENTS.md . # working rules for every assistant (Codex + Claude) cp "$SKILLS_ROOT"/new-website/templates/CLAUDE.md . # one line: @AGENTS.md # Claude Code only: mkdir -p .claude/hooks cp "$SKILLS_ROOT"/new-website/templates/claude/settings.json .claude/settings.json cp "$SKILLS_ROOT"/new-website/templates/claude/hooks/git-stand.mjs .claude/hooks/ # sync hook the settings register.claude/settings.jsoncopy — it's Claude Code-specific. Use their own approval systems instead (Codex:AGENTS.md+ Codex rules/config; Antigravity: its sandbox approval model). For Claude's allow/deny model and how to extend it safely when a prompt keeps recurring, usewebsite-permissions. FillAGENTS.mdnow, per the scaffold note at its top: site name, live URL, the preview URL (two-stage:main.<project>.pages.dev; single-stage: "pull-request previews only,<branch>.<project>.pages.dev" —mainis live there; where Cloudflare gives the project an address other than its name, use that address, which the project's page and the setup script's report name, so update it once the project exists),[TITLE_SUFFIX]= the| {SITE.name}stringBase.astroappends,[SUFFIX_LENGTH]= its length, and[TITLE_MAX]= 60 minus that length; keep ONE publish-model block in its §2 (the interview's Q6 answer) and delete the other. §5 (collaborators, rights level, merge rule, who publishes) ships with single-owner defaults, not slots;website-team-setuprewrites it when a team forms.AGENTS.mdstays English for every owner, like the skills: agents read it, and its Language rule has them reply in the person's language. Only the human-facingPUBLISHING.mdgets translated (see §4). Skills travel with the repo — copy the twenty-nine always-on skills in, plus any conditional setup skills selected by the interview, so the handoffs resolve for the receiving party. "Always-on" here means always copied into the project, not necessarily always run:
business-listings-setuptravels with every repo but only executes when §4a's gate says the site is a claimable entity — it still needs to be in the repo so a later session can run it once that's true. The always-on set is the seven corewebsite-*siblings,website-positioning-check(always copied, optional to run), the three SEO-depth skills they delegate to —ai-seo,schema-markup,seo-audit—site-architecture(IA), the three marketing skills the pipeline delegates to —customer-research,copywriting,image— plusoutgoing-link-audit(external link sweep),internal-link-audit(orphan/thin-page sweep),og-images(per-page share cards),website-permissions(allowlist),search-console-setup(post-launch GSC/Bing/IndexNow),business-listings-setup(post-launch Business Profile/Bing Places/sameAs— gated per §4a),website-motion(optional polish — copied so the recipient can opt in later; it never runs on its own),website-story(optional story layer — same rule: copied, never runs unasked; offered once at §2a),website-scorecard(optional public scorecard — same rule: copied, never runs unasked),website-brand-kit(optional colours and logo — same rule: copied, never runs unasked; asked about once at §2b),website-tagline(optional line under the logo — same rule: copied, never runs unasked),website-new-page(adding or rewriting a page — same rule: copied, never runs unasked; it runs when the owner asks for a new page, an article or a rewrite),website-launch-check(the launch check: run at steps 5a, 10c and 12a, and whenever the owner asks), andwebsite-team-setup(copied so the day a second person joins, the session that sets up the team finds it; it never runs on its own either):$SKILLS_ROOTentries are often symlinks (e.g. amake installcheckout symlinks each skill from this suite repo) — usecp -RLto dereference them, notcp -R, or the copy ships broken symlinks pointing back at the developer's own machine instead of a self-contained skill directory:mkdir -p "$PROJECT_SKILLS_DIR" cp -RL "$SKILLS_ROOT"/website-positioning \ "$SKILLS_ROOT"/website-positioning-check \ "$SKILLS_ROOT"/website-content-guide \ "$SKILLS_ROOT"/website-seo-geo \ "$SKILLS_ROOT"/website-design-system \ "$SKILLS_ROOT"/website-testimonials \ "$SKILLS_ROOT"/website-qa \ "$SKILLS_ROOT"/website-review \ "$SKILLS_ROOT"/ai-seo \ "$SKILLS_ROOT"/schema-markup \ "$SKILLS_ROOT"/seo-audit \ "$SKILLS_ROOT"/site-architecture \ "$SKILLS_ROOT"/customer-research \ "$SKILLS_ROOT"/copywriting \ "$SKILLS_ROOT"/image \ "$SKILLS_ROOT"/outgoing-link-audit \ "$SKILLS_ROOT"/internal-link-audit \ "$SKILLS_ROOT"/og-images \ "$SKILLS_ROOT"/website-permissions \ "$SKILLS_ROOT"/search-console-setup \ "$SKILLS_ROOT"/business-listings-setup \ "$SKILLS_ROOT"/website-motion \ "$SKILLS_ROOT"/website-story \ "$SKILLS_ROOT"/website-scorecard \ "$SKILLS_ROOT"/website-brand-kit \ "$SKILLS_ROOT"/website-tagline \ "$SKILLS_ROOT"/website-new-page \ "$SKILLS_ROOT"/website-launch-check \ "$SKILLS_ROOT"/website-team-setup \ "$PROJECT_SKILLS_DIR"/ # The brand kit's scripts/tests is the toolkit's own test suite (about 1.7 MB). No site # runs it, so it stays behind; whats-new.sh --refresh leaves out the same folder. rm -rf "$PROJECT_SKILLS_DIR"/website-brand-kit/scripts/testsThe global copies stay the updateable source of truth; the project copies are the frozen handoff set.
Conditional setup skills — run the matching line ONLY when the interview selected it (they don't ship with a declared one-language, CMS-free site without a contact form; a multilingual-PHASED site is single-locale at scaffold time and still gets astro-i18n-setup):
# If Q3 = "non-technical editor" (Keystatic): cp -RL "$SKILLS_ROOT"/keystatic-setup "$PROJECT_SKILLS_DIR"/ # If Q4 = "2+ languages at launch" OR "multilingual, one language first" # (phased rollout): the phased site NEEDS the skill vendored from day 1 — # its Phase 2 instruction is "run astro-i18n-setup when translations are # ready", which can't resolve if the skill was never copied. cp -RL "$SKILLS_ROOT"/astro-i18n-setup "$PROJECT_SKILLS_DIR"/ # If Q2 = a contact form: cp -RL "$SKILLS_ROOT"/website-contact-form "$PROJECT_SKILLS_DIR"/ # If the owner wants photos that rotate by themselves (not an interview question: on request): cp -RL "$SKILLS_ROOT"/website-hero-carousel "$PROJECT_SKILLS_DIR"/Sanity check (always, once all copying above — the primary batch AND any conditional skills — is complete) — confirm nothing was copied as a symlink:
find "$PROJECT_SKILLS_DIR" -maxdepth 1 -type l # Must print nothing. Any output means a skill copied as a symlink, not a # real directory — the handoff repo would ship broken links back to this # machine. Re-run the copy for the listed name(s) with cp -RL.Stamp the copies (always, after ALL skills are copied) — records which suite state they came from, so
make whats-new PROJECT=<site-dir>(the site's full path) in the suite repo can later report which bundled skills have upstream updates:# The suite's scripts/whats-new.sh is the ONLY writer of the stamp format. Resolve # the suite clone via the symlink and verify it really is the suite — a copied # (non-symlink) install sitting inside some other git repo must NOT stamp that # repo's HEAD. SUITE_SRC="$(readlink "$SKILLS_ROOT/new-website" 2>/dev/null || echo "$SKILLS_ROOT/new-website")" SUITE_REPO="$(git -C "$SUITE_SRC" rev-parse --show-toplevel 2>/dev/null || true)" if [ -n "$SUITE_REPO" ] && [ -f "$SUITE_REPO/scripts/whats-new.sh" ] \ && [ -d "$SUITE_REPO/skills/new-website" ]; then bash "$SUITE_REPO/scripts/whats-new.sh" --stamp "$PROJECT_SKILLS_DIR" # Also stamp the FROZEN template copies (tests/*.spec.ts, CONTENT_GUIDE.md): # whats-new reports their upstream drift separately — --refresh never touches # them, they're merged by hand (they may carry site edits like the PAGES list). bash "$SUITE_REPO/scripts/whats-new.sh" --stamp-tests tests else # No usable suite clone (zip/copy install): record that the baseline is unknown. # whats-new can't report for this project until re-stamped from a real clone. printf 'suite_commit: unknown\ncopied: %s\n' "$(date +%Y-%m-%d)" \ > "$PROJECT_SKILLS_DIR"/SUITE-VERSION fi- Docs — copy
templates/positioning.md→POSITIONING.md,templates/content-guide.md→CONTENT_GUIDE.mdandtemplates/brand.md→BRAND.md; fill the[BRACKET]slots in pipeline steps 2–3.STORY.mdexists only if the owner opted in at §2a;website-storycopies it from its owntemplates/story.md.TAGLINE.mdexists only if the owner asked for a tagline;website-taglinecopies it from its owntemplates/tagline.md. If the owner answered §2b: write her answers into the project README beside the decision-interview answers, and, where she chose or brought a palette, fillBRAND.md's colour tables with it (website-brand-kit§5a says what goes where). These documents are all that step 2b writes before the tests are green. Confirm green:
npm run build && npm test(the overlay passes the a11y/seo/navigation/anchors/orphans/images/tone/positioning/placeholders/email/links/llms-coverage/middleware suite out of the box). Then build pages test-first: add the route totests/_helpers.tsPAGESbefore writing the page (suite goes red), build until green, commit. New features get their test first too —website-qa§1b maps feature → test. Tell the owner about the generator tag, once, after this first green run, unless the project README already has a "generator tag:" line. The starter writes<meta name="generator" content="Croftweaver">into every page (GENERATOR_TAGinsrc/config.ts). Say it in one plain chat message, in these words:One small thing you can't see: your pages' code has a tiny name tag that says "made with Croftweaver". People who visit your site won't see it on the page. It doesn't send anything to anyone and doesn't link anywhere. If you'd rather not have it, just say "remove the Croftweaver tag" and I'll take it out.
Then record "generator tag: on" with the decision answers in the project README (told twice after an interruption is harmless; never told is not). If the owner asks to remove it, now or later: set
GENERATOR_TAGtofalse, runnpm test, and change the line to "generator tag: off". It is not the credit of §4b, and it is told, not asked. If the owner has a palette or a logo from §2b, runwebsite-brand-kit§6 (the intake) now, on the green baseline and before the first page: it checks the palette and the logo, writes them into the site, runs these same tests again and puts every file back if they go red. Commit once it is green.
3a. Outgoing-link sweep — ask, but only if the site links out
The offline tests/links.spec.ts guard ships in the suite and runs in CI from commit
1 — nothing to decide there. The liveness sweep (outgoing-link-audit) is
network-dependent and optional, so gate it on whether the built site actually has
external links, and let the user choose:
cd "$(git rev-parse --show-toplevel)"
[ -d dist ] || npm run build >/dev/null
SITE_HOST=$(grep -oE "site:[[:space:]]*['\"]https?://[^'\"]+" astro.config.mjs \
| sed -E "s#.*://(www\.)?##; s#/.*##" | head -1)
EXT=$(grep -rhoE '<a [^>]*href="https?://[^"]+"' dist --include='*.html' \
| grep -oE 'href="https?://[^"]+"' | sed -E 's/^href="//; s/"$//' \
| grep -vE "://(www\.)?${SITE_HOST}(/|$|:)" | sort -u | wc -l | tr -d ' ')EXTis 0 → the site links only to itself. Say so and skip silently — do not ask.EXT≥ 1 → ask whether to run the liveness sweep now, as a plain message in the chat (not a pick-list box, §1), e.g. "The site has N outgoing links. Run theoutgoing-link-auditliveness sweep before launch? (fetches each third-party URL; ~a few seconds each)" — options Run it now (recommended) / Skip — I'll run it monthly. If they say run it, invoke theoutgoing-link-auditskill; otherwise note it as a monthly follow-up.
4. Launch & handoff checklist
Deploy-time guardrails — templates/PUBLISHING.md § "For AI assistants — deploy-time guardrails"
That section is the single source of truth — §3 copies PUBLISHING.md into every site, so
the post-handoff agent carries it too (the scaffolded README's deploy section points there).
Read it in full before the FIRST deploy, when a brand-new page goes live, and when a live
URL looks stale; this summary covers routine pushes.
Always announce whether a push is PREVIEW or LIVE (two-stage sites: "this
is NOT live yet" + the preview URL on every push to main; live only after
npm run ship AND the production build finishes). Never request — or
announce — a brand-new page's URL on the live domain before its build is
Active: the first request — the owner clicking your announcement counts —
caches a 404 at the edge that only a manual dashboard purge clears. Verify on
the hash deployment URL until Active, touch the bare canonical URL last, and
hold Search Console Request Indexing until then.
- All QA green:
npm test, with no "not ready to launch" warning:UNFILLED_UNTIL_LAUNCHintests/placeholders.spec.tsis empty. The starter lists the privacy page, the imprint and the manifest there until their[BRACKET]slots are filled; each entry comes out when its target is filled and, for the two legal pages, once the owner has answered the question below and the draft notice is cleared (the test turns red until it does). An entry still listed at launch ships that placeholder: the test only warns about a listed target. And an empty list is not the whole check: the spec has no rule for the starter's example values, so read the home page and both legal pages once for the nameExampleand the addresshello@example.com, andpublic/llms.txtfor its own[bracket]slots. - Nothing left unshipped (two-stage sites):
git log origin/production..origin/mainis empty — merged-but-unpromoted work is invisibly unshipped. If not empty and it should ship:npm run ship(which also VERIFIES the live site serves the new build via/build.txt— push alone is not proof of live). - Double-Knuth review clean (
website-review): both passes — run it as the final gate AND after adding the last page. - Launch check verdict (
website-launch-check, step 10c): 🟢, or 🟡 with the owner's yes in chat; the report isdocs/launch/launch-check-<date>-prelaunch.md(+.html), and for a relaunchdocs/launch/launch-comparison-<date>.md. Its "not checked yet: not deployed" rows wait for the live re-check (12a). - Lighthouse / PageSpeed run; FCP & LCP within target (see
website-qa). - Outgoing links healthy (
outgoing-link-audit) — only if the site links out (§3a). No DEAD/REBRAND left unhandled; any retired domain added totests/links.spec.tsSTALE_DOMAINS. - No orphaned pages —
tests/orphans.spec.tsgreen (every page reachable from home). For a multi-page site, runinternal-link-auditand resolve orphans + thin pages; deliberately-unlinked pages listed inORPHAN_EXEMPTwith a reason. sitemap-index.xmlpresent;robots.txt+llms.txtaccurate. (llms.txt coverage — everyPAGESroute listed, no stale entry for a removed or renamed page — is enforced both ways bytests/llms-coverage.spec.ts; wording accuracy stays a human check.)- Required schema validates (Rich Results / schema.org validator).
- OG share cards generated (
npm run og, theog-imagesskill): a 1200×630 JPEG ≤300 KB per page (public/images/og/), each page wiringimage=(or the default).tests/seo.spec.tsenforces size/dimensions; WhatsApp drops previews over ~300 KB. Plus favicon/manifest icon set in place (no logo yet: an interim letter-mark built aswebsite-brand-kit/references/tab-icon.mdsays), and the manifest's[BRACKET]fields (name/short_name/description) filled in the site's content language withlangset fromSITE.locale—placeholders.spec.tscatches a bracket left there, but no spec reads the manifest's language, so English in a German site's manifest ships silently otherwise. - Imprint/legal + privacy pages present (EEAT trust + DE legal requirement).
The starter ships a GDPR privacy draft (
src/pages/privacy.astro): every[BRACKET]slot filled, the analytics section matching the real setup. No analytics: the analytics paragraphs deleted,USE_ANALYTICS = falseas shipped. With Plausible:USE_ANALYTICS = true,bash tests/check_analytics_gate.shgreen, and theEmail Tapgoal exists in Plausible (§1 Q5). In script mode (the default) also the host inscript-srcandconnect-srcofpublic/_headers. In server mode also:PLAUSIBLE_HOSTandPLAUSIBLE_DOMAINset as Production variables (and nowhere in script mode or with analytics off), the host left out ofpublic/_headers(no script loads), the privacy page's server-mode slots kept, and one page view and one email tap on the live domain seen under Today. Whether the site may run without a consent banner: §1 Q5's last point. German-market sites: don't re-translate — swap in the vetted German drafts that ship in the site repo, whose own file headers carry the exact steps:src/pages/_datenschutz.astro(§ 25 TDDDG-aware; underscore = unrouted until renamed; German-only sites serve it at/datenschutzREPLACING/privacy, multilingual sites use its text at/de/privacy) andsrc/pages/impressum.astro(§ 5 DDG + § 18 Abs. 2 MStV — required for providers established in Germany, whatever the site's language, and for sites targeting the German market; fill/delete per its header and section comments, which also cover Austrian/Swiss adaptation and the six-piece removal for providers with no German nexus). Ask the owner about the drafts, once, before the deploy item below, asAGENTS.md§5 says (the site carries it; the text is not repeated here). The imprint and the privacy page carry a draft notice until the owner has answered. - Deployed to Cloudflare Pages per the chosen publish model (§1 Q6).
Two-stage: create the live branch (
git checkout -b production && git push -u origin production && git checkout main— end back onmain), then in Cloudflare set Production branch =production— somain= noindexed preview,production= live; analytics, once switched on, fires on production only. Single-stage:main= live (default Cloudflare production branch). Creating the Pages project + first deploy (the step a Cloudflare newcomer struggles with) — offer the bootstrap options inreferences/CLOUDFLARE_FIRST_DEPLOY.md: for a site on GitHub, recommend the setup script (scripts/cloudflare-setup.mjs setup: one short-lived token, the owner's yes to a printed list, a project connected to GitHub with the checklist's settings; the script'slaunchstage attaches the domain on launch day), with the token-assisted direct upload (no GitHub connection; you run Wrangler), git-integration by hand and browser/manual as the other offered paths. Either way: hand the ownerPUBLISHING.mdand walk the first publish with them. Non-English-speaking owner? TranslatePUBLISHING.mdin-session first (e.g. save asPUBLISHING.de.md, or replace the copy) — it is the owner's PERMANENT reference, unlike your conversational guidance, and the shipped template is English. Any translation or replacement MUST keep the "For AI assistants — deploy-time guardrails" section (translated is fine, dropped is not — it is the post-handoff agent's only copy of those rules). - Dependabot security fixes on for the site's GitHub repo: run
SETUP.md's Dependabot lines from the site folder; the output must name this repo and showalerts: onandsecurity updates: on. Not skippable: the site's lockfile is its own, so no suite update ever patches it. The starter also ships.github/dependabot.yml(monthly, grouped version updates; a new major of any package but the two held back comes as its own pull request; Dependabot raises no new version pull request while five are open, so merge or close them): keep it if someone will merge the monthly pull request (name them in the site's README), delete it if not; security updates stay on either way. <project>.pages.devredirects to the live domain: once the live domain serves this build, Production variableCANONICAL_URLset and redeployed;curl -sIon the alias shows301(references/CLOUDFLARE_FIRST_DEPLOY.md, "After go-live").- Search engines notified (
search-console-setup): live domain added to Google Search Console (Domain property + DNS TXT) and Bing (import from GSC),sitemap-index.xmlsubmitted to both, and IndexNow on (Cloudflare Crawler Hints toggle). Register the production domain only — never a preview host. - Business listings claimed, if applicable (
business-listings-setup, §4a):sameAsverification done for any named entity — every URL in schema confirmed live or manually verified logged-out, per that skill's own verification rules; directories claimed where a suitable one exists for the category; Google Business Profile and Bing Places live only if the entity actually qualifies (the skill's §1 step 0 checks). "No relevant directory found" and "not eligible" are valid, non-blocking outcomes — distinct from "skipped by owner choice". - Repo self-contained for the receiving party:
.gitignore,.claude/,POSITIONING.md,CONTENT_GUIDE.md,BRAND.md(+STORY.mdif opted in),tests/,SETUP.md,PUBLISHING.md(with its "For AI assistants" guardrails section intact),AGENTS.md+CLAUDE.md(no[BRACKET]slot left, the scaffold note removed, one publish-model block kept in §2), and aREADME.mdwith the decision answers + "how to add a page / run tests / deploy". - Credit settled (§4b), after the site is live: asked once, or found already
in the footer, and the outcome recorded in the project
README.md. "Declined" is a complete, valid outcome.
4a. Business listings — ask, but only if the site is a claimable entity
business-listings-setup claims category directories and verifies the site's
sameAs schema resolves — both apply to any named entity. Google Business
Profile and Bing Places are narrower: they need a real address customers visit,
or staff who travel to serve customers, which the skill's own §1 checks. Naming
the entity is enough to run the skill; it is not enough to guarantee a Google/Bing
profile will follow, and the ask below says so. Gate it the same way as the
outgoing-link sweep in §3a: check a condition, then let the user choose rather
than assuming.
- The site has no claimable identity at all (a personal blog with no business or professional practice behind it, a resource hub with no identity of its own) → there is nothing to claim. Say so and skip silently — do not ask. A sole proprietor or consultant who trades under their own name (a photographer, a freelancer) still counts as a named entity here — the test is "is there a business or practice to claim," not "is the name different from a person's."
- The site represents a named, real-world entity (an event series, a studio, a
local business, a community or organization with its own name) → ask whether to
run it now — e.g. "This looks like a named entity (
<name>) that could claim directory listings and verify its schema profiles. It may also qualify for a Google Business Profile if it has a real address or serves customers in person — the skill checks that itself. Want to set this up now? It needs a few clicks from you at each platform — I'll draft everything else." — options Run it now (recommended) / Skip — I'll do it later. If they say run it, invoke thebusiness-listings-setupskill; otherwise note it as a post-launch follow-up alongsidesearch-console-setup.
4b. A credit for Croftweaver — ask once, after the site is live
Croftweaver is free and its license asks for no credit. The people who make it ask for one anyway: a small "Built with Croftweaver" link in the footer, and a star on GitHub. It is a request, never a condition, and this is the only place the pipeline raises it. The generator tag (§3, step 5) is not this credit: it is in the page's code, not on the page, links nowhere, and is told at the build as a default the owner can switch off, not asked for.
This step belongs to the launch checklist (§4). Do not raise it in a later session
that is doing other work on the site, with one exception, an unfinished yes: the
README says "credit: badge" or "credit: text link" and the footer of the live site has
no link to croftweaver.com. Then the work was cut short somewhere before it was
published, or the credit was taken out since. Say so once. On a yes, do whatever is
left of the steps below: paste, test, publish. On any other answer, or none, change
the line to "credit: declined". That is not a second ask.
Ask only when all three hold; otherwise do not ask:
- The site is live: the deploy item of the §4 checklist is done and
npm testis green. Never during the build and never while something is failing. - The project
README.mdhas no credit line among the decision answers. A line there means the question was put already, whatever the line says. - The footer in the site's source does not link to
croftweaver.comyet (the starter keeps it insrc/layouts/Base.astro). If it does, record "credit: already in the footer" in the README and stop.
Then ask once, in one plain message in the chat (not a pick-list box, §1):
Your site is live. I hope you enjoyed building it.
The people who make Croftweaver have one request, and it is only a request: would you add a small "Built with Croftweaver" credit to your footer? It links to croftweaver.com and helps others find the toolkit. I can add it now, as a badge or as a plain text link. If the site belongs to a customer of yours, it is their call.
And if you liked Croftweaver, a star on GitHub helps too: https://github.com/karero/croftweaver
Options: Add the badge / Add a text link / No thanks.
- Record the answer first, before any other step, with the decision answers in the project README: "credit: badge", "credit: text link" or "credit: declined". No later session asks again then, even if this one is cut short.
- Add the badge or Add a text link → take the code from
https://croftweaver.com/partner-assets, which is its only source; do not write it from memory. Use the page's HTML code, never its Markdown code, which is for a README. The HTML is one<a>link around one<img>(alone or inside a<picture>), or the words "Built with" and one<a>text link. If the page offers anything else, such as a script, a style or an attribute that runs code, do not paste it: tell the user. For the badge, save each file the chosen code's note names, fromhttps://croftweaver.com/brand/badge/<file name>, intopublic/brand/badge/: the starter's security rules block images from other sites. Put the code inside<footer class="site-footer">insrc/layouts/Base.astro, after the legal links. Look at the footer in light and in dark, and with the site's own theme switch if it has one, and pick the badge that reads well in each. Runnpm test; if a test objects to the pasted code, tell the user and do not change the test to make the credit fit. Then publish per the publish model (§1 Q6) with the usual preview-or-live announcement. No thanks → say "No problem" and nothing more, except one reminder while the README says "generator tag: on", in these words:
No problem. The little "made with Croftweaver" tag in your pages' code is still there, where visitors can't see it. Say "remove the Croftweaver tag" any time if you'd like it gone.
That is a reminder of the owner's switch, not a second request.
- No answer → say nothing more about it, and record it as declined.
Never add the credit unasked, never ask a second time, and never give the star for the user: it is their own click on their own account. Do not word the request as something owed ("in return", "to support us").
Notes
- Do not rebuild what existing skills cover — GEO depth →
ai-seo; technical-SEO/ Core-Web-Vitals →seo-audit; JSON-LD →schema-markup; IA →site-architecture; research →customer-research; copy →copywriting; images →image. - After any code/copy edit, re-run
website-qa. "Done" means the tests are green, not that the file was written. - Run
website-review(the Double-Knuth two-pass audit) at the end of a build (pre-launch) and after adding/editing a page — it's the cross-file consistency gate on top ofwebsite-qa's tests (a new route can silently breakPAGES, nav, the sitemap, internal links or canonical). - Editing the skill suite itself (these templates, the test specs, or a sibling
skill) is different from reviewing a site: green tests do not catch doc↔test drift,
so run
/code-reviewbefore trusting the change. The Double-Knuth pass that hardened this suite found exactly that class of issue.
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.

