This guide covers first-time downstream setup — creating a new site from the core theme, installing dependencies, and configuring the minimum required settings before first deployment.
Once setup is complete, follow the Deployment Guide (DOC-00021) for the production deployment procedure.
Who This Is For
- Implementors bootstrapping a new downstream site from the core theme
- Developers creating a new client site repository
Prerequisites
- Node.js 22+
- Git
- Access to the core theme repository URL (or using the default public theme)
- (Optional) GitHub CLI (external link) — only needed if you want the script to auto-create a GitHub repository
Quick Start
The create-site script bootstraps a new site from the core theme in a single command. It clones the theme, removes development artifacts, wires the upstream remote, and installs dependencies.
Private repositories: If the core theme repository is private, add an authorization header to the
curlcommand:curl -fsSL -H "Authorization: token $(gh auth token)" <url>. This requires the GitHub CLI to be installed and authenticated. The examples below show the unauthenticated form for public repositories.
Option A: Local-Only (No Remote)
Create a working local site with no remote configured. This is the simplest path and the best starting point if you want to explore the theme or aren’t ready to create a hosted repository yet.
curl -fsSL https://raw.githubusercontent.com/adamslowe/astro-agency/main/scripts/create-site.mjs | node --input-type=module - --name my-site
This produces a ready-to-run local site:
my-site/
├── .git/ # full git repo
│ └── remotes:
│ └── core # points to the core theme (for future updates)
│ └── (no origin — add one when you're ready)
├── src/
├── package.json
├── node_modules/
└── ...
Start the dev server to verify:
cd my-site
npm run dev
When you’re ready to connect a remote:
git remote add origin <your-repo-url>
git push -u origin main
Option B: With a Pre-Created Repository
If you’ve already created an empty repository on GitHub, GitLab, or another host:
curl -fsSL https://raw.githubusercontent.com/adamslowe/astro-agency/main/scripts/create-site.mjs | node --input-type=module - --name my-site --origin <your-repo-url>
Important: Do not clone the downstream repository first. Run the command from the parent directory where you want the site folder created. The script clones the core theme into a new
my-site/directory and then points itsoriginremote at your downstream repository. The downstream repo is a push target, not something you clone yourself.
The script wires origin to your repository and pushes the initial commit automatically.
If the remote repository contains initialization files (README, LICENSE, .gitignore), the script detects them and prompts you to delete them and continue or cancel. When running via curl pipe (non-interactive), use the --force flag to auto-overwrite instead.
Option C: Auto-Create on GitHub
If you have the GitHub CLI (external link) installed and authenticated, the script can create the repository for you:
# Public repo under your account
curl -fsSL https://raw.githubusercontent.com/adamslowe/astro-agency/main/scripts/create-site.mjs | node --input-type=module - --name my-site --org your-username
# Private repo under an organization
curl -fsSL https://raw.githubusercontent.com/adamslowe/astro-agency/main/scripts/create-site.mjs | node --input-type=module - --name my-site --org your-org --private
As with Option B, run this from the parent directory — do not create or clone the repository first. The script handles both repo creation and site setup.
Command Reference
node scripts/create-site.mjs --name <name> [options]
| Flag | Default | Purpose |
|---|---|---|
--name <name> | (required) | Site directory name |
--origin <url> | — | Pre-existing downstream repo URL |
--org <name> | — | GitHub org/user — auto-creates repo via gh CLI |
--private | false | Create a private GitHub repo (requires --org) |
--core <url> | https://github.com/adamslowe/astro-agency.git | Core theme clone URL |
--ref <tag|branch> | latest tagged release | Git ref to clone |
--site-name <name> | — | Site display name (sets siteName and siteTitle in site.ts) |
--site-url <url> | — | Production URL (sets the export in site-url.ts) |
--no-install | — | Skip npm install |
--force | — | Auto-overwrite initialization files in a non-empty remote repo (for non-interactive / piped usage) |
--dry-run | — | Print what would happen without executing |
--help | — | Show usage information |
Exit Codes
| Code | Meaning |
|---|---|
0 | Success |
1 | User error (missing or invalid arguments) |
2 | Environment error (missing git, gh not authenticated, etc.) |
What the Script Does
Regardless of which option you choose, the script performs these steps:
- Validates prerequisites — checks for
git, Node 22+, and (if--orgis used) an authenticatedghCLI - Creates the downstream repository — only if
--orgis provided; skipped for--originand local-only - Checks remote repo state — if
--originor--orgis used, checks whether the remote contains initialization files (README, LICENSE,.gitignore). If found: prompts to delete and continue when running interactively, or requires--forcewhen piped. - Clones the core theme with
--no-checkoutat the specified ref (defaults to the latest tagged release) - Selectively checks out files — reads
.downstream-excludefrom the cloned ref and filters out development artifacts (.docs/,.claude/,.superpowers/,.vscode/,TODO.md,BACKLOG.md,AGENTS.md,CLAUDE.md,ARCHITECTURE.md) - Wires the
coreremote — renames the clone’sorigintocore, preserving the upstream relationship for futuresync-coreoperations - Wires
origin— if--originor--orgwas provided, setsoriginto the downstream repo URL; otherwise leaves it unset - Seeds
.core/and root stubs — copiesAGENTS.md,ARCHITECTURE.md,CLAUDE.md, andGIT.mdfrom core git history into.core/at the site root (core-owned, overwritten on everysync:core). Also writes site-ownedAGENTS.md,CLAUDE.md, andARCHITECTURE.mdstubs that reference.core/for platform conventions and carry site-specific content. - Commits a clean baseline — amends the initial commit to reflect the excluded-file removal
- Pushes — if
originis set, force-pushesmainto the downstream remote (replacing any initialization files) - Configures the site — updates
package.jsonname (from--name), and if provided, setssiteName/siteTitleinsite.ts(from--site-name) and the production URL insite-url.ts(from--site-url) - Installs dependencies — runs
npm install(unless--no-installis passed)
Configure the Site
If you used --site-name and --site-url during setup, the core identity fields are already set. Otherwise, review and customize these SITE-OWNED configuration files:
| File | Purpose |
|---|---|
package.json | Package name (auto-set from --name) |
src/site/config/site.ts | Site identity, contact details, branding, and general site configuration |
src/site/config/platform.config.ts | Build-safe settings consumed by Astro config, middleware, routes, and validation scripts |
src/site/config/site-url.ts | Production site URL |
src/site/config/menu.ts | Navigation configuration |
src/site/config/site-docs.ts | Site-docs collection configuration: topics, display settings, and factory call. Delete the route files in src/pages/site-docs/ to disable. |
At minimum, set the site name, title, production URL, primary contact details, and any logo imports referenced by site.ts.
Replace Placeholder Assets
| Path | Replace with |
|---|---|
src/site/assets/images/logo/ | Site logos used by site.ts |
public/favicon.ico | Browser tab icon |
public/favicon.svg | Modern favicon |
public/apple-touch-icon.png | iOS icon |
public/og-image.png | Default social sharing image |
Review Site Styling
You do not need to customize styles before launch, but site-level overrides live in src/site/styles/ if needed:
| File | Purpose |
|---|---|
primitives.css | Primitive token overrides (--pt-*) |
semantic.css | Semantic token overrides (--st-*) |
light.css | Light mode token overrides |
dark.css | Dark mode token overrides |
fonts.config.ts (in src/site/config/) | Font configuration — imports core defaults, replace to customize |
base.css | Site-level base overrides |
components.css | Site-level component class overrides |
bridge.css | Site-level Tailwind utility registrations |
starwind-bridge.css | Optional Starwind bridge activation and Starwind-specific aliases |
fluid-tokens.css also exists in src/site/styles/ but is auto-generated by npm run generate:tokens. Do not edit it by hand.
Replace Placeholder Content
The default content collections live in src/content/ and are SITE-OWNED. Replace the shipped placeholder content in:
src/content/pages/src/content/articles/src/content/fragments/- Theme documentation lives in
src/core/content/theme-docs/(CORE-OWNED, syncs automatically)
If your site does not need the theme documentation system, set themeDocs.enabled: false in src/site/config/platform.config.ts. No routes, nav links, or build output will be generated.
Validate Before First Deploy
Run the full quality gate:
npm run validate
Fix any failures before making the first deployment. See the Validation Troubleshooting guide (DOC-00036) if needed.
Keeping Up with Core Updates
The create-site script wires a core remote that points back to the shared theme repository. When the core theme publishes a new release, pull those changes into your site with:
npm run sync:core
This merges updated CORE-OWNED files (components, layouts, utilities, theme-docs) into your site while leaving SITE-OWNED files untouched. After syncing, run npm run validate, resolve any conflicts, and redeploy. See Downstream Re-Deployment After Core Sync in the Deployment Guide (DOC-00021) for the full procedure.
Next Steps
With the site set up and validated, follow the Deployment Guide (DOC-00021) for the production deployment procedure.
Appendix A: Manual Repository Setup
These steps are the manual equivalent of running the create-site script. Use them if you need full control over each step or want to understand what the script does.
A1: Clone and Set Up (with setup-downstream.mjs)
Before starting, create an empty repository on your git host (do not initialize it with a README, license, or .gitignore). Copy its clone URL.
-
Clone the core theme with
--no-checkoutand enter the new site directory:git clone --no-checkout <core-theme-url> <your-site-name> cd <your-site-name> -
Check out the scripts directory:
git checkout HEAD -- scripts/ -
Run the downstream setup script:
node scripts/setup-downstream.mjs --origin <your-site-repo-url>The script detects the
--no-checkoutstate, selectively checks out files (excluding development artifacts via.downstream-exclude), saves the core theme as thecoreremote, pointsoriginat your new downstream repository, verifies the wiring, and pushes the initial state. -
Install dependencies and verify:
npm install npm run dev
A2: Fully Manual (No Scripts)
Before starting, create an empty repository on your git host. Copy its clone URL.
Clone URL examples:
- GitHub HTTPS:
https://github.com/<org-or-user>/<repo>.git - GitHub SSH:
[email protected]:<org-or-user>/<repo>.git - GitLab SSH:
[email protected]:<group>/<repo>.git - Local bare repo:
/path/to/remotes/<repo>.git
-
Clone the core theme into your new site directory:
git clone <core-theme-url> <your-site-name> cd <your-site-name> -
Remove development artifacts that are not needed in downstream sites. The
.downstream-excludefile at the project root lists the paths to remove:# Remove excluded directories rm -rf .docs/ .claude/ .superpowers/ .vscode/ # Remove excluded files rm -f TODO.md BACKLOG.md AGENTS.md CLAUDE.md ARCHITECTURE.mdRefer to
.downstream-excludefor the current list — the examples above reflect the initial manifest and may not be complete if the manifest has been updated since. -
Point
originat the new downstream repository:git remote set-url origin <your-site-repo-url> -
Add the upstream core theme as the
coreremote:git remote add core <core-theme-url> -
Verify the remote wiring:
git remote -vYou should see
originpointing to the downstream site repository andcorepointing to the shared theme repository. -
Commit the clean downstream baseline and push:
git add -A git commit -m "chore: initial downstream setup" git push -u origin main