diff --git a/CLAUDE.md b/CLAUDE.md index aec94e1..4ddd7fa 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,7 +13,9 @@ Whatnot podcast). ## Commands - **Dev server:** `pnpm dev` (runs on localhost:4321) -- **Build:** `pnpm build` (runs `astro check` then `astro build`) +- **Build:** `pnpm build` (runs `astro check`, `astro build`, then + `scripts/vercel-md-negotiation.mjs`, which injects `Accept: text/markdown` + content-negotiation routes into the Vercel build output) - **Lint:** `pnpm lint` (ESLint with caching) - **Lint fix:** `pnpm lint:fix` - **All tests:** `pnpm test` (runs unit + e2e concurrently) @@ -56,7 +58,11 @@ connection is configured in `db/index.ts`. - `src/pages/` — Astro pages and API routes. Dynamic episode pages use `[episode].astro`. LLM-friendly `.html.md.ts` endpoints generate markdown - versions. + versions. `openapi.json.ts` publishes an OpenAPI spec for the JSON API. + `[...notFound].astro` is an on-demand (prerender=false) catch-all that + returns agent-friendly 404s: JSON errors for `/api/*`, a markdown body for + `Accept: text/markdown` clients, and the styled 404 page otherwise. API + routes return structured JSON errors via `src/lib/api-errors.ts`. - `src/components/` — Mix of `.astro` (static) and `.tsx` (Preact interactive) components. The audio player (`src/components/player/`) and search dialog are Preact. diff --git a/README.md b/README.md index ca08401..4c9416d 100644 --- a/README.md +++ b/README.md @@ -250,12 +250,40 @@ fine without them, using episode descriptions and metadata from your RSS feed. All of the following endpoints are automatically generated at build time from your `starpod.config.ts` and RSS feed: -- `/llms.txt` - Main discovery file +- `/llms.txt` - Main discovery file, including "when to use this site" guidance + for agents and a developer resources section - `/for-llms` - Human-readable guide page - `/for-llms.html.md` - Markdown version of guide +- `/index.html.md` - Markdown version of the homepage - `/about.html.md` - Markdown version of about page +- `/contact.html.md` - Markdown version of the contact page - `/episodes-index.html.md` - Complete episode listing - `/{episode-slug}.html.md` - Individual episode with transcript - `/{episode-number}.html.md` - Alternative episode URL +- `/openapi.json` - OpenAPI 3.1 spec describing the JSON API endpoints + (episode search, episode pagination, contact form) No configuration needed - it just works! + +#### Markdown Content Negotiation + +Agents can also request any page that has a markdown twin with an +`Accept: text/markdown` header and get the markdown version back from the same +URL, per [acceptmarkdown.com](https://acceptmarkdown.com). Both variants are +served with `Vary: Accept` so CDNs cache them separately. + +This is implemented by `scripts/vercel-md-negotiation.mjs`, which runs as part +of `pnpm build` and injects Accept-based rewrite routes into the Vercel build +output. If you customize the `build` script in `package.json`, keep the +`node scripts/vercel-md-negotiation.mjs` step after `astro build`. (Deploying +somewhere other than Vercel? The `.html.md` URLs still work everywhere; only +the Accept-header negotiation is Vercel-specific.) + +#### Agent-Friendly Errors + +- Nonexistent paths return a real HTTP 404: browsers get the styled 404 page, + `Accept: text/markdown` clients get a short markdown body pointing at the + sitemap, `llms.txt`, and the episodes index, and `/api/*` paths get a + structured JSON error +- API errors are structured JSON with a stable `error.code`, a message, and a + resolution `hint` - never an HTML error page