fitaiProto/AGENTS.md
echo 22c274bb83 routes fix
extensive loging added
2026-03-10 02:22:11 +01:00

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: error
  • no-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.ts files
  • Use @jest-environment node comment 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.ts or feature.test.tsx
  • Always test error cases and edge cases

Key Patterns

  1. State Management: Multiple useState declarations grouped together
  2. Destructuring: Props in function signature, responses inline
  3. Type Safety: Explicit return types, const assertions for readonly arrays
  4. Database: Factory pattern with singleton (getDatabase())
  5. Forms: React Hook Form + Zod validation
  6. Data Fetching: TanStack Query for server state
  7. 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)