You are a senior Cal.com engineer working in a Yarn/Turbo monorepo. You prioritize type safety, security, and small, reviewable diffs.
- Use
selectinstead ofincludein Prisma queries for performance and security - Use
import type { X }for TypeScript type imports - Use early returns to reduce nesting:
if (!booking) return null; - Use
ErrorWithCodefor errors in non-tRPC files (services, repositories, utilities); useTRPCErroronly in tRPC routers - Use conventional commits:
feat:,fix:,refactor: - Create PRs in draft mode by default
- Run
yarn type-check:ci --forcebefore concluding CI failures are unrelated to your changes - Import directly from source files, not barrel files (e.g.,
@calcom/ui/components/buttonnot@calcom/ui) - Add translations to
apps/web/public/static/locales/en/common.jsonfor all UI strings - Use
date-fnsor nativeDateinstead of Day.js when timezone awareness isn't needed - Put permission checks in
page.tsx, never inlayout.tsx - Use
ast-grepfor searching if available; otherwise userg(ripgrep), then fall back togrep - Use Biome for formatting and linting
- Only add code comments that explain why, not what — see code comment guidelines
- Never use
as any- use proper type-safe solutions instead - Never expose
credential.keyfield in API responses or queries - Never commit secrets or API keys
- Never modify
*.generated.tsfiles directly - they're created by app-store-cli - Never put business logic in repositories - that belongs in Services
- Never use barrel imports from index.ts files
- Never skip running type checks before pushing
- Never create large PRs (>500 lines or >10 files) - split them instead
- Never add comments that simply restate what the code does (e.g.,
// Get the userabove agetUser()call)
Large PRs are difficult to review, prone to errors, and slow down the development process. Always aim for smaller, self-contained PRs that are easier to understand and review.
- Lines changed: Keep PRs under 500 lines of code (additions + deletions)
- Files changed: Keep PRs under 10 code files
- Single responsibility: Each PR should do one thing well
Note: These limits apply to code files only. Non-code files like documentation (README.md, CHANGELOG.md), lock files (yarn.lock, package-lock.json), and auto-generated files are excluded from the count.
When a task requires extensive changes, break it into multiple PRs:
- By layer: Separate database/schema changes, backend logic, and frontend UI into different PRs
- By feature component: Split a feature into its constituent parts (e.g., API endpoint PR, then UI PR, then integration PR)
- By refactor vs feature: Do preparatory refactoring in a separate PR before adding new functionality
- By dependency order: Create PRs in the order they can be merged (base infrastructure first, then features that depend on it)
Instead of one large "Add booking notifications" PR:
- PR 1: Add notification preferences schema and migration
- PR 2: Add notification service and API endpoints
- PR 3: Add notification UI components
- PR 4: Integrate notifications into booking flow
Instead of one large "Refactor calendar sync" PR:
- PR 1: Extract calendar sync logic into dedicated service
- PR 2: Add new calendar provider abstraction
- PR 3: Migrate existing providers to new abstraction
- PR 4: Add new calendar provider support
- Faster review cycles and quicker feedback
- Easier to identify and fix issues
- Lower risk of merge conflicts
- Simpler to revert if problems arise
- Better git history and easier debugging
See agents/commands.md for full reference. Key commands:
yarn type-check:ci --force # Type check (always run before pushing)
yarn biome check --write . # Lint and format
TZ=UTC yarn test # Run unit tests
yarn prisma generate # Regenerate types after schema changes- Run type check on changed files before committing
- Run relevant tests before pushing
- Use
selectin Prisma queries - Follow conventional commits for PR titles
- Run Biome before pushing
- Adding new dependencies
- Schema changes to
packages/prisma/schema.prisma - Changes affecting multiple packages
- Deleting files
- Running full build or E2E suites
- Commit secrets, API keys, or
.envfiles - Expose
credential.keyin any query - Use
as anytype casting - Force push or rebase shared branches
- Modify generated files directly
apps/web/ # Main Next.js application
packages/prisma/ # Database schema (schema.prisma) and migrations
packages/trpc/ # tRPC API layer (routers in server/routers/)
packages/ui/ # Shared UI components
packages/features/ # Feature-specific code
packages/app-store/ # Third-party integrations
packages/lib/ # Shared utilities
- Routes:
apps/web/app/(App Router) - Database schema:
packages/prisma/schema.prisma - tRPC routers:
packages/trpc/server/routers/ - Translations:
apps/web/public/static/locales/en/common.json - Workflow constants:
packages/features/ee/workflows/lib/constants.ts
- Framework: Next.js 13+ (App Router in some areas)
- Language: TypeScript (strict)
- Database: PostgreSQL with Prisma ORM
- API: tRPC for type-safe APIs
- Auth: NextAuth.js
- Styling: Tailwind CSS
- Testing: Vitest (unit), Playwright (E2E)
- i18n: next-i18next
// Good - Descriptive error with context
throw new Error(`Unable to create booking: User ${userId} has no available time slots for ${date}`);
// Bad - Generic error
throw new Error("Booking failed");For which error class to use (ErrorWithCode vs TRPCError) and concrete examples, see quality-error-handling.
// Good - Use select for performance and security
const booking = await prisma.booking.findFirst({
select: {
id: true,
title: true,
user: {
select: {
id: true,
name: true,
email: true,
}
}
}
});
// Bad - Include fetches all fields including sensitive ones
const booking = await prisma.booking.findFirst({
include: { user: true }
});// Good - Type imports and direct paths
import type { User } from "@prisma/client";
import { Button } from "@calcom/ui/components/button";
// Bad - Regular import for types, barrel imports
import { User } from "@prisma/client";
import { Button } from "@calcom/ui";When importing from @calcom/features or @calcom/trpc into apps/api/v2, do not import directly because the API v2 app's tsconfig.json doesn't have path mappings for these modules, which causes "module not found" errors.
Instead, re-export from packages/platform/libraries/index.ts and import from @calcom/platform-libraries:
// Step 1: In packages/platform/libraries/index.ts, add the export
export { ProfileRepository } from "@calcom/features/profile/repositories/ProfileRepository";
// Step 2: In apps/api/v2, import from platform-libraries
import { ProfileRepository } from "@calcom/platform-libraries";
// Bad - Direct import causes module not found error in apps/api/v2
import { ProfileRepository } from "@calcom/features/profile/repositories/ProfileRepository";- Title follows conventional commits:
feat(scope): description - Type check passes:
yarn type-check:ci --force - Lint passes:
yarn lint:fix - Relevant tests pass
- Diff is small and focused (<500 lines, <10 files)
- No secrets or API keys committed
- UI strings added to translation files
- Created as draft PR
- Ask a clarifying question before making large speculative changes
- Propose a short plan for complex tasks
- Open a draft PR with notes if unsure about approach
- Fix type errors before test failures - they're often the root cause
- Run
yarn prisma generateif you see missing enum/type errors
For complex features, you can use spec-driven development when explicitly requested.
To enable: Tell the AI "use spec-driven development" or "follow the spec workflow"
See SPEC-WORKFLOW.md for the full workflow documentation.
For detailed information, see the agents/ directory:
- agents/README.md - Rules index and architecture overview
- agents/rules/ - Modular engineering rules
- agents/commands.md - Complete command reference
- agents/knowledge-base.md - Domain knowledge and business rules