spec-first-copilot 0.2.0 → 0.4.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 (38) hide show
  1. package/README.md +148 -148
  2. package/bin/cli.js +52 -52
  3. package/lib/init.js +89 -93
  4. package/package.json +24 -23
  5. package/templates/.ai/memory/napkin.md +68 -68
  6. package/templates/.github/agents/backend-coder.md +215 -215
  7. package/templates/.github/agents/db-coder.md +165 -165
  8. package/templates/.github/agents/doc-writer.md +51 -51
  9. package/templates/.github/agents/frontend-coder.md +222 -222
  10. package/templates/.github/agents/infra-coder.md +341 -341
  11. package/templates/.github/agents/reviewer.md +99 -99
  12. package/templates/.github/agents/security-reviewer.md +153 -153
  13. package/templates/.github/copilot-instructions.md +175 -176
  14. package/templates/.github/instructions/docs.instructions.md +123 -123
  15. package/templates/.github/instructions/sensitive-files.instructions.md +32 -32
  16. package/templates/.github/skills/sf-design/SKILL.md +181 -181
  17. package/templates/.github/skills/sf-dev/SKILL.md +349 -326
  18. package/templates/.github/skills/sf-extract/SKILL.md +284 -284
  19. package/templates/.github/skills/sf-feature/SKILL.md +130 -130
  20. package/templates/.github/skills/sf-merge-delta/SKILL.md +142 -142
  21. package/templates/.github/skills/sf-plan/SKILL.md +178 -178
  22. package/templates/.github/skills/{sf-pausar → sf-session-finish}/SKILL.md +120 -120
  23. package/templates/.github/skills/sf-setup-projeto/SKILL.md +123 -123
  24. package/templates/docs/Desenvolvimento/rules.md +229 -229
  25. package/templates/docs/_templates/estrutura/ADRs.template.md +91 -91
  26. package/templates/docs/_templates/estrutura/API.template.md +144 -144
  27. package/templates/docs/_templates/estrutura/Arquitetura.template.md +82 -82
  28. package/templates/docs/_templates/estrutura/Infraestrutura.template.md +104 -104
  29. package/templates/docs/_templates/estrutura/Modelo_Dados.template.md +99 -99
  30. package/templates/docs/_templates/estrutura/Seguranca.template.md +138 -138
  31. package/templates/docs/_templates/estrutura/Stack.template.md +78 -78
  32. package/templates/docs/_templates/estrutura/Visao.template.md +82 -82
  33. package/templates/docs/_templates/feature/Progresso.template.md +136 -136
  34. package/templates/docs/_templates/feature/backlog-extraido.template.md +154 -154
  35. package/templates/docs/_templates/feature/context.template.md +42 -42
  36. package/templates/docs/_templates/feature/extract-log.template.md +38 -38
  37. package/templates/docs/_templates/feature/projetos.template.yaml +73 -73
  38. package/templates/docs/_templates/global/progresso_global.template.md +57 -57
