spec-first-claude 0.6.0-beta.1 → 0.6.0-beta.10

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.
package/bin/cli.js CHANGED
@@ -8,19 +8,19 @@ const args = process.argv.slice(2);
8
8
  const command = args[0];
9
9
 
10
10
  function printUsage() {
11
- console.log('Usage:');
12
- console.log(' spec-first-claude init --name=<project-name> [--target=<path>]');
13
- console.log(' spec-first-claude update [--target=<path>] [--force]');
11
+ console.log('Uso:');
12
+ console.log(' spec-first-claude init --name=<nome-do-projeto> [--target=<caminho>]');
13
+ console.log(' spec-first-claude update [--target=<caminho>] [--force]');
14
14
  console.log('');
15
- console.log('Commands:');
16
- console.log(' init Create a new spec-first project');
17
- console.log(' update Update an existing project with new framework files');
18
- console.log(' (adds missing files, updates framework files like commands/adapters/agents)');
15
+ console.log('Comandos:');
16
+ console.log(' init Cria um novo projeto spec-first');
17
+ console.log(' update Atualiza projeto existente com arquivos novos do framework');
18
+ console.log(' (adiciona faltantes, atualiza commands/adapters/agents/templates)');
19
19
  console.log('');
20
- console.log('Options:');
21
- console.log(' --name=<name> Project name (required for init)');
22
- console.log(' --target=<path> Target directory (defaults to ./<name> for init, cwd for update)');
23
- console.log(' --force Force update all files, not just framework files (use with caution)');
20
+ console.log('Opções:');
21
+ console.log(' --name=<nome> Nome do projeto (obrigatório no init)');
22
+ console.log(' --target=<path> Diretório destino (default: ./<nome> no init, cwd no update)');
23
+ console.log(' --force Força atualizar TODOS arquivos, não os do framework (cuidado)');
24
24
  }
25
25
 
