Files
calchat/CLAUDE.md
Linus Waldowsky 675785ec93 feat: replace Claude with GPT for AI chat integration
- Replace ClaudeAdapter with GPTAdapter using OpenAI GPT (gpt-5-mini)
- Implement function calling for calendar operations (getDay, proposeCreate/Update/Delete, searchEvents)
- Add provider-agnostic AI utilities in ai/utils/ (systemPrompt, toolDefinitions, toolExecutor, eventFormatter)
- Add USE_TEST_RESPONSES env var to toggle between real AI and test responses
- Switch ChatService.processMessage to use real AI provider
- Add npm run format command for Prettier
- Update CLAUDE.md with new architecture
2026-01-10 00:22:59 +01:00

17 KiB

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)

npm install          # Install all dependencies for all workspaces
npm run format       # Format all TypeScript files with Prettier

Client (apps/client) - Expo React Native app

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

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
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
├── services/
│   ├── index.ts                # Re-exports all services
│   ├── ApiClient.ts            # HTTP client (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
│   ├── AuthController.ts     # login(), register(), refresh(), logout()
│   ├── ChatController.ts     # sendMessage(), confirmEvent(), rejectEvent(), getConversations(), getConversation()
│   └── EventController.ts    # create(), getById(), getAll(), getByDateRange(), update(), delete()
├── middleware/
│   └── AuthMiddleware.ts     # authenticate() - JWT validation
├── 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:

import { IdVirtual } from './types';

const Schema = new Schema<Doc, Model<Doc, {}, {}, IdVirtual>, {}, {}, 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).

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)

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

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)
  • 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
  • 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