{"owner":"lukevella","repo":"rallly","hasSkills":true,"totalSkillsCount":111,"totalTokensCount":49428,"categories":["anthropic-skill","cursor-rule","claude-rule"],"hasMcp":false,"mcpConfig":null,"found":[".agents/skills/prisma-postgres/SKILL.md",".agents/skills/prisma-cli/SKILL.md",".claude/skills/fix-crowdin-pr/SKILL.md",".agents/skills/prisma-database-setup/SKILL.md",".agents/skills/prisma-client-api/SKILL.md",".agents/skills/prisma-upgrade-v7/SKILL.md",".agents/skills/two-factor-authentication-best-practices/SKILL.md",".agents/skills/devmoji/SKILL.md",".agents/skills/organization-best-practices/SKILL.md",".agents/skills/email-and-password-best-practices/SKILL.md",".agents/skills/logging/SKILL.md",".agents/skills/create-auth-skill/SKILL.md",".agents/skills/better-auth-security-best-practices/SKILL.MD",".agents/skills/better-auth-best-practices/SKILL.md",".cursorrules","CLAUDE.md","packages/utils/biome.json","packages/utils/package.json","packages/utils/tsconfig.json","packages/ui/biome.json","packages/ui/components.json","packages/ui/package.json","packages/ui/tsconfig.json","packages/tsconfig/next.json","packages/tsconfig/node.json","packages/tsconfig/package.json","packages/tsconfig/react.json","packages/test-helpers/package.json","packages/test-helpers/tsconfig.json","packages/tailwind-config/package.json","packages/screenshots/package.json","packages/screenshots/tsconfig.json","packages/posthog/biome.json","packages/posthog/package.json","packages/posthog/tsconfig.json","packages/logger/package.json","packages/logger/tsconfig.json","packages/languages/biome.json","packages/languages/package.json","packages/languages/tsconfig.json","packages/emails/biome.json","packages/emails/package.json","packages/emails/tsconfig.json","packages/dayjs/biome.json","packages/dayjs/package.json","packages/database/biome.json","packages/database/package.json","packages/database/tsconfig.json","packages/billing/biome.json","packages/billing/package.json","packages/billing/tsconfig.json",".agents/skills/prisma-upgrade-v7/references/accelerate-users.md",".agents/skills/prisma-upgrade-v7/references/driver-adapters.md",".agents/skills/prisma-upgrade-v7/references/env-variables.md",".agents/skills/prisma-upgrade-v7/references/esm-support.md",".agents/skills/prisma-upgrade-v7/references/prisma-config.md",".agents/skills/prisma-upgrade-v7/references/removed-features.md",".agents/skills/prisma-upgrade-v7/references/schema-changes.md",".agents/skills/prisma-postgres/references/console-and-connections.md",".agents/skills/prisma-postgres/references/create-db-cli.md",".agents/skills/prisma-postgres/references/management-api.md",".agents/skills/prisma-postgres/references/management-api-sdk.md",".agents/skills/prisma-database-setup/references/cockroachdb.md",".agents/skills/prisma-database-setup/references/mongodb.md",".agents/skills/prisma-database-setup/references/mysql.md",".agents/skills/prisma-database-setup/references/postgresql.md",".agents/skills/prisma-database-setup/references/prisma-client-setup.md",".agents/skills/prisma-database-setup/references/prisma-postgres.md",".agents/skills/prisma-database-setup/references/sqlite.md",".agents/skills/prisma-database-setup/references/sqlserver.md",".agents/skills/prisma-client-api/references/client-methods.md",".agents/skills/prisma-client-api/references/constructor.md",".agents/skills/prisma-client-api/references/filters.md",".agents/skills/prisma-client-api/references/model-queries.md",".agents/skills/prisma-client-api/references/query-options.md",".agents/skills/prisma-client-api/references/raw-queries.md",".agents/skills/prisma-client-api/references/relations.md",".agents/skills/prisma-client-api/references/transactions.md",".agents/skills/prisma-cli/references/db-execute.md",".agents/skills/prisma-cli/references/db-pull.md",".agents/skills/prisma-cli/references/db-push.md",".agents/skills/prisma-cli/references/db-seed.md",".agents/skills/prisma-cli/references/debug.md",".agents/skills/prisma-cli/references/dev.md",".agents/skills/prisma-cli/references/format.md",".agents/skills/prisma-cli/references/generate.md",".agents/skills/prisma-cli/references/init.md",".agents/skills/prisma-cli/references/migrate-deploy.md",".agents/skills/prisma-cli/references/migrate-dev.md",".agents/skills/prisma-cli/references/migrate-diff.md",".agents/skills/prisma-cli/references/migrate-reset.md",".agents/skills/prisma-cli/references/migrate-resolve.md",".agents/skills/prisma-cli/references/migrate-status.md",".agents/skills/prisma-cli/references/studio.md",".agents/skills/prisma-cli/references/validate.md","packages/emails/locales/zh/emails.json","packages/emails/locales/sv/emails.json","packages/emails/locales/ru/emails.json","packages/emails/locales/pt-BR/emails.json","packages/emails/locales/pl/emails.json","packages/emails/locales/no/emails.json","packages/emails/locales/nl/emails.json","packages/emails/locales/it/emails.json","packages/emails/locales/hu/emails.json","packages/emails/locales/fr/emails.json","packages/emails/locales/fi/emails.json","packages/emails/locales/es/emails.json","packages/emails/locales/en/emails.json","packages/emails/locales/de/emails.json","packages/emails/locales/da/emails.json","packages/emails/locales/cs/emails.json"],"skills":{".agents/skills/prisma-postgres/SKILL.md":"---\nname: prisma-postgres\ndescription: Prisma Postgres setup and operations guidance across Console, create-db CLI, Management API, and Management API SDK. Use when creating Prisma Postgres databases, working in Prisma Console, provisioning with create-db/create-pg/create-postgres, or integrating programmatic provisioning with service tokens or OAuth.\nlicense: MIT\nmetadata:\n  author: prisma\n  version: \"1.0.0\"\n---\n\n# Prisma Postgres\n\nGuidance for creating, managing, and integrating Prisma Postgres across interactive and programmatic workflows.\n\n## When to Apply\n\nReference this skill when:\n- Setting up Prisma Postgres from Prisma Console\n- Provisioning instant temporary databases with `create-db`\n- Managing Prisma Postgres resources via Management API\n- Using `@prisma/management-api-sdk` in TypeScript/JavaScript\n- Handling claim URLs, connection strings, regions, and auth flows\n\n## Rule Categories by Priority\n\n| Priority | Category | Impact | Prefix |\n|----------|----------|--------|--------|\n| 1 | CLI Provisioning | CRITICAL | `create-db-cli` |\n| 2 | Management API | CRITICAL | `management-api` |\n| 3 | Management API SDK | HIGH | `management-api-sdk` |\n| 4 | Console and Connections | HIGH | `console-and-connections` |\n\n## Quick Reference\n\n- `create-db-cli` - instant databases and CLI options\n- `management-api` - service token and OAuth API workflows\n- `management-api-sdk` - typed SDK usage with token storage\n- `console-and-connections` - Console operations and connection setup\n\n## Core Workflows\n\n### 1. Console-first workflow\n\nUse Prisma Console for manual setup and operations:\n\n- Open `https://console.prisma.io`\n- Create/select workspace and project\n- Use Studio in the project sidebar to view/edit data\n- Retrieve direct connection details from the project UI\n\n### 2. Quick provisioning with create-db\n\nUse `create-db` when you need a database immediately:\n\n```bash\nnpx create-db@latest\n```\n\nAliases:\n\n```bash\nnpx create-pg@latest\nnpx create-postgres@latest\n```\n\nFor app integrations, you can also use the programmatic API (`create()` / `regions()`) from the `create-db` npm package.\n\nTemporary databases auto-delete after ~24 hours unless claimed.\n\n### 3. Programmatic provisioning with Management API\n\nUse API endpoints on:\n\n```text\nhttps://api.prisma.io/v1\n```\n\nExplore the schema and endpoints using:\n\n- OpenAPI docs: `https://api.prisma.io/v1/doc`\n- Swagger Editor: `https://api.prisma.io/v1/swagger-editor`\n\nAuth options:\n\n- Service token (workspace server-to-server)\n- OAuth 2.0 (act on behalf of users)\n\n### 4. Type-safe integration with Management API SDK\n\nInstall and use:\n\n```bash\nnpm install @prisma/management-api-sdk\n```\n\nUse `createManagementApiClient` for existing tokens, or `createManagementApiSdk` for OAuth + token refresh.\n\n## Rule Files\n\nDetailed guidance lives in:\n\n```\nreferences/console-and-connections.md\nreferences/create-db-cli.md\nreferences/management-api.md\nreferences/management-api-sdk.md\n```\n\n## How to Use\n\nStart with `references/create-db-cli.md` for fast setup, then switch to `references/management-api.md` or `references/management-api-sdk.md` when you need programmatic provisioning.\n",".agents/skills/prisma-cli/SKILL.md":"---\nname: prisma-cli\ndescription: Prisma CLI commands reference covering all available commands, options, and usage patterns. Use when running Prisma CLI commands, setting up projects, generating client, running migrations, or managing databases. Triggers on \"prisma init\", \"prisma generate\", \"prisma migrate\", \"prisma db\", \"prisma studio\".\nlicense: MIT\nmetadata:\n  author: prisma\n  version: \"7.0.0\"\n---\n\n# Prisma CLI Reference\n\nComplete reference for all Prisma CLI commands. This skill provides guidance on command usage, options, and best practices for Prisma ORM 7.x.\n\n## When to Apply\n\nReference this skill when:\n- Setting up a new Prisma project (`prisma init`)\n- Generating Prisma Client (`prisma generate`)\n- Running database migrations (`prisma migrate`)\n- Managing database state (`prisma db push/pull`)\n- Using local development database (`prisma dev`)\n- Debugging Prisma issues (`prisma debug`)\n\n## Rule Categories by Priority\n\n| Priority | Category | Impact | Prefix |\n|----------|----------|--------|--------|\n| 1 | Setup | HIGH | `init` |\n| 2 | Generation | HIGH | `generate` |\n| 3 | Development | HIGH | `dev` |\n| 4 | Database | HIGH | `db-` |\n| 5 | Migrations | CRITICAL | `migrate-` |\n| 6 | Utility | MEDIUM | `studio`, `validate`, `format`, `debug` |\n\n## Command Categories\n\n| Category | Commands | Purpose |\n|----------|----------|---------|\n| Setup | `init` | Bootstrap new Prisma project |\n| Generation | `generate` | Generate Prisma Client |\n| Validation | `validate`, `format` | Schema validation and formatting |\n| Development | `dev` | Local Prisma Postgres for development |\n| Database | `db pull`, `db push`, `db seed`, `db execute` | Direct database operations |\n| Migrations | `migrate dev`, `migrate deploy`, `migrate reset`, `migrate status`, `migrate diff`, `migrate resolve` | Schema migrations |\n| Utility | `studio`, `version`, `debug` | Development tools |\n\n## Quick Reference\n\n### Project Setup\n\n```bash\n# Initialize new project (creates prisma/ folder and prisma.config.ts)\nprisma init\n\n# Initialize with specific database\nprisma init --datasource-provider postgresql\nprisma init --datasource-provider mysql\nprisma init --datasource-provider sqlite\n\n# Initialize with Prisma Postgres (cloud)\nprisma init --db\n\n# Initialize with AI-generated schema\nprisma init --prompt \"E-commerce app with users, products, orders\"\n```\n\n### Client Generation\n\n```bash\n# Generate Prisma Client\nprisma generate\n\n# Watch mode for development\nprisma generate --watch\n\n# Generate without engine (for Accelerate/edge)\nprisma generate --no-engine\n\n# Generate specific generator only\nprisma generate --generator client\n```\n\n### Bun Runtime\n\nWhen using Bun, always add the `--bun` flag so Prisma runs with the Bun runtime (otherwise it falls back to Node.js because of the CLI shebang):\n\n```bash\nbunx --bun prisma init\nbunx --bun prisma generate\n```\n\n### Local Development Database\n\n```bash\n# Start local Prisma Postgres\nprisma dev\n\n# Start with specific name\nprisma dev --name myproject\n\n# Start in background (detached)\nprisma dev --detach\n\n# List all local instances\nprisma dev ls\n\n# Stop instance\nprisma dev stop myproject\n\n# Remove instance data\nprisma dev rm myproject\n```\n\n### Database Operations\n\n```bash\n# Pull schema from existing database\nprisma db pull\n\n# Push schema to database (no migrations)\nprisma db push\n\n# Seed database\nprisma db seed\n\n# Execute raw SQL\nprisma db execute --file ./script.sql\n```\n\n### Migrations (Development)\n\n```bash\n# Create and apply migration\nprisma migrate dev\n\n# Create migration with name\nprisma migrate dev --name add_users_table\n\n# Create migration without applying\nprisma migrate dev --create-only\n\n# Reset database and apply all migrations\nprisma migrate reset\n```\n\n### Migrations (Production)\n\n```bash\n# Apply pending migrations (CI/CD)\nprisma migrate deploy\n\n# Check migration status\nprisma migrate status\n\n# Compare schemas and generate diff\nprisma migrate diff --from-config-datasource --to-schema schema.prisma --script\n```\n\n### Utility Commands\n\n```bash\n# Open Prisma Studio (database GUI)\nprisma studio\n\n# Show version info\nprisma version\nprisma -v\n\n# Debug information\nprisma debug\n\n# Validate schema\nprisma validate\n\n# Format schema\nprisma format\n```\n\n## Prisma 7 Changes\n\n### New Configuration File\n\nPrisma 7 uses `prisma.config.ts` for CLI configuration:\n\n```typescript\nimport 'dotenv/config'\nimport { defineConfig, env } from 'prisma/config'\n\nexport default defineConfig({\n  schema: 'prisma/schema.prisma',\n  migrations: {\n    path: 'prisma/migrations',\n    seed: 'tsx prisma/seed.ts',\n  },\n  datasource: {\n    url: env('DATABASE_URL'),\n  },\n})\n```\n\n### Removed Flags\n\n- `--skip-generate` removed from `migrate dev` and `db push`\n- `--skip-seed` removed from `migrate dev`\n- `--schema` and `--url` removed from `db execute`\n- Run `prisma generate` explicitly after migrations\n\n### Environment Variables\n\nEnvironment variables are no longer auto-loaded. Use `dotenv`:\n\n```typescript\n// prisma.config.ts\nimport 'dotenv/config'\n```\n\n## Rule Files\n\nSee individual rule files for detailed command documentation:\n\n```\nreferences/init.md           - Project initialization\nreferences/generate.md       - Client generation\nreferences/dev.md            - Local development database\nreferences/db-pull.md        - Database introspection\nreferences/db-push.md        - Schema push\nreferences/db-seed.md        - Database seeding\nreferences/db-execute.md     - Raw SQL execution\nreferences/migrate-dev.md    - Development migrations\nreferences/migrate-deploy.md - Production migrations\nreferences/migrate-reset.md  - Database reset\nreferences/migrate-status.md - Migration status\nreferences/migrate-resolve.md - Migration resolution\nreferences/migrate-diff.md   - Schema diffing\nreferences/studio.md         - Database GUI\nreferences/validate.md       - Schema validation\nreferences/format.md         - Schema formatting\nreferences/debug.md          - Debug info\n```\n\n## How to Use\n\nUse the command categories above for navigation, then open the specific command reference file you need.\n",".claude/skills/fix-crowdin-pr/SKILL.md":"---\nname: fix-crowdin-pr\ndescription: Fix coderabbit review findings on a Crowdin translation PR (l10n_main), keeping Crowdin and the PR branch in sync. Takes an optional PR number, defaults to the open \"🌐 New Crowdin updates\" PR.\n---\n\n# Fix Crowdin PR\n\nFix coderabbit findings on a Crowdin sync PR so the fix survives the next sync.\n\n## Why this order matters\n\nCrowdin force pushes `l10n_main` on every sync. A fix that lands only on the PR branch gets overwritten; a fix made only in Crowdin waits for the next sync. Therefore: upload corrections to Crowdin **first**, then push the identical changes to the branch. The next sync then exports identical content and the branch stays stable.\n\n## Credentials\n\nToken and project id live in the macOS Keychain:\n\n```\nsecurity find-generic-password -s rallly-billing.CROWDIN_PERSONAL_TOKEN -w\nsecurity find-generic-password -s rallly-billing.CROWDIN_PROJECT_ID -w\n```\n\nThe token is the secret: retrieve it inline, never echo it. The project id is not secret — it is 527308 and appears in API paths below. Files are namespaced under Crowdin branch `main` (branchId 10).\n\n## Steps\n\n### 1. Find findings\n\nDefault to the open PR titled \"🌐 New Crowdin updates\" (`gh pr list --search \"🌐 New Crowdin updates\" --state open`), or use the PR number given as an argument.\n\n```\ngh api repos/lukevella/rallly/pulls/<PR>/comments --paginate\n```\n\nFilter to comments authored by `coderabbitai`. Extract path, line, and the suggested fix from each body.\n\n### 2. Check out the branch\n\nCheck out `l10n_main` in the session worktree.\n\n### 3. Apply fixes\n\nJudge each finding on its merits — coderabbit is sometimes wrong. Skip anything that contradicts the English source in `apps/web/public/locales/en/`, `packages/emails/locales/en/`, or `apps/landing/public/locales/en/`, and note why.\n\nApply accepted fixes with a python script using exact string replacement:\n\n- Assert each old string occurs exactly once in the file before replacing.\n- After all edits, `json.load` every touched file to prove it still parses.\n\n### 4. Upload to Crowdin\n\nDerive the affected languages from the touched file paths (the locale directory segment), then upload.\n\n**Primary: Crowdin CLI** (works since `preserve_hierarchy: true` landed in crowdin.yml, PR #2717). One run per affected language:\n\n```\nCROWDIN_PERSONAL_TOKEN=$(security find-generic-password -s rallly-billing.CROWDIN_PERSONAL_TOKEN -w) \\\nCROWDIN_PROJECT_ID=$(security find-generic-password -s rallly-billing.CROWDIN_PROJECT_ID -w) \\\nnpx -y @crowdin/cli@4 upload translations -b main -l <crowdin-lang-id> --auto-approve-imported --no-progress\n```\n\nThe CLI uploads all translation files for the language, not just touched ones; identical translations are skipped by default, so this is harmless. Do not run `crowdin status` — the token scopes don't cover it.\n\n**Fallback: REST API** — use when the CLI fails or Java/npx is unavailable (it is faster and uploads only touched files, but needs hand-rolled requests). Per touched file and language:\n\n1. `POST https://api.crowdin.com/api/v2/storages` with headers `Authorization: Bearer <token>`, `Crowdin-API-FileName: <basename>`, `Content-Type: application/octet-stream`, body = raw file bytes. Response gives `storageId`.\n2. `POST https://api.crowdin.com/api/v2/projects/527308/translations/<crowdin-lang-id>` with JSON body:\n\n   ```json\n   {\"storageId\": <id>, \"fileId\": <fileId>, \"importEqSuggestions\": false, \"autoApproveImported\": true}\n   ```\n\nKnown fileIds: web `app.json` = 35, emails `emails.json` = 68, landing `blog.json` = 53, `common.json` = 55, `home.json` = 57, `pricing.json` = 59. If a fileId is unknown, list via `GET /api/v2/projects/527308/files`.\n\n### 5. Verify\n\nConfirm at least one fix landed as the current translation:\n\n```\nGET /api/v2/projects/527308/languages/<crowdin-lang-id>/translations?stringIds=<ids>&fileId=<fileId>\n```\n\nFind stringIds via `GET /api/v2/projects/527308/strings?fileId=<fileId>&filter=<key>&scope=identifier`.\n\nDo **not** use `GET /api/v2/projects/527308/translations?stringId=...` for verification — it lists suggestions oldest first and ignores orderBy, so it shows stale entries.\n\n### 6. Commit and push\n\nCommit to `l10n_main` with a `🌐` gitmoji message and push. `--no-verify` is acceptable in a fresh worktree (husky needs node_modules).\n\n### 7. Reply and resolve every comment\n\nCrowdin syncs keep adding new translations, and each sync can bring new coderabbit comments. Unresolved threads must mean \"not yet addressed\" — so after pushing, reply to **every** finding and resolve its thread, applied or skipped:\n\n- Applied: cite the fix commit sha and note the Crowdin upload was verified.\n- Skipped: state the reason (e.g. matches the English source, contradicts the file's register).\n\nReply to a review comment (comment ids come from the step 1 fetch):\n\n```\ngh api -X POST repos/lukevella/rallly/pulls/<PR>/comments/<comment-id>/replies -f body='...'\n```\n\nResolving needs GraphQL — list thread ids, then resolve the ones still open (coderabbit auto-resolves some threads when it sees the fix commit; skip those):\n\n```\ngh api graphql -f query='query { repository(owner: \"lukevella\", name: \"rallly\") { pullRequest(number: <PR>) { reviewThreads(first: 50) { nodes { id isResolved comments(first: 1) { nodes { databaseId } } } } } } }'\ngh api graphql -f query='mutation { resolveReviewThread(input: {threadId: \"<thread-id>\"}) { thread { isResolved } } }'\n```\n\n### 8. Report\n\nSummarize: findings applied, findings skipped and why, Crowdin verification result, pushed commit sha, and confirmation that all threads are replied to and resolved.\n\n## Language id mapping\n\nRepo locale dirs use two letter codes (`%two_letters_code%` in crowdin.yml) plus `pt-BR` and `zh-Hant`. Crowdin language ids differ for some:\n\n| Repo dir | Crowdin id |\n|----------|------------|\n| es | es-ES |\n| sv | sv-SE |\n| zh | zh-CN |\n| pt-BR | pt-BR |\n\nEverything else observed so far matches the two letter code. If unsure, check `targetLanguageIds` via `GET /api/v2/projects/527308` — an upload to a wrong language id fails loudly, so a mistake here can't corrupt data, only waste a round trip.\n\n## Gotchas\n\n- The Bash tool sandbox drops PATH inside shell functions. Make the REST API calls from a python script that reads the token via `subprocess` + `security`, not from shell functions wrapping curl.\n- Never write the token to a file, echo it, or pass it where it lands in logs. Keychain lookups inline only.\n- Findings on languages you can't judge: prefer skipping over guessing. A wrong \"fix\" auto-approved into Crowdin displaces a human translation.\n",".agents/skills/prisma-database-setup/SKILL.md":"---\nname: prisma-database-setup\ndescription: Guides for configuring Prisma with different database providers (PostgreSQL, MySQL, SQLite, MongoDB, etc.). Use when setting up a new project, changing databases, or troubleshooting connection issues. Triggers on \"configure postgres\", \"connect to mysql\", \"setup mongodb\", \"sqlite setup\".\nlicense: MIT\nmetadata:\n  author: prisma\n  version: \"1.0.0\"\n---\n\n# Prisma Database Setup\n\nComprehensive guides for configuring Prisma ORM with various database providers.\n\n## When to Apply\n\nReference this skill when:\n- Initializing a new Prisma project\n- Switching database providers\n- Configuring connection strings and environment variables\n- Troubleshooting database connection issues\n- Setting up database-specific features\n- Generating and instantiating Prisma Client\n\n## Rule Categories by Priority\n\n| Priority | Category | Impact | Prefix |\n|----------|----------|--------|--------|\n| 1 | Provider Guides | CRITICAL | provider names |\n| 2 | Prisma Postgres | HIGH | `prisma-postgres` |\n| 3 | Client Setup | CRITICAL | `prisma-client-setup` |\n\n## System Prerequisites (Prisma ORM 7)\n\n- **Node.js 20.19.0+**\n- **TypeScript 5.4.0+**\n\n## Bun Runtime\n\nIf you're using Bun, run Prisma CLI commands with `bunx --bun prisma ...` so Prisma uses the Bun runtime instead of falling back to Node.js.\n\n## Supported Databases\n\n| Database | Provider String | Notes |\n|----------|-----------------|-------|\n| PostgreSQL | `postgresql` | Default, full feature support |\n| MySQL | `mysql` | Widespread support, some JSON diffs |\n| SQLite | `sqlite` | Local file-based, no enum/scalar lists |\n| MongoDB | `mongodb` | **NOT SUPPORTED IN v7** (Use v6) |\n| SQL Server | `sqlserver` | Microsoft ecosystem |\n| CockroachDB | `cockroachdb` | Distributed SQL, Postgres-compatible |\n| Prisma Postgres | `postgresql` | Managed serverless database |\n\n## Configuration Files\n\nPrisma v7 uses two main files for configuration:\n\n1. **`prisma/schema.prisma`**: Defines the `datasource` block.\n2. **`prisma.config.ts`**: Configures the connection URL (replaces env loading in schema).\n\n## Driver Adapters (Prisma ORM 7)\n\nPrisma ORM 7 uses the query compiler by default, which **requires a driver adapter**. Choose the adapter and driver for your database and pass the adapter to `PrismaClient`.\n\n| Database | Adapter | JS Driver |\n|----------|---------|-----------|\n| PostgreSQL | `@prisma/adapter-pg` | `pg` |\n| CockroachDB | `@prisma/adapter-pg` | `pg` |\n| Prisma Postgres | `@prisma/adapter-ppg` | `@prisma/ppg` |\n| MySQL / MariaDB | `@prisma/adapter-mariadb` | `mariadb` |\n| SQLite | `@prisma/adapter-better-sqlite3` | `better-sqlite3` |\n| SQLite (Turso/LibSQL) | `@prisma/adapter-libsql` | `@libsql/client` |\n| SQL Server | `@prisma/adapter-mssql` | `node-mssql` |\n\nExample (PostgreSQL):\n\n```ts\nimport 'dotenv/config'\nimport { PrismaClient } from '../generated/client'\nimport { PrismaPg } from '@prisma/adapter-pg'\n\nconst adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL })\nconst prisma = new PrismaClient({ adapter })\n```\n\n## Prisma Client Setup (Required)\n\nPrisma Client must be installed and generated for any database.\n\n1. Install Prisma CLI and Prisma Client:\n   ```bash\n   npm install prisma --save-dev\n   npm install @prisma/client\n   ```\n\n1. Add a generator block (output is required in Prisma v7):\n   ```prisma\n   generator client {\n     provider = \"prisma-client\"\n     output   = \"../generated\"\n   }\n   ```\n\n1. Generate Prisma Client:\n   ```bash\n   npx prisma generate\n   ```\n\n1. Instantiate Prisma Client with the database-specific driver adapter:\n   ```typescript\n   import { PrismaClient } from '../generated/client'\n   import { PrismaPg } from '@prisma/adapter-pg'\n\n   const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL })\n   const prisma = new PrismaClient({ adapter })\n   ```\n\n1. Re-run `prisma generate` after every schema change.\n\n## Quick Reference\n\n### PostgreSQL\n```prisma\ndatasource db {\n  provider = \"postgresql\"\n}\n\ngenerator client {\n  provider = \"prisma-client\"\n  output   = \"../generated\"\n}\n```\n\n### MySQL\n```prisma\ndatasource db {\n  provider = \"mysql\"\n}\n\ngenerator client {\n  provider = \"prisma-client\"\n  output   = \"../generated\"\n}\n```\n\n### SQLite\n```prisma\ndatasource db {\n  provider = \"sqlite\"\n}\n\ngenerator client {\n  provider = \"prisma-client\"\n  output   = \"../generated\"\n}\n```\n\n### MongoDB (Prisma v6 only)\n```prisma\ndatasource db {\n  provider = \"mongodb\"\n  url      = env(\"DATABASE_URL\")\n}\n\ngenerator client {\n  provider = \"prisma-client-js\"\n}\n```\n\n## Rule Files\n\nSee individual rule files for detailed setup instructions:\n\n```\nreferences/postgresql.md\nreferences/mysql.md\nreferences/sqlite.md\nreferences/mongodb.md\nreferences/sqlserver.md\nreferences/cockroachdb.md\nreferences/prisma-postgres.md\nreferences/prisma-client-setup.md\n```\n\n## How to Use\n\nChoose the provider reference file for your database, then apply `references/prisma-client-setup.md` to complete client generation and adapter setup.\n",".agents/skills/prisma-client-api/SKILL.md":"---\nname: prisma-client-api\ndescription: Prisma Client API reference covering model queries, filters, operators, and client methods. Use when writing database queries, using CRUD operations, filtering data, or configuring Prisma Client. Triggers on \"prisma query\", \"findMany\", \"create\", \"update\", \"delete\", \"$transaction\".\nlicense: MIT\nmetadata:\n  author: prisma\n  version: \"7.0.0\"\n---\n\n# Prisma Client API Reference\n\nComplete API reference for Prisma Client. This skill provides guidance on model queries, filtering, relations, and client methods for Prisma ORM 7.x.\n\n## When to Apply\n\nReference this skill when:\n- Writing database queries with Prisma Client\n- Performing CRUD operations (create, read, update, delete)\n- Filtering and sorting data\n- Working with relations\n- Using transactions\n- Configuring client options\n\n## Rule Categories by Priority\n\n| Priority | Category | Impact | Prefix |\n|----------|----------|--------|--------|\n| 1 | Client Construction | HIGH | `constructor` |\n| 2 | Model Queries | CRITICAL | `model-queries` |\n| 3 | Query Shape | HIGH | `query-options` |\n| 4 | Filtering | HIGH | `filters` |\n| 5 | Relations | HIGH | `relations` |\n| 6 | Transactions | CRITICAL | `transactions` |\n| 7 | Raw SQL | CRITICAL | `raw-queries` |\n| 8 | Client Methods | MEDIUM | `client-methods` |\n\n## Quick Reference\n\n- `constructor` - `PrismaClient` setup and adapter wiring\n- `model-queries` - CRUD operations and bulk operations\n- `query-options` - `select`, `include`, `omit`, sort, pagination\n- `filters` - scalar and logical filter operators\n- `relations` - relation reads and nested writes\n- `transactions` - array and interactive transaction patterns\n- `raw-queries` - `$queryRaw` and `$executeRaw` safety\n- `client-methods` - lifecycle and extension methods\n\n## Client Instantiation (v7)\n\n```typescript\nimport { PrismaClient } from '../generated/client'\nimport { PrismaPg } from '@prisma/adapter-pg'\n\nconst adapter = new PrismaPg({\n  connectionString: process.env.DATABASE_URL\n})\n\nconst prisma = new PrismaClient({ adapter })\n```\n\n## Model Query Methods\n\n| Method | Description |\n|--------|-------------|\n| `findUnique()` | Find one record by unique field |\n| `findUniqueOrThrow()` | Find one or throw error |\n| `findFirst()` | Find first matching record |\n| `findFirstOrThrow()` | Find first or throw error |\n| `findMany()` | Find multiple records |\n| `create()` | Create a new record |\n| `createMany()` | Create multiple records |\n| `createManyAndReturn()` | Create multiple and return them |\n| `update()` | Update one record |\n| `updateMany()` | Update multiple records |\n| `updateManyAndReturn()` | Update multiple and return them |\n| `upsert()` | Update or create record |\n| `delete()` | Delete one record |\n| `deleteMany()` | Delete multiple records |\n| `count()` | Count matching records |\n| `aggregate()` | Aggregate values (sum, avg, etc.) |\n| `groupBy()` | Group and aggregate |\n\n## Query Options\n\n| Option | Description |\n|--------|-------------|\n| `where` | Filter conditions |\n| `select` | Fields to include |\n| `include` | Relations to load |\n| `omit` | Fields to exclude |\n| `orderBy` | Sort order |\n| `take` | Limit results |\n| `skip` | Skip results (pagination) |\n| `cursor` | Cursor-based pagination |\n| `distinct` | Unique values only |\n\n## Client Methods\n\n| Method | Description |\n|--------|-------------|\n| `$connect()` | Explicitly connect to database |\n| `$disconnect()` | Disconnect from database |\n| `$transaction()` | Execute transaction |\n| `$queryRaw()` | Execute raw SQL query |\n| `$executeRaw()` | Execute raw SQL command |\n| `$on()` | Subscribe to events |\n| `$extends()` | Add extensions |\n\n## Quick Examples\n\n### Find records\n\n```typescript\n// Find by unique field\nconst user = await prisma.user.findUnique({\n  where: { email: 'alice@prisma.io' }\n})\n\n// Find with filter\nconst users = await prisma.user.findMany({\n  where: { role: 'ADMIN' },\n  orderBy: { createdAt: 'desc' },\n  take: 10\n})\n```\n\n### Create records\n\n```typescript\nconst user = await prisma.user.create({\n  data: {\n    email: 'alice@prisma.io',\n    name: 'Alice',\n    posts: {\n      create: { title: 'Hello World' }\n    }\n  },\n  include: { posts: true }\n})\n```\n\n### Update records\n\n```typescript\nconst user = await prisma.user.update({\n  where: { id: 1 },\n  data: { name: 'Alice Smith' }\n})\n```\n\n### Delete records\n\n```typescript\nawait prisma.user.delete({\n  where: { id: 1 }\n})\n```\n\n### Transactions\n\n```typescript\nconst [user, post] = await prisma.$transaction([\n  prisma.user.create({ data: { email: 'alice@prisma.io' } }),\n  prisma.post.create({ data: { title: 'Hello', authorId: 1 } })\n])\n```\n\n## Rule Files\n\nDetailed API documentation:\n\n```\nreferences/constructor.md        - PrismaClient constructor options\nreferences/model-queries.md      - CRUD operations\nreferences/query-options.md      - select, include, omit, where, orderBy\nreferences/filters.md            - Filter conditions and operators\nreferences/relations.md          - Relation queries and nested operations\nreferences/transactions.md       - Transaction API\nreferences/raw-queries.md        - $queryRaw, $executeRaw\nreferences/client-methods.md     - $connect, $disconnect, $on, $extends\n```\n\n## Filter Operators\n\n| Operator | Description |\n|----------|-------------|\n| `equals` | Exact match |\n| `not` | Not equal |\n| `in` | In array |\n| `notIn` | Not in array |\n| `lt`, `lte` | Less than |\n| `gt`, `gte` | Greater than |\n| `contains` | String contains |\n| `startsWith` | String starts with |\n| `endsWith` | String ends with |\n| `mode` | Case sensitivity |\n\n## Relation Filters\n\n| Operator | Description |\n|----------|-------------|\n| `some` | At least one related record matches |\n| `every` | All related records match |\n| `none` | No related records match |\n| `is` | Related record matches (1-to-1) |\n| `isNot` | Related record doesn't match |\n\n## Resources\n\n- [Prisma Client API Reference](https://www.prisma.io/docs/orm/reference/prisma-client-reference)\n- [CRUD Operations](https://www.prisma.io/docs/orm/prisma-client/queries/crud)\n- [Filtering and Sorting](https://www.prisma.io/docs/orm/prisma-client/queries/filtering-and-sorting)\n\n## How to Use\n\nPick the category from the table above, then open the matching reference file for implementation details and examples.\n",".agents/skills/prisma-upgrade-v7/SKILL.md":"---\nname: prisma-upgrade-v7\ndescription: Complete migration guide from Prisma ORM v6 to v7 covering all breaking changes. Use when upgrading Prisma versions, encountering v7 errors, or migrating existing projects. Triggers on \"upgrade to prisma 7\", \"prisma 7 migration\", \"prisma-client generator\", \"driver adapter required\".\nlicense: MIT\nmetadata:\n  author: prisma\n  version: \"7.0.0\"\n---\n\n# Upgrade to Prisma ORM 7\n\nComplete guide for migrating from Prisma ORM v6 to v7. This upgrade introduces significant breaking changes including ESM-only support, required driver adapters, and a new configuration system.\n\n## When to Apply\n\nReference this skill when:\n- Upgrading from Prisma v6 to v7\n- Updating to the `prisma-client` generator\n- Setting up driver adapters\n- Configuring `prisma.config.ts`\n- Fixing import errors after upgrade\n\n## Rule Categories by Priority\n\n| Priority | Category | Impact | Prefix |\n|----------|----------|--------|--------|\n| 1 | Schema Migration | CRITICAL | `schema-changes` |\n| 2 | Database Connectivity | CRITICAL | `driver-adapters` |\n| 3 | Module System | CRITICAL | `esm-support` |\n| 4 | Config and Env | HIGH | `prisma-config`, `env-variables` |\n| 5 | Removed Features | HIGH | `removed-features` |\n| 6 | Accelerate | HIGH | `accelerate-users` |\n\n## Quick Reference\n\n- `schema-changes` - generator and output migration for Prisma v7\n- `driver-adapters` - required adapter installation and client wiring\n- `esm-support` - package/module requirements for ESM\n- `prisma-config` - creating and using `prisma.config.ts`\n- `env-variables` - explicit environment loading\n- `removed-features` - removed middleware, metrics, and flags\n- `accelerate-users` - migration notes for Accelerate users\n\n## Important Notes\n\n- **MongoDB not yet supported in v7** - Continue using v6 for MongoDB\n- **Node.js 20.19.0+** required\n- **TypeScript 5.4.0+** required\n\n## Upgrade Steps Overview\n\n1. Update packages to v7\n2. Configure ESM in package.json\n3. Update TypeScript configuration\n4. Update schema generator block\n5. Create prisma.config.ts\n6. Install and configure driver adapter\n7. Update Prisma Client imports\n8. Update client instantiation\n9. Remove deprecated code (middleware, env vars)\n10. Run generate and test\n\n## Quick Upgrade Commands\n\n```bash\n# Update packages\nnpm install @prisma/client@7\nnpm install -D prisma@7\n\n# Install driver adapter (PostgreSQL example)\nnpm install @prisma/adapter-pg\n\n# Install dotenv for env loading\nnpm install dotenv\n\n# Regenerate client\nnpx prisma generate\n```\n\n## Breaking Changes Summary\n\n| Change | v6 | v7 |\n|--------|----|----|\n| Module format | CommonJS | ESM only |\n| Generator provider | `prisma-client-js` | `prisma-client` |\n| Output path | Auto (node_modules) | Required explicit |\n| Driver adapters | Optional | Required |\n| Config file | `.env` + schema | `prisma.config.ts` |\n| Env loading | Automatic | Manual (dotenv) |\n| Middleware | `$use()` | Client Extensions |\n| Metrics | Preview feature | Removed |\n\n## Rule Files\n\nDetailed migration guides for each breaking change:\n\n```\nreferences/esm-support.md        - ESM module configuration\nreferences/schema-changes.md     - Generator and schema updates\nreferences/driver-adapters.md    - Required driver adapter setup\nreferences/prisma-config.md      - New configuration file\nreferences/env-variables.md      - Environment variable loading\nreferences/removed-features.md   - Middleware, metrics, and CLI flags\nreferences/accelerate-users.md   - Special handling for Accelerate\n```\n\n## Step-by-Step Migration\n\n### 1. Update package.json for ESM\n\n```json\n{\n  \"type\": \"module\"\n}\n```\n\n### 2. Update tsconfig.json\n\n```json\n{\n  \"compilerOptions\": {\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"target\": \"ES2023\",\n    \"strict\": true,\n    \"esModuleInterop\": true\n  }\n}\n```\n\n### 3. Update schema.prisma\n\n```prisma\n// Before (v6)\ngenerator client {\n  provider = \"prisma-client-js\"\n}\n\n// After (v7)\ngenerator client {\n  provider = \"prisma-client\"\n  output   = \"../generated\"\n}\n```\n\n### 4. Create prisma.config.ts\n\n```typescript\nimport 'dotenv/config'\nimport { defineConfig, env } from 'prisma/config'\n\nexport default defineConfig({\n  schema: 'prisma/schema.prisma',\n  migrations: {\n    path: 'prisma/migrations',\n  },\n  datasource: {\n    url: env('DATABASE_URL'),\n  },\n})\n```\n\n### 5. Install driver adapter\n\n```bash\n# PostgreSQL\nnpm install @prisma/adapter-pg\n\n# MySQL\nnpm install @prisma/adapter-mariadb mariadb\n\n# SQLite\nnpm install @prisma/adapter-better-sqlite3\n\n# Prisma Postgres\nnpm install @prisma/adapter-ppg @prisma/ppg\n\n# Neon\nnpm install @prisma/adapter-neon\n```\n\n### 6. Update client instantiation\n\n```typescript\n// Before (v6)\nimport { PrismaClient } from '@prisma/client'\nconst prisma = new PrismaClient()\n\n// After (v7)\nimport { PrismaClient } from '../generated/client'\nimport { PrismaPg } from '@prisma/adapter-pg'\n\nconst adapter = new PrismaPg({\n  connectionString: process.env.DATABASE_URL\n})\nconst prisma = new PrismaClient({ adapter })\n```\n\n### 7. Run migrations and generate\n\n```bash\nnpx prisma generate\nnpx prisma migrate dev  # if needed\n```\n\n## Troubleshooting\n\n### \"Cannot find module\" errors\n- Check `output` path in generator block matches import path\n- Ensure `prisma generate` ran successfully\n\n### SSL certificate errors\n- Add `ssl: { rejectUnauthorized: false }` to adapter config\n- Or properly configure SSL certificates\n\n### Connection timeout issues\n- Driver adapters use different pool defaults\n- Configure pool settings explicitly on the adapter\n\n## Resources\n\n- [Official v7 Upgrade Guide](https://www.prisma.io/docs/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-7)\n- [Driver Adapters Documentation](https://www.prisma.io/docs/orm/overview/databases/database-drivers)\n- [Prisma Config Reference](https://www.prisma.io/docs/orm/reference/prisma-config-reference)\n\n## How to Use\n\nFollow `references/schema-changes.md` and `references/driver-adapters.md` first, then apply the remaining reference files based on your project setup.\n",".agents/skills/two-factor-authentication-best-practices/SKILL.md":"---\nname: two-factor-authentication-best-practices\ndescription: This skill provides guidance and enforcement rules for implementing secure two-factor authentication (2FA) using Better Auth's twoFactor plugin.\n---\n\n## Setting Up Two-Factor Authentication\n\nWhen adding 2FA to your application, configure the `twoFactor` plugin with your app name as the issuer. This name appears in authenticator apps when users scan the QR code.\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { twoFactor } from \"better-auth/plugins\";\n\nexport const auth = betterAuth({\n  appName: \"My App\", // Used as the default issuer for TOTP\n  plugins: [\n    twoFactor({\n      issuer: \"My App\", // Optional: override the app name for 2FA specifically\n    }),\n  ],\n});\n```\n\n**Note**: After adding the plugin, run `npx @better-auth/cli migrate` to add the required database fields and tables.\n\n### Client-Side Setup\n\nAdd the client plugin and configure the redirect behavior for 2FA verification:\n\n```ts\nimport { createAuthClient } from \"better-auth/client\";\nimport { twoFactorClient } from \"better-auth/client/plugins\";\n\nexport const authClient = createAuthClient({\n  plugins: [\n    twoFactorClient({\n      onTwoFactorRedirect() {\n        window.location.href = \"/2fa\"; // Redirect to your 2FA verification page\n      },\n    }),\n  ],\n});\n```\n\n## Enabling 2FA for Users\n\nWhen a user enables 2FA, require their password for verification. The enable endpoint returns a TOTP URI for QR code generation and backup codes for account recovery.\n\n```ts\nconst enable2FA = async (password: string) => {\n  const { data, error } = await authClient.twoFactor.enable({\n    password,\n  });\n\n  if (data) {\n    // data.totpURI - Use this to generate a QR code\n    // data.backupCodes - Display these to the user for safekeeping\n  }\n};\n```\n\n**Important**: The `twoFactorEnabled` flag on the user is not set to `true` until the user successfully verifies their first TOTP code. This ensures users have properly configured their authenticator app before 2FA is fully active.\n\n### Skipping Initial Verification\n\nIf you want to enable 2FA immediately without requiring verification, set `skipVerificationOnEnable`:\n\n```ts\ntwoFactor({\n  skipVerificationOnEnable: true, // Not recommended for most use cases\n});\n```\n\n**Note**: This is generally not recommended as it doesn't confirm the user has successfully set up their authenticator app.\n\n## TOTP (Authenticator App)\n\nTOTP generates time-based codes using an authenticator app (Google Authenticator, Authy, etc.). Codes are valid for 30 seconds by default.\n\n### Displaying the QR Code\n\nUse the TOTP URI to generate a QR code for users to scan:\n\n```tsx\nimport QRCode from \"react-qr-code\";\n\nconst TotpSetup = ({ totpURI }: { totpURI: string }) => {\n  return <QRCode value={totpURI} />;\n};\n```\n\n### Verifying TOTP Codes\n\nBetter Auth accepts codes from one period before and one after the current time, accommodating minor clock differences between devices:\n\n```ts\nconst verifyTotp = async (code: string) => {\n  const { data, error } = await authClient.twoFactor.verifyTotp({\n    code,\n    trustDevice: true, // Optional: remember this device for 30 days\n  });\n};\n```\n\n### TOTP Configuration Options\n\n```ts\ntwoFactor({\n  totpOptions: {\n    digits: 6, // 6 or 8 digits (default: 6)\n    period: 30, // Code validity period in seconds (default: 30)\n  },\n});\n```\n\n## OTP (Email/SMS)\n\nOTP sends a one-time code to the user's email or phone. You must implement the `sendOTP` function to deliver codes.\n\n### Configuring OTP Delivery\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { twoFactor } from \"better-auth/plugins\";\nimport { sendEmail } from \"./email\";\n\nexport const auth = betterAuth({\n  plugins: [\n    twoFactor({\n      otpOptions: {\n        sendOTP: async ({ user, otp }, ctx) => {\n          await sendEmail({\n            to: user.email,\n            subject: \"Your verification code\",\n            text: `Your code is: ${otp}`,\n          });\n        },\n        period: 5, // Code validity in minutes (default: 3)\n        digits: 6, // Number of digits (default: 6)\n        allowedAttempts: 5, // Max verification attempts (default: 5)\n      },\n    }),\n  ],\n});\n```\n\n### Sending and Verifying OTP\n\n```ts\n// Request an OTP to be sent\nconst sendOtp = async () => {\n  const { data, error } = await authClient.twoFactor.sendOtp();\n};\n\n// Verify the OTP code\nconst verifyOtp = async (code: string) => {\n  const { data, error } = await authClient.twoFactor.verifyOtp({\n    code,\n    trustDevice: true,\n  });\n};\n```\n\n### OTP Storage Security\n\nConfigure how OTP codes are stored in the database:\n\n```ts\ntwoFactor({\n  otpOptions: {\n    storeOTP: \"encrypted\", // Options: \"plain\", \"encrypted\", \"hashed\"\n  },\n});\n```\n\nFor custom encryption:\n\n```ts\ntwoFactor({\n  otpOptions: {\n    storeOTP: {\n      encrypt: async (token) => myEncrypt(token),\n      decrypt: async (token) => myDecrypt(token),\n    },\n  },\n});\n```\n\n## Backup Codes\n\nBackup codes provide account recovery when users lose access to their authenticator app or phone. They are generated automatically when 2FA is enabled.\n\n### Displaying Backup Codes\n\nAlways show backup codes to users when they enable 2FA:\n\n```tsx\nconst BackupCodes = ({ codes }: { codes: string[] }) => {\n  return (\n    <div>\n      <p>Save these codes in a secure location:</p>\n      <ul>\n        {codes.map((code, i) => (\n          <li key={i}>{code}</li>\n        ))}\n      </ul>\n    </div>\n  );\n};\n```\n\n### Regenerating Backup Codes\n\nWhen users need new codes, regenerate them (this invalidates all previous codes):\n\n```ts\nconst regenerateBackupCodes = async (password: string) => {\n  const { data, error } = await authClient.twoFactor.generateBackupCodes({\n    password,\n  });\n  // data.backupCodes contains the new codes\n};\n```\n\n### Using Backup Codes for Recovery\n\n```ts\nconst verifyBackupCode = async (code: string) => {\n  const { data, error } = await authClient.twoFactor.verifyBackupCode({\n    code,\n    trustDevice: true,\n  });\n};\n```\n\n**Note**: Each backup code can only be used once and is removed from the database after successful verification.\n\n### Backup Code Configuration\n\n```ts\ntwoFactor({\n  backupCodeOptions: {\n    amount: 10, // Number of codes to generate (default: 10)\n    length: 10, // Length of each code (default: 10)\n    storeBackupCodes: \"encrypted\", // Options: \"plain\", \"encrypted\"\n  },\n});\n```\n\n## Handling 2FA During Sign-In\n\nWhen a user with 2FA enabled signs in, the response includes `twoFactorRedirect: true`:\n\n```ts\nconst signIn = async (email: string, password: string) => {\n  const { data, error } = await authClient.signIn.email(\n    {\n      email,\n      password,\n    },\n    {\n      onSuccess(context) {\n        if (context.data.twoFactorRedirect) {\n          // Redirect to 2FA verification page\n          window.location.href = \"/2fa\";\n        }\n      },\n    }\n  );\n};\n```\n\n### Server-Side 2FA Detection\n\nWhen using `auth.api.signInEmail` on the server, check for 2FA redirect:\n\n```ts\nconst response = await auth.api.signInEmail({\n  body: {\n    email: \"user@example.com\",\n    password: \"password\",\n  },\n});\n\nif (\"twoFactorRedirect\" in response) {\n  // Handle 2FA verification\n}\n```\n\n## Trusted Devices\n\nTrusted devices allow users to skip 2FA verification on subsequent sign-ins for a configurable period.\n\n### Enabling Trust on Verification\n\nPass `trustDevice: true` when verifying 2FA:\n\n```ts\nawait authClient.twoFactor.verifyTotp({\n  code: \"123456\",\n  trustDevice: true,\n});\n```\n\n### Configuring Trust Duration\n\n```ts\ntwoFactor({\n  trustDeviceMaxAge: 30 * 24 * 60 * 60, // 30 days in seconds (default)\n});\n```\n\n**Note**: The trust period refreshes on each successful sign-in within the trust window.\n\n## Security Considerations\n\n### Session Management\n\nDuring the 2FA flow:\n\n1. User signs in with credentials\n2. Session cookie is removed (not yet authenticated)\n3. A temporary two-factor cookie is set (default: 10-minute expiration)\n4. User verifies via TOTP, OTP, or backup code\n5. Session cookie is created upon successful verification\n\nConfigure the two-factor cookie expiration:\n\n```ts\ntwoFactor({\n  twoFactorCookieMaxAge: 600, // 10 minutes in seconds (default)\n});\n```\n\n### Rate Limiting\n\nBetter Auth applies built-in rate limiting to all 2FA endpoints (3 requests per 10 seconds). For OTP verification, additional attempt limiting is applied:\n\n```ts\ntwoFactor({\n  otpOptions: {\n    allowedAttempts: 5, // Max attempts per OTP code (default: 5)\n  },\n});\n```\n\n### Encryption at Rest\n\n- TOTP secrets are encrypted using symmetric encryption with your auth secret\n- Backup codes are stored encrypted by default\n- OTP codes can be configured for plain, encrypted, or hashed storage\n\n### Constant-Time Comparison\n\nBetter Auth uses constant-time comparison for OTP verification to prevent timing attacks.\n\n### Credential Account Requirement\n\nTwo-factor authentication can only be enabled for credential (email/password) accounts. For social accounts, it's assumed the provider already handles 2FA.\n\n## Disabling 2FA\n\nAllow users to disable 2FA with password confirmation:\n\n```ts\nconst disable2FA = async (password: string) => {\n  const { data, error } = await authClient.twoFactor.disable({\n    password,\n  });\n};\n```\n\n**Note**: When 2FA is disabled, trusted device records are revoked.\n\n## Complete Configuration Example\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { twoFactor } from \"better-auth/plugins\";\nimport { sendEmail } from \"./email\";\n\nexport const auth = betterAuth({\n  appName: \"My App\",\n  plugins: [\n    twoFactor({\n      // TOTP settings\n      issuer: \"My App\",\n      totpOptions: {\n        digits: 6,\n        period: 30,\n      },\n      // OTP settings\n      otpOptions: {\n        sendOTP: async ({ user, otp }) => {\n          await sendEmail({\n            to: user.email,\n            subject: \"Your verification code\",\n            text: `Your code is: ${otp}`,\n          });\n        },\n        period: 5,\n        allowedAttempts: 5,\n        storeOTP: \"encrypted\",\n      },\n      // Backup code settings\n      backupCodeOptions: {\n        amount: 10,\n        length: 10,\n        storeBackupCodes: \"encrypted\",\n      },\n      // Session settings\n      twoFactorCookieMaxAge: 600, // 10 minutes\n      trustDeviceMaxAge: 30 * 24 * 60 * 60, // 30 days\n    }),\n  ],\n});\n```\n",".agents/skills/devmoji/SKILL.md":"---\nname: devmoji\ndescription: Devmoji commit message emoji conventions. Use when writing git commit messages, formatting commit descriptions, or choosing the right emoji prefix for a commit. Triggers on \"commit message\", \"git commit\", \"commit emoji\", \"devmoji\".\nuser-invocable: false\n---\n\n# Devmoji Commit Conventions\n\nThis project uses emoji prefixes in commit messages following devmoji/gitmoji conventions. Every commit message MUST start with an emoji followed by a space, then the description.\n\n## Format\n\n```\n<emoji> <Short description>\n```\n\n**Examples from this repo:**\n- `🐛 Fix hydration error on dashboard page`\n- `✨ Add support for in-memory rate limiting`\n- `♻️ Move locale sync`\n- `⚡️ Preload dayjs locales`\n- `💄 Disable overscroll`\n\n## Emoji Reference\n\n| Emoji | Code | When to use |\n|-------|------|-------------|\n| ✨ | `:sparkles:` | New feature or capability |\n| 🐛 | `:bug:` | Bug fix |\n| ♻️ | `:recycle:` | Refactoring (no feature or fix) |\n| ⚡️ | `:zap:` | Performance improvement |\n| 💄 | `:lipstick:` | UI/style/cosmetic change |\n| 🔧 | `:wrench:` | Configuration file change |\n| 🔥 | `:fire:` | Remove code or files |\n| 📝 | `:memo:` | Documentation |\n| 🌐 | `:globe_with_meridians:` | Internationalization/localization |\n| 🔒️ | `:lock:` | Security fix or improvement |\n| ⬆️ | `:arrow_up:` | Upgrade dependencies |\n| 🚀 | `:rocket:` | Deploy or release |\n| 🔖 | `:bookmark:` | Version tag / release |\n| 👷 | `:construction_worker:` | CI/CD changes |\n| 🧑‍💻 | `:technologist:` | Developer experience improvement |\n| 🌱 | `:seedling:` | Seed data / database seeding |\n| 🔊 | `:loud_sound:` | Add or update logs |\n| ➕ | `:heavy_plus_sign:` | Add dependency |\n| ➖ | `:heavy_minus_sign:` | Remove dependency |\n| 🚨 | `:rotating_light:` | Fix linting / compiler warnings |\n| ✅ | `:white_check_mark:` | Add or update tests |\n| 🏗️ | `:building_construction:` | Architectural change |\n| 📦 | `:package:` | Build system change |\n| 🗃️ | `:card_file_box:` | Database / migration change |\n| 🩹 | `:adhesive_bandage:` | Simple fix for non-critical issue |\n| ⏪️ | `:rewind:` | Revert changes |\n\n## Rules\n\n1. **Always prefix** the commit message with exactly one primary emoji\n2. **No conventional commit type prefix** — use `🐛 Fix bug` not `fix: 🐛 Fix bug`\n3. **Capitalize** the first word after the emoji\n4. **No period** at the end of the subject line\n5. **Keep it short** — subject line under 72 characters\n6. Pick the **most specific** emoji that applies (e.g. `💄` for a pure CSS change, not `✨`)\n",".agents/skills/organization-best-practices/SKILL.md":"---\nname: organization-best-practices\ndescription: This skill provides guidance and enforcement rules for implementing multi-tenant organizations, teams, and role-based access control using Better Auth's organization plugin.\n---\n\n## Setting Up Organizations\n\nWhen adding organizations to your application, configure the `organization` plugin with appropriate limits and permissions.\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { organization } from \"better-auth/plugins\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n      allowUserToCreateOrganization: true,\n      organizationLimit: 5, // Max orgs per user\n      membershipLimit: 100, // Max members per org\n    }),\n  ],\n});\n```\n\n**Note**: After adding the plugin, run `npx @better-auth/cli migrate` to add the required database tables.\n\n### Client-Side Setup\n\nAdd the client plugin to access organization methods:\n\n```ts\nimport { createAuthClient } from \"better-auth/client\";\nimport { organizationClient } from \"better-auth/client/plugins\";\n\nexport const authClient = createAuthClient({\n  plugins: [organizationClient()],\n});\n```\n\n## Creating Organizations\n\nOrganizations are the top-level entity for grouping users. When created, the creator is automatically assigned the `owner` role.\n\n```ts\nconst createOrg = async () => {\n  const { data, error } = await authClient.organization.create({\n    name: \"My Company\",\n    slug: \"my-company\",\n    logo: \"https://example.com/logo.png\",\n    metadata: { plan: \"pro\" },\n  });\n};\n```\n\n### Controlling Organization Creation\n\nRestrict who can create organizations based on user attributes:\n\n```ts\norganization({\n  allowUserToCreateOrganization: async (user) => {\n    return user.emailVerified === true;\n  },\n  organizationLimit: async (user) => {\n    // Premium users get more organizations\n    return user.plan === \"premium\" ? 20 : 3;\n  },\n});\n```\n\n### Creating Organizations on Behalf of Users\n\nAdministrators can create organizations for other users (server-side only):\n\n```ts\nawait auth.api.createOrganization({\n  body: {\n    name: \"Client Organization\",\n    slug: \"client-org\",\n    userId: \"user-id-who-will-be-owner\", // `userId` is required\n  },\n});\n```\n\n**Note**: The `userId` parameter cannot be used alongside session headers.\n\n\n## Active Organizations\n\nThe active organization is stored in the session and scopes subsequent API calls. Always set an active organization after the user selects one.\n\n```ts\nconst setActive = async (organizationId: string) => {\n  const { data, error } = await authClient.organization.setActive({\n    organizationId,\n  });\n};\n```\n\nMany endpoints use the active organization when `organizationId` is not provided:\n\n```ts\n// These use the active organization automatically\nawait authClient.organization.listMembers();\nawait authClient.organization.listInvitations();\nawait authClient.organization.inviteMember({ email: \"user@example.com\", role: \"member\" });\n```\n\n### Getting Full Organization Data\n\nRetrieve the active organization with all its members, invitations, and teams:\n\n```ts\nconst { data } = await authClient.organization.getFullOrganization();\n// data.organization, data.members, data.invitations, data.teams\n```\n\n## Members\n\nMembers are users who belong to an organization. Each member has a role that determines their permissions.\n\n### Adding Members (Server-Side)\n\nAdd members directly without invitations (useful for admin operations):\n\n```ts\nawait auth.api.addMember({\n  body: {\n    userId: \"user-id\",\n    role: \"member\",\n    organizationId: \"org-id\",\n  },\n});\n```\n\n**Note**: For client-side member additions, use the invitation system instead.\n\n### Assigning Multiple Roles\n\nMembers can have multiple roles for fine-grained permissions:\n\n```ts\nawait auth.api.addMember({\n  body: {\n    userId: \"user-id\",\n    role: [\"admin\", \"moderator\"],\n    organizationId: \"org-id\",\n  },\n});\n```\n\n### Removing Members\n\nRemove members by ID or email:\n\n```ts\nawait authClient.organization.removeMember({\n  memberIdOrEmail: \"user@example.com\",\n});\n```\n\n**Important**: The last owner cannot be removed. Assign the owner role to another member first.\n\n### Updating Member Roles\n\n```ts\nawait authClient.organization.updateMemberRole({\n  memberId: \"member-id\",\n  role: \"admin\",\n});\n```\n\n### Membership Limits\n\nControl the maximum number of members per organization:\n\n```ts\norganization({\n  membershipLimit: async (user, organization) => {\n    if (organization.metadata?.plan === \"enterprise\") {\n      return 1000;\n    }\n    return 50;\n  },\n});\n```\n\n## Invitations\n\nThe invitation system allows admins to invite users via email. Configure email sending to enable invitations.\n\n### Setting Up Invitation Emails\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { organization } from \"better-auth/plugins\";\nimport { sendEmail } from \"./email\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n      sendInvitationEmail: async (data) => {\n        const { email, organization, inviter, invitation } = data;\n\n        await sendEmail({\n          to: email,\n          subject: `Join ${organization.name}`,\n          html: `\n            <p>${inviter.user.name} invited you to join ${organization.name}</p>\n            <a href=\"https://yourapp.com/accept-invite?id=${invitation.id}\">\n              Accept Invitation\n            </a>\n          `,\n        });\n      },\n    }),\n  ],\n});\n```\n\n### Sending Invitations\n\n```ts\nawait authClient.organization.inviteMember({\n  email: \"newuser@example.com\",\n  role: \"member\",\n});\n```\n\n### Creating Shareable Invitation URLs\n\nFor sharing via Slack, SMS, or in-app notifications:\n\n```ts\nconst { data } = await authClient.organization.getInvitationURL({\n  email: \"newuser@example.com\",\n  role: \"member\",\n  callbackURL: \"https://yourapp.com/dashboard\",\n});\n\n// Share data.url via any channel\n```\n\n**Note**: This endpoint does not call `sendInvitationEmail`. Handle delivery yourself.\n\n### Accepting Invitations\n\n```ts\nawait authClient.organization.acceptInvitation({\n  invitationId: \"invitation-id\",\n});\n```\n\n### Invitation Configuration\n\n```ts\norganization({\n  invitationExpiresIn: 60 * 60 * 24 * 7, // 7 days (default: 48 hours)\n  invitationLimit: 100, // Max pending invitations per org\n  cancelPendingInvitationsOnReInvite: true, // Cancel old invites when re-inviting\n});\n```\n\n## Roles & Permissions\n\nThe plugin provides role-based access control (RBAC) with three default roles:\n\n| Role | Description |\n|------|-------------|\n| `owner` | Full access, can delete organization |\n| `admin` | Can manage members, invitations, settings |\n| `member` | Basic access to organization resources |\n\n\n### Checking Permissions\n\n```ts\nconst { data } = await authClient.organization.hasPermission({\n  permission: \"member:write\",\n});\n\nif (data?.hasPermission) {\n  // User can manage members\n}\n```\n\n### Client-Side Permission Checks\n\nFor UI rendering without API calls:\n\n```ts\nconst canManageMembers = authClient.organization.checkRolePermission({\n  role: \"admin\",\n  permissions: [\"member:write\"],\n});\n```\n\n**Note**: For dynamic access control, the client side role permission check will not work. Please use the `hasPermission` endpoint.\n\n## Teams\n\nTeams allow grouping members within an organization.\n\n### Enabling Teams\n\n```ts\nimport { organization } from \"better-auth/plugins\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n        teams: {\n            enabled: true\n        }\n    }),\n  ],\n});\n```\n\n### Creating Teams\n\n```ts\nconst { data } = await authClient.organization.createTeam({\n  name: \"Engineering\",\n});\n```\n\n### Managing Team Members\n\n```ts\n// Add a member to a team (must be org member first)\nawait authClient.organization.addTeamMember({\n  teamId: \"team-id\",\n  userId: \"user-id\",\n});\n\n// Remove from team (stays in org)\nawait authClient.organization.removeTeamMember({\n  teamId: \"team-id\",\n  userId: \"user-id\",\n});\n```\n\n### Active Teams\n\nSimilar to active organizations, set an active team for the session:\n\n```ts\nawait authClient.organization.setActiveTeam({\n  teamId: \"team-id\",\n});\n```\n\n### Team Limits\n\n```ts\norganization({\n  teams: {\n      maximumTeams: 20, // Max teams per org\n      maximumMembersPerTeam: 50, // Max members per team\n      allowRemovingAllTeams: false, // Prevent removing last team\n  }\n});\n```\n\n## Dynamic Access Control\n\nFor applications needing custom roles per organization at runtime, enable dynamic access control.\n\n### Enabling Dynamic Access Control\n\n```ts\nimport { organization } from \"better-auth/plugins\";\nimport { dynamicAccessControl } from \"@better-auth/organization/addons\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n        dynamicAccessControl: {\n            enabled: true\n        }\n    }),\n  ],\n});\n```\n\n### Creating Custom Roles\n\n```ts\nawait authClient.organization.createRole({\n  role: \"moderator\",\n  permission: {\n    member: [\"read\"],\n    invitation: [\"read\"],\n  },\n});\n```\n\n### Updating and Deleting Roles\n\n```ts\n// Update role permissions\nawait authClient.organization.updateRole({\n  roleId: \"role-id\",\n  permission: {\n    member: [\"read\", \"write\"],\n  },\n});\n\n// Delete a custom role\nawait authClient.organization.deleteRole({\n  roleId: \"role-id\",\n});\n```\n\n**Note**: Pre-defined roles (owner, admin, member) cannot be deleted. Roles assigned to members cannot be deleted until members are reassigned.\n\n## Lifecycle Hooks\n\nExecute custom logic at various points in the organization lifecycle:\n\n```ts\norganization({\n  hooks: {\n    organization: {\n      beforeCreate: async ({ data, user }) => {\n        // Validate or modify data before creation\n        return {\n          data: {\n            ...data,\n            metadata: { ...data.metadata, createdBy: user.id },\n          },\n        };\n      },\n      afterCreate: async ({ organization, member }) => {\n        // Post-creation logic (e.g., send welcome email, create default resources)\n        await createDefaultResources(organization.id);\n      },\n      beforeDelete: async ({ organization }) => {\n        // Cleanup before deletion\n        await archiveOrganizationData(organization.id);\n      },\n    },\n    member: {\n      afterCreate: async ({ member, organization }) => {\n        await notifyAdmins(organization.id, `New member joined`);\n      },\n    },\n    invitation: {\n      afterCreate: async ({ invitation, organization, inviter }) => {\n        await logInvitation(invitation);\n      },\n    },\n  },\n});\n```\n\n## Schema Customization\n\nCustomize table names, field names, and add additional fields:\n\n```ts\norganization({\n  schema: {\n    organization: {\n      modelName: \"workspace\", // Rename table\n      fields: {\n        name: \"workspaceName\", // Rename fields\n      },\n      additionalFields: {\n        billingId: {\n          type: \"string\",\n          required: false,\n        },\n      },\n    },\n    member: {\n      additionalFields: {\n        department: {\n          type: \"string\",\n          required: false,\n        },\n        title: {\n          type: \"string\",\n          required: false,\n        },\n      },\n    },\n  },\n});\n```\n\n## Security Considerations\n\n### Owner Protection\n\n- The last owner cannot be removed from an organization\n- The last owner cannot leave the organization\n- The owner role cannot be removed from the last owner\n\nAlways ensure ownership transfer before removing the current owner:\n\n```ts\n// Transfer ownership first\nawait authClient.organization.updateMemberRole({\n  memberId: \"new-owner-member-id\",\n  role: \"owner\",\n});\n\n// Then the previous owner can be demoted or removed\n```\n\n### Organization Deletion\n\nDeleting an organization removes all associated data (members, invitations, teams). Prevent accidental deletion:\n\n```ts\norganization({\n  disableOrganizationDeletion: true, // Disable via config\n});\n```\n\nOr implement soft delete via hooks:\n\n```ts\norganization({\n  hooks: {\n    organization: {\n      beforeDelete: async ({ organization }) => {\n        // Archive instead of delete\n        await archiveOrganization(organization.id);\n        throw new Error(\"Organization archived, not deleted\");\n      },\n    },\n  },\n});\n```\n\n### Invitation Security\n\n- Invitations expire after 48 hours by default\n- Only the invited email address can accept an invitation\n- Pending invitations can be cancelled by organization admins\n\n## Complete Configuration Example\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { organization } from \"better-auth/plugins\";\nimport { sendEmail } from \"./email\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n      // Organization limits\n      allowUserToCreateOrganization: true,\n      organizationLimit: 10,\n      membershipLimit: 100,\n      creatorRole: \"owner\",\n\n      // Slugs\n      defaultOrganizationIdField: \"slug\",\n\n      // Invitations\n      invitationExpiresIn: 60 * 60 * 24 * 7, // 7 days\n      invitationLimit: 50,\n      sendInvitationEmail: async (data) => {\n        await sendEmail({\n          to: data.email,\n          subject: `Join ${data.organization.name}`,\n          html: `<a href=\"https://app.com/invite/${data.invitation.id}\">Accept</a>`,\n        });\n      },\n\n      // Hooks\n      hooks: {\n        organization: {\n          afterCreate: async ({ organization }) => {\n            console.log(`Organization ${organization.name} created`);\n          },\n        },\n      },\n    }),\n  ],\n});\n```\n",".agents/skills/email-and-password-best-practices/SKILL.md":"---\nname: email-and-password-best-practices\ndescription: This skill provides guidance and enforcement rules for implementing secure email and password authentication using Better Auth.\n---\n\n## Email Verification Setup\n\nWhen enabling email/password authentication, configure `emailVerification.sendVerificationEmail` to verify user email addresses. This helps prevent fake sign-ups and ensures users have access to the email they registered with.\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { sendEmail } from \"./email\"; // your email sending function\n\nexport const auth = betterAuth({\n  emailVerification: {\n    sendVerificationEmail: async ({ user, url, token }, request) => {\n      await sendEmail({\n        to: user.email,\n        subject: \"Verify your email address\",\n        text: `Click the link to verify your email: ${url}`,\n      });\n    },\n  },\n});\n```\n\n**Note**: The `url` parameter contains the full verification link. The `token` is available if you need to build a custom verification URL.\n\n### Requiring Email Verification\n\nFor stricter security, enable `emailAndPassword.requireEmailVerification` to block sign-in until the user verifies their email. When enabled, unverified users will receive a new verification email on each sign-in attempt.\n\n```ts\nexport const auth = betterAuth({\n  emailAndPassword: {\n    requireEmailVerification: true,\n  },\n});\n```\n\n**Note**: This requires `sendVerificationEmail` to be configured and only applies to email/password sign-ins.\n\n## Client side validation\n\nWhile Better Auth validates inputs server-side, implementing client-side validation is still recommended for two key reasons:\n\n1. **Improved UX**: Users receive immediate feedback when inputs don't meet requirements, rather than waiting for a server round-trip.\n2. **Reduced server load**: Invalid requests are caught early, minimizing unnecessary network traffic to your auth server.\n\n## Callback URLs\n\nAlways use absolute URLs (including the origin) for callback URLs in sign-up and sign-in requests. This prevents Better Auth from needing to infer the origin, which can cause issues when your backend and frontend are on different domains.\n\n```ts\nconst { data, error } = await authClient.signUp.email({\n  callbackURL: \"https://example.com/callback\", // absolute URL with origin\n});\n```\n\n## Password Reset Flows\n\nPassword reset flows are essential to any email/password system, we recommend setting this up.\n\nTo allow users to reset a password first you need to provide `sendResetPassword` function to the email and password authenticator.\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { sendEmail } from \"./email\"; // your email sending function\n\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    // Custom email sending function to send reset-password email\n    sendResetPassword: async ({ user, url, token }, request) => {\n      void sendEmail({\n        to: user.email,\n        subject: \"Reset your password\",\n        text: `Click the link to reset your password: ${url}`,\n      });\n    },\n    // Optional event hook\n    onPasswordReset: async ({ user }, request) => {\n      // your logic here\n      console.log(`Password for user ${user.email} has been reset.`);\n    },\n  },\n});\n```\n\n### Security considerations\n\nBetter Auth implements several security measures in the password reset flow:\n\n#### Timing attack prevention\n\n- **Background email sending**: Better Auth uses `runInBackgroundOrAwait` internally to send reset emails without blocking the response. This prevents attackers from measuring response times to determine if an email exists.\n- **Dummy operations on invalid requests**: When a user is not found, Better Auth still performs token generation and a database lookup (with a dummy value) to maintain consistent response times.\n- **Constant response message**: The API always returns `\"If this email exists in our system, check your email for the reset link\"` regardless of whether the user exists.\n\nOn serverless platforms, configure a background task handler to ensure emails are sent reliably:\n\n```ts\nexport const auth = betterAuth({\n  advanced: {\n    backgroundTasks: {\n      handler: (promise) => {\n        // Use platform-specific methods like waitUntil\n        waitUntil(promise);\n      },\n    },\n  },\n});\n```\n\n#### Token security\n\n- **Cryptographically random tokens**: Reset tokens are generated using `generateId(24)`, producing a 24-character alphanumeric string (a-z, A-Z, 0-9) with high entropy.\n- **Token expiration**: Tokens expire after **1 hour** by default. Configure with `resetPasswordTokenExpiresIn` (in seconds):\n\n```ts\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    resetPasswordTokenExpiresIn: 60 * 30, // 30 minutes\n  },\n});\n```\n\n- **Single-use tokens**: Tokens are deleted immediately after successful password reset, preventing reuse.\n\n#### Session revocation\n\nEnable `revokeSessionsOnPasswordReset` to invalidate all existing sessions when a password is reset. This ensures that if an attacker has an active session, it will be terminated:\n\n```ts\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    revokeSessionsOnPasswordReset: true,\n  },\n});\n```\n\n#### Redirect URL validation\n\nThe `redirectTo` parameter is validated against your `trustedOrigins` configuration to prevent open redirect attacks. Malicious redirect URLs will be rejected with a 403 error.\n\n#### Password requirements\n\nDuring password reset, the new password must meet length requirements:\n- **Minimum**: 8 characters (default), configurable via `minPasswordLength`\n- **Maximum**: 128 characters (default), configurable via `maxPasswordLength`\n\n```ts\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    minPasswordLength: 12,\n    maxPasswordLength: 256,\n  },\n});\n```\n\n### Sending the password reset\n\nOnce the password reset configurations are set-up, you can now call the `requestPasswordReset` function to send reset password link to user. If the user exists, it will trigger the `sendResetPassword` function you provided in the auth config.\n\n```ts\nconst data = await auth.api.requestPasswordReset({\n  body: {\n    email: \"john.doe@example.com\", // required\n    redirectTo: \"https://example.com/reset-password\",\n  },\n});\n```\n\nOr authClient:\n\n```ts\nconst { data, error } = await authClient.requestPasswordReset({\n  email: \"john.doe@example.com\", // required\n  redirectTo: \"https://example.com/reset-password\",\n});\n```\n\n**Note**: While the `email` is required, we also recommend configuring the `redirectTo` for a smoother user experience.\n\n## Password Hashing\n\nBetter Auth uses `scrypt` by default for password hashing. This is a solid choice because:\n\n- It's designed to be slow and memory-intensive, making brute-force attacks costly\n- It's natively supported by Node.js (no external dependencies)\n- OWASP recommends it when Argon2id isn't available\n\n### Custom Hashing Algorithm\n\nTo use a different algorithm (e.g., Argon2id), provide custom `hash` and `verify` functions in the `emailAndPassword.password` configuration:\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { hash, verify, type Options } from \"@node-rs/argon2\";\n\nconst argon2Options: Options = {\n  memoryCost: 65536, // 64 MiB\n  timeCost: 3, // 3 iterations\n  parallelism: 4, // 4 parallel lanes\n  outputLen: 32, // 32 byte output\n  algorithm: 2, // Argon2id variant\n};\n\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    password: {\n      hash: (password) => hash(password, argon2Options),\n      verify: ({ password, hash: storedHash }) =>\n        verify(storedHash, password, argon2Options),\n    },\n  },\n});\n```\n\n**Note**: If you switch hashing algorithms on an existing system, users with passwords hashed using the old algorithm won't be able to sign in. Plan a migration strategy if needed.\n",".agents/skills/logging/SKILL.md":"---\nname: logging\ndescription: Wide events logging pattern and conventions for this project. Use when writing logging code, adding observability, or implementing request tracing.\nuser-invocable: false\n---\n\n# Logging Guidelines\n\nThis project follows the **Wide Events** pattern. Logs are optimized for querying, not writing.\n\n## Core Principle\n\nEmit **one comprehensive event per request per service** containing all contextual information. Do not scatter log statements throughout your code.\n\n**Wrong mental model:** Log what your code is doing\n**Correct mental model:** Log what happened to this request\n\n## Wide Event Structure\n\nEach event should include:\n\n```typescript\n{\n  // Request metadata\n  requestId: string,\n  traceId: string,\n  timestamp: Date,\n  service: string,\n\n  // HTTP details\n  method: string,\n  path: string,\n  statusCode: number,\n  durationMs: number,\n\n  // User context\n  userId?: string,\n  subscriptionTier?: string,\n\n  // Business context\n  pollId?: string,\n  spaceId?: string,\n  featureFlags: Record<string, boolean>,\n\n  // Error details (when applicable)\n  errorType?: string,\n  errorCode?: string,\n  errorMessage?: string,\n  isRetriable?: boolean,\n\n  // Performance metrics\n  dbQueryCount?: number,\n  dbQueryDurationMs?: number\n}\n```\n\n## Implementation Pattern\n\nUse middleware to build the event throughout the request lifecycle:\n\n1. Initialize event at request start with request/service metadata\n2. Enrich with user context after authentication\n3. Add business context as processing occurs\n4. Emit single event in finally block after request completes\n\n## Anti-Patterns\n\n**Never do these:**\n\n- Scattered `console.log` statements throughout code\n- String-based log messages without structured data\n- Low-context logs with only timestamp, level, and message\n- Multiple log lines for a single request\n\n**Example of what NOT to do:**\n\n```typescript\n// BAD - scattered, low-context logs\nconsole.log(\"Starting request\");\nconsole.log(\"User authenticated\");\nconsole.log(\"Fetching poll\");\nconsole.log(\"Request complete\");\n```\n\n**Example of correct approach:**\n\n```typescript\n// GOOD - single wide event with full context\nlogger.info({\n  requestId: ctx.requestId,\n  userId: ctx.user?.id,\n  pollId: params.pollId,\n  method: \"GET\",\n  path: \"/api/polls/:id\",\n  statusCode: 200,\n  durationMs: 45,\n  dbQueryCount: 2,\n  dbQueryDurationMs: 12\n});\n```\n\n## Tail Sampling\n\nWhen implementing log sampling, make decisions **after** request completion:\n\n**Always capture 100%:**\n- All errors (5xx status, exceptions)\n- Requests exceeding p99 latency\n- Pro/enterprise user requests\n- Requests with feature flags under rollout\n\n**Sample remaining traffic:** 1-5% of successful, fast requests\n\n## High-Cardinality Data\n\nInclude high-cardinality fields (userId, pollId, requestId). Modern observability tools handle this efficiently. Aim for 50+ fields per event. Include:\n\n- All IDs involved in the request\n- All feature flags evaluated\n- All external service calls made\n- Timing breakdown for significant operations\n- User tier and account metadata\n",".agents/skills/create-auth-skill/SKILL.md":"---\nname: create-auth-skill\ndescription: Skill for creating auth layers in TypeScript/JavaScript apps using Better Auth.\n---\n\n# Create Auth Skill\n\nGuide for adding authentication to TypeScript/JavaScript applications using Better Auth.\n\n**For code examples and syntax, see [better-auth.com/docs](https://better-auth.com/docs).**\n\n---\n\n## Phase 1: Planning (REQUIRED before implementation)\n\nBefore writing any code, gather requirements by scanning the project and asking the user structured questions. This ensures the implementation matches their needs.\n\n### Step 1: Scan the project\n\nAnalyze the codebase to auto-detect:\n- **Framework** — Look for `next.config`, `svelte.config`, `nuxt.config`, `astro.config`, `vite.config`, or Express/Hono entry files.\n- **Database/ORM** — Look for `prisma/schema.prisma`, `drizzle.config`, `package.json` deps (`pg`, `mysql2`, `better-sqlite3`, `mongoose`, `mongodb`).\n- **Existing auth** — Look for existing auth libraries (`next-auth`, `lucia`, `clerk`, `supabase/auth`, `firebase/auth`) in `package.json` or imports.\n- **Package manager** — Check for `pnpm-lock.yaml`, `yarn.lock`, `bun.lockb`, or `package-lock.json`.\n\nUse what you find to pre-fill defaults and skip questions you can already answer.\n\n### Step 2: Ask planning questions\n\nUse the `AskQuestion` tool to ask the user **all applicable questions in a single call**. Skip any question you already have a confident answer for from the scan. Group them under a title like \"Auth Setup Planning\".\n\n**Questions to ask:**\n\n1. **Project type** (skip if detected)\n   - Prompt: \"What type of project is this?\"\n   - Options: New project from scratch | Adding auth to existing project | Migrating from another auth library\n\n2. **Framework** (skip if detected)\n   - Prompt: \"Which framework are you using?\"\n   - Options: Next.js (App Router) | Next.js (Pages Router) | SvelteKit | Nuxt | Astro | Express | Hono | SolidStart | Other\n\n3. **Database & ORM** (skip if detected)\n   - Prompt: \"Which database setup will you use?\"\n   - Options: PostgreSQL (Prisma) | PostgreSQL (Drizzle) | PostgreSQL (pg driver) | MySQL (Prisma) | MySQL (Drizzle) | MySQL (mysql2 driver) | SQLite (Prisma) | SQLite (Drizzle) | SQLite (better-sqlite3 driver) | MongoDB (Mongoose) | MongoDB (native driver)\n\n4. **Authentication methods** (always ask, allow multiple)\n   - Prompt: \"Which sign-in methods do you need?\"\n   - Options: Email & password | Social OAuth (Google, GitHub, etc.) | Magic link (passwordless email) | Passkey (WebAuthn) | Phone number\n   - `allow_multiple: true`\n\n5. **Social providers** (only if they selected Social OAuth above — ask in a follow-up call)\n   - Prompt: \"Which social providers do you need?\"\n   - Options: Google | GitHub | Apple | Microsoft | Discord | Twitter/X\n   - `allow_multiple: true`\n\n6. **Email verification** (only if Email & password was selected above — ask in a follow-up call)\n   - Prompt: \"Do you want to require email verification?\"\n   - Options: Yes | No\n\n7. **Email provider** (only if email verification is Yes, or if Password reset is selected in features — ask in a follow-up call)\n   - Prompt: \"How do you want to send emails?\"\n   - Options: Resend | Mock it for now (console.log)\n\n8. **Features & plugins** (always ask, allow multiple)\n   - Prompt: \"Which additional features do you need?\"\n   - Options: Two-factor authentication (2FA) | Organizations / teams | Admin dashboard | API bearer tokens | Password reset | None of these\n   - `allow_multiple: true`\n\n9. **Auth pages** (always ask, allow multiple — pre-select based on earlier answers)\n   - Prompt: \"Which auth pages do you need?\"\n   - Options vary based on previous answers:\n     - Always available: Sign in | Sign up\n     - If Email & password selected: Forgot password | Reset password\n     - If email verification enabled: Email verification\n   - `allow_multiple: true`\n\n10. **Auth UI style** (always ask)\n   - Prompt: \"What style do you want for the auth pages? Pick one or describe your own.\"\n   - Options: Minimal & clean | Centered card with background | Split layout (form + hero image) | Floating / glassmorphism | Other (I'll describe)\n\n### Step 3: Summarize the plan\n\nAfter collecting answers, present a concise implementation plan as a markdown checklist. Example:\n\n```\n## Auth Implementation Plan\n\n- **Framework:** Next.js (App Router)\n- **Database:** PostgreSQL via Prisma\n- **Auth methods:** Email/password, Google OAuth, GitHub OAuth\n- **Plugins:** 2FA, Organizations, Email verification\n- **UI:** Custom forms\n\n### Steps\n1. Install `better-auth` and `@better-auth/cli`\n2. Create `lib/auth.ts` with server config\n3. Create `lib/auth-client.ts` with React client\n4. Set up route handler at `app/api/auth/[...all]/route.ts`\n5. Configure Prisma adapter and generate schema\n6. Add Google & GitHub OAuth providers\n7. Enable `twoFactor` and `organization` plugins\n8. Set up email verification handler\n9. Run migrations\n10. Create sign-in / sign-up pages\n```\n\nAsk the user to confirm the plan before proceeding to Phase 2.\n\n---\n\n## Phase 2: Implementation\n\nOnly proceed here after the user confirms the plan from Phase 1.\n\nFollow the decision tree below, guided by the answers collected above.\n\n```\nIs this a new/empty project?\n├─ YES → New project setup\n│   1. Install better-auth (+ scoped packages per plan)\n│   2. Create auth.ts with all planned config\n│   3. Create auth-client.ts with framework client\n│   4. Set up route handler\n│   5. Set up environment variables\n│   6. Run CLI migrate/generate\n│   7. Add plugins from plan\n│   8. Create auth UI pages\n│\n├─ MIGRATING → Migration from existing auth\n│   1. Audit current auth for gaps\n│   2. Plan incremental migration\n│   3. Install better-auth alongside existing auth\n│   4. Migrate routes, then session logic, then UI\n│   5. Remove old auth library\n│   6. See migration guides in docs\n│\n└─ ADDING → Add auth to existing project\n    1. Analyze project structure\n    2. Install better-auth\n    3. Create auth config matching plan\n    4. Add route handler\n    5. Run schema migrations\n    6. Integrate into existing pages\n    7. Add planned plugins and features\n```\n\nAt the end of implementation, guide users thoroughly on remaining next steps (e.g., setting up OAuth app credentials, deploying env vars, testing flows).\n\n---\n\n## Installation\n\n**Core:** `npm install better-auth`\n\n**Scoped packages (as needed):**\n| Package | Use case |\n|---------|----------|\n| `@better-auth/passkey` | WebAuthn/Passkey auth |\n| `@better-auth/sso` | SAML/OIDC enterprise SSO |\n| `@better-auth/stripe` | Stripe payments |\n| `@better-auth/scim` | SCIM user provisioning |\n| `@better-auth/expo` | React Native/Expo |\n\n---\n\n## Environment Variables\n\n```env\nBETTER_AUTH_SECRET=<32+ chars, generate with: openssl rand -base64 32>\nBETTER_AUTH_URL=http://localhost:3000\nDATABASE_URL=<your database connection string>\n```\n\nAdd OAuth secrets as needed: `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`, `GOOGLE_CLIENT_ID`, etc.\n\n---\n\n## Server Config (auth.ts)\n\n**Location:** `lib/auth.ts` or `src/lib/auth.ts`\n\n**Minimal config needs:**\n- `database` - Connection or adapter\n- `emailAndPassword: { enabled: true }` - For email/password auth\n\n**Standard config adds:**\n- `socialProviders` - OAuth providers (google, github, etc.)\n- `emailVerification.sendVerificationEmail` - Email verification handler\n- `emailAndPassword.sendResetPassword` - Password reset handler\n\n**Full config adds:**\n- `plugins` - Array of feature plugins\n- `session` - Expiry, cookie cache settings\n- `account.accountLinking` - Multi-provider linking\n- `rateLimit` - Rate limiting config\n\n**Export types:** `export type Session = typeof auth.$Infer.Session`\n\n---\n\n## Client Config (auth-client.ts)\n\n**Import by framework:**\n| Framework | Import |\n|-----------|--------|\n| React/Next.js | `better-auth/react` |\n| Vue | `better-auth/vue` |\n| Svelte | `better-auth/svelte` |\n| Solid | `better-auth/solid` |\n| Vanilla JS | `better-auth/client` |\n\n**Client plugins** go in `createAuthClient({ plugins: [...] })`.\n\n**Common exports:** `signIn`, `signUp`, `signOut`, `useSession`, `getSession`\n\n---\n\n## Route Handler Setup\n\n| Framework | File | Handler |\n|-----------|------|---------|\n| Next.js App Router | `app/api/auth/[...all]/route.ts` | `toNextJsHandler(auth)` → export `{ GET, POST }` |\n| Next.js Pages | `pages/api/auth/[...all].ts` | `toNextJsHandler(auth)` → default export |\n| Express | Any file | `app.all(\"/api/auth/*\", toNodeHandler(auth))` |\n| SvelteKit | `src/hooks.server.ts` | `svelteKitHandler(auth)` |\n| SolidStart | Route file | `solidStartHandler(auth)` |\n| Hono | Route file | `auth.handler(c.req.raw)` |\n\n**Next.js Server Components:** Add `nextCookies()` plugin to auth config.\n\n---\n\n## Database Migrations\n\n| Adapter | Command |\n|---------|---------|\n| Built-in Kysely | `npx @better-auth/cli@latest migrate` (applies directly) |\n| Prisma | `npx @better-auth/cli@latest generate --output prisma/schema.prisma` then `npx prisma migrate dev` |\n| Drizzle | `npx @better-auth/cli@latest generate --output src/db/auth-schema.ts` then `npx drizzle-kit push` |\n\n**Re-run after adding plugins.**\n\n---\n\n## Database Adapters\n\n| Database | Setup |\n|----------|-------|\n| SQLite | Pass `better-sqlite3` or `bun:sqlite` instance directly |\n| PostgreSQL | Pass `pg.Pool` instance directly |\n| MySQL | Pass `mysql2` pool directly |\n| Prisma | `prismaAdapter(prisma, { provider: \"postgresql\" })` from `better-auth/adapters/prisma` |\n| Drizzle | `drizzleAdapter(db, { provider: \"pg\" })` from `better-auth/adapters/drizzle` |\n| MongoDB | `mongodbAdapter(db)` from `better-auth/adapters/mongodb` |\n\n---\n\n## Common Plugins\n\n| Plugin | Server Import | Client Import | Purpose |\n|--------|---------------|---------------|---------|\n| `twoFactor` | `better-auth/plugins` | `twoFactorClient` | 2FA with TOTP/OTP |\n| `organization` | `better-auth/plugins` | `organizationClient` | Teams/orgs |\n| `admin` | `better-auth/plugins` | `adminClient` | User management |\n| `bearer` | `better-auth/plugins` | - | API token auth |\n| `openAPI` | `better-auth/plugins` | - | API docs |\n| `passkey` | `@better-auth/passkey` | `passkeyClient` | WebAuthn |\n| `sso` | `@better-auth/sso` | - | Enterprise SSO |\n\n**Plugin pattern:** Server plugin + client plugin + run migrations.\n\n---\n\n## Auth UI Implementation\n\n**Sign in flow:**\n1. `signIn.email({ email, password })` or `signIn.social({ provider, callbackURL })`\n2. Handle `error` in response\n3. Redirect on success\n\n**Session check (client):** `useSession()` hook returns `{ data: session, isPending }`\n\n**Session check (server):** `auth.api.getSession({ headers: await headers() })`\n\n**Protected routes:** Check session, redirect to `/sign-in` if null.\n\n---\n\n## Security Checklist\n\n- [ ] `BETTER_AUTH_SECRET` set (32+ chars)\n- [ ] `advanced.useSecureCookies: true` in production\n- [ ] `trustedOrigins` configured\n- [ ] Rate limits enabled\n- [ ] Email verification enabled\n- [ ] Password reset implemented\n- [ ] 2FA for sensitive apps\n- [ ] CSRF protection NOT disabled\n- [ ] `account.accountLinking` reviewed\n\n---\n\n## Troubleshooting\n\n| Issue | Fix |\n|-------|-----|\n| \"Secret not set\" | Add `BETTER_AUTH_SECRET` env var |\n| \"Invalid Origin\" | Add domain to `trustedOrigins` |\n| Cookies not setting | Check `baseURL` matches domain; enable secure cookies in prod |\n| OAuth callback errors | Verify redirect URIs in provider dashboard |\n| Type errors after adding plugin | Re-run CLI generate/migrate |\n\n---\n\n## Resources\n\n- [Docs](https://better-auth.com/docs)\n- [Examples](https://github.com/better-auth/examples)\n- [Plugins](https://better-auth.com/docs/concepts/plugins)\n- [CLI](https://better-auth.com/docs/concepts/cli)\n- [Migration Guides](https://better-auth.com/docs/guides)\n",".agents/skills/better-auth-security-best-practices/SKILL.MD":"# better-auth-security-best-practices\n\nPath: `.agents/skills/better-auth-security-best-practices/SKILL.MD`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/better-auth-security-best-practices/SKILL.MD)",".agents/skills/better-auth-best-practices/SKILL.md":"# better-auth-best-practices\n\nPath: `.agents/skills/better-auth-best-practices/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/better-auth-best-practices/SKILL.md)",".cursorrules":"# Cursor IDE Native Rules\n\nPath: `.cursorrules`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.cursorrules)","CLAUDE.md":"# Claude Agent Guidelines & System Prompt\n\nPath: `CLAUDE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/CLAUDE.md)","packages/utils/biome.json":"# Utils Skill\n\nPath: `packages/utils/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/utils/biome.json)","packages/utils/package.json":"# Utils Skill\n\nPath: `packages/utils/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/utils/package.json)","packages/utils/tsconfig.json":"# Utils Skill\n\nPath: `packages/utils/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/utils/tsconfig.json)","packages/ui/biome.json":"# Ui Skill\n\nPath: `packages/ui/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/ui/biome.json)","packages/ui/components.json":"# Ui Skill\n\nPath: `packages/ui/components.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/ui/components.json)","packages/ui/package.json":"# Ui Skill\n\nPath: `packages/ui/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/ui/package.json)","packages/ui/tsconfig.json":"# Ui Skill\n\nPath: `packages/ui/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/ui/tsconfig.json)","packages/tsconfig/next.json":"# Tsconfig Skill\n\nPath: `packages/tsconfig/next.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tsconfig/next.json)","packages/tsconfig/node.json":"# Tsconfig Skill\n\nPath: `packages/tsconfig/node.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tsconfig/node.json)","packages/tsconfig/package.json":"# Tsconfig Skill\n\nPath: `packages/tsconfig/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tsconfig/package.json)","packages/tsconfig/react.json":"# Tsconfig Skill\n\nPath: `packages/tsconfig/react.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tsconfig/react.json)","packages/test-helpers/package.json":"# Test-helpers Skill\n\nPath: `packages/test-helpers/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/test-helpers/package.json)","packages/test-helpers/tsconfig.json":"# Test-helpers Skill\n\nPath: `packages/test-helpers/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/test-helpers/tsconfig.json)","packages/tailwind-config/package.json":"# Tailwind-config Skill\n\nPath: `packages/tailwind-config/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tailwind-config/package.json)","packages/screenshots/package.json":"# Screenshots Skill\n\nPath: `packages/screenshots/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/screenshots/package.json)","packages/screenshots/tsconfig.json":"# Screenshots Skill\n\nPath: `packages/screenshots/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/screenshots/tsconfig.json)","packages/posthog/biome.json":"# Posthog Skill\n\nPath: `packages/posthog/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/posthog/biome.json)","packages/posthog/package.json":"# Posthog Skill\n\nPath: `packages/posthog/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/posthog/package.json)","packages/posthog/tsconfig.json":"# Posthog Skill\n\nPath: `packages/posthog/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/posthog/tsconfig.json)","packages/logger/package.json":"# Logger Skill\n\nPath: `packages/logger/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/logger/package.json)","packages/logger/tsconfig.json":"# Logger Skill\n\nPath: `packages/logger/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/logger/tsconfig.json)","packages/languages/biome.json":"# Languages Skill\n\nPath: `packages/languages/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/languages/biome.json)","packages/languages/package.json":"# Languages Skill\n\nPath: `packages/languages/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/languages/package.json)","packages/languages/tsconfig.json":"# Languages Skill\n\nPath: `packages/languages/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/languages/tsconfig.json)","packages/emails/biome.json":"# Emails Skill\n\nPath: `packages/emails/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/biome.json)","packages/emails/package.json":"# Emails Skill\n\nPath: `packages/emails/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/package.json)","packages/emails/tsconfig.json":"# Emails Skill\n\nPath: `packages/emails/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/tsconfig.json)","packages/dayjs/biome.json":"# Dayjs Skill\n\nPath: `packages/dayjs/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/dayjs/biome.json)","packages/dayjs/package.json":"# Dayjs Skill\n\nPath: `packages/dayjs/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/dayjs/package.json)","packages/database/biome.json":"# Database Skill\n\nPath: `packages/database/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/database/biome.json)","packages/database/package.json":"# Database Skill\n\nPath: `packages/database/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/database/package.json)","packages/database/tsconfig.json":"# Database Skill\n\nPath: `packages/database/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/database/tsconfig.json)","packages/billing/biome.json":"# Billing Skill\n\nPath: `packages/billing/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/billing/biome.json)","packages/billing/package.json":"# Billing Skill\n\nPath: `packages/billing/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/billing/package.json)","packages/billing/tsconfig.json":"# Billing Skill\n\nPath: `packages/billing/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/billing/tsconfig.json)",".agents/skills/prisma-upgrade-v7/references/accelerate-users.md":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/accelerate-users.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/accelerate-users.md)",".agents/skills/prisma-upgrade-v7/references/driver-adapters.md":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/driver-adapters.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/driver-adapters.md)",".agents/skills/prisma-upgrade-v7/references/env-variables.md":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/env-variables.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/env-variables.md)",".agents/skills/prisma-upgrade-v7/references/esm-support.md":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/esm-support.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/esm-support.md)",".agents/skills/prisma-upgrade-v7/references/prisma-config.md":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/prisma-config.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/prisma-config.md)",".agents/skills/prisma-upgrade-v7/references/removed-features.md":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/removed-features.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/removed-features.md)",".agents/skills/prisma-upgrade-v7/references/schema-changes.md":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/schema-changes.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/schema-changes.md)",".agents/skills/prisma-postgres/references/console-and-connections.md":"# References Skill\n\nPath: `.agents/skills/prisma-postgres/references/console-and-connections.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-postgres/references/console-and-connections.md)",".agents/skills/prisma-postgres/references/create-db-cli.md":"# References Skill\n\nPath: `.agents/skills/prisma-postgres/references/create-db-cli.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-postgres/references/create-db-cli.md)",".agents/skills/prisma-postgres/references/management-api.md":"# References Skill\n\nPath: `.agents/skills/prisma-postgres/references/management-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-postgres/references/management-api.md)",".agents/skills/prisma-postgres/references/management-api-sdk.md":"# References Skill\n\nPath: `.agents/skills/prisma-postgres/references/management-api-sdk.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-postgres/references/management-api-sdk.md)",".agents/skills/prisma-database-setup/references/cockroachdb.md":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/cockroachdb.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/cockroachdb.md)",".agents/skills/prisma-database-setup/references/mongodb.md":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/mongodb.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/mongodb.md)",".agents/skills/prisma-database-setup/references/mysql.md":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/mysql.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/mysql.md)",".agents/skills/prisma-database-setup/references/postgresql.md":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/postgresql.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/postgresql.md)",".agents/skills/prisma-database-setup/references/prisma-client-setup.md":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/prisma-client-setup.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/prisma-client-setup.md)",".agents/skills/prisma-database-setup/references/prisma-postgres.md":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/prisma-postgres.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/prisma-postgres.md)",".agents/skills/prisma-database-setup/references/sqlite.md":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/sqlite.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/sqlite.md)",".agents/skills/prisma-database-setup/references/sqlserver.md":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/sqlserver.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/sqlserver.md)",".agents/skills/prisma-client-api/references/client-methods.md":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/client-methods.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/client-methods.md)",".agents/skills/prisma-client-api/references/constructor.md":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/constructor.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/constructor.md)",".agents/skills/prisma-client-api/references/filters.md":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/filters.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/filters.md)",".agents/skills/prisma-client-api/references/model-queries.md":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/model-queries.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/model-queries.md)",".agents/skills/prisma-client-api/references/query-options.md":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/query-options.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/query-options.md)",".agents/skills/prisma-client-api/references/raw-queries.md":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/raw-queries.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/raw-queries.md)",".agents/skills/prisma-client-api/references/relations.md":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/relations.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/relations.md)",".agents/skills/prisma-client-api/references/transactions.md":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/transactions.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/transactions.md)",".agents/skills/prisma-cli/references/db-execute.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/db-execute.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-execute.md)",".agents/skills/prisma-cli/references/db-pull.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/db-pull.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-pull.md)",".agents/skills/prisma-cli/references/db-push.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/db-push.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-push.md)",".agents/skills/prisma-cli/references/db-seed.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/db-seed.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-seed.md)",".agents/skills/prisma-cli/references/debug.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/debug.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/debug.md)",".agents/skills/prisma-cli/references/dev.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/dev.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/dev.md)",".agents/skills/prisma-cli/references/format.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/format.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/format.md)",".agents/skills/prisma-cli/references/generate.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/generate.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/generate.md)",".agents/skills/prisma-cli/references/init.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/init.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/init.md)",".agents/skills/prisma-cli/references/migrate-deploy.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-deploy.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-deploy.md)",".agents/skills/prisma-cli/references/migrate-dev.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-dev.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-dev.md)",".agents/skills/prisma-cli/references/migrate-diff.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-diff.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-diff.md)",".agents/skills/prisma-cli/references/migrate-reset.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-reset.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-reset.md)",".agents/skills/prisma-cli/references/migrate-resolve.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-resolve.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-resolve.md)",".agents/skills/prisma-cli/references/migrate-status.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-status.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-status.md)",".agents/skills/prisma-cli/references/studio.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/studio.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/studio.md)",".agents/skills/prisma-cli/references/validate.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/validate.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/validate.md)","packages/emails/locales/zh/emails.json":"# Zh Skill\n\nPath: `packages/emails/locales/zh/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/zh/emails.json)","packages/emails/locales/sv/emails.json":"# Sv Skill\n\nPath: `packages/emails/locales/sv/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/sv/emails.json)","packages/emails/locales/ru/emails.json":"# Ru Skill\n\nPath: `packages/emails/locales/ru/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/ru/emails.json)","packages/emails/locales/pt-BR/emails.json":"# Pt-BR Skill\n\nPath: `packages/emails/locales/pt-BR/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/pt-BR/emails.json)","packages/emails/locales/pl/emails.json":"# Pl Skill\n\nPath: `packages/emails/locales/pl/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/pl/emails.json)","packages/emails/locales/no/emails.json":"# No Skill\n\nPath: `packages/emails/locales/no/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/no/emails.json)","packages/emails/locales/nl/emails.json":"# Nl Skill\n\nPath: `packages/emails/locales/nl/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/nl/emails.json)","packages/emails/locales/it/emails.json":"# It Skill\n\nPath: `packages/emails/locales/it/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/it/emails.json)","packages/emails/locales/hu/emails.json":"# Hu Skill\n\nPath: `packages/emails/locales/hu/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/hu/emails.json)","packages/emails/locales/fr/emails.json":"# Fr Skill\n\nPath: `packages/emails/locales/fr/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/fr/emails.json)","packages/emails/locales/fi/emails.json":"# Fi Skill\n\nPath: `packages/emails/locales/fi/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/fi/emails.json)","packages/emails/locales/es/emails.json":"# Es Skill\n\nPath: `packages/emails/locales/es/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/es/emails.json)","packages/emails/locales/en/emails.json":"# En Skill\n\nPath: `packages/emails/locales/en/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/en/emails.json)","packages/emails/locales/de/emails.json":"# De Skill\n\nPath: `packages/emails/locales/de/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/de/emails.json)","packages/emails/locales/da/emails.json":"# Da Skill\n\nPath: `packages/emails/locales/da/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/da/emails.json)","packages/emails/locales/cs/emails.json":"# Cs Skill\n\nPath: `packages/emails/locales/cs/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/cs/emails.json)"},"files":{".agents/skills/prisma-postgres/SKILL.md":"---\nname: prisma-postgres\ndescription: Prisma Postgres setup and operations guidance across Console, create-db CLI, Management API, and Management API SDK. Use when creating Prisma Postgres databases, working in Prisma Console, provisioning with create-db/create-pg/create-postgres, or integrating programmatic provisioning with service tokens or OAuth.\nlicense: MIT\nmetadata:\n  author: prisma\n  version: \"1.0.0\"\n---\n\n# Prisma Postgres\n\nGuidance for creating, managing, and integrating Prisma Postgres across interactive and programmatic workflows.\n\n## When to Apply\n\nReference this skill when:\n- Setting up Prisma Postgres from Prisma Console\n- Provisioning instant temporary databases with `create-db`\n- Managing Prisma Postgres resources via Management API\n- Using `@prisma/management-api-sdk` in TypeScript/JavaScript\n- Handling claim URLs, connection strings, regions, and auth flows\n\n## Rule Categories by Priority\n\n| Priority | Category | Impact | Prefix |\n|----------|----------|--------|--------|\n| 1 | CLI Provisioning | CRITICAL | `create-db-cli` |\n| 2 | Management API | CRITICAL | `management-api` |\n| 3 | Management API SDK | HIGH | `management-api-sdk` |\n| 4 | Console and Connections | HIGH | `console-and-connections` |\n\n## Quick Reference\n\n- `create-db-cli` - instant databases and CLI options\n- `management-api` - service token and OAuth API workflows\n- `management-api-sdk` - typed SDK usage with token storage\n- `console-and-connections` - Console operations and connection setup\n\n## Core Workflows\n\n### 1. Console-first workflow\n\nUse Prisma Console for manual setup and operations:\n\n- Open `https://console.prisma.io`\n- Create/select workspace and project\n- Use Studio in the project sidebar to view/edit data\n- Retrieve direct connection details from the project UI\n\n### 2. Quick provisioning with create-db\n\nUse `create-db` when you need a database immediately:\n\n```bash\nnpx create-db@latest\n```\n\nAliases:\n\n```bash\nnpx create-pg@latest\nnpx create-postgres@latest\n```\n\nFor app integrations, you can also use the programmatic API (`create()` / `regions()`) from the `create-db` npm package.\n\nTemporary databases auto-delete after ~24 hours unless claimed.\n\n### 3. Programmatic provisioning with Management API\n\nUse API endpoints on:\n\n```text\nhttps://api.prisma.io/v1\n```\n\nExplore the schema and endpoints using:\n\n- OpenAPI docs: `https://api.prisma.io/v1/doc`\n- Swagger Editor: `https://api.prisma.io/v1/swagger-editor`\n\nAuth options:\n\n- Service token (workspace server-to-server)\n- OAuth 2.0 (act on behalf of users)\n\n### 4. Type-safe integration with Management API SDK\n\nInstall and use:\n\n```bash\nnpm install @prisma/management-api-sdk\n```\n\nUse `createManagementApiClient` for existing tokens, or `createManagementApiSdk` for OAuth + token refresh.\n\n## Rule Files\n\nDetailed guidance lives in:\n\n```\nreferences/console-and-connections.md\nreferences/create-db-cli.md\nreferences/management-api.md\nreferences/management-api-sdk.md\n```\n\n## How to Use\n\nStart with `references/create-db-cli.md` for fast setup, then switch to `references/management-api.md` or `references/management-api-sdk.md` when you need programmatic provisioning.\n",".agents/skills/prisma-cli/SKILL.md":"---\nname: prisma-cli\ndescription: Prisma CLI commands reference covering all available commands, options, and usage patterns. Use when running Prisma CLI commands, setting up projects, generating client, running migrations, or managing databases. Triggers on \"prisma init\", \"prisma generate\", \"prisma migrate\", \"prisma db\", \"prisma studio\".\nlicense: MIT\nmetadata:\n  author: prisma\n  version: \"7.0.0\"\n---\n\n# Prisma CLI Reference\n\nComplete reference for all Prisma CLI commands. This skill provides guidance on command usage, options, and best practices for Prisma ORM 7.x.\n\n## When to Apply\n\nReference this skill when:\n- Setting up a new Prisma project (`prisma init`)\n- Generating Prisma Client (`prisma generate`)\n- Running database migrations (`prisma migrate`)\n- Managing database state (`prisma db push/pull`)\n- Using local development database (`prisma dev`)\n- Debugging Prisma issues (`prisma debug`)\n\n## Rule Categories by Priority\n\n| Priority | Category | Impact | Prefix |\n|----------|----------|--------|--------|\n| 1 | Setup | HIGH | `init` |\n| 2 | Generation | HIGH | `generate` |\n| 3 | Development | HIGH | `dev` |\n| 4 | Database | HIGH | `db-` |\n| 5 | Migrations | CRITICAL | `migrate-` |\n| 6 | Utility | MEDIUM | `studio`, `validate`, `format`, `debug` |\n\n## Command Categories\n\n| Category | Commands | Purpose |\n|----------|----------|---------|\n| Setup | `init` | Bootstrap new Prisma project |\n| Generation | `generate` | Generate Prisma Client |\n| Validation | `validate`, `format` | Schema validation and formatting |\n| Development | `dev` | Local Prisma Postgres for development |\n| Database | `db pull`, `db push`, `db seed`, `db execute` | Direct database operations |\n| Migrations | `migrate dev`, `migrate deploy`, `migrate reset`, `migrate status`, `migrate diff`, `migrate resolve` | Schema migrations |\n| Utility | `studio`, `version`, `debug` | Development tools |\n\n## Quick Reference\n\n### Project Setup\n\n```bash\n# Initialize new project (creates prisma/ folder and prisma.config.ts)\nprisma init\n\n# Initialize with specific database\nprisma init --datasource-provider postgresql\nprisma init --datasource-provider mysql\nprisma init --datasource-provider sqlite\n\n# Initialize with Prisma Postgres (cloud)\nprisma init --db\n\n# Initialize with AI-generated schema\nprisma init --prompt \"E-commerce app with users, products, orders\"\n```\n\n### Client Generation\n\n```bash\n# Generate Prisma Client\nprisma generate\n\n# Watch mode for development\nprisma generate --watch\n\n# Generate without engine (for Accelerate/edge)\nprisma generate --no-engine\n\n# Generate specific generator only\nprisma generate --generator client\n```\n\n### Bun Runtime\n\nWhen using Bun, always add the `--bun` flag so Prisma runs with the Bun runtime (otherwise it falls back to Node.js because of the CLI shebang):\n\n```bash\nbunx --bun prisma init\nbunx --bun prisma generate\n```\n\n### Local Development Database\n\n```bash\n# Start local Prisma Postgres\nprisma dev\n\n# Start with specific name\nprisma dev --name myproject\n\n# Start in background (detached)\nprisma dev --detach\n\n# List all local instances\nprisma dev ls\n\n# Stop instance\nprisma dev stop myproject\n\n# Remove instance data\nprisma dev rm myproject\n```\n\n### Database Operations\n\n```bash\n# Pull schema from existing database\nprisma db pull\n\n# Push schema to database (no migrations)\nprisma db push\n\n# Seed database\nprisma db seed\n\n# Execute raw SQL\nprisma db execute --file ./script.sql\n```\n\n### Migrations (Development)\n\n```bash\n# Create and apply migration\nprisma migrate dev\n\n# Create migration with name\nprisma migrate dev --name add_users_table\n\n# Create migration without applying\nprisma migrate dev --create-only\n\n# Reset database and apply all migrations\nprisma migrate reset\n```\n\n### Migrations (Production)\n\n```bash\n# Apply pending migrations (CI/CD)\nprisma migrate deploy\n\n# Check migration status\nprisma migrate status\n\n# Compare schemas and generate diff\nprisma migrate diff --from-config-datasource --to-schema schema.prisma --script\n```\n\n### Utility Commands\n\n```bash\n# Open Prisma Studio (database GUI)\nprisma studio\n\n# Show version info\nprisma version\nprisma -v\n\n# Debug information\nprisma debug\n\n# Validate schema\nprisma validate\n\n# Format schema\nprisma format\n```\n\n## Prisma 7 Changes\n\n### New Configuration File\n\nPrisma 7 uses `prisma.config.ts` for CLI configuration:\n\n```typescript\nimport 'dotenv/config'\nimport { defineConfig, env } from 'prisma/config'\n\nexport default defineConfig({\n  schema: 'prisma/schema.prisma',\n  migrations: {\n    path: 'prisma/migrations',\n    seed: 'tsx prisma/seed.ts',\n  },\n  datasource: {\n    url: env('DATABASE_URL'),\n  },\n})\n```\n\n### Removed Flags\n\n- `--skip-generate` removed from `migrate dev` and `db push`\n- `--skip-seed` removed from `migrate dev`\n- `--schema` and `--url` removed from `db execute`\n- Run `prisma generate` explicitly after migrations\n\n### Environment Variables\n\nEnvironment variables are no longer auto-loaded. Use `dotenv`:\n\n```typescript\n// prisma.config.ts\nimport 'dotenv/config'\n```\n\n## Rule Files\n\nSee individual rule files for detailed command documentation:\n\n```\nreferences/init.md           - Project initialization\nreferences/generate.md       - Client generation\nreferences/dev.md            - Local development database\nreferences/db-pull.md        - Database introspection\nreferences/db-push.md        - Schema push\nreferences/db-seed.md        - Database seeding\nreferences/db-execute.md     - Raw SQL execution\nreferences/migrate-dev.md    - Development migrations\nreferences/migrate-deploy.md - Production migrations\nreferences/migrate-reset.md  - Database reset\nreferences/migrate-status.md - Migration status\nreferences/migrate-resolve.md - Migration resolution\nreferences/migrate-diff.md   - Schema diffing\nreferences/studio.md         - Database GUI\nreferences/validate.md       - Schema validation\nreferences/format.md         - Schema formatting\nreferences/debug.md          - Debug info\n```\n\n## How to Use\n\nUse the command categories above for navigation, then open the specific command reference file you need.\n",".claude/skills/fix-crowdin-pr/SKILL.md":"---\nname: fix-crowdin-pr\ndescription: Fix coderabbit review findings on a Crowdin translation PR (l10n_main), keeping Crowdin and the PR branch in sync. Takes an optional PR number, defaults to the open \"🌐 New Crowdin updates\" PR.\n---\n\n# Fix Crowdin PR\n\nFix coderabbit findings on a Crowdin sync PR so the fix survives the next sync.\n\n## Why this order matters\n\nCrowdin force pushes `l10n_main` on every sync. A fix that lands only on the PR branch gets overwritten; a fix made only in Crowdin waits for the next sync. Therefore: upload corrections to Crowdin **first**, then push the identical changes to the branch. The next sync then exports identical content and the branch stays stable.\n\n## Credentials\n\nToken and project id live in the macOS Keychain:\n\n```\nsecurity find-generic-password -s rallly-billing.CROWDIN_PERSONAL_TOKEN -w\nsecurity find-generic-password -s rallly-billing.CROWDIN_PROJECT_ID -w\n```\n\nThe token is the secret: retrieve it inline, never echo it. The project id is not secret — it is 527308 and appears in API paths below. Files are namespaced under Crowdin branch `main` (branchId 10).\n\n## Steps\n\n### 1. Find findings\n\nDefault to the open PR titled \"🌐 New Crowdin updates\" (`gh pr list --search \"🌐 New Crowdin updates\" --state open`), or use the PR number given as an argument.\n\n```\ngh api repos/lukevella/rallly/pulls/<PR>/comments --paginate\n```\n\nFilter to comments authored by `coderabbitai`. Extract path, line, and the suggested fix from each body.\n\n### 2. Check out the branch\n\nCheck out `l10n_main` in the session worktree.\n\n### 3. Apply fixes\n\nJudge each finding on its merits — coderabbit is sometimes wrong. Skip anything that contradicts the English source in `apps/web/public/locales/en/`, `packages/emails/locales/en/`, or `apps/landing/public/locales/en/`, and note why.\n\nApply accepted fixes with a python script using exact string replacement:\n\n- Assert each old string occurs exactly once in the file before replacing.\n- After all edits, `json.load` every touched file to prove it still parses.\n\n### 4. Upload to Crowdin\n\nDerive the affected languages from the touched file paths (the locale directory segment), then upload.\n\n**Primary: Crowdin CLI** (works since `preserve_hierarchy: true` landed in crowdin.yml, PR #2717). One run per affected language:\n\n```\nCROWDIN_PERSONAL_TOKEN=$(security find-generic-password -s rallly-billing.CROWDIN_PERSONAL_TOKEN -w) \\\nCROWDIN_PROJECT_ID=$(security find-generic-password -s rallly-billing.CROWDIN_PROJECT_ID -w) \\\nnpx -y @crowdin/cli@4 upload translations -b main -l <crowdin-lang-id> --auto-approve-imported --no-progress\n```\n\nThe CLI uploads all translation files for the language, not just touched ones; identical translations are skipped by default, so this is harmless. Do not run `crowdin status` — the token scopes don't cover it.\n\n**Fallback: REST API** — use when the CLI fails or Java/npx is unavailable (it is faster and uploads only touched files, but needs hand-rolled requests). Per touched file and language:\n\n1. `POST https://api.crowdin.com/api/v2/storages` with headers `Authorization: Bearer <token>`, `Crowdin-API-FileName: <basename>`, `Content-Type: application/octet-stream`, body = raw file bytes. Response gives `storageId`.\n2. `POST https://api.crowdin.com/api/v2/projects/527308/translations/<crowdin-lang-id>` with JSON body:\n\n   ```json\n   {\"storageId\": <id>, \"fileId\": <fileId>, \"importEqSuggestions\": false, \"autoApproveImported\": true}\n   ```\n\nKnown fileIds: web `app.json` = 35, emails `emails.json` = 68, landing `blog.json` = 53, `common.json` = 55, `home.json` = 57, `pricing.json` = 59. If a fileId is unknown, list via `GET /api/v2/projects/527308/files`.\n\n### 5. Verify\n\nConfirm at least one fix landed as the current translation:\n\n```\nGET /api/v2/projects/527308/languages/<crowdin-lang-id>/translations?stringIds=<ids>&fileId=<fileId>\n```\n\nFind stringIds via `GET /api/v2/projects/527308/strings?fileId=<fileId>&filter=<key>&scope=identifier`.\n\nDo **not** use `GET /api/v2/projects/527308/translations?stringId=...` for verification — it lists suggestions oldest first and ignores orderBy, so it shows stale entries.\n\n### 6. Commit and push\n\nCommit to `l10n_main` with a `🌐` gitmoji message and push. `--no-verify` is acceptable in a fresh worktree (husky needs node_modules).\n\n### 7. Reply and resolve every comment\n\nCrowdin syncs keep adding new translations, and each sync can bring new coderabbit comments. Unresolved threads must mean \"not yet addressed\" — so after pushing, reply to **every** finding and resolve its thread, applied or skipped:\n\n- Applied: cite the fix commit sha and note the Crowdin upload was verified.\n- Skipped: state the reason (e.g. matches the English source, contradicts the file's register).\n\nReply to a review comment (comment ids come from the step 1 fetch):\n\n```\ngh api -X POST repos/lukevella/rallly/pulls/<PR>/comments/<comment-id>/replies -f body='...'\n```\n\nResolving needs GraphQL — list thread ids, then resolve the ones still open (coderabbit auto-resolves some threads when it sees the fix commit; skip those):\n\n```\ngh api graphql -f query='query { repository(owner: \"lukevella\", name: \"rallly\") { pullRequest(number: <PR>) { reviewThreads(first: 50) { nodes { id isResolved comments(first: 1) { nodes { databaseId } } } } } } }'\ngh api graphql -f query='mutation { resolveReviewThread(input: {threadId: \"<thread-id>\"}) { thread { isResolved } } }'\n```\n\n### 8. Report\n\nSummarize: findings applied, findings skipped and why, Crowdin verification result, pushed commit sha, and confirmation that all threads are replied to and resolved.\n\n## Language id mapping\n\nRepo locale dirs use two letter codes (`%two_letters_code%` in crowdin.yml) plus `pt-BR` and `zh-Hant`. Crowdin language ids differ for some:\n\n| Repo dir | Crowdin id |\n|----------|------------|\n| es | es-ES |\n| sv | sv-SE |\n| zh | zh-CN |\n| pt-BR | pt-BR |\n\nEverything else observed so far matches the two letter code. If unsure, check `targetLanguageIds` via `GET /api/v2/projects/527308` — an upload to a wrong language id fails loudly, so a mistake here can't corrupt data, only waste a round trip.\n\n## Gotchas\n\n- The Bash tool sandbox drops PATH inside shell functions. Make the REST API calls from a python script that reads the token via `subprocess` + `security`, not from shell functions wrapping curl.\n- Never write the token to a file, echo it, or pass it where it lands in logs. Keychain lookups inline only.\n- Findings on languages you can't judge: prefer skipping over guessing. A wrong \"fix\" auto-approved into Crowdin displaces a human translation.\n",".agents/skills/prisma-database-setup/SKILL.md":"---\nname: prisma-database-setup\ndescription: Guides for configuring Prisma with different database providers (PostgreSQL, MySQL, SQLite, MongoDB, etc.). Use when setting up a new project, changing databases, or troubleshooting connection issues. Triggers on \"configure postgres\", \"connect to mysql\", \"setup mongodb\", \"sqlite setup\".\nlicense: MIT\nmetadata:\n  author: prisma\n  version: \"1.0.0\"\n---\n\n# Prisma Database Setup\n\nComprehensive guides for configuring Prisma ORM with various database providers.\n\n## When to Apply\n\nReference this skill when:\n- Initializing a new Prisma project\n- Switching database providers\n- Configuring connection strings and environment variables\n- Troubleshooting database connection issues\n- Setting up database-specific features\n- Generating and instantiating Prisma Client\n\n## Rule Categories by Priority\n\n| Priority | Category | Impact | Prefix |\n|----------|----------|--------|--------|\n| 1 | Provider Guides | CRITICAL | provider names |\n| 2 | Prisma Postgres | HIGH | `prisma-postgres` |\n| 3 | Client Setup | CRITICAL | `prisma-client-setup` |\n\n## System Prerequisites (Prisma ORM 7)\n\n- **Node.js 20.19.0+**\n- **TypeScript 5.4.0+**\n\n## Bun Runtime\n\nIf you're using Bun, run Prisma CLI commands with `bunx --bun prisma ...` so Prisma uses the Bun runtime instead of falling back to Node.js.\n\n## Supported Databases\n\n| Database | Provider String | Notes |\n|----------|-----------------|-------|\n| PostgreSQL | `postgresql` | Default, full feature support |\n| MySQL | `mysql` | Widespread support, some JSON diffs |\n| SQLite | `sqlite` | Local file-based, no enum/scalar lists |\n| MongoDB | `mongodb` | **NOT SUPPORTED IN v7** (Use v6) |\n| SQL Server | `sqlserver` | Microsoft ecosystem |\n| CockroachDB | `cockroachdb` | Distributed SQL, Postgres-compatible |\n| Prisma Postgres | `postgresql` | Managed serverless database |\n\n## Configuration Files\n\nPrisma v7 uses two main files for configuration:\n\n1. **`prisma/schema.prisma`**: Defines the `datasource` block.\n2. **`prisma.config.ts`**: Configures the connection URL (replaces env loading in schema).\n\n## Driver Adapters (Prisma ORM 7)\n\nPrisma ORM 7 uses the query compiler by default, which **requires a driver adapter**. Choose the adapter and driver for your database and pass the adapter to `PrismaClient`.\n\n| Database | Adapter | JS Driver |\n|----------|---------|-----------|\n| PostgreSQL | `@prisma/adapter-pg` | `pg` |\n| CockroachDB | `@prisma/adapter-pg` | `pg` |\n| Prisma Postgres | `@prisma/adapter-ppg` | `@prisma/ppg` |\n| MySQL / MariaDB | `@prisma/adapter-mariadb` | `mariadb` |\n| SQLite | `@prisma/adapter-better-sqlite3` | `better-sqlite3` |\n| SQLite (Turso/LibSQL) | `@prisma/adapter-libsql` | `@libsql/client` |\n| SQL Server | `@prisma/adapter-mssql` | `node-mssql` |\n\nExample (PostgreSQL):\n\n```ts\nimport 'dotenv/config'\nimport { PrismaClient } from '../generated/client'\nimport { PrismaPg } from '@prisma/adapter-pg'\n\nconst adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL })\nconst prisma = new PrismaClient({ adapter })\n```\n\n## Prisma Client Setup (Required)\n\nPrisma Client must be installed and generated for any database.\n\n1. Install Prisma CLI and Prisma Client:\n   ```bash\n   npm install prisma --save-dev\n   npm install @prisma/client\n   ```\n\n1. Add a generator block (output is required in Prisma v7):\n   ```prisma\n   generator client {\n     provider = \"prisma-client\"\n     output   = \"../generated\"\n   }\n   ```\n\n1. Generate Prisma Client:\n   ```bash\n   npx prisma generate\n   ```\n\n1. Instantiate Prisma Client with the database-specific driver adapter:\n   ```typescript\n   import { PrismaClient } from '../generated/client'\n   import { PrismaPg } from '@prisma/adapter-pg'\n\n   const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL })\n   const prisma = new PrismaClient({ adapter })\n   ```\n\n1. Re-run `prisma generate` after every schema change.\n\n## Quick Reference\n\n### PostgreSQL\n```prisma\ndatasource db {\n  provider = \"postgresql\"\n}\n\ngenerator client {\n  provider = \"prisma-client\"\n  output   = \"../generated\"\n}\n```\n\n### MySQL\n```prisma\ndatasource db {\n  provider = \"mysql\"\n}\n\ngenerator client {\n  provider = \"prisma-client\"\n  output   = \"../generated\"\n}\n```\n\n### SQLite\n```prisma\ndatasource db {\n  provider = \"sqlite\"\n}\n\ngenerator client {\n  provider = \"prisma-client\"\n  output   = \"../generated\"\n}\n```\n\n### MongoDB (Prisma v6 only)\n```prisma\ndatasource db {\n  provider = \"mongodb\"\n  url      = env(\"DATABASE_URL\")\n}\n\ngenerator client {\n  provider = \"prisma-client-js\"\n}\n```\n\n## Rule Files\n\nSee individual rule files for detailed setup instructions:\n\n```\nreferences/postgresql.md\nreferences/mysql.md\nreferences/sqlite.md\nreferences/mongodb.md\nreferences/sqlserver.md\nreferences/cockroachdb.md\nreferences/prisma-postgres.md\nreferences/prisma-client-setup.md\n```\n\n## How to Use\n\nChoose the provider reference file for your database, then apply `references/prisma-client-setup.md` to complete client generation and adapter setup.\n",".agents/skills/prisma-client-api/SKILL.md":"---\nname: prisma-client-api\ndescription: Prisma Client API reference covering model queries, filters, operators, and client methods. Use when writing database queries, using CRUD operations, filtering data, or configuring Prisma Client. Triggers on \"prisma query\", \"findMany\", \"create\", \"update\", \"delete\", \"$transaction\".\nlicense: MIT\nmetadata:\n  author: prisma\n  version: \"7.0.0\"\n---\n\n# Prisma Client API Reference\n\nComplete API reference for Prisma Client. This skill provides guidance on model queries, filtering, relations, and client methods for Prisma ORM 7.x.\n\n## When to Apply\n\nReference this skill when:\n- Writing database queries with Prisma Client\n- Performing CRUD operations (create, read, update, delete)\n- Filtering and sorting data\n- Working with relations\n- Using transactions\n- Configuring client options\n\n## Rule Categories by Priority\n\n| Priority | Category | Impact | Prefix |\n|----------|----------|--------|--------|\n| 1 | Client Construction | HIGH | `constructor` |\n| 2 | Model Queries | CRITICAL | `model-queries` |\n| 3 | Query Shape | HIGH | `query-options` |\n| 4 | Filtering | HIGH | `filters` |\n| 5 | Relations | HIGH | `relations` |\n| 6 | Transactions | CRITICAL | `transactions` |\n| 7 | Raw SQL | CRITICAL | `raw-queries` |\n| 8 | Client Methods | MEDIUM | `client-methods` |\n\n## Quick Reference\n\n- `constructor` - `PrismaClient` setup and adapter wiring\n- `model-queries` - CRUD operations and bulk operations\n- `query-options` - `select`, `include`, `omit`, sort, pagination\n- `filters` - scalar and logical filter operators\n- `relations` - relation reads and nested writes\n- `transactions` - array and interactive transaction patterns\n- `raw-queries` - `$queryRaw` and `$executeRaw` safety\n- `client-methods` - lifecycle and extension methods\n\n## Client Instantiation (v7)\n\n```typescript\nimport { PrismaClient } from '../generated/client'\nimport { PrismaPg } from '@prisma/adapter-pg'\n\nconst adapter = new PrismaPg({\n  connectionString: process.env.DATABASE_URL\n})\n\nconst prisma = new PrismaClient({ adapter })\n```\n\n## Model Query Methods\n\n| Method | Description |\n|--------|-------------|\n| `findUnique()` | Find one record by unique field |\n| `findUniqueOrThrow()` | Find one or throw error |\n| `findFirst()` | Find first matching record |\n| `findFirstOrThrow()` | Find first or throw error |\n| `findMany()` | Find multiple records |\n| `create()` | Create a new record |\n| `createMany()` | Create multiple records |\n| `createManyAndReturn()` | Create multiple and return them |\n| `update()` | Update one record |\n| `updateMany()` | Update multiple records |\n| `updateManyAndReturn()` | Update multiple and return them |\n| `upsert()` | Update or create record |\n| `delete()` | Delete one record |\n| `deleteMany()` | Delete multiple records |\n| `count()` | Count matching records |\n| `aggregate()` | Aggregate values (sum, avg, etc.) |\n| `groupBy()` | Group and aggregate |\n\n## Query Options\n\n| Option | Description |\n|--------|-------------|\n| `where` | Filter conditions |\n| `select` | Fields to include |\n| `include` | Relations to load |\n| `omit` | Fields to exclude |\n| `orderBy` | Sort order |\n| `take` | Limit results |\n| `skip` | Skip results (pagination) |\n| `cursor` | Cursor-based pagination |\n| `distinct` | Unique values only |\n\n## Client Methods\n\n| Method | Description |\n|--------|-------------|\n| `$connect()` | Explicitly connect to database |\n| `$disconnect()` | Disconnect from database |\n| `$transaction()` | Execute transaction |\n| `$queryRaw()` | Execute raw SQL query |\n| `$executeRaw()` | Execute raw SQL command |\n| `$on()` | Subscribe to events |\n| `$extends()` | Add extensions |\n\n## Quick Examples\n\n### Find records\n\n```typescript\n// Find by unique field\nconst user = await prisma.user.findUnique({\n  where: { email: 'alice@prisma.io' }\n})\n\n// Find with filter\nconst users = await prisma.user.findMany({\n  where: { role: 'ADMIN' },\n  orderBy: { createdAt: 'desc' },\n  take: 10\n})\n```\n\n### Create records\n\n```typescript\nconst user = await prisma.user.create({\n  data: {\n    email: 'alice@prisma.io',\n    name: 'Alice',\n    posts: {\n      create: { title: 'Hello World' }\n    }\n  },\n  include: { posts: true }\n})\n```\n\n### Update records\n\n```typescript\nconst user = await prisma.user.update({\n  where: { id: 1 },\n  data: { name: 'Alice Smith' }\n})\n```\n\n### Delete records\n\n```typescript\nawait prisma.user.delete({\n  where: { id: 1 }\n})\n```\n\n### Transactions\n\n```typescript\nconst [user, post] = await prisma.$transaction([\n  prisma.user.create({ data: { email: 'alice@prisma.io' } }),\n  prisma.post.create({ data: { title: 'Hello', authorId: 1 } })\n])\n```\n\n## Rule Files\n\nDetailed API documentation:\n\n```\nreferences/constructor.md        - PrismaClient constructor options\nreferences/model-queries.md      - CRUD operations\nreferences/query-options.md      - select, include, omit, where, orderBy\nreferences/filters.md            - Filter conditions and operators\nreferences/relations.md          - Relation queries and nested operations\nreferences/transactions.md       - Transaction API\nreferences/raw-queries.md        - $queryRaw, $executeRaw\nreferences/client-methods.md     - $connect, $disconnect, $on, $extends\n```\n\n## Filter Operators\n\n| Operator | Description |\n|----------|-------------|\n| `equals` | Exact match |\n| `not` | Not equal |\n| `in` | In array |\n| `notIn` | Not in array |\n| `lt`, `lte` | Less than |\n| `gt`, `gte` | Greater than |\n| `contains` | String contains |\n| `startsWith` | String starts with |\n| `endsWith` | String ends with |\n| `mode` | Case sensitivity |\n\n## Relation Filters\n\n| Operator | Description |\n|----------|-------------|\n| `some` | At least one related record matches |\n| `every` | All related records match |\n| `none` | No related records match |\n| `is` | Related record matches (1-to-1) |\n| `isNot` | Related record doesn't match |\n\n## Resources\n\n- [Prisma Client API Reference](https://www.prisma.io/docs/orm/reference/prisma-client-reference)\n- [CRUD Operations](https://www.prisma.io/docs/orm/prisma-client/queries/crud)\n- [Filtering and Sorting](https://www.prisma.io/docs/orm/prisma-client/queries/filtering-and-sorting)\n\n## How to Use\n\nPick the category from the table above, then open the matching reference file for implementation details and examples.\n",".agents/skills/prisma-upgrade-v7/SKILL.md":"---\nname: prisma-upgrade-v7\ndescription: Complete migration guide from Prisma ORM v6 to v7 covering all breaking changes. Use when upgrading Prisma versions, encountering v7 errors, or migrating existing projects. Triggers on \"upgrade to prisma 7\", \"prisma 7 migration\", \"prisma-client generator\", \"driver adapter required\".\nlicense: MIT\nmetadata:\n  author: prisma\n  version: \"7.0.0\"\n---\n\n# Upgrade to Prisma ORM 7\n\nComplete guide for migrating from Prisma ORM v6 to v7. This upgrade introduces significant breaking changes including ESM-only support, required driver adapters, and a new configuration system.\n\n## When to Apply\n\nReference this skill when:\n- Upgrading from Prisma v6 to v7\n- Updating to the `prisma-client` generator\n- Setting up driver adapters\n- Configuring `prisma.config.ts`\n- Fixing import errors after upgrade\n\n## Rule Categories by Priority\n\n| Priority | Category | Impact | Prefix |\n|----------|----------|--------|--------|\n| 1 | Schema Migration | CRITICAL | `schema-changes` |\n| 2 | Database Connectivity | CRITICAL | `driver-adapters` |\n| 3 | Module System | CRITICAL | `esm-support` |\n| 4 | Config and Env | HIGH | `prisma-config`, `env-variables` |\n| 5 | Removed Features | HIGH | `removed-features` |\n| 6 | Accelerate | HIGH | `accelerate-users` |\n\n## Quick Reference\n\n- `schema-changes` - generator and output migration for Prisma v7\n- `driver-adapters` - required adapter installation and client wiring\n- `esm-support` - package/module requirements for ESM\n- `prisma-config` - creating and using `prisma.config.ts`\n- `env-variables` - explicit environment loading\n- `removed-features` - removed middleware, metrics, and flags\n- `accelerate-users` - migration notes for Accelerate users\n\n## Important Notes\n\n- **MongoDB not yet supported in v7** - Continue using v6 for MongoDB\n- **Node.js 20.19.0+** required\n- **TypeScript 5.4.0+** required\n\n## Upgrade Steps Overview\n\n1. Update packages to v7\n2. Configure ESM in package.json\n3. Update TypeScript configuration\n4. Update schema generator block\n5. Create prisma.config.ts\n6. Install and configure driver adapter\n7. Update Prisma Client imports\n8. Update client instantiation\n9. Remove deprecated code (middleware, env vars)\n10. Run generate and test\n\n## Quick Upgrade Commands\n\n```bash\n# Update packages\nnpm install @prisma/client@7\nnpm install -D prisma@7\n\n# Install driver adapter (PostgreSQL example)\nnpm install @prisma/adapter-pg\n\n# Install dotenv for env loading\nnpm install dotenv\n\n# Regenerate client\nnpx prisma generate\n```\n\n## Breaking Changes Summary\n\n| Change | v6 | v7 |\n|--------|----|----|\n| Module format | CommonJS | ESM only |\n| Generator provider | `prisma-client-js` | `prisma-client` |\n| Output path | Auto (node_modules) | Required explicit |\n| Driver adapters | Optional | Required |\n| Config file | `.env` + schema | `prisma.config.ts` |\n| Env loading | Automatic | Manual (dotenv) |\n| Middleware | `$use()` | Client Extensions |\n| Metrics | Preview feature | Removed |\n\n## Rule Files\n\nDetailed migration guides for each breaking change:\n\n```\nreferences/esm-support.md        - ESM module configuration\nreferences/schema-changes.md     - Generator and schema updates\nreferences/driver-adapters.md    - Required driver adapter setup\nreferences/prisma-config.md      - New configuration file\nreferences/env-variables.md      - Environment variable loading\nreferences/removed-features.md   - Middleware, metrics, and CLI flags\nreferences/accelerate-users.md   - Special handling for Accelerate\n```\n\n## Step-by-Step Migration\n\n### 1. Update package.json for ESM\n\n```json\n{\n  \"type\": \"module\"\n}\n```\n\n### 2. Update tsconfig.json\n\n```json\n{\n  \"compilerOptions\": {\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"target\": \"ES2023\",\n    \"strict\": true,\n    \"esModuleInterop\": true\n  }\n}\n```\n\n### 3. Update schema.prisma\n\n```prisma\n// Before (v6)\ngenerator client {\n  provider = \"prisma-client-js\"\n}\n\n// After (v7)\ngenerator client {\n  provider = \"prisma-client\"\n  output   = \"../generated\"\n}\n```\n\n### 4. Create prisma.config.ts\n\n```typescript\nimport 'dotenv/config'\nimport { defineConfig, env } from 'prisma/config'\n\nexport default defineConfig({\n  schema: 'prisma/schema.prisma',\n  migrations: {\n    path: 'prisma/migrations',\n  },\n  datasource: {\n    url: env('DATABASE_URL'),\n  },\n})\n```\n\n### 5. Install driver adapter\n\n```bash\n# PostgreSQL\nnpm install @prisma/adapter-pg\n\n# MySQL\nnpm install @prisma/adapter-mariadb mariadb\n\n# SQLite\nnpm install @prisma/adapter-better-sqlite3\n\n# Prisma Postgres\nnpm install @prisma/adapter-ppg @prisma/ppg\n\n# Neon\nnpm install @prisma/adapter-neon\n```\n\n### 6. Update client instantiation\n\n```typescript\n// Before (v6)\nimport { PrismaClient } from '@prisma/client'\nconst prisma = new PrismaClient()\n\n// After (v7)\nimport { PrismaClient } from '../generated/client'\nimport { PrismaPg } from '@prisma/adapter-pg'\n\nconst adapter = new PrismaPg({\n  connectionString: process.env.DATABASE_URL\n})\nconst prisma = new PrismaClient({ adapter })\n```\n\n### 7. Run migrations and generate\n\n```bash\nnpx prisma generate\nnpx prisma migrate dev  # if needed\n```\n\n## Troubleshooting\n\n### \"Cannot find module\" errors\n- Check `output` path in generator block matches import path\n- Ensure `prisma generate` ran successfully\n\n### SSL certificate errors\n- Add `ssl: { rejectUnauthorized: false }` to adapter config\n- Or properly configure SSL certificates\n\n### Connection timeout issues\n- Driver adapters use different pool defaults\n- Configure pool settings explicitly on the adapter\n\n## Resources\n\n- [Official v7 Upgrade Guide](https://www.prisma.io/docs/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-7)\n- [Driver Adapters Documentation](https://www.prisma.io/docs/orm/overview/databases/database-drivers)\n- [Prisma Config Reference](https://www.prisma.io/docs/orm/reference/prisma-config-reference)\n\n## How to Use\n\nFollow `references/schema-changes.md` and `references/driver-adapters.md` first, then apply the remaining reference files based on your project setup.\n",".agents/skills/two-factor-authentication-best-practices/SKILL.md":"---\nname: two-factor-authentication-best-practices\ndescription: This skill provides guidance and enforcement rules for implementing secure two-factor authentication (2FA) using Better Auth's twoFactor plugin.\n---\n\n## Setting Up Two-Factor Authentication\n\nWhen adding 2FA to your application, configure the `twoFactor` plugin with your app name as the issuer. This name appears in authenticator apps when users scan the QR code.\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { twoFactor } from \"better-auth/plugins\";\n\nexport const auth = betterAuth({\n  appName: \"My App\", // Used as the default issuer for TOTP\n  plugins: [\n    twoFactor({\n      issuer: \"My App\", // Optional: override the app name for 2FA specifically\n    }),\n  ],\n});\n```\n\n**Note**: After adding the plugin, run `npx @better-auth/cli migrate` to add the required database fields and tables.\n\n### Client-Side Setup\n\nAdd the client plugin and configure the redirect behavior for 2FA verification:\n\n```ts\nimport { createAuthClient } from \"better-auth/client\";\nimport { twoFactorClient } from \"better-auth/client/plugins\";\n\nexport const authClient = createAuthClient({\n  plugins: [\n    twoFactorClient({\n      onTwoFactorRedirect() {\n        window.location.href = \"/2fa\"; // Redirect to your 2FA verification page\n      },\n    }),\n  ],\n});\n```\n\n## Enabling 2FA for Users\n\nWhen a user enables 2FA, require their password for verification. The enable endpoint returns a TOTP URI for QR code generation and backup codes for account recovery.\n\n```ts\nconst enable2FA = async (password: string) => {\n  const { data, error } = await authClient.twoFactor.enable({\n    password,\n  });\n\n  if (data) {\n    // data.totpURI - Use this to generate a QR code\n    // data.backupCodes - Display these to the user for safekeeping\n  }\n};\n```\n\n**Important**: The `twoFactorEnabled` flag on the user is not set to `true` until the user successfully verifies their first TOTP code. This ensures users have properly configured their authenticator app before 2FA is fully active.\n\n### Skipping Initial Verification\n\nIf you want to enable 2FA immediately without requiring verification, set `skipVerificationOnEnable`:\n\n```ts\ntwoFactor({\n  skipVerificationOnEnable: true, // Not recommended for most use cases\n});\n```\n\n**Note**: This is generally not recommended as it doesn't confirm the user has successfully set up their authenticator app.\n\n## TOTP (Authenticator App)\n\nTOTP generates time-based codes using an authenticator app (Google Authenticator, Authy, etc.). Codes are valid for 30 seconds by default.\n\n### Displaying the QR Code\n\nUse the TOTP URI to generate a QR code for users to scan:\n\n```tsx\nimport QRCode from \"react-qr-code\";\n\nconst TotpSetup = ({ totpURI }: { totpURI: string }) => {\n  return <QRCode value={totpURI} />;\n};\n```\n\n### Verifying TOTP Codes\n\nBetter Auth accepts codes from one period before and one after the current time, accommodating minor clock differences between devices:\n\n```ts\nconst verifyTotp = async (code: string) => {\n  const { data, error } = await authClient.twoFactor.verifyTotp({\n    code,\n    trustDevice: true, // Optional: remember this device for 30 days\n  });\n};\n```\n\n### TOTP Configuration Options\n\n```ts\ntwoFactor({\n  totpOptions: {\n    digits: 6, // 6 or 8 digits (default: 6)\n    period: 30, // Code validity period in seconds (default: 30)\n  },\n});\n```\n\n## OTP (Email/SMS)\n\nOTP sends a one-time code to the user's email or phone. You must implement the `sendOTP` function to deliver codes.\n\n### Configuring OTP Delivery\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { twoFactor } from \"better-auth/plugins\";\nimport { sendEmail } from \"./email\";\n\nexport const auth = betterAuth({\n  plugins: [\n    twoFactor({\n      otpOptions: {\n        sendOTP: async ({ user, otp }, ctx) => {\n          await sendEmail({\n            to: user.email,\n            subject: \"Your verification code\",\n            text: `Your code is: ${otp}`,\n          });\n        },\n        period: 5, // Code validity in minutes (default: 3)\n        digits: 6, // Number of digits (default: 6)\n        allowedAttempts: 5, // Max verification attempts (default: 5)\n      },\n    }),\n  ],\n});\n```\n\n### Sending and Verifying OTP\n\n```ts\n// Request an OTP to be sent\nconst sendOtp = async () => {\n  const { data, error } = await authClient.twoFactor.sendOtp();\n};\n\n// Verify the OTP code\nconst verifyOtp = async (code: string) => {\n  const { data, error } = await authClient.twoFactor.verifyOtp({\n    code,\n    trustDevice: true,\n  });\n};\n```\n\n### OTP Storage Security\n\nConfigure how OTP codes are stored in the database:\n\n```ts\ntwoFactor({\n  otpOptions: {\n    storeOTP: \"encrypted\", // Options: \"plain\", \"encrypted\", \"hashed\"\n  },\n});\n```\n\nFor custom encryption:\n\n```ts\ntwoFactor({\n  otpOptions: {\n    storeOTP: {\n      encrypt: async (token) => myEncrypt(token),\n      decrypt: async (token) => myDecrypt(token),\n    },\n  },\n});\n```\n\n## Backup Codes\n\nBackup codes provide account recovery when users lose access to their authenticator app or phone. They are generated automatically when 2FA is enabled.\n\n### Displaying Backup Codes\n\nAlways show backup codes to users when they enable 2FA:\n\n```tsx\nconst BackupCodes = ({ codes }: { codes: string[] }) => {\n  return (\n    <div>\n      <p>Save these codes in a secure location:</p>\n      <ul>\n        {codes.map((code, i) => (\n          <li key={i}>{code}</li>\n        ))}\n      </ul>\n    </div>\n  );\n};\n```\n\n### Regenerating Backup Codes\n\nWhen users need new codes, regenerate them (this invalidates all previous codes):\n\n```ts\nconst regenerateBackupCodes = async (password: string) => {\n  const { data, error } = await authClient.twoFactor.generateBackupCodes({\n    password,\n  });\n  // data.backupCodes contains the new codes\n};\n```\n\n### Using Backup Codes for Recovery\n\n```ts\nconst verifyBackupCode = async (code: string) => {\n  const { data, error } = await authClient.twoFactor.verifyBackupCode({\n    code,\n    trustDevice: true,\n  });\n};\n```\n\n**Note**: Each backup code can only be used once and is removed from the database after successful verification.\n\n### Backup Code Configuration\n\n```ts\ntwoFactor({\n  backupCodeOptions: {\n    amount: 10, // Number of codes to generate (default: 10)\n    length: 10, // Length of each code (default: 10)\n    storeBackupCodes: \"encrypted\", // Options: \"plain\", \"encrypted\"\n  },\n});\n```\n\n## Handling 2FA During Sign-In\n\nWhen a user with 2FA enabled signs in, the response includes `twoFactorRedirect: true`:\n\n```ts\nconst signIn = async (email: string, password: string) => {\n  const { data, error } = await authClient.signIn.email(\n    {\n      email,\n      password,\n    },\n    {\n      onSuccess(context) {\n        if (context.data.twoFactorRedirect) {\n          // Redirect to 2FA verification page\n          window.location.href = \"/2fa\";\n        }\n      },\n    }\n  );\n};\n```\n\n### Server-Side 2FA Detection\n\nWhen using `auth.api.signInEmail` on the server, check for 2FA redirect:\n\n```ts\nconst response = await auth.api.signInEmail({\n  body: {\n    email: \"user@example.com\",\n    password: \"password\",\n  },\n});\n\nif (\"twoFactorRedirect\" in response) {\n  // Handle 2FA verification\n}\n```\n\n## Trusted Devices\n\nTrusted devices allow users to skip 2FA verification on subsequent sign-ins for a configurable period.\n\n### Enabling Trust on Verification\n\nPass `trustDevice: true` when verifying 2FA:\n\n```ts\nawait authClient.twoFactor.verifyTotp({\n  code: \"123456\",\n  trustDevice: true,\n});\n```\n\n### Configuring Trust Duration\n\n```ts\ntwoFactor({\n  trustDeviceMaxAge: 30 * 24 * 60 * 60, // 30 days in seconds (default)\n});\n```\n\n**Note**: The trust period refreshes on each successful sign-in within the trust window.\n\n## Security Considerations\n\n### Session Management\n\nDuring the 2FA flow:\n\n1. User signs in with credentials\n2. Session cookie is removed (not yet authenticated)\n3. A temporary two-factor cookie is set (default: 10-minute expiration)\n4. User verifies via TOTP, OTP, or backup code\n5. Session cookie is created upon successful verification\n\nConfigure the two-factor cookie expiration:\n\n```ts\ntwoFactor({\n  twoFactorCookieMaxAge: 600, // 10 minutes in seconds (default)\n});\n```\n\n### Rate Limiting\n\nBetter Auth applies built-in rate limiting to all 2FA endpoints (3 requests per 10 seconds). For OTP verification, additional attempt limiting is applied:\n\n```ts\ntwoFactor({\n  otpOptions: {\n    allowedAttempts: 5, // Max attempts per OTP code (default: 5)\n  },\n});\n```\n\n### Encryption at Rest\n\n- TOTP secrets are encrypted using symmetric encryption with your auth secret\n- Backup codes are stored encrypted by default\n- OTP codes can be configured for plain, encrypted, or hashed storage\n\n### Constant-Time Comparison\n\nBetter Auth uses constant-time comparison for OTP verification to prevent timing attacks.\n\n### Credential Account Requirement\n\nTwo-factor authentication can only be enabled for credential (email/password) accounts. For social accounts, it's assumed the provider already handles 2FA.\n\n## Disabling 2FA\n\nAllow users to disable 2FA with password confirmation:\n\n```ts\nconst disable2FA = async (password: string) => {\n  const { data, error } = await authClient.twoFactor.disable({\n    password,\n  });\n};\n```\n\n**Note**: When 2FA is disabled, trusted device records are revoked.\n\n## Complete Configuration Example\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { twoFactor } from \"better-auth/plugins\";\nimport { sendEmail } from \"./email\";\n\nexport const auth = betterAuth({\n  appName: \"My App\",\n  plugins: [\n    twoFactor({\n      // TOTP settings\n      issuer: \"My App\",\n      totpOptions: {\n        digits: 6,\n        period: 30,\n      },\n      // OTP settings\n      otpOptions: {\n        sendOTP: async ({ user, otp }) => {\n          await sendEmail({\n            to: user.email,\n            subject: \"Your verification code\",\n            text: `Your code is: ${otp}`,\n          });\n        },\n        period: 5,\n        allowedAttempts: 5,\n        storeOTP: \"encrypted\",\n      },\n      // Backup code settings\n      backupCodeOptions: {\n        amount: 10,\n        length: 10,\n        storeBackupCodes: \"encrypted\",\n      },\n      // Session settings\n      twoFactorCookieMaxAge: 600, // 10 minutes\n      trustDeviceMaxAge: 30 * 24 * 60 * 60, // 30 days\n    }),\n  ],\n});\n```\n",".agents/skills/devmoji/SKILL.md":"---\nname: devmoji\ndescription: Devmoji commit message emoji conventions. Use when writing git commit messages, formatting commit descriptions, or choosing the right emoji prefix for a commit. Triggers on \"commit message\", \"git commit\", \"commit emoji\", \"devmoji\".\nuser-invocable: false\n---\n\n# Devmoji Commit Conventions\n\nThis project uses emoji prefixes in commit messages following devmoji/gitmoji conventions. Every commit message MUST start with an emoji followed by a space, then the description.\n\n## Format\n\n```\n<emoji> <Short description>\n```\n\n**Examples from this repo:**\n- `🐛 Fix hydration error on dashboard page`\n- `✨ Add support for in-memory rate limiting`\n- `♻️ Move locale sync`\n- `⚡️ Preload dayjs locales`\n- `💄 Disable overscroll`\n\n## Emoji Reference\n\n| Emoji | Code | When to use |\n|-------|------|-------------|\n| ✨ | `:sparkles:` | New feature or capability |\n| 🐛 | `:bug:` | Bug fix |\n| ♻️ | `:recycle:` | Refactoring (no feature or fix) |\n| ⚡️ | `:zap:` | Performance improvement |\n| 💄 | `:lipstick:` | UI/style/cosmetic change |\n| 🔧 | `:wrench:` | Configuration file change |\n| 🔥 | `:fire:` | Remove code or files |\n| 📝 | `:memo:` | Documentation |\n| 🌐 | `:globe_with_meridians:` | Internationalization/localization |\n| 🔒️ | `:lock:` | Security fix or improvement |\n| ⬆️ | `:arrow_up:` | Upgrade dependencies |\n| 🚀 | `:rocket:` | Deploy or release |\n| 🔖 | `:bookmark:` | Version tag / release |\n| 👷 | `:construction_worker:` | CI/CD changes |\n| 🧑‍💻 | `:technologist:` | Developer experience improvement |\n| 🌱 | `:seedling:` | Seed data / database seeding |\n| 🔊 | `:loud_sound:` | Add or update logs |\n| ➕ | `:heavy_plus_sign:` | Add dependency |\n| ➖ | `:heavy_minus_sign:` | Remove dependency |\n| 🚨 | `:rotating_light:` | Fix linting / compiler warnings |\n| ✅ | `:white_check_mark:` | Add or update tests |\n| 🏗️ | `:building_construction:` | Architectural change |\n| 📦 | `:package:` | Build system change |\n| 🗃️ | `:card_file_box:` | Database / migration change |\n| 🩹 | `:adhesive_bandage:` | Simple fix for non-critical issue |\n| ⏪️ | `:rewind:` | Revert changes |\n\n## Rules\n\n1. **Always prefix** the commit message with exactly one primary emoji\n2. **No conventional commit type prefix** — use `🐛 Fix bug` not `fix: 🐛 Fix bug`\n3. **Capitalize** the first word after the emoji\n4. **No period** at the end of the subject line\n5. **Keep it short** — subject line under 72 characters\n6. Pick the **most specific** emoji that applies (e.g. `💄` for a pure CSS change, not `✨`)\n",".agents/skills/organization-best-practices/SKILL.md":"---\nname: organization-best-practices\ndescription: This skill provides guidance and enforcement rules for implementing multi-tenant organizations, teams, and role-based access control using Better Auth's organization plugin.\n---\n\n## Setting Up Organizations\n\nWhen adding organizations to your application, configure the `organization` plugin with appropriate limits and permissions.\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { organization } from \"better-auth/plugins\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n      allowUserToCreateOrganization: true,\n      organizationLimit: 5, // Max orgs per user\n      membershipLimit: 100, // Max members per org\n    }),\n  ],\n});\n```\n\n**Note**: After adding the plugin, run `npx @better-auth/cli migrate` to add the required database tables.\n\n### Client-Side Setup\n\nAdd the client plugin to access organization methods:\n\n```ts\nimport { createAuthClient } from \"better-auth/client\";\nimport { organizationClient } from \"better-auth/client/plugins\";\n\nexport const authClient = createAuthClient({\n  plugins: [organizationClient()],\n});\n```\n\n## Creating Organizations\n\nOrganizations are the top-level entity for grouping users. When created, the creator is automatically assigned the `owner` role.\n\n```ts\nconst createOrg = async () => {\n  const { data, error } = await authClient.organization.create({\n    name: \"My Company\",\n    slug: \"my-company\",\n    logo: \"https://example.com/logo.png\",\n    metadata: { plan: \"pro\" },\n  });\n};\n```\n\n### Controlling Organization Creation\n\nRestrict who can create organizations based on user attributes:\n\n```ts\norganization({\n  allowUserToCreateOrganization: async (user) => {\n    return user.emailVerified === true;\n  },\n  organizationLimit: async (user) => {\n    // Premium users get more organizations\n    return user.plan === \"premium\" ? 20 : 3;\n  },\n});\n```\n\n### Creating Organizations on Behalf of Users\n\nAdministrators can create organizations for other users (server-side only):\n\n```ts\nawait auth.api.createOrganization({\n  body: {\n    name: \"Client Organization\",\n    slug: \"client-org\",\n    userId: \"user-id-who-will-be-owner\", // `userId` is required\n  },\n});\n```\n\n**Note**: The `userId` parameter cannot be used alongside session headers.\n\n\n## Active Organizations\n\nThe active organization is stored in the session and scopes subsequent API calls. Always set an active organization after the user selects one.\n\n```ts\nconst setActive = async (organizationId: string) => {\n  const { data, error } = await authClient.organization.setActive({\n    organizationId,\n  });\n};\n```\n\nMany endpoints use the active organization when `organizationId` is not provided:\n\n```ts\n// These use the active organization automatically\nawait authClient.organization.listMembers();\nawait authClient.organization.listInvitations();\nawait authClient.organization.inviteMember({ email: \"user@example.com\", role: \"member\" });\n```\n\n### Getting Full Organization Data\n\nRetrieve the active organization with all its members, invitations, and teams:\n\n```ts\nconst { data } = await authClient.organization.getFullOrganization();\n// data.organization, data.members, data.invitations, data.teams\n```\n\n## Members\n\nMembers are users who belong to an organization. Each member has a role that determines their permissions.\n\n### Adding Members (Server-Side)\n\nAdd members directly without invitations (useful for admin operations):\n\n```ts\nawait auth.api.addMember({\n  body: {\n    userId: \"user-id\",\n    role: \"member\",\n    organizationId: \"org-id\",\n  },\n});\n```\n\n**Note**: For client-side member additions, use the invitation system instead.\n\n### Assigning Multiple Roles\n\nMembers can have multiple roles for fine-grained permissions:\n\n```ts\nawait auth.api.addMember({\n  body: {\n    userId: \"user-id\",\n    role: [\"admin\", \"moderator\"],\n    organizationId: \"org-id\",\n  },\n});\n```\n\n### Removing Members\n\nRemove members by ID or email:\n\n```ts\nawait authClient.organization.removeMember({\n  memberIdOrEmail: \"user@example.com\",\n});\n```\n\n**Important**: The last owner cannot be removed. Assign the owner role to another member first.\n\n### Updating Member Roles\n\n```ts\nawait authClient.organization.updateMemberRole({\n  memberId: \"member-id\",\n  role: \"admin\",\n});\n```\n\n### Membership Limits\n\nControl the maximum number of members per organization:\n\n```ts\norganization({\n  membershipLimit: async (user, organization) => {\n    if (organization.metadata?.plan === \"enterprise\") {\n      return 1000;\n    }\n    return 50;\n  },\n});\n```\n\n## Invitations\n\nThe invitation system allows admins to invite users via email. Configure email sending to enable invitations.\n\n### Setting Up Invitation Emails\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { organization } from \"better-auth/plugins\";\nimport { sendEmail } from \"./email\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n      sendInvitationEmail: async (data) => {\n        const { email, organization, inviter, invitation } = data;\n\n        await sendEmail({\n          to: email,\n          subject: `Join ${organization.name}`,\n          html: `\n            <p>${inviter.user.name} invited you to join ${organization.name}</p>\n            <a href=\"https://yourapp.com/accept-invite?id=${invitation.id}\">\n              Accept Invitation\n            </a>\n          `,\n        });\n      },\n    }),\n  ],\n});\n```\n\n### Sending Invitations\n\n```ts\nawait authClient.organization.inviteMember({\n  email: \"newuser@example.com\",\n  role: \"member\",\n});\n```\n\n### Creating Shareable Invitation URLs\n\nFor sharing via Slack, SMS, or in-app notifications:\n\n```ts\nconst { data } = await authClient.organization.getInvitationURL({\n  email: \"newuser@example.com\",\n  role: \"member\",\n  callbackURL: \"https://yourapp.com/dashboard\",\n});\n\n// Share data.url via any channel\n```\n\n**Note**: This endpoint does not call `sendInvitationEmail`. Handle delivery yourself.\n\n### Accepting Invitations\n\n```ts\nawait authClient.organization.acceptInvitation({\n  invitationId: \"invitation-id\",\n});\n```\n\n### Invitation Configuration\n\n```ts\norganization({\n  invitationExpiresIn: 60 * 60 * 24 * 7, // 7 days (default: 48 hours)\n  invitationLimit: 100, // Max pending invitations per org\n  cancelPendingInvitationsOnReInvite: true, // Cancel old invites when re-inviting\n});\n```\n\n## Roles & Permissions\n\nThe plugin provides role-based access control (RBAC) with three default roles:\n\n| Role | Description |\n|------|-------------|\n| `owner` | Full access, can delete organization |\n| `admin` | Can manage members, invitations, settings |\n| `member` | Basic access to organization resources |\n\n\n### Checking Permissions\n\n```ts\nconst { data } = await authClient.organization.hasPermission({\n  permission: \"member:write\",\n});\n\nif (data?.hasPermission) {\n  // User can manage members\n}\n```\n\n### Client-Side Permission Checks\n\nFor UI rendering without API calls:\n\n```ts\nconst canManageMembers = authClient.organization.checkRolePermission({\n  role: \"admin\",\n  permissions: [\"member:write\"],\n});\n```\n\n**Note**: For dynamic access control, the client side role permission check will not work. Please use the `hasPermission` endpoint.\n\n## Teams\n\nTeams allow grouping members within an organization.\n\n### Enabling Teams\n\n```ts\nimport { organization } from \"better-auth/plugins\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n        teams: {\n            enabled: true\n        }\n    }),\n  ],\n});\n```\n\n### Creating Teams\n\n```ts\nconst { data } = await authClient.organization.createTeam({\n  name: \"Engineering\",\n});\n```\n\n### Managing Team Members\n\n```ts\n// Add a member to a team (must be org member first)\nawait authClient.organization.addTeamMember({\n  teamId: \"team-id\",\n  userId: \"user-id\",\n});\n\n// Remove from team (stays in org)\nawait authClient.organization.removeTeamMember({\n  teamId: \"team-id\",\n  userId: \"user-id\",\n});\n```\n\n### Active Teams\n\nSimilar to active organizations, set an active team for the session:\n\n```ts\nawait authClient.organization.setActiveTeam({\n  teamId: \"team-id\",\n});\n```\n\n### Team Limits\n\n```ts\norganization({\n  teams: {\n      maximumTeams: 20, // Max teams per org\n      maximumMembersPerTeam: 50, // Max members per team\n      allowRemovingAllTeams: false, // Prevent removing last team\n  }\n});\n```\n\n## Dynamic Access Control\n\nFor applications needing custom roles per organization at runtime, enable dynamic access control.\n\n### Enabling Dynamic Access Control\n\n```ts\nimport { organization } from \"better-auth/plugins\";\nimport { dynamicAccessControl } from \"@better-auth/organization/addons\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n        dynamicAccessControl: {\n            enabled: true\n        }\n    }),\n  ],\n});\n```\n\n### Creating Custom Roles\n\n```ts\nawait authClient.organization.createRole({\n  role: \"moderator\",\n  permission: {\n    member: [\"read\"],\n    invitation: [\"read\"],\n  },\n});\n```\n\n### Updating and Deleting Roles\n\n```ts\n// Update role permissions\nawait authClient.organization.updateRole({\n  roleId: \"role-id\",\n  permission: {\n    member: [\"read\", \"write\"],\n  },\n});\n\n// Delete a custom role\nawait authClient.organization.deleteRole({\n  roleId: \"role-id\",\n});\n```\n\n**Note**: Pre-defined roles (owner, admin, member) cannot be deleted. Roles assigned to members cannot be deleted until members are reassigned.\n\n## Lifecycle Hooks\n\nExecute custom logic at various points in the organization lifecycle:\n\n```ts\norganization({\n  hooks: {\n    organization: {\n      beforeCreate: async ({ data, user }) => {\n        // Validate or modify data before creation\n        return {\n          data: {\n            ...data,\n            metadata: { ...data.metadata, createdBy: user.id },\n          },\n        };\n      },\n      afterCreate: async ({ organization, member }) => {\n        // Post-creation logic (e.g., send welcome email, create default resources)\n        await createDefaultResources(organization.id);\n      },\n      beforeDelete: async ({ organization }) => {\n        // Cleanup before deletion\n        await archiveOrganizationData(organization.id);\n      },\n    },\n    member: {\n      afterCreate: async ({ member, organization }) => {\n        await notifyAdmins(organization.id, `New member joined`);\n      },\n    },\n    invitation: {\n      afterCreate: async ({ invitation, organization, inviter }) => {\n        await logInvitation(invitation);\n      },\n    },\n  },\n});\n```\n\n## Schema Customization\n\nCustomize table names, field names, and add additional fields:\n\n```ts\norganization({\n  schema: {\n    organization: {\n      modelName: \"workspace\", // Rename table\n      fields: {\n        name: \"workspaceName\", // Rename fields\n      },\n      additionalFields: {\n        billingId: {\n          type: \"string\",\n          required: false,\n        },\n      },\n    },\n    member: {\n      additionalFields: {\n        department: {\n          type: \"string\",\n          required: false,\n        },\n        title: {\n          type: \"string\",\n          required: false,\n        },\n      },\n    },\n  },\n});\n```\n\n## Security Considerations\n\n### Owner Protection\n\n- The last owner cannot be removed from an organization\n- The last owner cannot leave the organization\n- The owner role cannot be removed from the last owner\n\nAlways ensure ownership transfer before removing the current owner:\n\n```ts\n// Transfer ownership first\nawait authClient.organization.updateMemberRole({\n  memberId: \"new-owner-member-id\",\n  role: \"owner\",\n});\n\n// Then the previous owner can be demoted or removed\n```\n\n### Organization Deletion\n\nDeleting an organization removes all associated data (members, invitations, teams). Prevent accidental deletion:\n\n```ts\norganization({\n  disableOrganizationDeletion: true, // Disable via config\n});\n```\n\nOr implement soft delete via hooks:\n\n```ts\norganization({\n  hooks: {\n    organization: {\n      beforeDelete: async ({ organization }) => {\n        // Archive instead of delete\n        await archiveOrganization(organization.id);\n        throw new Error(\"Organization archived, not deleted\");\n      },\n    },\n  },\n});\n```\n\n### Invitation Security\n\n- Invitations expire after 48 hours by default\n- Only the invited email address can accept an invitation\n- Pending invitations can be cancelled by organization admins\n\n## Complete Configuration Example\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { organization } from \"better-auth/plugins\";\nimport { sendEmail } from \"./email\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n      // Organization limits\n      allowUserToCreateOrganization: true,\n      organizationLimit: 10,\n      membershipLimit: 100,\n      creatorRole: \"owner\",\n\n      // Slugs\n      defaultOrganizationIdField: \"slug\",\n\n      // Invitations\n      invitationExpiresIn: 60 * 60 * 24 * 7, // 7 days\n      invitationLimit: 50,\n      sendInvitationEmail: async (data) => {\n        await sendEmail({\n          to: data.email,\n          subject: `Join ${data.organization.name}`,\n          html: `<a href=\"https://app.com/invite/${data.invitation.id}\">Accept</a>`,\n        });\n      },\n\n      // Hooks\n      hooks: {\n        organization: {\n          afterCreate: async ({ organization }) => {\n            console.log(`Organization ${organization.name} created`);\n          },\n        },\n      },\n    }),\n  ],\n});\n```\n",".agents/skills/email-and-password-best-practices/SKILL.md":"---\nname: email-and-password-best-practices\ndescription: This skill provides guidance and enforcement rules for implementing secure email and password authentication using Better Auth.\n---\n\n## Email Verification Setup\n\nWhen enabling email/password authentication, configure `emailVerification.sendVerificationEmail` to verify user email addresses. This helps prevent fake sign-ups and ensures users have access to the email they registered with.\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { sendEmail } from \"./email\"; // your email sending function\n\nexport const auth = betterAuth({\n  emailVerification: {\n    sendVerificationEmail: async ({ user, url, token }, request) => {\n      await sendEmail({\n        to: user.email,\n        subject: \"Verify your email address\",\n        text: `Click the link to verify your email: ${url}`,\n      });\n    },\n  },\n});\n```\n\n**Note**: The `url` parameter contains the full verification link. The `token` is available if you need to build a custom verification URL.\n\n### Requiring Email Verification\n\nFor stricter security, enable `emailAndPassword.requireEmailVerification` to block sign-in until the user verifies their email. When enabled, unverified users will receive a new verification email on each sign-in attempt.\n\n```ts\nexport const auth = betterAuth({\n  emailAndPassword: {\n    requireEmailVerification: true,\n  },\n});\n```\n\n**Note**: This requires `sendVerificationEmail` to be configured and only applies to email/password sign-ins.\n\n## Client side validation\n\nWhile Better Auth validates inputs server-side, implementing client-side validation is still recommended for two key reasons:\n\n1. **Improved UX**: Users receive immediate feedback when inputs don't meet requirements, rather than waiting for a server round-trip.\n2. **Reduced server load**: Invalid requests are caught early, minimizing unnecessary network traffic to your auth server.\n\n## Callback URLs\n\nAlways use absolute URLs (including the origin) for callback URLs in sign-up and sign-in requests. This prevents Better Auth from needing to infer the origin, which can cause issues when your backend and frontend are on different domains.\n\n```ts\nconst { data, error } = await authClient.signUp.email({\n  callbackURL: \"https://example.com/callback\", // absolute URL with origin\n});\n```\n\n## Password Reset Flows\n\nPassword reset flows are essential to any email/password system, we recommend setting this up.\n\nTo allow users to reset a password first you need to provide `sendResetPassword` function to the email and password authenticator.\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { sendEmail } from \"./email\"; // your email sending function\n\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    // Custom email sending function to send reset-password email\n    sendResetPassword: async ({ user, url, token }, request) => {\n      void sendEmail({\n        to: user.email,\n        subject: \"Reset your password\",\n        text: `Click the link to reset your password: ${url}`,\n      });\n    },\n    // Optional event hook\n    onPasswordReset: async ({ user }, request) => {\n      // your logic here\n      console.log(`Password for user ${user.email} has been reset.`);\n    },\n  },\n});\n```\n\n### Security considerations\n\nBetter Auth implements several security measures in the password reset flow:\n\n#### Timing attack prevention\n\n- **Background email sending**: Better Auth uses `runInBackgroundOrAwait` internally to send reset emails without blocking the response. This prevents attackers from measuring response times to determine if an email exists.\n- **Dummy operations on invalid requests**: When a user is not found, Better Auth still performs token generation and a database lookup (with a dummy value) to maintain consistent response times.\n- **Constant response message**: The API always returns `\"If this email exists in our system, check your email for the reset link\"` regardless of whether the user exists.\n\nOn serverless platforms, configure a background task handler to ensure emails are sent reliably:\n\n```ts\nexport const auth = betterAuth({\n  advanced: {\n    backgroundTasks: {\n      handler: (promise) => {\n        // Use platform-specific methods like waitUntil\n        waitUntil(promise);\n      },\n    },\n  },\n});\n```\n\n#### Token security\n\n- **Cryptographically random tokens**: Reset tokens are generated using `generateId(24)`, producing a 24-character alphanumeric string (a-z, A-Z, 0-9) with high entropy.\n- **Token expiration**: Tokens expire after **1 hour** by default. Configure with `resetPasswordTokenExpiresIn` (in seconds):\n\n```ts\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    resetPasswordTokenExpiresIn: 60 * 30, // 30 minutes\n  },\n});\n```\n\n- **Single-use tokens**: Tokens are deleted immediately after successful password reset, preventing reuse.\n\n#### Session revocation\n\nEnable `revokeSessionsOnPasswordReset` to invalidate all existing sessions when a password is reset. This ensures that if an attacker has an active session, it will be terminated:\n\n```ts\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    revokeSessionsOnPasswordReset: true,\n  },\n});\n```\n\n#### Redirect URL validation\n\nThe `redirectTo` parameter is validated against your `trustedOrigins` configuration to prevent open redirect attacks. Malicious redirect URLs will be rejected with a 403 error.\n\n#### Password requirements\n\nDuring password reset, the new password must meet length requirements:\n- **Minimum**: 8 characters (default), configurable via `minPasswordLength`\n- **Maximum**: 128 characters (default), configurable via `maxPasswordLength`\n\n```ts\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    minPasswordLength: 12,\n    maxPasswordLength: 256,\n  },\n});\n```\n\n### Sending the password reset\n\nOnce the password reset configurations are set-up, you can now call the `requestPasswordReset` function to send reset password link to user. If the user exists, it will trigger the `sendResetPassword` function you provided in the auth config.\n\n```ts\nconst data = await auth.api.requestPasswordReset({\n  body: {\n    email: \"john.doe@example.com\", // required\n    redirectTo: \"https://example.com/reset-password\",\n  },\n});\n```\n\nOr authClient:\n\n```ts\nconst { data, error } = await authClient.requestPasswordReset({\n  email: \"john.doe@example.com\", // required\n  redirectTo: \"https://example.com/reset-password\",\n});\n```\n\n**Note**: While the `email` is required, we also recommend configuring the `redirectTo` for a smoother user experience.\n\n## Password Hashing\n\nBetter Auth uses `scrypt` by default for password hashing. This is a solid choice because:\n\n- It's designed to be slow and memory-intensive, making brute-force attacks costly\n- It's natively supported by Node.js (no external dependencies)\n- OWASP recommends it when Argon2id isn't available\n\n### Custom Hashing Algorithm\n\nTo use a different algorithm (e.g., Argon2id), provide custom `hash` and `verify` functions in the `emailAndPassword.password` configuration:\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { hash, verify, type Options } from \"@node-rs/argon2\";\n\nconst argon2Options: Options = {\n  memoryCost: 65536, // 64 MiB\n  timeCost: 3, // 3 iterations\n  parallelism: 4, // 4 parallel lanes\n  outputLen: 32, // 32 byte output\n  algorithm: 2, // Argon2id variant\n};\n\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    password: {\n      hash: (password) => hash(password, argon2Options),\n      verify: ({ password, hash: storedHash }) =>\n        verify(storedHash, password, argon2Options),\n    },\n  },\n});\n```\n\n**Note**: If you switch hashing algorithms on an existing system, users with passwords hashed using the old algorithm won't be able to sign in. Plan a migration strategy if needed.\n",".agents/skills/logging/SKILL.md":"---\nname: logging\ndescription: Wide events logging pattern and conventions for this project. Use when writing logging code, adding observability, or implementing request tracing.\nuser-invocable: false\n---\n\n# Logging Guidelines\n\nThis project follows the **Wide Events** pattern. Logs are optimized for querying, not writing.\n\n## Core Principle\n\nEmit **one comprehensive event per request per service** containing all contextual information. Do not scatter log statements throughout your code.\n\n**Wrong mental model:** Log what your code is doing\n**Correct mental model:** Log what happened to this request\n\n## Wide Event Structure\n\nEach event should include:\n\n```typescript\n{\n  // Request metadata\n  requestId: string,\n  traceId: string,\n  timestamp: Date,\n  service: string,\n\n  // HTTP details\n  method: string,\n  path: string,\n  statusCode: number,\n  durationMs: number,\n\n  // User context\n  userId?: string,\n  subscriptionTier?: string,\n\n  // Business context\n  pollId?: string,\n  spaceId?: string,\n  featureFlags: Record<string, boolean>,\n\n  // Error details (when applicable)\n  errorType?: string,\n  errorCode?: string,\n  errorMessage?: string,\n  isRetriable?: boolean,\n\n  // Performance metrics\n  dbQueryCount?: number,\n  dbQueryDurationMs?: number\n}\n```\n\n## Implementation Pattern\n\nUse middleware to build the event throughout the request lifecycle:\n\n1. Initialize event at request start with request/service metadata\n2. Enrich with user context after authentication\n3. Add business context as processing occurs\n4. Emit single event in finally block after request completes\n\n## Anti-Patterns\n\n**Never do these:**\n\n- Scattered `console.log` statements throughout code\n- String-based log messages without structured data\n- Low-context logs with only timestamp, level, and message\n- Multiple log lines for a single request\n\n**Example of what NOT to do:**\n\n```typescript\n// BAD - scattered, low-context logs\nconsole.log(\"Starting request\");\nconsole.log(\"User authenticated\");\nconsole.log(\"Fetching poll\");\nconsole.log(\"Request complete\");\n```\n\n**Example of correct approach:**\n\n```typescript\n// GOOD - single wide event with full context\nlogger.info({\n  requestId: ctx.requestId,\n  userId: ctx.user?.id,\n  pollId: params.pollId,\n  method: \"GET\",\n  path: \"/api/polls/:id\",\n  statusCode: 200,\n  durationMs: 45,\n  dbQueryCount: 2,\n  dbQueryDurationMs: 12\n});\n```\n\n## Tail Sampling\n\nWhen implementing log sampling, make decisions **after** request completion:\n\n**Always capture 100%:**\n- All errors (5xx status, exceptions)\n- Requests exceeding p99 latency\n- Pro/enterprise user requests\n- Requests with feature flags under rollout\n\n**Sample remaining traffic:** 1-5% of successful, fast requests\n\n## High-Cardinality Data\n\nInclude high-cardinality fields (userId, pollId, requestId). Modern observability tools handle this efficiently. Aim for 50+ fields per event. Include:\n\n- All IDs involved in the request\n- All feature flags evaluated\n- All external service calls made\n- Timing breakdown for significant operations\n- User tier and account metadata\n",".agents/skills/create-auth-skill/SKILL.md":"---\nname: create-auth-skill\ndescription: Skill for creating auth layers in TypeScript/JavaScript apps using Better Auth.\n---\n\n# Create Auth Skill\n\nGuide for adding authentication to TypeScript/JavaScript applications using Better Auth.\n\n**For code examples and syntax, see [better-auth.com/docs](https://better-auth.com/docs).**\n\n---\n\n## Phase 1: Planning (REQUIRED before implementation)\n\nBefore writing any code, gather requirements by scanning the project and asking the user structured questions. This ensures the implementation matches their needs.\n\n### Step 1: Scan the project\n\nAnalyze the codebase to auto-detect:\n- **Framework** — Look for `next.config`, `svelte.config`, `nuxt.config`, `astro.config`, `vite.config`, or Express/Hono entry files.\n- **Database/ORM** — Look for `prisma/schema.prisma`, `drizzle.config`, `package.json` deps (`pg`, `mysql2`, `better-sqlite3`, `mongoose`, `mongodb`).\n- **Existing auth** — Look for existing auth libraries (`next-auth`, `lucia`, `clerk`, `supabase/auth`, `firebase/auth`) in `package.json` or imports.\n- **Package manager** — Check for `pnpm-lock.yaml`, `yarn.lock`, `bun.lockb`, or `package-lock.json`.\n\nUse what you find to pre-fill defaults and skip questions you can already answer.\n\n### Step 2: Ask planning questions\n\nUse the `AskQuestion` tool to ask the user **all applicable questions in a single call**. Skip any question you already have a confident answer for from the scan. Group them under a title like \"Auth Setup Planning\".\n\n**Questions to ask:**\n\n1. **Project type** (skip if detected)\n   - Prompt: \"What type of project is this?\"\n   - Options: New project from scratch | Adding auth to existing project | Migrating from another auth library\n\n2. **Framework** (skip if detected)\n   - Prompt: \"Which framework are you using?\"\n   - Options: Next.js (App Router) | Next.js (Pages Router) | SvelteKit | Nuxt | Astro | Express | Hono | SolidStart | Other\n\n3. **Database & ORM** (skip if detected)\n   - Prompt: \"Which database setup will you use?\"\n   - Options: PostgreSQL (Prisma) | PostgreSQL (Drizzle) | PostgreSQL (pg driver) | MySQL (Prisma) | MySQL (Drizzle) | MySQL (mysql2 driver) | SQLite (Prisma) | SQLite (Drizzle) | SQLite (better-sqlite3 driver) | MongoDB (Mongoose) | MongoDB (native driver)\n\n4. **Authentication methods** (always ask, allow multiple)\n   - Prompt: \"Which sign-in methods do you need?\"\n   - Options: Email & password | Social OAuth (Google, GitHub, etc.) | Magic link (passwordless email) | Passkey (WebAuthn) | Phone number\n   - `allow_multiple: true`\n\n5. **Social providers** (only if they selected Social OAuth above — ask in a follow-up call)\n   - Prompt: \"Which social providers do you need?\"\n   - Options: Google | GitHub | Apple | Microsoft | Discord | Twitter/X\n   - `allow_multiple: true`\n\n6. **Email verification** (only if Email & password was selected above — ask in a follow-up call)\n   - Prompt: \"Do you want to require email verification?\"\n   - Options: Yes | No\n\n7. **Email provider** (only if email verification is Yes, or if Password reset is selected in features — ask in a follow-up call)\n   - Prompt: \"How do you want to send emails?\"\n   - Options: Resend | Mock it for now (console.log)\n\n8. **Features & plugins** (always ask, allow multiple)\n   - Prompt: \"Which additional features do you need?\"\n   - Options: Two-factor authentication (2FA) | Organizations / teams | Admin dashboard | API bearer tokens | Password reset | None of these\n   - `allow_multiple: true`\n\n9. **Auth pages** (always ask, allow multiple — pre-select based on earlier answers)\n   - Prompt: \"Which auth pages do you need?\"\n   - Options vary based on previous answers:\n     - Always available: Sign in | Sign up\n     - If Email & password selected: Forgot password | Reset password\n     - If email verification enabled: Email verification\n   - `allow_multiple: true`\n\n10. **Auth UI style** (always ask)\n   - Prompt: \"What style do you want for the auth pages? Pick one or describe your own.\"\n   - Options: Minimal & clean | Centered card with background | Split layout (form + hero image) | Floating / glassmorphism | Other (I'll describe)\n\n### Step 3: Summarize the plan\n\nAfter collecting answers, present a concise implementation plan as a markdown checklist. Example:\n\n```\n## Auth Implementation Plan\n\n- **Framework:** Next.js (App Router)\n- **Database:** PostgreSQL via Prisma\n- **Auth methods:** Email/password, Google OAuth, GitHub OAuth\n- **Plugins:** 2FA, Organizations, Email verification\n- **UI:** Custom forms\n\n### Steps\n1. Install `better-auth` and `@better-auth/cli`\n2. Create `lib/auth.ts` with server config\n3. Create `lib/auth-client.ts` with React client\n4. Set up route handler at `app/api/auth/[...all]/route.ts`\n5. Configure Prisma adapter and generate schema\n6. Add Google & GitHub OAuth providers\n7. Enable `twoFactor` and `organization` plugins\n8. Set up email verification handler\n9. Run migrations\n10. Create sign-in / sign-up pages\n```\n\nAsk the user to confirm the plan before proceeding to Phase 2.\n\n---\n\n## Phase 2: Implementation\n\nOnly proceed here after the user confirms the plan from Phase 1.\n\nFollow the decision tree below, guided by the answers collected above.\n\n```\nIs this a new/empty project?\n├─ YES → New project setup\n│   1. Install better-auth (+ scoped packages per plan)\n│   2. Create auth.ts with all planned config\n│   3. Create auth-client.ts with framework client\n│   4. Set up route handler\n│   5. Set up environment variables\n│   6. Run CLI migrate/generate\n│   7. Add plugins from plan\n│   8. Create auth UI pages\n│\n├─ MIGRATING → Migration from existing auth\n│   1. Audit current auth for gaps\n│   2. Plan incremental migration\n│   3. Install better-auth alongside existing auth\n│   4. Migrate routes, then session logic, then UI\n│   5. Remove old auth library\n│   6. See migration guides in docs\n│\n└─ ADDING → Add auth to existing project\n    1. Analyze project structure\n    2. Install better-auth\n    3. Create auth config matching plan\n    4. Add route handler\n    5. Run schema migrations\n    6. Integrate into existing pages\n    7. Add planned plugins and features\n```\n\nAt the end of implementation, guide users thoroughly on remaining next steps (e.g., setting up OAuth app credentials, deploying env vars, testing flows).\n\n---\n\n## Installation\n\n**Core:** `npm install better-auth`\n\n**Scoped packages (as needed):**\n| Package | Use case |\n|---------|----------|\n| `@better-auth/passkey` | WebAuthn/Passkey auth |\n| `@better-auth/sso` | SAML/OIDC enterprise SSO |\n| `@better-auth/stripe` | Stripe payments |\n| `@better-auth/scim` | SCIM user provisioning |\n| `@better-auth/expo` | React Native/Expo |\n\n---\n\n## Environment Variables\n\n```env\nBETTER_AUTH_SECRET=<32+ chars, generate with: openssl rand -base64 32>\nBETTER_AUTH_URL=http://localhost:3000\nDATABASE_URL=<your database connection string>\n```\n\nAdd OAuth secrets as needed: `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`, `GOOGLE_CLIENT_ID`, etc.\n\n---\n\n## Server Config (auth.ts)\n\n**Location:** `lib/auth.ts` or `src/lib/auth.ts`\n\n**Minimal config needs:**\n- `database` - Connection or adapter\n- `emailAndPassword: { enabled: true }` - For email/password auth\n\n**Standard config adds:**\n- `socialProviders` - OAuth providers (google, github, etc.)\n- `emailVerification.sendVerificationEmail` - Email verification handler\n- `emailAndPassword.sendResetPassword` - Password reset handler\n\n**Full config adds:**\n- `plugins` - Array of feature plugins\n- `session` - Expiry, cookie cache settings\n- `account.accountLinking` - Multi-provider linking\n- `rateLimit` - Rate limiting config\n\n**Export types:** `export type Session = typeof auth.$Infer.Session`\n\n---\n\n## Client Config (auth-client.ts)\n\n**Import by framework:**\n| Framework | Import |\n|-----------|--------|\n| React/Next.js | `better-auth/react` |\n| Vue | `better-auth/vue` |\n| Svelte | `better-auth/svelte` |\n| Solid | `better-auth/solid` |\n| Vanilla JS | `better-auth/client` |\n\n**Client plugins** go in `createAuthClient({ plugins: [...] })`.\n\n**Common exports:** `signIn`, `signUp`, `signOut`, `useSession`, `getSession`\n\n---\n\n## Route Handler Setup\n\n| Framework | File | Handler |\n|-----------|------|---------|\n| Next.js App Router | `app/api/auth/[...all]/route.ts` | `toNextJsHandler(auth)` → export `{ GET, POST }` |\n| Next.js Pages | `pages/api/auth/[...all].ts` | `toNextJsHandler(auth)` → default export |\n| Express | Any file | `app.all(\"/api/auth/*\", toNodeHandler(auth))` |\n| SvelteKit | `src/hooks.server.ts` | `svelteKitHandler(auth)` |\n| SolidStart | Route file | `solidStartHandler(auth)` |\n| Hono | Route file | `auth.handler(c.req.raw)` |\n\n**Next.js Server Components:** Add `nextCookies()` plugin to auth config.\n\n---\n\n## Database Migrations\n\n| Adapter | Command |\n|---------|---------|\n| Built-in Kysely | `npx @better-auth/cli@latest migrate` (applies directly) |\n| Prisma | `npx @better-auth/cli@latest generate --output prisma/schema.prisma` then `npx prisma migrate dev` |\n| Drizzle | `npx @better-auth/cli@latest generate --output src/db/auth-schema.ts` then `npx drizzle-kit push` |\n\n**Re-run after adding plugins.**\n\n---\n\n## Database Adapters\n\n| Database | Setup |\n|----------|-------|\n| SQLite | Pass `better-sqlite3` or `bun:sqlite` instance directly |\n| PostgreSQL | Pass `pg.Pool` instance directly |\n| MySQL | Pass `mysql2` pool directly |\n| Prisma | `prismaAdapter(prisma, { provider: \"postgresql\" })` from `better-auth/adapters/prisma` |\n| Drizzle | `drizzleAdapter(db, { provider: \"pg\" })` from `better-auth/adapters/drizzle` |\n| MongoDB | `mongodbAdapter(db)` from `better-auth/adapters/mongodb` |\n\n---\n\n## Common Plugins\n\n| Plugin | Server Import | Client Import | Purpose |\n|--------|---------------|---------------|---------|\n| `twoFactor` | `better-auth/plugins` | `twoFactorClient` | 2FA with TOTP/OTP |\n| `organization` | `better-auth/plugins` | `organizationClient` | Teams/orgs |\n| `admin` | `better-auth/plugins` | `adminClient` | User management |\n| `bearer` | `better-auth/plugins` | - | API token auth |\n| `openAPI` | `better-auth/plugins` | - | API docs |\n| `passkey` | `@better-auth/passkey` | `passkeyClient` | WebAuthn |\n| `sso` | `@better-auth/sso` | - | Enterprise SSO |\n\n**Plugin pattern:** Server plugin + client plugin + run migrations.\n\n---\n\n## Auth UI Implementation\n\n**Sign in flow:**\n1. `signIn.email({ email, password })` or `signIn.social({ provider, callbackURL })`\n2. Handle `error` in response\n3. Redirect on success\n\n**Session check (client):** `useSession()` hook returns `{ data: session, isPending }`\n\n**Session check (server):** `auth.api.getSession({ headers: await headers() })`\n\n**Protected routes:** Check session, redirect to `/sign-in` if null.\n\n---\n\n## Security Checklist\n\n- [ ] `BETTER_AUTH_SECRET` set (32+ chars)\n- [ ] `advanced.useSecureCookies: true` in production\n- [ ] `trustedOrigins` configured\n- [ ] Rate limits enabled\n- [ ] Email verification enabled\n- [ ] Password reset implemented\n- [ ] 2FA for sensitive apps\n- [ ] CSRF protection NOT disabled\n- [ ] `account.accountLinking` reviewed\n\n---\n\n## Troubleshooting\n\n| Issue | Fix |\n|-------|-----|\n| \"Secret not set\" | Add `BETTER_AUTH_SECRET` env var |\n| \"Invalid Origin\" | Add domain to `trustedOrigins` |\n| Cookies not setting | Check `baseURL` matches domain; enable secure cookies in prod |\n| OAuth callback errors | Verify redirect URIs in provider dashboard |\n| Type errors after adding plugin | Re-run CLI generate/migrate |\n\n---\n\n## Resources\n\n- [Docs](https://better-auth.com/docs)\n- [Examples](https://github.com/better-auth/examples)\n- [Plugins](https://better-auth.com/docs/concepts/plugins)\n- [CLI](https://better-auth.com/docs/concepts/cli)\n- [Migration Guides](https://better-auth.com/docs/guides)\n",".agents/skills/better-auth-security-best-practices/SKILL.MD":"# better-auth-security-best-practices\n\nPath: `.agents/skills/better-auth-security-best-practices/SKILL.MD`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/better-auth-security-best-practices/SKILL.MD)",".agents/skills/better-auth-best-practices/SKILL.md":"# better-auth-best-practices\n\nPath: `.agents/skills/better-auth-best-practices/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/better-auth-best-practices/SKILL.md)",".cursorrules":"# Cursor IDE Native Rules\n\nPath: `.cursorrules`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.cursorrules)","CLAUDE.md":"# Claude Agent Guidelines & System Prompt\n\nPath: `CLAUDE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/CLAUDE.md)","packages/utils/biome.json":"# Utils Skill\n\nPath: `packages/utils/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/utils/biome.json)","packages/utils/package.json":"# Utils Skill\n\nPath: `packages/utils/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/utils/package.json)","packages/utils/tsconfig.json":"# Utils Skill\n\nPath: `packages/utils/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/utils/tsconfig.json)","packages/ui/biome.json":"# Ui Skill\n\nPath: `packages/ui/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/ui/biome.json)","packages/ui/components.json":"# Ui Skill\n\nPath: `packages/ui/components.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/ui/components.json)","packages/ui/package.json":"# Ui Skill\n\nPath: `packages/ui/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/ui/package.json)","packages/ui/tsconfig.json":"# Ui Skill\n\nPath: `packages/ui/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/ui/tsconfig.json)","packages/tsconfig/next.json":"# Tsconfig Skill\n\nPath: `packages/tsconfig/next.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tsconfig/next.json)","packages/tsconfig/node.json":"# Tsconfig Skill\n\nPath: `packages/tsconfig/node.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tsconfig/node.json)","packages/tsconfig/package.json":"# Tsconfig Skill\n\nPath: `packages/tsconfig/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tsconfig/package.json)","packages/tsconfig/react.json":"# Tsconfig Skill\n\nPath: `packages/tsconfig/react.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tsconfig/react.json)","packages/test-helpers/package.json":"# Test-helpers Skill\n\nPath: `packages/test-helpers/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/test-helpers/package.json)","packages/test-helpers/tsconfig.json":"# Test-helpers Skill\n\nPath: `packages/test-helpers/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/test-helpers/tsconfig.json)","packages/tailwind-config/package.json":"# Tailwind-config Skill\n\nPath: `packages/tailwind-config/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tailwind-config/package.json)","packages/screenshots/package.json":"# Screenshots Skill\n\nPath: `packages/screenshots/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/screenshots/package.json)","packages/screenshots/tsconfig.json":"# Screenshots Skill\n\nPath: `packages/screenshots/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/screenshots/tsconfig.json)","packages/posthog/biome.json":"# Posthog Skill\n\nPath: `packages/posthog/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/posthog/biome.json)","packages/posthog/package.json":"# Posthog Skill\n\nPath: `packages/posthog/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/posthog/package.json)","packages/posthog/tsconfig.json":"# Posthog Skill\n\nPath: `packages/posthog/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/posthog/tsconfig.json)","packages/logger/package.json":"# Logger Skill\n\nPath: `packages/logger/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/logger/package.json)","packages/logger/tsconfig.json":"# Logger Skill\n\nPath: `packages/logger/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/logger/tsconfig.json)","packages/languages/biome.json":"# Languages Skill\n\nPath: `packages/languages/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/languages/biome.json)","packages/languages/package.json":"# Languages Skill\n\nPath: `packages/languages/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/languages/package.json)","packages/languages/tsconfig.json":"# Languages Skill\n\nPath: `packages/languages/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/languages/tsconfig.json)","packages/emails/biome.json":"# Emails Skill\n\nPath: `packages/emails/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/biome.json)","packages/emails/package.json":"# Emails Skill\n\nPath: `packages/emails/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/package.json)","packages/emails/tsconfig.json":"# Emails Skill\n\nPath: `packages/emails/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/tsconfig.json)","packages/dayjs/biome.json":"# Dayjs Skill\n\nPath: `packages/dayjs/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/dayjs/biome.json)","packages/dayjs/package.json":"# Dayjs Skill\n\nPath: `packages/dayjs/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/dayjs/package.json)","packages/database/biome.json":"# Database Skill\n\nPath: `packages/database/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/database/biome.json)","packages/database/package.json":"# Database Skill\n\nPath: `packages/database/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/database/package.json)","packages/database/tsconfig.json":"# Database Skill\n\nPath: `packages/database/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/database/tsconfig.json)","packages/billing/biome.json":"# Billing Skill\n\nPath: `packages/billing/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/billing/biome.json)","packages/billing/package.json":"# Billing Skill\n\nPath: `packages/billing/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/billing/package.json)","packages/billing/tsconfig.json":"# Billing Skill\n\nPath: `packages/billing/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/billing/tsconfig.json)",".agents/skills/prisma-upgrade-v7/references/accelerate-users.md":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/accelerate-users.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/accelerate-users.md)",".agents/skills/prisma-upgrade-v7/references/driver-adapters.md":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/driver-adapters.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/driver-adapters.md)",".agents/skills/prisma-upgrade-v7/references/env-variables.md":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/env-variables.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/env-variables.md)",".agents/skills/prisma-upgrade-v7/references/esm-support.md":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/esm-support.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/esm-support.md)",".agents/skills/prisma-upgrade-v7/references/prisma-config.md":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/prisma-config.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/prisma-config.md)",".agents/skills/prisma-upgrade-v7/references/removed-features.md":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/removed-features.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/removed-features.md)",".agents/skills/prisma-upgrade-v7/references/schema-changes.md":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/schema-changes.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/schema-changes.md)",".agents/skills/prisma-postgres/references/console-and-connections.md":"# References Skill\n\nPath: `.agents/skills/prisma-postgres/references/console-and-connections.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-postgres/references/console-and-connections.md)",".agents/skills/prisma-postgres/references/create-db-cli.md":"# References Skill\n\nPath: `.agents/skills/prisma-postgres/references/create-db-cli.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-postgres/references/create-db-cli.md)",".agents/skills/prisma-postgres/references/management-api.md":"# References Skill\n\nPath: `.agents/skills/prisma-postgres/references/management-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-postgres/references/management-api.md)",".agents/skills/prisma-postgres/references/management-api-sdk.md":"# References Skill\n\nPath: `.agents/skills/prisma-postgres/references/management-api-sdk.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-postgres/references/management-api-sdk.md)",".agents/skills/prisma-database-setup/references/cockroachdb.md":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/cockroachdb.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/cockroachdb.md)",".agents/skills/prisma-database-setup/references/mongodb.md":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/mongodb.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/mongodb.md)",".agents/skills/prisma-database-setup/references/mysql.md":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/mysql.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/mysql.md)",".agents/skills/prisma-database-setup/references/postgresql.md":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/postgresql.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/postgresql.md)",".agents/skills/prisma-database-setup/references/prisma-client-setup.md":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/prisma-client-setup.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/prisma-client-setup.md)",".agents/skills/prisma-database-setup/references/prisma-postgres.md":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/prisma-postgres.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/prisma-postgres.md)",".agents/skills/prisma-database-setup/references/sqlite.md":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/sqlite.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/sqlite.md)",".agents/skills/prisma-database-setup/references/sqlserver.md":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/sqlserver.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/sqlserver.md)",".agents/skills/prisma-client-api/references/client-methods.md":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/client-methods.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/client-methods.md)",".agents/skills/prisma-client-api/references/constructor.md":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/constructor.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/constructor.md)",".agents/skills/prisma-client-api/references/filters.md":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/filters.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/filters.md)",".agents/skills/prisma-client-api/references/model-queries.md":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/model-queries.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/model-queries.md)",".agents/skills/prisma-client-api/references/query-options.md":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/query-options.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/query-options.md)",".agents/skills/prisma-client-api/references/raw-queries.md":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/raw-queries.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/raw-queries.md)",".agents/skills/prisma-client-api/references/relations.md":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/relations.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/relations.md)",".agents/skills/prisma-client-api/references/transactions.md":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/transactions.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/transactions.md)",".agents/skills/prisma-cli/references/db-execute.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/db-execute.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-execute.md)",".agents/skills/prisma-cli/references/db-pull.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/db-pull.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-pull.md)",".agents/skills/prisma-cli/references/db-push.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/db-push.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-push.md)",".agents/skills/prisma-cli/references/db-seed.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/db-seed.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-seed.md)",".agents/skills/prisma-cli/references/debug.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/debug.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/debug.md)",".agents/skills/prisma-cli/references/dev.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/dev.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/dev.md)",".agents/skills/prisma-cli/references/format.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/format.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/format.md)",".agents/skills/prisma-cli/references/generate.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/generate.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/generate.md)",".agents/skills/prisma-cli/references/init.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/init.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/init.md)",".agents/skills/prisma-cli/references/migrate-deploy.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-deploy.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-deploy.md)",".agents/skills/prisma-cli/references/migrate-dev.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-dev.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-dev.md)",".agents/skills/prisma-cli/references/migrate-diff.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-diff.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-diff.md)",".agents/skills/prisma-cli/references/migrate-reset.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-reset.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-reset.md)",".agents/skills/prisma-cli/references/migrate-resolve.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-resolve.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-resolve.md)",".agents/skills/prisma-cli/references/migrate-status.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-status.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-status.md)",".agents/skills/prisma-cli/references/studio.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/studio.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/studio.md)",".agents/skills/prisma-cli/references/validate.md":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/validate.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/validate.md)","packages/emails/locales/zh/emails.json":"# Zh Skill\n\nPath: `packages/emails/locales/zh/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/zh/emails.json)","packages/emails/locales/sv/emails.json":"# Sv Skill\n\nPath: `packages/emails/locales/sv/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/sv/emails.json)","packages/emails/locales/ru/emails.json":"# Ru Skill\n\nPath: `packages/emails/locales/ru/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/ru/emails.json)","packages/emails/locales/pt-BR/emails.json":"# Pt-BR Skill\n\nPath: `packages/emails/locales/pt-BR/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/pt-BR/emails.json)","packages/emails/locales/pl/emails.json":"# Pl Skill\n\nPath: `packages/emails/locales/pl/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/pl/emails.json)","packages/emails/locales/no/emails.json":"# No Skill\n\nPath: `packages/emails/locales/no/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/no/emails.json)","packages/emails/locales/nl/emails.json":"# Nl Skill\n\nPath: `packages/emails/locales/nl/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/nl/emails.json)","packages/emails/locales/it/emails.json":"# It Skill\n\nPath: `packages/emails/locales/it/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/it/emails.json)","packages/emails/locales/hu/emails.json":"# Hu Skill\n\nPath: `packages/emails/locales/hu/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/hu/emails.json)","packages/emails/locales/fr/emails.json":"# Fr Skill\n\nPath: `packages/emails/locales/fr/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/fr/emails.json)","packages/emails/locales/fi/emails.json":"# Fi Skill\n\nPath: `packages/emails/locales/fi/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/fi/emails.json)","packages/emails/locales/es/emails.json":"# Es Skill\n\nPath: `packages/emails/locales/es/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/es/emails.json)","packages/emails/locales/en/emails.json":"# En Skill\n\nPath: `packages/emails/locales/en/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/en/emails.json)","packages/emails/locales/de/emails.json":"# De Skill\n\nPath: `packages/emails/locales/de/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/de/emails.json)","packages/emails/locales/da/emails.json":"# Da Skill\n\nPath: `packages/emails/locales/da/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/da/emails.json)","packages/emails/locales/cs/emails.json":"# Cs Skill\n\nPath: `packages/emails/locales/cs/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/cs/emails.json)"},"items":[{"name":"SKILL.md","path":".agents/skills/better-auth-best-practices/SKILL.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/better-auth-best-practices/SKILL.md","title":"better-auth-best-practices","category":"anthropic-skill","format":"markdown","content":"# better-auth-best-practices\n\nPath: `.agents/skills/better-auth-best-practices/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/better-auth-best-practices/SKILL.md)","tokens":300,"sizeBytes":226},{"name":"SKILL.MD","path":".agents/skills/better-auth-security-best-practices/SKILL.MD","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/better-auth-security-best-practices/SKILL.MD","title":"better-auth-security-best-practices","category":"anthropic-skill","format":"markdown","content":"# better-auth-security-best-practices\n\nPath: `.agents/skills/better-auth-security-best-practices/SKILL.MD`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/better-auth-security-best-practices/SKILL.MD)","tokens":300,"sizeBytes":253},{"name":"SKILL.md","path":".agents/skills/create-auth-skill/SKILL.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/create-auth-skill/SKILL.md","title":"create-auth-skill","category":"anthropic-skill","format":"markdown","content":"---\nname: create-auth-skill\ndescription: Skill for creating auth layers in TypeScript/JavaScript apps using Better Auth.\n---\n\n# Create Auth Skill\n\nGuide for adding authentication to TypeScript/JavaScript applications using Better Auth.\n\n**For code examples and syntax, see [better-auth.com/docs](https://better-auth.com/docs).**\n\n---\n\n## Phase 1: Planning (REQUIRED before implementation)\n\nBefore writing any code, gather requirements by scanning the project and asking the user structured questions. This ensures the implementation matches their needs.\n\n### Step 1: Scan the project\n\nAnalyze the codebase to auto-detect:\n- **Framework** — Look for `next.config`, `svelte.config`, `nuxt.config`, `astro.config`, `vite.config`, or Express/Hono entry files.\n- **Database/ORM** — Look for `prisma/schema.prisma`, `drizzle.config`, `package.json` deps (`pg`, `mysql2`, `better-sqlite3`, `mongoose`, `mongodb`).\n- **Existing auth** — Look for existing auth libraries (`next-auth`, `lucia`, `clerk`, `supabase/auth`, `firebase/auth`) in `package.json` or imports.\n- **Package manager** — Check for `pnpm-lock.yaml`, `yarn.lock`, `bun.lockb`, or `package-lock.json`.\n\nUse what you find to pre-fill defaults and skip questions you can already answer.\n\n### Step 2: Ask planning questions\n\nUse the `AskQuestion` tool to ask the user **all applicable questions in a single call**. Skip any question you already have a confident answer for from the scan. Group them under a title like \"Auth Setup Planning\".\n\n**Questions to ask:**\n\n1. **Project type** (skip if detected)\n   - Prompt: \"What type of project is this?\"\n   - Options: New project from scratch | Adding auth to existing project | Migrating from another auth library\n\n2. **Framework** (skip if detected)\n   - Prompt: \"Which framework are you using?\"\n   - Options: Next.js (App Router) | Next.js (Pages Router) | SvelteKit | Nuxt | Astro | Express | Hono | SolidStart | Other\n\n3. **Database & ORM** (skip if detected)\n   - Prompt: \"Which database setup will you use?\"\n   - Options: PostgreSQL (Prisma) | PostgreSQL (Drizzle) | PostgreSQL (pg driver) | MySQL (Prisma) | MySQL (Drizzle) | MySQL (mysql2 driver) | SQLite (Prisma) | SQLite (Drizzle) | SQLite (better-sqlite3 driver) | MongoDB (Mongoose) | MongoDB (native driver)\n\n4. **Authentication methods** (always ask, allow multiple)\n   - Prompt: \"Which sign-in methods do you need?\"\n   - Options: Email & password | Social OAuth (Google, GitHub, etc.) | Magic link (passwordless email) | Passkey (WebAuthn) | Phone number\n   - `allow_multiple: true`\n\n5. **Social providers** (only if they selected Social OAuth above — ask in a follow-up call)\n   - Prompt: \"Which social providers do you need?\"\n   - Options: Google | GitHub | Apple | Microsoft | Discord | Twitter/X\n   - `allow_multiple: true`\n\n6. **Email verification** (only if Email & password was selected above — ask in a follow-up call)\n   - Prompt: \"Do you want to require email verification?\"\n   - Options: Yes | No\n\n7. **Email provider** (only if email verification is Yes, or if Password reset is selected in features — ask in a follow-up call)\n   - Prompt: \"How do you want to send emails?\"\n   - Options: Resend | Mock it for now (console.log)\n\n8. **Features & plugins** (always ask, allow multiple)\n   - Prompt: \"Which additional features do you need?\"\n   - Options: Two-factor authentication (2FA) | Organizations / teams | Admin dashboard | API bearer tokens | Password reset | None of these\n   - `allow_multiple: true`\n\n9. **Auth pages** (always ask, allow multiple — pre-select based on earlier answers)\n   - Prompt: \"Which auth pages do you need?\"\n   - Options vary based on previous answers:\n     - Always available: Sign in | Sign up\n     - If Email & password selected: Forgot password | Reset password\n     - If email verification enabled: Email verification\n   - `allow_multiple: true`\n\n10. **Auth UI style** (always ask)\n   - Prompt: \"What style do you want for the auth pages? Pick one or describe your own.\"\n   - Options: Minimal & clean | Centered card with background | Split layout (form + hero image) | Floating / glassmorphism | Other (I'll describe)\n\n### Step 3: Summarize the plan\n\nAfter collecting answers, present a concise implementation plan as a markdown checklist. Example:\n\n```\n## Auth Implementation Plan\n\n- **Framework:** Next.js (App Router)\n- **Database:** PostgreSQL via Prisma\n- **Auth methods:** Email/password, Google OAuth, GitHub OAuth\n- **Plugins:** 2FA, Organizations, Email verification\n- **UI:** Custom forms\n\n### Steps\n1. Install `better-auth` and `@better-auth/cli`\n2. Create `lib/auth.ts` with server config\n3. Create `lib/auth-client.ts` with React client\n4. Set up route handler at `app/api/auth/[...all]/route.ts`\n5. Configure Prisma adapter and generate schema\n6. Add Google & GitHub OAuth providers\n7. Enable `twoFactor` and `organization` plugins\n8. Set up email verification handler\n9. Run migrations\n10. Create sign-in / sign-up pages\n```\n\nAsk the user to confirm the plan before proceeding to Phase 2.\n\n---\n\n## Phase 2: Implementation\n\nOnly proceed here after the user confirms the plan from Phase 1.\n\nFollow the decision tree below, guided by the answers collected above.\n\n```\nIs this a new/empty project?\n├─ YES → New project setup\n│   1. Install better-auth (+ scoped packages per plan)\n│   2. Create auth.ts with all planned config\n│   3. Create auth-client.ts with framework client\n│   4. Set up route handler\n│   5. Set up environment variables\n│   6. Run CLI migrate/generate\n│   7. Add plugins from plan\n│   8. Create auth UI pages\n│\n├─ MIGRATING → Migration from existing auth\n│   1. Audit current auth for gaps\n│   2. Plan incremental migration\n│   3. Install better-auth alongside existing auth\n│   4. Migrate routes, then session logic, then UI\n│   5. Remove old auth library\n│   6. See migration guides in docs\n│\n└─ ADDING → Add auth to existing project\n    1. Analyze project structure\n    2. Install better-auth\n    3. Create auth config matching plan\n    4. Add route handler\n    5. Run schema migrations\n    6. Integrate into existing pages\n    7. Add planned plugins and features\n```\n\nAt the end of implementation, guide users thoroughly on remaining next steps (e.g., setting up OAuth app credentials, deploying env vars, testing flows).\n\n---\n\n## Installation\n\n**Core:** `npm install better-auth`\n\n**Scoped packages (as needed):**\n| Package | Use case |\n|---------|----------|\n| `@better-auth/passkey` | WebAuthn/Passkey auth |\n| `@better-auth/sso` | SAML/OIDC enterprise SSO |\n| `@better-auth/stripe` | Stripe payments |\n| `@better-auth/scim` | SCIM user provisioning |\n| `@better-auth/expo` | React Native/Expo |\n\n---\n\n## Environment Variables\n\n```env\nBETTER_AUTH_SECRET=<32+ chars, generate with: openssl rand -base64 32>\nBETTER_AUTH_URL=http://localhost:3000\nDATABASE_URL=<your database connection string>\n```\n\nAdd OAuth secrets as needed: `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`, `GOOGLE_CLIENT_ID`, etc.\n\n---\n\n## Server Config (auth.ts)\n\n**Location:** `lib/auth.ts` or `src/lib/auth.ts`\n\n**Minimal config needs:**\n- `database` - Connection or adapter\n- `emailAndPassword: { enabled: true }` - For email/password auth\n\n**Standard config adds:**\n- `socialProviders` - OAuth providers (google, github, etc.)\n- `emailVerification.sendVerificationEmail` - Email verification handler\n- `emailAndPassword.sendResetPassword` - Password reset handler\n\n**Full config adds:**\n- `plugins` - Array of feature plugins\n- `session` - Expiry, cookie cache settings\n- `account.accountLinking` - Multi-provider linking\n- `rateLimit` - Rate limiting config\n\n**Export types:** `export type Session = typeof auth.$Infer.Session`\n\n---\n\n## Client Config (auth-client.ts)\n\n**Import by framework:**\n| Framework | Import |\n|-----------|--------|\n| React/Next.js | `better-auth/react` |\n| Vue | `better-auth/vue` |\n| Svelte | `better-auth/svelte` |\n| Solid | `better-auth/solid` |\n| Vanilla JS | `better-auth/client` |\n\n**Client plugins** go in `createAuthClient({ plugins: [...] })`.\n\n**Common exports:** `signIn`, `signUp`, `signOut`, `useSession`, `getSession`\n\n---\n\n## Route Handler Setup\n\n| Framework | File | Handler |\n|-----------|------|---------|\n| Next.js App Router | `app/api/auth/[...all]/route.ts` | `toNextJsHandler(auth)` → export `{ GET, POST }` |\n| Next.js Pages | `pages/api/auth/[...all].ts` | `toNextJsHandler(auth)` → default export |\n| Express | Any file | `app.all(\"/api/auth/*\", toNodeHandler(auth))` |\n| SvelteKit | `src/hooks.server.ts` | `svelteKitHandler(auth)` |\n| SolidStart | Route file | `solidStartHandler(auth)` |\n| Hono | Route file | `auth.handler(c.req.raw)` |\n\n**Next.js Server Components:** Add `nextCookies()` plugin to auth config.\n\n---\n\n## Database Migrations\n\n| Adapter | Command |\n|---------|---------|\n| Built-in Kysely | `npx @better-auth/cli@latest migrate` (applies directly) |\n| Prisma | `npx @better-auth/cli@latest generate --output prisma/schema.prisma` then `npx prisma migrate dev` |\n| Drizzle | `npx @better-auth/cli@latest generate --output src/db/auth-schema.ts` then `npx drizzle-kit push` |\n\n**Re-run after adding plugins.**\n\n---\n\n## Database Adapters\n\n| Database | Setup |\n|----------|-------|\n| SQLite | Pass `better-sqlite3` or `bun:sqlite` instance directly |\n| PostgreSQL | Pass `pg.Pool` instance directly |\n| MySQL | Pass `mysql2` pool directly |\n| Prisma | `prismaAdapter(prisma, { provider: \"postgresql\" })` from `better-auth/adapters/prisma` |\n| Drizzle | `drizzleAdapter(db, { provider: \"pg\" })` from `better-auth/adapters/drizzle` |\n| MongoDB | `mongodbAdapter(db)` from `better-auth/adapters/mongodb` |\n\n---\n\n## Common Plugins\n\n| Plugin | Server Import | Client Import | Purpose |\n|--------|---------------|---------------|---------|\n| `twoFactor` | `better-auth/plugins` | `twoFactorClient` | 2FA with TOTP/OTP |\n| `organization` | `better-auth/plugins` | `organizationClient` | Teams/orgs |\n| `admin` | `better-auth/plugins` | `adminClient` | User management |\n| `bearer` | `better-auth/plugins` | - | API token auth |\n| `openAPI` | `better-auth/plugins` | - | API docs |\n| `passkey` | `@better-auth/passkey` | `passkeyClient` | WebAuthn |\n| `sso` | `@better-auth/sso` | - | Enterprise SSO |\n\n**Plugin pattern:** Server plugin + client plugin + run migrations.\n\n---\n\n## Auth UI Implementation\n\n**Sign in flow:**\n1. `signIn.email({ email, password })` or `signIn.social({ provider, callbackURL })`\n2. Handle `error` in response\n3. Redirect on success\n\n**Session check (client):** `useSession()` hook returns `{ data: session, isPending }`\n\n**Session check (server):** `auth.api.getSession({ headers: await headers() })`\n\n**Protected routes:** Check session, redirect to `/sign-in` if null.\n\n---\n\n## Security Checklist\n\n- [ ] `BETTER_AUTH_SECRET` set (32+ chars)\n- [ ] `advanced.useSecureCookies: true` in production\n- [ ] `trustedOrigins` configured\n- [ ] Rate limits enabled\n- [ ] Email verification enabled\n- [ ] Password reset implemented\n- [ ] 2FA for sensitive apps\n- [ ] CSRF protection NOT disabled\n- [ ] `account.accountLinking` reviewed\n\n---\n\n## Troubleshooting\n\n| Issue | Fix |\n|-------|-----|\n| \"Secret not set\" | Add `BETTER_AUTH_SECRET` env var |\n| \"Invalid Origin\" | Add domain to `trustedOrigins` |\n| Cookies not setting | Check `baseURL` matches domain; enable secure cookies in prod |\n| OAuth callback errors | Verify redirect URIs in provider dashboard |\n| Type errors after adding plugin | Re-run CLI generate/migrate |\n\n---\n\n## Resources\n\n- [Docs](https://better-auth.com/docs)\n- [Examples](https://github.com/better-auth/examples)\n- [Plugins](https://better-auth.com/docs/concepts/plugins)\n- [CLI](https://better-auth.com/docs/concepts/cli)\n- [Migration Guides](https://better-auth.com/docs/guides)\n","frontmatter":{"name":"create-auth-skill","description":"Skill for creating auth layers in TypeScript/JavaScript apps using Better Auth."},"isInternal":false,"tokens":2994,"sizeBytes":11780},{"name":"SKILL.md","path":".agents/skills/devmoji/SKILL.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/devmoji/SKILL.md","title":"devmoji","category":"anthropic-skill","format":"markdown","content":"---\nname: devmoji\ndescription: Devmoji commit message emoji conventions. Use when writing git commit messages, formatting commit descriptions, or choosing the right emoji prefix for a commit. Triggers on \"commit message\", \"git commit\", \"commit emoji\", \"devmoji\".\nuser-invocable: false\n---\n\n# Devmoji Commit Conventions\n\nThis project uses emoji prefixes in commit messages following devmoji/gitmoji conventions. Every commit message MUST start with an emoji followed by a space, then the description.\n\n## Format\n\n```\n<emoji> <Short description>\n```\n\n**Examples from this repo:**\n- `🐛 Fix hydration error on dashboard page`\n- `✨ Add support for in-memory rate limiting`\n- `♻️ Move locale sync`\n- `⚡️ Preload dayjs locales`\n- `💄 Disable overscroll`\n\n## Emoji Reference\n\n| Emoji | Code | When to use |\n|-------|------|-------------|\n| ✨ | `:sparkles:` | New feature or capability |\n| 🐛 | `:bug:` | Bug fix |\n| ♻️ | `:recycle:` | Refactoring (no feature or fix) |\n| ⚡️ | `:zap:` | Performance improvement |\n| 💄 | `:lipstick:` | UI/style/cosmetic change |\n| 🔧 | `:wrench:` | Configuration file change |\n| 🔥 | `:fire:` | Remove code or files |\n| 📝 | `:memo:` | Documentation |\n| 🌐 | `:globe_with_meridians:` | Internationalization/localization |\n| 🔒️ | `:lock:` | Security fix or improvement |\n| ⬆️ | `:arrow_up:` | Upgrade dependencies |\n| 🚀 | `:rocket:` | Deploy or release |\n| 🔖 | `:bookmark:` | Version tag / release |\n| 👷 | `:construction_worker:` | CI/CD changes |\n| 🧑‍💻 | `:technologist:` | Developer experience improvement |\n| 🌱 | `:seedling:` | Seed data / database seeding |\n| 🔊 | `:loud_sound:` | Add or update logs |\n| ➕ | `:heavy_plus_sign:` | Add dependency |\n| ➖ | `:heavy_minus_sign:` | Remove dependency |\n| 🚨 | `:rotating_light:` | Fix linting / compiler warnings |\n| ✅ | `:white_check_mark:` | Add or update tests |\n| 🏗️ | `:building_construction:` | Architectural change |\n| 📦 | `:package:` | Build system change |\n| 🗃️ | `:card_file_box:` | Database / migration change |\n| 🩹 | `:adhesive_bandage:` | Simple fix for non-critical issue |\n| ⏪️ | `:rewind:` | Revert changes |\n\n## Rules\n\n1. **Always prefix** the commit message with exactly one primary emoji\n2. **No conventional commit type prefix** — use `🐛 Fix bug` not `fix: 🐛 Fix bug`\n3. **Capitalize** the first word after the emoji\n4. **No period** at the end of the subject line\n5. **Keep it short** — subject line under 72 characters\n6. Pick the **most specific** emoji that applies (e.g. `💄` for a pure CSS change, not `✨`)\n","frontmatter":{"name":"devmoji","description":"Devmoji commit message emoji conventions. Use when writing git commit messages, formatting commit descriptions, or choosing the right emoji prefix for a commit. Triggers on \"commit message\", \"git commit\", \"commit emoji\", \"devmoji\".","user-invocable":false},"isInternal":false,"tokens":733,"sizeBytes":2614},{"name":"SKILL.md","path":".agents/skills/email-and-password-best-practices/SKILL.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/email-and-password-best-practices/SKILL.md","title":"email-and-password-best-practices","category":"anthropic-skill","format":"markdown","content":"---\nname: email-and-password-best-practices\ndescription: This skill provides guidance and enforcement rules for implementing secure email and password authentication using Better Auth.\n---\n\n## Email Verification Setup\n\nWhen enabling email/password authentication, configure `emailVerification.sendVerificationEmail` to verify user email addresses. This helps prevent fake sign-ups and ensures users have access to the email they registered with.\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { sendEmail } from \"./email\"; // your email sending function\n\nexport const auth = betterAuth({\n  emailVerification: {\n    sendVerificationEmail: async ({ user, url, token }, request) => {\n      await sendEmail({\n        to: user.email,\n        subject: \"Verify your email address\",\n        text: `Click the link to verify your email: ${url}`,\n      });\n    },\n  },\n});\n```\n\n**Note**: The `url` parameter contains the full verification link. The `token` is available if you need to build a custom verification URL.\n\n### Requiring Email Verification\n\nFor stricter security, enable `emailAndPassword.requireEmailVerification` to block sign-in until the user verifies their email. When enabled, unverified users will receive a new verification email on each sign-in attempt.\n\n```ts\nexport const auth = betterAuth({\n  emailAndPassword: {\n    requireEmailVerification: true,\n  },\n});\n```\n\n**Note**: This requires `sendVerificationEmail` to be configured and only applies to email/password sign-ins.\n\n## Client side validation\n\nWhile Better Auth validates inputs server-side, implementing client-side validation is still recommended for two key reasons:\n\n1. **Improved UX**: Users receive immediate feedback when inputs don't meet requirements, rather than waiting for a server round-trip.\n2. **Reduced server load**: Invalid requests are caught early, minimizing unnecessary network traffic to your auth server.\n\n## Callback URLs\n\nAlways use absolute URLs (including the origin) for callback URLs in sign-up and sign-in requests. This prevents Better Auth from needing to infer the origin, which can cause issues when your backend and frontend are on different domains.\n\n```ts\nconst { data, error } = await authClient.signUp.email({\n  callbackURL: \"https://example.com/callback\", // absolute URL with origin\n});\n```\n\n## Password Reset Flows\n\nPassword reset flows are essential to any email/password system, we recommend setting this up.\n\nTo allow users to reset a password first you need to provide `sendResetPassword` function to the email and password authenticator.\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { sendEmail } from \"./email\"; // your email sending function\n\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    // Custom email sending function to send reset-password email\n    sendResetPassword: async ({ user, url, token }, request) => {\n      void sendEmail({\n        to: user.email,\n        subject: \"Reset your password\",\n        text: `Click the link to reset your password: ${url}`,\n      });\n    },\n    // Optional event hook\n    onPasswordReset: async ({ user }, request) => {\n      // your logic here\n      console.log(`Password for user ${user.email} has been reset.`);\n    },\n  },\n});\n```\n\n### Security considerations\n\nBetter Auth implements several security measures in the password reset flow:\n\n#### Timing attack prevention\n\n- **Background email sending**: Better Auth uses `runInBackgroundOrAwait` internally to send reset emails without blocking the response. This prevents attackers from measuring response times to determine if an email exists.\n- **Dummy operations on invalid requests**: When a user is not found, Better Auth still performs token generation and a database lookup (with a dummy value) to maintain consistent response times.\n- **Constant response message**: The API always returns `\"If this email exists in our system, check your email for the reset link\"` regardless of whether the user exists.\n\nOn serverless platforms, configure a background task handler to ensure emails are sent reliably:\n\n```ts\nexport const auth = betterAuth({\n  advanced: {\n    backgroundTasks: {\n      handler: (promise) => {\n        // Use platform-specific methods like waitUntil\n        waitUntil(promise);\n      },\n    },\n  },\n});\n```\n\n#### Token security\n\n- **Cryptographically random tokens**: Reset tokens are generated using `generateId(24)`, producing a 24-character alphanumeric string (a-z, A-Z, 0-9) with high entropy.\n- **Token expiration**: Tokens expire after **1 hour** by default. Configure with `resetPasswordTokenExpiresIn` (in seconds):\n\n```ts\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    resetPasswordTokenExpiresIn: 60 * 30, // 30 minutes\n  },\n});\n```\n\n- **Single-use tokens**: Tokens are deleted immediately after successful password reset, preventing reuse.\n\n#### Session revocation\n\nEnable `revokeSessionsOnPasswordReset` to invalidate all existing sessions when a password is reset. This ensures that if an attacker has an active session, it will be terminated:\n\n```ts\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    revokeSessionsOnPasswordReset: true,\n  },\n});\n```\n\n#### Redirect URL validation\n\nThe `redirectTo` parameter is validated against your `trustedOrigins` configuration to prevent open redirect attacks. Malicious redirect URLs will be rejected with a 403 error.\n\n#### Password requirements\n\nDuring password reset, the new password must meet length requirements:\n- **Minimum**: 8 characters (default), configurable via `minPasswordLength`\n- **Maximum**: 128 characters (default), configurable via `maxPasswordLength`\n\n```ts\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    minPasswordLength: 12,\n    maxPasswordLength: 256,\n  },\n});\n```\n\n### Sending the password reset\n\nOnce the password reset configurations are set-up, you can now call the `requestPasswordReset` function to send reset password link to user. If the user exists, it will trigger the `sendResetPassword` function you provided in the auth config.\n\n```ts\nconst data = await auth.api.requestPasswordReset({\n  body: {\n    email: \"john.doe@example.com\", // required\n    redirectTo: \"https://example.com/reset-password\",\n  },\n});\n```\n\nOr authClient:\n\n```ts\nconst { data, error } = await authClient.requestPasswordReset({\n  email: \"john.doe@example.com\", // required\n  redirectTo: \"https://example.com/reset-password\",\n});\n```\n\n**Note**: While the `email` is required, we also recommend configuring the `redirectTo` for a smoother user experience.\n\n## Password Hashing\n\nBetter Auth uses `scrypt` by default for password hashing. This is a solid choice because:\n\n- It's designed to be slow and memory-intensive, making brute-force attacks costly\n- It's natively supported by Node.js (no external dependencies)\n- OWASP recommends it when Argon2id isn't available\n\n### Custom Hashing Algorithm\n\nTo use a different algorithm (e.g., Argon2id), provide custom `hash` and `verify` functions in the `emailAndPassword.password` configuration:\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { hash, verify, type Options } from \"@node-rs/argon2\";\n\nconst argon2Options: Options = {\n  memoryCost: 65536, // 64 MiB\n  timeCost: 3, // 3 iterations\n  parallelism: 4, // 4 parallel lanes\n  outputLen: 32, // 32 byte output\n  algorithm: 2, // Argon2id variant\n};\n\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    password: {\n      hash: (password) => hash(password, argon2Options),\n      verify: ({ password, hash: storedHash }) =>\n        verify(storedHash, password, argon2Options),\n    },\n  },\n});\n```\n\n**Note**: If you switch hashing algorithms on an existing system, users with passwords hashed using the old algorithm won't be able to sign in. Plan a migration strategy if needed.\n","frontmatter":{"name":"email-and-password-best-practices","description":"This skill provides guidance and enforcement rules for implementing secure email and password authentication using Better Auth."},"isInternal":false,"tokens":1704,"sizeBytes":7855},{"name":"SKILL.md","path":".agents/skills/logging/SKILL.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/logging/SKILL.md","title":"logging","category":"anthropic-skill","format":"markdown","content":"---\nname: logging\ndescription: Wide events logging pattern and conventions for this project. Use when writing logging code, adding observability, or implementing request tracing.\nuser-invocable: false\n---\n\n# Logging Guidelines\n\nThis project follows the **Wide Events** pattern. Logs are optimized for querying, not writing.\n\n## Core Principle\n\nEmit **one comprehensive event per request per service** containing all contextual information. Do not scatter log statements throughout your code.\n\n**Wrong mental model:** Log what your code is doing\n**Correct mental model:** Log what happened to this request\n\n## Wide Event Structure\n\nEach event should include:\n\n```typescript\n{\n  // Request metadata\n  requestId: string,\n  traceId: string,\n  timestamp: Date,\n  service: string,\n\n  // HTTP details\n  method: string,\n  path: string,\n  statusCode: number,\n  durationMs: number,\n\n  // User context\n  userId?: string,\n  subscriptionTier?: string,\n\n  // Business context\n  pollId?: string,\n  spaceId?: string,\n  featureFlags: Record<string, boolean>,\n\n  // Error details (when applicable)\n  errorType?: string,\n  errorCode?: string,\n  errorMessage?: string,\n  isRetriable?: boolean,\n\n  // Performance metrics\n  dbQueryCount?: number,\n  dbQueryDurationMs?: number\n}\n```\n\n## Implementation Pattern\n\nUse middleware to build the event throughout the request lifecycle:\n\n1. Initialize event at request start with request/service metadata\n2. Enrich with user context after authentication\n3. Add business context as processing occurs\n4. Emit single event in finally block after request completes\n\n## Anti-Patterns\n\n**Never do these:**\n\n- Scattered `console.log` statements throughout code\n- String-based log messages without structured data\n- Low-context logs with only timestamp, level, and message\n- Multiple log lines for a single request\n\n**Example of what NOT to do:**\n\n```typescript\n// BAD - scattered, low-context logs\nconsole.log(\"Starting request\");\nconsole.log(\"User authenticated\");\nconsole.log(\"Fetching poll\");\nconsole.log(\"Request complete\");\n```\n\n**Example of correct approach:**\n\n```typescript\n// GOOD - single wide event with full context\nlogger.info({\n  requestId: ctx.requestId,\n  userId: ctx.user?.id,\n  pollId: params.pollId,\n  method: \"GET\",\n  path: \"/api/polls/:id\",\n  statusCode: 200,\n  durationMs: 45,\n  dbQueryCount: 2,\n  dbQueryDurationMs: 12\n});\n```\n\n## Tail Sampling\n\nWhen implementing log sampling, make decisions **after** request completion:\n\n**Always capture 100%:**\n- All errors (5xx status, exceptions)\n- Requests exceeding p99 latency\n- Pro/enterprise user requests\n- Requests with feature flags under rollout\n\n**Sample remaining traffic:** 1-5% of successful, fast requests\n\n## High-Cardinality Data\n\nInclude high-cardinality fields (userId, pollId, requestId). Modern observability tools handle this efficiently. Aim for 50+ fields per event. Include:\n\n- All IDs involved in the request\n- All feature flags evaluated\n- All external service calls made\n- Timing breakdown for significant operations\n- User tier and account metadata\n","frontmatter":{"name":"logging","description":"Wide events logging pattern and conventions for this project. Use when writing logging code, adding observability, or implementing request tracing.","user-invocable":false},"isInternal":false,"tokens":681,"sizeBytes":3052},{"name":"SKILL.md","path":".agents/skills/organization-best-practices/SKILL.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/organization-best-practices/SKILL.md","title":"organization-best-practices","category":"anthropic-skill","format":"markdown","content":"---\nname: organization-best-practices\ndescription: This skill provides guidance and enforcement rules for implementing multi-tenant organizations, teams, and role-based access control using Better Auth's organization plugin.\n---\n\n## Setting Up Organizations\n\nWhen adding organizations to your application, configure the `organization` plugin with appropriate limits and permissions.\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { organization } from \"better-auth/plugins\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n      allowUserToCreateOrganization: true,\n      organizationLimit: 5, // Max orgs per user\n      membershipLimit: 100, // Max members per org\n    }),\n  ],\n});\n```\n\n**Note**: After adding the plugin, run `npx @better-auth/cli migrate` to add the required database tables.\n\n### Client-Side Setup\n\nAdd the client plugin to access organization methods:\n\n```ts\nimport { createAuthClient } from \"better-auth/client\";\nimport { organizationClient } from \"better-auth/client/plugins\";\n\nexport const authClient = createAuthClient({\n  plugins: [organizationClient()],\n});\n```\n\n## Creating Organizations\n\nOrganizations are the top-level entity for grouping users. When created, the creator is automatically assigned the `owner` role.\n\n```ts\nconst createOrg = async () => {\n  const { data, error } = await authClient.organization.create({\n    name: \"My Company\",\n    slug: \"my-company\",\n    logo: \"https://example.com/logo.png\",\n    metadata: { plan: \"pro\" },\n  });\n};\n```\n\n### Controlling Organization Creation\n\nRestrict who can create organizations based on user attributes:\n\n```ts\norganization({\n  allowUserToCreateOrganization: async (user) => {\n    return user.emailVerified === true;\n  },\n  organizationLimit: async (user) => {\n    // Premium users get more organizations\n    return user.plan === \"premium\" ? 20 : 3;\n  },\n});\n```\n\n### Creating Organizations on Behalf of Users\n\nAdministrators can create organizations for other users (server-side only):\n\n```ts\nawait auth.api.createOrganization({\n  body: {\n    name: \"Client Organization\",\n    slug: \"client-org\",\n    userId: \"user-id-who-will-be-owner\", // `userId` is required\n  },\n});\n```\n\n**Note**: The `userId` parameter cannot be used alongside session headers.\n\n\n## Active Organizations\n\nThe active organization is stored in the session and scopes subsequent API calls. Always set an active organization after the user selects one.\n\n```ts\nconst setActive = async (organizationId: string) => {\n  const { data, error } = await authClient.organization.setActive({\n    organizationId,\n  });\n};\n```\n\nMany endpoints use the active organization when `organizationId` is not provided:\n\n```ts\n// These use the active organization automatically\nawait authClient.organization.listMembers();\nawait authClient.organization.listInvitations();\nawait authClient.organization.inviteMember({ email: \"user@example.com\", role: \"member\" });\n```\n\n### Getting Full Organization Data\n\nRetrieve the active organization with all its members, invitations, and teams:\n\n```ts\nconst { data } = await authClient.organization.getFullOrganization();\n// data.organization, data.members, data.invitations, data.teams\n```\n\n## Members\n\nMembers are users who belong to an organization. Each member has a role that determines their permissions.\n\n### Adding Members (Server-Side)\n\nAdd members directly without invitations (useful for admin operations):\n\n```ts\nawait auth.api.addMember({\n  body: {\n    userId: \"user-id\",\n    role: \"member\",\n    organizationId: \"org-id\",\n  },\n});\n```\n\n**Note**: For client-side member additions, use the invitation system instead.\n\n### Assigning Multiple Roles\n\nMembers can have multiple roles for fine-grained permissions:\n\n```ts\nawait auth.api.addMember({\n  body: {\n    userId: \"user-id\",\n    role: [\"admin\", \"moderator\"],\n    organizationId: \"org-id\",\n  },\n});\n```\n\n### Removing Members\n\nRemove members by ID or email:\n\n```ts\nawait authClient.organization.removeMember({\n  memberIdOrEmail: \"user@example.com\",\n});\n```\n\n**Important**: The last owner cannot be removed. Assign the owner role to another member first.\n\n### Updating Member Roles\n\n```ts\nawait authClient.organization.updateMemberRole({\n  memberId: \"member-id\",\n  role: \"admin\",\n});\n```\n\n### Membership Limits\n\nControl the maximum number of members per organization:\n\n```ts\norganization({\n  membershipLimit: async (user, organization) => {\n    if (organization.metadata?.plan === \"enterprise\") {\n      return 1000;\n    }\n    return 50;\n  },\n});\n```\n\n## Invitations\n\nThe invitation system allows admins to invite users via email. Configure email sending to enable invitations.\n\n### Setting Up Invitation Emails\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { organization } from \"better-auth/plugins\";\nimport { sendEmail } from \"./email\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n      sendInvitationEmail: async (data) => {\n        const { email, organization, inviter, invitation } = data;\n\n        await sendEmail({\n          to: email,\n          subject: `Join ${organization.name}`,\n          html: `\n            <p>${inviter.user.name} invited you to join ${organization.name}</p>\n            <a href=\"https://yourapp.com/accept-invite?id=${invitation.id}\">\n              Accept Invitation\n            </a>\n          `,\n        });\n      },\n    }),\n  ],\n});\n```\n\n### Sending Invitations\n\n```ts\nawait authClient.organization.inviteMember({\n  email: \"newuser@example.com\",\n  role: \"member\",\n});\n```\n\n### Creating Shareable Invitation URLs\n\nFor sharing via Slack, SMS, or in-app notifications:\n\n```ts\nconst { data } = await authClient.organization.getInvitationURL({\n  email: \"newuser@example.com\",\n  role: \"member\",\n  callbackURL: \"https://yourapp.com/dashboard\",\n});\n\n// Share data.url via any channel\n```\n\n**Note**: This endpoint does not call `sendInvitationEmail`. Handle delivery yourself.\n\n### Accepting Invitations\n\n```ts\nawait authClient.organization.acceptInvitation({\n  invitationId: \"invitation-id\",\n});\n```\n\n### Invitation Configuration\n\n```ts\norganization({\n  invitationExpiresIn: 60 * 60 * 24 * 7, // 7 days (default: 48 hours)\n  invitationLimit: 100, // Max pending invitations per org\n  cancelPendingInvitationsOnReInvite: true, // Cancel old invites when re-inviting\n});\n```\n\n## Roles & Permissions\n\nThe plugin provides role-based access control (RBAC) with three default roles:\n\n| Role | Description |\n|------|-------------|\n| `owner` | Full access, can delete organization |\n| `admin` | Can manage members, invitations, settings |\n| `member` | Basic access to organization resources |\n\n\n### Checking Permissions\n\n```ts\nconst { data } = await authClient.organization.hasPermission({\n  permission: \"member:write\",\n});\n\nif (data?.hasPermission) {\n  // User can manage members\n}\n```\n\n### Client-Side Permission Checks\n\nFor UI rendering without API calls:\n\n```ts\nconst canManageMembers = authClient.organization.checkRolePermission({\n  role: \"admin\",\n  permissions: [\"member:write\"],\n});\n```\n\n**Note**: For dynamic access control, the client side role permission check will not work. Please use the `hasPermission` endpoint.\n\n## Teams\n\nTeams allow grouping members within an organization.\n\n### Enabling Teams\n\n```ts\nimport { organization } from \"better-auth/plugins\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n        teams: {\n            enabled: true\n        }\n    }),\n  ],\n});\n```\n\n### Creating Teams\n\n```ts\nconst { data } = await authClient.organization.createTeam({\n  name: \"Engineering\",\n});\n```\n\n### Managing Team Members\n\n```ts\n// Add a member to a team (must be org member first)\nawait authClient.organization.addTeamMember({\n  teamId: \"team-id\",\n  userId: \"user-id\",\n});\n\n// Remove from team (stays in org)\nawait authClient.organization.removeTeamMember({\n  teamId: \"team-id\",\n  userId: \"user-id\",\n});\n```\n\n### Active Teams\n\nSimilar to active organizations, set an active team for the session:\n\n```ts\nawait authClient.organization.setActiveTeam({\n  teamId: \"team-id\",\n});\n```\n\n### Team Limits\n\n```ts\norganization({\n  teams: {\n      maximumTeams: 20, // Max teams per org\n      maximumMembersPerTeam: 50, // Max members per team\n      allowRemovingAllTeams: false, // Prevent removing last team\n  }\n});\n```\n\n## Dynamic Access Control\n\nFor applications needing custom roles per organization at runtime, enable dynamic access control.\n\n### Enabling Dynamic Access Control\n\n```ts\nimport { organization } from \"better-auth/plugins\";\nimport { dynamicAccessControl } from \"@better-auth/organization/addons\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n        dynamicAccessControl: {\n            enabled: true\n        }\n    }),\n  ],\n});\n```\n\n### Creating Custom Roles\n\n```ts\nawait authClient.organization.createRole({\n  role: \"moderator\",\n  permission: {\n    member: [\"read\"],\n    invitation: [\"read\"],\n  },\n});\n```\n\n### Updating and Deleting Roles\n\n```ts\n// Update role permissions\nawait authClient.organization.updateRole({\n  roleId: \"role-id\",\n  permission: {\n    member: [\"read\", \"write\"],\n  },\n});\n\n// Delete a custom role\nawait authClient.organization.deleteRole({\n  roleId: \"role-id\",\n});\n```\n\n**Note**: Pre-defined roles (owner, admin, member) cannot be deleted. Roles assigned to members cannot be deleted until members are reassigned.\n\n## Lifecycle Hooks\n\nExecute custom logic at various points in the organization lifecycle:\n\n```ts\norganization({\n  hooks: {\n    organization: {\n      beforeCreate: async ({ data, user }) => {\n        // Validate or modify data before creation\n        return {\n          data: {\n            ...data,\n            metadata: { ...data.metadata, createdBy: user.id },\n          },\n        };\n      },\n      afterCreate: async ({ organization, member }) => {\n        // Post-creation logic (e.g., send welcome email, create default resources)\n        await createDefaultResources(organization.id);\n      },\n      beforeDelete: async ({ organization }) => {\n        // Cleanup before deletion\n        await archiveOrganizationData(organization.id);\n      },\n    },\n    member: {\n      afterCreate: async ({ member, organization }) => {\n        await notifyAdmins(organization.id, `New member joined`);\n      },\n    },\n    invitation: {\n      afterCreate: async ({ invitation, organization, inviter }) => {\n        await logInvitation(invitation);\n      },\n    },\n  },\n});\n```\n\n## Schema Customization\n\nCustomize table names, field names, and add additional fields:\n\n```ts\norganization({\n  schema: {\n    organization: {\n      modelName: \"workspace\", // Rename table\n      fields: {\n        name: \"workspaceName\", // Rename fields\n      },\n      additionalFields: {\n        billingId: {\n          type: \"string\",\n          required: false,\n        },\n      },\n    },\n    member: {\n      additionalFields: {\n        department: {\n          type: \"string\",\n          required: false,\n        },\n        title: {\n          type: \"string\",\n          required: false,\n        },\n      },\n    },\n  },\n});\n```\n\n## Security Considerations\n\n### Owner Protection\n\n- The last owner cannot be removed from an organization\n- The last owner cannot leave the organization\n- The owner role cannot be removed from the last owner\n\nAlways ensure ownership transfer before removing the current owner:\n\n```ts\n// Transfer ownership first\nawait authClient.organization.updateMemberRole({\n  memberId: \"new-owner-member-id\",\n  role: \"owner\",\n});\n\n// Then the previous owner can be demoted or removed\n```\n\n### Organization Deletion\n\nDeleting an organization removes all associated data (members, invitations, teams). Prevent accidental deletion:\n\n```ts\norganization({\n  disableOrganizationDeletion: true, // Disable via config\n});\n```\n\nOr implement soft delete via hooks:\n\n```ts\norganization({\n  hooks: {\n    organization: {\n      beforeDelete: async ({ organization }) => {\n        // Archive instead of delete\n        await archiveOrganization(organization.id);\n        throw new Error(\"Organization archived, not deleted\");\n      },\n    },\n  },\n});\n```\n\n### Invitation Security\n\n- Invitations expire after 48 hours by default\n- Only the invited email address can accept an invitation\n- Pending invitations can be cancelled by organization admins\n\n## Complete Configuration Example\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { organization } from \"better-auth/plugins\";\nimport { sendEmail } from \"./email\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n      // Organization limits\n      allowUserToCreateOrganization: true,\n      organizationLimit: 10,\n      membershipLimit: 100,\n      creatorRole: \"owner\",\n\n      // Slugs\n      defaultOrganizationIdField: \"slug\",\n\n      // Invitations\n      invitationExpiresIn: 60 * 60 * 24 * 7, // 7 days\n      invitationLimit: 50,\n      sendInvitationEmail: async (data) => {\n        await sendEmail({\n          to: data.email,\n          subject: `Join ${data.organization.name}`,\n          html: `<a href=\"https://app.com/invite/${data.invitation.id}\">Accept</a>`,\n        });\n      },\n\n      // Hooks\n      hooks: {\n        organization: {\n          afterCreate: async ({ organization }) => {\n            console.log(`Organization ${organization.name} created`);\n          },\n        },\n      },\n    }),\n  ],\n});\n```\n","frontmatter":{"name":"organization-best-practices","description":"This skill provides guidance and enforcement rules for implementing multi-tenant organizations, teams, and role-based access control using Better Auth's organization plugin."},"isInternal":false,"tokens":2918,"sizeBytes":13263},{"name":"db-execute.md","path":".agents/skills/prisma-cli/references/db-execute.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-execute.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/db-execute.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-execute.md)","tokens":300,"sizeBytes":216},{"name":"db-pull.md","path":".agents/skills/prisma-cli/references/db-pull.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-pull.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/db-pull.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-pull.md)","tokens":300,"sizeBytes":210},{"name":"db-push.md","path":".agents/skills/prisma-cli/references/db-push.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-push.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/db-push.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-push.md)","tokens":300,"sizeBytes":210},{"name":"db-seed.md","path":".agents/skills/prisma-cli/references/db-seed.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-seed.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/db-seed.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-seed.md)","tokens":300,"sizeBytes":210},{"name":"debug.md","path":".agents/skills/prisma-cli/references/debug.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/debug.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/debug.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/debug.md)","tokens":300,"sizeBytes":206},{"name":"dev.md","path":".agents/skills/prisma-cli/references/dev.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/dev.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/dev.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/dev.md)","tokens":300,"sizeBytes":202},{"name":"format.md","path":".agents/skills/prisma-cli/references/format.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/format.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/format.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/format.md)","tokens":300,"sizeBytes":208},{"name":"generate.md","path":".agents/skills/prisma-cli/references/generate.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/generate.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/generate.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/generate.md)","tokens":300,"sizeBytes":212},{"name":"init.md","path":".agents/skills/prisma-cli/references/init.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/init.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/init.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/init.md)","tokens":300,"sizeBytes":204},{"name":"migrate-deploy.md","path":".agents/skills/prisma-cli/references/migrate-deploy.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-deploy.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-deploy.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-deploy.md)","tokens":300,"sizeBytes":224},{"name":"migrate-dev.md","path":".agents/skills/prisma-cli/references/migrate-dev.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-dev.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-dev.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-dev.md)","tokens":300,"sizeBytes":218},{"name":"migrate-diff.md","path":".agents/skills/prisma-cli/references/migrate-diff.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-diff.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-diff.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-diff.md)","tokens":300,"sizeBytes":220},{"name":"migrate-reset.md","path":".agents/skills/prisma-cli/references/migrate-reset.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-reset.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-reset.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-reset.md)","tokens":300,"sizeBytes":222},{"name":"migrate-resolve.md","path":".agents/skills/prisma-cli/references/migrate-resolve.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-resolve.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-resolve.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-resolve.md)","tokens":300,"sizeBytes":226},{"name":"migrate-status.md","path":".agents/skills/prisma-cli/references/migrate-status.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-status.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-status.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-status.md)","tokens":300,"sizeBytes":224},{"name":"studio.md","path":".agents/skills/prisma-cli/references/studio.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/studio.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/studio.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/studio.md)","tokens":300,"sizeBytes":208},{"name":"validate.md","path":".agents/skills/prisma-cli/references/validate.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/validate.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-cli/references/validate.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/validate.md)","tokens":300,"sizeBytes":212},{"name":"SKILL.md","path":".agents/skills/prisma-cli/SKILL.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/SKILL.md","title":"prisma-cli","category":"anthropic-skill","format":"markdown","content":"---\nname: prisma-cli\ndescription: Prisma CLI commands reference covering all available commands, options, and usage patterns. Use when running Prisma CLI commands, setting up projects, generating client, running migrations, or managing databases. Triggers on \"prisma init\", \"prisma generate\", \"prisma migrate\", \"prisma db\", \"prisma studio\".\nlicense: MIT\nmetadata:\n  author: prisma\n  version: \"7.0.0\"\n---\n\n# Prisma CLI Reference\n\nComplete reference for all Prisma CLI commands. This skill provides guidance on command usage, options, and best practices for Prisma ORM 7.x.\n\n## When to Apply\n\nReference this skill when:\n- Setting up a new Prisma project (`prisma init`)\n- Generating Prisma Client (`prisma generate`)\n- Running database migrations (`prisma migrate`)\n- Managing database state (`prisma db push/pull`)\n- Using local development database (`prisma dev`)\n- Debugging Prisma issues (`prisma debug`)\n\n## Rule Categories by Priority\n\n| Priority | Category | Impact | Prefix |\n|----------|----------|--------|--------|\n| 1 | Setup | HIGH | `init` |\n| 2 | Generation | HIGH | `generate` |\n| 3 | Development | HIGH | `dev` |\n| 4 | Database | HIGH | `db-` |\n| 5 | Migrations | CRITICAL | `migrate-` |\n| 6 | Utility | MEDIUM | `studio`, `validate`, `format`, `debug` |\n\n## Command Categories\n\n| Category | Commands | Purpose |\n|----------|----------|---------|\n| Setup | `init` | Bootstrap new Prisma project |\n| Generation | `generate` | Generate Prisma Client |\n| Validation | `validate`, `format` | Schema validation and formatting |\n| Development | `dev` | Local Prisma Postgres for development |\n| Database | `db pull`, `db push`, `db seed`, `db execute` | Direct database operations |\n| Migrations | `migrate dev`, `migrate deploy`, `migrate reset`, `migrate status`, `migrate diff`, `migrate resolve` | Schema migrations |\n| Utility | `studio`, `version`, `debug` | Development tools |\n\n## Quick Reference\n\n### Project Setup\n\n```bash\n# Initialize new project (creates prisma/ folder and prisma.config.ts)\nprisma init\n\n# Initialize with specific database\nprisma init --datasource-provider postgresql\nprisma init --datasource-provider mysql\nprisma init --datasource-provider sqlite\n\n# Initialize with Prisma Postgres (cloud)\nprisma init --db\n\n# Initialize with AI-generated schema\nprisma init --prompt \"E-commerce app with users, products, orders\"\n```\n\n### Client Generation\n\n```bash\n# Generate Prisma Client\nprisma generate\n\n# Watch mode for development\nprisma generate --watch\n\n# Generate without engine (for Accelerate/edge)\nprisma generate --no-engine\n\n# Generate specific generator only\nprisma generate --generator client\n```\n\n### Bun Runtime\n\nWhen using Bun, always add the `--bun` flag so Prisma runs with the Bun runtime (otherwise it falls back to Node.js because of the CLI shebang):\n\n```bash\nbunx --bun prisma init\nbunx --bun prisma generate\n```\n\n### Local Development Database\n\n```bash\n# Start local Prisma Postgres\nprisma dev\n\n# Start with specific name\nprisma dev --name myproject\n\n# Start in background (detached)\nprisma dev --detach\n\n# List all local instances\nprisma dev ls\n\n# Stop instance\nprisma dev stop myproject\n\n# Remove instance data\nprisma dev rm myproject\n```\n\n### Database Operations\n\n```bash\n# Pull schema from existing database\nprisma db pull\n\n# Push schema to database (no migrations)\nprisma db push\n\n# Seed database\nprisma db seed\n\n# Execute raw SQL\nprisma db execute --file ./script.sql\n```\n\n### Migrations (Development)\n\n```bash\n# Create and apply migration\nprisma migrate dev\n\n# Create migration with name\nprisma migrate dev --name add_users_table\n\n# Create migration without applying\nprisma migrate dev --create-only\n\n# Reset database and apply all migrations\nprisma migrate reset\n```\n\n### Migrations (Production)\n\n```bash\n# Apply pending migrations (CI/CD)\nprisma migrate deploy\n\n# Check migration status\nprisma migrate status\n\n# Compare schemas and generate diff\nprisma migrate diff --from-config-datasource --to-schema schema.prisma --script\n```\n\n### Utility Commands\n\n```bash\n# Open Prisma Studio (database GUI)\nprisma studio\n\n# Show version info\nprisma version\nprisma -v\n\n# Debug information\nprisma debug\n\n# Validate schema\nprisma validate\n\n# Format schema\nprisma format\n```\n\n## Prisma 7 Changes\n\n### New Configuration File\n\nPrisma 7 uses `prisma.config.ts` for CLI configuration:\n\n```typescript\nimport 'dotenv/config'\nimport { defineConfig, env } from 'prisma/config'\n\nexport default defineConfig({\n  schema: 'prisma/schema.prisma',\n  migrations: {\n    path: 'prisma/migrations',\n    seed: 'tsx prisma/seed.ts',\n  },\n  datasource: {\n    url: env('DATABASE_URL'),\n  },\n})\n```\n\n### Removed Flags\n\n- `--skip-generate` removed from `migrate dev` and `db push`\n- `--skip-seed` removed from `migrate dev`\n- `--schema` and `--url` removed from `db execute`\n- Run `prisma generate` explicitly after migrations\n\n### Environment Variables\n\nEnvironment variables are no longer auto-loaded. Use `dotenv`:\n\n```typescript\n// prisma.config.ts\nimport 'dotenv/config'\n```\n\n## Rule Files\n\nSee individual rule files for detailed command documentation:\n\n```\nreferences/init.md           - Project initialization\nreferences/generate.md       - Client generation\nreferences/dev.md            - Local development database\nreferences/db-pull.md        - Database introspection\nreferences/db-push.md        - Schema push\nreferences/db-seed.md        - Database seeding\nreferences/db-execute.md     - Raw SQL execution\nreferences/migrate-dev.md    - Development migrations\nreferences/migrate-deploy.md - Production migrations\nreferences/migrate-reset.md  - Database reset\nreferences/migrate-status.md - Migration status\nreferences/migrate-resolve.md - Migration resolution\nreferences/migrate-diff.md   - Schema diffing\nreferences/studio.md         - Database GUI\nreferences/validate.md       - Schema validation\nreferences/format.md         - Schema formatting\nreferences/debug.md          - Debug info\n```\n\n## How to Use\n\nUse the command categories above for navigation, then open the specific command reference file you need.\n","frontmatter":{"name":"prisma-cli","description":"Prisma CLI commands reference covering all available commands, options, and usage patterns. Use when running Prisma CLI commands, setting up projects, generating client, running migrations, or managing databases. Triggers on \"prisma init\", \"prisma generate\", \"prisma migrate\", \"prisma db\", \"prisma studio\".","license":"MIT","metadata":{"author":"prisma","version":"7.0.0"}},"isInternal":false,"tokens":1441,"sizeBytes":6049},{"name":"client-methods.md","path":".agents/skills/prisma-client-api/references/client-methods.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/client-methods.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/client-methods.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/client-methods.md)","tokens":300,"sizeBytes":238},{"name":"constructor.md","path":".agents/skills/prisma-client-api/references/constructor.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/constructor.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/constructor.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/constructor.md)","tokens":300,"sizeBytes":232},{"name":"filters.md","path":".agents/skills/prisma-client-api/references/filters.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/filters.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/filters.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/filters.md)","tokens":300,"sizeBytes":224},{"name":"model-queries.md","path":".agents/skills/prisma-client-api/references/model-queries.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/model-queries.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/model-queries.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/model-queries.md)","tokens":300,"sizeBytes":236},{"name":"query-options.md","path":".agents/skills/prisma-client-api/references/query-options.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/query-options.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/query-options.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/query-options.md)","tokens":300,"sizeBytes":236},{"name":"raw-queries.md","path":".agents/skills/prisma-client-api/references/raw-queries.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/raw-queries.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/raw-queries.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/raw-queries.md)","tokens":300,"sizeBytes":232},{"name":"relations.md","path":".agents/skills/prisma-client-api/references/relations.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/relations.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/relations.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/relations.md)","tokens":300,"sizeBytes":228},{"name":"transactions.md","path":".agents/skills/prisma-client-api/references/transactions.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/transactions.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/transactions.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/transactions.md)","tokens":300,"sizeBytes":234},{"name":"SKILL.md","path":".agents/skills/prisma-client-api/SKILL.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/SKILL.md","title":"prisma-client-api","category":"anthropic-skill","format":"markdown","content":"---\nname: prisma-client-api\ndescription: Prisma Client API reference covering model queries, filters, operators, and client methods. Use when writing database queries, using CRUD operations, filtering data, or configuring Prisma Client. Triggers on \"prisma query\", \"findMany\", \"create\", \"update\", \"delete\", \"$transaction\".\nlicense: MIT\nmetadata:\n  author: prisma\n  version: \"7.0.0\"\n---\n\n# Prisma Client API Reference\n\nComplete API reference for Prisma Client. This skill provides guidance on model queries, filtering, relations, and client methods for Prisma ORM 7.x.\n\n## When to Apply\n\nReference this skill when:\n- Writing database queries with Prisma Client\n- Performing CRUD operations (create, read, update, delete)\n- Filtering and sorting data\n- Working with relations\n- Using transactions\n- Configuring client options\n\n## Rule Categories by Priority\n\n| Priority | Category | Impact | Prefix |\n|----------|----------|--------|--------|\n| 1 | Client Construction | HIGH | `constructor` |\n| 2 | Model Queries | CRITICAL | `model-queries` |\n| 3 | Query Shape | HIGH | `query-options` |\n| 4 | Filtering | HIGH | `filters` |\n| 5 | Relations | HIGH | `relations` |\n| 6 | Transactions | CRITICAL | `transactions` |\n| 7 | Raw SQL | CRITICAL | `raw-queries` |\n| 8 | Client Methods | MEDIUM | `client-methods` |\n\n## Quick Reference\n\n- `constructor` - `PrismaClient` setup and adapter wiring\n- `model-queries` - CRUD operations and bulk operations\n- `query-options` - `select`, `include`, `omit`, sort, pagination\n- `filters` - scalar and logical filter operators\n- `relations` - relation reads and nested writes\n- `transactions` - array and interactive transaction patterns\n- `raw-queries` - `$queryRaw` and `$executeRaw` safety\n- `client-methods` - lifecycle and extension methods\n\n## Client Instantiation (v7)\n\n```typescript\nimport { PrismaClient } from '../generated/client'\nimport { PrismaPg } from '@prisma/adapter-pg'\n\nconst adapter = new PrismaPg({\n  connectionString: process.env.DATABASE_URL\n})\n\nconst prisma = new PrismaClient({ adapter })\n```\n\n## Model Query Methods\n\n| Method | Description |\n|--------|-------------|\n| `findUnique()` | Find one record by unique field |\n| `findUniqueOrThrow()` | Find one or throw error |\n| `findFirst()` | Find first matching record |\n| `findFirstOrThrow()` | Find first or throw error |\n| `findMany()` | Find multiple records |\n| `create()` | Create a new record |\n| `createMany()` | Create multiple records |\n| `createManyAndReturn()` | Create multiple and return them |\n| `update()` | Update one record |\n| `updateMany()` | Update multiple records |\n| `updateManyAndReturn()` | Update multiple and return them |\n| `upsert()` | Update or create record |\n| `delete()` | Delete one record |\n| `deleteMany()` | Delete multiple records |\n| `count()` | Count matching records |\n| `aggregate()` | Aggregate values (sum, avg, etc.) |\n| `groupBy()` | Group and aggregate |\n\n## Query Options\n\n| Option | Description |\n|--------|-------------|\n| `where` | Filter conditions |\n| `select` | Fields to include |\n| `include` | Relations to load |\n| `omit` | Fields to exclude |\n| `orderBy` | Sort order |\n| `take` | Limit results |\n| `skip` | Skip results (pagination) |\n| `cursor` | Cursor-based pagination |\n| `distinct` | Unique values only |\n\n## Client Methods\n\n| Method | Description |\n|--------|-------------|\n| `$connect()` | Explicitly connect to database |\n| `$disconnect()` | Disconnect from database |\n| `$transaction()` | Execute transaction |\n| `$queryRaw()` | Execute raw SQL query |\n| `$executeRaw()` | Execute raw SQL command |\n| `$on()` | Subscribe to events |\n| `$extends()` | Add extensions |\n\n## Quick Examples\n\n### Find records\n\n```typescript\n// Find by unique field\nconst user = await prisma.user.findUnique({\n  where: { email: 'alice@prisma.io' }\n})\n\n// Find with filter\nconst users = await prisma.user.findMany({\n  where: { role: 'ADMIN' },\n  orderBy: { createdAt: 'desc' },\n  take: 10\n})\n```\n\n### Create records\n\n```typescript\nconst user = await prisma.user.create({\n  data: {\n    email: 'alice@prisma.io',\n    name: 'Alice',\n    posts: {\n      create: { title: 'Hello World' }\n    }\n  },\n  include: { posts: true }\n})\n```\n\n### Update records\n\n```typescript\nconst user = await prisma.user.update({\n  where: { id: 1 },\n  data: { name: 'Alice Smith' }\n})\n```\n\n### Delete records\n\n```typescript\nawait prisma.user.delete({\n  where: { id: 1 }\n})\n```\n\n### Transactions\n\n```typescript\nconst [user, post] = await prisma.$transaction([\n  prisma.user.create({ data: { email: 'alice@prisma.io' } }),\n  prisma.post.create({ data: { title: 'Hello', authorId: 1 } })\n])\n```\n\n## Rule Files\n\nDetailed API documentation:\n\n```\nreferences/constructor.md        - PrismaClient constructor options\nreferences/model-queries.md      - CRUD operations\nreferences/query-options.md      - select, include, omit, where, orderBy\nreferences/filters.md            - Filter conditions and operators\nreferences/relations.md          - Relation queries and nested operations\nreferences/transactions.md       - Transaction API\nreferences/raw-queries.md        - $queryRaw, $executeRaw\nreferences/client-methods.md     - $connect, $disconnect, $on, $extends\n```\n\n## Filter Operators\n\n| Operator | Description |\n|----------|-------------|\n| `equals` | Exact match |\n| `not` | Not equal |\n| `in` | In array |\n| `notIn` | Not in array |\n| `lt`, `lte` | Less than |\n| `gt`, `gte` | Greater than |\n| `contains` | String contains |\n| `startsWith` | String starts with |\n| `endsWith` | String ends with |\n| `mode` | Case sensitivity |\n\n## Relation Filters\n\n| Operator | Description |\n|----------|-------------|\n| `some` | At least one related record matches |\n| `every` | All related records match |\n| `none` | No related records match |\n| `is` | Related record matches (1-to-1) |\n| `isNot` | Related record doesn't match |\n\n## Resources\n\n- [Prisma Client API Reference](https://www.prisma.io/docs/orm/reference/prisma-client-reference)\n- [CRUD Operations](https://www.prisma.io/docs/orm/prisma-client/queries/crud)\n- [Filtering and Sorting](https://www.prisma.io/docs/orm/prisma-client/queries/filtering-and-sorting)\n\n## How to Use\n\nPick the category from the table above, then open the matching reference file for implementation details and examples.\n","frontmatter":{"name":"prisma-client-api","description":"Prisma Client API reference covering model queries, filters, operators, and client methods. Use when writing database queries, using CRUD operations, filtering data, or configuring Prisma Client. Triggers on \"prisma query\", \"findMany\", \"create\", \"update\", \"delete\", \"$transaction\".","license":"MIT","metadata":{"author":"prisma","version":"7.0.0"}},"isInternal":false,"tokens":1558,"sizeBytes":6265},{"name":"cockroachdb.md","path":".agents/skills/prisma-database-setup/references/cockroachdb.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/cockroachdb.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/cockroachdb.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/cockroachdb.md)","tokens":300,"sizeBytes":240},{"name":"mongodb.md","path":".agents/skills/prisma-database-setup/references/mongodb.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/mongodb.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/mongodb.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/mongodb.md)","tokens":300,"sizeBytes":232},{"name":"mysql.md","path":".agents/skills/prisma-database-setup/references/mysql.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/mysql.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/mysql.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/mysql.md)","tokens":300,"sizeBytes":228},{"name":"postgresql.md","path":".agents/skills/prisma-database-setup/references/postgresql.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/postgresql.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/postgresql.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/postgresql.md)","tokens":300,"sizeBytes":238},{"name":"prisma-client-setup.md","path":".agents/skills/prisma-database-setup/references/prisma-client-setup.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/prisma-client-setup.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/prisma-client-setup.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/prisma-client-setup.md)","tokens":300,"sizeBytes":256},{"name":"prisma-postgres.md","path":".agents/skills/prisma-database-setup/references/prisma-postgres.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/prisma-postgres.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/prisma-postgres.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/prisma-postgres.md)","tokens":300,"sizeBytes":248},{"name":"sqlite.md","path":".agents/skills/prisma-database-setup/references/sqlite.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/sqlite.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/sqlite.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/sqlite.md)","tokens":300,"sizeBytes":230},{"name":"sqlserver.md","path":".agents/skills/prisma-database-setup/references/sqlserver.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/sqlserver.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-database-setup/references/sqlserver.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/references/sqlserver.md)","tokens":300,"sizeBytes":236},{"name":"SKILL.md","path":".agents/skills/prisma-database-setup/SKILL.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-database-setup/SKILL.md","title":"prisma-database-setup","category":"anthropic-skill","format":"markdown","content":"---\nname: prisma-database-setup\ndescription: Guides for configuring Prisma with different database providers (PostgreSQL, MySQL, SQLite, MongoDB, etc.). Use when setting up a new project, changing databases, or troubleshooting connection issues. Triggers on \"configure postgres\", \"connect to mysql\", \"setup mongodb\", \"sqlite setup\".\nlicense: MIT\nmetadata:\n  author: prisma\n  version: \"1.0.0\"\n---\n\n# Prisma Database Setup\n\nComprehensive guides for configuring Prisma ORM with various database providers.\n\n## When to Apply\n\nReference this skill when:\n- Initializing a new Prisma project\n- Switching database providers\n- Configuring connection strings and environment variables\n- Troubleshooting database connection issues\n- Setting up database-specific features\n- Generating and instantiating Prisma Client\n\n## Rule Categories by Priority\n\n| Priority | Category | Impact | Prefix |\n|----------|----------|--------|--------|\n| 1 | Provider Guides | CRITICAL | provider names |\n| 2 | Prisma Postgres | HIGH | `prisma-postgres` |\n| 3 | Client Setup | CRITICAL | `prisma-client-setup` |\n\n## System Prerequisites (Prisma ORM 7)\n\n- **Node.js 20.19.0+**\n- **TypeScript 5.4.0+**\n\n## Bun Runtime\n\nIf you're using Bun, run Prisma CLI commands with `bunx --bun prisma ...` so Prisma uses the Bun runtime instead of falling back to Node.js.\n\n## Supported Databases\n\n| Database | Provider String | Notes |\n|----------|-----------------|-------|\n| PostgreSQL | `postgresql` | Default, full feature support |\n| MySQL | `mysql` | Widespread support, some JSON diffs |\n| SQLite | `sqlite` | Local file-based, no enum/scalar lists |\n| MongoDB | `mongodb` | **NOT SUPPORTED IN v7** (Use v6) |\n| SQL Server | `sqlserver` | Microsoft ecosystem |\n| CockroachDB | `cockroachdb` | Distributed SQL, Postgres-compatible |\n| Prisma Postgres | `postgresql` | Managed serverless database |\n\n## Configuration Files\n\nPrisma v7 uses two main files for configuration:\n\n1. **`prisma/schema.prisma`**: Defines the `datasource` block.\n2. **`prisma.config.ts`**: Configures the connection URL (replaces env loading in schema).\n\n## Driver Adapters (Prisma ORM 7)\n\nPrisma ORM 7 uses the query compiler by default, which **requires a driver adapter**. Choose the adapter and driver for your database and pass the adapter to `PrismaClient`.\n\n| Database | Adapter | JS Driver |\n|----------|---------|-----------|\n| PostgreSQL | `@prisma/adapter-pg` | `pg` |\n| CockroachDB | `@prisma/adapter-pg` | `pg` |\n| Prisma Postgres | `@prisma/adapter-ppg` | `@prisma/ppg` |\n| MySQL / MariaDB | `@prisma/adapter-mariadb` | `mariadb` |\n| SQLite | `@prisma/adapter-better-sqlite3` | `better-sqlite3` |\n| SQLite (Turso/LibSQL) | `@prisma/adapter-libsql` | `@libsql/client` |\n| SQL Server | `@prisma/adapter-mssql` | `node-mssql` |\n\nExample (PostgreSQL):\n\n```ts\nimport 'dotenv/config'\nimport { PrismaClient } from '../generated/client'\nimport { PrismaPg } from '@prisma/adapter-pg'\n\nconst adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL })\nconst prisma = new PrismaClient({ adapter })\n```\n\n## Prisma Client Setup (Required)\n\nPrisma Client must be installed and generated for any database.\n\n1. Install Prisma CLI and Prisma Client:\n   ```bash\n   npm install prisma --save-dev\n   npm install @prisma/client\n   ```\n\n1. Add a generator block (output is required in Prisma v7):\n   ```prisma\n   generator client {\n     provider = \"prisma-client\"\n     output   = \"../generated\"\n   }\n   ```\n\n1. Generate Prisma Client:\n   ```bash\n   npx prisma generate\n   ```\n\n1. Instantiate Prisma Client with the database-specific driver adapter:\n   ```typescript\n   import { PrismaClient } from '../generated/client'\n   import { PrismaPg } from '@prisma/adapter-pg'\n\n   const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL })\n   const prisma = new PrismaClient({ adapter })\n   ```\n\n1. Re-run `prisma generate` after every schema change.\n\n## Quick Reference\n\n### PostgreSQL\n```prisma\ndatasource db {\n  provider = \"postgresql\"\n}\n\ngenerator client {\n  provider = \"prisma-client\"\n  output   = \"../generated\"\n}\n```\n\n### MySQL\n```prisma\ndatasource db {\n  provider = \"mysql\"\n}\n\ngenerator client {\n  provider = \"prisma-client\"\n  output   = \"../generated\"\n}\n```\n\n### SQLite\n```prisma\ndatasource db {\n  provider = \"sqlite\"\n}\n\ngenerator client {\n  provider = \"prisma-client\"\n  output   = \"../generated\"\n}\n```\n\n### MongoDB (Prisma v6 only)\n```prisma\ndatasource db {\n  provider = \"mongodb\"\n  url      = env(\"DATABASE_URL\")\n}\n\ngenerator client {\n  provider = \"prisma-client-js\"\n}\n```\n\n## Rule Files\n\nSee individual rule files for detailed setup instructions:\n\n```\nreferences/postgresql.md\nreferences/mysql.md\nreferences/sqlite.md\nreferences/mongodb.md\nreferences/sqlserver.md\nreferences/cockroachdb.md\nreferences/prisma-postgres.md\nreferences/prisma-client-setup.md\n```\n\n## How to Use\n\nChoose the provider reference file for your database, then apply `references/prisma-client-setup.md` to complete client generation and adapter setup.\n","frontmatter":{"name":"prisma-database-setup","description":"Guides for configuring Prisma with different database providers (PostgreSQL, MySQL, SQLite, MongoDB, etc.). Use when setting up a new project, changing databases, or troubleshooting connection issues. Triggers on \"configure postgres\", \"connect to mysql\", \"setup mongodb\", \"sqlite setup\".","license":"MIT","metadata":{"author":"prisma","version":"1.0.0"}},"isInternal":false,"tokens":1270,"sizeBytes":4983},{"name":"console-and-connections.md","path":".agents/skills/prisma-postgres/references/console-and-connections.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-postgres/references/console-and-connections.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-postgres/references/console-and-connections.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-postgres/references/console-and-connections.md)","tokens":300,"sizeBytes":252},{"name":"create-db-cli.md","path":".agents/skills/prisma-postgres/references/create-db-cli.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-postgres/references/create-db-cli.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-postgres/references/create-db-cli.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-postgres/references/create-db-cli.md)","tokens":300,"sizeBytes":232},{"name":"management-api-sdk.md","path":".agents/skills/prisma-postgres/references/management-api-sdk.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-postgres/references/management-api-sdk.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-postgres/references/management-api-sdk.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-postgres/references/management-api-sdk.md)","tokens":300,"sizeBytes":242},{"name":"management-api.md","path":".agents/skills/prisma-postgres/references/management-api.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-postgres/references/management-api.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-postgres/references/management-api.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-postgres/references/management-api.md)","tokens":300,"sizeBytes":234},{"name":"SKILL.md","path":".agents/skills/prisma-postgres/SKILL.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-postgres/SKILL.md","title":"prisma-postgres","category":"anthropic-skill","format":"markdown","content":"---\nname: prisma-postgres\ndescription: Prisma Postgres setup and operations guidance across Console, create-db CLI, Management API, and Management API SDK. Use when creating Prisma Postgres databases, working in Prisma Console, provisioning with create-db/create-pg/create-postgres, or integrating programmatic provisioning with service tokens or OAuth.\nlicense: MIT\nmetadata:\n  author: prisma\n  version: \"1.0.0\"\n---\n\n# Prisma Postgres\n\nGuidance for creating, managing, and integrating Prisma Postgres across interactive and programmatic workflows.\n\n## When to Apply\n\nReference this skill when:\n- Setting up Prisma Postgres from Prisma Console\n- Provisioning instant temporary databases with `create-db`\n- Managing Prisma Postgres resources via Management API\n- Using `@prisma/management-api-sdk` in TypeScript/JavaScript\n- Handling claim URLs, connection strings, regions, and auth flows\n\n## Rule Categories by Priority\n\n| Priority | Category | Impact | Prefix |\n|----------|----------|--------|--------|\n| 1 | CLI Provisioning | CRITICAL | `create-db-cli` |\n| 2 | Management API | CRITICAL | `management-api` |\n| 3 | Management API SDK | HIGH | `management-api-sdk` |\n| 4 | Console and Connections | HIGH | `console-and-connections` |\n\n## Quick Reference\n\n- `create-db-cli` - instant databases and CLI options\n- `management-api` - service token and OAuth API workflows\n- `management-api-sdk` - typed SDK usage with token storage\n- `console-and-connections` - Console operations and connection setup\n\n## Core Workflows\n\n### 1. Console-first workflow\n\nUse Prisma Console for manual setup and operations:\n\n- Open `https://console.prisma.io`\n- Create/select workspace and project\n- Use Studio in the project sidebar to view/edit data\n- Retrieve direct connection details from the project UI\n\n### 2. Quick provisioning with create-db\n\nUse `create-db` when you need a database immediately:\n\n```bash\nnpx create-db@latest\n```\n\nAliases:\n\n```bash\nnpx create-pg@latest\nnpx create-postgres@latest\n```\n\nFor app integrations, you can also use the programmatic API (`create()` / `regions()`) from the `create-db` npm package.\n\nTemporary databases auto-delete after ~24 hours unless claimed.\n\n### 3. Programmatic provisioning with Management API\n\nUse API endpoints on:\n\n```text\nhttps://api.prisma.io/v1\n```\n\nExplore the schema and endpoints using:\n\n- OpenAPI docs: `https://api.prisma.io/v1/doc`\n- Swagger Editor: `https://api.prisma.io/v1/swagger-editor`\n\nAuth options:\n\n- Service token (workspace server-to-server)\n- OAuth 2.0 (act on behalf of users)\n\n### 4. Type-safe integration with Management API SDK\n\nInstall and use:\n\n```bash\nnpm install @prisma/management-api-sdk\n```\n\nUse `createManagementApiClient` for existing tokens, or `createManagementApiSdk` for OAuth + token refresh.\n\n## Rule Files\n\nDetailed guidance lives in:\n\n```\nreferences/console-and-connections.md\nreferences/create-db-cli.md\nreferences/management-api.md\nreferences/management-api-sdk.md\n```\n\n## How to Use\n\nStart with `references/create-db-cli.md` for fast setup, then switch to `references/management-api.md` or `references/management-api-sdk.md` when you need programmatic provisioning.\n","frontmatter":{"name":"prisma-postgres","description":"Prisma Postgres setup and operations guidance across Console, create-db CLI, Management API, and Management API SDK. Use when creating Prisma Postgres databases, working in Prisma Console, provisioning with create-db/create-pg/create-postgres, or integrating programmatic provisioning with service tokens or OAuth.","license":"MIT","metadata":{"author":"prisma","version":"1.0.0"}},"isInternal":false,"tokens":735,"sizeBytes":3152},{"name":"accelerate-users.md","path":".agents/skills/prisma-upgrade-v7/references/accelerate-users.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/accelerate-users.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/accelerate-users.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/accelerate-users.md)","tokens":300,"sizeBytes":242},{"name":"driver-adapters.md","path":".agents/skills/prisma-upgrade-v7/references/driver-adapters.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/driver-adapters.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/driver-adapters.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/driver-adapters.md)","tokens":300,"sizeBytes":240},{"name":"env-variables.md","path":".agents/skills/prisma-upgrade-v7/references/env-variables.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/env-variables.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/env-variables.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/env-variables.md)","tokens":300,"sizeBytes":236},{"name":"esm-support.md","path":".agents/skills/prisma-upgrade-v7/references/esm-support.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/esm-support.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/esm-support.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/esm-support.md)","tokens":300,"sizeBytes":232},{"name":"prisma-config.md","path":".agents/skills/prisma-upgrade-v7/references/prisma-config.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/prisma-config.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/prisma-config.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/prisma-config.md)","tokens":300,"sizeBytes":236},{"name":"removed-features.md","path":".agents/skills/prisma-upgrade-v7/references/removed-features.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/removed-features.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/removed-features.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/removed-features.md)","tokens":300,"sizeBytes":242},{"name":"schema-changes.md","path":".agents/skills/prisma-upgrade-v7/references/schema-changes.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/schema-changes.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# References Skill\n\nPath: `.agents/skills/prisma-upgrade-v7/references/schema-changes.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/references/schema-changes.md)","tokens":300,"sizeBytes":238},{"name":"SKILL.md","path":".agents/skills/prisma-upgrade-v7/SKILL.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-upgrade-v7/SKILL.md","title":"prisma-upgrade-v7","category":"anthropic-skill","format":"markdown","content":"---\nname: prisma-upgrade-v7\ndescription: Complete migration guide from Prisma ORM v6 to v7 covering all breaking changes. Use when upgrading Prisma versions, encountering v7 errors, or migrating existing projects. Triggers on \"upgrade to prisma 7\", \"prisma 7 migration\", \"prisma-client generator\", \"driver adapter required\".\nlicense: MIT\nmetadata:\n  author: prisma\n  version: \"7.0.0\"\n---\n\n# Upgrade to Prisma ORM 7\n\nComplete guide for migrating from Prisma ORM v6 to v7. This upgrade introduces significant breaking changes including ESM-only support, required driver adapters, and a new configuration system.\n\n## When to Apply\n\nReference this skill when:\n- Upgrading from Prisma v6 to v7\n- Updating to the `prisma-client` generator\n- Setting up driver adapters\n- Configuring `prisma.config.ts`\n- Fixing import errors after upgrade\n\n## Rule Categories by Priority\n\n| Priority | Category | Impact | Prefix |\n|----------|----------|--------|--------|\n| 1 | Schema Migration | CRITICAL | `schema-changes` |\n| 2 | Database Connectivity | CRITICAL | `driver-adapters` |\n| 3 | Module System | CRITICAL | `esm-support` |\n| 4 | Config and Env | HIGH | `prisma-config`, `env-variables` |\n| 5 | Removed Features | HIGH | `removed-features` |\n| 6 | Accelerate | HIGH | `accelerate-users` |\n\n## Quick Reference\n\n- `schema-changes` - generator and output migration for Prisma v7\n- `driver-adapters` - required adapter installation and client wiring\n- `esm-support` - package/module requirements for ESM\n- `prisma-config` - creating and using `prisma.config.ts`\n- `env-variables` - explicit environment loading\n- `removed-features` - removed middleware, metrics, and flags\n- `accelerate-users` - migration notes for Accelerate users\n\n## Important Notes\n\n- **MongoDB not yet supported in v7** - Continue using v6 for MongoDB\n- **Node.js 20.19.0+** required\n- **TypeScript 5.4.0+** required\n\n## Upgrade Steps Overview\n\n1. Update packages to v7\n2. Configure ESM in package.json\n3. Update TypeScript configuration\n4. Update schema generator block\n5. Create prisma.config.ts\n6. Install and configure driver adapter\n7. Update Prisma Client imports\n8. Update client instantiation\n9. Remove deprecated code (middleware, env vars)\n10. Run generate and test\n\n## Quick Upgrade Commands\n\n```bash\n# Update packages\nnpm install @prisma/client@7\nnpm install -D prisma@7\n\n# Install driver adapter (PostgreSQL example)\nnpm install @prisma/adapter-pg\n\n# Install dotenv for env loading\nnpm install dotenv\n\n# Regenerate client\nnpx prisma generate\n```\n\n## Breaking Changes Summary\n\n| Change | v6 | v7 |\n|--------|----|----|\n| Module format | CommonJS | ESM only |\n| Generator provider | `prisma-client-js` | `prisma-client` |\n| Output path | Auto (node_modules) | Required explicit |\n| Driver adapters | Optional | Required |\n| Config file | `.env` + schema | `prisma.config.ts` |\n| Env loading | Automatic | Manual (dotenv) |\n| Middleware | `$use()` | Client Extensions |\n| Metrics | Preview feature | Removed |\n\n## Rule Files\n\nDetailed migration guides for each breaking change:\n\n```\nreferences/esm-support.md        - ESM module configuration\nreferences/schema-changes.md     - Generator and schema updates\nreferences/driver-adapters.md    - Required driver adapter setup\nreferences/prisma-config.md      - New configuration file\nreferences/env-variables.md      - Environment variable loading\nreferences/removed-features.md   - Middleware, metrics, and CLI flags\nreferences/accelerate-users.md   - Special handling for Accelerate\n```\n\n## Step-by-Step Migration\n\n### 1. Update package.json for ESM\n\n```json\n{\n  \"type\": \"module\"\n}\n```\n\n### 2. Update tsconfig.json\n\n```json\n{\n  \"compilerOptions\": {\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"target\": \"ES2023\",\n    \"strict\": true,\n    \"esModuleInterop\": true\n  }\n}\n```\n\n### 3. Update schema.prisma\n\n```prisma\n// Before (v6)\ngenerator client {\n  provider = \"prisma-client-js\"\n}\n\n// After (v7)\ngenerator client {\n  provider = \"prisma-client\"\n  output   = \"../generated\"\n}\n```\n\n### 4. Create prisma.config.ts\n\n```typescript\nimport 'dotenv/config'\nimport { defineConfig, env } from 'prisma/config'\n\nexport default defineConfig({\n  schema: 'prisma/schema.prisma',\n  migrations: {\n    path: 'prisma/migrations',\n  },\n  datasource: {\n    url: env('DATABASE_URL'),\n  },\n})\n```\n\n### 5. Install driver adapter\n\n```bash\n# PostgreSQL\nnpm install @prisma/adapter-pg\n\n# MySQL\nnpm install @prisma/adapter-mariadb mariadb\n\n# SQLite\nnpm install @prisma/adapter-better-sqlite3\n\n# Prisma Postgres\nnpm install @prisma/adapter-ppg @prisma/ppg\n\n# Neon\nnpm install @prisma/adapter-neon\n```\n\n### 6. Update client instantiation\n\n```typescript\n// Before (v6)\nimport { PrismaClient } from '@prisma/client'\nconst prisma = new PrismaClient()\n\n// After (v7)\nimport { PrismaClient } from '../generated/client'\nimport { PrismaPg } from '@prisma/adapter-pg'\n\nconst adapter = new PrismaPg({\n  connectionString: process.env.DATABASE_URL\n})\nconst prisma = new PrismaClient({ adapter })\n```\n\n### 7. Run migrations and generate\n\n```bash\nnpx prisma generate\nnpx prisma migrate dev  # if needed\n```\n\n## Troubleshooting\n\n### \"Cannot find module\" errors\n- Check `output` path in generator block matches import path\n- Ensure `prisma generate` ran successfully\n\n### SSL certificate errors\n- Add `ssl: { rejectUnauthorized: false }` to adapter config\n- Or properly configure SSL certificates\n\n### Connection timeout issues\n- Driver adapters use different pool defaults\n- Configure pool settings explicitly on the adapter\n\n## Resources\n\n- [Official v7 Upgrade Guide](https://www.prisma.io/docs/orm/more/upgrade-guides/upgrading-versions/upgrading-to-prisma-7)\n- [Driver Adapters Documentation](https://www.prisma.io/docs/orm/overview/databases/database-drivers)\n- [Prisma Config Reference](https://www.prisma.io/docs/orm/reference/prisma-config-reference)\n\n## How to Use\n\nFollow `references/schema-changes.md` and `references/driver-adapters.md` first, then apply the remaining reference files based on your project setup.\n","frontmatter":{"name":"prisma-upgrade-v7","description":"Complete migration guide from Prisma ORM v6 to v7 covering all breaking changes. Use when upgrading Prisma versions, encountering v7 errors, or migrating existing projects. Triggers on \"upgrade to prisma 7\", \"prisma 7 migration\", \"prisma-client generator\", \"driver adapter required\".","license":"MIT","metadata":{"author":"prisma","version":"7.0.0"}},"isInternal":false,"tokens":1524,"sizeBytes":6014},{"name":"SKILL.md","path":".agents/skills/two-factor-authentication-best-practices/SKILL.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/two-factor-authentication-best-practices/SKILL.md","title":"two-factor-authentication-best-practices","category":"anthropic-skill","format":"markdown","content":"---\nname: two-factor-authentication-best-practices\ndescription: This skill provides guidance and enforcement rules for implementing secure two-factor authentication (2FA) using Better Auth's twoFactor plugin.\n---\n\n## Setting Up Two-Factor Authentication\n\nWhen adding 2FA to your application, configure the `twoFactor` plugin with your app name as the issuer. This name appears in authenticator apps when users scan the QR code.\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { twoFactor } from \"better-auth/plugins\";\n\nexport const auth = betterAuth({\n  appName: \"My App\", // Used as the default issuer for TOTP\n  plugins: [\n    twoFactor({\n      issuer: \"My App\", // Optional: override the app name for 2FA specifically\n    }),\n  ],\n});\n```\n\n**Note**: After adding the plugin, run `npx @better-auth/cli migrate` to add the required database fields and tables.\n\n### Client-Side Setup\n\nAdd the client plugin and configure the redirect behavior for 2FA verification:\n\n```ts\nimport { createAuthClient } from \"better-auth/client\";\nimport { twoFactorClient } from \"better-auth/client/plugins\";\n\nexport const authClient = createAuthClient({\n  plugins: [\n    twoFactorClient({\n      onTwoFactorRedirect() {\n        window.location.href = \"/2fa\"; // Redirect to your 2FA verification page\n      },\n    }),\n  ],\n});\n```\n\n## Enabling 2FA for Users\n\nWhen a user enables 2FA, require their password for verification. The enable endpoint returns a TOTP URI for QR code generation and backup codes for account recovery.\n\n```ts\nconst enable2FA = async (password: string) => {\n  const { data, error } = await authClient.twoFactor.enable({\n    password,\n  });\n\n  if (data) {\n    // data.totpURI - Use this to generate a QR code\n    // data.backupCodes - Display these to the user for safekeeping\n  }\n};\n```\n\n**Important**: The `twoFactorEnabled` flag on the user is not set to `true` until the user successfully verifies their first TOTP code. This ensures users have properly configured their authenticator app before 2FA is fully active.\n\n### Skipping Initial Verification\n\nIf you want to enable 2FA immediately without requiring verification, set `skipVerificationOnEnable`:\n\n```ts\ntwoFactor({\n  skipVerificationOnEnable: true, // Not recommended for most use cases\n});\n```\n\n**Note**: This is generally not recommended as it doesn't confirm the user has successfully set up their authenticator app.\n\n## TOTP (Authenticator App)\n\nTOTP generates time-based codes using an authenticator app (Google Authenticator, Authy, etc.). Codes are valid for 30 seconds by default.\n\n### Displaying the QR Code\n\nUse the TOTP URI to generate a QR code for users to scan:\n\n```tsx\nimport QRCode from \"react-qr-code\";\n\nconst TotpSetup = ({ totpURI }: { totpURI: string }) => {\n  return <QRCode value={totpURI} />;\n};\n```\n\n### Verifying TOTP Codes\n\nBetter Auth accepts codes from one period before and one after the current time, accommodating minor clock differences between devices:\n\n```ts\nconst verifyTotp = async (code: string) => {\n  const { data, error } = await authClient.twoFactor.verifyTotp({\n    code,\n    trustDevice: true, // Optional: remember this device for 30 days\n  });\n};\n```\n\n### TOTP Configuration Options\n\n```ts\ntwoFactor({\n  totpOptions: {\n    digits: 6, // 6 or 8 digits (default: 6)\n    period: 30, // Code validity period in seconds (default: 30)\n  },\n});\n```\n\n## OTP (Email/SMS)\n\nOTP sends a one-time code to the user's email or phone. You must implement the `sendOTP` function to deliver codes.\n\n### Configuring OTP Delivery\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { twoFactor } from \"better-auth/plugins\";\nimport { sendEmail } from \"./email\";\n\nexport const auth = betterAuth({\n  plugins: [\n    twoFactor({\n      otpOptions: {\n        sendOTP: async ({ user, otp }, ctx) => {\n          await sendEmail({\n            to: user.email,\n            subject: \"Your verification code\",\n            text: `Your code is: ${otp}`,\n          });\n        },\n        period: 5, // Code validity in minutes (default: 3)\n        digits: 6, // Number of digits (default: 6)\n        allowedAttempts: 5, // Max verification attempts (default: 5)\n      },\n    }),\n  ],\n});\n```\n\n### Sending and Verifying OTP\n\n```ts\n// Request an OTP to be sent\nconst sendOtp = async () => {\n  const { data, error } = await authClient.twoFactor.sendOtp();\n};\n\n// Verify the OTP code\nconst verifyOtp = async (code: string) => {\n  const { data, error } = await authClient.twoFactor.verifyOtp({\n    code,\n    trustDevice: true,\n  });\n};\n```\n\n### OTP Storage Security\n\nConfigure how OTP codes are stored in the database:\n\n```ts\ntwoFactor({\n  otpOptions: {\n    storeOTP: \"encrypted\", // Options: \"plain\", \"encrypted\", \"hashed\"\n  },\n});\n```\n\nFor custom encryption:\n\n```ts\ntwoFactor({\n  otpOptions: {\n    storeOTP: {\n      encrypt: async (token) => myEncrypt(token),\n      decrypt: async (token) => myDecrypt(token),\n    },\n  },\n});\n```\n\n## Backup Codes\n\nBackup codes provide account recovery when users lose access to their authenticator app or phone. They are generated automatically when 2FA is enabled.\n\n### Displaying Backup Codes\n\nAlways show backup codes to users when they enable 2FA:\n\n```tsx\nconst BackupCodes = ({ codes }: { codes: string[] }) => {\n  return (\n    <div>\n      <p>Save these codes in a secure location:</p>\n      <ul>\n        {codes.map((code, i) => (\n          <li key={i}>{code}</li>\n        ))}\n      </ul>\n    </div>\n  );\n};\n```\n\n### Regenerating Backup Codes\n\nWhen users need new codes, regenerate them (this invalidates all previous codes):\n\n```ts\nconst regenerateBackupCodes = async (password: string) => {\n  const { data, error } = await authClient.twoFactor.generateBackupCodes({\n    password,\n  });\n  // data.backupCodes contains the new codes\n};\n```\n\n### Using Backup Codes for Recovery\n\n```ts\nconst verifyBackupCode = async (code: string) => {\n  const { data, error } = await authClient.twoFactor.verifyBackupCode({\n    code,\n    trustDevice: true,\n  });\n};\n```\n\n**Note**: Each backup code can only be used once and is removed from the database after successful verification.\n\n### Backup Code Configuration\n\n```ts\ntwoFactor({\n  backupCodeOptions: {\n    amount: 10, // Number of codes to generate (default: 10)\n    length: 10, // Length of each code (default: 10)\n    storeBackupCodes: \"encrypted\", // Options: \"plain\", \"encrypted\"\n  },\n});\n```\n\n## Handling 2FA During Sign-In\n\nWhen a user with 2FA enabled signs in, the response includes `twoFactorRedirect: true`:\n\n```ts\nconst signIn = async (email: string, password: string) => {\n  const { data, error } = await authClient.signIn.email(\n    {\n      email,\n      password,\n    },\n    {\n      onSuccess(context) {\n        if (context.data.twoFactorRedirect) {\n          // Redirect to 2FA verification page\n          window.location.href = \"/2fa\";\n        }\n      },\n    }\n  );\n};\n```\n\n### Server-Side 2FA Detection\n\nWhen using `auth.api.signInEmail` on the server, check for 2FA redirect:\n\n```ts\nconst response = await auth.api.signInEmail({\n  body: {\n    email: \"user@example.com\",\n    password: \"password\",\n  },\n});\n\nif (\"twoFactorRedirect\" in response) {\n  // Handle 2FA verification\n}\n```\n\n## Trusted Devices\n\nTrusted devices allow users to skip 2FA verification on subsequent sign-ins for a configurable period.\n\n### Enabling Trust on Verification\n\nPass `trustDevice: true` when verifying 2FA:\n\n```ts\nawait authClient.twoFactor.verifyTotp({\n  code: \"123456\",\n  trustDevice: true,\n});\n```\n\n### Configuring Trust Duration\n\n```ts\ntwoFactor({\n  trustDeviceMaxAge: 30 * 24 * 60 * 60, // 30 days in seconds (default)\n});\n```\n\n**Note**: The trust period refreshes on each successful sign-in within the trust window.\n\n## Security Considerations\n\n### Session Management\n\nDuring the 2FA flow:\n\n1. User signs in with credentials\n2. Session cookie is removed (not yet authenticated)\n3. A temporary two-factor cookie is set (default: 10-minute expiration)\n4. User verifies via TOTP, OTP, or backup code\n5. Session cookie is created upon successful verification\n\nConfigure the two-factor cookie expiration:\n\n```ts\ntwoFactor({\n  twoFactorCookieMaxAge: 600, // 10 minutes in seconds (default)\n});\n```\n\n### Rate Limiting\n\nBetter Auth applies built-in rate limiting to all 2FA endpoints (3 requests per 10 seconds). For OTP verification, additional attempt limiting is applied:\n\n```ts\ntwoFactor({\n  otpOptions: {\n    allowedAttempts: 5, // Max attempts per OTP code (default: 5)\n  },\n});\n```\n\n### Encryption at Rest\n\n- TOTP secrets are encrypted using symmetric encryption with your auth secret\n- Backup codes are stored encrypted by default\n- OTP codes can be configured for plain, encrypted, or hashed storage\n\n### Constant-Time Comparison\n\nBetter Auth uses constant-time comparison for OTP verification to prevent timing attacks.\n\n### Credential Account Requirement\n\nTwo-factor authentication can only be enabled for credential (email/password) accounts. For social accounts, it's assumed the provider already handles 2FA.\n\n## Disabling 2FA\n\nAllow users to disable 2FA with password confirmation:\n\n```ts\nconst disable2FA = async (password: string) => {\n  const { data, error } = await authClient.twoFactor.disable({\n    password,\n  });\n};\n```\n\n**Note**: When 2FA is disabled, trusted device records are revoked.\n\n## Complete Configuration Example\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { twoFactor } from \"better-auth/plugins\";\nimport { sendEmail } from \"./email\";\n\nexport const auth = betterAuth({\n  appName: \"My App\",\n  plugins: [\n    twoFactor({\n      // TOTP settings\n      issuer: \"My App\",\n      totpOptions: {\n        digits: 6,\n        period: 30,\n      },\n      // OTP settings\n      otpOptions: {\n        sendOTP: async ({ user, otp }) => {\n          await sendEmail({\n            to: user.email,\n            subject: \"Your verification code\",\n            text: `Your code is: ${otp}`,\n          });\n        },\n        period: 5,\n        allowedAttempts: 5,\n        storeOTP: \"encrypted\",\n      },\n      // Backup code settings\n      backupCodeOptions: {\n        amount: 10,\n        length: 10,\n        storeBackupCodes: \"encrypted\",\n      },\n      // Session settings\n      twoFactorCookieMaxAge: 600, // 10 minutes\n      trustDeviceMaxAge: 30 * 24 * 60 * 60, // 30 days\n    }),\n  ],\n});\n```\n","frontmatter":{"name":"two-factor-authentication-best-practices","description":"This skill provides guidance and enforcement rules for implementing secure two-factor authentication (2FA) using Better Auth's twoFactor plugin."},"isInternal":false,"tokens":2485,"sizeBytes":10272},{"name":"SKILL.md","path":".claude/skills/fix-crowdin-pr/SKILL.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.claude/skills/fix-crowdin-pr/SKILL.md","title":"fix-crowdin-pr","category":"anthropic-skill","format":"markdown","content":"---\nname: fix-crowdin-pr\ndescription: Fix coderabbit review findings on a Crowdin translation PR (l10n_main), keeping Crowdin and the PR branch in sync. Takes an optional PR number, defaults to the open \"🌐 New Crowdin updates\" PR.\n---\n\n# Fix Crowdin PR\n\nFix coderabbit findings on a Crowdin sync PR so the fix survives the next sync.\n\n## Why this order matters\n\nCrowdin force pushes `l10n_main` on every sync. A fix that lands only on the PR branch gets overwritten; a fix made only in Crowdin waits for the next sync. Therefore: upload corrections to Crowdin **first**, then push the identical changes to the branch. The next sync then exports identical content and the branch stays stable.\n\n## Credentials\n\nToken and project id live in the macOS Keychain:\n\n```\nsecurity find-generic-password -s rallly-billing.CROWDIN_PERSONAL_TOKEN -w\nsecurity find-generic-password -s rallly-billing.CROWDIN_PROJECT_ID -w\n```\n\nThe token is the secret: retrieve it inline, never echo it. The project id is not secret — it is 527308 and appears in API paths below. Files are namespaced under Crowdin branch `main` (branchId 10).\n\n## Steps\n\n### 1. Find findings\n\nDefault to the open PR titled \"🌐 New Crowdin updates\" (`gh pr list --search \"🌐 New Crowdin updates\" --state open`), or use the PR number given as an argument.\n\n```\ngh api repos/lukevella/rallly/pulls/<PR>/comments --paginate\n```\n\nFilter to comments authored by `coderabbitai`. Extract path, line, and the suggested fix from each body.\n\n### 2. Check out the branch\n\nCheck out `l10n_main` in the session worktree.\n\n### 3. Apply fixes\n\nJudge each finding on its merits — coderabbit is sometimes wrong. Skip anything that contradicts the English source in `apps/web/public/locales/en/`, `packages/emails/locales/en/`, or `apps/landing/public/locales/en/`, and note why.\n\nApply accepted fixes with a python script using exact string replacement:\n\n- Assert each old string occurs exactly once in the file before replacing.\n- After all edits, `json.load` every touched file to prove it still parses.\n\n### 4. Upload to Crowdin\n\nDerive the affected languages from the touched file paths (the locale directory segment), then upload.\n\n**Primary: Crowdin CLI** (works since `preserve_hierarchy: true` landed in crowdin.yml, PR #2717). One run per affected language:\n\n```\nCROWDIN_PERSONAL_TOKEN=$(security find-generic-password -s rallly-billing.CROWDIN_PERSONAL_TOKEN -w) \\\nCROWDIN_PROJECT_ID=$(security find-generic-password -s rallly-billing.CROWDIN_PROJECT_ID -w) \\\nnpx -y @crowdin/cli@4 upload translations -b main -l <crowdin-lang-id> --auto-approve-imported --no-progress\n```\n\nThe CLI uploads all translation files for the language, not just touched ones; identical translations are skipped by default, so this is harmless. Do not run `crowdin status` — the token scopes don't cover it.\n\n**Fallback: REST API** — use when the CLI fails or Java/npx is unavailable (it is faster and uploads only touched files, but needs hand-rolled requests). Per touched file and language:\n\n1. `POST https://api.crowdin.com/api/v2/storages` with headers `Authorization: Bearer <token>`, `Crowdin-API-FileName: <basename>`, `Content-Type: application/octet-stream`, body = raw file bytes. Response gives `storageId`.\n2. `POST https://api.crowdin.com/api/v2/projects/527308/translations/<crowdin-lang-id>` with JSON body:\n\n   ```json\n   {\"storageId\": <id>, \"fileId\": <fileId>, \"importEqSuggestions\": false, \"autoApproveImported\": true}\n   ```\n\nKnown fileIds: web `app.json` = 35, emails `emails.json` = 68, landing `blog.json` = 53, `common.json` = 55, `home.json` = 57, `pricing.json` = 59. If a fileId is unknown, list via `GET /api/v2/projects/527308/files`.\n\n### 5. Verify\n\nConfirm at least one fix landed as the current translation:\n\n```\nGET /api/v2/projects/527308/languages/<crowdin-lang-id>/translations?stringIds=<ids>&fileId=<fileId>\n```\n\nFind stringIds via `GET /api/v2/projects/527308/strings?fileId=<fileId>&filter=<key>&scope=identifier`.\n\nDo **not** use `GET /api/v2/projects/527308/translations?stringId=...` for verification — it lists suggestions oldest first and ignores orderBy, so it shows stale entries.\n\n### 6. Commit and push\n\nCommit to `l10n_main` with a `🌐` gitmoji message and push. `--no-verify` is acceptable in a fresh worktree (husky needs node_modules).\n\n### 7. Reply and resolve every comment\n\nCrowdin syncs keep adding new translations, and each sync can bring new coderabbit comments. Unresolved threads must mean \"not yet addressed\" — so after pushing, reply to **every** finding and resolve its thread, applied or skipped:\n\n- Applied: cite the fix commit sha and note the Crowdin upload was verified.\n- Skipped: state the reason (e.g. matches the English source, contradicts the file's register).\n\nReply to a review comment (comment ids come from the step 1 fetch):\n\n```\ngh api -X POST repos/lukevella/rallly/pulls/<PR>/comments/<comment-id>/replies -f body='...'\n```\n\nResolving needs GraphQL — list thread ids, then resolve the ones still open (coderabbit auto-resolves some threads when it sees the fix commit; skip those):\n\n```\ngh api graphql -f query='query { repository(owner: \"lukevella\", name: \"rallly\") { pullRequest(number: <PR>) { reviewThreads(first: 50) { nodes { id isResolved comments(first: 1) { nodes { databaseId } } } } } } }'\ngh api graphql -f query='mutation { resolveReviewThread(input: {threadId: \"<thread-id>\"}) { thread { isResolved } } }'\n```\n\n### 8. Report\n\nSummarize: findings applied, findings skipped and why, Crowdin verification result, pushed commit sha, and confirmation that all threads are replied to and resolved.\n\n## Language id mapping\n\nRepo locale dirs use two letter codes (`%two_letters_code%` in crowdin.yml) plus `pt-BR` and `zh-Hant`. Crowdin language ids differ for some:\n\n| Repo dir | Crowdin id |\n|----------|------------|\n| es | es-ES |\n| sv | sv-SE |\n| zh | zh-CN |\n| pt-BR | pt-BR |\n\nEverything else observed so far matches the two letter code. If unsure, check `targetLanguageIds` via `GET /api/v2/projects/527308` — an upload to a wrong language id fails loudly, so a mistake here can't corrupt data, only waste a round trip.\n\n## Gotchas\n\n- The Bash tool sandbox drops PATH inside shell functions. Make the REST API calls from a python script that reads the token via `subprocess` + `security`, not from shell functions wrapping curl.\n- Never write the token to a file, echo it, or pass it where it lands in logs. Keychain lookups inline only.\n- Findings on languages you can't judge: prefer skipping over guessing. A wrong \"fix\" auto-approved into Crowdin displaces a human translation.\n","frontmatter":{"name":"fix-crowdin-pr","description":"Fix coderabbit review findings on a Crowdin translation PR (l10n_main), keeping Crowdin and the PR branch in sync. Takes an optional PR number, defaults to the open \"🌐 New Crowdin updates\" PR."},"isInternal":false,"tokens":1685,"sizeBytes":6627},{"name":"biome.json","path":"packages/billing/biome.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/billing/biome.json","title":"Billing Skill","category":"anthropic-skill","format":"json","content":"# Billing Skill\n\nPath: `packages/billing/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/billing/biome.json)","tokens":300,"sizeBytes":167},{"name":"package.json","path":"packages/billing/package.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/billing/package.json","title":"Billing Skill","category":"anthropic-skill","format":"json","content":"# Billing Skill\n\nPath: `packages/billing/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/billing/package.json)","tokens":300,"sizeBytes":171},{"name":"tsconfig.json","path":"packages/billing/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/billing/tsconfig.json","title":"Billing Skill","category":"anthropic-skill","format":"json","content":"# Billing Skill\n\nPath: `packages/billing/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/billing/tsconfig.json)","tokens":300,"sizeBytes":173},{"name":"biome.json","path":"packages/database/biome.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/database/biome.json","title":"Database Skill","category":"anthropic-skill","format":"json","content":"# Database Skill\n\nPath: `packages/database/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/database/biome.json)","tokens":300,"sizeBytes":170},{"name":"package.json","path":"packages/database/package.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/database/package.json","title":"Database Skill","category":"anthropic-skill","format":"json","content":"# Database Skill\n\nPath: `packages/database/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/database/package.json)","tokens":300,"sizeBytes":174},{"name":"tsconfig.json","path":"packages/database/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/database/tsconfig.json","title":"Database Skill","category":"anthropic-skill","format":"json","content":"# Database Skill\n\nPath: `packages/database/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/database/tsconfig.json)","tokens":300,"sizeBytes":176},{"name":"biome.json","path":"packages/dayjs/biome.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/dayjs/biome.json","title":"Dayjs Skill","category":"anthropic-skill","format":"json","content":"# Dayjs Skill\n\nPath: `packages/dayjs/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/dayjs/biome.json)","tokens":300,"sizeBytes":161},{"name":"package.json","path":"packages/dayjs/package.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/dayjs/package.json","title":"Dayjs Skill","category":"anthropic-skill","format":"json","content":"# Dayjs Skill\n\nPath: `packages/dayjs/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/dayjs/package.json)","tokens":300,"sizeBytes":165},{"name":"biome.json","path":"packages/emails/biome.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/biome.json","title":"Emails Skill","category":"anthropic-skill","format":"json","content":"# Emails Skill\n\nPath: `packages/emails/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/biome.json)","tokens":300,"sizeBytes":164},{"name":"emails.json","path":"packages/emails/locales/cs/emails.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/cs/emails.json","title":"Cs Skill","category":"anthropic-skill","format":"json","content":"# Cs Skill\n\nPath: `packages/emails/locales/cs/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/cs/emails.json)","tokens":300,"sizeBytes":184},{"name":"emails.json","path":"packages/emails/locales/da/emails.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/da/emails.json","title":"Da Skill","category":"anthropic-skill","format":"json","content":"# Da Skill\n\nPath: `packages/emails/locales/da/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/da/emails.json)","tokens":300,"sizeBytes":184},{"name":"emails.json","path":"packages/emails/locales/de/emails.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/de/emails.json","title":"De Skill","category":"anthropic-skill","format":"json","content":"# De Skill\n\nPath: `packages/emails/locales/de/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/de/emails.json)","tokens":300,"sizeBytes":184},{"name":"emails.json","path":"packages/emails/locales/en/emails.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/en/emails.json","title":"En Skill","category":"anthropic-skill","format":"json","content":"# En Skill\n\nPath: `packages/emails/locales/en/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/en/emails.json)","tokens":300,"sizeBytes":184},{"name":"emails.json","path":"packages/emails/locales/es/emails.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/es/emails.json","title":"Es Skill","category":"anthropic-skill","format":"json","content":"# Es Skill\n\nPath: `packages/emails/locales/es/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/es/emails.json)","tokens":300,"sizeBytes":184},{"name":"emails.json","path":"packages/emails/locales/fi/emails.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/fi/emails.json","title":"Fi Skill","category":"anthropic-skill","format":"json","content":"# Fi Skill\n\nPath: `packages/emails/locales/fi/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/fi/emails.json)","tokens":300,"sizeBytes":184},{"name":"emails.json","path":"packages/emails/locales/fr/emails.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/fr/emails.json","title":"Fr Skill","category":"anthropic-skill","format":"json","content":"# Fr Skill\n\nPath: `packages/emails/locales/fr/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/fr/emails.json)","tokens":300,"sizeBytes":184},{"name":"emails.json","path":"packages/emails/locales/hu/emails.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/hu/emails.json","title":"Hu Skill","category":"anthropic-skill","format":"json","content":"# Hu Skill\n\nPath: `packages/emails/locales/hu/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/hu/emails.json)","tokens":300,"sizeBytes":184},{"name":"emails.json","path":"packages/emails/locales/it/emails.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/it/emails.json","title":"It Skill","category":"anthropic-skill","format":"json","content":"# It Skill\n\nPath: `packages/emails/locales/it/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/it/emails.json)","tokens":300,"sizeBytes":184},{"name":"emails.json","path":"packages/emails/locales/nl/emails.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/nl/emails.json","title":"Nl Skill","category":"anthropic-skill","format":"json","content":"# Nl Skill\n\nPath: `packages/emails/locales/nl/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/nl/emails.json)","tokens":300,"sizeBytes":184},{"name":"emails.json","path":"packages/emails/locales/no/emails.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/no/emails.json","title":"No Skill","category":"anthropic-skill","format":"json","content":"# No Skill\n\nPath: `packages/emails/locales/no/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/no/emails.json)","tokens":300,"sizeBytes":184},{"name":"emails.json","path":"packages/emails/locales/pl/emails.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/pl/emails.json","title":"Pl Skill","category":"anthropic-skill","format":"json","content":"# Pl Skill\n\nPath: `packages/emails/locales/pl/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/pl/emails.json)","tokens":300,"sizeBytes":184},{"name":"emails.json","path":"packages/emails/locales/pt-BR/emails.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/pt-BR/emails.json","title":"Pt-BR Skill","category":"anthropic-skill","format":"json","content":"# Pt-BR Skill\n\nPath: `packages/emails/locales/pt-BR/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/pt-BR/emails.json)","tokens":300,"sizeBytes":193},{"name":"emails.json","path":"packages/emails/locales/ru/emails.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/ru/emails.json","title":"Ru Skill","category":"anthropic-skill","format":"json","content":"# Ru Skill\n\nPath: `packages/emails/locales/ru/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/ru/emails.json)","tokens":300,"sizeBytes":184},{"name":"emails.json","path":"packages/emails/locales/sv/emails.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/sv/emails.json","title":"Sv Skill","category":"anthropic-skill","format":"json","content":"# Sv Skill\n\nPath: `packages/emails/locales/sv/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/sv/emails.json)","tokens":300,"sizeBytes":184},{"name":"emails.json","path":"packages/emails/locales/zh/emails.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/zh/emails.json","title":"Zh Skill","category":"anthropic-skill","format":"json","content":"# Zh Skill\n\nPath: `packages/emails/locales/zh/emails.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/locales/zh/emails.json)","tokens":300,"sizeBytes":184},{"name":"package.json","path":"packages/emails/package.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/package.json","title":"Emails Skill","category":"anthropic-skill","format":"json","content":"# Emails Skill\n\nPath: `packages/emails/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/package.json)","tokens":300,"sizeBytes":168},{"name":"tsconfig.json","path":"packages/emails/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/tsconfig.json","title":"Emails Skill","category":"anthropic-skill","format":"json","content":"# Emails Skill\n\nPath: `packages/emails/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/emails/tsconfig.json)","tokens":300,"sizeBytes":170},{"name":"biome.json","path":"packages/languages/biome.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/languages/biome.json","title":"Languages Skill","category":"anthropic-skill","format":"json","content":"# Languages Skill\n\nPath: `packages/languages/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/languages/biome.json)","tokens":300,"sizeBytes":173},{"name":"package.json","path":"packages/languages/package.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/languages/package.json","title":"Languages Skill","category":"anthropic-skill","format":"json","content":"# Languages Skill\n\nPath: `packages/languages/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/languages/package.json)","tokens":300,"sizeBytes":177},{"name":"tsconfig.json","path":"packages/languages/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/languages/tsconfig.json","title":"Languages Skill","category":"anthropic-skill","format":"json","content":"# Languages Skill\n\nPath: `packages/languages/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/languages/tsconfig.json)","tokens":300,"sizeBytes":179},{"name":"package.json","path":"packages/logger/package.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/logger/package.json","title":"Logger Skill","category":"anthropic-skill","format":"json","content":"# Logger Skill\n\nPath: `packages/logger/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/logger/package.json)","tokens":300,"sizeBytes":168},{"name":"tsconfig.json","path":"packages/logger/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/logger/tsconfig.json","title":"Logger Skill","category":"anthropic-skill","format":"json","content":"# Logger Skill\n\nPath: `packages/logger/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/logger/tsconfig.json)","tokens":300,"sizeBytes":170},{"name":"biome.json","path":"packages/posthog/biome.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/posthog/biome.json","title":"Posthog Skill","category":"anthropic-skill","format":"json","content":"# Posthog Skill\n\nPath: `packages/posthog/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/posthog/biome.json)","tokens":300,"sizeBytes":167},{"name":"package.json","path":"packages/posthog/package.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/posthog/package.json","title":"Posthog Skill","category":"anthropic-skill","format":"json","content":"# Posthog Skill\n\nPath: `packages/posthog/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/posthog/package.json)","tokens":300,"sizeBytes":171},{"name":"tsconfig.json","path":"packages/posthog/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/posthog/tsconfig.json","title":"Posthog Skill","category":"anthropic-skill","format":"json","content":"# Posthog Skill\n\nPath: `packages/posthog/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/posthog/tsconfig.json)","tokens":300,"sizeBytes":173},{"name":"package.json","path":"packages/screenshots/package.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/screenshots/package.json","title":"Screenshots Skill","category":"anthropic-skill","format":"json","content":"# Screenshots Skill\n\nPath: `packages/screenshots/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/screenshots/package.json)","tokens":300,"sizeBytes":183},{"name":"tsconfig.json","path":"packages/screenshots/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/screenshots/tsconfig.json","title":"Screenshots Skill","category":"anthropic-skill","format":"json","content":"# Screenshots Skill\n\nPath: `packages/screenshots/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/screenshots/tsconfig.json)","tokens":300,"sizeBytes":185},{"name":"package.json","path":"packages/tailwind-config/package.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tailwind-config/package.json","title":"Tailwind-config Skill","category":"anthropic-skill","format":"json","content":"# Tailwind-config Skill\n\nPath: `packages/tailwind-config/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tailwind-config/package.json)","tokens":300,"sizeBytes":195},{"name":"package.json","path":"packages/test-helpers/package.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/test-helpers/package.json","title":"Test-helpers Skill","category":"anthropic-skill","format":"json","content":"# Test-helpers Skill\n\nPath: `packages/test-helpers/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/test-helpers/package.json)","tokens":300,"sizeBytes":186},{"name":"tsconfig.json","path":"packages/test-helpers/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/test-helpers/tsconfig.json","title":"Test-helpers Skill","category":"anthropic-skill","format":"json","content":"# Test-helpers Skill\n\nPath: `packages/test-helpers/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/test-helpers/tsconfig.json)","tokens":300,"sizeBytes":188},{"name":"next.json","path":"packages/tsconfig/next.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tsconfig/next.json","title":"Tsconfig Skill","category":"anthropic-skill","format":"json","content":"# Tsconfig Skill\n\nPath: `packages/tsconfig/next.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tsconfig/next.json)","tokens":300,"sizeBytes":168},{"name":"node.json","path":"packages/tsconfig/node.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tsconfig/node.json","title":"Tsconfig Skill","category":"anthropic-skill","format":"json","content":"# Tsconfig Skill\n\nPath: `packages/tsconfig/node.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tsconfig/node.json)","tokens":300,"sizeBytes":168},{"name":"package.json","path":"packages/tsconfig/package.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tsconfig/package.json","title":"Tsconfig Skill","category":"anthropic-skill","format":"json","content":"# Tsconfig Skill\n\nPath: `packages/tsconfig/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tsconfig/package.json)","tokens":300,"sizeBytes":174},{"name":"react.json","path":"packages/tsconfig/react.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tsconfig/react.json","title":"Tsconfig Skill","category":"anthropic-skill","format":"json","content":"# Tsconfig Skill\n\nPath: `packages/tsconfig/react.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/tsconfig/react.json)","tokens":300,"sizeBytes":170},{"name":"biome.json","path":"packages/ui/biome.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/ui/biome.json","title":"Ui Skill","category":"anthropic-skill","format":"json","content":"# Ui Skill\n\nPath: `packages/ui/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/ui/biome.json)","tokens":300,"sizeBytes":152},{"name":"components.json","path":"packages/ui/components.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/ui/components.json","title":"Ui Skill","category":"anthropic-skill","format":"json","content":"# Ui Skill\n\nPath: `packages/ui/components.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/ui/components.json)","tokens":300,"sizeBytes":162},{"name":"package.json","path":"packages/ui/package.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/ui/package.json","title":"Ui Skill","category":"anthropic-skill","format":"json","content":"# Ui Skill\n\nPath: `packages/ui/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/ui/package.json)","tokens":300,"sizeBytes":156},{"name":"tsconfig.json","path":"packages/ui/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/ui/tsconfig.json","title":"Ui Skill","category":"anthropic-skill","format":"json","content":"# Ui Skill\n\nPath: `packages/ui/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/ui/tsconfig.json)","tokens":300,"sizeBytes":158},{"name":"biome.json","path":"packages/utils/biome.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/utils/biome.json","title":"Utils Skill","category":"anthropic-skill","format":"json","content":"# Utils Skill\n\nPath: `packages/utils/biome.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/utils/biome.json)","tokens":300,"sizeBytes":161},{"name":"package.json","path":"packages/utils/package.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/utils/package.json","title":"Utils Skill","category":"anthropic-skill","format":"json","content":"# Utils Skill\n\nPath: `packages/utils/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/utils/package.json)","tokens":300,"sizeBytes":165},{"name":"tsconfig.json","path":"packages/utils/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/utils/tsconfig.json","title":"Utils Skill","category":"anthropic-skill","format":"json","content":"# Utils Skill\n\nPath: `packages/utils/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/packages/utils/tsconfig.json)","tokens":300,"sizeBytes":167},{"name":".cursorrules","path":".cursorrules","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/.cursorrules","title":"Cursor IDE Native Rules","category":"cursor-rule","format":"markdown","content":"# Cursor IDE Native Rules\n\nPath: `.cursorrules`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.cursorrules)","tokens":300,"sizeBytes":147},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/lukevella/rallly/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# Claude Agent Guidelines & System Prompt\n\nPath: `CLAUDE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/CLAUDE.md)","tokens":300,"sizeBytes":157}],"systemPromptSnippet":"<yakaai_skills repo=\"lukevella/rallly\">\n<!-- File: .agents/skills/better-auth-best-practices/SKILL.md (Tokens: ~300 | Category: anthropic-skill) -->\n# better-auth-best-practices\n\nPath: `.agents/skills/better-auth-best-practices/SKILL.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/better-auth-best-practices/SKILL.md)\n\n<!-- File: .agents/skills/better-auth-security-best-practices/SKILL.MD (Tokens: ~300 | Category: anthropic-skill) -->\n# better-auth-security-best-practices\n\nPath: `.agents/skills/better-auth-security-best-practices/SKILL.MD`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/better-auth-security-best-practices/SKILL.MD)\n\n<!-- File: .agents/skills/create-auth-skill/SKILL.md (Tokens: ~2994 | Category: anthropic-skill) -->\n---\nname: create-auth-skill\ndescription: Skill for creating auth layers in TypeScript/JavaScript apps using Better Auth.\n---\n\n# Create Auth Skill\n\nGuide for adding authentication to TypeScript/JavaScript applications using Better Auth.\n\n**For code examples and syntax, see [better-auth.com/docs](https://better-auth.com/docs).**\n\n---\n\n## Phase 1: Planning (REQUIRED before implementation)\n\nBefore writing any code, gather requirements by scanning the project and asking the user structured questions. This ensures the implementation matches their needs.\n\n### Step 1: Scan the project\n\nAnalyze the codebase to auto-detect:\n- **Framework** — Look for `next.config`, `svelte.config`, `nuxt.config`, `astro.config`, `vite.config`, or Express/Hono entry files.\n- **Database/ORM** — Look for `prisma/schema.prisma`, `drizzle.config`, `package.json` deps (`pg`, `mysql2`, `better-sqlite3`, `mongoose`, `mongodb`).\n- **Existing auth** — Look for existing auth libraries (`next-auth`, `lucia`, `clerk`, `supabase/auth`, `firebase/auth`) in `package.json` or imports.\n- **Package manager** — Check for `pnpm-lock.yaml`, `yarn.lock`, `bun.lockb`, or `package-lock.json`.\n\nUse what you find to pre-fill defaults and skip questions you can already answer.\n\n### Step 2: Ask planning questions\n\nUse the `AskQuestion` tool to ask the user **all applicable questions in a single call**. Skip any question you already have a confident answer for from the scan. Group them under a title like \"Auth Setup Planning\".\n\n**Questions to ask:**\n\n1. **Project type** (skip if detected)\n   - Prompt: \"What type of project is this?\"\n   - Options: New project from scratch | Adding auth to existing project | Migrating from another auth library\n\n2. **Framework** (skip if detected)\n   - Prompt: \"Which framework are you using?\"\n   - Options: Next.js (App Router) | Next.js (Pages Router) | SvelteKit | Nuxt | Astro | Express | Hono | SolidStart | Other\n\n3. **Database & ORM** (skip if detected)\n   - Prompt: \"Which database setup will you use?\"\n   - Options: PostgreSQL (Prisma) | PostgreSQL (Drizzle) | PostgreSQL (pg driver) | MySQL (Prisma) | MySQL (Drizzle) | MySQL (mysql2 driver) | SQLite (Prisma) | SQLite (Drizzle) | SQLite (better-sqlite3 driver) | MongoDB (Mongoose) | MongoDB (native driver)\n\n4. **Authentication methods** (always ask, allow multiple)\n   - Prompt: \"Which sign-in methods do you need?\"\n   - Options: Email & password | Social OAuth (Google, GitHub, etc.) | Magic link (passwordless email) | Passkey (WebAuthn) | Phone number\n   - `allow_multiple: true`\n\n5. **Social providers** (only if they selected Social OAuth above — ask in a follow-up call)\n   - Prompt: \"Which social providers do you need?\"\n   - Options: Google | GitHub | Apple | Microsoft | Discord | Twitter/X\n   - `allow_multiple: true`\n\n6. **Email verification** (only if Email & password was selected above — ask in a follow-up call)\n   - Prompt: \"Do you want to require email verification?\"\n   - Options: Yes | No\n\n7. **Email provider** (only if email verification is Yes, or if Password reset is selected in features — ask in a follow-up call)\n   - Prompt: \"How do you want to send emails?\"\n   - Options: Resend | Mock it for now (console.log)\n\n8. **Features & plugins** (always ask, allow multiple)\n   - Prompt: \"Which additional features do you need?\"\n   - Options: Two-factor authentication (2FA) | Organizations / teams | Admin dashboard | API bearer tokens | Password reset | None of these\n   - `allow_multiple: true`\n\n9. **Auth pages** (always ask, allow multiple — pre-select based on earlier answers)\n   - Prompt: \"Which auth pages do you need?\"\n   - Options vary based on previous answers:\n     - Always available: Sign in | Sign up\n     - If Email & password selected: Forgot password | Reset password\n     - If email verification enabled: Email verification\n   - `allow_multiple: true`\n\n10. **Auth UI style** (always ask)\n   - Prompt: \"What style do you want for the auth pages? Pick one or describe your own.\"\n   - Options: Minimal & clean | Centered card with background | Split layout (form + hero image) | Floating / glassmorphism | Other (I'll describe)\n\n### Step 3: Summarize the plan\n\nAfter collecting answers, present a concise implementation plan as a markdown checklist. Example:\n\n```\n## Auth Implementation Plan\n\n- **Framework:** Next.js (App Router)\n- **Database:** PostgreSQL via Prisma\n- **Auth methods:** Email/password, Google OAuth, GitHub OAuth\n- **Plugins:** 2FA, Organizations, Email verification\n- **UI:** Custom forms\n\n### Steps\n1. Install `better-auth` and `@better-auth/cli`\n2. Create `lib/auth.ts` with server config\n3. Create `lib/auth-client.ts` with React client\n4. Set up route handler at `app/api/auth/[...all]/route.ts`\n5. Configure Prisma adapter and generate schema\n6. Add Google & GitHub OAuth providers\n7. Enable `twoFactor` and `organization` plugins\n8. Set up email verification handler\n9. Run migrations\n10. Create sign-in / sign-up pages\n```\n\nAsk the user to confirm the plan before proceeding to Phase 2.\n\n---\n\n## Phase 2: Implementation\n\nOnly proceed here after the user confirms the plan from Phase 1.\n\nFollow the decision tree below, guided by the answers collected above.\n\n```\nIs this a new/empty project?\n├─ YES → New project setup\n│   1. Install better-auth (+ scoped packages per plan)\n│   2. Create auth.ts with all planned config\n│   3. Create auth-client.ts with framework client\n│   4. Set up route handler\n│   5. Set up environment variables\n│   6. Run CLI migrate/generate\n│   7. Add plugins from plan\n│   8. Create auth UI pages\n│\n├─ MIGRATING → Migration from existing auth\n│   1. Audit current auth for gaps\n│   2. Plan incremental migration\n│   3. Install better-auth alongside existing auth\n│   4. Migrate routes, then session logic, then UI\n│   5. Remove old auth library\n│   6. See migration guides in docs\n│\n└─ ADDING → Add auth to existing project\n    1. Analyze project structure\n    2. Install better-auth\n    3. Create auth config matching plan\n    4. Add route handler\n    5. Run schema migrations\n    6. Integrate into existing pages\n    7. Add planned plugins and features\n```\n\nAt the end of implementation, guide users thoroughly on remaining next steps (e.g., setting up OAuth app credentials, deploying env vars, testing flows).\n\n---\n\n## Installation\n\n**Core:** `npm install better-auth`\n\n**Scoped packages (as needed):**\n| Package | Use case |\n|---------|----------|\n| `@better-auth/passkey` | WebAuthn/Passkey auth |\n| `@better-auth/sso` | SAML/OIDC enterprise SSO |\n| `@better-auth/stripe` | Stripe payments |\n| `@better-auth/scim` | SCIM user provisioning |\n| `@better-auth/expo` | React Native/Expo |\n\n---\n\n## Environment Variables\n\n```env\nBETTER_AUTH_SECRET=<32+ chars, generate with: openssl rand -base64 32>\nBETTER_AUTH_URL=http://localhost:3000\nDATABASE_URL=<your database connection string>\n```\n\nAdd OAuth secrets as needed: `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`, `GOOGLE_CLIENT_ID`, etc.\n\n---\n\n## Server Config (auth.ts)\n\n**Location:** `lib/auth.ts` or `src/lib/auth.ts`\n\n**Minimal config needs:**\n- `database` - Connection or adapter\n- `emailAndPassword: { enabled: true }` - For email/password auth\n\n**Standard config adds:**\n- `socialProviders` - OAuth providers (google, github, etc.)\n- `emailVerification.sendVerificationEmail` - Email verification handler\n- `emailAndPassword.sendResetPassword` - Password reset handler\n\n**Full config adds:**\n- `plugins` - Array of feature plugins\n- `session` - Expiry, cookie cache settings\n- `account.accountLinking` - Multi-provider linking\n- `rateLimit` - Rate limiting config\n\n**Export types:** `export type Session = typeof auth.$Infer.Session`\n\n---\n\n## Client Config (auth-client.ts)\n\n**Import by framework:**\n| Framework | Import |\n|-----------|--------|\n| React/Next.js | `better-auth/react` |\n| Vue | `better-auth/vue` |\n| Svelte | `better-auth/svelte` |\n| Solid | `better-auth/solid` |\n| Vanilla JS | `better-auth/client` |\n\n**Client plugins** go in `createAuthClient({ plugins: [...] })`.\n\n**Common exports:** `signIn`, `signUp`, `signOut`, `useSession`, `getSession`\n\n---\n\n## Route Handler Setup\n\n| Framework | File | Handler |\n|-----------|------|---------|\n| Next.js App Router | `app/api/auth/[...all]/route.ts` | `toNextJsHandler(auth)` → export `{ GET, POST }` |\n| Next.js Pages | `pages/api/auth/[...all].ts` | `toNextJsHandler(auth)` → default export |\n| Express | Any file | `app.all(\"/api/auth/*\", toNodeHandler(auth))` |\n| SvelteKit | `src/hooks.server.ts` | `svelteKitHandler(auth)` |\n| SolidStart | Route file | `solidStartHandler(auth)` |\n| Hono | Route file | `auth.handler(c.req.raw)` |\n\n**Next.js Server Components:** Add `nextCookies()` plugin to auth config.\n\n---\n\n## Database Migrations\n\n| Adapter | Command |\n|---------|---------|\n| Built-in Kysely | `npx @better-auth/cli@latest migrate` (applies directly) |\n| Prisma | `npx @better-auth/cli@latest generate --output prisma/schema.prisma` then `npx prisma migrate dev` |\n| Drizzle | `npx @better-auth/cli@latest generate --output src/db/auth-schema.ts` then `npx drizzle-kit push` |\n\n**Re-run after adding plugins.**\n\n---\n\n## Database Adapters\n\n| Database | Setup |\n|----------|-------|\n| SQLite | Pass `better-sqlite3` or `bun:sqlite` instance directly |\n| PostgreSQL | Pass `pg.Pool` instance directly |\n| MySQL | Pass `mysql2` pool directly |\n| Prisma | `prismaAdapter(prisma, { provider: \"postgresql\" })` from `better-auth/adapters/prisma` |\n| Drizzle | `drizzleAdapter(db, { provider: \"pg\" })` from `better-auth/adapters/drizzle` |\n| MongoDB | `mongodbAdapter(db)` from `better-auth/adapters/mongodb` |\n\n---\n\n## Common Plugins\n\n| Plugin | Server Import | Client Import | Purpose |\n|--------|---------------|---------------|---------|\n| `twoFactor` | `better-auth/plugins` | `twoFactorClient` | 2FA with TOTP/OTP |\n| `organization` | `better-auth/plugins` | `organizationClient` | Teams/orgs |\n| `admin` | `better-auth/plugins` | `adminClient` | User management |\n| `bearer` | `better-auth/plugins` | - | API token auth |\n| `openAPI` | `better-auth/plugins` | - | API docs |\n| `passkey` | `@better-auth/passkey` | `passkeyClient` | WebAuthn |\n| `sso` | `@better-auth/sso` | - | Enterprise SSO |\n\n**Plugin pattern:** Server plugin + client plugin + run migrations.\n\n---\n\n## Auth UI Implementation\n\n**Sign in flow:**\n1. `signIn.email({ email, password })` or `signIn.social({ provider, callbackURL })`\n2. Handle `error` in response\n3. Redirect on success\n\n**Session check (client):** `useSession()` hook returns `{ data: session, isPending }`\n\n**Session check (server):** `auth.api.getSession({ headers: await headers() })`\n\n**Protected routes:** Check session, redirect to `/sign-in` if null.\n\n---\n\n## Security Checklist\n\n- [ ] `BETTER_AUTH_SECRET` set (32+ chars)\n- [ ] `advanced.useSecureCookies: true` in production\n- [ ] `trustedOrigins` configured\n- [ ] Rate limits enabled\n- [ ] Email verification enabled\n- [ ] Password reset implemented\n- [ ] 2FA for sensitive apps\n- [ ] CSRF protection NOT disabled\n- [ ] `account.accountLinking` reviewed\n\n---\n\n## Troubleshooting\n\n| Issue | Fix |\n|-------|-----|\n| \"Secret not set\" | Add `BETTER_AUTH_SECRET` env var |\n| \"Invalid Origin\" | Add domain to `trustedOrigins` |\n| Cookies not setting | Check `baseURL` matches domain; enable secure cookies in prod |\n| OAuth callback errors | Verify redirect URIs in provider dashboard |\n| Type errors after adding plugin | Re-run CLI generate/migrate |\n\n---\n\n## Resources\n\n- [Docs](https://better-auth.com/docs)\n- [Examples](https://github.com/better-auth/examples)\n- [Plugins](https://better-auth.com/docs/concepts/plugins)\n- [CLI](https://better-auth.com/docs/concepts/cli)\n- [Migration Guides](https://better-auth.com/docs/guides)\n\n<!-- File: .agents/skills/devmoji/SKILL.md (Tokens: ~733 | Category: anthropic-skill) -->\n---\nname: devmoji\ndescription: Devmoji commit message emoji conventions. Use when writing git commit messages, formatting commit descriptions, or choosing the right emoji prefix for a commit. Triggers on \"commit message\", \"git commit\", \"commit emoji\", \"devmoji\".\nuser-invocable: false\n---\n\n# Devmoji Commit Conventions\n\nThis project uses emoji prefixes in commit messages following devmoji/gitmoji conventions. Every commit message MUST start with an emoji followed by a space, then the description.\n\n## Format\n\n```\n<emoji> <Short description>\n```\n\n**Examples from this repo:**\n- `🐛 Fix hydration error on dashboard page`\n- `✨ Add support for in-memory rate limiting`\n- `♻️ Move locale sync`\n- `⚡️ Preload dayjs locales`\n- `💄 Disable overscroll`\n\n## Emoji Reference\n\n| Emoji | Code | When to use |\n|-------|------|-------------|\n| ✨ | `:sparkles:` | New feature or capability |\n| 🐛 | `:bug:` | Bug fix |\n| ♻️ | `:recycle:` | Refactoring (no feature or fix) |\n| ⚡️ | `:zap:` | Performance improvement |\n| 💄 | `:lipstick:` | UI/style/cosmetic change |\n| 🔧 | `:wrench:` | Configuration file change |\n| 🔥 | `:fire:` | Remove code or files |\n| 📝 | `:memo:` | Documentation |\n| 🌐 | `:globe_with_meridians:` | Internationalization/localization |\n| 🔒️ | `:lock:` | Security fix or improvement |\n| ⬆️ | `:arrow_up:` | Upgrade dependencies |\n| 🚀 | `:rocket:` | Deploy or release |\n| 🔖 | `:bookmark:` | Version tag / release |\n| 👷 | `:construction_worker:` | CI/CD changes |\n| 🧑‍💻 | `:technologist:` | Developer experience improvement |\n| 🌱 | `:seedling:` | Seed data / database seeding |\n| 🔊 | `:loud_sound:` | Add or update logs |\n| ➕ | `:heavy_plus_sign:` | Add dependency |\n| ➖ | `:heavy_minus_sign:` | Remove dependency |\n| 🚨 | `:rotating_light:` | Fix linting / compiler warnings |\n| ✅ | `:white_check_mark:` | Add or update tests |\n| 🏗️ | `:building_construction:` | Architectural change |\n| 📦 | `:package:` | Build system change |\n| 🗃️ | `:card_file_box:` | Database / migration change |\n| 🩹 | `:adhesive_bandage:` | Simple fix for non-critical issue |\n| ⏪️ | `:rewind:` | Revert changes |\n\n## Rules\n\n1. **Always prefix** the commit message with exactly one primary emoji\n2. **No conventional commit type prefix** — use `🐛 Fix bug` not `fix: 🐛 Fix bug`\n3. **Capitalize** the first word after the emoji\n4. **No period** at the end of the subject line\n5. **Keep it short** — subject line under 72 characters\n6. Pick the **most specific** emoji that applies (e.g. `💄` for a pure CSS change, not `✨`)\n\n<!-- File: .agents/skills/email-and-password-best-practices/SKILL.md (Tokens: ~1704 | Category: anthropic-skill) -->\n---\nname: email-and-password-best-practices\ndescription: This skill provides guidance and enforcement rules for implementing secure email and password authentication using Better Auth.\n---\n\n## Email Verification Setup\n\nWhen enabling email/password authentication, configure `emailVerification.sendVerificationEmail` to verify user email addresses. This helps prevent fake sign-ups and ensures users have access to the email they registered with.\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { sendEmail } from \"./email\"; // your email sending function\n\nexport const auth = betterAuth({\n  emailVerification: {\n    sendVerificationEmail: async ({ user, url, token }, request) => {\n      await sendEmail({\n        to: user.email,\n        subject: \"Verify your email address\",\n        text: `Click the link to verify your email: ${url}`,\n      });\n    },\n  },\n});\n```\n\n**Note**: The `url` parameter contains the full verification link. The `token` is available if you need to build a custom verification URL.\n\n### Requiring Email Verification\n\nFor stricter security, enable `emailAndPassword.requireEmailVerification` to block sign-in until the user verifies their email. When enabled, unverified users will receive a new verification email on each sign-in attempt.\n\n```ts\nexport const auth = betterAuth({\n  emailAndPassword: {\n    requireEmailVerification: true,\n  },\n});\n```\n\n**Note**: This requires `sendVerificationEmail` to be configured and only applies to email/password sign-ins.\n\n## Client side validation\n\nWhile Better Auth validates inputs server-side, implementing client-side validation is still recommended for two key reasons:\n\n1. **Improved UX**: Users receive immediate feedback when inputs don't meet requirements, rather than waiting for a server round-trip.\n2. **Reduced server load**: Invalid requests are caught early, minimizing unnecessary network traffic to your auth server.\n\n## Callback URLs\n\nAlways use absolute URLs (including the origin) for callback URLs in sign-up and sign-in requests. This prevents Better Auth from needing to infer the origin, which can cause issues when your backend and frontend are on different domains.\n\n```ts\nconst { data, error } = await authClient.signUp.email({\n  callbackURL: \"https://example.com/callback\", // absolute URL with origin\n});\n```\n\n## Password Reset Flows\n\nPassword reset flows are essential to any email/password system, we recommend setting this up.\n\nTo allow users to reset a password first you need to provide `sendResetPassword` function to the email and password authenticator.\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { sendEmail } from \"./email\"; // your email sending function\n\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    // Custom email sending function to send reset-password email\n    sendResetPassword: async ({ user, url, token }, request) => {\n      void sendEmail({\n        to: user.email,\n        subject: \"Reset your password\",\n        text: `Click the link to reset your password: ${url}`,\n      });\n    },\n    // Optional event hook\n    onPasswordReset: async ({ user }, request) => {\n      // your logic here\n      console.log(`Password for user ${user.email} has been reset.`);\n    },\n  },\n});\n```\n\n### Security considerations\n\nBetter Auth implements several security measures in the password reset flow:\n\n#### Timing attack prevention\n\n- **Background email sending**: Better Auth uses `runInBackgroundOrAwait` internally to send reset emails without blocking the response. This prevents attackers from measuring response times to determine if an email exists.\n- **Dummy operations on invalid requests**: When a user is not found, Better Auth still performs token generation and a database lookup (with a dummy value) to maintain consistent response times.\n- **Constant response message**: The API always returns `\"If this email exists in our system, check your email for the reset link\"` regardless of whether the user exists.\n\nOn serverless platforms, configure a background task handler to ensure emails are sent reliably:\n\n```ts\nexport const auth = betterAuth({\n  advanced: {\n    backgroundTasks: {\n      handler: (promise) => {\n        // Use platform-specific methods like waitUntil\n        waitUntil(promise);\n      },\n    },\n  },\n});\n```\n\n#### Token security\n\n- **Cryptographically random tokens**: Reset tokens are generated using `generateId(24)`, producing a 24-character alphanumeric string (a-z, A-Z, 0-9) with high entropy.\n- **Token expiration**: Tokens expire after **1 hour** by default. Configure with `resetPasswordTokenExpiresIn` (in seconds):\n\n```ts\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    resetPasswordTokenExpiresIn: 60 * 30, // 30 minutes\n  },\n});\n```\n\n- **Single-use tokens**: Tokens are deleted immediately after successful password reset, preventing reuse.\n\n#### Session revocation\n\nEnable `revokeSessionsOnPasswordReset` to invalidate all existing sessions when a password is reset. This ensures that if an attacker has an active session, it will be terminated:\n\n```ts\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    revokeSessionsOnPasswordReset: true,\n  },\n});\n```\n\n#### Redirect URL validation\n\nThe `redirectTo` parameter is validated against your `trustedOrigins` configuration to prevent open redirect attacks. Malicious redirect URLs will be rejected with a 403 error.\n\n#### Password requirements\n\nDuring password reset, the new password must meet length requirements:\n- **Minimum**: 8 characters (default), configurable via `minPasswordLength`\n- **Maximum**: 128 characters (default), configurable via `maxPasswordLength`\n\n```ts\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    minPasswordLength: 12,\n    maxPasswordLength: 256,\n  },\n});\n```\n\n### Sending the password reset\n\nOnce the password reset configurations are set-up, you can now call the `requestPasswordReset` function to send reset password link to user. If the user exists, it will trigger the `sendResetPassword` function you provided in the auth config.\n\n```ts\nconst data = await auth.api.requestPasswordReset({\n  body: {\n    email: \"john.doe@example.com\", // required\n    redirectTo: \"https://example.com/reset-password\",\n  },\n});\n```\n\nOr authClient:\n\n```ts\nconst { data, error } = await authClient.requestPasswordReset({\n  email: \"john.doe@example.com\", // required\n  redirectTo: \"https://example.com/reset-password\",\n});\n```\n\n**Note**: While the `email` is required, we also recommend configuring the `redirectTo` for a smoother user experience.\n\n## Password Hashing\n\nBetter Auth uses `scrypt` by default for password hashing. This is a solid choice because:\n\n- It's designed to be slow and memory-intensive, making brute-force attacks costly\n- It's natively supported by Node.js (no external dependencies)\n- OWASP recommends it when Argon2id isn't available\n\n### Custom Hashing Algorithm\n\nTo use a different algorithm (e.g., Argon2id), provide custom `hash` and `verify` functions in the `emailAndPassword.password` configuration:\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { hash, verify, type Options } from \"@node-rs/argon2\";\n\nconst argon2Options: Options = {\n  memoryCost: 65536, // 64 MiB\n  timeCost: 3, // 3 iterations\n  parallelism: 4, // 4 parallel lanes\n  outputLen: 32, // 32 byte output\n  algorithm: 2, // Argon2id variant\n};\n\nexport const auth = betterAuth({\n  emailAndPassword: {\n    enabled: true,\n    password: {\n      hash: (password) => hash(password, argon2Options),\n      verify: ({ password, hash: storedHash }) =>\n        verify(storedHash, password, argon2Options),\n    },\n  },\n});\n```\n\n**Note**: If you switch hashing algorithms on an existing system, users with passwords hashed using the old algorithm won't be able to sign in. Plan a migration strategy if needed.\n\n<!-- File: .agents/skills/logging/SKILL.md (Tokens: ~681 | Category: anthropic-skill) -->\n---\nname: logging\ndescription: Wide events logging pattern and conventions for this project. Use when writing logging code, adding observability, or implementing request tracing.\nuser-invocable: false\n---\n\n# Logging Guidelines\n\nThis project follows the **Wide Events** pattern. Logs are optimized for querying, not writing.\n\n## Core Principle\n\nEmit **one comprehensive event per request per service** containing all contextual information. Do not scatter log statements throughout your code.\n\n**Wrong mental model:** Log what your code is doing\n**Correct mental model:** Log what happened to this request\n\n## Wide Event Structure\n\nEach event should include:\n\n```typescript\n{\n  // Request metadata\n  requestId: string,\n  traceId: string,\n  timestamp: Date,\n  service: string,\n\n  // HTTP details\n  method: string,\n  path: string,\n  statusCode: number,\n  durationMs: number,\n\n  // User context\n  userId?: string,\n  subscriptionTier?: string,\n\n  // Business context\n  pollId?: string,\n  spaceId?: string,\n  featureFlags: Record<string, boolean>,\n\n  // Error details (when applicable)\n  errorType?: string,\n  errorCode?: string,\n  errorMessage?: string,\n  isRetriable?: boolean,\n\n  // Performance metrics\n  dbQueryCount?: number,\n  dbQueryDurationMs?: number\n}\n```\n\n## Implementation Pattern\n\nUse middleware to build the event throughout the request lifecycle:\n\n1. Initialize event at request start with request/service metadata\n2. Enrich with user context after authentication\n3. Add business context as processing occurs\n4. Emit single event in finally block after request completes\n\n## Anti-Patterns\n\n**Never do these:**\n\n- Scattered `console.log` statements throughout code\n- String-based log messages without structured data\n- Low-context logs with only timestamp, level, and message\n- Multiple log lines for a single request\n\n**Example of what NOT to do:**\n\n```typescript\n// BAD - scattered, low-context logs\nconsole.log(\"Starting request\");\nconsole.log(\"User authenticated\");\nconsole.log(\"Fetching poll\");\nconsole.log(\"Request complete\");\n```\n\n**Example of correct approach:**\n\n```typescript\n// GOOD - single wide event with full context\nlogger.info({\n  requestId: ctx.requestId,\n  userId: ctx.user?.id,\n  pollId: params.pollId,\n  method: \"GET\",\n  path: \"/api/polls/:id\",\n  statusCode: 200,\n  durationMs: 45,\n  dbQueryCount: 2,\n  dbQueryDurationMs: 12\n});\n```\n\n## Tail Sampling\n\nWhen implementing log sampling, make decisions **after** request completion:\n\n**Always capture 100%:**\n- All errors (5xx status, exceptions)\n- Requests exceeding p99 latency\n- Pro/enterprise user requests\n- Requests with feature flags under rollout\n\n**Sample remaining traffic:** 1-5% of successful, fast requests\n\n## High-Cardinality Data\n\nInclude high-cardinality fields (userId, pollId, requestId). Modern observability tools handle this efficiently. Aim for 50+ fields per event. Include:\n\n- All IDs involved in the request\n- All feature flags evaluated\n- All external service calls made\n- Timing breakdown for significant operations\n- User tier and account metadata\n\n<!-- File: .agents/skills/organization-best-practices/SKILL.md (Tokens: ~2918 | Category: anthropic-skill) -->\n---\nname: organization-best-practices\ndescription: This skill provides guidance and enforcement rules for implementing multi-tenant organizations, teams, and role-based access control using Better Auth's organization plugin.\n---\n\n## Setting Up Organizations\n\nWhen adding organizations to your application, configure the `organization` plugin with appropriate limits and permissions.\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { organization } from \"better-auth/plugins\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n      allowUserToCreateOrganization: true,\n      organizationLimit: 5, // Max orgs per user\n      membershipLimit: 100, // Max members per org\n    }),\n  ],\n});\n```\n\n**Note**: After adding the plugin, run `npx @better-auth/cli migrate` to add the required database tables.\n\n### Client-Side Setup\n\nAdd the client plugin to access organization methods:\n\n```ts\nimport { createAuthClient } from \"better-auth/client\";\nimport { organizationClient } from \"better-auth/client/plugins\";\n\nexport const authClient = createAuthClient({\n  plugins: [organizationClient()],\n});\n```\n\n## Creating Organizations\n\nOrganizations are the top-level entity for grouping users. When created, the creator is automatically assigned the `owner` role.\n\n```ts\nconst createOrg = async () => {\n  const { data, error } = await authClient.organization.create({\n    name: \"My Company\",\n    slug: \"my-company\",\n    logo: \"https://example.com/logo.png\",\n    metadata: { plan: \"pro\" },\n  });\n};\n```\n\n### Controlling Organization Creation\n\nRestrict who can create organizations based on user attributes:\n\n```ts\norganization({\n  allowUserToCreateOrganization: async (user) => {\n    return user.emailVerified === true;\n  },\n  organizationLimit: async (user) => {\n    // Premium users get more organizations\n    return user.plan === \"premium\" ? 20 : 3;\n  },\n});\n```\n\n### Creating Organizations on Behalf of Users\n\nAdministrators can create organizations for other users (server-side only):\n\n```ts\nawait auth.api.createOrganization({\n  body: {\n    name: \"Client Organization\",\n    slug: \"client-org\",\n    userId: \"user-id-who-will-be-owner\", // `userId` is required\n  },\n});\n```\n\n**Note**: The `userId` parameter cannot be used alongside session headers.\n\n\n## Active Organizations\n\nThe active organization is stored in the session and scopes subsequent API calls. Always set an active organization after the user selects one.\n\n```ts\nconst setActive = async (organizationId: string) => {\n  const { data, error } = await authClient.organization.setActive({\n    organizationId,\n  });\n};\n```\n\nMany endpoints use the active organization when `organizationId` is not provided:\n\n```ts\n// These use the active organization automatically\nawait authClient.organization.listMembers();\nawait authClient.organization.listInvitations();\nawait authClient.organization.inviteMember({ email: \"user@example.com\", role: \"member\" });\n```\n\n### Getting Full Organization Data\n\nRetrieve the active organization with all its members, invitations, and teams:\n\n```ts\nconst { data } = await authClient.organization.getFullOrganization();\n// data.organization, data.members, data.invitations, data.teams\n```\n\n## Members\n\nMembers are users who belong to an organization. Each member has a role that determines their permissions.\n\n### Adding Members (Server-Side)\n\nAdd members directly without invitations (useful for admin operations):\n\n```ts\nawait auth.api.addMember({\n  body: {\n    userId: \"user-id\",\n    role: \"member\",\n    organizationId: \"org-id\",\n  },\n});\n```\n\n**Note**: For client-side member additions, use the invitation system instead.\n\n### Assigning Multiple Roles\n\nMembers can have multiple roles for fine-grained permissions:\n\n```ts\nawait auth.api.addMember({\n  body: {\n    userId: \"user-id\",\n    role: [\"admin\", \"moderator\"],\n    organizationId: \"org-id\",\n  },\n});\n```\n\n### Removing Members\n\nRemove members by ID or email:\n\n```ts\nawait authClient.organization.removeMember({\n  memberIdOrEmail: \"user@example.com\",\n});\n```\n\n**Important**: The last owner cannot be removed. Assign the owner role to another member first.\n\n### Updating Member Roles\n\n```ts\nawait authClient.organization.updateMemberRole({\n  memberId: \"member-id\",\n  role: \"admin\",\n});\n```\n\n### Membership Limits\n\nControl the maximum number of members per organization:\n\n```ts\norganization({\n  membershipLimit: async (user, organization) => {\n    if (organization.metadata?.plan === \"enterprise\") {\n      return 1000;\n    }\n    return 50;\n  },\n});\n```\n\n## Invitations\n\nThe invitation system allows admins to invite users via email. Configure email sending to enable invitations.\n\n### Setting Up Invitation Emails\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { organization } from \"better-auth/plugins\";\nimport { sendEmail } from \"./email\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n      sendInvitationEmail: async (data) => {\n        const { email, organization, inviter, invitation } = data;\n\n        await sendEmail({\n          to: email,\n          subject: `Join ${organization.name}`,\n          html: `\n            <p>${inviter.user.name} invited you to join ${organization.name}</p>\n            <a href=\"https://yourapp.com/accept-invite?id=${invitation.id}\">\n              Accept Invitation\n            </a>\n          `,\n        });\n      },\n    }),\n  ],\n});\n```\n\n### Sending Invitations\n\n```ts\nawait authClient.organization.inviteMember({\n  email: \"newuser@example.com\",\n  role: \"member\",\n});\n```\n\n### Creating Shareable Invitation URLs\n\nFor sharing via Slack, SMS, or in-app notifications:\n\n```ts\nconst { data } = await authClient.organization.getInvitationURL({\n  email: \"newuser@example.com\",\n  role: \"member\",\n  callbackURL: \"https://yourapp.com/dashboard\",\n});\n\n// Share data.url via any channel\n```\n\n**Note**: This endpoint does not call `sendInvitationEmail`. Handle delivery yourself.\n\n### Accepting Invitations\n\n```ts\nawait authClient.organization.acceptInvitation({\n  invitationId: \"invitation-id\",\n});\n```\n\n### Invitation Configuration\n\n```ts\norganization({\n  invitationExpiresIn: 60 * 60 * 24 * 7, // 7 days (default: 48 hours)\n  invitationLimit: 100, // Max pending invitations per org\n  cancelPendingInvitationsOnReInvite: true, // Cancel old invites when re-inviting\n});\n```\n\n## Roles & Permissions\n\nThe plugin provides role-based access control (RBAC) with three default roles:\n\n| Role | Description |\n|------|-------------|\n| `owner` | Full access, can delete organization |\n| `admin` | Can manage members, invitations, settings |\n| `member` | Basic access to organization resources |\n\n\n### Checking Permissions\n\n```ts\nconst { data } = await authClient.organization.hasPermission({\n  permission: \"member:write\",\n});\n\nif (data?.hasPermission) {\n  // User can manage members\n}\n```\n\n### Client-Side Permission Checks\n\nFor UI rendering without API calls:\n\n```ts\nconst canManageMembers = authClient.organization.checkRolePermission({\n  role: \"admin\",\n  permissions: [\"member:write\"],\n});\n```\n\n**Note**: For dynamic access control, the client side role permission check will not work. Please use the `hasPermission` endpoint.\n\n## Teams\n\nTeams allow grouping members within an organization.\n\n### Enabling Teams\n\n```ts\nimport { organization } from \"better-auth/plugins\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n        teams: {\n            enabled: true\n        }\n    }),\n  ],\n});\n```\n\n### Creating Teams\n\n```ts\nconst { data } = await authClient.organization.createTeam({\n  name: \"Engineering\",\n});\n```\n\n### Managing Team Members\n\n```ts\n// Add a member to a team (must be org member first)\nawait authClient.organization.addTeamMember({\n  teamId: \"team-id\",\n  userId: \"user-id\",\n});\n\n// Remove from team (stays in org)\nawait authClient.organization.removeTeamMember({\n  teamId: \"team-id\",\n  userId: \"user-id\",\n});\n```\n\n### Active Teams\n\nSimilar to active organizations, set an active team for the session:\n\n```ts\nawait authClient.organization.setActiveTeam({\n  teamId: \"team-id\",\n});\n```\n\n### Team Limits\n\n```ts\norganization({\n  teams: {\n      maximumTeams: 20, // Max teams per org\n      maximumMembersPerTeam: 50, // Max members per team\n      allowRemovingAllTeams: false, // Prevent removing last team\n  }\n});\n```\n\n## Dynamic Access Control\n\nFor applications needing custom roles per organization at runtime, enable dynamic access control.\n\n### Enabling Dynamic Access Control\n\n```ts\nimport { organization } from \"better-auth/plugins\";\nimport { dynamicAccessControl } from \"@better-auth/organization/addons\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n        dynamicAccessControl: {\n            enabled: true\n        }\n    }),\n  ],\n});\n```\n\n### Creating Custom Roles\n\n```ts\nawait authClient.organization.createRole({\n  role: \"moderator\",\n  permission: {\n    member: [\"read\"],\n    invitation: [\"read\"],\n  },\n});\n```\n\n### Updating and Deleting Roles\n\n```ts\n// Update role permissions\nawait authClient.organization.updateRole({\n  roleId: \"role-id\",\n  permission: {\n    member: [\"read\", \"write\"],\n  },\n});\n\n// Delete a custom role\nawait authClient.organization.deleteRole({\n  roleId: \"role-id\",\n});\n```\n\n**Note**: Pre-defined roles (owner, admin, member) cannot be deleted. Roles assigned to members cannot be deleted until members are reassigned.\n\n## Lifecycle Hooks\n\nExecute custom logic at various points in the organization lifecycle:\n\n```ts\norganization({\n  hooks: {\n    organization: {\n      beforeCreate: async ({ data, user }) => {\n        // Validate or modify data before creation\n        return {\n          data: {\n            ...data,\n            metadata: { ...data.metadata, createdBy: user.id },\n          },\n        };\n      },\n      afterCreate: async ({ organization, member }) => {\n        // Post-creation logic (e.g., send welcome email, create default resources)\n        await createDefaultResources(organization.id);\n      },\n      beforeDelete: async ({ organization }) => {\n        // Cleanup before deletion\n        await archiveOrganizationData(organization.id);\n      },\n    },\n    member: {\n      afterCreate: async ({ member, organization }) => {\n        await notifyAdmins(organization.id, `New member joined`);\n      },\n    },\n    invitation: {\n      afterCreate: async ({ invitation, organization, inviter }) => {\n        await logInvitation(invitation);\n      },\n    },\n  },\n});\n```\n\n## Schema Customization\n\nCustomize table names, field names, and add additional fields:\n\n```ts\norganization({\n  schema: {\n    organization: {\n      modelName: \"workspace\", // Rename table\n      fields: {\n        name: \"workspaceName\", // Rename fields\n      },\n      additionalFields: {\n        billingId: {\n          type: \"string\",\n          required: false,\n        },\n      },\n    },\n    member: {\n      additionalFields: {\n        department: {\n          type: \"string\",\n          required: false,\n        },\n        title: {\n          type: \"string\",\n          required: false,\n        },\n      },\n    },\n  },\n});\n```\n\n## Security Considerations\n\n### Owner Protection\n\n- The last owner cannot be removed from an organization\n- The last owner cannot leave the organization\n- The owner role cannot be removed from the last owner\n\nAlways ensure ownership transfer before removing the current owner:\n\n```ts\n// Transfer ownership first\nawait authClient.organization.updateMemberRole({\n  memberId: \"new-owner-member-id\",\n  role: \"owner\",\n});\n\n// Then the previous owner can be demoted or removed\n```\n\n### Organization Deletion\n\nDeleting an organization removes all associated data (members, invitations, teams). Prevent accidental deletion:\n\n```ts\norganization({\n  disableOrganizationDeletion: true, // Disable via config\n});\n```\n\nOr implement soft delete via hooks:\n\n```ts\norganization({\n  hooks: {\n    organization: {\n      beforeDelete: async ({ organization }) => {\n        // Archive instead of delete\n        await archiveOrganization(organization.id);\n        throw new Error(\"Organization archived, not deleted\");\n      },\n    },\n  },\n});\n```\n\n### Invitation Security\n\n- Invitations expire after 48 hours by default\n- Only the invited email address can accept an invitation\n- Pending invitations can be cancelled by organization admins\n\n## Complete Configuration Example\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { organization } from \"better-auth/plugins\";\nimport { sendEmail } from \"./email\";\n\nexport const auth = betterAuth({\n  plugins: [\n    organization({\n      // Organization limits\n      allowUserToCreateOrganization: true,\n      organizationLimit: 10,\n      membershipLimit: 100,\n      creatorRole: \"owner\",\n\n      // Slugs\n      defaultOrganizationIdField: \"slug\",\n\n      // Invitations\n      invitationExpiresIn: 60 * 60 * 24 * 7, // 7 days\n      invitationLimit: 50,\n      sendInvitationEmail: async (data) => {\n        await sendEmail({\n          to: data.email,\n          subject: `Join ${data.organization.name}`,\n          html: `<a href=\"https://app.com/invite/${data.invitation.id}\">Accept</a>`,\n        });\n      },\n\n      // Hooks\n      hooks: {\n        organization: {\n          afterCreate: async ({ organization }) => {\n            console.log(`Organization ${organization.name} created`);\n          },\n        },\n      },\n    }),\n  ],\n});\n```\n\n<!-- File: .agents/skills/prisma-cli/references/db-execute.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-cli/references/db-execute.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-execute.md)\n\n<!-- File: .agents/skills/prisma-cli/references/db-pull.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-cli/references/db-pull.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-pull.md)\n\n<!-- File: .agents/skills/prisma-cli/references/db-push.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-cli/references/db-push.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-push.md)\n\n<!-- File: .agents/skills/prisma-cli/references/db-seed.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-cli/references/db-seed.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/db-seed.md)\n\n<!-- File: .agents/skills/prisma-cli/references/debug.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-cli/references/debug.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/debug.md)\n\n<!-- File: .agents/skills/prisma-cli/references/dev.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-cli/references/dev.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/dev.md)\n\n<!-- File: .agents/skills/prisma-cli/references/format.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-cli/references/format.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/format.md)\n\n<!-- File: .agents/skills/prisma-cli/references/generate.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-cli/references/generate.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/generate.md)\n\n<!-- File: .agents/skills/prisma-cli/references/init.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-cli/references/init.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/init.md)\n\n<!-- File: .agents/skills/prisma-cli/references/migrate-deploy.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-deploy.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-deploy.md)\n\n<!-- File: .agents/skills/prisma-cli/references/migrate-dev.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-dev.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-dev.md)\n\n<!-- File: .agents/skills/prisma-cli/references/migrate-diff.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-diff.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-diff.md)\n\n<!-- File: .agents/skills/prisma-cli/references/migrate-reset.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-reset.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-reset.md)\n\n<!-- File: .agents/skills/prisma-cli/references/migrate-resolve.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-resolve.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-resolve.md)\n\n<!-- File: .agents/skills/prisma-cli/references/migrate-status.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-cli/references/migrate-status.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/migrate-status.md)\n\n<!-- File: .agents/skills/prisma-cli/references/studio.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-cli/references/studio.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/studio.md)\n\n<!-- File: .agents/skills/prisma-cli/references/validate.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-cli/references/validate.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-cli/references/validate.md)\n\n<!-- File: .agents/skills/prisma-cli/SKILL.md (Tokens: ~1441 | Category: anthropic-skill) -->\n---\nname: prisma-cli\ndescription: Prisma CLI commands reference covering all available commands, options, and usage patterns. Use when running Prisma CLI commands, setting up projects, generating client, running migrations, or managing databases. Triggers on \"prisma init\", \"prisma generate\", \"prisma migrate\", \"prisma db\", \"prisma studio\".\nlicense: MIT\nmetadata:\n  author: prisma\n  version: \"7.0.0\"\n---\n\n# Prisma CLI Reference\n\nComplete reference for all Prisma CLI commands. This skill provides guidance on command usage, options, and best practices for Prisma ORM 7.x.\n\n## When to Apply\n\nReference this skill when:\n- Setting up a new Prisma project (`prisma init`)\n- Generating Prisma Client (`prisma generate`)\n- Running database migrations (`prisma migrate`)\n- Managing database state (`prisma db push/pull`)\n- Using local development database (`prisma dev`)\n- Debugging Prisma issues (`prisma debug`)\n\n## Rule Categories by Priority\n\n| Priority | Category | Impact | Prefix |\n|----------|----------|--------|--------|\n| 1 | Setup | HIGH | `init` |\n| 2 | Generation | HIGH | `generate` |\n| 3 | Development | HIGH | `dev` |\n| 4 | Database | HIGH | `db-` |\n| 5 | Migrations | CRITICAL | `migrate-` |\n| 6 | Utility | MEDIUM | `studio`, `validate`, `format`, `debug` |\n\n## Command Categories\n\n| Category | Commands | Purpose |\n|----------|----------|---------|\n| Setup | `init` | Bootstrap new Prisma project |\n| Generation | `generate` | Generate Prisma Client |\n| Validation | `validate`, `format` | Schema validation and formatting |\n| Development | `dev` | Local Prisma Postgres for development |\n| Database | `db pull`, `db push`, `db seed`, `db execute` | Direct database operations |\n| Migrations | `migrate dev`, `migrate deploy`, `migrate reset`, `migrate status`, `migrate diff`, `migrate resolve` | Schema migrations |\n| Utility | `studio`, `version`, `debug` | Development tools |\n\n## Quick Reference\n\n### Project Setup\n\n```bash\n# Initialize new project (creates prisma/ folder and prisma.config.ts)\nprisma init\n\n# Initialize with specific database\nprisma init --datasource-provider postgresql\nprisma init --datasource-provider mysql\nprisma init --datasource-provider sqlite\n\n# Initialize with Prisma Postgres (cloud)\nprisma init --db\n\n# Initialize with AI-generated schema\nprisma init --prompt \"E-commerce app with users, products, orders\"\n```\n\n### Client Generation\n\n```bash\n# Generate Prisma Client\nprisma generate\n\n# Watch mode for development\nprisma generate --watch\n\n# Generate without engine (for Accelerate/edge)\nprisma generate --no-engine\n\n# Generate specific generator only\nprisma generate --generator client\n```\n\n### Bun Runtime\n\nWhen using Bun, always add the `--bun` flag so Prisma runs with the Bun runtime (otherwise it falls back to Node.js because of the CLI shebang):\n\n```bash\nbunx --bun prisma init\nbunx --bun prisma generate\n```\n\n### Local Development Database\n\n```bash\n# Start local Prisma Postgres\nprisma dev\n\n# Start with specific name\nprisma dev --name myproject\n\n# Start in background (detached)\nprisma dev --detach\n\n# List all local instances\nprisma dev ls\n\n# Stop instance\nprisma dev stop myproject\n\n# Remove instance data\nprisma dev rm myproject\n```\n\n### Database Operations\n\n```bash\n# Pull schema from existing database\nprisma db pull\n\n# Push schema to database (no migrations)\nprisma db push\n\n# Seed database\nprisma db seed\n\n# Execute raw SQL\nprisma db execute --file ./script.sql\n```\n\n### Migrations (Development)\n\n```bash\n# Create and apply migration\nprisma migrate dev\n\n# Create migration with name\nprisma migrate dev --name add_users_table\n\n# Create migration without applying\nprisma migrate dev --create-only\n\n# Reset database and apply all migrations\nprisma migrate reset\n```\n\n### Migrations (Production)\n\n```bash\n# Apply pending migrations (CI/CD)\nprisma migrate deploy\n\n# Check migration status\nprisma migrate status\n\n# Compare schemas and generate diff\nprisma migrate diff --from-config-datasource --to-schema schema.prisma --script\n```\n\n### Utility Commands\n\n```bash\n# Open Prisma Studio (database GUI)\nprisma studio\n\n# Show version info\nprisma version\nprisma -v\n\n# Debug information\nprisma debug\n\n# Validate schema\nprisma validate\n\n# Format schema\nprisma format\n```\n\n## Prisma 7 Changes\n\n### New Configuration File\n\nPrisma 7 uses `prisma.config.ts` for CLI configuration:\n\n```typescript\nimport 'dotenv/config'\nimport { defineConfig, env } from 'prisma/config'\n\nexport default defineConfig({\n  schema: 'prisma/schema.prisma',\n  migrations: {\n    path: 'prisma/migrations',\n    seed: 'tsx prisma/seed.ts',\n  },\n  datasource: {\n    url: env('DATABASE_URL'),\n  },\n})\n```\n\n### Removed Flags\n\n- `--skip-generate` removed from `migrate dev` and `db push`\n- `--skip-seed` removed from `migrate dev`\n- `--schema` and `--url` removed from `db execute`\n- Run `prisma generate` explicitly after migrations\n\n### Environment Variables\n\nEnvironment variables are no longer auto-loaded. Use `dotenv`:\n\n```typescript\n// prisma.config.ts\nimport 'dotenv/config'\n```\n\n## Rule Files\n\nSee individual rule files for detailed command documentation:\n\n```\nreferences/init.md           - Project initialization\nreferences/generate.md       - Client generation\nreferences/dev.md            - Local development database\nreferences/db-pull.md        - Database introspection\nreferences/db-push.md        - Schema push\nreferences/db-seed.md        - Database seeding\nreferences/db-execute.md     - Raw SQL execution\nreferences/migrate-dev.md    - Development migrations\nreferences/migrate-deploy.md - Production migrations\nreferences/migrate-reset.md  - Database reset\nreferences/migrate-status.md - Migration status\nreferences/migrate-resolve.md - Migration resolution\nreferences/migrate-diff.md   - Schema diffing\nreferences/studio.md         - Database GUI\nreferences/validate.md       - Schema validation\nreferences/format.md         - Schema formatting\nreferences/debug.md          - Debug info\n```\n\n## How to Use\n\nUse the command categories above for navigation, then open the specific command reference file you need.\n\n<!-- File: .agents/skills/prisma-client-api/references/client-methods.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/client-methods.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/client-methods.md)\n\n<!-- File: .agents/skills/prisma-client-api/references/constructor.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/constructor.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/constructor.md)\n\n<!-- File: .agents/skills/prisma-client-api/references/filters.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/filters.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/filters.md)\n\n<!-- File: .agents/skills/prisma-client-api/references/model-queries.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/model-queries.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/model-queries.md)\n\n<!-- File: .agents/skills/prisma-client-api/references/query-options.md (Tokens: ~300 | Category: anthropic-skill) -->\n# References Skill\n\nPath: `.agents/skills/prisma-client-api/references/query-options.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/lukevella/rallly/HEAD/.agents/skills/prisma-client-api/references/query-options.md)\n\n<!-- ... +81 more modular skills available via /api/skills/lukevella/rallly?path={path} -->\n</yakaai_skills>"}