product-runner 0.5.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 (37) hide show
  1. package/README.md +165 -0
  2. package/common/agents/README.md +68 -0
  3. package/common/agents/agente-conceituacao.md +141 -0
  4. package/common/agents/agente-documentacao-funcional.md +107 -0
  5. package/common/agents/agente-gerador-spec.md +106 -0
  6. package/common/agents/agente-kickoff.md +121 -0
  7. package/common/agents/agente-prod-runner.md +107 -0
  8. package/common/agents/agente-review-code.md +97 -0
  9. package/common/agents/agente-review-llm.md +94 -0
  10. package/common/agents/agente-review-product.md +98 -0
  11. package/common/agents/agente-user-review.md +99 -0
  12. package/common/agents/protocolo-de-gates.md +51 -0
  13. package/common/claude-md.template.md +210 -0
  14. package/common/design-principles.md +229 -0
  15. package/common/lessons-learned.md +440 -0
  16. package/common/pipeline.md +143 -0
  17. package/common/spec-guide.md +327 -0
  18. package/common/specs/_open-issues.md +46 -0
  19. package/common/specs/_overview.md +75 -0
  20. package/dist/cli.js +187 -0
  21. package/dist/migrations.js +147 -0
  22. package/dist/scaffold.js +276 -0
  23. package/dist/update.js +400 -0
  24. package/migrations/0.3.0.md +54 -0
  25. package/migrations/0.4.0.md +76 -0
  26. package/migrations/0.5.0.md +55 -0
  27. package/migrations/README.md +68 -0
  28. package/package.json +41 -0
  29. package/profile-cli/README.md +54 -0
  30. package/profile-cli/claude-md.extension.md +102 -0
  31. package/profile-cli/code-patterns.md +363 -0
  32. package/profile-ssr/DESIGN-SYSTEM.md +795 -0
  33. package/profile-ssr/README.md +51 -0
  34. package/profile-ssr/api-patterns.md +70 -0
  35. package/profile-ssr/claude-md.extension.md +113 -0
  36. package/profile-ssr/code-patterns.md +175 -0
  37. package/profile-ssr/ui-patterns.md +97 -0
