This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
- NO
anyTYPES IN TYPESCRIPT - This project uses strict TypeScript with Biome. Usinganywill break the build. - RUN
bun checkBEFORE PRESENTING CODE - Verify all type checking passes - USE PROPER TYPES - Import from libraries, define interfaces, or use
unknownwith type guards
A modern, production-ready CRM application using React Router 7 deployed to Cloudflare with authentication, server-side rendering, and database integration.
bun dev # Start dev server at http://localhost:5173
bun check # Run all checks (types, linting, formatting)
bun biome:check # Run Biome linter and formatter onlybun convex:dev # Start Convex dev server
bun convex:deploy # Deploy to productionbun build # Build for production
bun preview # Preview production build locally
bun deploy # Build and deploy to Cloudflare
bun deploy:staging # Deploy to staging environment
bun deploy:prod # Deploy to production environmentbun tail:prod # Stream production logs
bun tail:staging # Stream staging logs
wrangler secret put VARIABLE_NAME # Add production secrets/app- Frontend React code (routes, components, hooks)/convex- Convex backend functions and schema/workers- Cloudflare Workers entry points
- Frontend: React 19, React Router 7, TypeScript, TailwindCSS, ShadCN UI
- Backend: Cloudflare Workers, Hono, Convex
- Database: Convex real-time database
- Auth: Clerk authentication (external service)
- Tooling: Bun, Biome, Wrangler
~/*- Maps to/app/*(frontend imports)~~/*- Maps to root-level packages (worker imports)
ABSOLUTELY NO any TYPES ALLOWED - THIS IS NON-NEGOTIABLE
- The codebase uses TypeScript in strict mode with Biome linting
- Using
anytype will cause build failures and require rework - Before writing ANY TypeScript code:
- Understand the existing types being used
- Import proper types from libraries
- Define explicit interfaces/types when needed
- Use
unknownand type guards if type is truly unknown - Use generic types
<T>for flexible but type-safe code
Instead of any, use:
unknown- for truly unknown types (requires type guards)Record<string, unknown>- for objects with unknown structure- Specific types like
string,number,boolean - Union types like
string | number - Imported types from libraries (e.g.,
import type { User } from "@clerk/nextjs/server") - Defined interfaces or type aliases
- Generic constraints like
<T extends object>
- Indentation: Tabs (not spaces)
- Quotes: Double quotes for strings
- Imports: Auto-organized by Biome
- Linting: Always run
bun checkafter changes
- React components:
PascalCase - Files:
kebab-case - Variables/functions:
camelCase - Database tables:
snake_case
// Convex types - ALWAYS import from generated files
import type { Doc, Id } from "../../convex/_generated/dataModel";
import type { api } from "../../convex/_generated/api";
// Clerk types - import from Clerk packages
import type { User } from "@clerk/nextjs/server";
// React Router types
import type { Route } from "./+types/route-name";
// Component props - define explicit interfaces
interface ButtonProps {
onClick: () => void;
children: React.ReactNode;
variant?: "primary" | "secondary";
}
// API responses - use proper typing
interface ApiResponse<T> {
data: T;
error?: string;
}
// Form data - define interfaces
interface FormData {
email: string;
password: string;
}
// NEVER DO THIS:
// const user: any = getData(); β
// const props: any = { ... }; β
// function process(data: any) β- Database schema:
/convex/schema.ts - Functions:
/convex/directory
- Convex Functions: Define queries, mutations, and actions in
/convex/ - Frontend Hooks: Use Convex React hooks (
useQuery,useMutation) - Real-time Updates: Automatic reactivity with Convex subscriptions
- Error Handling: Always handle loading and error states in components
user- Default role for all users (basic access)admin- Administrative access (can view/manage users, access admin dashboard)superadmin- Full system access (all permissions including role management)
- Authentication is handled by Clerk (external service)
- User roles stored in Clerk's publicMetadata
- Sign in/up via Clerk's prebuilt components
- Session management handled by Clerk
The application uses a granular permission system defined in two places:
Used for server-side authorization in Cloudflare Workers:
export const PERMISSIONS = {
// Admin permissions
ADMIN_ACCESS: "admin_access",
VIEW_USERS: "view_users",
// SuperAdmin permissions
MANAGE_USERS: "manage_users",
ASSIGN_ROLES: "assign_roles",
SYSTEM_CONFIG: "system_config",
} as const;
// Role-permission mapping
export const ROLE_PERMISSIONS: Record<UserRole, Permission[]> = {
user: [],
admin: [PERMISSIONS.ADMIN_ACCESS, PERMISSIONS.VIEW_USERS],
superadmin: [
/* all permissions */
],
};Used for frontend authorization and UI permission checks:
export const PERMISSIONS = {
// User management
"users.view": "View user list and details",
"users.create": "Create new users",
"users.edit": "Edit user information",
"users.delete": "Delete users",
"users.manage_roles": "Manage user roles",
// Admin permissions
"admin.access": "Access admin dashboard",
"admin.view_stats": "View system statistics",
// System permissions
"system.manage_api": "Manage API keys and webhooks",
"system.manage_security": "Manage security settings",
// ... more permissions
};Helper Functions Available:
rolesHavePermission(roles, permission)- Check if roles have a specific permissiongetPermissionsForRoles(roles)- Get all permissions for a set of rolescanAssignRole(assignerRoles, roleToAssign)- Check if user can assign a rolecanManageUser(actorRoles, targetRoles)- Check if user can manage another userPermissionCheckerclass - For React components
Usage in Components:
import { useAuth } from "~/hooks/use-auth";
function MyComponent() {
const { hasPermission, hasRole } = useAuth();
if (hasPermission("users.edit")) {
// Show edit UI
}
if (hasRole("admin")) {
// Show admin features
}
}- Routes under
_auth.*require authentication - Admin routes check for admin/superadmin roles
- Use Convex auth helpers for protected functions
wrangler.jsonc- Public variables invarssection.env- Local development secrets.env.example- Documentation of required secrets
After modifying environment variables:
- Update
wrangler.jsonc - Run
bun cf-typegento regenerate types - Types appear in
worker-configuration.d.ts(auto-generated)
VITE_CLERK_PUBLISHABLE_KEY- Clerk publishable key (client-side)CLERK_SECRET_KEY- Clerk secret key (server-side)
bunx --bun shadcn@latest add button # Add new componentComponents are installed to /app/components/ui/
- Use
PublicLayoutwrapper for pages with navigation/footer - Handle loading states with
LoadingSpinnercomponent - Show errors with proper error boundaries
- Use
toastfor user notifications
- URL: http://localhost:5173
- Database: Local Convex instance
- Email: Mock email service (logs to console)
- Build:
bun build:staging - Deploy:
bun deploy:staging - Logs:
bun tail:staging
- Database: Production Convex deployment
- Deploy:
bun deploy:prod - Logs:
bun tail:prod
- Create Convex function in
/convex/directory - Define schema if needed in
/convex/schema.ts - Use Convex hooks in React components
- Create route file in
/app/routes/ - Use
_auth.prefix for protected routes - Add
metaexport for SEO - Wrap in
PublicLayoutif needed
- Edit
/convex/schema.ts - Update Convex functions as needed
- Test with
bun convex:dev - Deploy with
bun convex:deploy
If you encounter type errors, DO NOT use any to bypass them:
- Run
bun checkto see all type errors - Check that
bun cf-typegenwas run after env changes - Common fixes for type errors:
- Import the correct type from the library
- Check existing code for how similar types are handled
- Define a proper interface or type alias
- Use
unknownwith type narrowing if type is dynamic - Look for existing type definitions in the codebase
- NEVER commit or present code with
anytypes
- Check Convex function arguments and return types
- Verify schema definitions match usage
- Use Convex dashboard for debugging
- Check magic link expiration (15 minutes)
- Verify email service configuration
- Check user roles in database