26
26
  function parseArgs(args) {
@@ -40,7 +40,7 @@ if (command === 'init') {
40
40
  const opts = parseArgs(args.slice(1));
41
41
 
42
42
  if (!opts.name) {
43
- console.error('Error: --name is required.\n');
43
+ console.error('Erro: --name é obrigatório.\n');
44
44
  printUsage();
45
45
  process.exit(1);
46
46
  }
@@ -64,7 +64,7 @@ if (command === 'init') {
64
64
  } else {
65
65
  printUsage();
66
66
  if (command) {
67
- console.error(`\nUnknown command: "${command}"`);
67
+ console.error(`\nComando desconhecido: "${command}"`);
68
68
  }
69
69
  process.exit(command ? 1 : 0);
70
70
  }
package/lib/init.js CHANGED
@@ -42,18 +42,31 @@ function init({ name, templatesDir, targetDir }) {
42
42
  const dest = targetDir || path.resolve(process.cwd(), name);
43
43
 
44
44
  const existed = fs.existsSync(dest);
45
- console.log(`\n${existed ? 'Initializing' : 'Creating'} project "${name}" in ${dest}\n`);
45
+ console.log(`\n${existed ? 'Inicializando' : 'Criando'} projeto "${name}" em ${dest}\n`);
46
46
 
47
47
  const replacements = { [PLACEHOLDER]: name };
48
48
  copyDir(templatesDir, dest, replacements);
49
49
  patchProjectName(dest, name);
50
50
 
51
- console.log(`Done! Project "${name}" is ready.`);
52
- console.log('\nNext steps:');
51
+ console.log(`Pronto! Projeto "${name}" criado.`);
52
+ console.log('\nPróximos passos:');
53
53
  console.log(` 1. cd ${name}`);
54
- console.log(' 2. Create a folder in workspace/Input/ with your project files (e.g. workspace/Input/my_app/)');
55
- console.log(' 3. Copy sfw.config.yml.example to sfw.config.yml and configure your backend');
56
- console.log(' 4. Run /new-project <folder-name> to start the pipeline');
54
+ console.log(' 2. Crie uma pasta em workspace/Input/ com os insumos do seu projeto');
55
+ console.log(' (ex: workspace/Input/meu_app/requisitos.md)');
56
+ console.log('');
57
+ console.log(' 3. (Opcional) Configure o backend de input/output:');
58
+ console.log(' cp sfw.config.yml.example sfw.config.yml');
59
+ console.log(' Sem isso, o framework roda 100% local (sem Confluence/etc.)');
60
+ console.log('');
61
+ console.log(' 4. (Opcional, se vai usar Confluence) Prepare o ambiente ANTES de abrir o Claude:');
62
+ console.log(' node .claude/scripts/bootstrap-confluence.js');
63
+ console.log(' Valida uvx, coleta credenciais, gera .mcp.json e pré-cacheia o MCP.');
64
+ console.log(' Evita o ciclo "abrir Claude → faltar dep → reiniciar".');
65
+ console.log('');
66
+ console.log(' 5. Abra o Claude Code no diretório do projeto e rode:');
67
+ console.log(' /sfw-start <nome-da-pasta-em-Input>');
68
+ console.log(' O framework detecta first-run automaticamente (cria docs/) vs feature');
69
+ console.log(' (atualiza docs/ via merge).');
57
70
  console.log('');
58
71
  }
59
72
 
package/lib/update.js CHANGED
@@ -18,36 +18,36 @@ function update({ templatesDir, targetDir, force }) {
18
18
  const dest = targetDir || process.cwd();
19
19
 
20
20
  if (!fs.existsSync(dest)) {
21
- console.error(`Error: directory "${dest}" does not exist.`);
22
- console.error('Run "spec-first-claude init --name=<name>" first to create a project.');
21
+ console.error(`Erro: diretório "${dest}" não existe.`);
22
+ console.error('Rode "spec-first-claude init --name=<nome>" primeiro pra criar o projeto.');
23
23
  process.exit(1);
24
24
  }
25
25
 
26
26
  const claudeDir = path.join(dest, '.claude');
27
27
  if (!fs.existsSync(claudeDir)) {
28
- console.error('Error: not a spec-first project (missing .claude/ directory).');
29
- console.error('Run "spec-first-claude init --name=<name>" first.');
28
+ console.error('Erro: não é um projeto spec-first (pasta .claude/ não encontrada).');
29
+ console.error('Rode "spec-first-claude init --name=<nome>" primeiro.');
30
30
  process.exit(1);
31
31
  }
32
32
 
33
- console.log(`\nUpdating project in ${dest}\n`);
33
+ console.log(`\nAtualizando projeto em ${dest}\n`);
34
34
 
35
35
  const stats = { added: [], updated: [], skipped: 0 };
36
36
  syncDir(templatesDir, dest, force, stats, dest);
37
37
 
38
38
  console.log('');
39
39
  if (stats.added.length > 0) {
40
- console.log(`Added (${stats.added.length}):`);
40
+ console.log(`Adicionados (${stats.added.length}):`);
41
41
  for (const f of stats.added) console.log(` + ${f}`);
42
42
  }
43
43
  if (stats.updated.length > 0) {
44
- console.log(`Updated (${stats.updated.length}):`);
44
+ console.log(`Atualizados (${stats.updated.length}):`);
45
45
  for (const f of stats.updated) console.log(` ~ ${f}`);
46
46
  }
47
47
  if (stats.added.length === 0 && stats.updated.length === 0) {
48
- console.log('Already up to date no new files to add.');
48
+ console.log(' está atualizadonenhum arquivo novo pra adicionar.');
49
49
  } else {
50
- console.log(`\n${stats.added.length} added, ${stats.updated.length} updated, ${stats.skipped} unchanged`);
50
+ console.log(`\n${stats.added.length} adicionado(s), ${stats.updated.length} atualizado(s), ${stats.skipped} inalterado(s)`);
51
51
  }
52
52
  console.log('');
53
53
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-first-claude",
3
- "version": "0.6.0-beta.1",
3
+ "version": "0.6.0-beta.10",
4
4
  "description": "Spec-first workflow kit for Claude Code — AI-driven development with specs, not guesswork",
5
5
  "bin": {
6
6
  "spec-first-claude": "bin/cli.js"
@@ -8,6 +8,282 @@
8
8
 
9
9
  ---
10
10
 
11
+ ## 0.6.0-beta.10 (2026-04-13)
12
+
13
+ ### `bootstrap-confluence.js` agora é idempotente
14
+
15
+ Script agora lê `.mcp.json` existente e reusa valores da entry `atlassian` em
16
+ vez de perguntar tudo de novo:
17
+
18
+ - Se `CONFLUENCE_URL` já existe: mostra URL atual, pergunta "Manter? [S/n]"
19
+ - Se `CONFLUENCE_USERNAME` já existe: mostra email atual, pergunta "Manter? [S/n]"
20
+ - Se `CONFLUENCE_API_TOKEN` já existe: mostra token mascarado (`abcd…wxyz`), pergunta "Manter? [S/n]"
21
+ - Default ENTER = manter. Responder `n` abre o prompt completo da informação.
22
+
23
+ Se todos os 3 campos foram mantidos: detecta que nada mudou e **não reescreve**
24
+ `.mcp.json` (evita dirty git). Se mudou algo: reescreve preservando outras
25
+ entries MCP (Supabase, GitHub, etc.).
26
+
27
+ Útil pra: reexecução após novo rebuild do MCP, rotação apenas do token, debug
28
+ de setup sem perder credenciais já válidas.
29
+
30
+ ---
31
+
32
+ ## 0.6.0-beta.9 (2026-04-13)
33
+
34
+ ### CLI em pt-BR + dica de bootstrap-confluence no init
35
+
36
+ Mensagens do CLI (`init` e `update`) traduzidas para pt-BR — usuário final é o público.
37
+
38
+ Mensagem do `init` reescrita para deixar **explícito o passo opcional de pré-setup
39
+ do Confluence**:
40
+
41
+ ```
42
+ Pronto! Projeto "X" criado.
43
+
44
+ Próximos passos:
45
+ 1. cd X
46
+ 2. Crie uma pasta em workspace/Input/ com os insumos do seu projeto
47
+ 3. (Opcional) Configure o backend de input/output:
48
+ cp sfw.config.yml.example sfw.config.yml
49
+ 4. (Opcional, se vai usar Confluence) Prepare o ambiente ANTES de abrir o Claude:
50
+ node .claude/scripts/bootstrap-confluence.js
51
+ 5. Abra o Claude Code e rode: /sfw-start <nome-da-pasta>
52
+ ```
53
+
54
+ O passo 4 era invisível antes — só aparecia inline na "Dica" do skill `/mcp`.
55
+ Agora aparece logo no fim do `init`, no momento exato em que o usuário vai decidir
56
+ se prepara o Confluence ou não.
57
+
58
+ ---
59
+
60
+ ## 0.6.0-beta.8 (2026-04-13)
61
+
62
+ ### Novo: `bootstrap-confluence.js` (opcional, pré-Claude)
63
+
64
+ Script Node cross-platform em `.claude/scripts/bootstrap-confluence.js` que prepara
65
+ o ambiente para o backend Confluence **antes** de abrir o Claude Code. Evita o ciclo
66
+ "abrir Claude → descobrir que falta `uvx` ou credenciais → sair, ajustar, reiniciar".
67
+
68
+ O que faz:
69
+ 1. Valida `uvx` instalado (orienta install se faltar — não instala automaticamente)
70
+ 2. Coleta URL do Confluence, email Atlassian e API Token (com link pra gerar o token)
71
+ 3. Gera/mescla `.mcp.json` preservando outras entries de MCP existentes
72
+ 4. Garante `.mcp.json` no `.gitignore`
73
+ 5. Pré-cacheia o pacote `mcp-atlassian` via `uvx` (100+ deps no primeiro uso)
74
+
75
+ Depois é só abrir o Claude Code e rodar `/mcp confluence` — vai direto pra descoberta
76
+ da árvore no Confluence, sem reiniciar.
77
+
78
+ Uso: `node .claude/scripts/bootstrap-confluence.js`
79
+ Opcional — `/mcp confluence` sozinho também cobre esses passos, mas exige reiniciar
80
+ Claude Code após criar `.mcp.json`.
81
+
82
+ Skill `/mcp` ganhou nota referenciando o script como atalho recomendado.
83
+
84
+ ### Primeira release sincronizada pelo `sync-kits`
85
+
86
+ Esta é a primeira release onde **toda a edição aconteceu só no kit Claude** e o script
87
+ `npm run sync-kits` propagou para o kit Copilot automaticamente. Fluxo validado.
88
+
89
+ ---
90
+
91
+ ## 0.6.0-beta.7 (2026-04-13)
92
+
93
+ ### Script de sync entre kits (interno, não afeta o user)
94
+
95
+ Criado `scripts/sync-kits.js` no repo SFW (não entra no pacote publicado). Mantém o
96
+ kit Copilot (`.github/`) sincronizado com o kit Claude (`.claude/`) aplicando renames
97
+ automaticamente (`.claude/` → `.github/`, `/extract` → `/sf-extract`, etc.).
98
+
99
+ Fluxo agora: editar apenas em `spec-first-claude`, rodar `npm run sync-kits`, commit
100
+ dos 2 kits juntos. CI pode usar `npm run check-kits` pra detectar drift.
101
+
102
+ **Sem impacto pro usuário do kit Claude** — conteúdo idêntico ao beta.6.
103
+
104
+ ---
105
+
106
+ ## 0.6.0-beta.6 (2026-04-13)
107
+
108
+ ### Fixes detectados no smoke test de `beta.5`
109
+
110
+ Rodamos o pipeline completo (`/sfw-start → /extract → /design → /plan`) em projeto
111
+ novo com feature simples (todo app backend) e detectamos 6 inconsistências entre
112
+ skills e templates. Todas corrigidas:
113
+
114
+ **1. PRD §14 — coluna "Resolvido em docs/" adicionada**
115
+
116
+ Skill `/extract` (beta.5) descreveu mecanismo do Analyzer marcar ambiguidades como
117
+ "resolvido em docs/X §Y", mas o template do PRD não tinha coluna para isso. Agora
118
+ a tabela de §14 tem 6 colunas: `ID | Pergunta | Contexto | Fonte | Resposta | Resolvido em docs/`.
119
+
120
+ **2. PRD §14 — renomeada "Resposta do usuário" → "Resposta"**
121
+
122
+ Skill `/extract` fala em preencher "coluna `Resposta`" mas o template tinha
123
+ "Resposta do usuário". Alinhado com o skill. Também adicionado ID padronizado
124
+ `AMB-NNN` na primeira coluna (antes era só `#`).
125
+
126
+ **3. PRD §14 e SDD §12 padronizados**
127
+
128
+ SDD §12 Ambiguidades também passou a usar ID `AMB-NNN` continuando a sequência
129
+ do PRD §14. Texto do bloqueio do SDD corrigido: não é o `/design` que bloqueia
130
+ no §12 (ele já gerou), é o `/plan` (que lê o SDD como input).
131
+
132
+ **4. Status do Progresso.md clarificado**
133
+
134
+ `Status Geral` do Progresso.md era confundido com o status da pipeline em
135
+ `.context.md`. Renomeado para `Status das Fases` + bloco explicativo deixando
136
+ claro que **este arquivo rastreia fases de entrega do PRD §11, não o pipeline**.
137
+
138
+ **5. Bool tipado corretamente no `.context.md`**
139
+
140
+ Antes: `first_run: "{{true|false}}"` (string com aspas). Depois: `first_run: {{true|false}}`
141
+ (bool YAML real). Instruções do template reforçam: "SEM aspas".
142
+
143
+ **6. `projetos.yaml` sem stack hardcoded**
144
+
145
+ Template tinha `stack: ".NET 8"` literal no exemplo `api`. Podia induzir projetos
146
+ Node/Python a errar. Agora usa `{{STACK_BACKEND}}` como placeholder + bloco
147
+ explicativo listando exemplos por stack (Backend/Frontend/Mobile) como referência.
148
+ Instruções reforçam: "stack DEVE vir do SDD §3.1".
149
+
150
+ ### Extras
151
+
152
+ **SDD template com guidance sobre escopo pequeno**
153
+
154
+ Adicionado bloco no topo do template explicando que 3-4 seções preenchidas + o
155
+ resto como "N/A" é comportamento esperado para apps simples — não inflar conteúdo
156
+ pra "preencher" seções vazias. Áreas não tocadas (§4-§7) usam `GATE: NÃO`.
157
+
158
+ ---
159
+
160
+ ## 0.6.0-beta.5 (2026-04-13)
161
+
162
+ ### Refino dos templates de specs/
163
+
164
+ Templates de projeção do SDD (`specs/{scope}/`) refinados com padrões consolidados de BDD/contract-first. Dois passos de refino fizeram parte desta release.
165
+
166
+ **`scenarios.template.md`** — mudanças:
167
+ - Seção "Fluxos de Erro e Edge Cases" separada do happy path com coluna "Ref CA" (antes ficavam perdidos e soltos)
168
+ - Seção "Pré-condições Globais" no topo (setup do contexto que aplica a todos os cenários)
169
+ - Campo "Pré-condição" em cada CA (estado adicional específico daquele CA — não repete as globais)
170
+ - Tabela de fluxos com colunas Ator/Ação/Resultado em vez de texto linear
171
+ - UI/Componentes: orientação explícita para features backend-only ("N/A — backend-only")
172
+ - Estratégia de testes: coluna "Responsável" (em vez de "Quem dispara"), sem redefinir frameworks (referencia docs/)
173
+ - Bloco de terminologia explica diferença entre Pré-condições Globais vs Pré-condição do CA
174
+
175
+ **`contracts.template.md`** — mudanças:
176
+ - Separação explícita em **3 camadas**: Dados (schema do banco) → Validações de Entrada (formato/tipo do request) → Regras de Negócio (invariantes do domínio)
177
+ - "Validações de Entrada" virou seção top-level (antes estava enfiada dentro de Dados)
178
+ - Dados: subseção "Alterações em tabelas existentes" (ADD/ALTER/DROP COLUMN) — antes só tinha formato para tabela nova
179
+ - API: erros têm coluna "Código de domínio" (linkado às RN)
180
+ - Instrução explícita para não duplicar convenções globais de docs/apiContracts.md
181
+
182
+ **`tasks.template.md`** — mudanças:
183
+ - Padronização de áreas: `DB` (antes inconsistente com `BANCO`)
184
+ - Coluna "Ref SDD" (obrigatória) adicionada — rastreabilidade ao documento fonte para tasks sem CA (INFRA/DOC)
185
+ - Coluna "Ref CA" agora é opcional
186
+ - Seção "Done When" com placeholder real (antes só tinha exemplo em comentário)
187
+ - Removida seção "Regras por área" (era estática, contradiz áreas dinâmicas)
188
+ - Removida seção "Convenções desta feature" (dumping ground — convenções pertencem ao SDD ou docs/)
189
+ - Instrução explícita: bootstrap/first-run → toda task na Fase 1
190
+
191
+ **`brief.template.md`** — mudanças:
192
+ - Nova seção "Serviços tocados" (quais repos de projetos.yaml esta feature modifica + áreas)
193
+
194
+ ### Padronização de nome de área: `BANCO` → `DB`
195
+
196
+ Inconsistência detectada entre commands/agents/templates: alguns usavam `BANCO` (português),
197
+ outros usavam `DB`. Padronizado para **`DB`** (alinhado com nome do agente `db-coder.md` e
198
+ com `commands/design.md`). 14 arquivos atualizados:
199
+
200
+ - `.claude/agents/db-coder.md`
201
+ - `.claude/commands/dev.md` (tabela de agents + seletor por prefixo)
202
+ - `.claude/commands/plan.md` (mapeamento SDD → área + exemplo)
203
+ - `.claude/rules.md` (exemplo de quality gate)
204
+ - `.claude/templates/feature/projetos.template.yaml` (mapeamento de áreas)
205
+ - `.claude/templates/feature/Progresso.template.md` (ordem de execução)
206
+ - `.claude/templates/global/progresso_global.template.md` (exemplo)
207
+
208
+ **Breaking**: IDs de task geradas anteriormente com prefixo `BANCO-*` continuam válidas (IDs
209
+ estáveis, não renomear). Novas features geram `DB-*`.
210
+
211
+ ### `/discovery` declara modelo Opus
212
+
213
+ Skill não tinha declaração explícita de modelo — rodava com o default da sessão. Descrição
214
+ do skill ("arquiteto senior com mentalidade de consultor crítico... análises profundas e
215
+ honestas") é trabalho Opus. Adicionado bloco **"Agente: Discovery Analyst (Opus)"** no topo.
216
+
217
+ ### Doc gap: como responder ambiguidades do PRD §14
218
+
219
+ Skill `/extract` ganhou seção dedicada **"Como responder ambiguidades"** com fluxo
220
+ passo a passo (caminho oficial: editar coluna `Resposta` direto no PRD §14 e re-rodar
221
+ `/design`). Skill `/design` agora exibe instrução completa de retomada quando bloqueia
222
+ por ambiguidades pendentes, em vez de só "pedir resposta".
223
+
224
+ Caminho alternativo (adicionar `ajustes_v1.md` em Input + re-rodar `/extract`) está
225
+ documentado como exceção para mudanças estruturais.
226
+
227
+ ---
228
+
229
+ ## 0.6.0-beta.4 (2026-04-13)
230
+
231
+ ### UX: `/mcp confluence` self-contained pra usuário leigo
232
+
233
+ Perguntas do skill agora mostram EXATAMENTE onde achar cada informação inline,
234
+ sem depender de consulta externa ao SETUP.md:
235
+
236
+ - **URL do Confluence**: "abra no browser, copie URL base com /wiki"
237
+ - **API Token**: 4 sub-passos numerados (URL Atlassian, Create button, nome, copiar antes que suma)
238
+ - **Space Key**: mostra URL pattern `/wiki/spaces/XXX/` com seta + exemplos ("ST", "DEV", "PROJ")
239
+ - **Root Page ID**: 2 opções (A: Page ID com URL pattern `/pages/NNNN/`, B: título exato com aviso de match)
240
+
241
+ Qualquer modelo / usuário leigo consegue responder sem ficar travado.
242
+
243
+ ---
244
+
245
+ ## 0.6.0-beta.3 (2026-04-13)
246
+
247
+ ### Fix: `/mcp confluence` idempotente com `root_page_id` como âncora
248
+
249
+ Antes o skill era linear e podia pular a coleta do Page ID raiz do projeto depois de
250
+ um restart (caso user tivesse que reiniciar o Claude após criar `.mcp.json`), resultando
251
+ em `sfw.config.yml` sem `project.root_page_id` — framework ficava sem âncora.
252
+
253
+ Novo fluxo:
254
+ 1. Detecta estado de `sfw.config.yml` (tem root_page_id?) + `.mcp.json` (tem credenciais?)
255
+ 2. Se AMBOS existem → testa conexão e reconfirma Input/Output
256
+ 3. Se algo falta → pede só o que falta (idempotente)
257
+ 4. `root_page_id` validado contra `get_page(id)` antes de mapear árvore
258
+ 5. Handler explícito pra 401 (credencial expirou) e 404 (root inválido)
259
+
260
+ ### Fix: Auto-publish imperativo em `/extract`, `/design`, `/plan`
261
+
262
+ Antes a instrução era "Se `sfw.config.yml` com output.targets[] ativo: Executar /publish" —
263
+ ambígua, agent podia interpretar como opcional e pular.
264
+
265
+ Agora:
266
+ - Instrução imperativa: "Se `sfw.config.yml` existe → Chamar `/publish`. Execute."
267
+ - Cada skill adiciona **seção "Saída obrigatória"** reportando status do publish
268
+ (CREATED/UPDATED/SKIPPED/CONFLICT/ERROR) ou "SKIPPED (no sfw.config.yml)"
269
+ - User vê explicitamente o que aconteceu no Confluence
270
+
271
+ ### Fix: `publishes: [PRD, PRD, SDD, Progresso]` → `[PRD, SDD, Progresso]`
272
+
273
+ Resquício do rename `TRD → PRD` na seção de template do `/mcp confluence`. Agora gera yaml correto.
274
+
275
+ ---
276
+
277
+ ## 0.6.0-beta.2 (2026-04-12)
278
+
279
+ ### Fix: CLI `init` output
280
+
281
+ - Mensagem "Next steps" do `npx spec-first-claude init` apontava pro `/new-project` removido no v3
282
+ - Agora aponta pro `/sfw-start <folder-name>` + nota que framework detecta first-run automaticamente
283
+ - sfw.config.yml.example marcado como opcional (projeto funciona 100% local)
284
+
285
+ ---
286
+
11
287
  ## 0.6.0-beta.1 (2026-04-12) — Redesign v3 (BREAKING)
12
288
 
13
289
  Redesign estrutural eliminando o TRD e unificando o pipeline em um comando único.