@@ -0,0 +1,327 @@
1
+ # Spec guide
2
+
3
+ Como ler, escrever e implementar specs neste projeto. Este guia é a
4
+ **referência de formato e critérios** da spec: o template, os critérios
5
+ meta, a granularidade, as regras de implementação e o review.
6
+
7
+ A **geração** das specs (cortar um incremento conceituado em N specs
8
+ verticais) é o [agente-gerador-spec](./agents/agente-gerador-spec.md), que consome este guia e o preenche
9
+ a partir dos artefatos a montante — ver [pipeline](./pipeline.md). Para mudanças
10
+ pequenas ou projetos sem conceituação formal, escreva a spec direto neste
11
+ template (o pipeline inteiro não é obrigatório — ver "Fixes vs specs").
12
+
13
+ ---
14
+
15
+ ## O que é uma spec
16
+
17
+ Definição completa de uma mudança: contexto, dependências, entrega,
18
+ arquivos afetados, regras, não-objetivos e critérios binários
19
+ de "pronto".
20
+
21
+ Spec é **contrato**, não tutorial. Diz **O QUE** muda, não **COMO**
22
+ implementar linha a linha — exceto quando o "como" é parte da
23
+ decisão (ex: "use Object.setPrototypeOf, não factory fromPlain").
24
+
25
+ ---
26
+
27
+ ## Onde vivem
28
+
29
+ ```
30
+ specs/
31
+ ├── _overview.md ← roadmap, fases, dependências
32
+ ├── setup/ ← infra, hardening, ferramentas
33
+ │ ├── 00-hardening.md
34
+ │ ├── 01-test-harness.md
35
+ │ └── ...
36
+ ├── refactor/ ← reorganização do código existente
37
+ │ ├── 03-pure-logic.md
38
+ │ └── ...
39
+ └── {domínio}/ ← features novas (quando houver)
40
+ ```
41
+
42
+ Numeração indica ordem dentro do domínio. Domínios são estáveis;
43
+ fases podem mudar.
44
+
45
+ ---
46
+
47
+ ## Template
48
+
49
+ ```markdown
50
+ # {Domínio} — {Nome da mudança}
51
+
52
+ ## Contexto
53
+
54
+ Por que esta spec existe. Problema que resolve. Link pra spec
55
+ anterior se depender.
56
+
57
+ ## Depende de
58
+
59
+ - `{domínio}/NN-{spec anterior}` (se houver)
60
+
61
+ ## Entrega
62
+
63
+ O que funciona ao final. Visão de fora — o que muda no
64
+ comportamento ou na estrutura.
65
+
66
+ ## Entities envolvidas
67
+
68
+ Schemas, types, classes novos ou alterados. Sem código
69
+ completo — só assinaturas e decisões.
70
+
71
+ ## Mudanças por arquivo
72
+
73
+ Arquivo a arquivo, o que muda. Aceita pseudocódigo / blocos
74
+ curtos quando a decisão precisa ser explícita.
75
+
76
+ ## Regras de negócio
77
+
78
+ Validações, limites, edge cases. O que NÃO pode acontecer.
79
+
80
+ ## Não-objetivos
81
+
82
+ O que esta spec NÃO faz, mesmo que apareça tentação. Cita
83
+ spec futura se for fazer depois.
84
+
85
+ ## Critérios de aceite
86
+
87
+ - [ ] Cada item é binário: passa ou não passa.
88
+ - [ ] Verificável por comando, observação direta ou diff.
89
+
90
+ ## Notas pra implementação
91
+
92
+ Guardrails específicos: ferramentas, alternativas descartadas,
93
+ ordem sugerida, tradeoffs.
94
+
95
+ ## Decisões de implementação
96
+
97
+ (Preencher após implementação — divergências, escolhas, tentações
98
+ não feitas. Esta seção é OBRIGATÓRIA, não opcional.)
99
+ ```
100
+
101
+ ---
102
+
103
+ ## Critérios meta padrão
104
+
105
+ Toda spec aplica estes critérios **além** dos específicos listados
106
+ na própria spec. Não precisam ser repetidos em cada spec — esta
107
+ seção é a referência canônica. Specs referenciam por nome
108
+ ("aplicam-se também os critérios meta padrão M1, M2, M3").
109
+
110
+ Estes critérios existem porque regras textuais em `CLAUDE.md`
111
+ e neste guia foram observadas como insuficientes — Claude Code
112
+ lê uma vez e tende a esquecer durante a sessão. Critério binário
113
+ em checklist vence atenção.
114
+
115
+ > **Relação com os gates.** M1-M3 são a **aplicação à etapa de spec** do
116
+ > mesmo princípio do [protocolo-de-gates](./agents/protocolo-de-gates.md) (fonte canônica de gate e
117
+ > calibragem por stakes): checklist binário vence atenção textual. O
118
+ > protocolo governa os gates dos estágios a montante (conceituação,
119
+ > doc-funcional, geração); M1-M3 governam o fechamento da implementação.
120
+ > Em specs de UI, aplica-se também **M4** (responsividade — detalhe em
121
+ > `ui-patterns.md` do perfil SSR). Specs referenciam por nome:
122
+ > "aplicam-se M1, M2, M3" (e M4 quando houver UI).
123
+
124
+ ### M1 — Decisões de implementação preenchidas
125
+
126
+ - [ ] A seção `## Decisões de implementação` da spec contém ao
127
+ menos um parágrafo descrevendo: - Divergências da spec original (se houve). - Escolhas entre alternativas técnicas (qual e por quê). - Tentações de escopo NÃO executadas. - Status final (✅ / ⚠️ / ❌).
128
+ - [ ] Seção vazia ou só template original = critério **não** atendido.
129
+
130
+ ### M2 — Decisões técnicas explicitadas durante a sessão
131
+
132
+ - [ ] Cada escolha não-óbvia entre alternativas (cast vs guard,
133
+ qual lib, mock vs fixture, signature, etc.) aparece num
134
+ thinking block ou comentário durante a sessão.
135
+ - [ ] Reportada no resumo final junto com a justificativa.
136
+ - [ ] Decisões silenciosas (escolha tomada sem rastro) violam
137
+ este critério.
138
+
139
+ ### M3 — Release checklist explícito antes de declarar pronto
140
+
141
+ - [ ] Antes de reportar conclusão, percorrer cada critério de
142
+ aceite da spec e marcar ✅ / ❌ / ⚠️ explicitamente
143
+ (incluindo os meta).
144
+ - [ ] Reportar a varredura no resumo final.
145
+ - [ ] Smoke checks marcados como "pendente humano" exigem nota
146
+ curta explicando por que não foi feito automaticamente
147
+ (ex: precisa de credenciais reais, exige observação visual).
148
+
149
+ ---
150
+
151
+ ## Princípios
152
+
153
+ ### Spec é contrato, não tutorial
154
+
155
+ Regra: dizer **O QUE**, não **COMO** — exceto quando o COMO é
156
+ a essência da decisão.
157
+
158
+ Exemplos:
159
+
160
+ - ✅ "Validar `BINANCE_API_KEY` no boot. Se ausente, fail-fast com mensagem clara."
161
+ - ❌ "Linha 17, adicionar `if (!process.env.BINANCE_API_KEY) throw new Error(...)`."
162
+ - ✅ "Use `Object.setPrototypeOf`, não factory `fromPlain` — escolha justificada na nota X." (o COMO é parte da decisão)
163
+
164
+ ### Granularidade: uma spec = uma sessão
165
+
166
+ Spec entre **80-150 linhas**. Mais curta vira fix; mais longa
167
+ quebra em duas.
168
+
169
+ Se ao escrever a spec ela passa de 150 linhas, sinal de que tem
170
+ duas mudanças disfarçadas de uma. Separar.
171
+
172
+ ### Critérios binários
173
+
174
+ Cada critério de aceite é verificável por comando ou observação
175
+ direta. Sem "o sistema funciona corretamente".
176
+
177
+ Exemplos:
178
+
179
+ - ✅ `git grep -E '(eNls3mjIoTZ|vIE3NutkqUq6)'` retorna vazio.
180
+ - ✅ Rodar `npm test` retorna `Tests passed: 12`.
181
+ - ❌ "O bot está estável."
182
+ - ❌ "Erros são tratados de forma robusta."
183
+
184
+ ### Escopo travado por não-objetivos
185
+
186
+ Sempre listar "Não-objetivos" antes do checklist. LLM tende a
187
+ fazer mais que pedido — lista de "não fazer" ancora.
188
+
189
+ Itens típicos: "Não migrar X agora", "Não tocar em Y", "Não fazer
190
+ fix do bug Z (vai em refactor/03)".
191
+
192
+ ### Fases verticais, não horizontais
193
+
194
+ Cada spec deve entregar **valor visível de ponta a ponta** dentro
195
+ do seu escopo, não construir uma camada inteira pra próxima
196
+ spec usar.
197
+
198
+ - ❌ Horizontal: spec só de schemas, depois só de services, depois só de testes.
199
+ - ✅ Vertical: spec de "test harness com 1ª cobertura de WavesHistory" — entrega
200
+ schemas + setup + 1 teste rodando.
201
+
202
+ ---
203
+
204
+ ## Workflow
205
+
206
+ Este é o **trecho de implementação** do método (spec→implementa→review).
207
+ Quando o projeto passa pelo pipeline completo, a "escrita da spec" é o
208
+ [agente-gerador-spec](./agents/agente-gerador-spec.md) cortando o incremento conceituado — ver [pipeline](./pipeline.md).
209
+ Em mudança pequena, a spec é escrita direto neste template.
210
+
211
+ ### Quem faz o quê
212
+
213
+ | Etapa | Onde | Quem |
214
+ | ------------------------- | -------------------------- | ----------------------------------- |
215
+ | Análise, gap, decisão | Cowork (esta sessão) | LLM + você |
216
+ | Escrita da spec | Cowork | LLM ([agente-gerador-spec](./agents/agente-gerador-spec.md) no pipeline) + validação sua |
217
+ | Implementação | Claude Code (outra sessão) | LLM em sessão dedicada |
218
+ | Review | Cowork (volta aqui) | LLM + você |
219
+ | Decisões de implementação | Cowork (review preenche) | LLM + você |
220
+
221
+ ### Ciclo
222
+
223
+ 1. Cowork escreve spec → grava em `specs/.../NN-nome.md`.
224
+ 2. Você abre Claude Code apontando pro repo.
225
+ 3. Pede ao Code: "implementa `specs/.../NN-nome.md`".
226
+ 4. Code implementa, reporta no chat.
227
+ 5. Você traz o report de volta pra Cowork.
228
+ 6. Cowork revisa contra critérios, preenche "Decisões de implementação".
229
+ 7. Cowork escreve próxima spec.
230
+
231
+ ---
232
+
233
+ ## Como o Claude Code lê
234
+
235
+ Ordem sugerida (ele conhece esse padrão):
236
+
237
+ 1. Ler a spec INTEIRA antes de começar.
238
+ 2. Verificar "Depende de" — se a dependência não está implementada, parar.
239
+ 3. Ler `docs/` relevantes (referenciados na spec ou no CLAUDE.md).
240
+ 4. Implementar mudanças por arquivo.
241
+ 5. Rodar critérios de aceite localmente — confirmar cada um.
242
+ 6. Reportar:
243
+ - Cada critério ✅ ou ❌.
244
+ - Decisões de implementação (escolhas + tradeoffs).
245
+ - Notas: o que não testou, tentações que apareceram, divergências.
246
+
247
+ ### Regras operacionais durante implementação
248
+
249
+ - **3 strikes:** se uma abordagem falhou 3 vezes, parar e relatar.
250
+ Não entrar em loop tentando variações.
251
+ - **Spec vs realidade:** se a spec tem tradeoff técnico significativo
252
+ na implementação, parar e apresentar — spec é guia, não lei.
253
+ - **Menor superfície:** preferir solução que altera menos arquivos.
254
+ - **Não adivinhar:** se a spec não cobre, perguntar.
255
+ - **Verificar side-effects no top-level antes de prescrever import
256
+ em teste.** Ao escrever spec que importa código existente pra
257
+ testar, checar primeiro se o módulo de origem tem side-effects no
258
+ top-level (`process.exit`, `new Client(...)`, `run().catch(...)`,
259
+ etc.). Se tiver, **não prescrever apenas `export`** — não funciona.
260
+ Opções: (a) extrair função pra arquivo próprio (vira refactor,
261
+ amplia escopo da spec); (b) adiar teste pra spec posterior. Não há
262
+ solução barata via mock que valha o investimento dos 3 strikes.
263
+ _Lição extraída de `setup/01-test-harness` no tradeBot, onde a
264
+ spec assumiu que `export` em função de `index.ts` bastava._
265
+
266
+ - **Mudanças adjacentes vão pra outra spec.** Quando a implementação
267
+ expõe um problema que NÃO faz parte da intenção da spec atual
268
+ (bug em código adjacente, refactor tentador, melhoria não-pedida),
269
+ **não corrigir na mesma spec**. Anotar em "Decisões de
270
+ implementação" como achado, abrir spec separada se for relevante.
271
+
272
+ Exemplos:
273
+ - Spec de testes expõe que `WavesHistory.ticPrice` tem
274
+ comportamento esquisito num edge case → o teste reflete
275
+ o comportamento ATUAL (caracterização), fix vai em outra spec.
276
+ - Spec de refactor de service expõe um bug aritmético → anota,
277
+ não corrige junto.
278
+ - Spec de Zod expõe que um campo nullable nunca é nulo na
279
+ prática → anota como candidato a `.nonNullable()`, não
280
+ muda agora.
281
+
282
+ Razão: misturar correção com a intenção principal reduz o
283
+ binário "passou/falhou" da spec, esconde escopo, e confunde
284
+ rastreabilidade. O bug visto AGORA continua visível DEPOIS — anotar
285
+ é suficiente pra não se perder.
286
+
287
+ ---
288
+
289
+ ## Como o review funciona
290
+
291
+ Trazer o report do Code de volta pra Cowork. A revisão:
292
+
293
+ 1. Cruza critérios da spec com o estado real do código (não apenas
294
+ o report).
295
+ 2. Identifica divergências silenciosas (coisas mudadas que não foram
296
+ reportadas).
297
+ 3. Documenta na seção "Decisões de implementação" da spec — não
298
+ em mensagem solta.
299
+ 4. Se houver fix necessário pequeno, faz inline. Se for grande,
300
+ abre nova spec.
301
+
302
+ A seção "Decisões de implementação" é OBRIGATÓRIA. Spec sem ela
303
+ preenchida = spec incompleta.
304
+
305
+ ---
306
+
307
+ ## Fixes vs specs
308
+
309
+ Nem tudo precisa de spec.
310
+
311
+ | Situação | Spec? |
312
+ | ------------------------------------------------------ | -------------------------- |
313
+ | Adicionar entity, schema, service novo | Sim |
314
+ | Refactor estrutural (mover código entre pastas) | Sim |
315
+ | Mudar comportamento de cálculo / regra de trade | Sim |
316
+ | Bug menor (typo, parêntese errado, condição invertida) | Não |
317
+ | Ajuste de log / mensagem | Não |
318
+ | Atualização de dependência | Não (a menos que mude API) |
319
+
320
+ Regra: se altera contrato (schema, função pública, comportamento
321
+ observável de fora), spec. Se é correção do que já deveria funcionar,
322
+ fix direto.
323
+
324
+ ---
325
+
326
+ _Este guia é vivo. Padrões entram aqui quando aparecem na prática,
327
+ não por importação de "boas práticas" genéricas._
@@ -0,0 +1,46 @@
1
+ # Open issues
2
+
3
+ Achados detectados durante implementação que NÃO foram endereçados
4
+ na spec onde apareceram, conforme regra "mudanças adjacentes vão
5
+ pra outra spec" do [spec-guide](./spec-guide.md).
6
+
7
+ Cada item lista: contexto, descrição, impacto, e candidato a spec
8
+ onde será endereçado. Sem candidato explícito = lixo acumulando.
9
+
10
+ ---
11
+
12
+ ## Template de issue novo
13
+
14
+ ```markdown
15
+ ## #N — {Título curto e descritivo}
16
+
17
+ **Detectado em:** `{spec ou contexto onde apareceu}`
18
+ **Arquivo(s):** `path/to/file.ts`
19
+
20
+ {Descrição do problema. Linha de código se aplicável,
21
+ diagnóstico do que está errado, comportamento observado.}
22
+
23
+ **Impacto em produção:** {zero / médio / alto — descrição concreta}
24
+
25
+ **Candidato a endereçamento:** `{spec planejada}` — {motivo
26
+ de não fazer agora}.
27
+ ```
28
+
29
+ ---
30
+
31
+ ## Template de issue fechado
32
+
33
+ ```markdown
34
+ ## #N — {título original} ✅ FECHADA
35
+
36
+ **Fechada em:** `{spec que resolveu}`, {data} — {resumo do fix}.
37
+ ```
38
+
39
+ ---
40
+
41
+ *Adicionar aqui qualquer achado durante implementação que não
42
+ caiba na spec atual. Cada item DEVE ter "Candidato a endereçamento"
43
+ explícito — sem isso, vira lixo acumulado.*
44
+
45
+ *Ao fechar issue, mantém o conteúdo anterior + adiciona linha
46
+ "✅ FECHADA" + spec + resumo. Histórico preservado.*
@@ -0,0 +1,75 @@
1
+ # Overview das specs
2
+
3
+ > **Template.** Mapa das specs do projeto: roadmap, dependências, estado.
4
+ > Gerado e mantido junto com o gerador de spec (ver [pipeline](./pipeline.md)).
5
+ > Substitua os `{placeholders}` pelos do projeto.
6
+ >
7
+ > **Índice derivado, não fonte.** Este mapa é conveniência e **drift-a**. O
8
+ > estado real de cada spec é o **conteúdo da spec** (critérios marcados,
9
+ > veredito de review). Não responda "qual a próxima etapa" a partir desta
10
+ > tabela — confirme na spec. Mesmo princípio LDoc→HDoc. Como descobrir a
11
+ > etapa pelo rastro: [pipeline](./pipeline.md) ("Em que estágio estou?").
12
+
13
+ ## Princípio de organização
14
+
15
+ Roadmap por **corte vertical**: cada unidade entrega valor visível de
16
+ ponta a ponta (dados → lógica → interface), não por camada horizontal
17
+ (todos os models → todos os services → toda a UI).
18
+
19
+ Duas formas, conforme o projeto:
20
+
21
+ - **Por incremento de produto** — projetos que passam pelo pipeline de
22
+ conceituação. Cada incremento é uma fatia de valor (1+ casos de uso),
23
+ decomposto em N specs verticais pelo [agente-gerador-spec](./agents/agente-gerador-spec.md).
24
+ - **Por fase/domínio** — projetos menores, sem conceituação formal. Specs
25
+ agrupadas por domínio estável; numeração indica ordem.
26
+
27
+ Specs vivem em `specs/{domínio}/NN-nome.md`. Domínios são estáveis; o
28
+ roadmap é estável-mas-não-congelado (re-entry pode reordenar à luz do que
29
+ se aprende).
30
+
31
+ ## Fundação (`setup/`)
32
+
33
+ | Spec | Entrega | Status |
34
+ | --- | --- | --- |
35
+ | `setup/00-{bootstrap}` | {infra base, tooling} | ⏳ pendente |
36
+ | `setup/01-{...}` | {...} | ⏳ pendente |
37
+
38
+ ## Incremento 1 — {nome} _(ou: Fase 1 — {nome})_
39
+
40
+ **Valor:** {o que entrega de ponta a ponta}. Cobre {UCs / escopo}.
41
+
42
+ | Spec | Entrega | Depende de | Status |
43
+ | --- | --- | --- | --- |
44
+ | `{domínio}/01-{...}` | {fatia vertical com resultado visível} | `setup/...` | ⏳ pendente |
45
+ | `{domínio}/02-{...}` | {...} | `{domínio}/01` | ⏳ pendente |
46
+ | `{domínio}/03-{...}` | {fecha o ciclo mínimo} | `{domínio}/01` | ⏳ pendente |
47
+
48
+ ## Incrementos/Fases seguintes (baixa resolução)
49
+
50
+ Detalhados só na vez deles (re-entry da conceituação, se aplicável).
51
+
52
+ | # | Nome | Valor | UCs |
53
+ | --- | --- | --- | --- |
54
+ | 2 | {...} | {...} | {...} |
55
+ | 3 | {...} | {...} | {...} |
56
+
57
+ ## Grafo de dependências
58
+
59
+ ```
60
+ setup/00 → setup/01 → {incremento/fase 1}
61
+ ↘ {incremento/fase 2}
62
+ {incremento 1} → {o que depende dele}
63
+ ```
64
+
65
+ ## Resumo das entities
66
+
67
+ | Entity | Domínio | Descrição |
68
+ | --- | --- | --- |
69
+ | {...} | {...} | {...} |
70
+
71
+ ---
72
+
73
+ _Documento vivo. Atualizar ao implementar specs e ao detalhar novos
74
+ incrementos. Dependências são duras: spec que depende de outra não
75
+ implementada → parar e reportar (regra do [spec-guide](./spec-guide.md))._
package/dist/cli.js ADDED
@@ -0,0 +1,187 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from "node:util";
3
+ import { resolve } from "node:path";
4
+ import { scaffold, initProject, ENTRY_AGENT, } from "./scaffold.js";
5
+ import { update, renderPlan, HANDOFF_DIR } from "./update.js";
6
+ const HELP = `product-runner — scaffold de docs para projetos TS com AI
7
+
8
+ Uso:
9
+ npx product-runner init [--dir <path>] [--force]
10
+ npx product-runner --name <nome> --profile <cli|ssr> [opções]
11
+ npx product-runner update [--dir <path>] [--dry-run] [opções]
12
+
13
+ Comandos:
14
+ init Coloca os agentes de bootstrap (${ENTRY_AGENT} +
15
+ agente-kickoff.md) na raiz. Peça a uma LLM para ler
16
+ ${ENTRY_AGENT} e seguir — ele roteia o resto.
17
+ update Atualiza docs/ + CLAUDE.md de um projeto existente contra
18
+ a versão atual dos templates (adiciona / auto-merge /
19
+ revisar). Usa o manifesto se houver; senão, modo legado.
20
+ (sem comando) Gera docs/ + CLAUDE.md (o scaffold em si).
21
+
22
+ Opções (scaffold):
23
+ --name <string> Nome do projeto (obrigatório). Substitui {PROJECT_NAME}.
24
+ --profile <cli|ssr> Perfil de templates (obrigatório).
25
+ --dir <path> Diretório alvo. Default: "." (atual).
26
+ --port <number> Porta default; substitui {PORT}. Default: 3000.
27
+ --force Sobrescreve arquivos existentes.
28
+ -h, --help Mostra esta ajuda.
29
+
30
+ Opções (update):
31
+ --dir <path> Diretório do projeto. Default: "." (atual).
32
+ --dry-run Só imprime o plano; não escreve nada.
33
+ --profile <cli|ssr> Perfil (obrigatório se o projeto não tiver manifesto).
34
+ --normalize-links Converte links dos arquivos novos pro estilo do projeto.
35
+ --only <glob> Limita a ação aos paths que casam (ex.: "docs/agents/**").
36
+ --no-format-normalize Não normaliza formatação (Prettier) antes de comparar.
37
+
38
+ Fluxo recomendado:
39
+ 1. npx product-runner init
40
+ 2. Peça à sua LLM: "leia ${ENTRY_AGENT} e siga as instruções".
41
+
42
+ Exemplo (scaffold direto):
43
+ npx product-runner --name meu-app --profile ssr --port 3000 --dir .
44
+
45
+ Exemplo (update):
46
+ npx product-runner update --dry-run
47
+ `;
48
+ function fail(msg) {
49
+ console.error(`Erro: ${msg}\n`);
50
+ console.error(HELP);
51
+ process.exit(1);
52
+ }
53
+ async function runInit(dir, force) {
54
+ const targetDir = resolve(dir);
55
+ try {
56
+ const { files } = await initProject({ targetDir, force });
57
+ console.log(`✔ Agentes de bootstrap criados (${files.length}):`);
58
+ for (const f of files)
59
+ console.log(` ${f}`);
60
+ console.log("\nPróximo passo:");
61
+ console.log(` Abra sua LLM (Claude Code/Cowork) neste diretório e peça:`);
62
+ console.log(` "leia ${ENTRY_AGENT} e siga as instruções".`);
63
+ }
64
+ catch (err) {
65
+ fail(err instanceof Error ? err.message : String(err));
66
+ }
67
+ }
68
+ async function runScaffold(values) {
69
+ if (!values.name)
70
+ fail("--name é obrigatório.");
71
+ if (!values.profile)
72
+ fail("--profile é obrigatório.");
73
+ if (values.profile !== "cli" && values.profile !== "ssr") {
74
+ fail(`--profile deve ser "cli" ou "ssr" (recebido: "${values.profile}").`);
75
+ }
76
+ const targetDir = resolve(values.dir ?? ".");
77
+ try {
78
+ const result = await scaffold({
79
+ name: values.name,
80
+ profile: values.profile,
81
+ targetDir,
82
+ port: values.port ?? "3000",
83
+ force: values.force ?? false,
84
+ });
85
+ console.log(`✔ docs criados em: ${result.docsPath}`);
86
+ console.log(`✔ CLAUDE.md criado em: ${result.claudeMdPath}`);
87
+ console.log("\nPróximos passos:");
88
+ console.log(" 1. Revise o CLAUDE.md e preencha placeholders restantes ({...}).");
89
+ console.log(" 2. git init (se ainda não for um repo).");
90
+ console.log(" 3. Escreva a primeira spec em specs/setup/00-*.md (ver docs/spec-guide.md).");
91
+ }
92
+ catch (err) {
93
+ fail(err instanceof Error ? err.message : String(err));
94
+ }
95
+ }
96
+ async function runUpdate(values) {
97
+ if (values.profile !== undefined &&
98
+ values.profile !== "cli" &&
99
+ values.profile !== "ssr") {
100
+ fail(`--profile deve ser "cli" ou "ssr" (recebido: "${values.profile}").`);
101
+ }
102
+ const targetDir = resolve(values.dir ?? ".");
103
+ const dryRun = values["dry-run"] ?? false;
104
+ try {
105
+ const result = await update({
106
+ targetDir,
107
+ profile: values.profile,
108
+ dryRun,
109
+ normalizeLinks: values["normalize-links"] ?? false,
110
+ only: values.only,
111
+ formatNormalize: !(values["no-format-normalize"] ?? false),
112
+ });
113
+ const modeNote = result.mode === "legacy"
114
+ ? "⚠ Sem manifesto — modo legado (comparação 2-way normalizada).\n"
115
+ : "";
116
+ // Aviso crítico: normalização pedida mas Prettier ausente → REVISAR pode
117
+ // estar inflado só por diferença de formatação.
118
+ const formatNote = !(values["no-format-normalize"] ?? false) && !result.prettierFound
119
+ ? "⚠ Prettier não encontrado em node_modules/.bin — comparei SEM normalizar\n" +
120
+ " formatação. Arquivos podem cair em REVISAR só por estilo (aspas, tabelas).\n" +
121
+ " Rode `npm install` no projeto e tente de novo, ou use --no-format-normalize\n" +
122
+ " pra silenciar este aviso.\n"
123
+ : "";
124
+ const header = dryRun
125
+ ? "Plano de update — NADA será escrito.\n"
126
+ : "Update aplicado.\n";
127
+ console.log(modeNote + formatNote + header);
128
+ console.log(renderPlan(result));
129
+ const { add, automerge, review, uptodate } = result.counts;
130
+ const mig = result.migrations.length;
131
+ console.log(`\nResumo: ${mig} migration(s) · ${add} adicionar · ${automerge} auto-merge · ${review} revisar · ${uptodate} em dia.`);
132
+ const autoMig = result.migrations.some((m) => m.autoApply && m.ops.length);
133
+ const conductMig = result.migrations.filter((m) => m.body.trim().length > 0).length;
134
+ if (dryRun) {
135
+ if (autoMig) {
136
+ console.log("\nNota: buckets calculados ANTES das migrations; renomeações declaradas\nacima podem reclassificar itens ao aplicar.");
137
+ }
138
+ console.log("\nRode sem --dry-run pra aplicar migrations + ADICIONA + AUTO-MERGE.");
139
+ }
140
+ else {
141
+ if (conductMig > 0) {
142
+ console.log(`\n${conductMig} migration(s) pra conduzir: veja docs/${HANDOFF_DIR}/MIGRATION-*.md.`);
143
+ }
144
+ if (review > 0) {
145
+ console.log(`\n${review} arquivo(s) pra revisar: veja os handoffs em docs/${HANDOFF_DIR}/.`);
146
+ }
147
+ }
148
+ }
149
+ catch (err) {
150
+ fail(err instanceof Error ? err.message : String(err));
151
+ }
152
+ }
153
+ async function main() {
154
+ const { values, positionals } = parseArgs({
155
+ options: {
156
+ name: { type: "string" },
157
+ profile: { type: "string" },
158
+ dir: { type: "string", default: "." },
159
+ port: { type: "string", default: "3000" },
160
+ force: { type: "boolean", default: false },
161
+ help: { type: "boolean", short: "h", default: false },
162
+ "dry-run": { type: "boolean", default: false },
163
+ "normalize-links": { type: "boolean", default: false },
164
+ only: { type: "string" },
165
+ "no-format-normalize": { type: "boolean", default: false },
166
+ },
167
+ allowPositionals: true,
168
+ });
169
+ if (values.help) {
170
+ console.log(HELP);
171
+ return;
172
+ }
173
+ const command = positionals[0];
174
+ if (command === "init") {
175
+ await runInit(values.dir ?? ".", values.force ?? false);
176
+ return;
177
+ }
178
+ if (command === "update") {
179
+ await runUpdate(values);
180
+ return;
181
+ }
182
+ if (command !== undefined && command !== "scaffold") {
183
+ fail(`Comando desconhecido: "${command}". Use "init", "update" ou nenhum comando.`);
184
+ }
185
+ await runScaffold(values);
186
+ }
187
+ main();