@riligar/agents-kit 1.15.0 → 1.17.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 (26) hide show
  1. package/.agent/skills/riligar-design-system/SKILL.md +1 -0
  2. package/.agent/skills/riligar-design-system/references/design-system.md +0 -7
  3. package/.agent/skills/riligar-dev-auth-elysia/SKILL.md +2 -2
  4. package/.agent/skills/riligar-dev-dashboard/SKILL.md +331 -1
  5. package/.agent/skills/riligar-dev-dashboard/references/dependencies.md +44 -0
  6. package/.agent/skills/riligar-dev-database/SKILL.md +45 -0
  7. package/.agent/skills/riligar-dev-database/references/connection.md +74 -0
  8. package/.agent/skills/riligar-dev-database/references/migrations.md +70 -0
  9. package/.agent/skills/riligar-dev-database/references/queries.md +173 -0
  10. package/.agent/skills/riligar-dev-database/references/schema.md +106 -0
  11. package/.agent/skills/riligar-dev-manager/SKILL.md +13 -8
  12. package/.agent/skills/riligar-dev-website/SKILL.md +1 -1
  13. package/.agent/skills/riligar-dev-website-seo/SKILL.md +1 -1
  14. package/.agent/skills/riligar-infra-cloudfare/SKILL.md +350 -0
  15. package/.agent/skills/riligar-infra-cloudfare/references/cloudflare-api.md +139 -0
  16. package/.agent/skills/riligar-infra-cloudfare/references/email-routing.md +262 -0
  17. package/.agent/skills/riligar-infra-cloudfare/references/r2-storage.md +333 -0
  18. package/.agent/skills/riligar-infra-fly/SKILL.md +38 -1
  19. package/.agent/skills/riligar-infra-stripe/SKILL.md +2 -3
  20. package/.agent/skills/riligar-marketing-email/SKILL.md +0 -6
  21. package/.agent/skills/riligar-marketing-seo/SKILL.md +1 -7
  22. package/package.json +1 -1
  23. package/.agent/skills/riligar-dev-stack/SKILL.md +0 -110
  24. package/.agent/skills/riligar-dev-stack/references/tech-stack.md +0 -131
  25. package/.agent/skills/riligar-infra-cloudfare/.gitkeep +0 -0
  26. /package/.agent/skills/riligar-dev-auth-elysia/assets/{server-snippets.ts → server-snippets.js} +0 -0
@@ -23,6 +23,7 @@ Para atingir a excelência:
23
23
  - **[Theme Config](assets/theme.js)**: Configuração do tema com suporte a Dark Mode automático.
24
24
  - **[Visual references](references/visual-references.md)**: Mapeamento de nuances estéticas.
25
25
  - **[Anti-patterns](references/anti-patterns.md)**: O que evitar (especialmente CSS inline).
26
+ - **[Design Guidelines](references/design-system.md)**: Tipografia, cores e hierarquia visual detalhada.
26
27
 
27
28
  ## Checklist de Lapidação (RiLiGar Excellence)
28
29
 
@@ -104,10 +104,3 @@ Ao pedir código ou design para uma IA, use este formato:
104
104
  - **Modais:** Devem ter um backdrop com blur (`backdrop-blur-sm`) e fundo branco sólido. Sem cabeçalhos coloridos.
105
105
  - **Dashboards:** Devem privilegiar números grandes (Big Numbers) em monocromia, com rótulos pequenos abaixo. Gráficos devem ser limpos, sem grid lines pesadas.
106
106
 
107
- ---
108
-
109
- ## 🤖 Integração com Gemini CLI
110
-
111
- Este repositório possui uma **Skill do Gemini CLI** configurada em [`riligar-design-system/SKILL.md`](../SKILL.md). Ao usar o Gemini CLI neste repositório, o agente aplicará automaticamente estas diretrizes a qualquer código ou design gerado.
112
-
113
- Para saber como usar, veja o [README principal](../../README.md).
@@ -14,7 +14,7 @@ This skill provides a backend workflow for integrating authentication and permis
14
14
  Install the backend SDK:
