P PasteCode
Context Pack

AI Tool Directory — Context Pack

Copyable context pack for an AI tool directory site built with Astro and PostgreSQL, so agents understand the submission flow, tagging system, and content model from prompt one.

CursorClaude CodeCodexWindsurf AstroPostgreSQLTypeScriptTailwind
.md .json Updated Jun 8, 2026

Paste this at the top of a task so the agent understands the content model, moderation workflow, and build strategy for the AI tool directory before it makes any changes.

Project Background

A directory of AI tools and products. Each listing is a row in PostgreSQL,
pulled at build time by Astro's Content Layer and rendered to static HTML.
Submissions arrive via a public HTML form that posts to an Astro API endpoint;
entries are held in a `pending` state until an admin approves them through a
password-protected dashboard. The site is deployed to Cloudflare Pages with a
Neon Postgres database. Search is client-side Pagefind over the static output.

Stack

Astro 5 (static output, SSR disabled except for /api/* and /admin/* routes)
TypeScript (strict)
Tailwind CSS v4
PostgreSQL 16 via Neon (serverless HTTP driver @neondatabase/serverless)
Drizzle ORM + drizzle-kit for schema + migrations
Pagefind (client-side full-text search over built HTML)
Cloudflare Pages (static + Functions for the hybrid API routes)

Directory Structure

src/
pages/
index.astro # Homepage + featured tools grid
tools/
[slug].astro # Individual tool page (generated at build time)
index.astro # Browsable directory with category filters
api/
submit.ts # POST — accepts public tool submissions
admin/
approve.ts # POST — marks a listing approved (auth-gated)
reject.ts # POST — deletes a pending listing (auth-gated)
admin/
index.astro # Admin dashboard — lists pending submissions
lib/
db/
index.ts # Neon client + Drizzle instance
schema.ts # tools, categories, tags tables
auth.ts # Simple HMAC token check for /admin routes
slugify.ts # Deterministic slug generation from tool name
validate.ts # Zod schema for submission form fields
content/
featured/ # MDX overrides for hand-curated tool blurbs
components/
ToolCard.astro
CategoryFilter.astro
SubmissionForm.astro
drizzle/
migrations/
drizzle.config.ts

Coding Conventions

- Tool listings are the source of truth in Postgres, not MDX files.
MDX in src/content/featured/ only overrides the blurb for curated picks.
- Slugs are derived from the tool name via src/lib/slugify.ts and stored in
the DB. Never compute the slug on the fly — always read it from the DB row.
- All submission input is validated with the Zod schema in src/lib/validate.ts
before any DB write. Return 400 with field-level errors on failure.
- The /admin/* routes check a Bearer token with the HMAC verifier in
src/lib/auth.ts. Do not add another auth mechanism.
- DB calls happen only in Astro API endpoints (*.ts in pages/api/) and in the
getStaticPaths() of dynamic pages. Never in .astro components directly.
- Pagefind is run as a post-build step (`npx pagefind --site dist`).
Do not add client-side search that bypasses Pagefind.
- Category and tag slugs are normalized to lowercase kebab-case.
Maintain the canonical list in src/lib/db/schema.ts (categories table).

AI Task Boundaries

- Do not add a full auth system (NextAuth, Better Auth) — the admin gate is
intentionally a single shared HMAC token for simplicity.
- Do not move tool data into MDX content collections — the DB is the source
of truth and MDX is only for editorial overrides.
- Do not change the Astro output mode to fully SSR — the majority of pages
must remain static for Pagefind and CDN caching to work.
- Do not allow unapproved (status != 'approved') tools to appear in
getStaticPaths() or the public directory index.
- Schema changes must go through Drizzle migrations. Never use raw ALTER TABLE
against the Neon database directly.
- External URLs from user submissions must be stored as-is and only rendered
through rel="nofollow noopener noreferrer" links.
- Do not expose the admin token in any client-side bundle, Astro component
prop, or public API response.

llms.txt

# AI Tool Directory
Framework: Astro 5 (hybrid: static pages + SSR API routes)
DB: Neon Postgres via Drizzle ORM (src/lib/db/schema.ts)
Search: Pagefind (post-build, client-side over static HTML)
Submissions: POST /api/submit → status='pending' → admin approval
Admin auth: HMAC Bearer token (src/lib/auth.ts) — no user accounts
Slugs: generated once on insert, stored in DB (src/lib/slugify.ts)
Content overrides: src/content/featured/ MDX (editorial blurbs only)
Deploy: Cloudflare Pages (static + Functions)