{
  "id": "ai-tool-directory",
  "type": "context-packs",
  "category": "context",
  "locale": "de",
  "url": "/de/context/ai-tool-directory",
  "title": "AI Tool Directory — Kontextpaket",
  "description": "Kopierbares Kontextpaket für eine mit Astro und PostgreSQL erstellte AI-Tool-Verzeichnis-Website, damit Agenten den Einreichungsprozess, das Tagging-System und das Inhaltsmodell ab dem ersten Prompt verstehen.",
  "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": "Fügen Sie dies am Anfang einer Aufgabe ein, damit der Agent das Inhaltsmodell, den Moderationsworkflow und die Build-Strategie für das AI-Tool-Verzeichnis versteht, bevor er Änderungen vornimmt.\n\n## Projekthintergrund\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\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## Verzeichnisstruktur\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## Codierkonventionen\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## KI-Aufgabengrenzen\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```"
}