# Répertoire d'outils IA — Pack de contexte

> Pack de contexte copiable pour un site de répertoire d'outils IA construit avec Astro et PostgreSQL, afin que les agents comprennent le flux de soumission, le système de balisage et le modèle de contenu dès le premier prompt.

**Type:** Context Pack  
**Tools:** Cursor, Claude Code, Codex, Windsurf  
**Stack:** Astro, PostgreSQL, TypeScript, Tailwind  
**Updated:** 2026-06-08

---

Collez ceci en haut d'une tâche afin que l'agent comprenne le modèle de contenu,
le flux de modération et la stratégie de construction du répertoire d'outils IA avant
d'apporter des modifications.

## Contexte du projet

```txt
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 technique

```txt
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)
```

## Structure du répertoire

```txt
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
```

## Conventions de codage

```txt
- 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).
```

## Limites des tâches IA

```txt
- 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

```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)
```