ganbatte-os 0.2.37 → 0.2.41

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/.gos/agents/profiles/ganbatte-os-master.md +100 -0
  2. package/.gos/libraries/caveman-rules.md +58 -0
  3. package/.gos/libraries/cloudflare-stack-kb.md +161 -0
  4. package/.gos/libraries/component-reuse-gate.md +75 -0
  5. package/.gos/libraries/default-stack-kb.md +98 -0
  6. package/.gos/libraries/engineering-best-practices.md +208 -0
  7. package/.gos/libraries/gos-compress-setup.md +62 -0
  8. package/.gos/libraries/intake-questions-mom-test.md +91 -0
  9. package/.gos/libraries/lucide-icons-policy.md +174 -0
  10. package/.gos/libraries/security-best-practices.md +138 -0
  11. package/.gos/libraries/supabase-stack-kb.md +124 -0
  12. package/.gos/libraries/timer-pattern-spec.md +252 -0
  13. package/.gos/libraries/typeform-pattern-spec.md +204 -0
  14. package/.gos/libraries/ui-guardrails-checklist.md +144 -0
  15. package/.gos/libraries/visual-diff-lenses.md +114 -0
  16. package/.gos/playbooks/audit-streaming-playbook.md +86 -0
  17. package/.gos/skills/adr-tech-decisions/SKILL.md +166 -0
  18. package/.gos/skills/audit-screenshots/SKILL.md +200 -142
  19. package/.gos/skills/cloudflare-pages-setup/SKILL.md +180 -0
  20. package/.gos/skills/figma-print-diff/SKILL.md +170 -0
  21. package/.gos/skills/gos-caveman/SKILL.md +110 -0
  22. package/.gos/skills/gos-compress/SKILL.md +134 -0
  23. package/.gos/skills/gos-compress/scripts/compress.py +346 -0
  24. package/.gos/skills/gos-compress/scripts/setup.py +91 -0
  25. package/.gos/skills/idea-intake/SKILL.md +147 -0
  26. package/.gos/skills/plan-blueprint/SKILL.md +18 -3
  27. package/.gos/skills/plan-to-tasks/SKILL.md +37 -1
  28. package/.gos/skills/prd-from-intake/SKILL.md +94 -0
  29. package/.gos/skills/prototype-orchestrator/SKILL.md +120 -0
  30. package/.gos/skills/registry.json +12 -1
  31. package/.gos/skills/timer-component-pattern/SKILL.md +245 -0
  32. package/.gos/skills/typeform-form-pattern/SKILL.md +210 -0
  33. package/.gos/skills/ui-guardrails/SKILL.md +111 -0
  34. package/.gos/templates/intakeTemplate.md +41 -0
  35. package/.gos/templates/planTemplate.md +25 -4
  36. package/.gos/templates/prdLeanTemplate.md +40 -0
  37. package/.gos/templates/taskTemplate.md +29 -5
  38. package/package.json +1 -1
