9.9 KiB
Agent Development Guide for FitAI
This document provides essential guidelines for AI coding agents working in the FitAI codebase.
Project Overview
FitAI is a monorepo containing a fitness AI platform with:
- Admin App (
apps/admin): Next.js 16 web application for gym management - Mobile App (
apps/mobile): React Native/Expo mobile app for clients - Shared Packages: Database (
@fitai/database) and utilities (@fitai/shared)
Tech Stack: TypeScript, Next.js 16, React 19, React Native, Expo 54, Drizzle ORM, Clerk Auth, TanStack Query, Tailwind CSS
Build, Lint, and Test Commands
From Repository Root (/home/echo/dev/prototype)
# Development
npm run dev # Start both admin and mobile dev servers
npm run dev:admin # Start admin dev server (Next.js)
npm run dev:mobile # Start mobile dev server (Expo)
# Build
npm run build # Build both apps
npm run build:admin # Build admin app
npm run build:mobile # Build mobile app
# Linting
npm run lint # Lint both apps
npm run lint:admin # Lint admin app
npm run lint:mobile # Lint mobile app
# Type Checking
npm run typecheck # Check types in both apps
npm run typecheck:admin # Check types in admin
npm run typecheck:mobile # Check types in mobile
# Testing
npm run test # Run all tests
npm run test:admin # Run admin tests
npm run test:mobile # Run mobile tests
Admin App (apps/admin)
cd apps/admin
# Development
npm run dev # Start Next.js dev server (port 3000)
# Testing
npm test # Run all tests
npx jest # Run all tests
npx jest path/to/file.test.ts # Run specific test file
npx jest --testPathPattern=drizzle # Run tests matching pattern
npx jest src/lib/database/__tests__/drizzle.test.ts # Run specific test
# Build & Lint
npm run build # Build production bundle
npm run lint # Run ESLint
npm run typecheck # Type check without emitting
Mobile App (apps/mobile)
cd apps/mobile
# Development
npm start # Start Expo dev server
npm run android # Start on Android emulator
npm run ios # Start on iOS simulator
# Testing
npm test # Run all tests
npx jest path/to/file.test.ts # Run specific test file
npx jest --testPathPattern=component # Run tests matching pattern
# Build & Lint
npm run build # Build with Expo
npm run lint # Run ESLint
npm run typecheck # Type check
Code Style Guidelines
Import Organization
Order: External libraries → Internal imports, grouped logically
// External: React, Next.js, React Native
import { NextRequest, NextResponse } from "next/server";
import React from "react";
// External: Third-party libraries
import { auth, clerkClient } from "@clerk/nextjs/server";
import bcrypt from "bcryptjs";
// Monorepo packages
import { db, sql } from "@fitai/database";
// Internal: Path aliases or relative imports
import { getDatabase } from "@/lib/database";
import type { User } from "./types";
Key Rules:
- Use
import type { ... }for type-only imports - Admin app uses path aliases:
@/*→./src/* - Mobile app uses relative paths or
@/*alias - No automatic import sorting; manual grouping by category
Component Structure & Naming
Components: PascalCase function components with named exports
// Feature components - Named export (function)
export function UserManagement() {
return <div>...</div>
}
// UI library components - Named export (forwardRef constant)
const Card = React.forwardRef<HTMLDivElement, CardProps>(
({ className, ...props }, ref) => (
<div ref={ref} className={cn("rounded-lg", className)} {...props} />
)
)
Card.displayName = "Card"
export { Card }
Naming Conventions:
- Components:
UserManagement,GoalProgressCard - Functions:
handleEditUser,getGreeting - Custom hooks:
useUser,useAuth - API routes: Named exports
GET,POST,PUT,DELETE
Type Definitions
Interfaces for object shapes and React props; Types for unions and aliases
// Props interfaces (ComponentNameProps pattern)
interface UserManagementProps {
userId: string;
onUpdate?: () => void;
}
// Domain interfaces
interface User {
id: string;
email: string;
firstName: string;
lastName: string;
}
// Type aliases and unions
type UserRole = "admin" | "trainer" | "client";
type Status = "active" | "inactive" | "pending";
// No I prefix or T prefix
Function Patterns
Arrow functions for components and handlers; function declarations for utilities
// Component functions
export function UserCard() {
const handleClick = () => { /* ... */ } // Arrow function for handlers
return <button onClick={handleClick}>Click</button>
}
// Exported utility functions
export async function setUserRole(userId: string, role: UserRole) {
// ... implementation
}
// Always use async/await (never .then() chains)
const fetchData = async () => {
try {
const response = await fetch(url)
const data = await response.json()
return data
} catch (error) {
console.error('Failed to fetch:', error)
}
}
Error Handling
API Routes (Next.js):
export async function POST(request: NextRequest) {
try {
const { email, password } = await request.json();
// Early returns for validation
if (!email || !password) {
return NextResponse.json(
{ error: "Email and password are required" },
{ status: 400 },
);
}
// ... logic
return NextResponse.json({ success: true });
} catch (error) {
console.error("Login error:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 },
);
}
}
React Components:
// Admin: Use try-catch with console.error
const handleSave = async () => {
try {
await updateUser(userId, data);
} catch (error) {
console.error("Failed to update user:", error);
// Show error to user (toast, alert, etc.)
}
};
// Mobile: Use Alert for confirmations
const handleDelete = () => {
Alert.alert("Delete Goal", "Are you sure?", [
{ text: "Cancel", style: "cancel" },
{ text: "Delete", style: "destructive", onPress: onDelete },
]);
};
Comments & Documentation
Use JSDoc for public APIs; inline comments for complex logic only
/**
* Set a user's role in Clerk public metadata
*
* @param userId - Clerk user ID
* @param role - Role to assign (admin, trainer, or client)
* @returns Updated user object
*
* @example
* await setUserRole('user_abc123', 'admin')
*/
export async function setUserRole(userId: string, role: UserRole) {
// Implementation
}
// Inline comments for clarification
// Optimistically update local state so grid reflects changes immediately
setUsers((prev) => prev.map((u) => (u.id === id ? { ...u, ...updates } : u)));
File Naming Conventions
apps/admin/src/
├── app/
│ ├── api/*/route.ts # API routes (lowercase route.ts)
│ └── */page.tsx # Pages (lowercase page.tsx)
├── components/
│ ├── ui/button.tsx # UI primitives (kebab-case)
│ └── users/UserManagement.tsx # Feature components (PascalCase)
├── lib/
│ ├── database/index.ts # Utilities (kebab-case)
│ └── clerk-helpers.ts # Helpers (kebab-case)
apps/mobile/src/
├── app/
│ └── (tabs)/index.tsx # Routes (lowercase)
├── components/
│ └── GoalProgressCard.tsx # All components (PascalCase)
└── services/
└── fitnessGoals.ts # Services (camelCase)
Styling
Admin App: Tailwind CSS with utility classes
<div className="flex items-center gap-2 rounded-lg border p-4">
<Button className="bg-primary text-white hover:bg-primary/90">
Click me
</Button>
</div>
Mobile App: StyleSheet.create() at file bottom with theme system
import { theme } from '../styles/theme'
export function MyComponent() {
return <View style={styles.container}>...</View>
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: theme.colors.background,
padding: theme.spacing.md,
},
})
ESLint Rules (Admin)
@typescript-eslint/no-unused-vars: error@typescript-eslint/no-explicit-any: warn (allowed but discouraged)prefer-const: errorno-var: error- Extends:
next/core-web-vitals,@typescript-eslint/recommended
TypeScript Configuration
- Strict mode enabled: All strict type checking options on
- Path aliases:
@/*maps to./src/* - Module resolution:
bundler(admin),node(mobile) - Target: ES5 (admin), ESNext (mobile)
- Always provide explicit return types for exported functions
Testing Best Practices
- Tests in
__tests__/directories or*.test.tsfiles - Use
@jest-environment nodecomment for Node.js API tests - Admin: Jest + ts-jest + @testing-library/react
- Mobile: Jest + react-native preset + @testing-library/react-native
- Test file naming:
component.test.tsorfeature.test.tsx - Always test error cases and edge cases
Key Patterns
- State Management: Multiple
useStatedeclarations grouped together - Destructuring: Props in function signature, responses inline
- Type Safety: Explicit return types, const assertions for readonly arrays
- Database: Factory pattern with singleton (
getDatabase()) - Forms: React Hook Form + Zod validation
- Data Fetching: TanStack Query for server state
- Authentication: Clerk for both admin and mobile (different packages)
Node & Package Manager
- Node: >=18.0.0
- Package Manager: npm (>=9.0.0)
- Use
npm install(not yarn or pnpm)