mcp-subagents-opencode 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +602 -0
  3. package/build/config/timeouts.d.ts +9 -0
  4. package/build/config/timeouts.d.ts.map +1 -0
  5. package/build/config/timeouts.js +18 -0
  6. package/build/config/timeouts.js.map +1 -0
  7. package/build/helpers.d.ts +6 -0
  8. package/build/helpers.d.ts.map +1 -0
  9. package/build/helpers.js +47 -0
  10. package/build/helpers.js.map +1 -0
  11. package/build/index.d.ts +3 -0
  12. package/build/index.d.ts.map +1 -0
  13. package/build/index.js +245 -0
  14. package/build/index.js.map +1 -0
  15. package/build/models.d.ts +32 -0
  16. package/build/models.d.ts.map +1 -0
  17. package/build/models.js +58 -0
  18. package/build/models.js.map +1 -0
  19. package/build/server/register-notifications.d.ts +3 -0
  20. package/build/server/register-notifications.d.ts.map +1 -0
  21. package/build/server/register-notifications.js +77 -0
  22. package/build/server/register-notifications.js.map +1 -0
  23. package/build/server/register-resources.d.ts +3 -0
  24. package/build/server/register-resources.d.ts.map +1 -0
  25. package/build/server/register-resources.js +210 -0
  26. package/build/server/register-resources.js.map +1 -0
  27. package/build/server/register-retry-execution.d.ts +2 -0
  28. package/build/server/register-retry-execution.d.ts.map +1 -0
  29. package/build/server/register-retry-execution.js +28 -0
  30. package/build/server/register-retry-execution.js.map +1 -0
  31. package/build/server/register-tasks.d.ts +3 -0
  32. package/build/server/register-tasks.d.ts.map +1 -0
  33. package/build/server/register-tasks.js +52 -0
  34. package/build/server/register-tasks.js.map +1 -0
  35. package/build/server/register-tools.d.ts +3 -0
  36. package/build/server/register-tools.d.ts.map +1 -0
  37. package/build/server/register-tools.js +32 -0
  38. package/build/server/register-tools.js.map +1 -0
  39. package/build/server/resource-helpers.d.ts +21 -0
  40. package/build/server/resource-helpers.d.ts.map +1 -0
  41. package/build/server/resource-helpers.js +84 -0
  42. package/build/server/resource-helpers.js.map +1 -0
  43. package/build/services/account-manager.d.ts +88 -0
  44. package/build/services/account-manager.d.ts.map +1 -0
  45. package/build/services/account-manager.js +239 -0
  46. package/build/services/account-manager.js.map +1 -0
  47. package/build/services/claude-code-runner.d.ts +15 -0
  48. package/build/services/claude-code-runner.d.ts.map +1 -0
  49. package/build/services/claude-code-runner.js +475 -0
  50. package/build/services/claude-code-runner.js.map +1 -0
  51. package/build/services/client-context.d.ts +31 -0
  52. package/build/services/client-context.d.ts.map +1 -0
  53. package/build/services/client-context.js +44 -0
  54. package/build/services/client-context.js.map +1 -0
  55. package/build/services/exhaustion-fallback.d.ts +27 -0
  56. package/build/services/exhaustion-fallback.d.ts.map +1 -0
  57. package/build/services/exhaustion-fallback.js +30 -0
  58. package/build/services/exhaustion-fallback.js.map +1 -0
  59. package/build/services/fallback-orchestrator.d.ts +16 -0
  60. package/build/services/fallback-orchestrator.d.ts.map +1 -0
  61. package/build/services/fallback-orchestrator.js +48 -0
  62. package/build/services/fallback-orchestrator.js.map +1 -0
  63. package/build/services/opencode-client.d.ts +40 -0
  64. package/build/services/opencode-client.d.ts.map +1 -0
  65. package/build/services/opencode-client.js +147 -0
  66. package/build/services/opencode-client.js.map +1 -0
  67. package/build/services/opencode-spawner.d.ts +56 -0
  68. package/build/services/opencode-spawner.d.ts.map +1 -0
  69. package/build/services/opencode-spawner.js +426 -0
  70. package/build/services/opencode-spawner.js.map +1 -0
  71. package/build/services/output-file.d.ts +24 -0
  72. package/build/services/output-file.d.ts.map +1 -0
  73. package/build/services/output-file.js +90 -0
  74. package/build/services/output-file.js.map +1 -0
  75. package/build/services/progress-registry.d.ts +12 -0
  76. package/build/services/progress-registry.d.ts.map +1 -0
  77. package/build/services/progress-registry.js +97 -0
  78. package/build/services/progress-registry.js.map +1 -0
  79. package/build/services/question-registry.d.ts +79 -0
  80. package/build/services/question-registry.d.ts.map +1 -0
  81. package/build/services/question-registry.js +249 -0
  82. package/build/services/question-registry.js.map +1 -0
  83. package/build/services/retry-queue.d.ts +41 -0
  84. package/build/services/retry-queue.d.ts.map +1 -0
  85. package/build/services/retry-queue.js +195 -0
  86. package/build/services/retry-queue.js.map +1 -0
  87. package/build/services/sdk-client-manager.d.ts +149 -0
  88. package/build/services/sdk-client-manager.d.ts.map +1 -0
  89. package/build/services/sdk-client-manager.js +632 -0
  90. package/build/services/sdk-client-manager.js.map +1 -0
  91. package/build/services/sdk-session-adapter.d.ts +203 -0
  92. package/build/services/sdk-session-adapter.d.ts.map +1 -0
  93. package/build/services/sdk-session-adapter.js +1088 -0
  94. package/build/services/sdk-session-adapter.js.map +1 -0
  95. package/build/services/sdk-spawner.d.ts +42 -0
  96. package/build/services/sdk-spawner.d.ts.map +1 -0
  97. package/build/services/sdk-spawner.js +488 -0
  98. package/build/services/sdk-spawner.js.map +1 -0
  99. package/build/services/session-hooks.d.ts +24 -0
  100. package/build/services/session-hooks.d.ts.map +1 -0
  101. package/build/services/session-hooks.js +130 -0
  102. package/build/services/session-hooks.js.map +1 -0
  103. package/build/services/session-snapshot.d.ts +19 -0
  104. package/build/services/session-snapshot.d.ts.map +1 -0
  105. package/build/services/session-snapshot.js +203 -0
  106. package/build/services/session-snapshot.js.map +1 -0
  107. package/build/services/subscription-registry.d.ts +12 -0
  108. package/build/services/subscription-registry.d.ts.map +1 -0
  109. package/build/services/subscription-registry.js +27 -0
  110. package/build/services/subscription-registry.js.map +1 -0
  111. package/build/services/task-manager.d.ts +150 -0
  112. package/build/services/task-manager.d.ts.map +1 -0
  113. package/build/services/task-manager.js +765 -0
  114. package/build/services/task-manager.js.map +1 -0
  115. package/build/services/task-persistence.d.ts +29 -0
  116. package/build/services/task-persistence.d.ts.map +1 -0
  117. package/build/services/task-persistence.js +159 -0
  118. package/build/services/task-persistence.js.map +1 -0
  119. package/build/services/task-status-mapper.d.ts +21 -0
  120. package/build/services/task-status-mapper.d.ts.map +1 -0
  121. package/build/services/task-status-mapper.js +171 -0
  122. package/build/services/task-status-mapper.js.map +1 -0
  123. package/build/templates/index.d.ts +22 -0
  124. package/build/templates/index.d.ts.map +1 -0
  125. package/build/templates/index.js +147 -0
  126. package/build/templates/index.js.map +1 -0
  127. package/build/templates/overlays/coder-csharp.mdx +58 -0
  128. package/build/templates/overlays/coder-go.mdx +53 -0
  129. package/build/templates/overlays/coder-java.mdx +54 -0
  130. package/build/templates/overlays/coder-kotlin.mdx +56 -0
  131. package/build/templates/overlays/coder-nextjs.mdx +65 -0
  132. package/build/templates/overlays/coder-python.mdx +53 -0
  133. package/build/templates/overlays/coder-react.mdx +55 -0
  134. package/build/templates/overlays/coder-ruby.mdx +59 -0
  135. package/build/templates/overlays/coder-rust.mdx +48 -0
  136. package/build/templates/overlays/coder-supabase.mdx +268 -0
  137. package/build/templates/overlays/coder-supastarter.mdx +313 -0
  138. package/build/templates/overlays/coder-swift.mdx +56 -0
  139. package/build/templates/overlays/coder-tauri.mdx +566 -0
  140. package/build/templates/overlays/coder-triggerdev.mdx +296 -0
  141. package/build/templates/overlays/coder-typescript.mdx +45 -0
  142. package/build/templates/overlays/coder-vue.mdx +62 -0
  143. package/build/templates/overlays/planner-architecture.mdx +78 -0
  144. package/build/templates/overlays/planner-bugfix.mdx +36 -0
  145. package/build/templates/overlays/planner-feature.mdx +38 -0
  146. package/build/templates/overlays/planner-migration.mdx +50 -0
  147. package/build/templates/overlays/planner-refactor.mdx +57 -0
  148. package/build/templates/overlays/researcher-library.mdx +59 -0
  149. package/build/templates/overlays/researcher-performance.mdx +68 -0
  150. package/build/templates/overlays/researcher-security.mdx +86 -0
  151. package/build/templates/overlays/tester-graphql.mdx +191 -0
  152. package/build/templates/overlays/tester-playwright.mdx +621 -0
  153. package/build/templates/overlays/tester-rest.mdx +101 -0
  154. package/build/templates/overlays/tester-suite.mdx +177 -0
  155. package/build/templates/super-coder.mdx +529 -0
  156. package/build/templates/super-planner.mdx +568 -0
  157. package/build/templates/super-researcher.mdx +406 -0
  158. package/build/templates/super-tester.mdx +243 -0
  159. package/build/tools/answer-question.d.ts +30 -0
  160. package/build/tools/answer-question.d.ts.map +1 -0
  161. package/build/tools/answer-question.js +108 -0
  162. package/build/tools/answer-question.js.map +1 -0
  163. package/build/tools/cancel-task.d.ts +44 -0
  164. package/build/tools/cancel-task.d.ts.map +1 -0
  165. package/build/tools/cancel-task.js +144 -0
  166. package/build/tools/cancel-task.js.map +1 -0
  167. package/build/tools/send-message.d.ts +39 -0
  168. package/build/tools/send-message.d.ts.map +1 -0
  169. package/build/tools/send-message.js +124 -0
  170. package/build/tools/send-message.js.map +1 -0
  171. package/build/tools/shared-spawn.d.ts +56 -0
  172. package/build/tools/shared-spawn.d.ts.map +1 -0
  173. package/build/tools/shared-spawn.js +114 -0
  174. package/build/tools/shared-spawn.js.map +1 -0
  175. package/build/tools/spawn-agent.d.ts +85 -0
  176. package/build/tools/spawn-agent.d.ts.map +1 -0
  177. package/build/tools/spawn-agent.js +133 -0
  178. package/build/tools/spawn-agent.js.map +1 -0
  179. package/build/tools/spawn-coder.d.ts +70 -0
  180. package/build/tools/spawn-coder.d.ts.map +1 -0
  181. package/build/tools/spawn-coder.js +71 -0
  182. package/build/tools/spawn-coder.js.map +1 -0
  183. package/build/tools/spawn-planner.d.ts +70 -0
  184. package/build/tools/spawn-planner.d.ts.map +1 -0
  185. package/build/tools/spawn-planner.js +71 -0
  186. package/build/tools/spawn-planner.js.map +1 -0
  187. package/build/tools/spawn-researcher.d.ts +70 -0
  188. package/build/tools/spawn-researcher.d.ts.map +1 -0
  189. package/build/tools/spawn-researcher.js +70 -0
  190. package/build/tools/spawn-researcher.js.map +1 -0
  191. package/build/tools/spawn-task.d.ts +74 -0
  192. package/build/tools/spawn-task.d.ts.map +1 -0
  193. package/build/tools/spawn-task.js +107 -0
  194. package/build/tools/spawn-task.js.map +1 -0
  195. package/build/tools/spawn-tester.d.ts +70 -0
  196. package/build/tools/spawn-tester.d.ts.map +1 -0
  197. package/build/tools/spawn-tester.js +69 -0
  198. package/build/tools/spawn-tester.js.map +1 -0
  199. package/build/types.d.ts +101 -0
  200. package/build/types.d.ts.map +1 -0
  201. package/build/types.js +28 -0
  202. package/build/types.js.map +1 -0
  203. package/build/utils/brief-validator.d.ts +30 -0
  204. package/build/utils/brief-validator.d.ts.map +1 -0
  205. package/build/utils/brief-validator.js +254 -0
  206. package/build/utils/brief-validator.js.map +1 -0
  207. package/build/utils/format.d.ts +34 -0
  208. package/build/utils/format.d.ts.map +1 -0
  209. package/build/utils/format.js +55 -0
  210. package/build/utils/format.js.map +1 -0
  211. package/build/utils/sanitize.d.ts +240 -0
  212. package/build/utils/sanitize.d.ts.map +1 -0
  213. package/build/utils/sanitize.js +89 -0
  214. package/build/utils/sanitize.js.map +1 -0
  215. package/build/utils/task-id-generator.d.ts +10 -0
  216. package/build/utils/task-id-generator.d.ts.map +1 -0
  217. package/build/utils/task-id-generator.js +22 -0
  218. package/build/utils/task-id-generator.js.map +1 -0
  219. package/package.json +62 -0
