diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..92e4ae52 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,106 @@ +# PROJECT KNOWLEDGE BASE + +## OVERVIEW +SaaS template built with **SvelteKit (Svelte 5)** + **Tailwind v4** + **DaisyUI** + **Supabase (Auth + Postgres)** + **Stripe (Checkout + Customer Portal)**. + +Two major route domains: +- **Marketing** (pre-rendered, SEO-first) +- **Account dashboard** (authenticated, server actions, billing) + +## STRUCTURE +```text +./ +├── src/ +│ ├── hooks.server.ts # Supabase clients + safeGetSession + locals +│ ├── config.ts # SITE NAME/URL/DESCRIPTION + onboarding flag +│ ├── lib/ # mailer, search index, load helpers +│ └── routes/ +│ ├── (marketing)/ # public pages: home, pricing, blog, login, search +│ └── (admin)/account/ # dashboard: profile/settings/billing/subscriptions +├── supabase/ +│ └── migrations/ # incremental DB changes (preferred after initial setup) +├── database_migration.sql # one-shot initial schema for *new* Supabase projects +├── static/ # favicons/images (served as-is) +└── .github/workflows/ # CI: build/format/lint/check/test +``` + +## WHERE TO LOOK (TASK → FILES) +| Task | Location | Notes | +|------|----------|-------| +| Rename/brand the site | `src/config.ts`, `src/routes/(marketing)/+layout.svelte`, `static/` | Config drives SEO + sitemap origin. +| Marketing homepage copy/SEO | `src/routes/(marketing)/+page.svelte` | `prerender = true` (static). +| Pricing tiers + Stripe IDs | `src/routes/(marketing)/pricing/pricing_plans.ts` | Replace demo `price_...`/`prod_...`. +| Auth/session plumbing | `src/hooks.server.ts`, `src/routes/(marketing)/login/+layout.ts` | `safeGetSession` validates JWT via `getUser()`. +| OAuth providers + Auth UI theme | `src/routes/(marketing)/login/login_config.ts`, `src/app.css` | DaisyUI variables theme Supabase Auth UI. +| Dashboard (replace demo) | `src/routes/(admin)/account/(menu)/+page.svelte` | Explicit placeholder content. +| Account settings actions | `src/routes/(admin)/account/api/+page.server.ts` | Email/password/profile/delete actions. +| Billing flows | `src/routes/(admin)/account/subscription_helpers.server.ts`, `.../billing/manage/+page.server.ts`, `.../subscribe/[slug]/+page.server.ts` | Stripe Checkout + Portal. No webhook handler in this repo. +| Email sending + templates | `src/lib/mailer.ts`, `src/lib/emails/*.hbs`, `email_docs.md` | Disabled unless env vars set. +| Search index | `src/lib/build_index.ts`, `vite.config.ts`, `src/routes/(marketing)/search/**` | Index built at `vite build`. +| Database schema/RLS | `database_migration.sql`, `supabase/migrations/*` | RLS enabled on all included tables. +| CI commands | `package.json`, `checks.sh`, `.github/workflows/*.yml` | CI uses fake env vars. + +## TEMPLATE CUSTOMIZATION ZONES (DON’T MISS) +1. **Brand + SEO** + - `src/config.ts`: `WebsiteName`, `WebsiteBaseUrl`, `WebsiteDescription`. + - `src/routes/(marketing)/**`: per-page `` + `<meta name="description">`. + - `src/routes/(marketing)/sitemap.xml/+server.ts`: uses `WebsiteBaseUrl`. + +2. **Navigation + footer links (currently point to upstream + sponsor)** + - `src/routes/(marketing)/+layout.svelte`: header/footer nav + GitHub link + sponsor block. + +3. **Auth providers** + - `src/routes/(marketing)/login/login_config.ts`: `oauthProviders` list. + - Supabase Dashboard: redirect URLs + provider secrets. + - `src/routes/(marketing)/auth/callback/+server.js`: auth code exchange/redirect. + +4. **Pricing + subscriptions** + - `src/routes/(marketing)/pricing/pricing_plans.ts`: plan IDs, descriptions, and Stripe `price`/`product` IDs. + - `src/routes/(marketing)/pricing/+page.svelte`: FAQ + feature table are demo content. + +5. **Replace demo dashboard with your product** + - `src/routes/(admin)/account/(menu)/+page.svelte`: intentionally a placeholder. + - Add your own app routes under `src/routes/(admin)/...` or new groups. + +6. **Database model** + - New projects: start with `database_migration.sql`. + - Ongoing changes: add new files in `supabase/migrations/` (don’t keep editing the one-shot file). + - Always enable RLS + add policies for new user-facing tables. + +7. **Email (optional, easy to accidentally “turn on”)** + - Setting `PRIVATE_RESEND_API_KEY` enables sending (see `email_docs.md`). Customize templates first. + +## CONVENTIONS (PROJECT-SPECIFIC) +- **Marketing pages are pre-rendered** (`export const prerender = true` in several `+page.ts` / `+layout.ts`). If you add server-only/dynamic data, you must disable prerender for that route. +- **Search index is built during `vite build`** via a Vite plugin (`vite.config.ts`). In prod, `/search/api.json` is served from the built artifact. +- **Two Supabase clients**: + - `event.locals.supabase`: SSR client for user-scoped queries (RLS enforced). + - `event.locals.supabaseServiceRole`: service role client for privileged ops (bypasses RLS). + +## ANTI-PATTERNS (THIS REPO) +- Don’t put **service role** keys anywhere client-accessible. Keep `$env/static/private` imports server-only. +- Billing customer creation uses **insert (not upsert)** for `stripe_customers` to avoid accidental overwrites. +- There is a known **temporary CSS hack** for Supabase Auth UI button styling in `src/app.css`. + +## COMMANDS +```bash +# dev +npm run dev + +# format / lint / types +npm run format +npm run lint +npm run check + +# tests +npm run test +npm run test_run + +# one-shot: mirrors CI locally +chmod +x ./checks.sh +./checks.sh +``` + +## NOTES +- CI workflows set fake Supabase/Stripe env vars; local dev needs `.env.local` from `.env.example`. +- Stripe integration here is Checkout + Portal; if you need strict subscription state sync, you’ll likely add **Stripe webhooks** as a new `+server.ts` endpoint. diff --git a/README.md b/README.md index 21705974..755a5966 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,14 @@ Created by the folks at [Kiln AI](https://getkiln.ai)! It's the easiest tool for **[Kiln AI](https://getkiln.ai)** Rapid AI Prototyping and Dataset Collaboration Tool +## AI Agents support + +This repo uses [AGENTS.md](https://github.com/agentsmd/agents.md) guiding (plus nested `*/AGENTS.md`) to help AI agents (and humans) navigate the template and find the right customization zones. + +- 👍 **Recommended**: [OpenCode](https://github.com/anomalyco/opencode) with [oh-my-opencode](https://github.com/code-yeongyu/oh-my-opencode) plugin (native support for `AGENTS.md` + nested `AGENTS.md`). +- 👌 **Alternatives**: [OpenAI Codex](https://github.com/openai/codex), [Gemini CLI](https://github.com/google-gemini/gemini-cli). +- 🤷‍♂️ **Not recommended** _(vendor lock-in)_: **Claude Code**. _If you want to use it anyway, rename all `AGENTS.md` files to `CLAUDE.md`_. + ## Demo You can explore all the features using our fully functional demo [saasstarter.work](https://saasstarter.work). diff --git a/src/AGENTS.md b/src/AGENTS.md new file mode 100644 index 00000000..61622675 --- /dev/null +++ b/src/AGENTS.md @@ -0,0 +1,35 @@ +# src/ — APP CORE + +## OVERVIEW +Application entry plumbing: Supabase auth/session, shared types/config, and the SvelteKit route tree. + +## STRUCTURE +```text +src/ +├── hooks.server.ts # event.locals.supabase + service role + safeGetSession +├── config.ts # template knobs (name/base url/description, onboarding flag) +├── DatabaseDefinitions.ts # generated-ish DB types used by Supabase client +├── lib/ # cross-cutting utilities (mailer/search/load helpers) +└── routes/ # filesystem routing +``` + +## WHERE TO LOOK +- **Auth/session**: `hooks.server.ts` +- **Branding/SEO defaults**: `config.ts` +- **DB typing**: `DatabaseDefinitions.ts` + +Related typing glue: +- `app.d.ts`: defines `event.locals` types (supabase clients + session/user) +- `ambient.d.ts`: project ambient types + +## CHANGE THESE (TEMPLATE USERS) +- `config.ts`: set your product name, canonical base URL, and description. +- `hooks.server.ts`: if you add custom `locals` or request-wide auth rules, do it here. + +## CONVENTIONS / GOTCHAS +- `safeGetSession` is the “trusted” session getter: it calls `getSession()` and then `getUser()` to validate JWT. +- This repo uses a Supabase auth warning suppression shim in `hooks.server.ts` (see inline comment/link). Treat it as version-compat glue. + +## ANTI-PATTERNS +- Don’t import `$env/static/private` from any module that can end up client-side. +- Don’t use the service role client for user-facing queries; prefer RLS via `event.locals.supabase`. diff --git a/src/lib/AGENTS.md b/src/lib/AGENTS.md new file mode 100644 index 00000000..27d8800a --- /dev/null +++ b/src/lib/AGENTS.md @@ -0,0 +1,30 @@ +# src/lib/ — SHARED UTILITIES + +## OVERVIEW +Cross-cutting helpers used by routes: email, search indexing, and Supabase session helpers. + +## WHERE TO LOOK +- **Email sending**: `mailer.ts` + - Templates: `emails/welcome_email_{text,html}.hbs` + - Setup docs: `/email_docs.md` +- **Search indexing**: `build_index.ts` + - Build hook: `/vite.config.ts` (builds index after bundle) + - Runtime API: `src/routes/(marketing)/search/api.json/+server.ts` +- **Session helper**: `load_helpers.ts` (used in login/account layouts) + +## ENV VARS (USED HERE) +- `PRIVATE_RESEND_API_KEY`, `PRIVATE_ADMIN_EMAIL`, `PRIVATE_FROM_ADMIN_EMAIL` (email) +- Supabase/Stripe keys are imported by routes/hooks, not by most `src/lib/*` modules. + +## CHANGE THESE (TEMPLATE USERS) +- Email templates + from/subject values (see `email_docs.md`). +- `build_index.ts` exclude list if you add pages you don’t want searchable. + +## GOTCHAS +- Email is intentionally “optional”: if env vars are unset, sending is a no-op. +- In dev, `/search/api.json` can build the index on-demand; in prod it expects the prebuilt artifact. +- Search indexing only covers prerendered pages under `.svelte-kit/output/prerendered/pages`. + +## ANTI-PATTERNS +- Don’t add heavyweight per-request work into `build_index.ts` pathways; it’s designed for build-time caching. +- Don’t enable email in production without customizing templates/subjects first (welcome email is included by default). diff --git a/src/routes/(admin)/account/AGENTS.md b/src/routes/(admin)/account/AGENTS.md new file mode 100644 index 00000000..df33ad99 --- /dev/null +++ b/src/routes/(admin)/account/AGENTS.md @@ -0,0 +1,37 @@ +# src/routes/(admin)/account/ — AUTHENTICATED DASHBOARD + +## OVERVIEW +Authenticated account area: onboarding (create profile), settings, billing/subscriptions. + +## STRUCTURE (HIGH SIGNAL) +```text +src/routes/(admin)/account/ +├── +layout.server.ts # provides session + cookies from hooks +├── +layout.ts # creates Supabase client + enforces redirects +├── +layout.svelte # auth state change → invalidate("supabase:auth") +├── api/+page.server.ts # server actions: profile/email/password/delete +├── create_profile/+page.svelte # onboarding form (writes to profiles) +├── select_plan/+page.svelte # uses PricingModule (demo) +├── (menu)/ # sidebar + dashboard + billing + settings pages +├── subscribe/[slug]/+page.server.ts # Stripe Checkout session creation +└── subscription_helpers.server.ts # Stripe customer + subscription lookup +``` + +## WHERE TO LOOK +- **Auth enforcement**: `src/hooks.server.ts` (sets `locals.session/user` via `safeGetSession`) +- **Client/server Supabase wiring**: `+layout.ts` + `+layout.server.ts` +- **Replace the dashboard**: `(menu)/+page.svelte` (explicit placeholder) +- **Settings UI plumbing**: `(menu)/settings/settings_module.svelte` +- **Billing portal redirect**: `(menu)/billing/manage/+page.server.ts` + +## CHANGE THESE (TEMPLATE USERS) +- Replace `(menu)/+page.svelte` with your actual product UI. +- Extend `profiles` fields: + - DB: add migration(s) + - UI: `create_profile/+page.svelte`, `(menu)/settings/edit_profile/+page.svelte` + - Actions: `api/+page.server.ts` +- If your app needs teams/orgs, introduce a new table + RLS policies and adjust queries accordingly. + +## GOTCHAS / SECURITY +- `api/+page.server.ts` uses service role for admin operations (e.g. delete user). Keep it server-only. +- Stripe logic here is synchronous API calls; there is **no webhook endpoint** to reconcile async subscription events. diff --git a/src/routes/(marketing)/AGENTS.md b/src/routes/(marketing)/AGENTS.md new file mode 100644 index 00000000..ac2f6d98 --- /dev/null +++ b/src/routes/(marketing)/AGENTS.md @@ -0,0 +1,36 @@ +# src/routes/(marketing)/ — MARKETING + BLOG + LOGIN + +## OVERVIEW +SEO-first public pages: homepage, pricing, blog (with RSS), sitemap, search, contact form, and login UI. + +## WHERE TO LOOK +- **Header/footer + nav links**: `+layout.svelte` +- **Homepage**: `+page.svelte` (`prerender = true` in `+page.ts`) +- **Pricing**: `pricing/+page.svelte`, `pricing/pricing_plans.ts`, `pricing/pricing_module.svelte` +- **Blog index + config**: `blog/+page.svelte`, `blog/posts.ts` +- **Blog post SEO wrapper**: `blog/(posts)/+layout.svelte` (OG/Twitter tags) +- **RSS**: `blog/rss.xml/+server.ts` +- **Sitemap**: `sitemap.xml/+server.ts` (excludes admin group) +- **Search**: `search/+page.svelte`, `search/api.json/+server.ts` +- **Login flow**: `login/**`, `login/login_config.ts`, `auth/callback/+server.js` +- **Contact form**: `contact_us/+page.server.ts` (writes to DB + optional admin email) + +## CHANGE THESE (MOST COMMON) +1. **Brand links + footer content** + - `+layout.svelte` currently links to upstream GitHub and includes a sponsor block. + +2. **SEO + copy** + - `+page.svelte` (home), `pricing/+page.svelte`, `blog/posts.ts` (blog title/description). + +3. **Pricing tiers** + - `pricing/pricing_plans.ts`: set plan IDs and Stripe IDs. + - `pricing/+page.svelte`: FAQ + feature table are demo placeholders. + +4. **OAuth providers** + - `login/login_config.ts`: `oauthProviders` list. + +## GOTCHAS +- Blog posts are **Svelte pages**, not Markdown. Add a post by: + 1) adding an entry to `blog/posts.ts`, and + 2) creating the matching `blog/(posts)/<slug>/+page.svelte`. +- Search depends on pre-rendered HTML output; missing pages → rebuild (`npm run build`). diff --git a/src/routes/AGENTS.md b/src/routes/AGENTS.md new file mode 100644 index 00000000..c64155f3 --- /dev/null +++ b/src/routes/AGENTS.md @@ -0,0 +1,34 @@ +# src/routes/ — ROUTING MAP + +## OVERVIEW +SvelteKit filesystem routing. Route groups in parentheses (e.g. `(marketing)`) are organizational; they do **not** appear in the URL. + +## STRUCTURE +```text +src/routes/ +├── +layout.svelte # app-wide CSS + top navigation progress bar +├── +error.svelte # error boundary UI +├── (marketing)/ # public pages (mostly prerendered) +└── (admin)/account/ # authenticated dashboard +``` + +## WHERE TO LOOK +- **Public pages**: `src/routes/(marketing)/...` +- **Authenticated area**: `src/routes/(admin)/account/...` +- **Endpoints**: `**/+server.{ts,js}` (RSS, sitemap, auth callback, search API) + +Key endpoints in this repo: +- `src/routes/(marketing)/sitemap.xml/+server.ts` +- `src/routes/(marketing)/blog/rss.xml/+server.ts` +- `src/routes/(marketing)/search/api.json/+server.ts` +- `src/routes/(marketing)/auth/callback/+server.js` + +## CONVENTIONS +- `+page.server.ts` / `+server.ts` are server-only; safe for private env vars. +- Several marketing routes set `export const prerender = true` for static generation. + +When adding server-only features to a prerendered route, you may need to set `prerender = false` (otherwise build/prerender can break). + +## CHANGE THESE (TEMPLATE USERS) +- Decide whether your product needs separate route groups (e.g. `(app)`, `(docs)`). +- Keep marketing routes prerendered unless you truly need per-request server data. diff --git a/supabase/AGENTS.md b/supabase/AGENTS.md new file mode 100644 index 00000000..2ecea302 --- /dev/null +++ b/supabase/AGENTS.md @@ -0,0 +1,43 @@ +# supabase/ — DATABASE + RLS + +## OVERVIEW +Schema, policies, and migrations for the Supabase Postgres database. + +## WHAT EXISTS +Files: +- `database_migration.sql`: one-shot schema for brand-new Supabase projects. +- `migrations/20240730010101_initial.sql`: initial schema + RLS + trigger + storage bucket. +- `migrations/20240731051052_add_unsubscribed_to_profiles.sql`: example incremental migration. + +Core tables (public schema): +- `profiles` (user profile fields + `unsubscribed` flag) +- `stripe_customers` (maps app user → Stripe customer id) +- `contact_requests` (contact form submissions) + +Security: +- RLS enabled on all included tables. +- `profiles` policies restrict select/insert/update to `auth.uid() = id`. +- `avatars` storage bucket with public read + open upload policy (adjust if you need stricter rules). + +Trigger: +- `on_auth_user_created` inserts a row into `profiles` on new auth user. + +## WHERE APP CODE TOUCHES THE DB +- User-scoped (RLS enforced): queries via `event.locals.supabase` from `src/hooks.server.ts`. +- Privileged (bypasses RLS): `event.locals.supabaseServiceRole` is used for: + - `contact_requests` inserts (`src/routes/(marketing)/contact_us/+page.server.ts`) + - Stripe customer mapping (`src/routes/(admin)/account/subscription_helpers.server.ts`) + - admin user deletion (`src/routes/(admin)/account/api/+page.server.ts`) + +## CHANGE THESE (TEMPLATE USERS) +Typical extensions: +- Add columns to `profiles` (preferences, roles, onboarding state) +- Add domain tables for your product (projects, documents, teams, etc.) + +Safe workflow: +1. **New project**: run `database_migration.sql` once. +2. **After that**: create new incremental SQL files under `supabase/migrations/` and apply them in order. +3. Always: + - enable RLS for new tables, + - add policies, + - avoid relying on the service role for normal user flows.