{
  "id": "ai-tool-directory",
  "type": "context-packs",
  "category": "context",
  "locale": "fr",
  "url": "/fr/context/ai-tool-directory",
  "title": "Répertoire d'outils IA — Pack de contexte",
  "description": "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.",
  "tools": [
    "Cursor",
    "Claude Code",
    "Codex",
    "Windsurf"
  ],
  "stack": [
    "Astro",
    "PostgreSQL",
    "TypeScript",
    "Tailwind"
  ],
  "tags": [
    "astro",
    "postgres",
    "context-pack",
    "typescript",
    "search",
    "seo"
  ],
  "difficulty": null,
  "updated": "2026-06-08",
  "markdown": "Collez ceci en haut d'une tâche afin que l'agent comprenne le modèle de contenu,\nle flux de modération et la stratégie de construction du répertoire d'outils IA avant\nd'apporter des modifications.\n\n## Contexte du projet\n\n```txt\nA directory of AI tools and products. Each listing is a row in PostgreSQL,\npulled at build time by Astro's Content Layer and rendered to static HTML.\nSubmissions arrive via a public HTML form that posts to an Astro API endpoint;\nentries are held in a `pending` state until an admin approves them through a\npassword-protected dashboard. The site is deployed to Cloudflare Pages with a\nNeon Postgres database. Search is client-side Pagefind over the static output.\n```\n\n## Stack technique\n\n```txt\nAstro 5 (static output, SSR disabled except for /api/* and /admin/* routes)\nTypeScript (strict)\nTailwind CSS v4\nPostgreSQL 16 via Neon (serverless HTTP driver @neondatabase/serverless)\nDrizzle ORM + drizzle-kit for schema + migrations\nPagefind (client-side full-text search over built HTML)\nCloudflare Pages (static + Functions for the hybrid API routes)\n```\n\n## Structure du répertoire\n\n```txt\nsrc/\n  pages/\n    index.astro               # Homepage + featured tools grid\n    tools/\n      [slug].astro            # Individual tool page (generated at build time)\n      index.astro             # Browsable directory with category filters\n    api/\n      submit.ts               # POST — accepts public tool submissions\n      admin/\n        approve.ts            # POST — marks a listing approved (auth-gated)\n        reject.ts             # POST — deletes a pending listing (auth-gated)\n    admin/\n      index.astro             # Admin dashboard — lists pending submissions\n  lib/\n    db/\n      index.ts                # Neon client + Drizzle instance\n      schema.ts               # tools, categories, tags tables\n    auth.ts                   # Simple HMAC token check for /admin routes\n    slugify.ts                # Deterministic slug generation from tool name\n    validate.ts               # Zod schema for submission form fields\n  content/\n    featured/                 # MDX overrides for hand-curated tool blurbs\n  components/\n    ToolCard.astro\n    CategoryFilter.astro\n    SubmissionForm.astro\ndrizzle/\n  migrations/\ndrizzle.config.ts\n```\n\n## Conventions de codage\n\n```txt\n- Tool listings are the source of truth in Postgres, not MDX files.\n  MDX in src/content/featured/ only overrides the blurb for curated picks.\n- Slugs are derived from the tool name via src/lib/slugify.ts and stored in\n  the DB. Never compute the slug on the fly — always read it from the DB row.\n- All submission input is validated with the Zod schema in src/lib/validate.ts\n  before any DB write. Return 400 with field-level errors on failure.\n- The /admin/* routes check a Bearer token with the HMAC verifier in\n  src/lib/auth.ts. Do not add another auth mechanism.\n- DB calls happen only in Astro API endpoints (*.ts in pages/api/) and in the\n  getStaticPaths() of dynamic pages. Never in .astro components directly.\n- Pagefind is run as a post-build step (`npx pagefind --site dist`).\n  Do not add client-side search that bypasses Pagefind.\n- Category and tag slugs are normalized to lowercase kebab-case.\n  Maintain the canonical list in src/lib/db/schema.ts (categories table).\n```\n\n## Limites des tâches IA\n\n```txt\n- Do not add a full auth system (NextAuth, Better Auth) — the admin gate is\n  intentionally a single shared HMAC token for simplicity.\n- Do not move tool data into MDX content collections — the DB is the source\n  of truth and MDX is only for editorial overrides.\n- Do not change the Astro output mode to fully SSR — the majority of pages\n  must remain static for Pagefind and CDN caching to work.\n- Do not allow unapproved (status != 'approved') tools to appear in\n  getStaticPaths() or the public directory index.\n- Schema changes must go through Drizzle migrations. Never use raw ALTER TABLE\n  against the Neon database directly.\n- External URLs from user submissions must be stored as-is and only rendered\n  through rel=\"nofollow noopener noreferrer\" links.\n- Do not expose the admin token in any client-side bundle, Astro component\n  prop, or public API response.\n```\n\n## llms.txt\n\n```txt\n# AI Tool Directory\nFramework: Astro 5 (hybrid: static pages + SSR API routes)\nDB: Neon Postgres via Drizzle ORM (src/lib/db/schema.ts)\nSearch: Pagefind (post-build, client-side over static HTML)\nSubmissions: POST /api/submit → status='pending' → admin approval\nAdmin auth: HMAC Bearer token (src/lib/auth.ts) — no user accounts\nSlugs: generated once on insert, stored in DB (src/lib/slugify.ts)\nContent overrides: src/content/featured/ MDX (editorial blurbs only)\nDeploy: Cloudflare Pages (static + Functions)\n```"
}