@@ -0,0 +1,94 @@
1
+ ---
2
+ name: prd-from-intake
3
+ description: >
4
+ Converte intake.md (saida de idea-intake) em PRD lean otimizado para LLM. Foco em problema,
5
+ persona, JTBD, telas-chave, criterios de aceite e metricas. NAO inclui decisoes tecnicas
6
+ (deixadas para adr-tech-decisions). Output: docs/prd/PRD-NNN-<slug>/prd.md.
7
+ argument-hint: "<INTAKE-id> [--descartavel] [--lean|--full]"
8
+ allowedTools: [Read, Glob, Grep, Bash, Write, Edit, AskUserQuestion]
9
+ sourceDocs:
10
+ - templates/prdLeanTemplate.md
11
+ use-when:
12
+ - tem-se um intake.md pronto e precisa do PRD
13
+ - usuario diz "transforma essa ideia em PRD"
14
+ do-not-use-for:
15
+ - ideia ainda nao validada (rode idea-intake primeiro)
16
+ - decisoes tecnicas (use adr-tech-decisions)
17
+ - planejamento de tela (use plan-blueprint)
18
+ metadata:
19
+ category: documentation
20
+ ---
21
+
22
+ Voce esta executando como **Product Manager Lean** via skill `prd-from-intake`. Le um intake.md e produz PRD enxuto, otimizado para servir de contexto a outras skills (plan-blueprint, adr-tech-decisions, design-to-code).
23
+
24
+ ## Contrato
25
+
26
+ 1. Input obrigatorio: `INTAKE-id` (ex: INTAKE-007-agendamento-mae).
27
+ 2. Resolver path: `docs/intake/<INTAKE-id>/intake.md`. Ausente -> abortar.
28
+ 3. Modos:
29
+ - `--lean` (default): PRD em <500 palavras, somente o essencial.
30
+ - `--full`: PRD completo com riscos, dependencias, edge cases.
31
+ 4. Se `descartavel: true` no intake -> forca `--lean` automaticamente. Sobrescrever so com flag explicita.
32
+ 5. Output unico: `docs/prd/PRD-NNN-<slug>/prd.md`. NNN = ultimo PRD + 1.
33
+
34
+ ## Estrutura do PRD lean
35
+
36
+ ```markdown
37
+ ---
38
+ prd_id: PRD-NNN-<slug>
39
+ intake_ref: INTAKE-NNN-<slug>
40
+ descartavel: <bool>
41
+ status: pronto-para-adr
42
+ created_at: <iso>
43
+ ---
44
+
45
+ # <Titulo>
46
+
47
+ ## TL;DR (1 paragrafo, max 4 linhas)
48
+ <problema + persona + solucao + metrica de sucesso, comprimido>
49
+
50
+ ## Quem usa
51
+ <persona em 2 linhas>
52
+
53
+ ## Por que existe
54
+ <problema em 2 linhas, citando 1 historia real do intake>
55
+
56
+ ## O que faz (3-7 bullets)
57
+ - <feature 1>
58
+ - <feature 2>
59
+
60
+ ## Telas-chave
61
+ | # | Tela | Proposito | Inputs | Outputs |
62
+ |---|------|-----------|--------|---------|
63
+ | 1 | <nome> | <verbo + objeto> | <dado entrada> | <dado saida> |
64
+
65
+ ## Criterios de aceite (do nao-tecnico)
66
+ - [ ] Usuario consegue <acao primaria> em menos de <X> passos
67
+ - [ ] <criterio mensurivel>
68
+
69
+ ## Metrica de sucesso
70
+ <copia da metrica humana do intake + 1 metrica quantitativa derivada>
71
+
72
+ ## NAO faz parte (escopo negativo)
73
+ - <bullet>
74
+
75
+ ## Proximo passo
76
+ - Rodar `*adr-tech-decisions PRD-NNN-<slug>` para definir arquitetura.
77
+ ```
78
+
79
+ ## Compressao automatica
80
+
81
+ Se contagem de tokens do intake.md > 2000:
82
+ 1. Tentar usar `gos-compress` (skill wrapper sandeco) com rate=0.5 sobre intake.md antes de ler.
83
+ 2. Se `gos-compress` indisponivel (nao inicializado), usar fallback de leitura direta + summarizar antes de gerar PRD.
84
+
85
+ ## Regras criticas
86
+
87
+ - **Zero tecnologia**: nao mencionar framework, banco, deploy. Sao decisoes do ADR.
88
+ - **Compressao agressiva no lean**: se passou de 500 palavras, comprime ate caber.
89
+ - **Anti-template**: nao copiar bullets do intake literal — sintetizar. PRD e nova obra, nao paraphrase.
90
+ - **Marcar descartavel**: o flag se propaga ate o ADR, plan-blueprint e codegen — define se vale a pena ter testes E2E, CI/CD, rate limit, etc.
91
+
92
+ ## Input
93
+
94
+ $ARGUMENTS
@@ -0,0 +1,120 @@
1
+ ---
2
+ name: prototype-orchestrator
3
+ description: >
4
+ Orquestra pipeline ideia-para-prototipo end-to-end: idea-intake -> prd-from-intake ->
5
+ adr-tech-decisions -> stitch/figma generate-design -> design-critique -> handoff para
6
+ design-to-code. Pensado para usuarios nao-tecnicos com MVP descartavel. Decision gates
7
+ entre fases para abortar cedo.
8
+ argument-hint: "<frase da ideia ou 'continuar'> [--skip-figma] [--full-stack]"
9
+ allowedTools: [Read, Glob, Grep, Bash, Write, Edit, AskUserQuestion]
10
+ sourceDocs:
11
+ - skills/idea-intake/SKILL.md
12
+ - skills/prd-from-intake/SKILL.md
13
+ - skills/adr-tech-decisions/SKILL.md
14
+ - skills/figma-implement-design/SKILL.md
15
+ - skills/design-to-code/SKILL.md
16
+ - skills/figma-print-diff/SKILL.md
17
+ use-when:
18
+ - usuario nao-tecnico chega com uma ideia e quer prototipo visivel
19
+ - ja existe intake mas usuario quer levar ate Figma+codigo
20
+ - preciso iterar de "ideia" ate "tela rodando" sem perder contexto entre fases
21
+ do-not-use-for:
22
+ - tarefas tecnicas pontuais (use plan-blueprint direto)
23
+ - quando ja existe stack.md + PRD (entre direto via plan-blueprint)
24
+ metadata:
25
+ category: orchestration
26
+ ---
27
+
28
+ Voce esta executando como **Orquestrador Ideia-Prototipo** via skill `prototype-orchestrator`. Coordena 5 skills em sequencia com decision gates entre cada uma. Conduzido pelo `gos-master` para preservar comprehension gate cross-fase.
29
+
30
+ ## Pipeline
31
+
32
+ ```
33
+ [ideia bruta]
34
+ |
35
+ v
36
+ 1. idea-intake -> intake.md (gate: usuario valida resumo)
37
+ |
38
+ v
39
+ 2. prd-from-intake -> prd.md (gate: usuario aprova TL;DR)
40
+ |
41
+ v
42
+ 3. adr-tech-decisions -> ADR + stack.md (gate: usuario confirma decisoes tecnicas)
43
+ |
44
+ v
45
+ 4. (opcional) Stitch/Figma generate-design -> frames Figma (gate: usuario aprova mockups)
46
+ |
47
+ v
48
+ 5. plan-blueprint por tela -> plan + tasks (gate: validate-plan)
49
+ |
50
+ v
51
+ 6. design-to-code + figma-implement-design -> codigo
52
+ |
53
+ v
54
+ 7. figma-print-diff -> ajustes ate aprovacao visual
55
+ ```
56
+
57
+ ## Estado da orquestracao
58
+
59
+ Persistido em `.gos-local/prototype-session.json`:
60
+
61
+ ```json
62
+ {
63
+ "session_id": "proto-2026-05-09T14-22-03",
64
+ "slug": "<derivado da ideia>",
65
+ "current_phase": 3,
66
+ "phases": {
67
+ "1_intake": { "status": "done", "ref": "INTAKE-007-agendamento-mae" },
68
+ "2_prd": { "status": "done", "ref": "PRD-007-agendamento-mae" },
69
+ "3_adr": { "status": "in-progress", "ref": null },
70
+ "4_figma": { "status": "pending", "ref": null },
71
+ "5_plans": { "status": "pending", "refs": [] },
72
+ "6_codegen": { "status": "pending" },
73
+ "7_visual_qa": { "status": "pending" }
74
+ },
75
+ "started_at": "<iso>",
76
+ "updated_at": "<iso>"
77
+ }
78
+ ```
79
+
80
+ ## Decision gates entre fases
81
+
82
+ Cada gate usa `AskUserQuestion` com 3 opcoes:
83
+ 1. **Aprovar e seguir** -> avanca para proxima fase.
84
+ 2. **Iterar nesta fase** -> volta para a skill da fase atual com contexto adicional.
85
+ 3. **Abortar** -> grava sessao como `aborted` e devolve resumo.
86
+
87
+ ## Pre-flight
88
+
89
+ 1. Se `$ARGUMENTS == "continuar"`: ler `prototype-session.json`, retomar da fase atual.
90
+ 2. Senao: criar nova sessao + chamar fase 1 (idea-intake) com a frase da ideia.
91
+ 3. Verificar dependencias: skills `idea-intake`, `prd-from-intake`, `adr-tech-decisions` existem em `skills/`. Falta -> abortar.
92
+ 4. `--skip-figma` pula fase 4 (caso usuario queira ir direto para codigo).
93
+ 5. `--full-stack` forca perfil B/C no ADR (mesmo que descartavel).
94
+
95
+ ## Execucao
96
+
97
+ Para cada fase pendente:
98
+ 1. Imprimir cabecalho `[fase N/7] <nome>`.
99
+ 2. Invocar skill correspondente via `gos-master` (com comprehension gate).
100
+ 3. Persistir `ref` no session.json apos sucesso.
101
+ 4. Aplicar decision gate.
102
+ 5. Avancar.
103
+
104
+ ## Compressao de contexto entre fases
105
+
106
+ Para evitar inflacao de tokens (problema central do framework):
107
+ - A cada fase, comprimir output da anterior via `gos-compress` rate=0.4 antes de passar para a proxima.
108
+ - Manter snapshot integral em disco (`docs/intake/...`, `docs/prd/...`, etc.) — apenas o contexto in-memory e comprimido.
109
+ - Se `gos-compress` indisponivel: usar resumo manual de 200 palavras + path do arquivo original.
110
+
111
+ ## Regras criticas
112
+
113
+ - **Sempre perguntar (regra do dono)**: cada gate e obrigatorio. Nunca avancar fase sem confirmacao.
114
+ - **Descartavel-first**: se intake marcou descartavel, pular Storybook, E2E, design system robusto.
115
+ - **Anti-loop**: limite de 3 iteracoes por fase. Se exceder, sugerir abortar.
116
+ - **Output rastreavel**: ao final, gerar `docs/prototypes/PROTO-NNN-<slug>/index.md` com links para todos os artefatos.
117
+
118
+ ## Input
119
+
120
+ $ARGUMENTS
@@ -25,6 +25,17 @@
25
25
  { "slug": "progress-tracker", "path": "skills/progress-tracker/SKILL.md" },
