create-sdd-ai-stack 0.1.17

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 (72) hide show
  1. package/AGENTS.md +164 -0
  2. package/APP-STACK.md +31 -0
  3. package/APP.md +46 -0
  4. package/ARCHITECTURE.md +115 -0
  5. package/DESIGN.md +219 -0
  6. package/LICENSE +21 -0
  7. package/NEXT.md +345 -0
  8. package/NODE.md +107 -0
  9. package/REACT.md +92 -0
  10. package/README.md +260 -0
  11. package/SKILLS/check-docs/SKILL.md +24 -0
  12. package/SKILLS/check-docs/check-docs.mjs +56 -0
  13. package/SKILLS/create-feature/SKILL.md +40 -0
  14. package/SKILLS/create-feature/create-feature.mjs +128 -0
  15. package/SKILLS/create-feature.sh +112 -0
  16. package/SKILLS/install-submodule/SKILL.md +42 -0
  17. package/SKILLS/install-submodule/install-submodule.mjs +119 -0
  18. package/bin/create-sdd-ai-stack.mjs +93 -0
  19. package/docs/CHANGELOG.md +209 -0
  20. package/docs/PLANNING.md +68 -0
  21. package/docs/PRODUCT.md +76 -0
  22. package/docs/RELEASE.md +332 -0
  23. package/lib/check-links.mjs +42 -0
  24. package/lib/constants.mjs +60 -0
  25. package/lib/scaffold.mjs +253 -0
  26. package/package.json +80 -0
  27. package/specs/BACKLOG.md +24 -0
  28. package/specs/PLAN.md +57 -0
  29. package/specs/ROADMAP.md +35 -0
  30. package/specs/history/phases/phase-0-bootstrap.md +67 -0
  31. package/specs/tasks/TASK_TEMPLATE.md +46 -0
  32. package/src/cli.mjs +119 -0
  33. package/stacks/README.md +33 -0
  34. package/stacks/ai.md +52 -0
  35. package/stacks/ci.md +59 -0
  36. package/stacks/database.md +56 -0
  37. package/stacks/git.md +51 -0
  38. package/stacks/shadcn.md +52 -0
  39. package/stacks/tailwind.md +65 -0
  40. package/stacks/testing.md +74 -0
  41. package/stacks/typescript.md +58 -0
  42. package/template/next/.env.example +4 -0
  43. package/template/next/README.md +47 -0
  44. package/template/next/biome.json +36 -0
  45. package/template/next/gitignore +32 -0
  46. package/template/next/next.config.ts +12 -0
  47. package/template/next/package.json +47 -0
  48. package/template/next/playwright.config.ts +21 -0
  49. package/template/next/postcss.config.mjs +7 -0
  50. package/template/next/src/app/(app)/app/page.tsx +18 -0
  51. package/template/next/src/app/(app)/error.tsx +30 -0
  52. package/template/next/src/app/(app)/layout.tsx +25 -0
  53. package/template/next/src/app/(marketing)/page.tsx +73 -0
  54. package/template/next/src/app/globals.css +437 -0
  55. package/template/next/src/app/layout.tsx +41 -0
  56. package/template/next/src/app/not-found.tsx +11 -0
  57. package/template/next/src/features/example/actions.ts +43 -0
  58. package/template/next/src/features/example/application/create-example.usecase.ts +26 -0
  59. package/template/next/src/features/example/container.ts +16 -0
  60. package/template/next/src/features/example/domain/IExampleRepository.ts +11 -0
  61. package/template/next/src/features/example/domain/example.schema.ts +18 -0
  62. package/template/next/src/features/example/infrastructure/example.repository.ts +26 -0
  63. package/template/next/src/features/example/ui/create-example-form.tsx +56 -0
  64. package/template/next/src/proxy.ts +23 -0
  65. package/template/next/src/shared/lib/cn.ts +6 -0
  66. package/template/next/src/shared/server/auth.ts +32 -0
  67. package/template/next/src/shared/server/env.ts +14 -0
  68. package/template/next/src/shared/ui/index.ts +3 -0
  69. package/template/next/tests/e2e/routes.spec.ts +34 -0
  70. package/template/next/tests/unit/create-example.test.ts +55 -0
  71. package/template/next/tsconfig.json +37 -0
  72. package/template/next/vitest.config.ts +19 -0
