- Runtime: Node.js 20 (LTS), pnpm 9
- Framework: Next.js 15 (App Router) or SvelteKit — check the repo's package.json
- Language: TypeScript (strict mode)
- Styling: Tailwind CSS v4
- Database: PostgreSQL via Neon (serverless), Drizzle ORM
- Auth: Lucia v3 or Auth.js v5
- Payments: Stripe
- Email: SendGrid
- SMS: Twilio
- AI: OpenAI SDK + Google Gemini SDK
- Queue/Cache: QStash (jobs), Upstash Redis (cache/session)
- Storage: Tigris (S3-compatible object storage)
- Deploy: Vercel
- CI: GitHub Actions
- TypeScript strict — no
any, preferunknown - Named exports over default exports
- Conventional commits:
feat:,fix:,chore:,refactor:,docs: - No comments unless explaining why, not what
- Prefer
async/awaitover.then() - Server Actions for mutations in Next.js
- Zod for all external input validation
- All credentials are in GitHub org secrets — never hardcode
- Access via
process.env.SECRET_NAME— always check for undefined at the boundary - Use the canonical names:
DATABASE_URL,OPENAI_API_KEY,STRIPE_SECRET_KEY, etc.
- Error handling: throw typed errors, catch at the route/action boundary
- API routes: validate request body with Zod before any business logic
- Database: use transactions for multi-step writes
- Migrations: Drizzle Kit (
pnpm drizzle-kit pushfor dev, generate migrations for prod)
- Don't use
console.login production code — use a logger or remove - Don't commit
.envfiles — use org secrets - Don't bypass TypeScript with
@ts-ignoreunless absolutely necessary with a comment - Don't use
var— useconst/let - Don't write tests that rely on global state