26
26
  { "slug": "execute-plan", "path": "skills/execute-plan/SKILL.md" },
27
27
  { "slug": "validate-plan", "path": "skills/validate-plan/SKILL.md" },
28
- { "slug": "audit-screenshots", "path": "skills/audit-screenshots/SKILL.md" }
28
+ { "slug": "audit-screenshots", "path": "skills/audit-screenshots/SKILL.md" },
29
+ { "slug": "idea-intake", "path": "skills/idea-intake/SKILL.md" },
30
+ { "slug": "prd-from-intake", "path": "skills/prd-from-intake/SKILL.md" },
31
+ { "slug": "adr-tech-decisions", "path": "skills/adr-tech-decisions/SKILL.md" },
32
+ { "slug": "prototype-orchestrator", "path": "skills/prototype-orchestrator/SKILL.md" },
33
+ { "slug": "gos-caveman", "path": "skills/gos-caveman/SKILL.md" },
34
+ { "slug": "gos-compress", "path": "skills/gos-compress/SKILL.md" },
35
+ { "slug": "figma-print-diff", "path": "skills/figma-print-diff/SKILL.md" },
36
+ { "slug": "ui-guardrails", "path": "skills/ui-guardrails/SKILL.md" },
37
+ { "slug": "cloudflare-pages-setup", "path": "skills/cloudflare-pages-setup/SKILL.md" },
38
+ { "slug": "typeform-form-pattern", "path": "skills/typeform-form-pattern/SKILL.md" },
39
+ { "slug": "timer-component-pattern", "path": "skills/timer-component-pattern/SKILL.md" }
29
40
  ]
