Move inactive projects out of active workspace: - bauntown (community website) - maerchenzauber (AI story generation) - memoro (voice memo app) - news (news aggregation) - nutriphi (nutrition tracking) - reader (reading app) - uload (URL shortener) - wisekeep (AI wisdom extraction) Update CLAUDE.md documentation: - Add presi to active projects - Document archived projects section - Update workspace configuration Archived apps can be re-activated by moving back to apps/ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
17 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
Storyteller is a magical children's story generation app with:
- AI-powered story creation with consistent characters
- Custom character generation from descriptions or photos
- Multi-page illustrated stories
- Credit-based system via Mana Core
- Support for multiple languages (German/English)
Prerequisites
- Node.js 18+ and pnpm 9+
- Install pnpm globally:
npm install -g pnpm - Supabase account and project
- Mana Core account for authentication
- AI API keys (Azure OpenAI, Gemini, Replicate)
Quick Start
Local Development Setup (Mobile + Local Backend)
Step 1: Configure mobile to use local backend
# Edit apps/mobile/.env and set:
echo "EXPO_PUBLIC_STORYTELLER_BACKEND_URL=http://localhost:3002" > apps/mobile/.env
echo "EXPO_ROUTER_APP_ROOT=app" >> apps/mobile/.env
# For physical devices, use your computer's IP instead:
# EXPO_PUBLIC_STORYTELLER_BACKEND_URL=http://192.168.1.X:3002
Step 2: Start backend and mobile
# Terminal 1 - Backend
cd apps/backend && npm run dev
# Backend runs on http://localhost:3002
# Wait for "Nest application successfully started" message
# Terminal 2 - Mobile
cd apps/mobile && npm run dev
# Press 'i' for iOS, 'a' for Android, 'w' for web
Step 3: Verify connection
# In Terminal 1 (backend), you should see incoming requests from mobile app
# Check health endpoint:
curl http://localhost:3002/health | jq
Alternative: Mobile with Production Backend
# Edit apps/mobile/.env to use production:
echo "EXPO_PUBLIC_STORYTELLER_BACKEND_URL=https://storyteller-backend-111768794939.europe-west3.run.app" > apps/mobile/.env
echo "EXPO_ROUTER_APP_ROOT=app" >> apps/mobile/.env
# Restart Expo with cache clear (important!)
cd apps/mobile && npx expo start -c
Quick Backend Switch Reference
| Setup | apps/mobile/.env Value | Terminal Setup |
|---|---|---|
| Local Backend | http://localhost:3002 |
Run both backend + mobile |
| Physical Device | http://YOUR_IP:3002 |
Run both backend + mobile |
| Production Backend | https://storyteller-backend-111768794939.europe-west3.run.app |
Run mobile only |
Remember: Always restart Expo with -c flag after changing .env
Other Development Commands
Install dependencies (first time):
pnpm install
Start everything via Turborepo:
pnpm run dev # From root - starts all services
Backend only:
cd apps/backend && npm run dev
# Test: curl http://localhost:3002/health | jq
Mobile only:
cd apps/mobile && npm run dev
Landing page:
cd apps/landing && npm run dev
Web app:
cd apps/web && npm run dev
Project Structure
apps/maerchenzauber/
├── apps/
│ ├── backend/ # NestJS backend
│ │ ├── src/
│ │ │ ├── story/ # Story generation & management
│ │ │ │ ├── services/story-creation.service.ts # Main story generation logic
│ │ │ │ └── illustration.service.ts # Image generation
│ │ │ ├── character/ # Character creation & management
│ │ │ │ └── character.service.ts
│ │ │ ├── core/
│ │ │ │ ├── services/
│ │ │ │ │ ├── prompting.service.ts # AI prompt management
│ │ │ │ │ ├── image-supabase.service.ts # Image storage
│ │ │ │ │ └── supabase-data.service.ts # Database operations
│ │ │ │ └── consts/ # System prompts & constants
│ │ │ ├── settings/ # User settings
│ │ │ ├── credits/ # Credit management
│ │ │ ├── auth/ # Authentication proxy
│ │ │ └── health/ # Health checks
│ │ └── .env # Backend configuration
│ ├── mobile/ # React Native Expo app
│ │ ├── app/ # expo-router routes
│ │ │ └── (tabs)/
│ │ │ ├── (story)/ # Story screens
│ │ │ └── (character)/ # Character screens
│ │ ├── src/
│ │ │ ├── utils/api.ts # API client
│ │ │ ├── services/
│ │ │ │ ├── authService.ts
│ │ │ │ └── tokenManager.ts
│ │ │ ├── components/ # Reusable components
│ │ │ └── hooks/ # Custom React hooks
│ │ └── .env # Mobile configuration
│ ├── landing/ # Astro landing page
│ └── web/ # SvelteKit web app (NEW)
├── docs/ # Documentation
│ └── SYSTEM_CHARACTERS.md # System characters guide
├── turbo.json # Turborepo configuration
└── package.json # Root package with workspace scripts
Key Implementation Details
Story Generation Flow
- Character Selection: User selects existing character or creates new one
- Story Request: User provides story description, system selects author/illustrator personas
- Generation Pipeline:
- Generate story text (10 pages) via
story-creation.service.ts - Extract character descriptions
- Generate illustration prompts
- Create images via Replicate
- Translate to German
- Save to database
- Generate story text (10 pages) via
- Credit Deduction: 10 credits per story/character via Mana Core
System Characters
System characters are special read-only characters visible to all users:
- Identified by system user ID:
00000000-0000-0000-0000-000000000000 - Accessible to all authenticated users via RLS policies
- Cannot be edited or deleted by regular users
- UI hides edit/share buttons for system characters
- Currently includes "Finia" the wise fox as the default character
See detailed documentation: docs/SYSTEM_CHARACTERS.md
Key implementation points:
- Database: Characters table with system
user_id - Backend: Validation allows both owned and system characters
- Frontend: Conditional rendering based on
user_idcheck - Storage: Images stored in system user folder in Supabase Storage
AI Services Architecture
- Prompting:
backend/src/core/services/prompting.service.tshandles all AI interactions - System Prompts: Stored in
backend/src/core/consts/ - Image Generation:
backend/src/core/services/image-supabase.service.ts - Storage: Supabase Storage bucket
maerchenzauber - Models: Azure OpenAI (GPT-4), Google Gemini, Replicate (Flux for images)
Database (Supabase)
- Client:
backend/src/supabase/supabase.provider.ts - Data Service:
backend/src/core/services/supabase-data.service.ts - Tables:
characters,stories,story_collections,user_settings
Authentication
- Uses Mana Core for authentication
- Backend acts as proxy:
backend/src/auth/ - Mobile token management:
mobile/src/services/tokenManager.ts - Secure storage:
expo-secure-storefor tokens
Package Manager
This project uses PNPM as the package manager for better performance and disk space efficiency.
Key PNPM commands:
pnpm install # Install all dependencies
pnpm add <package> # Add a dependency to workspace root
pnpm add <package> -w # Add to workspace root (explicit)
pnpm add <package> --filter @storyteller/backend # Add to specific app
Available Scripts
Root Level
pnpm run dev- Start all services (Turborepo)pnpm run build- Build all applicationspnpm run lint- Lint all packagespnpm run type-check- TypeScript checkspnpm run format- Format with Prettierpnpm run clean- Clean build artifacts
Backend (cd apps/backend)
pnpm run dev- Start with hot reload (port 3002)pnpm run build- Build production bundlepnpm run start:prod- Run production buildpnpm run test- Run Jest testspnpm run test:watch- Tests in watch modepnpm run test:cov- Tests with coveragepnpm run type-check- TypeScript validation
Mobile (cd apps/mobile)
pnpm run dev- Start Expo dev serverpnpm run ios- Run on iOS simulatorpnpm run android- Run on Android emulatorpnpm run web- Run in browserpnpm run build- Export production buildpnpm run test- Run testspnpm run type-check- TypeScript validation
Landing Page (cd apps/landing)
pnpm run dev- Start Astro dev serverpnpm run build- Build static sitepnpm run preview- Preview production build
Web App (cd apps/web)
pnpm run dev- Start SvelteKit dev serverpnpm run build- Build production bundlepnpm run preview- Preview production build
Troubleshooting
Backend Won't Start
# Port 3002 already in use
lsof -i :3002
kill -9 $(lsof -t -i:3002)
# Check environment variables
cat backend/.env | grep -v "^#" | grep -v "^$"
# Database connection issues - verify:
# - MAERCHENZAUBER_SUPABASE_URL
# - MAERCHENZAUBER_SUPABASE_ANON_KEY
# - MAERCHENZAUBER_SUPABASE_SERVICE_ROLE_KEY
Mobile Can't Connect to Backend
# For physical devices, use computer IP instead of localhost
ifconfig | grep "inet " | grep -v 127.0.0.1
# Update apps/mobile/.env
EXPO_PUBLIC_STORYTELLER_BACKEND_URL=http://YOUR_IP:3002
# IMPORTANT: After changing .env, restart Expo dev server
cd apps/mobile && npx expo start -c # -c flag clears cache
Environment Variables Not Loading
# Mobile app: Environment changes require restart
# 1. Stop Expo dev server (Ctrl+C)
# 2. Clear cache and restart:
cd apps/mobile && npx expo start -c
# Backend: Changes require restart
# 1. Stop backend (Ctrl+C)
# 2. Restart:
cd apps/backend && npm run dev
# Verify environment is loaded (backend):
# Check logs on startup - you should see configuration being loaded
AI Service Errors
Check these environment variables in apps/backend/.env:
MAERCHENZAUBER_AZURE_OPENAI_ENDPOINTand_KEYMAERCHENZAUBER_GOOGLE_GENAI_API_KEYMAERCHENZAUBER_REPLICATE_API_KEY
iOS Deeplink Crashes
If the app crashes when opening deeplinks on iOS (especially character sharing links):
See complete debugging guide for detailed solutions.
Quick fixes:
- Check
expo-linear-gradientversion is 15.0.7+ - Ensure all
LinearGradientstyles usealignSelf: 'stretch'instead ofwidth: '100%' - Verify
MagicalLoadingScreenhascontextprop - Check
expo-imagehastransition={0}on navigation screens - Ensure each route directory has proper
_layout.tsxwithfreezeOnBlur: false
Common symptoms:
- Crash during navigation after deeplink opens
CoreGraphics CGContextDrawLinearGradienterrorsRNSScreen setViewToSnapshotcrashes- Image deallocation errors
API Testing
Health Checks
# Full health check
curl http://localhost:3002/health | jq
# Readiness check (for deployments)
curl http://localhost:3002/health/ready | jq
# Liveness check
curl http://localhost:3002/health/live | jq
Main Endpoints
# Authentication
POST /auth/signin # Sign in with email/password
POST /auth/signup # Create account
POST /auth/google-signin # Google OAuth
POST /auth/apple-signin # Apple Sign In
POST /auth/refresh # Refresh token
POST /auth/logout # Log out
# Characters
GET /character # List user's characters
POST /character/generate-images # Generate character
GET /character/:id # Get specific character
PUT /character/:id # Update character
DELETE /character/:id # Delete character
# Stories
GET /story # List user's stories
POST /story # Create new story
GET /story/:id # Get specific story
PUT /story/:id # Update story
DELETE /story/:id # Delete story
# Settings
GET /settings/user # User settings
PUT /settings/user # Update settings
GET /settings/creators # Available creators
GET /settings/languages # Supported languages
# Credits
GET /credits/balance # Get balance
GET /credits/history # Transaction history
POST /credits/check # Check availability
POST /credits/consume # Consume credits
Environment Variables
Backend (backend/.env)
Required:
MANA_SERVICE_URL- Mana Core auth service URLAPP_ID- Mana Core application IDSERVICE_KEY- Mana Core service keyMAERCHENZAUBER_SUPABASE_URL- Supabase project URLMAERCHENZAUBER_SUPABASE_ANON_KEY- Supabase anonymous keyMAERCHENZAUBER_SUPABASE_SERVICE_ROLE_KEY- Supabase service role keyMAERCHENZAUBER_AZURE_OPENAI_ENDPOINT- Azure OpenAI endpointMAERCHENZAUBER_AZURE_OPENAI_KEY- Azure OpenAI keyMAERCHENZAUBER_GOOGLE_GENAI_API_KEY- Google Gemini API keyMAERCHENZAUBER_REPLICATE_API_KEY- Replicate API tokenMAERCHENZAUBER_STORAGE_BUCKET- Supabase storage bucket name (default:maerchenzauber)
Optional:
PORT- Server port (default: 3002)NODE_ENV- Environment (development/production)
Mobile (apps/mobile/.env)
Required:
EXPO_PUBLIC_STORYTELLER_BACKEND_URL- Backend API URL- Local:
http://localhost:3002 - Production:
https://storyteller-backend-111768794939.europe-west3.run.app - Physical devices:
http://YOUR_COMPUTER_IP:3002
- Local:
EXPO_ROUTER_APP_ROOT- App root (alwaysapp)
Code Patterns & Standards
Backend (NestJS/TypeScript)
- Dependency Injection: Use NestJS DI with
@Injectable()decorators - Error Handling: Return
Result<T>type:{ data: T | null, error: Error | null } - DTOs: Use
class-validatordecorators for validation - Logging: Use console.log with service prefix:
console.log('[ServiceName]', 'message', data) - Database: All queries through
supabase-data.service.ts
Mobile (React Native/Expo)
- Components: Functional components with hooks only
- Navigation: Use
expo-routerfile-based routing - API Calls: Through
src/utils/api.tswith automatic token refresh - Storage: Use
expo-secure-storefor sensitive data,@react-native-async-storage/async-storagefor non-sensitive - State: Local state with
useState, no global state library currently
Error Handling Pattern
// Backend
async function operation(): Promise<Result<Data>> {
try {
const result = await doSomething();
return { data: result, error: null };
} catch (error) {
console.error('[ServiceName] Operation failed:', error);
return { data: null, error };
}
}
// Mobile
const response = await fetchWithAuth('/endpoint', {
method: 'POST',
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
Deployment
Backend (Google Cloud Run)
Current production: https://storyteller-backend-111768794939.europe-west3.run.app
cd apps/backend
# Build and test locally
docker build -t storyteller-backend .
docker run -p 3002:3002 --env-file .env storyteller-backend
# Deploy to Cloud Run
gcloud builds submit --tag gcr.io/PROJECT_ID/storyteller-backend
gcloud run deploy storyteller-backend \
--image gcr.io/PROJECT_ID/storyteller-backend \
--region europe-west3 \
--platform managed \
--allow-unauthenticated
Mobile (EAS Build)
EAS configuration in eas.json at project root.
cd apps/mobile
# Configure EAS (first time)
eas build:configure
# Build for iOS
eas build --platform ios --profile production
# Build for Android
eas build --platform android --profile production
# Submit to stores
eas submit --platform ios
eas submit --platform android
Landing Page (Netlify/Vercel)
cd apps/landing
# Build locally
npm run build
# Deploy (automatically via git push to main)
Web App (SvelteKit)
cd apps/web
# Build locally
npm run build
# Preview production build
npm run preview
Debugging
Backend Debugging
# Check logs in production
gcloud run services logs read storyteller-backend --limit=100
# Monitor specific service
gcloud logging read "resource.type=cloud_run_revision AND resource.labels.service_name=storyteller-backend" --limit=50
Mobile Debugging
- Network: Check API calls in
apps/mobile/src/utils/api.ts - Storage: Use React Native Debugger to inspect AsyncStorage/SecureStore
- Logs: Use
console.log('[ComponentName]', message)format - Expo: Use
npx expo start --dev-clientfor more debugging tools
Database Debugging
- Use Supabase Dashboard for direct queries
- Add
.select('*')to see all returned fields - Check RLS policies if queries return empty results