Create GitHub Pages Site
Turn "put this on GitHub Pages" into a correct, building, auto-deploying repo. The
hard part of GitHub Pages isn't the HTML — it's the deploy plumbing and the
base-path trap. This skill owns both: it stamps a vetted template, sets the
base path for the exact repo, ships the current official Pages workflow, and walks
the user through enabling Pages.
It also never stops at the template's demo content. A stamped template is a
skeleton, not the deliverable — the skill reads the target repo and authors a
site about that project: real name and pitch, the right kind of reference (CLI,
library, app, or catalog), real install/usage pulled from the repo, and labeled
image placeholders the user can drop real art into. Shipping a site that still says
"Hello, Astro" for someone's CLI is a failure, even if it deploys.
When to use which template
Pick by what the user is building. If unsure, ask one question (content vs. app)
and default to static-html for the simplest ask.
| Template |
Reach for it when… |
Tier |
Build |
static-html |
A landing page, a few hand-made pages, "just put this HTML up". No toolchain. |
static |
none |
astro |
A content site, blog, docs, or marketing page that should be fast and mostly static. |
SSG |
astro build |
react-vite |
An interactive single-page app / dashboard with client-side routing. |
SPA |
vite build |
eleventy |
A data/Markdown-driven site (blog, docs) where content is files + structured data. |
data SSG |
eleventy |
jekyll |
The user wants the GitHub-native path, or is migrating an existing Jekyll site. |
native |
Jekyll |
skills-catalog |
A browsable catalog for a repository of Copilot skills, especially when composing create-skills-repo. |
catalog |
registry-defined |
spectator |
A technical specification, RFC, architecture proposal, design proposal, engineering decision, or policy proposal built for structured review. |
SSG |
vitepress build docs |
The built-in registry pin must include the selected template. A custom registry
requires an explicit --registry-ref, and a local checkout uses
--templates-dir.
These seven cover the static / SSG / SPA / data / native / catalog / specification
quadrants. For richer
themes, point the user at upstream galleries (astro.build/themes,
jamstackthemes.dev, github.com/topics/github-pages-template) and adapt — don't try
to hand-build a theme from scratch.
The base-path trap (the thing to get right)
A GitHub project site is served from a subpath:
https://<user>.github.io/<repo>/. A site built assuming root (/) ships with
broken CSS, images, and links the moment it's a project site — assets 404 and the
page looks blank. A user/org site (<user>.github.io repo) is served from
/, so it must NOT carry a subpath prefix.
Each framework fixes this differently. The generator handles all of it from the
repo name; you rarely set it by hand:
| Template |
Mechanism |
Project value |
User-site value |
static-html |
relative URLs (./assets/...) |
— (immune) |
— (immune) |
astro |
base in astro.config.mjs |
/repo/ |
/ |
react-vite |
base in vite.config.js + basename + 404.html |
/repo/ |
/ |
eleventy |
pathPrefix via PATH_PREFIX env + url filter |
/repo/ |
/ |
jekyll |
baseurl in _config.yml + relative_url |
/repo (no slash) |
"" (empty) |
spectator |
base in spectator.config.ts and VitePress config |
/repo/ |
/ |
The generator detects the <user>.github.io user-site pattern and uses /
automatically. If the user hasn't named the repo yet, scaffold with the repo
they intend; if they truly don't know, use --base / and tell them to re-run (or
edit the base) once the repo exists.
Deployment model — one consistent flow
Every template deploys via the GitHub Actions Pages source, not from a branch.
Every external action reference must use a full commit SHA.
static-html, react-vite, eleventy use that chain directly.
astro uses the official withastro/action@v6 (it builds + produces the Pages
artifact) then actions/deploy-pages@v5.
jekyll uses GitHub's official actions/jekyll-build-pages@v1 then deploy-pages.
Every workflow limits top-level permissions to contents: read, grants Pages and
identity-token access only to the jobs that need them, uses one Pages concurrency
group, and deploys through the github-pages environment. There is no
gh-pages branch to manage.
Action versions deprecate aggressively. Update the template registry to a reviewed
action commit, then bump this skill's immutable default registry commit.
Interview first — never scaffold on a guess
This skill produces a real repo with a live deploy pipeline, so getting the inputs
right matters more than speed. Do not stamp anything until you know (a) which
template and (b) the target repo (or an explicit base path). If the prompt is bare
("make me a GitHub Pages site") or names no framework/repo, interview the user — ask
only for what's missing, one focused question at a time, using the ask_user tool:
What kind of site? Map the answer to a template:
- "a landing page / just some HTML / the simplest thing" →
static-html
- "a blog / docs / content / marketing site, fast" →
astro (or eleventy if
they say Markdown- or data-driven, or jekyll if they want the GitHub-native
path or are migrating an existing Jekyll site)
- "an app / dashboard / interactive / single-page app" →
react-vite
- "a catalog of Copilot skills / create a skills repo" →
skills-catalog
- "a specification / RFC / architecture or design proposal / engineering
decision / policy proposal / site like the Eval Authoring Guide" →
spectator
If they're unsure, ask the single discriminating question — content site or
interactive app? — and default to static-html for the simplest ask.
Which repo? Assume the current repo by default. The site is for the repo in
context unless the user says otherwise — don't ask "current or new." Detect the
current repo from git (git remote get-url origin, parsed to owner/name); the
generator does the same automatically when you omit --repo. The repo drives the
base path, so you must resolve it before stamping:
- Current repo detected → use it. State the assumption in one line
("Scaffolding into this repo, octocat/blog → base
/blog/") and proceed; no
question needed.
- No git context, detached, or no
origin → then ask for the owner/name,
and whether it's an existing repo or a new one to create.
- Only treat it as a new/different repo when the user explicitly asks for one.
For a user site, confirm the repo is named
<user>.github.io (base /);
otherwise it's a project site (base /repo/).
Title? Optional — default to the repo name; never block on it.
You usually don't need to ask "what should the site say?" — the content comes from
the repo (you digest it after stamping; see "Digest the repo, then author it").
When the target repo is the current/an existing one, run digest-repo.mjs early to
confirm the type and let it inform the template choice (e.g. a cli repo is a great
fit for astro or static-html with a command reference). If the repo is empty or
brand-new, fall back to asking what the site should cover.
Skip any question the prompt already answered: "an Astro blog for octocat/blog"
needs no interview (template astro, repo octocat/blog). For a bare "put this on
Pages" inside a repo, assume the current repo and ask only for the template. Ask
only for the gaps. When you inferred rather than were told, confirm in one line
before scaffolding — e.g. "Astro site → octocat/blog (current repo), base /blog/ —
go?"
Output location invariant
The user's current working directory is the destination workspace. A repo slug
such as octocat/blog determines the Pages base path and URLs; it does not mean the
site belongs in /tmp/blog, a sibling directory, or the skill's own directory.
- If the workspace already contains repo files, scaffold in place with
--dir . --force, even when it is not a git checkout and the prompt supplies the
repo slug explicitly.
- Never
cd into the skill directory to run the generator. Invoke
scripts/new-site.mjs by its absolute path while keeping the workspace as the
process working directory.
- Use another output directory only when the user explicitly asks for a subfolder
or a new/different repo.
- When another generator is composing the result, use
--staging-dir instead of
--dir. The staging directory is isolated and the consumer owns the final merge.
- Before reporting completion, confirm the generated config, content, and
.github/workflows files exist under the original working directory.
The workflow you follow
- Interview / gather context. Resolve the questions above via
ask_user. You
MUST end up with a chosen template and a target repo (or explicit --base) before
stamping. Default the target to the current repo (detect it from
git remote get-url origin); only ask when there's no git context or the user
wants a different/new repo.
- Pick the template from the table above.
- Stamp it with the generator (next section). This injects the base path,
site URL, and title, and lays down the deploy workflow.
- Place it in the repo:
- Current repo (default): stamp into the repo root (or a subfolder if it's a
subdirectory site, adjusting the workflow's upload path). From the original
working directory, invoke the generator by absolute path and pass
--dir .
--force to write in place. Reconcile an existing deploy.yml rather than
blindly overwriting it.
- New repo (only when asked): create it (e.g.
gh repo create <name> --public),
stamp into it, and push. Match the repo name you used for the base path.
- Digest the repo and author the site (see "Digest the repo, then author it"
below). This is the step that makes the site real: run the digest, replace
every default page/section with repo-derived content of the right kind, add
image placeholders + an
IMAGES.md, and prompt the user for the real images
(they can paste screenshots straight into the chat). Never skip to enabling Pages
on the demo content.
- Enable Pages. Tell the user (or do it with their approval):
Settings → Pages → Source → GitHub Actions. By CLI:
gh api -X POST repos/<owner>/<repo>/pages -f build_type=workflow (or PUT to
update). After the first push to main, the workflow runs and the live URL
appears in the Actions run summary and under Settings → Pages.
- Set the repo website link to the Pages URL (the "Website" field in the repo
header — same as ticking Settings → "Use your GitHub Pages website"). This is
just the repo's
homepage; point it at the site URL the generator prints:
gh repo edit <owner>/<repo> --homepage <site-url> (or
gh api -X PATCH repos/<owner>/<repo> -f homepage=<site-url>). Offer to set the
exact URL, or let the user supply a custom domain instead. There's no separate
"use Pages" boolean — setting homepage to the Pages URL is the checkbox.
- Verify it actually works (see "Validate" below) — don't claim success on a
green workflow alone, and don't claim success while template default copy or
"Hello, world" demo content is still on the page.
Stamp a site — the generator
node scripts/new-site.mjs <template> --repo <owner/name> [options]
| Option |
Purpose |
--repo <owner/name> |
Target repo. Derives the base path + URLs (and detects user sites). Defaults to the current repo's origin remote when omitted. |
--base </path/> |
Override the base path (e.g. / for a user site or local preview). |
--dir <path> |
Output directory (default: ./<repo-name>). |
--site-name "Title" |
Human title (default: derived from the repo name). |
--description "Text" |
Catalog description (default: derived from the title and repo). |
--author "Name" |
Catalog author (default: repo owner). |
--package-name <id> |
Package identifier (default: repo name). |
--marketplace-id <id> |
Marketplace identifier (default: owner and repo). |
--default-branch <id> |
Repository default branch used by catalog links and deployment triggers (default: main). |
--registry <owner/repo> |
Template registry repo to fetch from (default: jongio/gh-pages-templates; needs git + network). |
--registry-ref <sha> |
Full 40-character commit SHA. Required with a custom registry. Branches and tags are rejected. |
--templates-dir <path> |
Use a local templates/ folder instead of fetching (offline). |
--staging-dir <path> |
Write a validated tree to a new path without applying it to a target. |
--force |
Write into a non-empty directory. |
--json |
Emit a machine-readable result for composition. |
--list |
List available templates. |
Templates are not bundled in the skill. The generator fetches the built-in
jongio/gh-pages-templates registry at a reviewed, immutable commit. A custom
registry requires --registry-ref <full-sha>. Pass --templates-dir <path> to
scaffold from a local copy offline.
Examples:
# Run from the target workspace; SKILL_DIR is this skill's absolute directory.
# Scaffold in place for the current repo (base inferred from its origin remote):
node "$SKILL_DIR/scripts/new-site.mjs" astro --dir . --force
# The workspace is an existing project without git metadata; the slug only supplies
# deployment identity and the base path. Output still goes into the workspace:
node "$SKILL_DIR/scripts/new-site.mjs" astro --repo octocat/blog --dir . --force
# Create a subfolder only when the user explicitly requested one:
node "$SKILL_DIR/scripts/new-site.mjs" react-vite --repo octocat/dashboard --dir ./site
# A user site (served from "/") or a quick local scaffold:
node "$SKILL_DIR/scripts/new-site.mjs" static-html --base / --dir . --force
# Safely compose a skills catalog without touching the consumer's target:
node "$SKILL_DIR/scripts/new-site.mjs" skills-catalog \
--repo octocat/skills \
--templates-dir ../gh-pages-templates/templates \
--staging-dir ./.site-staging \
--json
The generator replaces the shared base and repository sentinels plus the catalog
sentinels __SITE_DESCRIPTION__, __REPO_OWNER__, __REPO_NAME__,
__AUTHOR_NAME__, and __MARKETPLACE_ID__. Injection is deterministic and the
result has no placeholders left. After stamping you may hand-edit content freely.
Metadata values must be plain context-safe text. Markup, control characters, and
characters that can break JSON, YAML, JavaScript, or HTML attributes are rejected.
Safe staging contract
Use --staging-dir whenever create-skills-repo or another tool will merge the
site into a larger output. The path must not exist and the option cannot be
combined with --dir or --force. The generator performs replacement and all
validation inside staging, returns only after success, and never touches the final
target. The consumer must check every destination conflict before copying staged
files.
Before either staging success or normal apply, the generator:
- verifies an external registry checkout matches the requested full commit SHA;
- proves the resolved template path remains inside the registry templates root;
- rejects every symlink in the template tree;
- rejects unresolved
__SENTINEL__ placeholders;
- scans
.github/workflows/*.yml and .yaml for excess permissions, unsafe
triggers, non-SHA action references, persisted checkout credentials,
lifecycle-capable installs without script suppression, and direct GitHub
context interpolation in shell commands.
Normal --dir generation uses a private staging directory and applies only after
all checks pass. This preserves existing template and --force behavior while
ensuring an invalid template cannot partially modify the target.
The exact pinned built-in snapshot has a compatibility normalization for its five
legacy templates. It upgrades their known action tags to reviewed commit SHAs,
disables persisted checkout credentials and lifecycle scripts, and then validates
the result. Never apply this normalization to a custom registry.
Digest the repo, then author it (never ship the template defaults)
Stamping gives you a working skeleton with demo content. The job is only half
done. Now make the site about the actual repo. This is not optional polish — it's
the deliverable.
1. Run the digest
node scripts/digest-repo.mjs --dir <path-to-repo> --json
It returns deterministic signals you build from: name, description (the pitch),
repoSlug, license, a type classification (cli | library | app |
action | collection | docs | site) with the reasons behind it, suggested
install commands, README usageExamples (code fences), badges, docFiles,
existing images (with role hints like logo/hero/screenshot), languages,
and subProjects (for monorepos/collections). Read the repo's README and key docs
yourself too — the digest points you at them; it doesn't replace judgment.
2. Build the right kind of site for the type
| Type |
Tell-tale signals |
Author the site around… |
cli |
bin, console_scripts, [[bin]], --help in README |
Install + Quickstart, then a command/flag reference (a section or page per command), copy-paste examples, config/exit codes. Hero = terminal demo. |
library |
main/exports, [lib], import examples |
Install, an import + usage snippet, an API reference (exported functions/types from the README/docs), examples, badges. Hero = a concept diagram. |
app |
web-framework dep, index.html, src/ |
A feature tour with screenshots, a "Get started"/live-demo CTA. Hero = an app screenshot. |
action |
action.yml |
A uses: snippet, an inputs/outputs table, an example workflow. |
collection |
plugin.json/marketplace.json + skills/, workspaces, packages/ |
A catalog: one card/detail per subProject (its pitch + install), plus a top-level install for the whole thing. Hero = a banner; per-item thumbnails. |
docs |
many Markdown docs, little code |
A docs nav + content, pulling the existing Markdown in. |
site / unknown |
none of the above |
A clean landing built from the repo's pitch and links. If the shape is unclear, ask the user what sections they want. |
3. Authoring rules
- Replace every default. No template demo copy survives — not the sample hero,
not "Hello, Astro/world", not the example blog posts, not lorem. After building,
grep the output for the template's stock phrases; none should remain.
- Use real values from the digest: the repo's name and
description as the
title/tagline, the install commands verbatim, the README usageExamples as
real code blocks, badges, license, and links to the repo and each
subProject. Don't invent features, commands, or APIs you can't see in the
repo — if something's unclear, leave a visible TODO for the user rather than
fabricate.
- Fit the template's content model. Use content collections (Astro/Eleventy:
one entry per command/skill/post), pages (React/static), etc. Add or rename
routes to match the content (e.g. a
commands/ or catalog/ section) instead of
forcing everything into the demo "blog". Keep the GitHub source link and the
base-path-aware internal links the template already wires — never hand-write
absolute /... links (use the template's base helper, or it breaks on a project
site).
- Keep Spectator free-form. Do not force a standard specification outline.
Derive pages, headings, and navigation from the supplied context. Preserve the
Spectator reading and feedback system, but remove every Project Northstar sample
page, label, requirement, source, and image reference.
- Label uncertainty in Spectator. Distinguish verified current behavior,
user-approved decisions, proposals, assumptions, risks, and open questions.
Do not invent owners, dates, requirements, interfaces, or security claims.
4. Add image placeholders the user can supply
Real sites need art the agent can't produce. Drop in obvious placeholders plus
a checklist so the user knows exactly what to provide:
node scripts/make-placeholder.mjs --out <site>/<images-dir> --preset <type> --repo owner/name
--preset is the repo type (cli/library/app/collection/site). It writes
labeled SVG placeholders (logo, social card, favicon, hero, and type-specific
shots) and an IMAGES.md manifest listing each file's purpose and recommended
dimensions. Then:
- Reference them from the pages you author — hero, top-bar logo, the OG/social
meta tag, and per-item thumbnails for a catalog.
- Reuse real images first. If the digest found an existing logo or screenshot
(e.g. a
docs/*.png), use it instead of a placeholder.
- Put images where the template serves static files:
astro, react-vite → public/images/ (served at ${BASE_URL}images/…)
spectator → docs/public/images/ (VitePress applies its configured base)
static-html → assets/images/ (relative ./assets/images/…)
eleventy → src/assets/images/ (through the url filter)
jekyll → assets/images/ (via relative_url)
- Leave
IMAGES.md in the images dir as the hand-off, and tell the user it's
there. A placeholder still deploys fine; it just visibly says "replace me".
Ask for real images
After authoring, ask only for images the site references. Give each role, filename,
and recommended dimensions; accept pasted images, file paths, or URLs. Keep
unprovided placeholders, update IMAGES.md, and do not block deployment.
digest-repo.mjs discovers source material; make-placeholder.mjs creates the
placeholder set and handoff manifest.
Per-template notes
- static-html — Zero build. All links are relative, so it's base-path-proof.
Ships
index.html, about.html, 404.html, assets/, and a .nojekyll.
- astro —
site = origin, base = /repo/. Internal links use
import.meta.env.BASE_URL. public/ is copied verbatim. Node 24 in CI by default.
- react-vite —
base in vite.config.js; React Router basename derived from
BASE_URL; copy-404.mjs (a postbuild hook) copies index.html → 404.html
so deep-link refreshes work on Pages. Reference public assets as /asset so Vite
rewrites them with the base.
- eleventy —
pathPrefix comes from the PATH_PREFIX env the workflow sets;
every link/asset uses the url filter. Posts live in src/posts/, listed via
collections.posts. Locally it serves at /.
- jekyll —
baseurl (no trailing slash) in _config.yml; links use
relative_url. Built in CI by jekyll-build-pages (honors the Gemfile). Local
dev needs Ruby + Bundler; CI does not.
- spectator: VitePress reads
base from spectator.config.ts. Content is
free-form Markdown under docs/; navigation is explicit in the shared config.
Preserve local search, page outlines, light/dark appearance, plain-language
callouts, page feedback, selected-text feedback, and GitHub edit links. Run
npm test, npm run build, and npm run test:e2e.
Current repo vs. new repo
- Current repo (the default): assume the site is for the repo in context. The
generator infers the base path from its
origin remote when you omit --repo.
Put the site at the root for a whole-repo site, or in a subfolder and point the
workflow's upload-pages-artifact path: at it. If a deploy.yml already exists,
reconcile — don't blindly overwrite.
- New repo (only when asked): create with
gh repo create, stamp, push to
main. For a user site, the repo MUST be named <user>.github.io and the base
is / — the generator handles the base when you pass that repo name.
Custom domains (documented, not automated)
For a custom domain: add a CNAME file (for static/Jekyll, at the served root;
for Astro, public/CNAME), set DNS at the registrar, and in Astro set site to
the domain and drop base. Don't automate DNS — explain the steps.
Template registry & contributing
Templates live in one place: the
jongio/gh-pages-templates
registry. The skill does not bundle its own copy — the generator fetches
templates from the registry at runtime (override with --registry <owner/repo>, or
scaffold offline from a local checkout with --templates-dir <path>):
node scripts/new-site.mjs astro --repo octocat/blog # pinned default registry
node scripts/new-site.mjs skills-catalog --repo octocat/skills \
--registry octocat/templates --registry-ref 0123456789abcdef0123456789abcdef01234567
node scripts/new-site.mjs astro --templates-dir ../gh-pages-templates/templates
Each template is a folder with a template.json manifest, a deploy workflow,
base-path handling via the sentinels, and a README.md. The registry also renders
the browsable gallery (live previews of every template) at
https://jongio.github.io/gh-pages-templates/ — the single home for browsing and
previewing. Point users there to browse, and to the registry's CONTRIBUTING.md to
submit a new template. Template changes (add/fix a template, bump an action version)
land in the registry, not here.
Validate — don't claim done on a green check
- Content check first. Confirm the site is about the repo, not the template:
the title/tagline, install commands, and examples are the repo's real values, and
no template demo copy survives (grep the built output for the template's stock
phrases — "Hello", "islands", "lorem", the sample post titles — and for leftover
__…__ sentinels). The page kind matches the repo type (CLI ref / API ref /
feature tour / catalog). Image placeholders exist and IMAGES.md is present.
- Build it. For
astro/react-vite/eleventy/spectator, run
npm ci --ignore-scripts --no-audit --no-fund then npm run build and confirm
both exit 0 and emit the output dir
(dist / _site). For jekyll, bundle exec jekyll build if Ruby is present.
- Check the base path. Open the built output and confirm asset/link URLs carry
the project prefix (
/repo/...) — not bare /.... This is the failure mode that
"looks deployed but renders blank."
- After deploy, load the live
page_url from the Actions run; click an
internal link and (for the SPA) refresh a sub-route to confirm the 404.html
fallback works.
- Only then report it as working.
Run the skill's own tests with npm test (the generator + the repo-digest and
placeholder checks, offline via a fixture). Template/workflow validation lives in
the jongio/gh-pages-templates registry.
Footguns
- Never ship a project site built for
/ — assets 404. Set the base path (the generator does this; verify it).
- Never put a subpath base on a user site (
<user>.github.io) — it must be / (Jekyll: baseurl: "").
- Never use
actions/upload-artifact for Pages — it's upload-pages-artifact.
- Never reach for
peaceiris/actions-gh-pages or a gh-pages branch — use the first-party Actions flow these templates ship.
- Never forget to set Source → GitHub Actions in Settings → Pages; the workflow can't publish until Pages is enabled for Actions.
- Don't leave the repo "Website" link blank — set
homepage to the Pages URL (gh repo edit --homepage) so visitors find the site; it's the same as the "Use your GitHub Pages website" checkbox.
- Never claim success because the workflow is green — load the URL and check an asset and an internal link actually resolve.
- Never ship the template's demo content. A stamped template that still says
"Hello, Astro", lists sample blog posts, or contains Project Northstar is a
failure. Digest the repo and author real content of the right kind.
- Never fabricate features, commands, or APIs to fill the page. Author only what
the repo actually shows; leave a visible
TODO when unsure.
- Don't leave bare image references with nothing behind them — add the placeholders +
IMAGES.md, or reuse the repo's existing images.
- Don't ship placeholders silently — tell the user which real images the site needs and that they can paste a screenshot into the chat to fill each one.
- Don't hand-roll a base-path setup when the generator + sentinels already do it correctly per framework.
- Never pass a branch or tag as
--registry-ref. Only a full commit SHA is
immutable enough to validate.
- Never compose directly into another generator's target. Stage first, check conflicts, then merge.
1---2name: create-gh-pages-site3description: Scaffold a working GitHub Pages website from a vetted template and wire it to deploy automatically. Use when the user wants to create, scaffold, or publish a site on GitHub Pages: a reviewable VitePress specification, a static page, an Astro or Eleventy site, a React (Vite) SPA, or a Jekyll site. Picks the right template, injects the correct base path for the target repo (the #1 thing people get wrong), adds the official GitHub Actions Pages deploy workflow, and sets it up in the user's current repo by default or a new one if asked. Does not stop at demo content: it digests the target repo (README, manifests, entry points, docs) and authors a site about that repo (CLI reference, API/usage page, feature tour, or catalog) with labeled image placeholders the user swaps in. Do NOT use for non-Pages hosting (Vercel/Netlify/Azure), deploying an existing app without a Pages target, or plain web pages unrelated to GitHub Pages.4---56# Create GitHub Pages Site78Turn "put this on GitHub Pages" into a correct, building, auto-deploying repo. The9hard part of GitHub Pages isn't the HTML — it's the deploy plumbing and the10**base-path trap**. This skill owns both: it stamps a vetted template, sets the11base path for the exact repo, ships the current official Pages workflow, and walks12the user through enabling Pages.1314It also never stops at the template's demo content. A stamped template is a15**skeleton, not the deliverable** — the skill reads the target repo and authors a16site about *that* project: real name and pitch, the right kind of reference (CLI,17library, app, or catalog), real install/usage pulled from the repo, and labeled18image placeholders the user can drop real art into. Shipping a site that still says19"Hello, Astro" for someone's CLI is a failure, even if it deploys.2021## When to use which template2223Pick by what the user is building. If unsure, ask one question (content vs. app)24and default to `static-html` for the simplest ask.2526| Template | Reach for it when… | Tier | Build |27| --- | --- | --- | --- |28| `static-html` | A landing page, a few hand-made pages, "just put this HTML up". No toolchain. | static | none |29| `astro` | A content site, blog, docs, or marketing page that should be fast and mostly static. | SSG | `astro build` |30| `react-vite` | An interactive single-page app / dashboard with client-side routing. | SPA | `vite build` |31| `eleventy` | A data/Markdown-driven site (blog, docs) where content is files + structured data. | data SSG | `eleventy` |32| `jekyll` | The user wants the GitHub-native path, or is migrating an existing Jekyll site. | native | Jekyll |33| `skills-catalog` | A browsable catalog for a repository of Copilot skills, especially when composing `create-skills-repo`. | catalog | registry-defined |34| `spectator` | A technical specification, RFC, architecture proposal, design proposal, engineering decision, or policy proposal built for structured review. | SSG | `vitepress build docs` |3536The built-in registry pin must include the selected template. A custom registry37requires an explicit `--registry-ref`, and a local checkout uses38`--templates-dir`.3940These seven cover the static / SSG / SPA / data / native / catalog / specification41quadrants. For richer42*themes*, point the user at upstream galleries (astro.build/themes,43jamstackthemes.dev, github.com/topics/github-pages-template) and adapt — don't try44to hand-build a theme from scratch.4546## The base-path trap (the thing to get right)4748A GitHub **project** site is served from a subpath:49`https://<user>.github.io/<repo>/`. A site built assuming root (`/`) ships with50broken CSS, images, and links the moment it's a project site — assets 404 and the51page looks blank. A **user/org** site (`<user>.github.io` repo) *is* served from52`/`, so it must NOT carry a subpath prefix.5354Each framework fixes this differently. The generator handles all of it from the55repo name; you rarely set it by hand:5657| Template | Mechanism | Project value | User-site value |58| --- | --- | --- | --- |59| `static-html` | relative URLs (`./assets/...`) | — (immune) | — (immune) |60| `astro` | `base` in `astro.config.mjs` | `/repo/` | `/` |61| `react-vite` | `base` in `vite.config.js` + `basename` + `404.html` | `/repo/` | `/` |62| `eleventy` | `pathPrefix` via `PATH_PREFIX` env + `url` filter | `/repo/` | `/` |63| `jekyll` | `baseurl` in `_config.yml` + `relative_url` | `/repo` (no slash) | `""` (empty) |64| `spectator` | `base` in `spectator.config.ts` and VitePress config | `/repo/` | `/` |6566**The generator detects the `<user>.github.io` user-site pattern and uses `/`67automatically.** If the user hasn't named the repo yet, scaffold with the repo68they intend; if they truly don't know, use `--base /` and tell them to re-run (or69edit the base) once the repo exists.7071## Deployment model — one consistent flow7273Every template deploys via the **GitHub Actions** Pages source, not from a branch.74Every external action reference must use a full commit SHA.7576- `static-html`, `react-vite`, `eleventy` use that chain directly.77- `astro` uses the official `withastro/action@v6` (it builds + produces the Pages78 artifact) then `actions/deploy-pages@v5`.79- `jekyll` uses GitHub's official `actions/jekyll-build-pages@v1` then `deploy-pages`.8081Every workflow limits top-level permissions to `contents: read`, grants Pages and82identity-token access only to the jobs that need them, uses one Pages concurrency83group, and deploys through the `github-pages` environment. There is **no84`gh-pages` branch** to manage.8586> Action versions deprecate aggressively. Update the template registry to a reviewed87> action commit, then bump this skill's immutable default registry commit.8889## Interview first — never scaffold on a guess9091This skill produces a real repo with a live deploy pipeline, so getting the inputs92right matters more than speed. **Do not stamp anything until you know (a) which93template and (b) the target repo (or an explicit base path).** If the prompt is bare94("make me a GitHub Pages site") or names no framework/repo, interview the user — ask95only for what's missing, one focused question at a time, using the `ask_user` tool:96971. **What kind of site?** Map the answer to a template:98 - "a landing page / just some HTML / the simplest thing" → `static-html`99 - "a blog / docs / content / marketing site, fast" → `astro` (or `eleventy` if100 they say Markdown- or data-driven, or `jekyll` if they want the GitHub-native101 path or are migrating an existing Jekyll site)102 - "an app / dashboard / interactive / single-page app" → `react-vite`103 - "a catalog of Copilot skills / create a skills repo" → `skills-catalog`104 - "a specification / RFC / architecture or design proposal / engineering105 decision / policy proposal / site like the Eval Authoring Guide" →106 `spectator`107108 If they're unsure, ask the single discriminating question — *content site or109 interactive app?* — and default to `static-html` for the simplest ask.1102. **Which repo? Assume the current repo by default.** The site is for the repo in111 context unless the user says otherwise — don't ask "current or new." Detect the112 current repo from git (`git remote get-url origin`, parsed to `owner/name`); the113 generator does the same automatically when you omit `--repo`. The repo drives the114 base path, so you must resolve it before stamping:115 - **Current repo detected** → use it. State the assumption in one line116 (*"Scaffolding into this repo, octocat/blog → base `/blog/`"*) and proceed; no117 question needed.118 - **No git context, detached, or no `origin`** → then ask for the `owner/name`,119 and whether it's an existing repo or a **new** one to create.120 - Only treat it as a **new/different** repo when the user explicitly asks for one.121 For a user site, confirm the repo is named `<user>.github.io` (base `/`);122 otherwise it's a project site (base `/repo/`).1233. **Title?** Optional — default to the repo name; never block on it.124125You usually don't need to ask "what should the site say?" — the **content comes from126the repo** (you digest it after stamping; see "Digest the repo, then author it").127When the target repo is the current/an existing one, run `digest-repo.mjs` early to128confirm the type and let it inform the template choice (e.g. a `cli` repo is a great129fit for `astro` or `static-html` with a command reference). If the repo is empty or130brand-new, fall back to asking what the site should cover.131132Skip any question the prompt already answered: *"an Astro blog for octocat/blog"*133needs no interview (template `astro`, repo `octocat/blog`). For a bare *"put this on134Pages"* inside a repo, assume the current repo and ask only for the template. Ask135only for the gaps. When you **inferred** rather than were told, confirm in one line136before scaffolding — e.g. *"Astro site → octocat/blog (current repo), base `/blog/` —137go?"*138139## Output location invariant140141**The user's current working directory is the destination workspace.** A repo slug142such as `octocat/blog` determines the Pages base path and URLs; it does not mean the143site belongs in `/tmp/blog`, a sibling directory, or the skill's own directory.144145- If the workspace already contains repo files, scaffold in place with146 `--dir . --force`, even when it is not a git checkout and the prompt supplies the147 repo slug explicitly.148- Never `cd` into the skill directory to run the generator. Invoke149 `scripts/new-site.mjs` by its absolute path while keeping the workspace as the150 process working directory.151- Use another output directory only when the user explicitly asks for a subfolder152 or a new/different repo.153- When another generator is composing the result, use `--staging-dir` instead of154 `--dir`. The staging directory is isolated and the consumer owns the final merge.155- Before reporting completion, confirm the generated config, content, and156 `.github/workflows` files exist under the original working directory.157158## The workflow you follow1591601. **Interview / gather context.** Resolve the questions above via `ask_user`. You161 MUST end up with a chosen template and a target repo (or explicit `--base`) before162 stamping. **Default the target to the current repo** (detect it from163 `git remote get-url origin`); only ask when there's no git context or the user164 wants a different/new repo.1652. **Pick the template** from the table above.1663. **Stamp it** with the generator (next section). This injects the base path,167 site URL, and title, and lays down the deploy workflow.1684. **Place it in the repo:**169 - *Current repo (default)*: stamp into the repo root (or a subfolder if it's a170 subdirectory site, adjusting the workflow's upload path). From the original171 working directory, invoke the generator by absolute path and pass `--dir .`172 `--force` to write in place. Reconcile an existing `deploy.yml` rather than173 blindly overwriting it.174 - *New repo (only when asked)*: create it (e.g. `gh repo create <name> --public`),175 stamp into it, and push. Match the repo name you used for the base path.1765. **Digest the repo and author the site** (see "Digest the repo, then author it"177 below). This is the step that makes the site *real*: run the digest, replace178 every default page/section with repo-derived content of the right kind, add179 image placeholders + an `IMAGES.md`, and **prompt the user for the real images**180 (they can paste screenshots straight into the chat). Never skip to enabling Pages181 on the demo content.1826. **Enable Pages.** Tell the user (or do it with their approval):183 **Settings → Pages → Source → GitHub Actions**. By CLI:184 `gh api -X POST repos/<owner>/<repo>/pages -f build_type=workflow` (or PUT to185 update). After the first push to `main`, the workflow runs and the live URL186 appears in the Actions run summary and under Settings → Pages.1877. **Set the repo website link** to the Pages URL (the "Website" field in the repo188 header — same as ticking *Settings → "Use your GitHub Pages website"*). This is189 just the repo's `homepage`; point it at the site URL the generator prints:190 `gh repo edit <owner>/<repo> --homepage <site-url>` (or191 `gh api -X PATCH repos/<owner>/<repo> -f homepage=<site-url>`). Offer to set the192 exact URL, or let the user supply a custom domain instead. There's no separate193 "use Pages" boolean — setting `homepage` to the Pages URL *is* the checkbox.1948. **Verify it actually works** (see "Validate" below) — don't claim success on a195 green workflow alone, and don't claim success while template default copy or196 "Hello, world" demo content is still on the page.197198## Stamp a site — the generator199200```sh201node scripts/new-site.mjs <template> --repo <owner/name> [options]202```203204| Option | Purpose |205| --- | --- |206| `--repo <owner/name>` | Target repo. Derives the base path + URLs (and detects user sites). **Defaults to the current repo's `origin` remote when omitted.** |207| `--base </path/>` | Override the base path (e.g. `/` for a user site or local preview). |208| `--dir <path>` | Output directory (default: `./<repo-name>`). |209| `--site-name "Title"` | Human title (default: derived from the repo name). |210| `--description "Text"` | Catalog description (default: derived from the title and repo). |211| `--author "Name"` | Catalog author (default: repo owner). |212| `--package-name <id>` | Package identifier (default: repo name). |213| `--marketplace-id <id>` | Marketplace identifier (default: owner and repo). |214| `--default-branch <id>` | Repository default branch used by catalog links and deployment triggers (default: `main`). |215| `--registry <owner/repo>` | Template registry repo to fetch from (default: `jongio/gh-pages-templates`; needs git + network). |216| `--registry-ref <sha>` | Full 40-character commit SHA. Required with a custom registry. Branches and tags are rejected. |217| `--templates-dir <path>` | Use a local `templates/` folder instead of fetching (offline). |218| `--staging-dir <path>` | Write a validated tree to a new path without applying it to a target. |219| `--force` | Write into a non-empty directory. |220| `--json` | Emit a machine-readable result for composition. |221| `--list` | List available templates. |222223Templates are **not bundled in the skill**. The generator fetches the built-in224`jongio/gh-pages-templates` registry at a reviewed, immutable commit. A custom225registry requires `--registry-ref <full-sha>`. Pass `--templates-dir <path>` to226scaffold from a local copy offline.227228Examples:229230```sh231# Run from the target workspace; SKILL_DIR is this skill's absolute directory.232# Scaffold in place for the current repo (base inferred from its origin remote):233node "$SKILL_DIR/scripts/new-site.mjs" astro --dir . --force234235# The workspace is an existing project without git metadata; the slug only supplies236# deployment identity and the base path. Output still goes into the workspace:237node "$SKILL_DIR/scripts/new-site.mjs" astro --repo octocat/blog --dir . --force238239# Create a subfolder only when the user explicitly requested one:240node "$SKILL_DIR/scripts/new-site.mjs" react-vite --repo octocat/dashboard --dir ./site241242# A user site (served from "/") or a quick local scaffold:243node "$SKILL_DIR/scripts/new-site.mjs" static-html --base / --dir . --force244245# Safely compose a skills catalog without touching the consumer's target:246node "$SKILL_DIR/scripts/new-site.mjs" skills-catalog \247 --repo octocat/skills \248 --templates-dir ../gh-pages-templates/templates \249 --staging-dir ./.site-staging \250 --json251```252253The generator replaces the shared base and repository sentinels plus the catalog254sentinels `__SITE_DESCRIPTION__`, `__REPO_OWNER__`, `__REPO_NAME__`,255`__AUTHOR_NAME__`, and `__MARKETPLACE_ID__`. Injection is deterministic and the256result has no placeholders left. After stamping you may hand-edit content freely.257Metadata values must be plain context-safe text. Markup, control characters, and258characters that can break JSON, YAML, JavaScript, or HTML attributes are rejected.259260### Safe staging contract261262Use `--staging-dir` whenever `create-skills-repo` or another tool will merge the263site into a larger output. The path must not exist and the option cannot be264combined with `--dir` or `--force`. The generator performs replacement and all265validation inside staging, returns only after success, and never touches the final266target. The consumer must check every destination conflict before copying staged267files.268269Before either staging success or normal apply, the generator:2702711. verifies an external registry checkout matches the requested full commit SHA;2722. proves the resolved template path remains inside the registry templates root;2733. rejects every symlink in the template tree;2744. rejects unresolved `__SENTINEL__` placeholders;2755. scans `.github/workflows/*.yml` and `.yaml` for excess permissions, unsafe276 triggers, non-SHA action references, persisted checkout credentials,277 lifecycle-capable installs without script suppression, and direct GitHub278 context interpolation in shell commands.279280Normal `--dir` generation uses a private staging directory and applies only after281all checks pass. This preserves existing template and `--force` behavior while282ensuring an invalid template cannot partially modify the target.283284The exact pinned built-in snapshot has a compatibility normalization for its five285legacy templates. It upgrades their known action tags to reviewed commit SHAs,286disables persisted checkout credentials and lifecycle scripts, and then validates287the result. Never apply this normalization to a custom registry.288289## Digest the repo, then author it (never ship the template defaults)290291Stamping gives you a working skeleton with **demo content**. The job is only half292done. Now make the site about the actual repo. This is not optional polish — it's293the deliverable.294295### 1. Run the digest296297```sh298node scripts/digest-repo.mjs --dir <path-to-repo> --json299```300301It returns deterministic signals you build from: `name`, `description` (the pitch),302`repoSlug`, `license`, a `type` classification (`cli` | `library` | `app` |303`action` | `collection` | `docs` | `site`) with the reasons behind it, suggested304`install` commands, README `usageExamples` (code fences), `badges`, `docFiles`,305existing `images` (with role hints like `logo`/`hero`/`screenshot`), `languages`,306and `subProjects` (for monorepos/collections). Read the repo's README and key docs307yourself too — the digest points you at them; it doesn't replace judgment.308309### 2. Build the right *kind* of site for the type310311| Type | Tell-tale signals | Author the site around… |312| --- | --- | --- |313| `cli` | `bin`, console_scripts, `[[bin]]`, `--help` in README | Install + Quickstart, then a **command/flag reference** (a section or page per command), copy-paste examples, config/exit codes. Hero = terminal demo. |314| `library` | `main`/`exports`, `[lib]`, import examples | Install, an **import + usage** snippet, an **API reference** (exported functions/types from the README/docs), examples, badges. Hero = a concept diagram. |315| `app` | web-framework dep, `index.html`, `src/` | A **feature tour** with screenshots, a "Get started"/live-demo CTA. Hero = an app screenshot. |316| `action` | `action.yml` | A `uses:` snippet, an **inputs/outputs table**, an example workflow. |317| `collection` | `plugin.json`/`marketplace.json` + `skills/`, workspaces, `packages/` | A **catalog**: one card/detail per `subProject` (its pitch + install), plus a top-level install for the whole thing. Hero = a banner; per-item thumbnails. |318| `docs` | many Markdown docs, little code | A **docs nav + content**, pulling the existing Markdown in. |319| `site` / unknown | none of the above | A clean landing built from the repo's pitch and links. If the shape is unclear, ask the user what sections they want. |320321### 3. Authoring rules322323- **Replace every default.** No template demo copy survives — not the sample hero,324 not "Hello, Astro/world", not the example blog posts, not lorem. After building,325 grep the output for the template's stock phrases; none should remain.326- **Use real values from the digest:** the repo's name and `description` as the327 title/tagline, the `install` commands verbatim, the README `usageExamples` as328 real code blocks, `badges`, `license`, and links to the repo and each329 `subProject`. **Don't invent** features, commands, or APIs you can't see in the330 repo — if something's unclear, leave a visible `TODO` for the user rather than331 fabricate.332- **Fit the template's content model.** Use content collections (Astro/Eleventy:333 one entry per command/skill/post), pages (React/static), etc. Add or rename334 routes to match the content (e.g. a `commands/` or `catalog/` section) instead of335 forcing everything into the demo "blog". Keep the GitHub source link and the336 base-path-aware internal links the template already wires — never hand-write337 absolute `/...` links (use the template's base helper, or it breaks on a project338 site).339- **Keep Spectator free-form.** Do not force a standard specification outline.340 Derive pages, headings, and navigation from the supplied context. Preserve the341 Spectator reading and feedback system, but remove every Project Northstar sample342 page, label, requirement, source, and image reference.343- **Label uncertainty in Spectator.** Distinguish verified current behavior,344 user-approved decisions, proposals, assumptions, risks, and open questions.345 Do not invent owners, dates, requirements, interfaces, or security claims.346347### 4. Add image placeholders the user can supply348349Real sites need art the agent can't produce. Drop in **obvious placeholders** plus350a checklist so the user knows exactly what to provide:351352```sh353node scripts/make-placeholder.mjs --out <site>/<images-dir> --preset <type> --repo owner/name354```355356`--preset` is the repo type (`cli`/`library`/`app`/`collection`/`site`). It writes357labeled SVG placeholders (logo, social card, favicon, hero, and type-specific358shots) and an `IMAGES.md` manifest listing each file's purpose and recommended359dimensions. Then:360361- **Reference them** from the pages you author — hero, top-bar logo, the OG/social362 meta tag, and per-item thumbnails for a catalog.363- **Reuse real images first.** If the digest found an existing logo or screenshot364 (e.g. a `docs/*.png`), use it instead of a placeholder.365- **Put images where the template serves static files:**366 - `astro`, `react-vite` → `public/images/` (served at `${BASE_URL}images/…`)367 - `spectator` → `docs/public/images/` (VitePress applies its configured base)368 - `static-html` → `assets/images/` (relative `./assets/images/…`)369 - `eleventy` → `src/assets/images/` (through the `url` filter)370 - `jekyll` → `assets/images/` (via `relative_url`)371- **Leave `IMAGES.md` in the images dir** as the hand-off, and tell the user it's372 there. A placeholder still deploys fine; it just visibly says "replace me".373374### Ask for real images375376After authoring, ask only for images the site references. Give each role, filename,377and recommended dimensions; accept pasted images, file paths, or URLs. Keep378unprovided placeholders, update `IMAGES.md`, and do not block deployment.379`digest-repo.mjs` discovers source material; `make-placeholder.mjs` creates the380placeholder set and handoff manifest.381382## Per-template notes383384- **static-html** — Zero build. All links are relative, so it's base-path-proof.385 Ships `index.html`, `about.html`, `404.html`, `assets/`, and a `.nojekyll`.386- **astro** — `site` = origin, `base` = `/repo/`. Internal links use387 `import.meta.env.BASE_URL`. `public/` is copied verbatim. Node 24 in CI by default.388- **react-vite** — `base` in `vite.config.js`; React Router `basename` derived from389 `BASE_URL`; `copy-404.mjs` (a `postbuild` hook) copies `index.html` → `404.html`390 so deep-link refreshes work on Pages. Reference public assets as `/asset` so Vite391 rewrites them with the base.392- **eleventy** — `pathPrefix` comes from the `PATH_PREFIX` env the workflow sets;393 every link/asset uses the `url` filter. Posts live in `src/posts/`, listed via394 `collections.posts`. Locally it serves at `/`.395- **jekyll** — `baseurl` (no trailing slash) in `_config.yml`; links use396 `relative_url`. Built in CI by `jekyll-build-pages` (honors the `Gemfile`). Local397 dev needs Ruby + Bundler; CI does not.398- **spectator**: VitePress reads `base` from `spectator.config.ts`. Content is399 free-form Markdown under `docs/`; navigation is explicit in the shared config.400 Preserve local search, page outlines, light/dark appearance, plain-language401 callouts, page feedback, selected-text feedback, and GitHub edit links. Run402 `npm test`, `npm run build`, and `npm run test:e2e`.403404## Current repo vs. new repo405- **Current repo (the default)**: assume the site is for the repo in context. The406 generator infers the base path from its `origin` remote when you omit `--repo`.407 Put the site at the root for a whole-repo site, or in a subfolder and point the408 workflow's `upload-pages-artifact` `path:` at it. If a `deploy.yml` already exists,409 reconcile — don't blindly overwrite.410- **New repo (only when asked)**: create with `gh repo create`, stamp, push to411 `main`. For a **user site**, the repo MUST be named `<user>.github.io` and the base412 is `/` — the generator handles the base when you pass that repo name.413414## Custom domains (documented, not automated)415For a custom domain: add a `CNAME` file (for static/Jekyll, at the served root;416for Astro, `public/CNAME`), set DNS at the registrar, and in Astro set `site` to417the domain and drop `base`. Don't automate DNS — explain the steps.418419## Template registry & contributing420Templates live in **one** place: the421**[`jongio/gh-pages-templates`](https://github.com/jongio/gh-pages-templates)**422registry. The skill does **not** bundle its own copy — the generator fetches423templates from the registry at runtime (override with `--registry <owner/repo>`, or424scaffold offline from a local checkout with `--templates-dir <path>`):425426```sh427node scripts/new-site.mjs astro --repo octocat/blog # pinned default registry428node scripts/new-site.mjs skills-catalog --repo octocat/skills \429 --registry octocat/templates --registry-ref 0123456789abcdef0123456789abcdef01234567430node scripts/new-site.mjs astro --templates-dir ../gh-pages-templates/templates431```432433Each template is a folder with a `template.json` manifest, a deploy workflow,434base-path handling via the sentinels, and a `README.md`. The registry also renders435the browsable gallery (live previews of every template) at436**https://jongio.github.io/gh-pages-templates/** — the single home for browsing and437previewing. Point users there to browse, and to the registry's `CONTRIBUTING.md` to438submit a new template. Template changes (add/fix a template, bump an action version)439land in the registry, not here.440441## Validate — don't claim done on a green check4424431. **Content check first.** Confirm the site is about the repo, not the template:444 the title/tagline, install commands, and examples are the repo's real values, and445 **no template demo copy survives** (grep the built output for the template's stock446 phrases — "Hello", "islands", "lorem", the sample post titles — and for leftover447 `__…__` sentinels). The page kind matches the repo type (CLI ref / API ref /448 feature tour / catalog). Image placeholders exist and `IMAGES.md` is present.4492. **Build it.** For `astro`/`react-vite`/`eleventy`/`spectator`, run450 `npm ci --ignore-scripts --no-audit --no-fund` then `npm run build` and confirm451 both exit 0 and emit the output dir452 (`dist` / `_site`). For `jekyll`, `bundle exec jekyll build` if Ruby is present.4533. **Check the base path.** Open the built output and confirm asset/link URLs carry454 the project prefix (`/repo/...`) — not bare `/...`. This is the failure mode that455 "looks deployed but renders blank."4564. **After deploy**, load the live `page_url` from the Actions run; click an457 internal link and (for the SPA) refresh a sub-route to confirm the `404.html`458 fallback works.4595. Only then report it as working.460461Run the skill's own tests with `npm test` (the generator + the repo-digest and462placeholder checks, offline via a fixture). Template/workflow validation lives in463the `jongio/gh-pages-templates` registry.464465## Footguns466- **Never** ship a project site built for `/` — assets 404. Set the base path (the generator does this; verify it).467- **Never** put a subpath base on a **user site** (`<user>.github.io`) — it must be `/` (Jekyll: `baseurl: ""`).468- **Never** use `actions/upload-artifact` for Pages — it's `upload-pages-artifact`.469- **Never** reach for `peaceiris/actions-gh-pages` or a `gh-pages` branch — use the first-party Actions flow these templates ship.470- **Never** forget to set **Source → GitHub Actions** in Settings → Pages; the workflow can't publish until Pages is enabled for Actions.471- **Don't** leave the repo "Website" link blank — set `homepage` to the Pages URL (`gh repo edit --homepage`) so visitors find the site; it's the same as the "Use your GitHub Pages website" checkbox.472- **Never** claim success because the workflow is green — load the URL and check an asset and an internal link actually resolve.473- **Never** ship the template's demo content. A stamped template that still says474 "Hello, Astro", lists sample blog posts, or contains Project Northstar is a475 failure. Digest the repo and author real content of the right kind.476- **Never** fabricate features, commands, or APIs to fill the page. Author only what477 the repo actually shows; leave a visible `TODO` when unsure.478- **Don't** leave bare image references with nothing behind them — add the placeholders + `IMAGES.md`, or reuse the repo's existing images.479- **Don't** ship placeholders silently — tell the user which real images the site needs and that they can paste a screenshot into the chat to fill each one.480- **Don't** hand-roll a base-path setup when the generator + sentinels already do it correctly per framework.481- **Never** pass a branch or tag as `--registry-ref`. Only a full commit SHA is482 immutable enough to validate.483- **Never** compose directly into another generator's target. Stage first, check conflicts, then merge.