Project structure
The tree is what git tracks in the starter today, with src/, src/pages/ and .github/ opened one level. The table under it is the map from AGENTS.md, the file an agent reads first, so the two never disagree. Files with .free. in the name are the free edition’s versions of their neighbours; the release tooling swaps them in.
zerodirs/├── .agents/├── .github/│ ├── docs/│ ├── release/│ └── workflows/├── docs/├── drizzle/├── integrations/├── public/├── scripts/├── seed/├── src/│ ├── actions/│ ├── assets/│ ├── components/│ ├── config/│ ├── content/│ ├── db/│ ├── features/│ ├── icons/│ ├── islands/│ ├── layouts/│ ├── lib/│ ├── loaders/│ ├── pages/│ │ ├── account/│ │ ├── admin/│ │ ├── api/│ │ ├── auth/│ │ ├── blog/│ │ ├── login/│ │ ├── og/│ │ ├── out/│ │ ├── submit/│ │ ├── 404.astro│ │ ├── [...path].astro│ │ ├── [advertise].astro│ │ ├── about.astro│ │ ├── index.astro│ │ ├── privacy.astro│ │ ├── robots.txt.ts│ │ ├── rss.xml.ts│ │ ├── search.astro│ │ └── terms.astro│ ├── server/│ ├── styles/│ ├── templates/│ ├── content.config.ts│ ├── env.d.ts│ ├── env.free.d.ts│ ├── middleware.ts│ └── worker.ts├── tests/├── .dev.vars.example├── .editorconfig├── .env.example├── .gitignore├── .nvmrc├── AGENTS.free.md├── AGENTS.md├── astro.config.ts├── CHANGELOG.md├── CLAUDE.md├── drizzle.config.ts├── LICENSE├── LICENSE.free├── lighthouserc.cjs├── lighthouserc.mobile.cjs├── package.json├── playwright.config.ts├── pnpm-lock.yaml├── README.free.md├── README.md├── site.config.ts├── tsconfig.json├── vitest.config.ts├── vitest.workers.config.ts├── worker-configuration.d.ts├── wrangler.free.jsonc└── wrangler.jsoncNothing under node_modules/, dist/, .astro/ or .wrangler/ is tracked: install, build, astro check and the dev server create those.
Where things live
Section titled “Where things live”The free edition column says what the release tooling does with the path: deleted means the free edition does not have it; replaced means it has a smaller version of it.
| Path | What | Free edition |
|---|---|---|
site.config.ts |
The one file a buyer edits: brand, routes, categories, tags, tiers, SEO, pSEO, theme, feature flags | yes |
astro.config.ts |
Derived from site.config.ts; adapter, fonts, integrations, env.schema |
yes |
src/config/schema.ts |
The zod schema and the ten cross-field rules behind pnpm check:config |
yes |
src/config/defaults.ts |
Every default the schema fills in | yes |
src/config/presets.ts |
Theme preset registry (default, warm, mono) plus each preset’s OG hex colours |
yes |
src/config/types.ts |
The public SiteConfig type |
yes |
src/content/listings/ |
One Markdown file per listing; the file name is the slug | yes |
src/content/blog/ |
Blog posts | yes |
src/content/pseo/ |
Optional per-slug intro/title overrides for generated pSEO pages | yes |
src/content/schema.ts |
Collection schemas: listingSchema, blogSchema, pseoCopySchema |
yes |
src/content.config.ts |
Collection definitions (which loader feeds which schema) | yes |
src/loaders/files.ts |
The Markdown listings loader | yes |
src/loaders/normalize.ts |
Source → Listing: category check, tag filter, date coercion, isFeatured |
yes |
src/pages/[...path].astro |
The only dynamic route; getStaticPaths builds every directory page |
yes |
src/pages/ |
Fixed pages: home, search, about, terms, privacy, 404, blog, /rss.xml, /robots.txt, OG endpoint |
yes |
src/lib/routes.ts |
The pure route table (buildRoutes, matchers, reserved segments) |
yes |
src/lib/paths.ts |
The only URL constructor in the project | yes |
src/lib/seo.ts |
Titles, descriptions, canonicals, noindex, sitemap chunks |
yes |
src/templates/ |
One template per page kind; the catch-all dispatches to these | yes |
src/components/listing/Detail.astro |
The listing detail body — the file to edit for “change the detail page” | yes |
src/components/ |
Everything else presentational (listing/, category/, blog/, pseo/, seo/, site/, ui/, and ads/ — the four placements site.config.ts ads switches on) |
yes |
src/features/index.ts |
Facade for the paid-only features; templates import from here, never from paid/ |
replaced |
src/features/paid/pseo/patterns.ts |
The pSEO template registry — a new template’s candidate logic goes here; src/features/paid/pseo/generate.ts handles minItems, collisions, maxPages, siblings and copy |
deleted |
src/features/paid/og/ |
Build-time OG image rendering (satori + resvg) | deleted |
src/styles/global.css |
Tailwind v4 entry: @source allow-list, dark variant, preset imports, @theme inline |
yes |
src/styles/presets/ |
One CSS file per theme preset | yes |
src/islands/SearchPanel.tsx |
The only hydrated component in the whole site | yes |
src/db/schema.ts |
The D1 schema. Migrations in drizzle/ are generated from it |
deleted |
src/loaders/d1.ts |
Build-time D1 REST loader (LISTINGS_SOURCE=d1) |
deleted |
src/middleware.ts |
Guards /admin/** and /_actions/; lazy-imports the DB so prerender stays clean |
deleted |
src/actions/ |
The two Action namespaces: submit (public) and admin |
deleted |
src/server/ |
Everything that runs per request: db, env (the only reader of the astro env module), token, admin-auth, submit, queries, meta-fetcher, media, jobs, queue, email/, rebuild/, auth/ |
deleted |
src/pages/submit/ |
The public two-step form and the token-addressed status page | deleted |
src/pages/account/ |
Submitter accounts (features.accounts): /account/ lists a signed-in submitter’s submissions; src/pages/login/ is the sign-in page and src/pages/auth/ the emailed-link and Google callbacks. src/server/auth/ holds sessions, links and the Google exchange |
deleted |
src/pages/admin/ |
Login, queue, listing detail, featured, stats | deleted |
src/worker.ts |
The Worker entry (main): the adapter’s fetch, plus scheduled running the four hourly jobs |
deleted |
integrations/sitemap.ts |
Chunked sitemap | yes |
integrations/pagefind.ts |
Search index, written at astro:build:done |
yes |
integrations/budget.ts |
Static-file count against the Cloudflare plan limit | yes |
integrations/seo-report.ts |
Inbound-link / orphan / sitemap audit; can fail the build | deleted |
scripts/ |
The CLI scripts behind the pnpm commands below |
yes |
tests/unit/ |
Vitest node tests (vitest.config.ts) |
yes |
seed/datasets/ai-tools/ |
The demo dataset pnpm content:use restores |
yes |
public/ |
Copied verbatim into the build: public/favicon.svg, public/og-default.png, public/logos/, public/_headers |
yes |
.agents/skills/ |
Task recipes; pnpm skills:sync copies them to .claude/skills/ |
deleted |
wrangler.jsonc |
Worker name, main, compatibility date, the DB / MEDIA / EMAIL bindings |
replaced |
.env.example |
Build-time variables | yes |
.dev.vars.example |
Runtime secrets | deleted |
Related
Section titled “Related”- Commands: every
pnpmscript, and which file it runs. - Configuration: the one file in the tree a buyer edits first.
- Free vs Pro: the line the last column draws, in prose.