package/stacks/git.md ADDED
@@ -0,0 +1,51 @@
1
+ # 🌿 GIT
2
+
3
+ ## 🚨 Regras não-negociáveis
4
+
5
+ 1. **Identidade fixa:** `Marcelino Sandroni <marcelino.sandroni@gmail.com>`. Configure uma vez:
6
+ ```bash
7
+ git config user.name "Marcelino Sandroni"
8
+ git config user.email "marcelino.sandroni@gmail.com"
9
+ ```
10
+ 2. **Conventional Commits obrigatório:**
11
+ ```
12
+ <tipo>(<escopo>): descrição curta imperativa. (Agent: <Ferramenta> - <Modelo>)
13
+ ```
14
+ Tipos: `feat`, `fix`, `refactor`, `test`, `docs`, `chore`, `perf`, `style`, `build`, `ci`.
15
+ 3. **Escopo = a área** (`feat(chat)`, `fix(auth)`, `docs(sdd)`). Sem escopo, `feat:`.
16
+ 4. **Uma task = um commit.** Não commite task de outra junto.
17
+ 5. **Gitflow simplificado:** `main` = produção, `feat/`, `fix/`, `chore/` para trabalho. PR vai para `main`.
18
+ 6. **Nunca commite:** `node_modules`, `.env`, `.env.local`, `.next`, build output, `*.log`, credencial.
19
+ 7. **Antes de commitar:** `git status`, `git diff`, `git log --oneline -10`. Stage só o que é da task.
20
+ 8. **Sem `--force`, sem `git config` hack, sem amend de commit já pushado.**
21
+
22
+ ## 🏷️ Versionamento (SemVer + Tags)
23
+
24
+ - `MAJOR.MINOR.PATCH` — `feat`→MINOR, `fix`→PATCH, breaking→MAJOR.
25
+ - Tag no merge da fase: `git tag v1.2.0` (anotada).
26
+ - Fechou fase? Arquive em `specs/history/phases/`, gere tag, um commit de arquivamento.
27
+
28
+ ## 🧭 Fluxo diário
29
+
30
+ ```bash
31
+ git checkout -b feat/billing # 1. branch
32
+ # ... codar e testar ...
33
+ git add -A # 2. stage (só da task)
34
+ git commit -m "feat(billing): cria slice de assinatura. (Agent: Cursor - Claude)"
35
+ git push -u origin feat/billing # 3. push
36
+ # 4. abrir PR contra main
37
+ ```
38
+
39
+ ## 🔁 Atualizando este core (submodule)
40
+
41
+ ```bash
42
+ git submodule update --remote --merge SDD # puxa regras mais novas
43
+ git add SDD && git commit -m "chore(sdd): atualiza core para vX.Y.Z"
44
+ ```
45
+
46
+ ## 🚫 Nunca
47
+
48
+ - Commitar com mensagem vaga ("update", "fix", ".").
49
+ - `git add -A` com lixo não relacionado na working tree.
50
+ - Commitar direto na `main` em trabalho em andamento.
51
+ - Reescrever histórico público.
@@ -0,0 +1,52 @@
1
+ # 🧩 SHADCN/UI
2
+
3
+ > **Biblioteca de UI OFICIAL do template.** Componentes vão para `src/shared/ui/`.
4
+
5
+ ## 🚨 Regras não-negociáveis
6
+
7
+ 1. **shadcn/ui é a única lib de UI.** Proibido MUI, AntD, Bootstrap, Chakra, styled-components.
8
+ 2. **Componente novo entra na pasta, não no registry.** `npx shadcn@latest add <comp>` e o arquivo nasce em `src/shared/ui/`.
9
+ 3. **Depois de adicionar, EDITE o componente local.** Ele é seu agora. Alinhe com [../DESIGN.md](../DESIGN.md) (tokens, raio, borda).
10
+ 4. **Todo componente novo precisa de:** `aria-*` nos que têm função, foco visível, e estado `disabled` real (não só visual).
11
+ 5. **Variante com `cva` (class-variance-authority).** Nunca `if/else` de className.
12
+ 6. **Ícone:** `lucide-react`. Tamanho default 16, stroke 2. Nada de SVG colado na mão.
13
+
14
+ ## 🧪 Fluxo para adicionar componente
15
+
16
+ ```bash
17
+ # 1. Instala e cria o arquivo
18
+ npx shadcn@latest add dialog
19
+
20
+ # 2. Verifique onde caiu: src/shared/ui/dialog.tsx
21
+
22
+ # 3. Edite o arquivo: troque as classes pelo DESIGN (tokens, rounded, border)
23
+ ```
24
+
25
+ ## 🎨 Sobreposição com o DESIGN
26
+
27
+ O shadcn traz um theme genérico. **O DESIGN.md manda.** Checklist ao tocar um componente:
28
+
29
+ - [ ] Cor de fundo/borda/text usando tokens do `globals.css` (`bg-surface-container`, `border-border-prominent`)
30
+ - [ ] Raio: `rounded-md` (0.375rem) em input/botão, `rounded-lg` (0.5rem) em card
31
+ - [ ] Borda hairline `1px`, **sem sombra pesada**
32
+ - [ ] Botão primário: `bg-primary text-on-primary hover:bg-primary/90` com glow lime sutil
33
+ - [ ] Foco: `focus-visible:ring-2 focus-visible:ring-primary`
34
+
35
+ ## 📂 Onde cada coisa mora
36
+
37
+ | Tipo | Caminho |
38
+ | --- | --- |
39
+ | Primitive genérica (Button, Dialog, Input) | `src/shared/ui/` |
40
+ | Componente de domínio (UserCard, InvoiceRow) | `src/features/<x>/ui/` |
41
+ | Composição (form + action + validação) | `src/features/<x>/ui/` |
42
+ | Hook de UI (useToast, useMediaQuery) | `src/shared/hooks/` |
43
+
44
+ ## 🚫 Anti-padrões
45
+
46
+ | ❌ Não faça | ✅ Faça |
47
+ | --- | --- |
48
+ | `<div onClick>` como botão | `<button type="button">` |
49
+ | Wrapper de `Dialog` com estado próprio espalhado | Estado no pai, ou `useActionState` |
50
+ | Criar componente que é 90% igual ao do shadcn | Editar o existente |
51
+ | Props customizadas com 8 booleanos | `variant` + `size` com cva |
52
+ | Acessibilidade ignorada em modal/drawer | `DialogTitle`, `DialogDescription`, foco preso |
@@ -0,0 +1,65 @@
1
+ # 🎨 TAILWIND CSS (v4)
2
+
3
+ > Stack de estilo do template. **Dark mode only.** Tokens completos em [../DESIGN.md](../DESIGN.md).
4
+
5
+ ## 🚨 Regras não-negociáveis
6
+
7
+ 1. **Nunca escreva cor/rgba literal em classe.** Use os tokens: `bg-surface`, `text-text-secondary`, `border-border-subtle`.
8
+ 2. **Nunca use `style={{}}` inline** (exceto variável de valor dinâmico do design).
9
+ 3. **Tokens vivem em `src/app/globals.css` no bloco `@theme`.** Para mudar o design, muda **lá** — nunca em componente.
10
+ 4. **Mobile-first.** Estilo base é mobile; breakpoint é o upgrade (`sm: md: lg:`).
11
+ 5. **Grid de 12 colunas, max-width 1320px, gutter 1.5rem/2rem.** Não inventa outro grid.
12
+ 6. **`<Image>` sempre com `sizes` explícito** em grid/lista (evita baixar imagem de 4k no celular).
13
+ 7. **Ordem das classes = mobile → estado → breakpoint.** Ajuda a leitura e o diff.
14
+
15
+ ## 🧱 Classes de componente (via `@layer components`)
16
+
17
+ O template define os primitivos. **Não repita a classe longa em 40 lugares** — use a variante.
18
+
19
+ ```tsx
20
+ <a className="btn-primary">Salvar</a>
21
+ <a className="btn-secondary">Cancelar</a>
22
+ <span className="chip">React 19</span> // chip inativo
23
+ <span className="chip chip-active">Ativo</span> // chip ativo (lime)
24
+ <div className="card">…</div> // card padrão
25
+ <div className="card-metric">…</div> // KPI card
26
+ <label className="field-label">E-mail</label>
27
+ <input className="field" />
28
+ ```
29
+
30
+ ## 🎯 Padrões de uso
31
+
32
+ ```tsx
33
+ // ✅ grid responsivo com 12 colunas no desktop
34
+ <div className="mx-auto w-full max-w-[1320px] px-6 lg:px-8">
35
+ <div className="grid grid-cols-4 gap-4 md:grid-cols-8 lg:grid-cols-12">
36
+ <div className="col-span-4 lg:col-span-6">…</div>
37
+ </div>
38
+ </div>
39
+
40
+ // ✅ glassmorphism (nav/status bar)
41
+ <header className="sticky top-0 z-50 border-b border-border-subtle bg-surface-raised/80 backdrop-blur-xl">
42
+
43
+ // ✅ glow lime no KPI
44
+ <div className="card-metric hover:shadow-[0_0_24px_var(--kpi-accent-glow)]">
45
+
46
+ // ❌ proibido
47
+ <div style={{ background: "#BAF336" }} />
48
+ <div className="bg-[#BAF336] text-[#0A0D12]" />
49
+ ```
50
+
51
+ ## 🚫 Proibido
52
+
53
+ | Padrão | Por quê |
54
+ | --- | --- |
55
+ | Cor literal (`#hex`, `rgb()`) em classe | Quebra o tema e o dark mode |
56
+ | `!important` | Esconde erro de especificidade |
57
+ | Classes arbitrárias sem necessidade | Cria CSS não rastreável |
58
+ | `tailwind.config.js` quando Tailwind v4 usa `@theme` no CSS | Duas fontes de verdade |
59
+ | Animações custom sem `prefers-reduced-motion` | Acessibilidade |
60
+
61
+ ## ✅ Acessibilidade (obrigatório)
62
+
63
+ - Foco **sempre visível**: use o token `focus-visible:ring-primary` no padrão de input/botão.
64
+ - Alvo de toque mínimo **44px** em mobile.
65
+ - Contraste: texto secundário (`#94A3B8`) sobre surface (`#11151C`) passa; texto muted (`#56657A`) só em detalhe decorativo.
@@ -0,0 +1,74 @@
1
+ # 🧪 TESTES
2
+
3
+ > Meta: **100% de cobertura nos fluxos críticos** (use cases, actions, mutations). UI visual tem E2E obrigatório.
4
+
5
+ ## 📐 Pirâmide do projeto
6
+
7
+ | Tipo | Ferramenta | Onde | Quantos |
8
+ | --- | --- | --- | --- |
9
+ | **Unit** | Vitest | `tests/unit` | use cases, helpers puros, validações |
10
+ | **Integração** | Vitest + DB mock | `tests/integration` | repositórios, actions, server components |
11
+ | **E2E** | Playwright | `tests/e2e` | fluxo real do usuário ponta a ponta |
12
+
13
+ ## 🚨 Regras não-negociáveis
14
+
15
+ 1. **Toda task precisa de teste.** Sem teste = task não está pronta. ([../specs/PLAN.md](../specs/PLAN.md) e Definition of Done)
16
+ 2. **Mokar dados, nunca chamar API de terceiro real** em teste unitário/integração. E2E pode usar sandbox.
17
+ 3. **Nome do teste descreve comportamento**, não implementação: `deve_negar_acesso_quando_usuario_bloqueado` > `teste2`.
18
+ 4. **Teste de UI = E2E com Playwright.** Não teste componente por snapshot (snapshot é mentira).
19
+ 5. **Prova de vida obrigatória:** pra marcar task `[x]`, cole o output verde do terminal na resposta.
20
+ 6. **E2E precisa de evidência visual:** screenshot ou trace anexado.
21
+
22
+ ## 🧩 Exemplo — unit de use case
23
+
24
+ ```ts
25
+ // tests/unit/features/billing/create-subscription.test.ts
26
+ import { describe, it, expect, vi } from "vitest";
27
+ import { CreateSubscriptionUseCase } from "@/features/billing/application/create-subscription";
28
+
29
+ describe("CreateSubscriptionUseCase", () => {
30
+ it("deve_negar_acesso_quando_usuario_bloqueado", async () => {
31
+ const repo = { create: vi.fn() };
32
+ const useCase = new CreateSubscriptionUseCase(repo as never);
33
+ await expect(
34
+ useCase.execute({ userId: "u1", planId: "p1", blocked: true }),
35
+ ).rejects.toThrow("Acesso negado");
36
+ expect(repo.create).not.toHaveBeenCalled();
37
+ });
38
+ });
39
+ ```
40
+
41
+ ## 🧩 Exemplo — E2E Playwright
42
+
43
+ ```ts
44
+ // tests/e2e/billing.spec.ts
45
+ import { test, expect } from "@playwright/test";
46
+
47
+ test("usuário assina um plano e vê o status", async ({ page }) => {
48
+ await page.goto("/billing");
49
+ await page.getByRole("button", { name: "Assinar Pro" }).click();
50
+ await expect(page.getByTestId("subscription-status")).toHaveText("Ativo");
51
+ await page.screenshot({ path: "tests/e2e/screenshots/billing-ativo.png" });
52
+ });
53
+ ```
54
+
55
+ ## ▶️ Comandos
56
+
57
+ ```bash
58
+ cd client # ou a raiz, dependendo do template
59
+ npm run test:unit # Vitest watch
60
+ npm run test:unit -- --run # uma passada
61
+ npm run test:coverage # cobertura
62
+ npm run test:e2e # Playwright
63
+ npm run test:e2e:ui # Playwright com UI
64
+ ```
65
+
66
+ ## 🚫 Anti-padrões
67
+
68
+ | ❌ | ✅ |
69
+ | --- | --- |
70
+ | Testar implementação (mock demais) | Testar comportamento observável |
71
+ | Snapshot de componente inteiro | E2E com `getByRole` |
72
+ | sleep fixo (`waitForTimeout`) | `await expect(...).toBeVisible()` (auto-wait) |
73
+ | `test.skip` silencioso | Issue registrada |
74
+ | Sem mock de terceiro (gastando $$) | MSW / fake local |
@@ -0,0 +1,58 @@
1
+ # 🔷 TYPESCRIPT
2
+
3
+ ## 🚨 Regras não-negociáveis
4
+
5
+ 1. **`strict: true` é obrigatório.** Sem exceção. `tsconfig.json` do template já vem assim.
6
+ 2. **`any` é PROIBIDO** no código de produção. Se você não dá, use `unknown` + narrowing.
7
+ 3. **Interface (`I*`) mora junto da implementação**, dentro do slice. Nada de pasta global de tipos.
8
+ 4. **`type` para união/interseção, `interface` para contrato de classe.** Consistência acima de preferência.
9
+ 5. **Funções que recebem `unknown` da fronteira validam com Zod dentro da mesma função.**
10
+ 6. **Retorno sempre explícito** em função exportada quando não for óbvio.
11
+ 7. **`as` só com justificativa em comentário.** Se precisa de `as`, o modelo está errado.
12
+
13
+ ## 🧩 Padrões
14
+
15
+ ```ts
16
+ // ✅ Contrato de domínio (dentro de features/x/domain/)
17
+ export interface IChatRepository {
18
+ save(userId: string, text: string): Promise<ChatMessage>;
19
+ }
20
+
21
+ // ✅ Validação de fronteira
22
+ import { z } from "zod";
23
+ const CreateUserSchema = z.object({
24
+ email: z.email(),
25
+ name: z.string().min(1).max(120),
26
+ });
27
+ export type CreateUserInput = z.infer<typeof CreateUserSchema>;
28
+ ```
29
+
30
+ ## 🚫 Proibido
31
+
32
+ | Padrão | Por quê | Use em vez disso |
33
+ | --- | --- | --- |
34
+ | `any` | some o type-check | `unknown` + guard |
35
+ | `enum` | complica serialização e narrow | `as const` + uniões literais |
36
+ | `!` non-null assertion | estouro em runtime | optional chaining + early return |
37
+ | `as unknown as T` | compilador não entendeu | reescreva o tipo |
38
+ | `namespace` | colisões + bundle | `module` ou escopo de pasta |
39
+ | `function` em vez de arrow | ruído | arrow function (exceto método de classe) |
40
+ | `@ts-ignore` | debt invisível | `@ts-expect-error` + comentário |
41
+
42
+ ## 📐 Configuração mínima do template
43
+
44
+ ```jsonc
45
+ // tsconfig.json
46
+ {
47
+ "compilerOptions": {
48
+ "strict": true,
49
+ "noUncheckedIndexedAccess": true, // index access seguro
50
+ "noImplicitOverride": true, // todo override explícito
51
+ "noFallthroughCasesInSwitch": true,
52
+ "verbatimModuleSyntax": true, // type-only import obrigatório
53
+ "paths": { "@/*": ["./src/*"] }
54
+ }
55
+ }
56
+ ```
57
+
58
+ > `verbatimModuleSyntax` obriga `import type { X } from ...`. Isso é de propósito: mantém o bundle limpo.
@@ -0,0 +1,4 @@
1
+ # Nunca commite este arquivo. Use .env.local (gitignored).
2
+ NODE_ENV=development
3
+ DATABASE_URL=
4
+ AUTH_SECRET=
@@ -0,0 +1,47 @@
1
+ # 🚀 [NOME DO APP]
2
+
3
+ > Projeto gerado com **SDD AI Stack** — `npx create-sdd-ai-stack`.
4
+ > As regras de desenvolvimento moram em [`SDD/`](./SDD). **Leia [`SDD/AGENTS.md`](./SDD/AGENTS.md) antes de codar.**
5
+
6
+ ## Stack
7
+
8
+ - **Next.js 16** (App Router, React 19.2, Turbopack, Cache Components)
9
+ - **TypeScript** strict + path alias `@/*`
10
+ - **Tailwind CSS v4** (tokens no `src/app/globals.css`)
11
+ - **Biome** (lint + format)
12
+ - **Vitest** (unit/integr) + **Playwright** (E2E)
13
+
14
+ ## Comandos
15
+
16
+ ```bash
17
+ npm run dev # http://localhost:3000
18
+ npm run build # build de produção
19
+ npm run typecheck # tsc --noEmit
20
+ npm run lint # biome check
21
+ npm run test:unit # vitest (watch)
22
+ npm run test:coverage
23
+ npm run test:e2e # playwright
24
+ ```
25
+
26
+ ## Regras do projeto
27
+
28
+ Todas as regras vivem em `SDD/`:
29
+
30
+ | Documento | Escopo |
31
+ | --- | --- |
32
+ | [`SDD/AGENTS.md`](./SDD/AGENTS.md) | Leis do agente + fluxo de entrega |
33
+ | [`SDD/NEXT.md`](./SDD/NEXT.md) | Next.js 16 (padrão) |
34
+ | [`SDD/DESIGN.md`](./SDD/DESIGN.md) | Design system |
35
+ | [`SDD/NODE.md`](./SDD/NODE.md) | Node.js (workers, cron) |
36
+ | [`SDD/REACT.md`](./SDD/REACT.md) | React |
37
+ | [`SDD/ARCHITECTURE.md`](./SDD/ARCHITECTURE.md) | Arquitetura global |
38
+ | [`SDD/stacks/`](./SDD/stacks) | TypeScript, Tailwind, shadcn, testes, DB, IA, Git, CI |
39
+ | [`SDD/specs/PLAN.md`](./SDD/specs/PLAN.md) | Tarefa atual |
40
+
41
+ ## Fluxo obrigatório
42
+
43
+ 1. Pegue a próxima task em `SDD/specs/PLAN.md`
44
+ 2. Implemente **um passo** por vez
45
+ 3. Rode `typecheck` + `test:unit` + `test:e2e`
46
+ 4. Cole a evidência verde no terminal
47
+ 5. Marque `[x]` no PLAN e commite
@@ -0,0 +1,36 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/2.5.14/schema.json",
3
+ "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
4
+ "files": {
5
+ "includes": ["**", "!**/node_modules", "!**/.next", "!**/SDD", "!**/coverage"]
6
+ },
7
+ "formatter": {
8
+ "enabled": true,
9
+ "indentStyle": "space",
10
+ "indentWidth": 2,
11
+ "lineWidth": 90
12
+ },
13
+ "linter": {
14
+ "enabled": true,
15
+ "rules": {
16
+ "preset": "recommended",
17
+ "correctness": {
18
+ "noUnusedVariables": "error"
19
+ },
20
+ "suspicious": {
21
+ "noExplicitAny": "error"
22
+ }
23
+ }
24
+ },
25
+ "javascript": {
26
+ "formatter": {
27
+ "quoteStyle": "double",
28
+ "semicolons": "always"
29
+ }
30
+ },
31
+ "css": {
32
+ "parser": {
33
+ "tailwindDirectives": true
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,32 @@
1
+ node_modules/
2
+ .next/
3
+ out/
4
+ build/
5
+ dist/
6
+ coverage/
7
+ test-results/
8
+ playwright-report/
9
+ blob-report/
10
+ .playwright/
11
+
12
+ .env
13
+ .env.local
14
+ .env*.local
15
+ !.env.example
16
+
17
+ *.log
18
+ npm-debug.log*
19
+ yarn-error.log*
20
+
21
+ .DS_Store
22
+ Thumbs.db
23
+
24
+ .vscode/*
25
+ !.vscode/extensions.json
26
+ .idea/
27
+
28
+ .vercel
29
+ *.tsbuildinfo
30
+ next-env.d.ts
31
+
32
+ SDD/node_modules/
@@ -0,0 +1,12 @@
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ // PPR + "use cache" + cacheLife. Ver SDD/NEXT.md §6
5
+ cacheComponents: true,
6
+ reactCompiler: true,
7
+ images: {
8
+ remotePatterns: [{ protocol: "https", hostname: "**" }],
9
+ },
10
+ };
11
+
12
+ export default nextConfig;
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "sdd-ai-stack-next",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=20.9.0"
8
+ },
9
+ "scripts": {
10
+ "dev": "next dev",
11
+ "build": "next build",
12
+ "start": "next start",
13
+ "lint": "biome check .",
14
+ "lint:fix": "biome check --write .",
15
+ "format": "biome format --write .",
16
+ "typecheck": "tsc --noEmit",
17
+ "test": "vitest run",
18
+ "test:unit": "vitest",
19
+ "test:coverage": "vitest run --coverage",
20
+ "test:e2e": "playwright test",
21
+ "test:e2e:ui": "playwright test --ui"
22
+ },
23
+ "dependencies": {
24
+ "next": "^16.3.7",
25
+ "react": "^19.3.0",
26
+ "react-dom": "^19.3.0",
27
+ "server-only": "^0.0.1",
28
+ "zod": "^4.6.5",
29
+ "class-variance-authority": "^0.7.1",
30
+ "clsx": "^2.1.1",
31
+ "tailwind-merge": "^3.3.1",
32
+ "lucide-react": "^0.469.0"
33
+ },
34
+ "devDependencies": {
35
+ "@biomejs/biome": "^2.0.0",
36
+ "@playwright/test": "^1.63.0",
37
+ "@tailwindcss/postcss": "^4.3.3",
38
+ "@types/node": "^22.10.0",
39
+ "@types/react": "^19.0.0",
40
+ "@types/react-dom": "^19.0.0",
41
+ "@vitest/coverage-v8": "^5.0.2",
42
+ "babel-plugin-react-compiler": "^1.0.0",
43
+ "tailwindcss": "^4.3.3",
44
+ "typescript": "^7.0.2",
45
+ "vitest": "^5.0.2"
46
+ }
47
+ }
@@ -0,0 +1,21 @@
1
+ import { defineConfig, devices } from "@playwright/test";
2
+
3
+ export default defineConfig({
4
+ testDir: "./tests/e2e",
5
+ fullyParallel: true,
6
+ forbidOnly: !!process.env.CI,
7
+ retries: process.env.CI ? 2 : 0,
8
+ reporter: [["list"], ["html", { open: "never" }]],
9
+ use: {
10
+ baseURL: "http://localhost:3000",
11
+ trace: "on-first-retry",
12
+ screenshot: "only-on-failure",
13
+ },
14
+ projects: [{ name: "chromium", use: { ...devices["Desktop Chrome"] } }],
15
+ webServer: {
16
+ command: "npm run dev",
17
+ url: "http://localhost:3000",
18
+ reuseExistingServer: !process.env.CI,
19
+ timeout: 120_000,
20
+ },
21
+ });
@@ -0,0 +1,7 @@
1
+ const config = {
2
+ plugins: {
3
+ "@tailwindcss/postcss": {},
4
+ },
5
+ };
6
+
7
+ export default config;
@@ -0,0 +1,18 @@
1
+ export default function AppPage() {
2
+ return (
3
+ <main className="container-grid py-16">
4
+ <h1 className="text-headline-lg">Área logada</h1>
5
+ <p className="mt-3 text-body-md text-text-secondary">
6
+ Crie aqui a primeira feature. Copie a estrutura de{" "}
7
+ <code className="text-code-inline text-primary">src/features/example</code>.
8
+ </p>
9
+
10
+ <div className="mt-8 flex flex-wrap gap-2">
11
+ <span className="chip chip-active">Next.js 16</span>
12
+ <span className="chip">React 19</span>
13
+ <span className="chip">TypeScript</span>
14
+ <span className="chip">Tailwind v4</span>
15
+ </div>
16
+ </main>
17
+ );
18
+ }
@@ -0,0 +1,30 @@
1
+ "use client";
2
+ import { AlertTriangle } from "lucide-react";
3
+
4
+ export default function ErrorPage({
5
+ error,
6
+ reset,
7
+ }: {
8
+ error: Error & { digest?: string };
9
+ reset: () => void;
10
+ }) {
11
+ return (
12
+ <main className="container-grid flex min-h-screen flex-col items-start justify-center py-24">
13
+ <span className="chip" style={{ borderColor: "var(--color-error)" }}>
14
+ <AlertTriangle size={12} />
15
+ Erro
16
+ </span>
17
+ <h1 className="mt-6 text-headline-lg">Algo quebrou.</h1>
18
+ <p className="mt-3 max-w-lg text-body-md text-text-secondary">
19
+ O erro foi registrado. Tente novamente — se persistir, o digest abaixo identifica
20
+ a ocorrência.
21
+ </p>
22
+ {error.digest ? (
23
+ <p className="mt-4 text-code-inline text-text-muted">digest: {error.digest}</p>
24
+ ) : null}
25
+ <button type="button" onClick={reset} className="btn-primary mt-8">
26
+ Tentar novamente
27
+ </button>
28
+ </main>
29
+ );
30
+ }
@@ -0,0 +1,25 @@
1
+ import type { Metadata } from "next";
2
+
3
+ export const metadata: Metadata = {
4
+ title: "Home",
5
+ description: "Área logada da aplicação.",
6
+ };
7
+
8
+ export default function AppLayout({ children }: Readonly<{ children: React.ReactNode }>) {
9
+ return (
10
+ <div className="min-h-screen">
11
+ {/* Nav rail: glassmorphism. Ver SDD/DESIGN.md §3 */}
12
+ <header className="sticky top-0 z-50 border-b border-border-subtle bg-surface-raised/80 backdrop-blur-xl">
13
+ <div className="container-grid flex h-14 items-center justify-between">
14
+ <span className="text-headline-sm font-semibold">[APP]</span>
15
+ <nav className="flex items-center gap-1">
16
+ <a href="/app" className="btn-ghost">
17
+ App
18
+ </a>
19
+ </nav>
20
+ </div>
21
+ </header>
22
+ {children}
23
+ </div>
24
+ );
25
+ }
@@ -0,0 +1,73 @@
1
+ export default function HomePage() {
2
+ return (
3
+ <main className="container-grid py-16 lg:py-24">
4
+ <p className="label-mono">Executive Engineering</p>
5
+
6
+ <h1 className="mt-4 text-display-hero-mobile font-extrabold tracking-tight text-balance lg:text-display-hero">
7
+ [HERO HEADLINE AQUI]
8
+ </h1>
9
+
10
+ <p className="mt-6 max-w-2xl text-body-lg text-text-secondary">
11
+ [Subtítulo de uma linha explicando o impacto operacional e financeiro do produto.]
12
+ </p>
13
+
14
+ <div className="mt-10 flex flex-wrap gap-3">
15
+ <a href="#principal" className="btn-primary">
16
+ Ação Executiva
17
+ </a>
18
+ <a href="#detalhe" className="btn-secondary">
19
+ Deep Dive
20
+ </a>
21
+ </div>
22
+
23
+ <section
24
+ id="principal"
25
+ className="mt-24 grid grid-cols-4 gap-4 md:grid-cols-8 lg:mt-36 lg:grid-cols-12"
26
+ >
27
+ <KpiCard label="Economia anual" value="R$ 24M" sub="12 serviços migrados" />
28
+ <KpiCard label="Throughput" value="100M/dia" sub="msgs processadas" />
29
+ <KpiCard label="Latência p99" value="42ms" sub="região São Paulo" />
30
+ <KpiCard label="Assets sob custódia" value="R$ 100bi" sub="auditoria ok" />
31
+ </section>
32
+
33
+ <section id="detalhe" className="mt-20">
34
+ <p className="label-mono">Stack</p>
35
+ <h2 className="mt-3 text-headline-md">Tecnologia em produção</h2>
36
+ <div className="mt-6 flex flex-wrap gap-2">
37
+ {STACK.map((tech) => (
38
+ <span key={tech} className="chip">
39
+ {tech}
40
+ </span>
41
+ ))}
42
+ </div>
43
+ </section>
44
+ </main>
45
+ );
46
+ }
47
+
48
+ const STACK = [
49
+ "Next.js 16",
50
+ "React 19",
51
+ "TypeScript",
52
+ "Tailwind v4",
53
+ "PostgreSQL",
54
+ "Prisma",
55
+ "Zod",
56
+ "Vitest",
57
+ "Playwright",
58
+ ];
59
+
60
+ function KpiCard({ label, value, sub }: { label: string; value: string; sub: string }) {
61
+ return (
62
+ <div className="card-metric col-span-4 md:col-span-4 lg:col-span-3">
63
+ <p className="label-mono flex items-center gap-2">
64
+ <span className="status-dot" />
65
+ {label}
66
+ </p>
67
+ <p className="mt-3 text-metric-stat-mobile font-bold tracking-tight text-primary lg:text-metric-stat">
68
+ {value}
69
+ </p>
70
+ <p className="mt-2 text-body-sm text-text-secondary">{sub}</p>
71
+ </div>
72
+ );
73
+ }