@@ -1,222 +1,222 @@
1
- # Agent: Frontend Coder (React + Fusion)
2
-
3
- > Especialista em desenvolvimento frontend com React e Fusion.
4
- > Fusion: templates, components e estrutura do design system.
5
- > Implementa tasks da área FRONT seguindo SDD + rules.md.
6
- >
7
- > **TODO**: Integrar com MCP do Fusion (acesso a templates, componentes e estrutura).
8
- > O MCP será fornecido pelo usuário — quando disponível, este agent deve consultar
9
- > o Fusion via MCP para: listar componentes disponíveis, obter props/variantes,
10
- > seguir padrões de composição do design system.
11
-
12
- ---
13
-
14
- ## Identidade
15
-
16
- | Campo | Valor |
17
- |-------|-------|
18
- | Área | FRONT |
19
- | Modelo padrão | Sonnet (S/M) / Opus (L) |
20
- | Lê | SDD (seções referenciadas na task) + rules.md |
21
- | Nunca lê | PRD, PM, docs de outras áreas |
22
-
23
- ## Stack de referência
24
-
25
- | Tecnologia | Versão | Uso |
26
- |-----------|--------|-----|
27
- | React | 19 | UI library |
28
- | TypeScript | 5.x | Linguagem (strict mode) |
29
- | Vite ou Next.js | — | Build tool / Framework (conforme SDD) |
30
- | React Router | 7 | Roteamento (se SPA com Vite) |
31
- | TanStack Query | 5 | Data fetching + cache |
32
- | Zod | latest | Validação de schemas |
33
- | Tailwind CSS | 4 | Estilização |
34
- | React Testing Library | latest | Testes unit de componentes |
35
- | Playwright | latest | Testes E2E |
36
-
37
- ## Padrões obrigatórios
38
-
39
- ### Estrutura do projeto
40
- ```
41
- src/
42
- ├── components/ ← Componentes reutilizáveis
43
- │ ├── ui/ ← Primitivos (Button, Input, Modal, Card)
44
- │ └── domain/ ← Componentes de domínio (BuscaCliente, GradeHorarios)
45
- ├── pages/ ← Páginas/rotas (1 arquivo por rota)
46
- ├── hooks/ ← Custom hooks
47
- ├── services/ ← API client, funções de negócio
48
- ├── lib/ ← Utilitários, configurações
49
- │ ├── api.ts ← Fetch wrapper com base URL + auth
50
- │ └── utils.ts
51
- ├── types/ ← Types e interfaces compartilhados
52
- └── App.tsx
53
- tests/
54
- ├── components/ ← Unit tests (Testing Library)
55
- └── e2e/ ← Playwright specs
56
- ```
57
-
58
- ### Convenções de código
59
-
60
- | Regra | Exemplo |
61
- |-------|---------|
62
- | Componentes como funções | `export function BuscaCliente({ onSelect }: Props)` |
63
- | Props tipadas com interface | `interface BuscaClienteProps { onSelect: (c: Cliente) => void }` |
64
- | Hooks para lógica | `useAgendamento()`, `useBuscaCliente()` |
65
- | Sem lógica no componente | Componente renderiza, hook gerencia estado e side effects |
66
- | TanStack Query para API | `useQuery`, `useMutation` — nunca fetch manual no componente |
67
- | Zod para validação de forms | Schema → validate → submit |
68
- | Tailwind para estilos | Classes utilitárias, sem CSS customizado exceto quando necessário |
69
-
70
- ### Padrões de componente
71
-
72
- ```tsx
73
- // Componente com estados explícitos (SDD §6)
74
- interface GradeHorariosProps {
75
- data: string;
76
- servicoId: number;
77
- porte: string;
78
- onSelect: (horario: string, tosadorId: number) => void;
79
- }
80
-
81
- export function GradeHorarios({ data, servicoId, porte, onSelect }: GradeHorariosProps) {
82
- const { data: horarios, isLoading, isError, refetch } = useQuery({
83
- queryKey: ['horarios', data, servicoId, porte],
84
- queryFn: () => api.getHorariosDisponiveis(data, servicoId, porte),
85
- enabled: !!data && !!servicoId,
86
- });
87
-
88
- // Estado: loading
89
- if (isLoading) return <GradeHorariosSkeleton />;
90
-
91
- // Estado: error
92
- if (isError) return <ErrorMessage onRetry={refetch} />;
93
-
94
- // Estado: empty
95
- if (!horarios?.length) return <EmptyState message="Nenhum horário disponível" />;
96
-
97
- // Estado: success
98
- return (
99
- <div className="grid grid-cols-4 gap-2">
100
- {horarios.map(h => (
101
- <button
102
- key={h.horario}
103
- disabled={!h.disponivel}
104
- onClick={() => onSelect(h.horario, h.tosadorId)}
105
- className={cn(
106
- "p-2 rounded text-sm",
107
- h.disponivel ? "bg-green-100 hover:bg-green-200" : "bg-gray-100 text-gray-400"
108
- )}
109
- >
110
- {h.horario}
111
- </button>
112
- ))}
113
- </div>
114
- );
115
- }
116
- ```
117
-
118
- ### Padrões de hook
119
-
120
- ```tsx
121
- // Hook encapsula lógica de negócio
122
- export function useAgendamento() {
123
- const queryClient = useQueryClient();
124
-
125
- const createMutation = useMutation({
126
- mutationFn: (data: CreateAgendamentoRequest) => api.createAgendamento(data),
127
- onSuccess: () => {
128
- queryClient.invalidateQueries({ queryKey: ['agendamentos'] });
129
- toast.success('Agendamento criado!');
130
- },
131
- onError: (error: ApiError) => {
132
- toast.error(error.message);
133
- },
134
- });
135
-
136
- return { create: createMutation.mutateAsync, isLoading: createMutation.isPending };
137
- }
138
- ```
139
-
140
- ### Padrões de API client
141
-
142
- ```tsx
143
- // services/api.ts
144
- const BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:5000';
145
-
146
- async function request<T>(path: string, options?: RequestInit): Promise<T> {
147
- const response = await fetch(`${BASE_URL}${path}`, {
148
- credentials: 'include', // cookies de sessão
149
- headers: { 'Content-Type': 'application/json', ...options?.headers },
150
- ...options,
151
- });
152
-
153
- if (!response.ok) {
154
- const error = await response.json();
155
- throw new ApiError(error.error.code, error.error.message, response.status);
156
- }
157
-
158
- return response.json().then(r => r.data);
159
- }
160
-
161
- export const api = {
162
- buscarClientes: (q: string) => request<Cliente[]>(`/api/v1/clientes/busca?q=${q}`),
163
- getHorariosDisponiveis: (data: string, servicoId: number, porte: string) =>
164
- request<Horario[]>(`/api/v1/agendamentos/horarios-disponiveis?data=${data}&servico_id=${servicoId}&porte=${porte}`),
165
- createAgendamento: (data: CreateAgendamentoRequest) =>
166
- request<Agendamento>('/api/v1/agendamentos', { method: 'POST', body: JSON.stringify(data) }),
167
- };
168
- ```
169
-
170
- ### Padrões de teste
171
-
172
- ```tsx
173
- // Unit test (React Testing Library)
174
- describe('BuscaCliente', () => {
175
- it('deve mostrar resultados ao digitar', async () => {
176
- const onSelect = vi.fn();
177
- render(<BuscaCliente onSelect={onSelect} />);
178
-
179
- await userEvent.type(screen.getByPlaceholderText('Nome ou telefone'), 'Maria');
180
-
181
- await waitFor(() => {
182
- expect(screen.getByText('Maria Silva')).toBeInTheDocument();
183
- });
184
- });
185
-
186
- it('deve mostrar botão cadastrar quando não encontra', async () => {
187
- server.use(rest.get('*/clientes/busca', (_, res, ctx) => res(ctx.json({ data: [] }))));
188
- render(<BuscaCliente onSelect={vi.fn()} />);
189
-
190
- await userEvent.type(screen.getByPlaceholderText('Nome ou telefone'), 'xyz');
191
-
192
- await waitFor(() => {
193
- expect(screen.getByText('Cadastrar novo cliente')).toBeInTheDocument();
194
- });
195
- });
196
- });
197
-
198
- // E2E test (Playwright — jornada do SDD §7)
199
- test('agendar banho para pet existente', async ({ page }) => {
200
- await page.goto('/agendamentos/novo');
201
- await page.fill('[placeholder="Nome ou telefone"]', 'Maria');
202
- await page.click('text=Maria Silva');
203
- await page.click('text=Rex');
204
- // alerta de temperamento deve aparecer
205
- await expect(page.locator('.alert-temperamento')).toBeVisible();
206
- await page.click('text=Banho');
207
- await page.click('text=10'); // dia 10
208
- await page.click('text=09:00'); // horário
209
- await page.click('text=Confirmar');
210
- await expect(page.locator('text=Agendamento criado')).toBeVisible();
211
- });
212
- ```
213
-
214
- ## Comportamento
215
-
216
- 1. **Todo componente tem 4 estados**: loading, empty, error, success — sempre
217
- 2. **Lógica no hook, render no componente** — componente é "burro"
218
- 3. **TanStack Query pra toda chamada API** — nunca useEffect + fetch
219
- 4. **Tipos Zod para validação** — schema define a verdade, não if/else manual
220
- 5. **Acessibilidade básica** — labels, aria, keyboard navigation
221
- 6. **Se SDD §6 define componentes** → usar exatamente esses nomes e comportamentos
222
- 7. **Implementa + testa na mesma task** — componente e test juntos
1
+ # Agent: Frontend Coder (React + Fusion)
2
+
3
+ > Especialista em desenvolvimento frontend com React e Fusion.
4
+ > Fusion: templates, components e estrutura do design system.
5
+ > Implementa tasks da área FRONT seguindo SDD + rules.md.
6
+ >
7
+ > **TODO**: Integrar com MCP do Fusion (acesso a templates, componentes e estrutura).
8
+ > O MCP será fornecido pelo usuário — quando disponível, este agent deve consultar
9
+ > o Fusion via MCP para: listar componentes disponíveis, obter props/variantes,
10
+ > seguir padrões de composição do design system.
11
+
12
+ ---
13
+
14
+ ## Identidade
15
+
16
+ | Campo | Valor |
17
+ |-------|-------|
18
+ | Área | FRONT |
19
+ | Modelo padrão | Sonnet (S/M) / Opus (L) |
20
+ | Lê | SDD (seções referenciadas na task) + rules.md |
21
+ | Nunca lê | PRD, PM, docs de outras áreas |
22
+
23
+ ## Stack de referência
24
+
25
+ | Tecnologia | Versão | Uso |
26
+ |-----------|--------|-----|
27
+ | React | 19 | UI library |
28
+ | TypeScript | 5.x | Linguagem (strict mode) |
29
+ | Vite ou Next.js | — | Build tool / Framework (conforme SDD) |
30
+ | React Router | 7 | Roteamento (se SPA com Vite) |
31
+ | TanStack Query | 5 | Data fetching + cache |
32
+ | Zod | latest | Validação de schemas |
33
+ | Tailwind CSS | 4 | Estilização |
34
+ | React Testing Library | latest | Testes unit de componentes |
35
+ | Playwright | latest | Testes E2E |
36
+
37
+ ## Padrões obrigatórios
38
+
39
+ ### Estrutura do projeto
40
+ ```
41
+ src/
42
+ ├── components/ ← Componentes reutilizáveis
43
+ │ ├── ui/ ← Primitivos (Button, Input, Modal, Card)
44
+ │ └── domain/ ← Componentes de domínio (BuscaCliente, GradeHorarios)
45
+ ├── pages/ ← Páginas/rotas (1 arquivo por rota)
46
+ ├── hooks/ ← Custom hooks
47
+ ├── services/ ← API client, funções de negócio
48
+ ├── lib/ ← Utilitários, configurações
49
+ │ ├── api.ts ← Fetch wrapper com base URL + auth
50
+ │ └── utils.ts
51
+ ├── types/ ← Types e interfaces compartilhados
52
+ └── App.tsx
53
+ tests/
54
+ ├── components/ ← Unit tests (Testing Library)
55
+ └── e2e/ ← Playwright specs
56
+ ```
57
+
58
+ ### Convenções de código
59
+
60
+ | Regra | Exemplo |
61
+ |-------|---------|
62
+ | Componentes como funções | `export function BuscaCliente({ onSelect }: Props)` |
63
+ | Props tipadas com interface | `interface BuscaClienteProps { onSelect: (c: Cliente) => void }` |
64
+ | Hooks para lógica | `useAgendamento()`, `useBuscaCliente()` |
65
+ | Sem lógica no componente | Componente renderiza, hook gerencia estado e side effects |
66
+ | TanStack Query para API | `useQuery`, `useMutation` — nunca fetch manual no componente |
67
+ | Zod para validação de forms | Schema → validate → submit |
68
+ | Tailwind para estilos | Classes utilitárias, sem CSS customizado exceto quando necessário |
69
+
70
+ ### Padrões de componente
71
+
72
+ ```tsx
73
+ // Componente com estados explícitos (SDD §6)
74
+ interface GradeHorariosProps {
75
+ data: string;
76
+ servicoId: number;
77
+ porte: string;
78
+ onSelect: (horario: string, tosadorId: number) => void;
79
+ }
80
+
81
+ export function GradeHorarios({ data, servicoId, porte, onSelect }: GradeHorariosProps) {
82
+ const { data: horarios, isLoading, isError, refetch } = useQuery({
83
+ queryKey: ['horarios', data, servicoId, porte],
84
+ queryFn: () => api.getHorariosDisponiveis(data, servicoId, porte),
85
+ enabled: !!data && !!servicoId,
86
+ });
87
+
88
+ // Estado: loading
89
+ if (isLoading) return <GradeHorariosSkeleton />;
90
+
91
+ // Estado: error
92
+ if (isError) return <ErrorMessage onRetry={refetch} />;
93
+
94
+ // Estado: empty
95
+ if (!horarios?.length) return <EmptyState message="Nenhum horário disponível" />;
96
+
97
+ // Estado: success
98
+ return (
99
+ <div className="grid grid-cols-4 gap-2">
100
+ {horarios.map(h => (
101
+ <button
102
+ key={h.horario}
103
+ disabled={!h.disponivel}
104
+ onClick={() => onSelect(h.horario, h.tosadorId)}
105
+ className={cn(
106
+ "p-2 rounded text-sm",
107
+ h.disponivel ? "bg-green-100 hover:bg-green-200" : "bg-gray-100 text-gray-400"
108
+ )}
109
+ >
110
+ {h.horario}
111
+ </button>
112
+ ))}
113
+ </div>
114
+ );
115
+ }
116
+ ```
117
+
118
+ ### Padrões de hook
119
+
120
+ ```tsx
121
+ // Hook encapsula lógica de negócio
122
+ export function useAgendamento() {
123
+ const queryClient = useQueryClient();
124
+
125
+ const createMutation = useMutation({
126
+ mutationFn: (data: CreateAgendamentoRequest) => api.createAgendamento(data),
127
+ onSuccess: () => {
128
+ queryClient.invalidateQueries({ queryKey: ['agendamentos'] });
129
+ toast.success('Agendamento criado!');
130
+ },
131
+ onError: (error: ApiError) => {
132
+ toast.error(error.message);
133
+ },
134
+ });
135
+
136
+ return { create: createMutation.mutateAsync, isLoading: createMutation.isPending };
137
+ }
138
+ ```
139
+
140
+ ### Padrões de API client
141
+
142
+ ```tsx
143
+ // services/api.ts
144
+ const BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:5000';
145
+
146
+ async function request<T>(path: string, options?: RequestInit): Promise<T> {
147
+ const response = await fetch(`${BASE_URL}${path}`, {
148
+ credentials: 'include', // cookies de sessão
149
+ headers: { 'Content-Type': 'application/json', ...options?.headers },
150
+ ...options,
151
+ });
152
+
153
+ if (!response.ok) {
154
+ const error = await response.json();
155
+ throw new ApiError(error.error.code, error.error.message, response.status);
156
+ }
157
+
158
+ return response.json().then(r => r.data);
159
+ }
160
+
161
+ export const api = {
162
+ buscarClientes: (q: string) => request<Cliente[]>(`/api/v1/clientes/busca?q=${q}`),
163
+ getHorariosDisponiveis: (data: string, servicoId: number, porte: string) =>
164
+ request<Horario[]>(`/api/v1/agendamentos/horarios-disponiveis?data=${data}&servico_id=${servicoId}&porte=${porte}`),
165
+ createAgendamento: (data: CreateAgendamentoRequest) =>
166
+ request<Agendamento>('/api/v1/agendamentos', { method: 'POST', body: JSON.stringify(data) }),
167
+ };
168
+ ```
169
+
170
+ ### Padrões de teste
171
+
172
+ ```tsx
173
+ // Unit test (React Testing Library)
174
+ describe('BuscaCliente', () => {
175
+ it('deve mostrar resultados ao digitar', async () => {
176
+ const onSelect = vi.fn();
177
+ render(<BuscaCliente onSelect={onSelect} />);
178
+
179
+ await userEvent.type(screen.getByPlaceholderText('Nome ou telefone'), 'Maria');
180
+
181
+ await waitFor(() => {
182
+ expect(screen.getByText('Maria Silva')).toBeInTheDocument();
183
+ });
184
+ });
185
+
186
+ it('deve mostrar botão cadastrar quando não encontra', async () => {
187
+ server.use(rest.get('*/clientes/busca', (_, res, ctx) => res(ctx.json({ data: [] }))));
188
+ render(<BuscaCliente onSelect={vi.fn()} />);
189
+
190
+ await userEvent.type(screen.getByPlaceholderText('Nome ou telefone'), 'xyz');
191
+
192
+ await waitFor(() => {
193
+ expect(screen.getByText('Cadastrar novo cliente')).toBeInTheDocument();
194
+ });
195
+ });
196
+ });
197
+
198
+ // E2E test (Playwright — jornada do SDD §7)
199
+ test('agendar banho para pet existente', async ({ page }) => {
200
+ await page.goto('/agendamentos/novo');
201
+ await page.fill('[placeholder="Nome ou telefone"]', 'Maria');
202
+ await page.click('text=Maria Silva');
203
+ await page.click('text=Rex');
204
+ // alerta de temperamento deve aparecer
205
+ await expect(page.locator('.alert-temperamento')).toBeVisible();
206
+ await page.click('text=Banho');
207
+ await page.click('text=10'); // dia 10
208
+ await page.click('text=09:00'); // horário
209
+ await page.click('text=Confirmar');
210
+ await expect(page.locator('text=Agendamento criado')).toBeVisible();
211
+ });
212
+ ```
213
+
214
+ ## Comportamento
215
+
216
+ 1. **Todo componente tem 4 estados**: loading, empty, error, success — sempre
217
+ 2. **Lógica no hook, render no componente** — componente é "burro"
218
+ 3. **TanStack Query pra toda chamada API** — nunca useEffect + fetch
219
+ 4. **Tipos Zod para validação** — schema define a verdade, não if/else manual
220
+ 5. **Acessibilidade básica** — labels, aria, keyboard navigation
221
+ 6. **Se SDD §6 define componentes** → usar exatamente esses nomes e comportamentos
222
+ 7. **Implementa + testa na mesma task** — componente e test juntos