@luanpdd/kit-mcp 1.7.0 → 1.8.1

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 (38) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/gates/agent-no-recursive-dispatch.md +48 -0
  3. package/gates/budget-description.md +68 -0
  4. package/gates/no-personal-uuid.md +72 -0
  5. package/gates/skill-must-include.md +69 -0
  6. package/gates/sync-idempotent.md +62 -0
  7. package/kit/agents/codebase-mapper.md +1 -1
  8. package/kit/agents/executor.md +17 -0
  9. package/kit/agents/planner.md +35 -0
  10. package/kit/agents/project-researcher.md +1 -1
  11. package/kit/agents/schema-checker.md +4 -4
  12. package/kit/agents/supabase-architect.md +153 -0
  13. package/kit/agents/supabase-auth-bootstrapper.md +298 -0
  14. package/kit/agents/supabase-edge-fn-writer.md +185 -0
  15. package/kit/agents/supabase-migration-writer.md +156 -0
  16. package/kit/agents/supabase-realtime-implementer.md +252 -0
  17. package/kit/agents/supabase-rls-writer.md +218 -0
  18. package/kit/agents/supabase-storage-implementer.md +240 -0
  19. package/kit/agents/user-profiler.md +1 -1
  20. package/kit/agents/verifier.md +1 -1
  21. package/kit/commands/depurar.md +17 -0
  22. package/kit/commands/fazer.md +15 -0
  23. package/kit/commands/supabase.md +148 -0
  24. package/kit/framework/workflows/discuss-phase.md +19 -0
  25. package/kit/framework/workflows/plan-phase.md +25 -0
  26. package/kit/skills/_shared-supabase/glossary.md +180 -0
  27. package/kit/skills/supabase-auth-ssr/SKILL.md +260 -0
  28. package/kit/skills/supabase-cron-queues/SKILL.md +266 -0
  29. package/kit/skills/supabase-database-functions/SKILL.md +247 -0
  30. package/kit/skills/supabase-declarative-schema/SKILL.md +183 -0
  31. package/kit/skills/supabase-edge-functions/SKILL.md +242 -0
  32. package/kit/skills/supabase-migrations/SKILL.md +175 -0
  33. package/kit/skills/supabase-pgvector-rag/SKILL.md +253 -0
  34. package/kit/skills/supabase-postgres-style/SKILL.md +138 -0
  35. package/kit/skills/supabase-realtime/SKILL.md +236 -0
  36. package/kit/skills/supabase-rls-policies/SKILL.md +185 -0
  37. package/kit/skills/supabase-storage/SKILL.md +234 -0
  38. package/package.json +1 -1
