# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Development Commands ### Build & Run ```bash pnpm dev # Start development server (Next.js 15) pnpm build # Build for production pnpm start # Start production server pnpm lint # Run ESLint ``` ### Testing ```bash pnpm test # Run Vitest unit tests pnpm test:e2e # Run Playwright E2E tests ``` ### Database ```bash pnpm prisma migrate dev # Run database migrations pnpm prisma migrate dev --name init # Create initial migration pnpm prisma db seed # Seed database (uses ts-node) pnpm prisma studio # Open Prisma Studio for database inspection ``` ## Architecture Overview This is a **Next.js 15 multilingual AI project navigation website** using the App Router architecture with the following key components: ### i18n Architecture (next-intl) - **Locales**: `zh` (default) and `en` - **Route pattern**: `/{locale}/path` (always prefixed with locale) - **Middleware**: `src/middleware.ts` handles locale detection and routing - **i18n config**: `src/i18n/request.ts` loads locale messages from `src/messages/{locale}.json` - **Messages**: Translation files at `src/messages/zh.json` and `src/messages/en.json` ### App Router Structure ``` src/app/ ├── [locale]/ # Locale-scoped routes │ ├── page.tsx # Home page │ ├── projects/ # Projects listing and details │ │ ├── page.tsx # Projects list │ │ └── [id]/ # Individual project details (slug-based) │ └── layout.tsx # Locale layout (header, footer) ├── api/ # API routes (no locale prefix) │ └── webhook/projects/route.ts # Webhook for project ingestion └── layout.tsx # Root layout ``` ### Database (Prisma + PostgreSQL) - **Provider**: Neon (serverless PostgreSQL) - **Schema**: `prisma/schema.prisma` defines models: `Project`, `Tag`, `ExternalLink`, `ProjectTag` - **Enums**: `ProjectStatus` (ACTIVE/ARCHIVED), `LinkType` (WEBSITE/GITHUB/HUGGINGFACE/PAPER) - **Client singleton**: `src/lib/prisma.ts` exports Prisma client instance - **Multilingual fields**: Most models have `name`/`nameEn`, `description`/`descriptionEn`, `content`/`contentEn` pairs - **Cascade deletions**: `ExternalLink` and `ProjectTag` use `onDelete: Cascade` - deleting a project automatically cleans up its links and tag connections - **Key constraints**: - `Project.slug`: Unique - `Tag.name`: Unique (tag names are globally unique) - `Tag.slug`: Unique - `ExternalLink`: `@@unique([projectId, url])` (each project can't have duplicate URLs) - **Indexes**: `idx_project_status_createdAt`, `idx_project_slug`, `idx_tag_slug`, `idx_link_projectId`, `idx_link_type`, `idx_link_type_url` (composite for efficient URL-based deduplication) #### Neon Database Setup - **Dashboard**: https://console.neon.tech - **Connection String Format**: `postgres://[user]:[password]@[host]/[database]?sslmode=require` - **Vercel Integration**: Set `DATABASE_URL` environment variable in Vercel Dashboard (do NOT use `vercel.json` env references) - **Free Tier**: 0.5GB storage, 300 hours compute/month - **Run Migrations**: After deployment, run `pnpm prisma db push` or use Neon's SQL Editor to create tables ### Webhook Deduplication Strategy The webhook at `src/app/api/webhook/projects/route.ts` implements a **multi-level deduplication** strategy to prevent duplicate projects: 1. **GitHub URL exact match** (highest priority) - via `ExternalLink` table using `idx_link_type_url` index 2. **Website URL exact match** - via `ExternalLink` table using `idx_link_type_url` index 3. **slug match** (fallback) - via `Project.slug` field When updating an existing project, the webhook: - Updates all project fields (name, description, content, status, source) - Replaces all tags (deletes old `ProjectTag` connections via `ProjectTag` table, creates new ones) - Replaces all links (deletes old `ExternalLink` entries, creates new ones) **Tag handling special case**: Due to `Tag.name` unique constraint, tag upsert follows: 1. First try to find existing tag by name 2. If not found, try upsert by slug 3. If slug conflicts, use the existing tag with that slug ### Data Fetching (Server-Side) - **Location**: `src/hooks/useProjects.ts` (server functions, not React hooks) - **Functions**: `getProjects()`, `getProjectBySlug()`, `getAllTags()`, `getTagsWithProjectCounts()` - **Usage**: Directly called in Server Components and route handlers - **Query transformation**: `getProjects()` and `getProjectBySlug()` flatten the `ProjectTag` junction table structure to return tags directly - **ISR**: Project detail pages use `export const revalidate = 300` (5 minutes) at `src/app/[locale]/projects/[id]/page.tsx` ### Validation (Zod) - **Schemas**: `src/lib/validations.ts` defines all Zod schemas - `ProjectInputSchema`: Validates incoming project data (1-10 tags, 1-10 links required) - `WebhookPayloadSchema`: Validates webhook requests with API key (1-100 projects per request) - `ProjectQuerySchema`: Validates query parameters (search, tags, status, page, limit) ### Styling (Tailwind CSS) - **Neo-brutalism design**: Sharp corners (0px radius), bold borders (4px shadows), hard edges - **Theme colors**: - Primary: Gold (#FFD700) - Secondary: Orange (#ff6f00) - Background light: #F5F2EB, dark: #121212 - Surface light: #FFFFFF, dark: #1E1E1E - **Typography**: Space Mono (headings), Inter (body) - **Dark mode**: Class-based with `dark:` prefix - **Config**: `tailwind.config.ts` extends theme with custom colors, shadows, and animations ### Content Rendering - **Markdown**: Project content fields support Markdown via `react-markdown` - **Plugins**: `rehype-raw`, `rehype-sanitize`, `rehype-shiki`, `remark-gfm` - **Usage**: `ProjectDetail` component renders `content`/`contentEn` as Markdown ### UI Components - **Radix UI primitives**: `@radix-ui/react-slot`, `@radix-ui/react-navigation-menu`, `@radix-ui/react-dropdown-menu`, `@radix-ui/react-separator` - **Lucide React icons**: Used throughout the app (package imports optimized via `experimental.optimizePackageImports`) - **Custom components**: `src/components/` organized by domain - `layout/`: Header, Footer, AnnouncementBar - `locale/`: LocaleSwitcher - `project/`: ProjectCard, ProjectList, ProjectDetail, ProjectSidebar, RelatedProjects, TagCloud, ExternalLinkCard, ShareButtons, MarkdownContent, GitHubBadges, GitHubTextStatsCard - `search/`: SearchBar - `ui/`: Base UI components (buttons, cards, etc.) ### Next.js Configuration - **next.config.js**: - `next-intl` plugin wrapper for i18n - Image domains: localhost, *.anthropic.com, img.shields.io - Lucide-react package import optimization - **tsconfig.json**: ES2017 target, strict mode enabled, noUncheckedIndexedAccess enabled - **Testing**: Vitest for unit tests, Playwright for E2E tests (configured but not extensively used yet) ### Project Discovery System 项目发现系统是自动化探索和收录 AI 项目的核心功能,采用**双 Agent 协作架构**实现上下文隔离: #### 架构组件 1. **自定义 Agents** (`.claude/agents/`): - `content-explorer-agent`: 项目内容探索专家,批量探索项目并生成结构化数据 - 使用 `agent-browser` 子任务并行探索 GitHub 项目 - 应用严格的内容质量标准(客观描述、避免营销术语、不写入动态数据) - 生成符合 `ProjectInputSchema` 的 JSON 数据 - `api-submitter-agent`: API 提交专家,处理探索结果的提交和状态更新 - 批量标记任务为 IN_PROGRESS - 提交探索数据到完成 API - 自动重试失败的提交(指数退避,最多3次) 2. **Claude Commands** (`.claude/commands/`): - `/discover-projects`: 主命令,协调探索和提交流程 - 参数解析(任务数量、批次大小) - 分批处理(默认每批3个任务) - Agent 调度和进度显示 - 结果汇总和错误报告 3. **API Endpoints** (`src/app/api/discovery/`): - `POST /api/discovery/tasks`: 创建新的探索任务(支持批量) - `GET /api/discovery/tasks`: 获取待处理任务列表(支持 status/limit/offset 过滤) - `PATCH /api/discovery/tasks/{id}`: 更新任务状态 - `POST /api/discovery/tasks/{id}/complete`: 完成任务并提交项目数据 - `GET /api/webhook/check-duplicates`: 检查项目是否已存在(URL 去重) 4. **Database Model**: - `ProjectDiscoveryTask`: 任务追踪表 - 状态: PENDING → IN_PROGRESS → COMPLETED/FAILED - 原始数据: `sourceUrl`, `sourceType` - 探索结果: `explorationData` (JSON), `explorationSummary` - 错误处理: `errorMessage`, `retryCount`, `lastRetryAt` - 索引: `idx_task_status_created`, `idx_task_source_url`, `idx_task_project_id` #### 数据流转 ``` 用户输入 URL → 创建 PENDING 任务 → /discover-projects 命令 ↓ 分批获取任务(每批3个) ↓ Content Explorer Agent (并行探索) → 探索结果 JSON ↓ API Submitter Agent (提交到生产环境 API) ↓ 更新任务状态 → COMPLETED/FAILED ``` #### 质量标准 - **数据模板**: `.claude/schemas/project-content-template.md` - **描述要求**: 清晰说明功能、突出价值、避免营销术语、10-500字 - **内容要求**: 从 README 提取并重新组织、不机械翻译、符合中文表达习惯 - **链接要求**: 必须包含 GITHUB 链接、所有链接可访问 - **标签要求**: 1-10 个标签、技术/应用/状态分类 - **动态数据处理**: Star/Fork 数量等动态数据不写入内容,使用 GitHub Badge 显示 #### 环境变量 - `WEBHOOK_API_KEY`: 生产环境 API 密钥(必需,用于认证) #### 使用示例 ```bash # 处理默认10个任务(每批3个) /discover-projects # 处理指定数量的任务 /discover-projects 5 # 自定义批次大小 /discover-projects 9 --batch=2 # 处理所有待处理任务 /discover-projects all --batch=5 ``` ## MCP Servers Usage (按需使用) 1. **context7**: 不确定 API 用法时查阅最新文档 2. **chrome-devtools-mcp**: 查看页面效果、调试 UI 修复 BUG 3. **web-search-prime**: 默认联网搜索工具 4. **vision-mcp-server**: 图片/视频理解 ## Git Commits Git 提交信息遵循约定式提交格式(详见上方 Code Quality & Standards → Git Commit Conventions)。 ## Development Workflow - Before viewing the page, check if there's already a project running at localhost:3000. If yes, access it directly; if no, then run `pnpm dev` - The dev server runs with hot-reload enabled for fast iteration - The middleware handles locale detection and routing automatically - no manual locale configuration needed - When adding new translations, update both `src/messages/zh.json` and `src/messages/en.json` - Database changes require running `pnpm prisma migrate dev` to update the schema - **Always verify API responses** with `WEBHOOK_API_KEY` when testing webhook or discovery endpoints - **Use `pnpm prisma studio`** to inspect database state during development ## Common Development Patterns ### Adding a New API Endpoint 1. Create route file in `src/app/api/` (e.g., `src/app/api/your-endpoint/route.ts`) 2. Import validation schemas from `@/lib/validations` 3. Use `crypto.timingSafeEqual()` for API key authentication (see webhook route:36-43) 4. Implement partial success mode for batch operations (see webhook route:55-67) 5. Return structured errors with `NextResponse.json()` ### Adding New Database Fields 1. Update `prisma/schema.prisma` with new fields 2. Run `pnpm prisma migrate dev --name your_migration_name` 3. Update Zod schemas in `src/lib/validations.ts` 4. Update TypeScript types in `src/hooks/useProjects.ts` if needed 5. Regenerate Prisma client: `pnpm prisma generate` ### Creating Custom Agents 1. Create agent file in `.claude/agents/your-agent.md` 2. Define agent role, capabilities, and task instructions 3. Reference existing agents (`content-explorer-agent`, `api-submitter-agent`) as templates 4. Test via corresponding Claude Command in `.claude/commands/` ### Working with Multilingual Content - All user-facing content should have both Chinese (`name`, `description`) and English (`nameEn`, `descriptionEn`) versions - Use `src/messages/zh.json` and `src/messages/en.json` for UI translations - For project data, prefer Chinese as primary language with English as optional - When creating content, avoid mechanical translation - write naturally for each locale ## Code Quality & Standards ### TypeScript Configuration - **Strict mode enabled** with additional safety flags: `noUncheckedIndexedAccess`, `noImplicitReturns`, `noFallthroughCasesInSwitch` - Path alias: `@/*` maps to `./src/*` - Target: ES2017 for modern browser support ### Validation & Security - **API Authentication**: Webhook uses timing-safe comparison (`crypto.timingSafeEqual`) to prevent timing attacks - **Input Validation**: All API inputs use Zod schemas with detailed error messages - **SQL Injection Prevention**: Prisma ORM with parameterized queries - **Data Sanitization**: Markdown content sanitized with `rehype-sanitize` plugin ### Error Handling Patterns - **Webhook**: Partial success mode - continues processing remaining projects even if individual projects fail - **Database**: Unique constraints use try-catch with fallback logic (e.g., tag slug conflicts in webhook) - **Console**: Use `console.warn()` for operational logs, `console.error()` for errors ### Git Commit Conventions - **Format**: `: ` (type in lowercase Chinese: feat/fix/refactor/chore) - **Types**: `feat` (新功能), `fix` (修复), `refactor` (重构), `chore` (杂项) - **Examples**: - `feat: 新增项目发现任务系统` - `fix: 修复 ESLint 警告` - `refactor: 重构项目内容标准实现职责分离` ## Testing Strategy ### Unit Tests (Vitest) - Location: Test files co-located with source code (e.g., `*.test.ts`) - Run: `pnpm test` for all tests, `pnpm test ` for specific tests - Configuration: Vitest with `@testing-library/jest-dom` matchers ### E2E Tests (Playwright) - Location: `tests/e2e/` or co-located with features - Run: `pnpm test:e2e` to execute all E2E tests - Usage: Focus on critical user journeys (project browsing, search, locale switching) ## Performance Considerations ### Database Optimization - **Index Strategy**: Composite indexes on frequently queried fields (status+createdAt, type+url) - **N+1 Prevention**: Batch queries for tags (see webhook route:94-98) - **Connection Pooling**: Prisma client singleton pattern (`src/lib/prisma.ts`) ### Frontend Performance - **ISR**: Project detail pages revalidated every 5 minutes (`revalidate = 300`) - **Package Optimization**: Lucide-react imports optimized via `experimental.optimizePackageImports` - **Image Domains**: Pre-configured for localhost, *.anthropic.com, img.shields.io ### API Rate Limiting - Webhook: Max 100 projects per request - Discovery tasks: Max 50 tasks per batch creation - All queries: Max 100 items per page (enforced via Zod schemas) ## Troubleshooting ### Common Issues **Database Connection Errors** - Verify `DATABASE_URL` is set in `.env.local` - Check Neon dashboard for database status - Run `pnpm prisma db push` to sync schema if needed **Webhook Authentication Failures** - Ensure `WEBHOOK_API_KEY` is set and matches (32+ characters) - Check that API key is sent in request body - Verify timing-safe comparison is used (never log raw API keys) **Missing Translations** - All new UI text must be added to both `src/messages/zh.json` and `src/messages/en.json` - Missing keys will display as `missing_key_name` in the UI - Test both locales (switch via URL path `/zh/` or `/en/`) **Build Errors After Schema Changes** - Run `pnpm prisma generate` to regenerate Prisma client - Restart dev server after schema changes - Check for TypeScript errors in generated types **Discovery Task Failures** - Check `src/app/api/discovery/lib/discovery-service.ts:16-80` for deduplication logic - Verify `WEBHOOK_API_KEY` is set for api-submitter-agent - Use `pnpm prisma studio` to inspect task status and error messages - Check browser console for agent-browser errors (requires chrome-devtools-mcp) ### Debugging Tips 1. **Enable Prisma Query Logging**: ```typescript // In src/lib/prisma.ts export const prisma = new PrismaClient({ log: ['query', 'error', 'warn'], }) ``` 2. **Check Database State**: ```bash pnpm prisma studio # Opens at http://localhost:5555 ``` 3. **Test API Endpoints**: ```bash # Create discovery task curl -X POST http://localhost:3000/api/discovery/tasks \ -H "Content-Type: application/json" \ -d '{"apiKey":"YOUR_KEY","tasks":[{"sourceUrl":"https://github.com/user/repo"}]}' ``` 4. **Validate Request Payloads**: - All schemas are in `src/lib/validations.ts` - Check schema errors in API response `details` field - Ensure all required fields are present ### Environment Variables Checklist For local development, ensure these are set in `.env.local`: ```bash # Database (Neon PostgreSQL) DATABASE_URL="postgres://[user]:[password]@[host]/[database]?sslmode=require" # Webhook API Key (32+ characters) WEBHOOK_API_KEY="your-secret-api-key-min-32-chars" ``` For production deployment (Vercel): - Set `DATABASE_URL` in Vercel Dashboard (Environment Variables) - Set `WEBHOOK_API_KEY` in Vercel Dashboard - Run `pnpm prisma db push` after first deployment to create tables - Do NOT use `vercel.json` for environment variables