Imported from shalean-developer/shalean-platform (
AGENTS.md). Install upstream withnpx skills add shalean-developer/shalean-platform. Copyright stays with the author.
AGENTS.md
Cursor Cloud specific instructions
Repo shape
- npm-based monorepo (no root workspace). Each app installs independently with
npm ciin its own dir; there is no top-level install. Node 20+ (enginesin rootpackage.json). - Shared code lives in
packages/*and is linked into apps viafile:deps (e.g.apps/web/node_modules/@shalean/*symlink topackages/*). - Primary runnable service is
apps/web(Next.js 16 App Router) — it is the backend/API (/api/*) for all clients. The two Expo apps (apps/mobile= cleaner,apps/customer-mobile= customer) are thin clients that call this API and defaultEXPO_PUBLIC_API_BASE_URLto production.
Running apps/web (dev)
- The update script runs
npm ciinapps/web. To start:cd apps/web && npm run dev(Next.js webpack, port 3000). Health check:GET /api/health. - CRITICAL: open the dev server via
http://localhost:3000, NOThttp://127.0.0.1:3000. On Next 16,127.0.0.1is treated as a cross-origin host and/_next/webpack-hmr+ dev chunks are blocked, so client components never hydrate (client pages like/book/<service>get stuck on gray skeleton loaders forever).localhostworks. - Use
npm run dev(webpack), notdev:turbo— Turbopack's default root (apps/web) can't resolve thefile:../../packages/*deps.
Env / graceful degradation
- The app boots and degrades gracefully with no secrets: marketing/blog pages and the booking form (real-time pricing via
@shalean/pricing, staticSERVICE_CONFIGfallback) all work without a database. - DB-backed features require Supabase env in
apps/web/.env.local(copy fromapps/web/.env.example):NEXT_PUBLIC_SUPABASE_URL,NEXT_PUBLIC_SUPABASE_ANON_KEY,SUPABASE_SERVICE_ROLE_KEY. Also addNEXT_PUBLIC_PAYSTACK_PUBLIC_KEY+PAYSTACK_SECRET_KEYfor the payment path. Without Supabase you will see "Area not yet covered / server configuration error" on suburb validation; auth, admin/office, dispatch, and payments also need real creds. Minimalapps/web/.env.local:SHALEAN_APP_ENV=development NEXT_PUBLIC_SITE_URL=http://localhost:3000 OUTBOUND_MESSAGING_DISABLED=true NEXT_PUBLIC_SUPABASE_URL=... NEXT_PUBLIC_SUPABASE_ANON_KEY=... SUPABASE_URL=... # same as NEXT_PUBLIC_SUPABASE_URL SUPABASE_SERVICE_ROLE_KEY=... NEXT_PUBLIC_PAYSTACK_PUBLIC_KEY=... PAYSTACK_SECRET_KEY=... - Never assume a remote development database is empty. Inspect it read-only before testing or resetting data, and use the governed seed commands below instead of manually copying production data or writing ad-hoc rows through REST/Studio.
Lint / test / build (see apps/web/package.json + apps/web/README.md)
- CI gates (
.github/workflows/web-test.yml) are the source of truth:npm run test:critical,npm run lint:booking-core, andnpm run typecheck. The packagenpm run builduses Turbopack; CI deliberately runs its own production Next.js build with Webpack. Do not describe one as the other. - Do NOT treat full
npm run lintas a gate: it reports pre-existing errors/warnings and is not run in CI. Uselint:booking-corefor the enforced lint gate.
Development database seed
-
Run the governed seed from repo root only against an approved development or staging project. It requires
NEXT_PUBLIC_SUPABASE_URLand the server-onlySUPABASE_SERVICE_ROLE_KEYinapps/web/.env.localor the shell environment:npm run db:seed:dev # seed / re-seed (idempotent) npm run db:seed:dev:reset # wipe seed rows then re-seed npm run db:seed:dev:dry-run # print plan without writing npm run db:seed:reference # export reference pricing from dev DB -
The seed creates: 1 city (Cape Town), 7 suburbs, 6 pricing services, 26 extras, 17 auth users (3 admin, 6 cleaner, 8 customer), 15 bookings, 3 recurring schedules, 5 earnings rows, 5 payout rows, 2 monthly invoices, 5 admin proposals.
-
All seed emails use
@example.com(IANA reserved, undeliverable). All seed phones use+27000...— the000area prefix is structurally impossible in South Africa (SA area codes never begin with zero) and cannot route to any real recipient via Twilio, Meta, or any provider. -
The seed has a multi-layer safety guard in
scripts/seed-dev.mjs: (1)SHALEAN_APP_ENVmust bedevelopmentorstaging; (2) ifSUPABASE_PROD_REFenv var is set the URL must not match it; (3) ifSEED_ALLOWED_PROJECT_REFSis set the ref must be in that list. Project refs are never hardcoded in source — they are read from environment variables (inapps/web/.env.local, gitignored) so no real identifiers appear in committed code. -
Outbound comms guard:
apps/web/lib/seed/devSeedGuard.tsexportsassertNotSeedRecipient(),assertNotSeedWhatsApp(),assertNotSeedSms(),assertNotSeedEmail()— import and call these before any SMS/WhatsApp/email/push provider call in non-production code paths. No-op inNODE_ENV=production. -
Seed script location:
scripts/seed-dev.mjs. Reference pricing SQL:supabase/seed/reference/pricing.sql. -
Unit tests for seed safety:
apps/web/lib/seed/__tests__/seedSafety.test.ts(80 tests). -
Recommended additional
.env.localentries for maximum seed safety:SUPABASE_PROD_REF=<prod-project-ref>andSEED_ALLOWED_PROJECT_REFS=<dev-ref>,<stg-ref>. -
Never commit, print, paste into chat, or expose
SUPABASE_SERVICE_ROLE_KEY; anyNEXT_PUBLIC_*variable is browser-visible and must never contain privileged credentials.
Mobile apps
- Not installed by the update script. Install on demand with
npm ciinsideapps/mobileorapps/customer-mobile, thennpm start(Expo). See each app's own README/AGENTS.md.