@@ -0,0 +1,234 @@
1
+ ---
2
+ name: supabase-storage
3
+ description: Use ao integrar Storage — buckets públicos vs privados, signed URLs com expiration, RLS sobre storage.objects com multi-tenant path, image transforms, TUS uploads.
4
+ ---
5
+
6
+ # Supabase — Storage
7
+
8
+ ## Quando usar
9
+
10
+ LLM carrega esta skill quando trabalhar com upload, download, ou serve de arquivos via Supabase Storage. Trigger phrases:
11
+
12
+ - "Supabase Storage", "upload de arquivo"
13
+ - "signed URL", "createSignedUrl"
14
+ - "bucket público vs privado"
15
+ - "RLS storage.objects"
16
+ - "multi-tenant arquivos"
17
+ - "image transforms Supabase"
18
+ - "TUS resumable upload"
19
+
20
+ ## Regras absolutas
21
+
22
+ - **Bucket privado é default em produção** — apenas dados públicos (avatares públicos, marketing) vão em buckets públicos.
23
+ - **Bucket público:** URL direta `getPublicUrl()` + servida via CDN (cache).
24
+ - **Bucket privado:** apenas `signed URL` (`createSignedUrl()`) com `expiresIn` curto (60s downloads, 3600s imagens).
25
+ - **`storage.objects`** — RLS sempre habilitada. Sem RLS, qualquer authenticated lê qualquer bucket privado.
26
+ - **`multi-tenant path`** isolation — usar `auth.uid()` (ou `org_id`) como path prefix: `<user_id>/<filename>`. Validar em RLS via `(storage.foldername(name))[1] = (select auth.uid())::text`.
27
+ - **Image transformations** apenas em buckets com transformation enabled (Pro plan+). Query params `?width=800&height=600&resize=contain`.
28
+ - **Uploads grandes (> 6 MB):** use TUS resumable protocol (`uploadToSignedUrl` + chunked upload).
29
+ - **Awareness de egress billing** — bucket público sem cache headers customizados pode disparar custo significativo. Use Smart CDN + TTL adequado.
30
+ - **Não overwrite arquivos públicos** com mesmo nome — CDN cache fica stale. Use versionamento (`avatar-v2.jpg`) ou random suffix.
31
+
32
+ ## Patterns canônicos
33
+
34
+ ### RLS multi-tenant em `storage.objects`
35
+
36
+ ```sql
37
+ -- PT-BR: usuário só vê arquivos sob seu próprio prefix de path
38
+ -- path canônico: <user_id>/<filename> (ex: 550e8400-e29b-41d4-a716-446655440000/avatar.jpg)
39
+
40
+ create policy "users_read_own_files"
41
+ on storage.objects for select to authenticated
42
+ using (
43
+ bucket_id = 'private-uploads'
44
+ and (storage.foldername(name))[1] = (select auth.uid())::text
45
+ );
46
+
47
+ create policy "users_insert_own_files"
48
+ on storage.objects for insert to authenticated
49
+ with check (
50
+ bucket_id = 'private-uploads'
51
+ and (storage.foldername(name))[1] = (select auth.uid())::text
52
+ );
53
+
54
+ create policy "users_update_own_files"
55
+ on storage.objects for update to authenticated
56
+ using (
57
+ bucket_id = 'private-uploads'
58
+ and (storage.foldername(name))[1] = (select auth.uid())::text
59
+ );
60
+
61
+ create policy "users_delete_own_files"
62
+ on storage.objects for delete to authenticated
63
+ using (
64
+ bucket_id = 'private-uploads'
65
+ and (storage.foldername(name))[1] = (select auth.uid())::text
66
+ );
67
+ ```
68
+
69
+ ### Upload com path multi-tenant
70
+
71
+ ```ts
72
+ // PT-BR: cliente — path sempre prefixado com user.id
73
+ import { createClient } from '@/utils/supabase/client'
74
+
75
+ async function uploadAvatar(file: File) {
76
+ const supabase = createClient()
77
+ const { data: { user } } = await supabase.auth.getUser()
78
+ if (!user) throw new Error('not authenticated')
79
+
80
+ // PT-BR: path = <user_id>/<filename>
81
+ const path = `${user.id}/avatar.jpg`
82
+
83
+ const { data, error } = await supabase.storage
84
+ .from('private-uploads')
85
+ .upload(path, file, {
86
+ cacheControl: '3600',
87
+ upsert: true, // PT-BR: sobrescreve se mesmo path
88
+ })
89
+
90
+ if (error) throw error
91
+ return data
92
+ }
93
+ ```
94
+
95
+ ### Signed URL — download privado
96
+
97
+ ```ts
98
+ // PT-BR: signed URL com expiração 1h
99
+ const { data, error } = await supabase.storage
100
+ .from('private-uploads')
101
+ .createSignedUrl(`${userId}/avatar.jpg`, 3600)
102
+
103
+ // data.signedUrl pode ser usado em <img src={data.signedUrl}> por 1h
104
+ ```
105
+
106
+ ### Image transformations (em bucket com transform habilitado)
107
+
108
+ ```ts
109
+ // PT-BR: signed URL com transformação inline
110
+ const { data } = await supabase.storage
111
+ .from('private-uploads')
112
+ .createSignedUrl(`${userId}/avatar.jpg`, 3600, {
113
+ transform: { width: 200, height: 200, resize: 'cover' },
114
+ })
115
+ ```
116
+
117
+ ### Public bucket — getPublicUrl + cache headers
118
+
119
+ ```ts
120
+ // PT-BR: para bucket PÚBLICO apenas (não funciona em privado)
121
+ const { data } = supabase.storage
122
+ .from('public-avatars')
123
+ .getPublicUrl('hero.jpg')
124
+
125
+ // PT-BR: ao upload, set cacheControl alto para reduzir egress
126
+ await supabase.storage
127
+ .from('public-avatars')
128
+ .upload('hero.jpg', file, {
129
+ cacheControl: '31536000', // 1 ano — assets imutáveis
130
+ upsert: false, // não sobrescrever (versionar via path)
131
+ })
132
+ ```
133
+
134
+ ### TUS resumable upload (arquivos grandes)
135
+
136
+ ```ts
137
+ // PT-BR: signed upload URL + TUS chunked upload (>6MB)
138
+ import * as tus from 'npm:tus-js-client'
139
+
140
+ async function uploadLarge(file: File, path: string) {
141
+ const supabase = createClient()
142
+ const { data, error } = await supabase.storage
143
+ .from('private-uploads')
144
+ .createSignedUploadUrl(path)
145
+ if (error) throw error
146
+
147
+ return new Promise((resolve, reject) => {
148
+ const upload = new tus.Upload(file, {
149
+ endpoint: data.signedUrl,
150
+ headers: { authorization: `Bearer ${data.token}` },
151
+ chunkSize: 6 * 1024 * 1024, // 6 MB chunks
152
+ onError: reject,
153
+ onSuccess: () => resolve(upload.url),
154
+ })
155
+ upload.start()
156
+ })
157
+ }
158
+ ```
159
+
160
+ ### Notas de futuro (alpha — não detalhar em produção)
161
+
162
+ - **Vector Buckets** e **Analytics Buckets** existem em alpha (2026). Mencione apenas como existência se relevante; pattern canônico ainda mudando — não detalhar.
163
+
164
+ ## Anti-patterns
165
+
166
+ ### Anti-pattern 1: Path sem prefix de tenant
167
+
168
+ **Errado:**
169
+ ```ts
170
+ await supabase.storage.from('private-uploads').upload('avatar.jpg', file)
171
+ ```
172
+
173
+ **Por quê:** path global — qualquer user sobrescreve `avatar.jpg`. RLS multi-tenant não consegue isolar.
174
+
175
+ **Certo:**
176
+ ```ts
177
+ const path = `${user.id}/avatar.jpg`
178
+ await supabase.storage.from('private-uploads').upload(path, file)
179
+ ```
180
+
181
+ ### Anti-pattern 2: Bucket privado sem RLS em `storage.objects`
182
+
183
+ **Errado:**
184
+ ```sql
185
+ create bucket 'private-uploads';
186
+ -- (esqueceu policies em storage.objects)
187
+ ```
188
+
189
+ **Por quê:** sem RLS em `storage.objects`, qualquer `authenticated` lê arquivos do bucket — multi-tenancy quebrado.
190
+
191
+ **Certo:** ver pattern "RLS multi-tenant" acima — 4 policies separadas (SELECT/INSERT/UPDATE/DELETE).
192
+
193
+ ### Anti-pattern 3: `getPublicUrl` em bucket privado
194
+
195
+ **Errado:**
196
+ ```ts
197
+ // PT-BR: bucket-id é privado
198
+ const { data } = supabase.storage.from('private-uploads').getPublicUrl('x.jpg')
199
+ // data.publicUrl retorna mas o URL não funciona (403)
200
+ ```
201
+
202
+ **Por quê:** `getPublicUrl` só funciona em buckets marcados public. Em privado, retorna URL que sempre dá 403.
203
+
204
+ **Certo:** use `createSignedUrl` com expiration:
205
+ ```ts
206
+ const { data } = await supabase.storage
207
+ .from('private-uploads')
208
+ .createSignedUrl('x.jpg', 3600)
209
+ // data.signedUrl funciona por 1h
210
+ ```
211
+
212
+ ### Anti-pattern 4: Overwrite de arquivo público com mesmo path
213
+
214
+ **Errado:**
215
+ ```ts
216
+ // PT-BR: hero.jpg público
217
+ await supabase.storage.from('public').upload('hero.jpg', newFile, { upsert: true })
218
+ // CDN cache antigo continua servindo old hero.jpg por horas/dias
219
+ ```
220
+
221
+ **Por quê:** CDN cache pelo path. Overwrite não invalida cache; usuários veem versão antiga.
222
+
223
+ **Certo:** versionar ou random suffix:
224
+ ```ts
225
+ await supabase.storage.from('public').upload(`hero-${version}.jpg`, newFile)
226
+ // ou: hero-<sha>.jpg, hero-<timestamp>.jpg
227
+ ```
228
+
229
+ ## Ver também
230
+
231
+ - [supabase-rls-policies](../supabase-rls-policies/SKILL.md) — RLS sobre `storage.objects` + multi-tenant pattern
232
+ - [supabase-auth-ssr](../supabase-auth-ssr/SKILL.md) — usuário autenticado obtém `auth.uid()` para path prefix
233
+ - [supabase-edge-functions](../supabase-edge-functions/SKILL.md) — Edge Functions podem mediar uploads complexos
234
+ - [glossário](../_shared-supabase/glossary.md) — termos PT-BR↔EN
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luanpdd/kit-mcp",
3
- "version": "1.7.0",
3
+ "version": "1.8.1",
4
4
  "description": "Generic infrastructure to ship YOUR personal kit of agents/commands/skills as an MCP server, with cross-IDE sync (Claude Code, Cursor, Codex, Gemini, Windsurf, Antigravity, Copilot, Trae).",
5
5
  "type": "module",
6
6
  "bin": {