@@ -0,0 +1,268 @@
1
+ ## SUPABASE-SPECIFIC GUIDELINES
2
+
3
+ You are working in a codebase that uses **Supabase**. Apply these principles with zero exceptions.
4
+
5
+ ### Client Initialization — Choose the Right Client
6
+
7
+ - **Browser / Client Components:** Use `createBrowserClient` from `@supabase/ssr` — NOT `createClient` from `@supabase/supabase-js`
8
+ - **Server Components / Server Actions / Route Handlers:** Use `createServerClient` from `@supabase/ssr` with cookie methods (`getAll`, `setAll`)
9
+ - **Edge Functions / server-only scripts:** Use `createClient` from `@supabase/supabase-js` — this is the ONLY place it belongs in an SSR app
10
+ - **Service role client:** Create with `createServerClient` using the service role key and empty cookie methods (`getAll: () => []`). NEVER use in client code
11
+ - **NEVER create a singleton server client** — server clients are request-scoped (cookies are per-request). Use a factory function like `createServerSupabaseClient()` called per-request
12
+ - **ALWAYS pass `<Database>` generic** to every client: `createClient<Database>(url, key)`
13
+ - Project structure: `lib/supabase/client.ts` (browser), `lib/supabase/server.ts` (server), `lib/supabase/middleware.ts` (session refresh)
14
+
15
+ ### Middleware — NON-NEGOTIABLE for SSR Auth
16
+
17
+ - **ALWAYS implement middleware** that calls `updateSession()` on every request — without it, sessions silently expire, causing random logouts
18
+ - Use `matcher` config to exclude static assets: `matcher: ['/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)']`
19
+ - The middleware refreshes the JWT and sets updated cookies — if you skip this, auth WILL break
20
+ - Wrap `setAll` in try/catch in Server Components (cookies are read-only there)
21
+
22
+ ### TypeScript Types — Generate and Use Them
23
+
24
+ - **ALWAYS generate types:** `npx supabase gen types typescript --project-id "$PROJECT_ID" > src/database.types.ts`
25
+ - For local dev: `npx supabase gen types typescript --local > src/database.types.ts`
26
+ - Add a `package.json` script: `"gen:types": "supabase gen types typescript --project-id $PROJECT_ID > src/database.types.ts"`
27
+ - **Extract row types properly:** `type User = Database['public']['Tables']['users']['Row']`
28
+ - **Insert types:** `type NewUser = Database['public']['Tables']['users']['Insert']`
29
+ - **Update types:** `type UserUpdate = Database['public']['Tables']['users']['Update']`
30
+ - Regenerate types after EVERY schema change — never hand-edit `database.types.ts`
31
+ - If a view column appears incorrectly as nullable, use `MergeDeep` from `type-fest` to override
32
+ - Known issue: field ordering in generated types can be inconsistent across runs — do NOT rely on git diff of types for CI gates
33
+
34
+ ### Row Level Security (RLS) — ALWAYS ENABLE
35
+
36
+ - **Enable RLS on EVERY table** in the `public` schema — no exceptions. Tables without RLS are fully exposed via the API
37
+ - RLS with no policies = zero access (not even authenticated users). Always create policies after enabling
38
+ - **Wrap auth functions in `(select ...)`** for performance: use `(select auth.uid()) = user_id` NOT `auth.uid() = user_id` — this caches the result per-statement instead of evaluating per-row (100x+ improvement on large tables)
39
+ - **Target specific roles:** Add `TO authenticated` on policies — don't rely on `auth.uid()` alone to exclude `anon`
40
+ - **Separate policies per operation:** Create distinct SELECT, INSERT, UPDATE, DELETE policies — never one catch-all
41
+ - **NEVER use `user_metadata` in RLS policies** — users can modify their own `user_metadata` via the API
42
+ - **Add indexes on RLS-referenced columns** — e.g., if policy checks `user_id`, add `CREATE INDEX idx_table_user_id ON table(user_id)`
43
+ - For team/tenant access patterns: `team_id IN (SELECT team_id FROM team_members WHERE user_id = (select auth.uid()))` — always put the user filter in the subquery, not the outer query
44
+ - **Use security definer functions** for complex RLS logic — but put them in a non-public schema to prevent API exposure
45
+ - Views bypass RLS by default. For Postgres 15+, use `CREATE VIEW ... WITH (security_invoker = true)`
46
+
47
+ ### Database Queries — Correct Patterns
48
+
49
+ ```typescript
50
+ // CORRECT: Select with typed response
51
+ const { data, error } = await supabase
52
+ .from('posts')
53
+ .select('id, title, author:profiles(name, avatar_url)')
54
+ .eq('published', true)
55
+ .order('created_at', { ascending: false })
56
+ .limit(20)
57
+
58
+ // CORRECT: Insert with returning data
59
+ const { data, error } = await supabase
60
+ .from('posts')
61
+ .insert({ title, body, user_id: userId })
62
+ .select()
63
+ .single()
64
+
65
+ // CORRECT: Update with filter
66
+ const { data, error } = await supabase
67
+ .from('posts')
68
+ .update({ title: newTitle })
69
+ .eq('id', postId)
70
+ .select()
71
+ .single()
72
+
73
+ // CORRECT: Null check — use .is(), NOT .eq()
74
+ const { data } = await supabase
75
+ .from('posts')
76
+ .select()
77
+ .is('deleted_at', null)
78
+ ```
79
+
80
+ - **ALWAYS handle `error`** — never destructure only `data`. Check `if (error) throw error` or handle gracefully
81
+ - **Select only needed columns** — `.select('id, name')` not `.select('*')` — reduces payload and improves performance
82
+ - Use `.single()` when expecting exactly one row, `.maybeSingle()` when zero or one
83
+ - For joins: `.select('*, comments(*)')` uses PostgREST resource embedding — requires foreign key relationships
84
+ - For `.rpc()` joins to work, Postgres functions MUST use `RETURNS SETOF <table_name>` — NOT `RETURNS TABLE(...)`
85
+ - Use `.or()` for OR conditions: `.or('status.eq.active,status.eq.pending')`
86
+ - Paginate with `.range(from, to)` — never fetch unbounded results
87
+
88
+ ### Auth — Patterns and Anti-Patterns
89
+
90
+ ```typescript
91
+ // CORRECT: Server-side user check (ALWAYS use getUser, not getSession for auth checks)
92
+ const { data: { user }, error } = await supabase.auth.getUser()
93
+ if (!user) redirect('/login')
94
+
95
+ // CORRECT: Sign up
96
+ const { data, error } = await supabase.auth.signUp({
97
+ email,
98
+ password,
99
+ options: { data: { full_name: name } } // goes to user_metadata
100
+ })
101
+
102
+ // CORRECT: Sign in with password
103
+ const { data, error } = await supabase.auth.signInWithPassword({ email, password })
104
+
105
+ // CORRECT: Magic link / OTP
106
+ const { error } = await supabase.auth.signInWithOtp({
107
+ email,
108
+ options: { shouldCreateUser: false } // prevent auto-signup
109
+ })
110
+
111
+ // CORRECT: OAuth
112
+ const { data, error } = await supabase.auth.signInWithOAuth({
113
+ provider: 'google',
114
+ options: { redirectTo: `${origin}/auth/callback` }
115
+ })
116
+
117
+ // CORRECT: Auth callback route handler (app/auth/callback/route.ts)
118
+ const { searchParams } = new URL(request.url)
119
+ const code = searchParams.get('code')
120
+ if (code) {
121
+ const supabase = await createServerSupabaseClient()
122
+ await supabase.auth.exchangeCodeForSession(code)
123
+ }
124
+ return NextResponse.redirect(new URL('/dashboard', request.url))
125
+ ```
126
+
127
+ - **Use `getUser()` for auth verification, not `getSession()`** — `getSession` reads from cookies without server validation; `getUser` makes a server call to verify the JWT
128
+ - **PKCE flow is the default** in `@supabase/ssr` — don't switch to implicit flow
129
+ - **ALWAYS implement the `/auth/callback` route** for PKCE code exchange
130
+ - Never store tokens manually — `@supabase/ssr` handles cookie storage automatically
131
+ - For magic links: edit the email template to use `{{ .TokenHash }}` with `type=magiclink` params for PKCE compatibility
132
+ - Session data from the server can be passed to client via React Context (`AuthProvider`) to avoid redundant `getUser()` calls
133
+
134
+ ### Storage — Buckets and Uploads
135
+
136
+ - **Default to private buckets** — public buckets bypass download access control entirely
137
+ - Private bucket access requires either: a valid JWT in the Authorization header, or a signed URL
138
+ - **RLS policies on `storage.objects`** control all storage operations — no policies = no uploads allowed
139
+ - **Use signed upload URLs** for client-side uploads to avoid body size limits (Next.js defaults to 1MB):
140
+
141
+ ```typescript
142
+ // Server Action: create signed upload URL
143
+ const { data, error } = await supabase.storage
144
+ .from('avatars')
145
+ .createSignedUploadUrl(`${userId}/${fileName}`)
146
+
147
+ // Client: upload to signed URL
148
+ const { error } = await supabase.storage
149
+ .from('avatars')
150
+ .uploadToSignedUrl(data.path, data.token, file)
151
+ ```
152
+
153
+ - **Scope file paths to user IDs:** `{user_id}/filename.ext` — then write RLS policies that match `(storage.foldername(name))[1] = (select auth.uid())::text`
154
+ - Signed download URLs have a configurable expiry — keep it as short as practical
155
+ - For public assets (logos, static images): use a public bucket but understand anyone with the URL can access the file
156
+ - **Upsert requires SELECT + UPDATE + INSERT policies** — not just INSERT
157
+
158
+ ### Edge Functions — Deno Runtime
159
+
160
+ ```typescript
161
+ // CORRECT: Modern Edge Function pattern
162
+ Deno.serve(async (req: Request) => {
163
+ // CORS handling
164
+ if (req.method === 'OPTIONS') {
165
+ return new Response('ok', {
166
+ headers: {
167
+ 'Access-Control-Allow-Origin': '*',
168
+ 'Access-Control-Allow-Methods': 'POST, OPTIONS',
169
+ 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
170
+ },
171
+ })
172
+ }
173
+
174
+ // Create Supabase client with user's JWT
175
+ const supabase = createClient<Database>(
176
+ Deno.env.get('SUPABASE_URL')!,
177
+ Deno.env.get('SUPABASE_ANON_KEY')!,
178
+ { global: { headers: { Authorization: req.headers.get('Authorization')! } } }
179
+ )
180
+
181
+ const { data: { user } } = await supabase.auth.getUser()
182
+ // ... function logic
183
+
184
+ return new Response(JSON.stringify({ success: true }), {
185
+ headers: { 'Content-Type': 'application/json' },
186
+ })
187
+ })
188
+ ```
189
+
190
+ - **Use `Deno.serve()`** — do NOT import `serve` from `deno.land/std`
191
+ - **Import specifiers:** Use `npm:@supabase/supabase-js` — NOT bare `@supabase/supabase-js`. Always pin versions: `npm:express@4.18.2`
192
+ - **Pre-populated env vars:** `SUPABASE_URL`, `SUPABASE_ANON_KEY`, `SUPABASE_SERVICE_ROLE_KEY`, `SUPABASE_DB_URL` are available automatically — do NOT set them manually
193
+ - **Shared code** goes in `supabase/functions/_shared/` — import via relative path. Never cross-import between functions
194
+ - **Fat functions pattern:** Group related routes into one function using Hono or Express — minimizes cold starts
195
+ - Use `EdgeRuntime.waitUntil(promise)` for background tasks that shouldn't block the response
196
+ - File writes only allowed in `/tmp`
197
+ - Design for idempotency — Edge Functions can be retried
198
+
199
+ ### Realtime — Use Wisely
200
+
201
+ ```typescript
202
+ // Postgres Changes (simple, but doesn't scale well)
203
+ const channel = supabase
204
+ .channel('posts-changes')
205
+ .on('postgres_changes',
206
+ { event: 'INSERT', schema: 'public', table: 'posts', filter: 'room_id=eq.123' },
207
+ (payload) => handleNewPost(payload.new)
208
+ )
209
+ .subscribe()
210
+
211
+ // Broadcast (preferred for most real-time use cases)
212
+ const channel = supabase.channel('room-123')
213
+ channel.on('broadcast', { event: 'cursor-move' }, ({ payload }) => {
214
+ updateCursor(payload)
215
+ })
216
+ channel.subscribe()
217
+ channel.send({ type: 'broadcast', event: 'cursor-move', payload: { x, y } })
218
+
219
+ // Presence
220
+ const channel = supabase.channel('room-123')
221
+ channel.on('presence', { event: 'sync' }, () => {
222
+ const state = channel.presenceState()
223
+ updateOnlineUsers(state)
224
+ })
225
+ channel.subscribe(async (status) => {
226
+ if (status === 'SUBSCRIBED') {
227
+ await channel.track({ user_id: userId, online_at: new Date().toISOString() })
228
+ }
229
+ })
230
+ ```
231
+
232
+ - **Prefer Broadcast over Postgres Changes** for scale — Postgres Changes checks RLS for every subscriber on every change (100 subscribers = 100 reads per change)
233
+ - **Unsubscribe on cleanup:** `supabase.removeChannel(channel)` in `useEffect` cleanup / `onUnmounted`
234
+ - Postgres Changes are processed on a single thread — they do NOT scale with compute upgrades
235
+ - Realtime does NOT guarantee delivery — design your app to handle missed messages
236
+ - Default limits: 100 channels per tenant, 200 concurrent users per channel, 100 events/second
237
+ - For high-throughput: use a dedicated "broadcast" table without RLS and filter on the client
238
+
239
+ ### API Keys — Critical Security Rules
240
+
241
+ - **Anon key / publishable key** (`NEXT_PUBLIC_SUPABASE_ANON_KEY`): Safe for client-side. RLS protects your data. This key is meant to be public
242
+ - **Service role key / secret key**: Bypasses ALL RLS. **NEVER expose in client code, NEVER prefix with `NEXT_PUBLIC_`**
243
+ - The service role key goes in server-only environment variables
244
+ - When creating a client with the service role key, the `Authorization` header defaults to that key — so ALL queries bypass RLS automatically
245
+ - **Key rotation:** Supabase is migrating from JWT-based anon/service_role keys to publishable (`sb_publishable_...`) and secret keys. Be aware that Edge Functions may need `--no-verify-jwt` with the new key format
246
+ - In Edge Functions: verify the user via `supabase.auth.getUser()` — do NOT trust the JWT blindly
247
+
248
+ ### Connection Management
249
+
250
+ - **Use Supavisor (connection pooler)** for server-side connections from ORMs (Prisma, Drizzle) — port `6543` for Transaction Mode, port `5432` for Session Mode
251
+ - **Transaction Mode** (port 6543): Shares connections, higher throughput, but NO prepared statements — incompatible with Prisma unless using `@prisma/adapter-pg`
252
+ - **Session Mode** (port 5432): Supports prepared statements, required by Prisma's default adapter
253
+ - If using the Supabase client library (`supabase-js`), you do NOT need Supavisor — it uses the REST API (PostgREST), not direct Postgres connections
254
+ - Keep pooler usage under 40% of max connections if also using REST API, Auth, Realtime, and Storage (they share the connection pool)
255
+ - Add `?pgbouncer=true` to connection strings when using Prisma with Supavisor
256
+
257
+ ### Common Anti-Patterns — NEVER Do These
258
+
259
+ - **NEVER disable RLS** "to make things work" — fix your policies instead
260
+ - **NEVER use `select('*')` in production** — select only the columns you need
261
+ - **NEVER put business logic in RLS policies** — keep RLS simple (auth checks only), put business logic in application code or Edge Functions
262
+ - **NEVER manage schema through the Dashboard UI in production** — use migrations (`supabase migration new`, SQL files in `supabase/migrations/`)
263
+ - **NEVER call `getSession()` for auth verification** on the server — it reads cookies without validating the JWT. Use `getUser()` instead
264
+ - **NEVER store the service role key in `NEXT_PUBLIC_*` env vars** or any client-accessible location
265
+ - **NEVER use `user_metadata` for authorization decisions** — users can modify their own metadata
266
+ - **NEVER create tables without thinking about RLS first** — add policies immediately after table creation
267
+ - **NEVER use bare `catch` without handling** — Supabase errors contain `code`, `message`, `details`, and `hint` fields. Log them all
268
+ - **NEVER dump everything into the `public` schema** — use custom schemas for internal tables, keeping only API-facing tables in `public`
@@ -0,0 +1,313 @@
1
+ ## SUPASTARTER CODING GUIDELINES
2
+
3
+ You are working in a **Supastarter** monorepo (Next.js App Router). Follow these conventions exactly.
4
+
5
+ ---
6
+
7
+ ### MONOREPO STRUCTURE
8
+
9
+ ```
10
+ apps/
11
+ web/ # Next.js SaaS app (App Router)
12
+ mail-preview/ # Email template previewer
13
+ docs/ # Documentation site
14
+ packages/
15
+ api/ # oRPC procedures & Hono handlers
16
+ auth/ # Better Auth config
17
+ database/ # Prisma + Drizzle schema & queries
18
+ ui/ # Shadcn UI components
19
+ mail/ # React Email templates
20
+ payments/ # Stripe/Polar/other providers
21
+ ai/ # Vercel AI SDK wrappers
22
+ i18n/ # next-intl config & translations
23
+ logs/ # Logger utilities
24
+ storage/ # S3/file storage
25
+ utils/ # Shared utilities
26
+ config/ # Workspace-level config
27
+ tooling/ # Build tooling & shared configs
28
+ ```
29
+
30
+ ---
31
+
32
+ ### IMPORTS & PATH ALIASES
33
+
34
+ Always use path aliases. Never use deep relative imports.
35
+
36
+ ```typescript
37
+ // Good
38
+ import { Button } from "@repo/ui/components/button";
39
+ import { cn } from "@repo/ui";
40
+ import { getSession } from "@saas/auth/lib/server";
41
+ import { db } from "@repo/database";
42
+
43
+ // Bad
44
+ import { Button } from "../../../packages/ui/components/button.tsx";
45
+ ```
46
+
47
+ | Alias | Maps To |
48
+ |-------|---------|
49
+ | `@/*` | `apps/web/*` |
50
+ | `@marketing/*` | `apps/web/modules/marketing/*` |
51
+ | `@saas/*` | `apps/web/modules/saas/*` |
52
+ | `@shared/*` | `apps/web/modules/shared/*` |
53
+ | `@repo/*` | `packages/*` |
54
+
55
+ ---
56
+
57
+ ### REACT SERVER COMPONENTS
58
+
59
+ Default to Server Components. Only add `"use client"` for interactivity, browser APIs, hooks (`useQuery`, `useMutation`), or forms.
60
+
61
+ ```typescript
62
+ // Server Component (default)
63
+ import { getSession } from "@saas/auth/lib/server";
64
+ import { redirect } from "next/navigation";
65
+
66
+ export default async function ProtectedPage() {
67
+ const session = await getSession();
68
+ if (!session) redirect("/auth/login");
69
+ return <div>Welcome, {session.user.name}</div>;
70
+ }
71
+
72
+ // Client Component (only when necessary)
73
+ "use client";
74
+ import { useState } from "react";
75
+ export function Counter() {
76
+ const [count, setCount] = useState(0);
77
+ return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
78
+ }
79
+ ```
80
+
81
+ Keep client components small and focused.
82
+
83
+ ---
84
+
85
+ ### MODULE ORGANIZATION
86
+
87
+ ```
88
+ apps/web/modules/
89
+ saas/
90
+ auth/ # components/, hooks/, lib/, constants/, config.ts
91
+ organizations/ # components/, hooks/, lib/
92
+ payments/
93
+ settings/
94
+ admin/
95
+ ai/
96
+ shared/ # Cross-cutting components, hooks, lib
97
+ marketing/ # Marketing-only components, lib
98
+ ```
99
+
100
+ ---
101
+
102
+ ### API LAYER (oRPC)
103
+
104
+ Procedures live at `packages/api/modules/[feature]/procedures/[action].ts`:
105
+
106
+ ```typescript
107
+ import { protectedProcedure } from "../../../orpc/procedures";
108
+ import { z } from "zod";
109
+ import { ORPCError } from "@orpc/client";
110
+
111
+ export const submitContactForm = publicProcedure
112
+ .route({ method: "POST", path: "/contact", tags: ["Contact"] })
113
+ .input(z.object({
114
+ name: z.string().min(3),
115
+ email: z.string().email(),
116
+ message: z.string().min(10),
117
+ }))
118
+ .use(localeMiddleware)
119
+ .handler(async ({ input, context }) => {
120
+ // Implementation
121
+ });
122
+ ```
123
+
124
+ **Procedure types:** `publicProcedure` (no auth), `protectedProcedure` (requires session), `adminProcedure` (requires admin role).
125
+
126
+ **Client-side data fetching:**
127
+ ```typescript
128
+ "use client";
129
+ import { useQuery } from "@tanstack/react-query";
130
+ import { orpc } from "@shared/lib/orpc-query-utils";
131
+
132
+ export function UsersList() {
133
+ const { data, isLoading } = useQuery(orpc.users.list.queryOptions());
134
+ if (isLoading) return <Skeleton />;
135
+ return <ul>{data?.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
136
+ }
137
+ ```
138
+
139
+ ---
140
+
141
+ ### DATABASE (Drizzle)
142
+
143
+ ```typescript
144
+ import { eq } from "drizzle-orm";
145
+ import { db } from "../client";
146
+
147
+ export async function getUserById(id: string) {
148
+ return await db.query.user.findFirst({
149
+ where: (user, { eq }) => eq(user.id, id),
150
+ });
151
+ }
152
+
153
+ export async function createUser(data: UserCreateInput) {
154
+ const [{ id }] = await db
155
+ .insert(user)
156
+ .values({ ...data, createdAt: new Date(), updatedAt: new Date() })
157
+ .returning({ id: user.id });
158
+ return await getUserById(id);
159
+ }
160
+ ```
161
+
162
+ Import from `@repo/database` exports — never raw Prisma imports.
163
+
164
+ ---
165
+
166
+ ### AUTH (Better Auth)
167
+
168
+ ```typescript
169
+ // Server-side (ALWAYS use getSession from @saas/auth/lib/server)
170
+ const session = await getSession();
171
+ if (!session) redirect("/auth/login");
172
+
173
+ // Client-side
174
+ "use client";
175
+ import { useSession } from "@saas/auth/hooks/use-session";
176
+ const { user, loaded } = useSession();
177
+ ```
178
+
179
+ Features: email/password, OAuth (Google, GitHub), magic link, passkey, 2FA, organization-scoped.
180
+
181
+ ---
182
+
183
+ ### UI COMPONENTS (Shadcn + CVA)
184
+
185
+ ```typescript
186
+ import { cva } from "class-variance-authority";
187
+ import { cn } from "../lib";
188
+
189
+ const buttonVariants = cva(
190
+ "flex items-center justify-center font-medium transition-colors",
191
+ {
192
+ variants: {
193
+ variant: { primary: "bg-primary text-primary-foreground", secondary: "bg-secondary" },
194
+ size: { sm: "h-6 px-3 text-xs", md: "h-9 px-4 text-sm", lg: "h-12 px-6 text-base" },
195
+ },
196
+ defaultVariants: { variant: "secondary", size: "md" },
197
+ },
198
+ );
199
+ ```
200
+
201
+ Use `cn()` for class merging. Use `<Button loading={...}>` prop for loading states.
202
+
203
+ ---
204
+
205
+ ### FORMS (React Hook Form + Zod)
206
+
207
+ ```typescript
208
+ "use client";
209
+ import { zodResolver } from "@hookform/resolvers/zod";
210
+ import { useForm } from "react-hook-form";
211
+ import { z } from "zod";
212
+ import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@repo/ui/components/form";
213
+
214
+ const formSchema = z.object({ email: z.string().email(), name: z.string().min(3) });
215
+
216
+ export function ContactForm() {
217
+ const form = useForm({ resolver: zodResolver(formSchema), defaultValues: { email: "", name: "" } });
218
+ return (
219
+ <Form {...form}>
220
+ <form onSubmit={form.handleSubmit(onSubmit)}>
221
+ <FormField control={form.control} name="email"
222
+ render={({ field }) => (
223
+ <FormItem><FormLabel>Email</FormLabel><FormControl><Input {...field} /></FormControl><FormMessage /></FormItem>
224
+ )} />
225
+ <Button type="submit" loading={form.formState.isSubmitting}>Submit</Button>
226
+ </form>
227
+ </Form>
228
+ );
229
+ }
230
+ ```
231
+
232
+ ---
233
+
234
+ ### I18N (next-intl)
235
+
236
+ ```typescript
237
+ // Server Component
238
+ import { getTranslations, setRequestLocale } from "next-intl/server";
239
+ const t = await getTranslations();
240
+ return <h1>{t("home.welcome.title")}</h1>;
241
+
242
+ // Client Component
243
+ "use client";
244
+ import { useTranslations } from "next-intl";
245
+ const t = useTranslations();
246
+ ```
247
+
248
+ ---
249
+
250
+ ### NAMING CONVENTIONS
251
+
252
+ | Type | Convention | Example |
253
+ |------|-----------|---------|
254
+ | Directories | kebab-case | `components/auth-form/` |
255
+ | Components | PascalCase.tsx | `LoginForm.tsx` |
256
+ | Functions | camelCase | `getUser()`, `handleSubmit()` |
257
+ | Booleans | is/has/can prefix | `isLoading`, `hasError` |
258
+ | Constants | SCREAMING_SNAKE | `MAX_RETRIES` |
259
+ | Types | PascalCase | `UserProps`, `AuthConfig` |
260
+
261
+ ---
262
+
263
+ ### TYPESCRIPT RULES
264
+
265
+ - Named exports only — never default exports
266
+ - `as const` assertions — never enums
267
+ - Strict typing — no `any` without justification
268
+ - Client env vars: prefix with `NEXT_PUBLIC_`
269
+ - Use `@repo/logs` logger — never `console.log` in production
270
+
271
+ ---
272
+
273
+ ### CONFIGURATION
274
+
275
+ ```typescript
276
+ // apps/web/config.ts
277
+ export const config = {
278
+ appName: "supastarter",
279
+ saas: { enabled: true, redirectAfterSignIn: "/app" },
280
+ marketing: { enabled: true },
281
+ } as const;
282
+
283
+ // Import: import { config } from "@/config";
284
+ ```
285
+
286
+ Each package has scoped config. Import via `@repo/[package]/config`.
287
+
288
+ ---
289
+
290
+ ### BUILD & TOOLING
291
+
292
+ ```bash
293
+ pnpm dev # Start dev (Turbo)
294
+ pnpm build # Build all packages
295
+ pnpm type-check # TypeScript check
296
+ pnpm lint # Biome lint
297
+ pnpm format # Biome format
298
+ pnpm e2e # Playwright E2E tests
299
+ ```
300
+
301
+ ---
302
+
303
+ ### ANTI-PATTERNS
304
+
305
+ - Deep relative imports — use path aliases
306
+ - Default exports — use named exports
307
+ - Enums — use `as const` + type extraction
308
+ - `select('*')` — select only needed columns
309
+ - Raw Prisma imports — use `@repo/database`
310
+ - `console.log` — use `@repo/logs` logger
311
+ - Hardcoded strings — use i18n translations
312
+ - Missing `NEXT_PUBLIC_` prefix — server vars won't reach the client
313
+ - Singleton server Supabase clients — server clients are request-scoped
@@ -0,0 +1,56 @@
1
+ ## SWIFT-SPECIFIC GUIDELINES
2
+
3
+ You are working in a **Swift** codebase. Apply these principles with zero exceptions.
4
+
5
+ ### Protocol-Oriented Programming
6
+ - Prefer **protocols** over class inheritance — Swift is protocol-oriented, not class-oriented
7
+ - Use protocol extensions for default implementations — avoid abstract base classes
8
+ - Use `associatedtype` for generic protocol requirements
9
+ - Compose behavior with multiple protocol conformances: `struct User: Identifiable, Codable, Hashable`
10
+ - Use `some Protocol` (opaque return types) to hide implementation details
11
+
12
+ ### Optionals — Safety First
13
+ - Use `guard let` for early exit unwrapping — keep the happy path unindented
14
+ - Use `if let` for conditional unwrapping when the `else` case continues execution
15
+ - **Never force-unwrap** (`!`) in production code — use `guard let`, `if let`, or `??`
16
+ - Use nil-coalescing `??` for default values: `let name = user?.name ?? "Unknown"`
17
+ - Use optional chaining `?.` for accessing nested optionals
18
+ - Use `compactMap` to filter out nils from collections
19
+
20
+ ### Value vs Reference Types
21
+ - Prefer **structs** (value types) over classes — structs are the default in Swift
22
+ - Use **classes** only when you need: identity (reference semantics), inheritance, deinit, or Objective-C interop
23
+ - Use `mutating` keyword on struct methods that modify `self`
24
+ - Understand copy-on-write behavior for large value types (Array, String, Dictionary have COW built in)
25
+
26
+ ### Error Handling
27
+ - Use `throws` functions with typed errors when possible (Swift 6+)
28
+ - Create error enums conforming to `Error`: `enum AuthError: Error { case invalidToken, expired }`
29
+ - Use `do/try/catch` with specific error cases — avoid `try?` unless you intentionally want to discard the error
30
+ - Use `Result<Success, Failure>` for asynchronous error handling in callback-based APIs
31
+
32
+ ### Modern Swift Patterns
33
+ - Use **async/await** for concurrency (Swift 5.5+) — prefer over completion handlers
34
+ - Use **actors** for thread-safe mutable state
35
+ - Use `@Sendable` closures and `Sendable` types for concurrency safety
36
+ - Use `Task` and `TaskGroup` for structured concurrency
37
+ - Use `@MainActor` for UI-related code
38
+
39
+ ### SwiftUI Patterns (if applicable)
40
+ - Use `@State` for view-local state, `@Binding` for parent-owned state
41
+ - Use `@ObservedObject` / `@StateObject` for reference-type view models
42
+ - Use `@Environment` for dependency injection from the environment
43
+ - Keep views small — extract subviews as separate structs
44
+ - Use `ViewModifier` for reusable view modifications
45
+
46
+ ### Naming Conventions
47
+ - `camelCase` for functions, methods, properties, variables
48
+ - `PascalCase` for types, protocols, enums
49
+ - Follow Swift API Design Guidelines: methods read as English phrases
50
+ - Factory methods: `make*` (e.g., `makeIterator()`)
51
+ - Boolean properties: `is*`, `has*`, `can*`, `should*`
52
+
53
+ ### Testing Considerations
54
+ - Use protocol-based dependency injection for testability
55
+ - Prefer value types that are `Equatable` — easy to assert in tests
56
+ - Use `XCTAssert*` or project's testing framework conventions