15
15
 
16
16
  ```bash
17
- npm install @riligar/auth-elysia
17
+ bun add @riligar/auth-elysia
18
18
  ```
19
19
 
20
20
  ### 2. Environment Variables
@@ -55,7 +55,7 @@ const app = new Elysia()
55
55
  .listen(3000)
56
56
  ```
57
57
 
58
- For more patterns, see [server-snippets.ts](assets/server-snippets.ts).
58
+ For more patterns, see [server-snippets.js](assets/server-snippets.js).
59
59
 
60
60
  ## Specialized Guides
61
61
 
@@ -15,6 +15,7 @@ description: Padrões React específicos do RiLiGar. Zustand, i18n, estrutura de
15
15
  > Sempre respeite também:
16
16
  > - @[.agent/skills/riligar-design-system] — UI exclusivo via Mantine, zero CSS
17
17
  > - Rules em `.agent/rules/` — clean-code, naming-conventions, code-style, javascript-only
18
+ > - [references/dependencies.md](references/dependencies.md) — Pacotes e versões do frontend, config Vite
18
19
 
19
20
  ---
20
21
 
@@ -246,7 +247,336 @@ export const WHATSAPP_SUPPORT_MESSAGE = '...' // ou via i18n se traduzível
246
247
 
247
248
  ---
248
249
 
250
+ ## 8. Padrões reutilizáveis
251
+
252
+ Estruturas que repetem pela codebase. Copie o esqueleto, ajuste apenas o conteúdo domínio-específico.
253
+
254
+ ### 8.1 Page Header
255
+
256
+ Presente em **todas** as pages. Estrutura idêntica sempre:
257
+
258
+ ```javascript
259
+ <Box py="xl">
260
+ <Group justify="space-between" align="flex-end" mb="xl">
261
+ <Stack gap={0}>
262
+ <Text size="xs" fw={700} c="dimmed" tt="uppercase" lts="0.1em">
263
+ {t('namespace.subtitle')}
264
+ </Text>
265
+ <Title order={1} style={{ letterSpacing: '-0.04em' }}>
266
+ {t('namespace.title')}
267
+ </Title>
268
+ </Stack>
269
+ {/* CTA opcional — ex: <Button leftSection={<IconPlus size={16} />}> */}
270
+ </Group>
271
+ {/* Conteúdo da página */}
272
+ </Box>
273
+ ```
274
+
275
+ ### 8.2 Empty State
276
+
277
+ Usado quando uma lista está vazia. Card com borda dashed, icone grande, texto e CTA:
278
+
279
+ ```javascript
280
+ <Card padding="xl" radius="md" withBorder style={{ borderStyle: 'dashed', textAlign: 'center' }}>
281
+ <Stack align="center" py="xl">
282
+ <IconDominio size={48} stroke={1} color="var(--mantine-color-gray-2)" />
283
+ <Text c="dimmed" size="sm">{t('namespace.emptyMessage')}</Text>
284
+ <Button onClick={handleCreate} leftSection={<IconPlus size={16} />}>
285
+ {t('namespace.createFirst')}
286
+ </Button>
287
+ </Stack>
288
+ </Card>
289
+ ```
290
+
291
+ ### 8.3 Loading Guard
292
+
293
+ Loader só aparece quando não há dados ainda (não sobrescreve lista existente):
294
+
295
+ ```javascript
296
+ {loading && data.length === 0 ? (
297
+ <Center style={{ height: 300 }}>
298
+ <Loader />
299
+ </Center>
300
+ ) : (
301
+ /* conteúdo normal */
302
+ )}
303
+ ```
304
+
305
+ ### 8.4 Card Grid
306
+
307
+ Layout responsivo padrão para listas de cards:
308
+
309
+ ```javascript
310
+ <SimpleGrid cols={{ base: 1, sm: 2, md: 3 }}>
311
+ {items.map((item) => (
312
+ <Card key={item.id} padding="lg" radius="md" withBorder>
313
+ {/* conteúdo do card */}
314
+ </Card>
315
+ ))}
316
+ </SimpleGrid>
317
+ ```
318
+
319
+ Para galerias (mais itens pequenos): `cols={{ base: 1, sm: 2, md: 3, lg: 4 }}`
320
+
321
+ ### 8.5 Modal
322
+
323
+ Sempre usa `useDisclosure`. Header com borderBottom específico:
324
+
325
+ ```javascript
326
+ const [opened, { open, close }] = useDisclosure(false)
327
+
328
+ <Modal
329
+ opened={opened}
330
+ onClose={close}
331
+ centered
332
+ radius="md"
333
+ padding="xl"
334
+ title={<Text fw={700}>{t('namespace.modalTitle')}</Text>}
335
+ styles={{ header: { borderBottom: '1px solid var(--mantine-color-gray-2)', marginBottom: 20 } }}
336
+ >
337
+ {/* corpo do modal */}
338
+ </Modal>
339
+ ```
340
+
341
+ Para múltiplos modais na mesma page, renomeia as funções: `{ open: openEdit, close: closeEdit }`
342
+
343
+ ### 8.6 Status Badge
344
+
345
+ Mapeia status → configuração visual via função que recebe `t`:
346
+
347
+ ```javascript
348
+ const getStatusConfig = (t) => ({
349
+ draft: { color: 'gray', icon: <IconCircleDotted size={16} />, label: t('posts.status.draft') },
350
+ scheduled: { color: 'blue', icon: <IconClock size={16} />, label: t('posts.status.scheduled') },
351
+ published: { color: 'green', icon: <IconCircleCheck size={16} />, label: t('posts.status.published') },
352
+ failed: { color: 'red', icon: <IconCircleX size={16} />, label: t('posts.status.failed') },
353
+ })
354
+
355
+ // Uso
356
+ const config = getStatusConfig(t)[status]
357
+ <Badge variant="dot" color={config.color}>{config.label}</Badge>
358
+ ```
359
+
360
+ ### 8.7 Search / Filter
361
+
362
+ Filtro local sem chamada de API — estado local + filter inline:
363
+
364
+ ```javascript
365
+ const [search, setSearch] = useState('')
366
+
367
+ const filtered = items.filter((item) =>
368
+ item.name.toLowerCase().includes(search.toLowerCase())
369
+ )
370
+
371
+ <TextInput
372
+ placeholder={t('common.search')}
373
+ leftSection={<IconSearch size={16} />}
374
+ value={search}
375
+ onChange={(e) => setSearch(e.target.value)}
376
+ />
377
+ ```
378
+
379
+ ---
380
+
381
+ ## 9. Padrões de dados e lógica
382
+
383
+ ### 9.1 Store — async action
384
+
385
+ Template exato que todas as actions seguem. Imports do service como namespace:
386
+
387
+ ```javascript
388
+ import { create } from 'zustand'
389
+ import * as feedsService from '../services/feeds.js'
390
+
391
+ export const useFeedStore = create((set) => ({
392
+ feeds: [],
393
+ loading: false,
394
+ error: null,
395
+
396
+ fetchFeeds: async () => {
397
+ set({ loading: true, error: null })
398
+ try {
399
+ const feeds = await feedsService.getAll()
400
+ set({ feeds, loading: false })
401
+ } catch (error) {
402
+ set({ error: error.message, loading: false })
403
+ throw error
404
+ }
405
+ },
406
+ }))
407
+ ```
408
+
409
+ Nota: services são importados como `import * as service` (namespace), não como objeto exportado.
410
+
411
+ ### 9.2 Store — mutação de listas
412
+
413
+ Atualizações imutáveis via `set(state => ...)` com spread + map/filter:
414
+
415
+ ```javascript
416
+ // Atualizar item na lista
417
+ updateFeed: (id, data) => set((state) => ({
418
+ feeds: state.feeds.map((f) => (f.id === id ? { ...f, ...data } : f))
419
+ })),
420
+
421
+ // Remover item
422
+ removeFeed: (id) => set((state) => ({
423
+ feeds: state.feeds.filter((f) => f.id !== id)
424
+ })),
425
+
426
+ // Adicionar item
427
+ addFeed: (feed) => set((state) => ({
428
+ feeds: [...state.feeds, feed]
429
+ })),
430
+ ```
431
+
432
+ ### 9.3 Notifications
433
+
434
+ Shape e convenção de cores consistente em toda a app:
435
+
436
+ ```javascript
437
+ import { notifications } from '@mantine/notifications'
438
+ import { IconCheck, IconX, IconAlertCircle } from '@tabler/icons-react'
439
+
440
+ // ✅ Sucesso
441
+ notifications.show({
442
+ title: t('common.success'),
443
+ message: t('namespace.savedMessage'),
444
+ color: 'green',
445
+ icon: <IconCheck size={18} />,
446
+ })
447
+
448
+ // ✅ Erro
449
+ notifications.show({
450
+ title: t('common.error'),
451
+ message: error.message,
452
+ color: 'red',
453
+ icon: <IconX size={18} />,
454
+ })
455
+
456
+ // ✅ Warning
457
+ notifications.show({
458
+ title: t('common.warning'),
459
+ message: t('namespace.warningMessage'),
460
+ color: 'yellow',
461
+ icon: <IconAlertCircle size={18} />,
462
+ })
463
+ ```
464
+
465
+ Icones de notification: sempre `size={18}`. Mensagens de erro: usa `error.message` diretamente (já vem traduzido do backend).
466
+
467
+ ### 9.4 dayjs + i18n
468
+
469
+ Locale do dayjs sincroniza com o idioma do i18n:
470
+
471
+ ```javascript
472
+ import dayjs from 'dayjs'
473
+ import relativeTime from 'dayjs/plugin/relativeTime'
474
+ import { useTranslation } from 'react-i18next'
475
+
476
+ dayjs.extend(relativeTime)
477
+
478
+ const MyComponent = () => {
479
+ const { i18n } = useTranslation()
480
+
481
+ useEffect(() => {
482
+ dayjs.locale(i18n.language)
483
+ }, [i18n.language])
484
+
485
+ // Formatos usados no projeto:
486
+ // dayjs(date).format('DD MMM, HH:mm') — compacto com hora
487
+ // dayjs(date).format('DD/MM/YYYY [at] HH:mm') — completo
488
+ // dayjs(date).fromNow() — relativo ("há 2 dias")
489
+ }
490
+ ```
491
+
492
+ ---
493
+
494
+ ## 10. Padrões de fluxo
495
+
496
+ ### 10.1 Route Guard (wrapper)
497
+
498
+ Componente que protege rotas verificando estado do store:
499
+
500
+ ```javascript
501
+ import { Navigate } from 'react-router-dom'
502
+ import { useFeedStore } from '../store/feed-store.js'
503
+
504
+ const RequireFeed = ({ children }) => {
505
+ const activeFeed = useFeedStore((s) => s.activeFeed)
506
+ if (!activeFeed) return <Navigate to="/" />
507
+ return children
508
+ }
509
+ ```
510
+
511
+ Usado na definição de rotas: `<RequireFeed><EditorPage /></RequireFeed>`
512
+
513
+ ### 10.2 Notificação via URL params
514
+
515
+ Após redirects externos (OAuth, Stripe checkout), status vem via query params:
516
+
517
+ ```javascript
518
+ import { useSearchParams } from 'react-router-dom'
519
+
520
+ const SubscriptionPage = () => {
521
+ const [searchParams, setSearchParams] = useSearchParams()
522
+ const { t } = useTranslation()
523
+
524
+ useEffect(() => {
525
+ if (searchParams.get('success')) {
526
+ notifications.show({ title: t('common.success'), message: t('subscription.successMessage'), color: 'green', icon: <IconCheck size={18} /> })
527
+ setSearchParams({})
528
+ } else if (searchParams.get('canceled')) {
529
+ notifications.show({ title: t('common.warning'), message: t('subscription.canceledMessage'), color: 'yellow', icon: <IconAlertCircle size={18} /> })
530
+ setSearchParams({})
531
+ }
532
+ }, [searchParams])
533
+ }
534
+ ```
535
+
536
+ ### 10.3 Autosave
537
+
538
+ Padrão usado no editor — debounce com state machine de status:
539
+
540
+ ```javascript
541
+ const [saveStatus, setSaveStatus] = useState('idle') // 'idle' | 'saving' | 'saved'
542
+
543
+ useEffect(() => {
544
+ if (!content) return
545
+ const timeout = setTimeout(async () => {
546
+ setSaveStatus('saving')
547
+ try {
548
+ await postsService.update(postId, { content })
549
+ setSaveStatus('saved')
550
+ // Reset para idle após 3s
551
+ setTimeout(() => setSaveStatus('idle'), 3000)
552
+ } catch {
553
+ setSaveStatus('idle')
554
+ }
555
+ }, 2000) // debounce de 2s
556
+
557
+ return () => clearTimeout(timeout)
558
+ }, [content, postId])
559
+ ```
560
+
561
+ ---
562
+
563
+ ## 11. Convenção de tamanhos de icones
564
+
565
+ Hierarquia consistente — sempre de `@tabler/icons-react`:
566
+
567
+ | Contexto | Size | Exemplo |
568
+ |---|---|---|
569
+ | Menu items / nav | 14 | Sidebar links |
570
+ | Inline / badges | 16 | Botões, labels, leftSection |
571
+ | Notifications | 18 | Icons nas notifications |
572
+ | Card headers | 20 | Ação principal do card |
573
+ | Feature cards | 24 | Cards de destaque |
574
+ | Empty states | 48 | Icone do empty state (com `stroke={1}`) |
575
+
576
+ Empty states usam `stroke={1}` para parecer mais leve. Icones decorativos genéricos usam `stroke={1.5}`.
577
+
578
+ ---
579
+
249
580
  ## Related Skills
250
581
 
251
582
  - @[.agent/skills/riligar-design-system]
252
- - @[.agent/skills/riligar-dev-stack]
@@ -0,0 +1,44 @@
1
+ # Dependências Frontend
2
+
3
+ ## Core
4
+
5
+ | Pacote | Versão | Descrição |
6
+ |---|---|---|
7
+ | `react` | ^19.x | Biblioteca UI |
8
+ | `react-dom` | ^19.x | React DOM renderer |
9
+ | `react-router-dom` | ^7.x | Roteamento |
10
+ | `vite` | ^5.x | Build tool |
11
+ | `zustand` | ^5.x | Gerenciamento de estado |
12
+ | `ky` | ^1.x | HTTP client |
13
+
14
+ ## UI
15
+
16
+ | Pacote | Versão | Descrição |
17
+ |---|---|---|
18
+ | `@mantine/core` | ^8.x | Componentes UI |
19
+ | `@mantine/hooks` | ^8.x | Hooks utilitários |
20
+ | `@mantine/form` | ^8.x | Gerenciamento de formulários |
21
+ | `@mantine/notifications` | ^8.x | Sistema de notificações |
22
+ | `@tabler/icons-react` | ^3.x | Iconografia |
23
+
24
+ > Use apenas Mantine para estilização. Sem CSS Modules, Custom CSS ou CSS In-line.
25
+
26
+ ## Configuração Vite
27
+
28
+ ```javascript
29
+ import { defineConfig } from 'vite'
30
+ import react from '@vitejs/plugin-react'
31
+
32
+ export default defineConfig({
33
+ plugins: [react()],
34
+ build: {
35
+ lib: {
36
+ entry: 'src/index.js',
37
+ formats: ['es', 'cjs'],
38
+ },
39
+ rollupOptions: {
40
+ external: ['react', 'react-dom', '@mantine/core'],
41
+ },
42
+ },
43
+ })
44
+ ```
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: riligar-dev-database
3
+ description: Database patterns for RiLiGar using Drizzle ORM + bun:sqlite. Use when setting up database connections, defining schemas, creating migrations, or writing queries. Covers SQLite on Fly.io volumes with the drizzle-kit workflow.
4
+ ---
5
+
6
+ # Database — Drizzle + bun:sqlite
7
+
8
+ > SQLite nativo no Bun. Zero drivers externos. Base de dados no volume do Fly.io (`/app/data`).
9
+
10
+ ## Referências
11
+
12
+ | Arquivo | Quando usar |
13
+ | --- | --- |
14
+ | [connection.md](references/connection.md) | Setup inicial: instalação, db.js, drizzle.config |
15
+ | [schema.md](references/schema.md) | Definir tabelas, tipos de colunas, relações |
16
+ | [migrations.md](references/migrations.md) | Criar e executar migrations com drizzle-kit |
17
+ | [queries.md](references/queries.md) | Select, insert, update, delete, queries com relações |
18
+
19
+ ## Quick Start
20
+
21
+ ```javascript
22
+ // database/db.js
23
+ import { drizzle } from 'drizzle-orm/bun-sqlite'
24
+ import Database from 'bun:sqlite'
25
+
26
+ const sqlite = new Database(process.env.DB_PATH ?? './data/database.db')
27
+ const db = drizzle({ client: sqlite })
28
+
29
+ export { db }
30
+ ```
31
+
32
+ ## Regras
33
+
34
+ - **Caminho do banco:** `/app/data/database.db` em produção (volume Fly.io). `./data/database.db` em desenvolvimento.
35
+ - **Migrations sempre:** Use `drizzle-kit generate` + `drizzle-kit migrate`. Nunca edite migrations à mão.
36
+ - **Schema único:** Todas as tabelas em `database/schema.js`.
37
+ - **Migrations no startup:** Use `migrate()` no `index.js` antes de `.listen()`.
38
+
39
+ ## Related Skills
40
+
41
+ | Need | Skill |
42
+ | --- | --- |
43
+ | **Backend (Elysia)** | @[.agent/skills/riligar-dev-manager] |
44
+ | **Payments (Stripe)** | @[.agent/skills/riligar-infra-stripe] |
45
+ | **Infrastructure** | @[.agent/skills/riligar-infra-fly] |
@@ -0,0 +1,74 @@
1
+ # Database Connection
2
+
3
+ ## Installation
4
+
5
+ ```bash
6
+ bun add drizzle-orm
7
+ bun add -d drizzle-kit
8
+ ```
9
+
10
+ No additional drivers needed — `bun:sqlite` is built into Bun.
11
+
12
+ ## db.js
13
+
14
+ ```javascript
15
+ // database/db.js
16
+ import { drizzle } from 'drizzle-orm/bun-sqlite'
17
+ import Database from 'bun:sqlite'
18
+
19
+ const sqlite = new Database(process.env.DB_PATH ?? './data/database.db')
20
+ const db = drizzle({ client: sqlite })
21
+
22
+ export { db }
23
+ ```
24
+
25
+ - Em **desenvolvimento**: `DB_PATH` não é definido → usa `./data/database.db`
26
+ - Em **produção** (Fly.io): `fly secrets set DB_PATH=/app/data/database.db`
27
+
28
+ ## drizzle.config.js
29
+
30
+ ```javascript
31
+ // drizzle.config.js (na raiz do projeto)
32
+ import { defineConfig } from 'drizzle-kit'
33
+
34
+ export default defineConfig({
35
+ dialect: 'sqlite',
36
+ schema: './database/schema.js',
37
+ out: './database/migrations',
38
+ dbCredentials: {
39
+ url: process.env.DB_PATH ?? './data/database.db',
40
+ },
41
+ })
42
+ ```
43
+
44
+ ## Migrations no Startup
45
+
46
+ No `index.js`, execute migrations antes de iniciar o servidor:
47
+
48
+ ```javascript
49
+ // index.js
50
+ import { migrate } from 'drizzle-orm/bun-sqlite/migrator'
51
+ import { db } from './database/db'
52
+
53
+ await migrate(db, { migrationsFolder: './database/migrations' })
54
+
55
+ // ... resto do setup
56
+ const app = new Elysia()
57
+ .use(routes)
58
+ .listen(3000)
59
+ ```
60
+
61
+ Isso garante que o banco esteja sempre atualizado quando o servidor inicia no Fly.io.
62
+
63
+ ## Estrutura de Arquivos
64
+
65
+ ```
66
+ database/
67
+ ├── db.js # Conexão (drizzle + bun:sqlite)
68
+ ├── schema.js # Todas as tabelas e relações
69
+ └── migrations/
70
+ ├── 0000_*.sql # Migrations geradas pelo drizzle-kit
71
+ ├── 0001_*.sql
72
+ └── meta/
73
+ └── _journal.json
74
+ ```
@@ -0,0 +1,70 @@
1
+ # Migrations — drizzle-kit
2
+
3
+ ## Fluxo Padrão
4
+
5
+ ```
6
+ 1. Edite database/schema.js
7
+ 2. bun run db:generate → gera migration SQL
8
+ 3. bun run db:migrate → aplica no banco
9
+ ```
10
+
11
+ ## Scripts no package.json
12
+
13
+ ```javascript
14
+ {
15
+ "scripts": {
16
+ "db:generate": "drizzle-kit generate",
17
+ "db:migrate": "drizzle-kit migrate",
18
+ "db:push": "drizzle-kit push",
19
+ "db:studio": "drizzle-kit studio"
20
+ }
21
+ }
22
+ ```
23
+
24
+ ## Comandos
25
+
26
+ ### Gerar migration
27
+
28
+ ```bash
29
+ bun run db:generate
30
+ ```
31
+
32
+ Compara o schema atual com o último snapshot. Gera um arquivo `.sql` em `database/migrations/`. Pergunta se algum campo foi renomeado (vs criado novo + deletado).
33
+
34
+ ### Aplicar migrations
35
+
36
+ ```bash
37
+ bun run db:migrate
38
+ ```
39
+
40
+ Lê as migrations não aplicadas, executa em ordem. Mantém histórico no banco.
41
+
42
+ ### Push (apenas desenvolvimento)
43
+
44
+ ```bash
45
+ bun run db:push
46
+ ```
47
+
48
+ Aplica mudanças diretamente no banco sem gerar arquivos de migration. Útil durante prototipage rápido. **Nunca use em produção.**
49
+
50
+ ### Studio (GUI)
51
+
52
+ ```bash
53
+ bun run db:studio
54
+ ```
55
+
56
+ Abre uma interface visual para explorar e editar dados no banco.
57
+
58
+ ## Migrations Programáticas (Startup)
59
+
60
+ Em produção, as migrations são executadas automaticamente no startup da aplicação:
61
+
62
+ ```javascript
63
+ // index.js
64
+ import { migrate } from 'drizzle-orm/bun-sqlite/migrator'
65
+ import { db } from './database/db'
66
+
67
+ await migrate(db, { migrationsFolder: './database/migrations' })
68
+ ```
69
+
70
+ Isso garante que o banco esteja sempre atualizado quando o servidor inicia no Fly.io.