30
41
  }
@@ -0,0 +1,245 @@
1
+ ---
2
+ name: timer-component-pattern
3
+ description: >
4
+ Gera componente Timer (countdown) React + TS com state machine, persistencia,
5
+ atalhos de teclado, edicao inline, tema dark/light, controles via Lucide React.
6
+ Baseado em code-kata-timer adaptado para React.
7
+ argument-hint: "<descricao do uso ou 'spec' para ver anatomia>"
8
+ allowedTools: [Read, Glob, Grep, Bash, Write, Edit, AskUserQuestion]
9
+ sourceDocs:
10
+ - libraries/timer-pattern-spec.md
11
+ - libraries/lucide-icons-policy.md
12
+ - libraries/ui-guardrails-checklist.md
13
+ use-when:
14
+ - usuario pede pomodoro, code kata timer, countdown, contador regressivo
15
+ - precisa de timer com play/pause/reset/edit
16
+ - quer atalhos de teclado (Space, R, E)
17
+ do-not-use-for:
18
+ - cronometro (countup) — variar via `mode: 'countup'` flag
19
+ - timer de polling (use TanStack Query refetchInterval)
20
+ - calendar/scheduler (use componente diferente)
21
+ metadata:
22
+ category: ui-pattern
23
+ ---
24
+
25
+ Voce esta executando como **Timer Pattern Generator** via skill `timer-component-pattern`. Gera componente React baseado em `libraries/timer-pattern-spec.md`.
26
+
27
+ ## Pre-flight
28
+
29
+ 1. AskUserQuestion:
30
+ - "Tempo default em segundos? (ex: 30 para code kata, 1500 para pomodoro 25min)"
31
+ - "Som ao terminar? (Web Audio beep / arquivo / nenhum)"
32
+ - "Tema dark/light togglavel? (sim / so dark / so light)"
33
+ - "Persistir entre sessoes? (localStorage / nao)"
34
+ - "Atalhos teclado? (sim default / nao)"
35
+ - "Editar tempo inline? (sim / nao — botao edit aparece se sim)"
36
+ 2. Validar stack: `lucide-react`, shadcn `Button`. Senao -> instruir instalacao.
37
+
38
+ ## Arquivos gerados
39
+
40
+ ```
41
+ src/components/timer/
42
+ Timer.tsx # componente principal
43
+ hooks/
44
+ useTimer.ts # hook com state machine
45
+ useTimerKeyboard.ts # atalhos
46
+ utils/
47
+ formatTime.ts # MM:SS
48
+ beep.ts # Web Audio (opcional)
49
+ types.ts # TimerStatus, TimerSettings
50
+ ```
51
+
52
+ ## Componente principal
53
+
54
+ ```tsx
55
+ import { useEffect } from 'react';
56
+ import { Play, Pause, RotateCcw, Pencil, Activity } from 'lucide-react';
57
+ import { Button } from '@/components/ui/button';
58
+ import { useTimer } from './hooks/useTimer';
59
+ import { useTimerKeyboard } from './hooks/useTimerKeyboard';
60
+ import { TimerStatus, isRunning } from './types';
61
+ import { formatTime } from './utils/formatTime';
62
+
63
+ interface TimerProps {
64
+ defaultSeconds?: number;
65
+ onComplete?: (duration: number) => void;
66
+ enableEdit?: boolean;
67
+ enableKeyboard?: boolean;
68
+ }
69
+
70
+ export function Timer({
71
+ defaultSeconds = 30,
72
+ onComplete,
73
+ enableEdit = true,
74
+ enableKeyboard = true,
75
+ }: TimerProps) {
76
+ const timer = useTimer(defaultSeconds);
77
+
78
+ useTimerKeyboard({
79
+ enabled: enableKeyboard,
80
+ status: timer.status,
81
+ actions: timer,
82
+ });
83
+
84
+ useEffect(() => {
85
+ function onDone(e: Event) {
86
+ const evt = e as CustomEvent<{ duration: number }>;
87
+ onComplete?.(evt.detail.duration);
88
+ }
89
+ window.addEventListener('timer:done', onDone);
90
+ return () => window.removeEventListener('timer:done', onDone);
91
+ }, [onComplete]);
92
+
93
+ return (
94
+ <div className="flex flex-col items-center justify-center gap-8 py-12">
95
+ {/* Indicator */}
96
+ {isRunning(timer.status) && (
97
+ <Activity className="h-6 w-6 text-primary animate-pulse" aria-label="Em execucao" />
98
+ )}
99
+
100
+ {/* Display */}
101
+ {timer.status === TimerStatus.EDITING ? (
102
+ <input
103
+ type="number"
104
+ autoFocus
105
+ defaultValue={timer.seconds}
106
+ className="text-7xl md:text-9xl font-mono font-bold tabular-nums bg-transparent w-72 text-center outline-none border-b-2 border-primary"
107
+ onBlur={(e) => {
108
+ const v = Number(e.currentTarget.value);
109
+ if (v > 0) timer.setSeconds(v);
110
+ timer.setStatus(TimerStatus.STOPPED);
111
+ }}
112
+ onKeyDown={(e) => {
113
+ if (e.key === 'Enter') e.currentTarget.blur();
114
+ if (e.key === 'Escape') timer.setStatus(TimerStatus.STOPPED);
115
+ }}
116
+ />
117
+ ) : (
118
+ <div
119
+ className="text-7xl md:text-9xl font-mono font-bold tabular-nums"
120
+ aria-live="polite"
121
+ aria-atomic="true"
122
+ >
123
+ {formatTime(timer.seconds)}
124
+ </div>
125
+ )}
126
+
127
+ {/* Controls */}
128
+ <div className="flex gap-3">
129
+ {timer.status === TimerStatus.RUNNING ? (
130
+ <Button onClick={timer.pause} size="lg" aria-label="Pausar">
131
+ <Pause className="h-5 w-5" />
132
+ </Button>
133
+ ) : (
134
+ <Button
135
+ onClick={timer.start}
136
+ size="lg"
137
+ disabled={timer.seconds === 0}
138
+ aria-label="Iniciar"
139
+ >
140
+ <Play className="h-5 w-5" />
141
+ </Button>
142
+ )}
143
+
144
+ <Button
145
+ onClick={() => timer.reset()}
146
+ size="lg"
147
+ variant="outline"
148
+ aria-label="Resetar"
149
+ >
150
+ <RotateCcw className="h-5 w-5" />
151
+ </Button>
152
+
153
+ {enableEdit && (
154
+ <Button
155
+ onClick={() => timer.setStatus(TimerStatus.EDITING)}
156
+ size="lg"
157
+ variant="outline"
158
+ disabled={isRunning(timer.status)}
159
+ aria-label="Editar tempo"
160
+ >
161
+ <Pencil className="h-5 w-5" />
162
+ </Button>
163
+ )}
164
+ </div>
165
+
166
+ {/* Hint atalhos */}
167
+ {enableKeyboard && (
168
+ <p className="text-xs text-muted-foreground">
169
+ Espaco play/pause - R reset - E editar
170
+ </p>
171
+ )}
172
+ </div>
173
+ );
174
+ }
175
+ ```
176
+
177
+ ## State machine (types.ts)
178
+
179
+ Conforme `libraries/timer-pattern-spec.md`:
180
+
181
+ ```ts
182
+ export const TimerStatus = {
183
+ STOPPED: 'STOPPED',
184
+ COUNTDOWN: 'COUNTDOWN',
185
+ RUNNING: 'RUNNING',
186
+ PAUSED: 'PAUSED',
187
+ EDITING: 'EDITING',
188
+ } as const;
189
+
190
+ export type TimerStatusType = typeof TimerStatus[keyof typeof TimerStatus];
191
+
192
+ export const isRunning = (s: TimerStatusType) =>
193
+ s === TimerStatus.RUNNING || s === TimerStatus.COUNTDOWN;
194
+ ```
195
+
196
+ ## Hook useTimer (com persistencia opcional)
197
+
198
+ Detalhe completo em `libraries/timer-pattern-spec.md`. Pontos criticos:
199
+ - `setInterval` com cleanup em `useEffect` return + ao parar.
200
+ - Ao restaurar `localStorage`: SEMPRE forcar status STOPPED (evita timer fantasma).
201
+ - Dispatch `CustomEvent('timer:done')` ao zerar.
202
+
203
+ ## Estados visuais
204
+
205
+ - **Loading**: NA.
206
+ - **Empty**: tempo `00:00` -> Play disabled, mensagem "Tempo zerado, edite para comecar".
207
+ - **Error**: NA em runtime.
208
+ - **Running**: `Activity` icon pulsando + `animate-pulse` no display opcional.
209
+ - **Done**: tela ou toast com `CheckCircle2` quando atinge 0.
210
+
211
+ ## Som ao terminar (utils/beep.ts)
212
+
213
+ Web Audio API (sem assets):
214
+ ```ts
215
+ export function beep(frequency = 800, duration = 200) {
216
+ const ctx = new (window.AudioContext || (window as any).webkitAudioContext)();
217
+ const osc = ctx.createOscillator();
218
+ osc.connect(ctx.destination);
219
+ osc.frequency.value = frequency;
220
+ osc.start();
221
+ setTimeout(() => { osc.stop(); ctx.close(); }, duration);
222
+ }
223
+ ```
224
+
225
+ Disparado em `useEffect` quando `seconds === 0 && previousSeconds !== 0`.
226
+
227
+ ## Modos disponiveis
228
+
229
+ Flags do componente:
230
+ - `mode="countdown"` (default) — diminui ate 0.
231
+ - `mode="countup"` — sobe a partir de 0 (cronometro).
232
+ - `mode="pomodoro"` — alterna 25min foco / 5min pausa.
233
+
234
+ Cada modo so muda a logica do hook, UI e controles ficam iguais.
235
+
236
+ ## Anti-patterns embutidos (warning ao usuario)
237
+
238
+ - "Sem cleanup de setInterval" -> recusar — gera memory leak.
239
+ - "Restaurar status RUNNING do localStorage" -> recusar — timer fantasma.
240
+ - "Emoji nos botoes" -> recusar — usar Lucide.
241
+ - "Sem `tabular-nums`" -> warning — digitos pulam visualmente.
242
+
243
+ ## Input
244
+
245
+ $ARGUMENTS
@@ -0,0 +1,210 @@
1
+ ---
2
+ name: typeform-form-pattern
3
+ description: >
4
+ Gera estrutura de form passo-a-passo estilo Typeform (uma pergunta por vez), com
5
+ questions[] tipadas, hooks de navegacao, validacao Zod, animacao, progress bar e
6
+ estados visuais. Lucide React (zero emoji). React + TS + Tailwind + shadcn.
7
+ argument-hint: "<descricao do form ou 'spec' para ver a anatomia>"
8
+ allowedTools: [Read, Glob, Grep, Bash, Write, Edit, AskUserQuestion]
9
+ sourceDocs:
10
+ - libraries/typeform-pattern-spec.md
11
+ - libraries/lucide-icons-policy.md
12
+ - libraries/ui-guardrails-checklist.md
13
+ use-when:
14
+ - usuario pede form de cadastro/onboarding/quiz/lead/survey
15
+ - precisa coletar dados em multiplas etapas com baixo cognitive load
16
+ - mobile-first form
17
+ do-not-use-for:
18
+ - settings page com varios campos editaveis simultaneamente (use form tradicional)
19
+ - input simples 1-2 campos (overhead nao compensa)
20
+ metadata:
21
+ category: ui-pattern
22
+ ---
23
+
24
+ Voce esta executando como **Typeform Pattern Generator** via skill `typeform-form-pattern`. Gera estrutura completa baseada em `libraries/typeform-pattern-spec.md`.
25
+
26
+ ## Pre-flight
27
+
28
+ 1. Pergunta ao usuario:
29
+ - "Quantas perguntas previstas?" (estimar — pode evoluir)
30
+ - "Salvar progresso entre sessoes? (sessionStorage / Supabase / nao)"
31
+ - "Tem tela de Welcome inicial? E de Review final?"
32
+ - "Submit final manda pra Supabase, Workers, ou apenas mostra resultado?"
33
+ 2. Validar stack: deve existir `tailwind.config`, `lucide-react` no package.json, shadcn `Button`, `Input`, `Card`, `Label`. Senao -> propor instalar.
34
+ 3. Aplicar `libraries/lucide-icons-policy.md` — zero emoji, todos icones via Lucide.
35
+
36
+ ## Geracao
37
+
38
+ ### Arquivos criados
39
+
40
+ ```
41
+ src/components/<feature>/
42
+ questions.ts # tipos + array de questions
43
+ TypeformContainer.tsx # estado + navegacao
44
+ questions/
45
+ WelcomeQuestion.tsx
46
+ TextQuestion.tsx
47
+ NumberQuestion.tsx
48
+ ChoiceQuestion.tsx
49
+ MultiChoiceQuestion.tsx
50
+ ReviewQuestion.tsx
51
+ hooks/
52
+ useTypeformFlow.ts
53
+ schemas/
54
+ formSchema.ts # Zod
55
+ ```
56
+
57
+ ### Estrutura base de `questions.ts`
58
+
59
+ ```ts
60
+ import type { ComponentType } from 'lucide-react';
61
+ import * as Icons from 'lucide-react';
62
+
63
+ export type QuestionType =
64
+ | 'welcome' | 'text' | 'number' | 'choice' | 'multiChoice' | 'date' | 'review';
65
+
66
+ export interface QuestionOption {
67
+ value: string;
68
+ label: string;
69
+ description?: string;
70
+ iconName?: keyof typeof Icons;
71
+ }
72
+
73
+ export interface Question<F = Record<string, unknown>> {
74
+ id: string;
75
+ type: QuestionType;
76
+ field: keyof F | null;
77
+ question: string | ((data: F) => string);
78
+ description?: string;
79
+ placeholder?: string;
80
+ suffix?: string;
81
+ decimal?: boolean;
82
+ buttonText?: string;
83
+ options?: QuestionOption[];
84
+ validate?: (value: unknown, data: F) => string | null;
85
+ showWhen?: (data: F) => boolean;
86
+ }
87
+
88
+ // Exemplo:
89
+ export const questions: Question<FormData>[] = [
90
+ {
91
+ id: 'welcome',
92
+ type: 'welcome',
93
+ field: null,
94
+ question: 'Vamos comecar',
95
+ description: 'Sera rapido — leva 2 minutos.',
96
+ buttonText: 'Comecar',
97
+ },
98
+ {
99
+ id: 'name',
100
+ type: 'text',
101
+ field: 'name',
102
+ question: 'Como podemos te chamar?',
103
+ placeholder: 'Seu nome',
104
+ validate: (v) => (!v ? 'Campo obrigatorio' : null),
105
+ },
106
+ // ...
107
+ ];
108
+ ```
109
+
110
+ ### Hook `useTypeformFlow`
111
+
112
+ Encapsula:
113
+ - `currentIndex`, `setCurrentIndex`
114
+ - `data`, `updateField`
115
+ - `activeQuestions` (filtrado por `showWhen`)
116
+ - `currentQ`
117
+ - `handleNext` (com validacao)
118
+ - `handlePrevious`
119
+ - `handleKeyPress` (Enter, Esc)
120
+ - `progress` (% concluido)
121
+ - Persistencia opcional (sessionStorage ou Supabase)
122
+
123
+ ### Container
124
+
125
+ ```tsx
126
+ export function TypeformContainer({ onComplete }: Props) {
127
+ const flow = useTypeformFlow(questions);
128
+
129
+ return (
130
+ <div className="min-h-screen flex flex-col">
131
+ <ProgressBar value={flow.progress} />
132
+
133
+ <main className="flex-1 flex items-center justify-center px-4">
134
+ <div className={`max-w-2xl w-full transition-all duration-300 ${
135
+ flow.isAnimating ? 'opacity-0 translate-y-4' : 'opacity-100 translate-y-0'
136
+ }`}>
137
+ <QuestionRenderer question={flow.currentQ} flow={flow} />
138
+ </div>
139
+ </main>
140
+
141
+ <NavButtons flow={flow} />
142
+ </div>
143
+ );
144
+ }
145
+ ```
146
+
147
+ ### Estados visuais (UI guardrails)
148
+
149
+ Aplicar `libraries/ui-guardrails-checklist.md`:
150
+
151
+ - **Loading** (submitting): botao final disabled + `<Loader2 className="h-4 w-4 animate-spin mr-2" />`.
152
+ - **Empty** (questions = []): tela "Nenhuma pergunta configurada" com `<AlertCircle />`.
153
+ - **Error**: inline acima do CTA primario.
154
+ - **Success**: tela final com `<CheckCircle2 className="h-16 w-16 text-success" />` + redirect/proximos passos.
155
+ - **Skeleton**: nao aplicavel (form nao tem fetch inicial geralmente).
156
+
157
+ ### Validacao com Zod
158
+
159
+ ```ts
160
+ // schemas/formSchema.ts
161
+ import { z } from 'zod';
162
+
163
+ export const formSchema = z.object({
164
+ name: z.string().min(2, 'Minimo 2 caracteres'),
165
+ email: z.string().email('Email invalido'),
166
+ age: z.coerce.number().int().min(13, 'Minimo 13 anos').max(120),
167
+ // ...
168
+ });
169
+
170
+ export type FormData = z.infer<typeof formSchema>;
171
+ ```
172
+
173
+ Chamar no submit final:
174
+ ```ts
175
+ const result = formSchema.safeParse(data);
176
+ if (!result.success) {
177
+ // mostrar primeiro erro inline
178
+ }
179
+ ```
180
+
181
+ ### Acessibilidade obrigatoria
182
+
183
+ - `aria-live="polite"` no container que troca de question.
184
+ - `aria-label` em botoes voltar / Lucide-icon-only.
185
+ - `useEffect` foca o input ao mudar de question.
186
+ - Roles ARIA em listas de opcoes (`role="radiogroup"` para choice).
187
+
188
+ ### Atalhos teclado
189
+
190
+ - `Enter` -> next.
191
+ - `Esc` ou `Backspace` (em campo vazio) -> previous.
192
+ - Em multiChoice: setas para navegar opcoes, espaco para toggle.
193
+
194
+ ## Customizacao por usuario
195
+
196
+ Apos gerar estrutura base, AskUserQuestion:
197
+ 1. "Cor primaria? (default: shadcn primary)"
198
+ 2. "Animacao de transicao? (fade / slide / nenhuma)"
199
+ 3. "Progress bar: top, bottom, ou nao?"
200
+ 4. "Permite voltar e editar respostas? (sim/nao)"
201
+
202
+ ## Anti-patterns embutidos (gera com warning se usuario insistir)
203
+
204
+ - "Quero todas as perguntas em uma tela" -> form tradicional, nao typeform-pattern.
205
+ - "Sem progress bar" -> warning, mas gera (UX ruim em mobile).
206
+ - "Emoji nas opcoes" -> recusar, usar Lucide.
207
+
208
+ ## Input
209
+
210
+ $ARGUMENTS