Markdown Converter
Agent skill for markdown-converter
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Sign in to like and favorite skills
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
npm start # Start Expo dev server (pick iOS/Android/Web from menu) npm run android # Run on Android emulator npm run ios # Run on iOS simulator npm run web # Run in web browser
Production builds use EAS CLI:
eas build --platform android|ios
Edge Function deployment:
supabase functions deploy saint-match
No test suite is configured yet.
Saint Match is a React Native app built with Expo 54 + Expo Router + TypeScript. It helps Catholics practice daily virtue through personalized challenges inspired by Catholic saints — "Duolingo for becoming a better person."
Client → Supabase Auth (anonymous-first) Client → Supabase Edge Function (saint-match) → Claude API (key server-side) Client → AsyncStorage (offline cache) ↔ Supabase DB (cloud source of truth)
app/ ├── _layout.tsx # Root layout (fonts, splash, AppProvider) ├── index.tsx # Splash → routes to welcome or home ├── (public)/ # Unauthenticated group │ ├── welcome.tsx # Onboarding intro │ └── onboarding.tsx # Full onboarding flow └── (auth)/ # Protected group (post-onboarding) ├── saint-match.tsx # View matched saint + accept challenge ├── celebration.tsx # Post-completion celebration ├── weekly-checkin.tsx # Rate weekly patience score (1-5) └── (tabs)/ # Bottom tab navigation ├── index.tsx # Home (main UX: emotion select → match) ├── calendar.tsx # Streak calendar view ├── portfolio.tsx # Virtue portfolio + completions log └── settings.tsx # Settings, account linking
Auth routing is onboarding-based —
isOnboarded in AppContext controls public vs auth flow. Anonymous Supabase auth happens transparently during init.
Single React Context (
context/AppContext.tsx) exposed via useApp() hook. Key state: streak data, active challenge, completions log, usage limits, pro status, Supabase session.
Initialization flow in
AppProvider:
ensureAnonymousSession() — creates or restores Supabase authrefreshAll() — loads all data from AsyncStorage (fast)syncAllData() — background sync with Supabase (slow, non-blocking)lib/claude.ts calls Edge Function → checks cache → calls Claude API → falls back to local saints datalib/supabase.ts — Supabase client (AsyncStorage session persistence) + auth helpers: ensureAnonymousSession(), linkEmailToAccount(), signOut().lib/claude.ts — Calls the saint-match Edge Function with JWT. Falls back to local matching if offline. Throws USAGE_LIMIT_REACHED on 429.lib/sync.ts — Bridge between AsyncStorage and Supabase. Push functions for completions, streaks, challenges. Pull for server-authoritative usage data. One-time migration of existing local data.lib/storage.ts — AsyncStorage CRUD (local offline layer). Keys prefixed @saint_match_*. Usage auto-resets weekly. Challenges auto-clear if from previous day.lib/streak.ts — Streak calculation with auto-reset if >1 day gap. 1 free streak freeze per week.lib/notifications.ts — Daily reminders (8:30 AM) and streak alerts (8:00 PM) via expo-notifications.lib/purchases.ts — Mock RevenueCat. Checks profiles.is_pro from Supabase when online. Free tier: 3 matches/week.Edge Function (
supabase/functions/saint-match/): Single endpoint that validates JWT, checks usage, queries match cache (6hr TTL), calls Claude API, stores cached responses, falls back to embedded local saints data.
Database tables (schema in
supabase/migrations/001_initial_schema.sql):
profiles — auto-created on signup, stores is_pro, is_onboarded, emailusage — weekly match count per user (server-side limit enforcement)match_cache — cached Claude responses with TTL (writable only by service_role)completions — challenge completion log (unique per user+date)streaks — one row per user with current/longest streakactive_challenges — today's challenge as JSONBpatience_scores — weekly self-assessment ratingsRLS enabled on all tables.
handle_new_user() trigger auto-creates profile + streak rows.
Client-side (
.env):
EXPO_PUBLIC_SUPABASE_URL — Supabase project URLEXPO_PUBLIC_SUPABASE_ANON_KEY — Supabase anon/public keyServer-side (Edge Function secrets):
ANTHROPIC_API_KEY — Claude API key (set via supabase secrets set)SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY — auto-provided by Supabase runtimeAll design tokens live in
constants/:
colors.ts — Sage green (#8B9D83) primary, terracotta (#D4735E) accent, cream (#FAF8F5) backgroundtypography.ts — Cormorant Garamond (serif, for titles/saint names) + Inter (sans, for body/UI). 13 named text styles.spacing.ts — 4px base unit scale, border radii, shadow presetsAll domain types in
types/index.ts: Saint, Emotion (6 values), MicroAction, SaintMatch, ActiveChallenge, Completion, StreakData, UsageData, PatienceScore.
Schema lives in
supabase/migrations/. Key files:
001_initial_schema.sql — Core tables, RLS policies, handle_new_user() trigger004_production_hardening.sql — Atomic usage RPC, performance indexes, DELETE RLS policies, cache cleanupStyleSheet.create() at bottom of each fileFadeIn, FadeInDown) and spring-based press interactions@/ path alias maps to project root (configured in tsconfig.json).catch(() => {}) sync to Supabase@saint_match_sync_queue) and replayed on next syncAllData()Run migrations in order in the Supabase SQL Editor:
001_initial_schema.sql (if fresh project)004_production_hardening.sql (indexes, atomic usage RPC, DELETE policies)Enable Anonymous Sign-Ins in Supabase Dashboard → Authentication → Settings
Set Edge Function secrets:
supabase secrets set ANTHROPIC_API_KEY=sk-ant-... # SUPABASE_URL, SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY are auto-provided
Deploy Edge Function:
supabase functions deploy saint-match
Verify the Edge Function:
.env.example to .env and fill in your Supabase project URL and anon keynpm installnpm start to verify