# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview **CalChat** is a calendar mobile app with AI support. The core concept is managing calendar events through a chat interface with an AI chatbot. Users can add, edit, and delete events via natural language conversation. This is a fullstack TypeScript monorepo with npm workspaces. ## Commands ### Root (monorepo) ```bash npm install # Install all dependencies for all workspaces npm run format # Format all TypeScript files with Prettier ``` ### Client (apps/client) - Expo React Native app ```bash npm run start -w @caldav/client # Start Expo dev server npm run android -w @caldav/client # Start on Android npm run ios -w @caldav/client # Start on iOS npm run web -w @caldav/client # Start web version npm run lint -w @caldav/client # Run ESLint ``` ### Server (apps/server) - Express.js backend ```bash npm run dev -w @caldav/server # Start dev server with hot reload (tsx watch) npm run build -w @caldav/server # Compile TypeScript npm run start -w @caldav/server # Run compiled server (port 3000) ``` ## Technology Stack | Area | Technology | Purpose | |------|------------|---------| | Frontend | React Native | Mobile UI Framework | | | Expo | Development platform | | | Expo-Router | File-based routing | | | NativeWind | Tailwind CSS for React Native | | | Zustand | State management | | | FlashList | High-performance lists | | Backend | Express.js | Web framework | | | MongoDB | Database | | | Mongoose | ODM | | | GPT (OpenAI) | AI/LLM for chat | | | JWT | Authentication | | | pino / pino-http | Structured logging | | | react-native-logs | Client-side logging | | Planned | iCalendar | Event export/import | ## Architecture ### Workspace Structure ``` apps/client - @caldav/client - Expo React Native app apps/server - @caldav/server - Express.js backend packages/shared - @caldav/shared - Shared TypeScript types and models ``` ### Frontend Architecture (apps/client) ``` src/ ├── app/ # Expo-Router file-based routing │ ├── _layout.tsx # Root Stack layout │ ├── index.tsx # Entry redirect │ ├── login.tsx # Login screen │ ├── register.tsx # Registration screen │ ├── (tabs)/ # Tab navigation group │ │ ├── _layout.tsx # Tab bar configuration │ │ ├── chat.tsx # Chat screen (AI conversation) │ │ └── calendar.tsx # Calendar overview │ ├── event/ │ │ └── [id].tsx # Event detail screen (dynamic route) │ └── note/ │ └── [id].tsx # Note editor for event (dynamic route) ├── components/ │ ├── BaseBackground.tsx # Common screen wrapper │ ├── Header.tsx # Header component │ ├── EventCardBase.tsx # Shared event card layout with icons (used by EventCard & ProposedEventCard) │ ├── EventCard.tsx # Calendar event card (uses EventCardBase + edit/delete buttons) │ ├── EventConfirmDialog.tsx # AI-proposed event confirmation modal │ └── ProposedEventCard.tsx # Chat event proposal (uses EventCardBase + confirm/reject buttons) ├── Themes.tsx # Centralized color/theme definitions ├── logging/ │ ├── index.ts # Re-exports │ └── logger.ts # react-native-logs config (apiLogger, storeLogger) ├── services/ │ ├── index.ts # Re-exports all services │ ├── ApiClient.ts # HTTP client with request logging (get, post, put, delete) │ ├── AuthService.ts # login(), register(), logout(), refresh() │ ├── EventService.ts # getAll(), getById(), getByDateRange(), create(), update(), delete() │ └── ChatService.ts # sendMessage(), confirmEvent(), rejectEvent(), getConversations(), getConversation() └── stores/ # Zustand state management ├── index.ts # Re-exports all stores ├── AuthStore.ts # user, token, isAuthenticated, login(), logout(), setToken() ├── ChatStore.ts # messages[], addMessage(), addMessages(), updateMessage(), clearMessages(), chatMessageToMessageData() └── EventsStore.ts # events[], setEvents(), addEvent(), updateEvent(), deleteEvent() ``` **Routing:** Tab-based navigation with Chat and Calendar as main screens. Auth screens (login, register) outside tabs. Dynamic routes for event detail and note editing. ### Backend Architecture (apps/server) ``` src/ ├── app.ts # Entry point, DI setup, Express config ├── controllers/ # Request handlers + middleware (per architecture diagram) │ ├── AuthController.ts # login(), register(), refresh(), logout() │ ├── ChatController.ts # sendMessage(), confirmEvent(), rejectEvent(), getConversations(), getConversation() │ ├── EventController.ts # create(), getById(), getAll(), getByDateRange(), update(), delete() │ ├── AuthMiddleware.ts # authenticate() - JWT validation │ └── LoggingMiddleware.ts # httpLogger - pino-http request logging ├── logging/ │ ├── index.ts # Re-exports │ ├── logger.ts # pino config with redact for sensitive data │ └── Logged.ts # @Logged() class decorator for automatic method logging ├── routes/ # API endpoint definitions │ ├── index.ts # Combines all routes under /api │ ├── auth.routes.ts # /api/auth/* │ ├── chat.routes.ts # /api/chat/* (protected) │ └── event.routes.ts # /api/events/* (protected) ├── services/ # Business logic │ ├── interfaces/ # DB-agnostic interfaces (for dependency injection) │ │ ├── AIProvider.ts # processMessage() │ │ ├── UserRepository.ts # + CreateUserData (server-internal DTO) │ │ ├── EventRepository.ts │ │ └── ChatRepository.ts │ ├── AuthService.ts │ ├── ChatService.ts │ └── EventService.ts ├── repositories/ # Data access (DB-specific implementations) │ ├── index.ts # Re-exports from ./mongo │ └── mongo/ # MongoDB implementation │ ├── models/ # Mongoose schemas │ │ ├── types.ts # Shared types (IdVirtual interface) │ │ ├── UserModel.ts │ │ ├── EventModel.ts │ │ └── ChatModel.ts │ ├── MongoUserRepository.ts │ ├── MongoEventRepository.ts │ └── MongoChatRepository.ts ├── ai/ │ ├── GPTAdapter.ts # Implements AIProvider using OpenAI GPT │ ├── index.ts # Re-exports GPTAdapter │ └── utils/ # Shared AI utilities (provider-agnostic) │ ├── index.ts # Re-exports │ ├── eventFormatter.ts # formatExistingEvents() for system prompt │ ├── systemPrompt.ts # buildSystemPrompt() - German calendar assistant prompt │ ├── toolDefinitions.ts # TOOL_DEFINITIONS - provider-agnostic tool specs │ └── toolExecutor.ts # executeToolCall() - handles getDay, proposeCreate/Update/Delete, searchEvents └── utils/ ├── jwt.ts # signToken(), verifyToken() ├── password.ts # hash(), compare() ├── eventFormatters.ts # getWeeksOverview(), getMonthOverview() - formatted event listings └── recurrenceExpander.ts # expandRecurringEvents() - expand recurring events into occurrences ``` **API Endpoints:** - `POST /api/auth/login` - User login - `POST /api/auth/register` - User registration - `POST /api/auth/refresh` - Refresh JWT token - `POST /api/auth/logout` - User logout - `GET /api/events` - Get all events (protected) - `GET /api/events/range` - Get events by date range (protected) - `GET /api/events/:id` - Get single event (protected) - `POST /api/events` - Create event (protected) - `PUT /api/events/:id` - Update event (protected) - `DELETE /api/events/:id` - Delete event (protected) - `POST /api/chat/message` - Send message to AI (protected) - `POST /api/chat/confirm/:conversationId/:messageId` - Confirm proposed event (protected) - `POST /api/chat/reject/:conversationId/:messageId` - Reject proposed event (protected) - `GET /api/chat/conversations` - Get all conversations (protected) - `GET /api/chat/conversations/:id` - Get messages of a conversation with cursor-based pagination (protected) - `GET /health` - Health check - `POST /api/ai/test` - AI test endpoint (development only) ### Shared Package (packages/shared) ``` src/ ├── index.ts ├── models/ │ ├── index.ts │ ├── User.ts # User, CreateUserDTO, LoginDTO, AuthResponse │ ├── CalendarEvent.ts # CalendarEvent, CreateEventDTO, UpdateEventDTO, ExpandedEvent │ ├── ChatMessage.ts # ChatMessage, Conversation, SendMessageDTO, CreateMessageDTO, │ │ # GetMessagesOptions, ChatResponse, ConversationSummary, │ │ # ProposedEventChange, EventAction, RespondedAction, UpdateMessageDTO │ └── Constants.ts # DAYS, MONTHS, Day, Month, DAY_INDEX, DAY_INDEX_TO_DAY, │ # DAY_TO_GERMAN, DAY_TO_GERMAN_SHORT, MONTH_TO_GERMAN └── utils/ ├── index.ts └── dateHelpers.ts # getDay() - get date for specific weekday relative to today ``` **Key Types:** - `User`: id, email, displayName, passwordHash?, createdAt?, updatedAt? - `CalendarEvent`: id, userId, title, description?, startTime, endTime, note?, isRecurring?, recurrenceRule? - `ExpandedEvent`: Extends CalendarEvent with occurrenceStart, occurrenceEnd (for recurring event instances) - `ChatMessage`: id, conversationId, sender ('user' | 'assistant'), content, proposedChange?, respondedAction? - `ProposedEventChange`: action ('create' | 'update' | 'delete'), eventId?, event?, updates? - `Conversation`: id, userId, createdAt?, updatedAt? (messages loaded separately via lazy loading) - `CreateEventDTO`: Used for creating events AND for AI-proposed events - `GetMessagesOptions`: Cursor-based pagination with `before?: string` and `limit?: number` - `ConversationSummary`: id, lastMessage?, createdAt? (for conversation list) - `UpdateMessageDTO`: respondedAction? (for marking messages as confirmed/rejected) - `RespondedAction`: 'confirm' | 'reject' (tracks user response to proposed events) - `Day`: "Monday" | "Tuesday" | ... | "Sunday" - `Month`: "January" | "February" | ... | "December" ### Database Abstraction The repository pattern allows swapping databases: - **Interfaces** (`services/interfaces/`) are DB-agnostic - **Implementations** (`repositories/mongo/`) are DB-specific - To add MySQL: create `repositories/mysql/` with TypeORM entities ### Mongoose Model Pattern All Mongoose models use a consistent pattern for TypeScript-safe `id` virtuals: ```typescript import { IdVirtual } from './types'; const Schema = new Schema, {}, {}, IdVirtual>( { /* fields */ }, { virtuals: { id: { get() { return this._id.toString(); } } }, toJSON: { virtuals: true, transform: (_, ret) => { delete ret._id; delete ret.__v; return ret; } } } ); ``` Repositories use `doc.toJSON() as unknown as Type` casting (required because Mongoose's TypeScript types don't reflect virtual fields in toJSON output). ### Logging Structured logging with pino (server) and react-native-logs (client). **Server Logging:** - `pino` with `pino-pretty` for development, JSON in production - `pino-http` middleware logs all HTTP requests (method, path, status, duration) - `@Logged()` class decorator for automatic method logging on repositories and services - Sensitive data (password, token, etc.) automatically redacted via pino's `redact` config **@Logged Decorator Pattern:** ```typescript @Logged("MongoEventRepository") export class MongoEventRepository implements EventRepository { ... } @Logged("GPTAdapter") export class GPTAdapter implements AIProvider { ... } ``` The decorator uses a Proxy to intercept method calls lazily, preserves sync/async nature, and logs start/completion/failure with duration. **Client Logging:** - `react-native-logs` with namespaced loggers (apiLogger, storeLogger) - ApiClient logs all requests with method, endpoint, status, duration - Log level: debug in __DEV__, warn in production ## MVP Feature Scope ### Must-Have - Chat interface with AI assistant (text input) for event management - Calendar overview - Manual event CRUD (without AI) - View completed events - Simple reminders - One note per event - Recurring events ### Nice-to-Have - iCalendar import/export - Multiple calendars - CalDAV synchronization with external services ## Development Environment ### MongoDB (Docker) ```bash cd apps/server/docker/mongo docker compose up -d # Start MongoDB + Mongo Express docker compose down # Stop services ``` - MongoDB: `localhost:27017` (root/mongoose) - Mongo Express UI: `localhost:8083` (admin/admin) ### Environment Variables Server requires `.env` file in `apps/server/`: ``` JWT_SECRET=your-secret-key JWT_EXPIRES_IN=1h MONGODB_URI=mongodb://root:mongoose@localhost:27017/calchat?authSource=admin OPENAI_API_KEY=sk-proj-... USE_TEST_RESPONSES=false # true = static test responses, false = real GPT AI LOG_LEVEL=debug # debug | info | warn | error | fatal NODE_ENV=development # development = pretty logs, production = JSON ``` ## Current Implementation Status **Backend:** - **Implemented:** - `AuthController`: login(), register() with error handling - `AuthService`: login(), register() with password validation - `MongoUserRepository`: findByEmail(), create() - `utils/password`: hash(), compare() using bcrypt - `utils/jwt`: signToken() (verifyToken() pending) - `dotenv` integration for environment variables - `ChatController`: sendMessage(), confirmEvent(), rejectEvent() - `ChatService`: processMessage() with test responses (create, update, delete actions), confirmEvent() handles all CRUD actions - `MongoEventRepository`: Full CRUD implemented (findById, findByUserId, findByDateRange, create, update, delete) - `EventController`: Full CRUD (create, getById, getAll, getByDateRange, update, delete) - `EventService`: Full CRUD with recurring event expansion via recurrenceExpander - `utils/eventFormatters`: getWeeksOverview(), getMonthOverview() with German localization - `utils/recurrenceExpander`: expandRecurringEvents() using rrule library for RRULE parsing - `ChatController`: getConversations(), getConversation() with cursor-based pagination support - `ChatService`: getConversations(), getConversation(), processMessage() uses real AI or test responses (via USE_TEST_RESPONSES), confirmEvent()/rejectEvent() update respondedAction and persist response messages - `MongoChatRepository`: Full CRUD implemented (getConversationsByUser, createConversation, getMessages with cursor pagination, createMessage, updateMessage) - `ChatRepository` interface: updateMessage() added for respondedAction tracking - `GPTAdapter`: Full implementation with OpenAI GPT (gpt-5-mini model), function calling for calendar operations - `ai/utils/`: Provider-agnostic shared utilities (systemPrompt, toolDefinitions, toolExecutor, eventFormatter) - `logging/`: Structured logging with pino, pino-http middleware, @Logged decorator - All repositories and GPTAdapter decorated with @Logged for automatic method logging - **Stubbed (TODO):** - `AuthMiddleware.authenticate()`: Currently uses fake user for testing - `AuthController`: refresh(), logout() - `AuthService`: refreshToken() **Shared:** Types, DTOs, constants (Day, Month with German translations), ExpandedEvent type, and date utilities defined and exported. **Frontend:** - Tab navigation (Chat, Calendar) implemented with basic UI - Calendar screen fully functional: - Month navigation with grid display and Ionicons (chevron-back/forward) - MonthSelector dropdown with infinite scroll (dynamically loads months, lazy-loaded when modal opens, cleared on close for memory efficiency) - Events loaded from API via EventService.getByDateRange() - Orange dot indicator for days with events - Tap-to-open modal overlay showing EventCards for selected day - Supports events from adjacent months visible in grid - Uses `useFocusEffect` for automatic reload on tab focus - Chat screen fully functional with FlashList, message sending, and event confirm/reject - Messages persisted to database via ChatService and loaded on mount - Tracks conversationId for message continuity across sessions - ChatStore with addMessages() for bulk loading, chatMessageToMessageData() helper - KeyboardAvoidingView for proper keyboard handling (iOS padding, Android height) - Auto-scroll to end on new messages and keyboard show - keyboardDismissMode="interactive" and keyboardShouldPersistTaps="handled" - `ApiClient`: get(), post(), put(), delete() implemented with request/response logging - `EventService`: getAll(), getById(), getByDateRange(), create(), update(), delete() - fully implemented - `ChatService`: sendMessage(), confirmEvent(), rejectEvent(), getConversations(), getConversation() - fully implemented with cursor pagination - `EventCardBase`: Shared base component with event layout (header, date/time/recurring icons, description) - used by both EventCard and ProposedEventCard - `EventCard`: Uses EventCardBase + edit/delete buttons for calendar display - `ProposedEventCard`: Uses EventCardBase + confirm/reject buttons for chat proposals (supports create/update/delete actions) - `Themes.tsx`: Centralized color definitions including textPrimary, borderPrimary, eventIndicator, secondaryBg - `EventsStore`: Zustand store with setEvents(), addEvent(), updateEvent(), deleteEvent() - stores ExpandedEvent[] - `ChatStore`: Zustand store with addMessage(), addMessages(), updateMessage(), clearMessages() - loads from server on mount and persists across tab switches - Auth screens (Login, Register), Event Detail, and Note screens exist as skeletons - AuthStore defined with `throw new Error('Not implemented')` ## Documentation Detailed architecture diagrams are in `docs/`: - `api-routes.md` - API endpoint overview (German) - `technisches_brainstorm.tex` - Technical concept document (German) - `architecture-class-diagram.puml` - Backend class diagram - `frontend-class-diagram.puml` - Frontend class diagram - `component-diagram.puml` - System component overview