vireum-spec-cli 0.1.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.
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.runBrief = runBrief;
40
+ const chalk_1 = __importDefault(require("chalk"));
41
+ const ora_1 = __importDefault(require("ora"));
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
44
+ async function runBrief() {
45
+ console.log(chalk_1.default.hex('#2D7DD2').bold('\n Vireum Spec — Brief\n'));
46
+ const specDir = path.join(process.cwd(), '.spec');
47
+ const spinner = (0, ora_1.default)('Lendo spec...').start();
48
+ await sleep(600);
49
+ spinner.stop();
50
+ if (!fs.existsSync(path.join(specDir, 'INDEX.md'))) {
51
+ console.log(chalk_1.default.red('\n❌ Spec não encontrado.'));
52
+ console.log(chalk_1.default.gray(' Execute primeiro: ') + chalk_1.default.white('vireum-spec init && vireum-spec distill\n'));
53
+ process.exit(1);
54
+ }
55
+ const index = lerArquivo(path.join(specDir, 'INDEX.md'));
56
+ const active = lerArquivo(path.join(specDir, 'tasks', 'active.md'));
57
+ const done = lerArquivo(path.join(specDir, 'tasks', 'done.md'));
58
+ const backlog = lerArquivo(path.join(specDir, 'tasks', 'backlog.md'));
59
+ const arch = lerArquivo(path.join(specDir, 'architecture.md'));
60
+ const risks = lerArquivo(path.join(specDir, 'risks.md'));
61
+ const changelog = lerArquivo(path.join(specDir, 'changelog.md'));
62
+ // ── Extrair dados ──────────────────────────────────────────────────────────
63
+ const projeto = extrairCampo(index, '# INDEX — ') || 'projeto';
64
+ const cliente = extrairInline(index, '**Cliente:**') || 'N/A';
65
+ const fase = extrairInline(index, '**Fase atual:**') || 'MVP';
66
+ const iniciado = extrairInline(index, '**Iniciado em:**') || 'N/A';
67
+ const objetivo = extrairSecaoBreve(index, '## Resumo do Objetivo');
68
+ const tasksActive = contarTasks(active, '## [');
69
+ const tasksDone = contarTasks(done, '- ') - 1;
70
+ const tasksBacklog = contarTasks(backlog, '- [ ]');
71
+ const totalMvp = tasksActive + Math.max(tasksDone, 0);
72
+ const progresso = totalMvp > 0 ? Math.round((Math.max(tasksDone, 0) / totalMvp) * 100) : 0;
73
+ const stack = extrairStack(arch);
74
+ const riscosAtivos = contarRiscos(risks);
75
+ const ultimaDecisao = extrairUltimaEntrada(changelog);
76
+ // ── Output ─────────────────────────────────────────────────────────────────
77
+ console.log(chalk_1.default.white.bold(` ${projeto}`));
78
+ console.log(chalk_1.default.gray(` Cliente: ${cliente} · Fase: ${fase} · Iniciado: ${iniciado}\n`));
79
+ console.log(chalk_1.default.hex('#2D7DD2')(' Objetivo'));
80
+ console.log(chalk_1.default.gray(` ${objetivo}\n`));
81
+ console.log(chalk_1.default.hex('#2D7DD2')(' Progresso do MVP'));
82
+ const barra = gerarBarra(progresso);
83
+ console.log(` ${barra} ${progresso}%`);
84
+ console.log(chalk_1.default.gray(` ${Math.max(tasksDone, 0)} concluída(s) · ${tasksActive} ativa(s) · ${tasksBacklog} no backlog\n`));
85
+ if (stack) {
86
+ console.log(chalk_1.default.hex('#2D7DD2')(' Stack'));
87
+ console.log(chalk_1.default.gray(` ${stack}\n`));
88
+ }
89
+ if (riscosAtivos > 0) {
90
+ console.log(chalk_1.default.yellow(` ⚠ ${riscosAtivos} risco(s) identificado(s) — veja .spec/risks.md\n`));
91
+ }
92
+ if (ultimaDecisao) {
93
+ console.log(chalk_1.default.hex('#2D7DD2')(' Última entrada no changelog'));
94
+ console.log(chalk_1.default.gray(` ${ultimaDecisao}\n`));
95
+ }
96
+ console.log(chalk_1.default.gray(' Para detalhes completos: abra os arquivos em .spec/\n'));
97
+ }
98
+ // ─── HELPERS ──────────────────────────────────────────────────────────────────
99
+ function lerArquivo(p) {
100
+ return fs.existsSync(p) ? fs.readFileSync(p, 'utf-8') : '';
101
+ }
102
+ function extrairCampo(texto, prefixo) {
103
+ const linha = texto.split('\n').find(l => l.startsWith(prefixo));
104
+ return linha ? linha.replace(prefixo, '').trim() : '';
105
+ }
106
+ function extrairInline(texto, campo) {
107
+ const regex = new RegExp(`${campo.replace(/\*/g, '\\*')}\\s*(.+)`);
108
+ const match = texto.match(regex);
109
+ return match ? match[1].trim() : '';
110
+ }
111
+ function extrairSecaoBreve(texto, secao) {
112
+ const idx = texto.indexOf(secao);
113
+ if (idx === -1)
114
+ return 'N/A';
115
+ const resto = texto.slice(idx + secao.length).trim();
116
+ const linha = resto.split('\n').find(l => l.trim() && !l.startsWith('#'));
117
+ return linha ? linha.trim().slice(0, 120) : 'N/A';
118
+ }
119
+ function extrairStack(arch) {
120
+ const frontend = extrairInline(arch, '- Frontend:');
121
+ const backend = extrairInline(arch, '- Backend:');
122
+ const banco = extrairInline(arch, '- Banco de dados:');
123
+ if (!frontend && !backend)
124
+ return '';
125
+ return [frontend, backend, banco].filter(Boolean).join(' · ');
126
+ }
127
+ function contarTasks(texto, prefixo) {
128
+ return texto.split('\n').filter(l => l.includes(prefixo)).length;
129
+ }
130
+ function contarRiscos(risks) {
131
+ const linhas = risks.split('\n').filter(l => l.trim() && !l.includes('Nenhum identificado') && !l.includes('|') &&
132
+ !l.startsWith('#') && !l.startsWith('>') && l.includes(':'));
133
+ return Math.max(linhas.length - 3, 0);
134
+ }
135
+ function extrairUltimaEntrada(changelog) {
136
+ const linhas = changelog.split('\n').filter(l => l.startsWith('## '));
137
+ if (linhas.length === 0)
138
+ return '';
139
+ return linhas[linhas.length - 1].replace('## ', '').trim();
140
+ }
141
+ function gerarBarra(pct) {
142
+ const total = 20;
143
+ const preench = Math.round((pct / 100) * total);
144
+ const vazio = total - preench;
145
+ const cor = pct >= 80 ? chalk_1.default.green : pct >= 40 ? chalk_1.default.yellow : chalk_1.default.gray;
146
+ return cor('█'.repeat(preench)) + chalk_1.default.gray('░'.repeat(vazio));
147
+ }
148
+ function sleep(ms) {
149
+ return new Promise(r => setTimeout(r, ms));
150
+ }
@@ -0,0 +1,372 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.runDistill = runDistill;
40
+ const chalk_1 = __importDefault(require("chalk"));
41
+ const ora_1 = __importDefault(require("ora"));
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
44
+ async function runDistill() {
45
+ console.log(chalk_1.default.hex('#2D7DD2').bold('\n Vireum Spec — Distill\n'));
46
+ const specDir = path.join(process.cwd(), '.spec');
47
+ const briefingPath = path.join(specDir, 'briefing.md');
48
+ if (!fs.existsSync(briefingPath)) {
49
+ console.log(chalk_1.default.red('\n❌ briefing.md não encontrado.'));
50
+ console.log(chalk_1.default.gray(' Execute primeiro: ') + chalk_1.default.white('vireum-spec init\n'));
51
+ process.exit(1);
52
+ }
53
+ const spinner = (0, ora_1.default)('Lendo briefing...').start();
54
+ const briefing = fs.readFileSync(briefingPath, 'utf-8');
55
+ await sleep(600);
56
+ spinner.succeed('Briefing lido');
57
+ const dados = parseBriefing(briefing);
58
+ const arquivos = [
59
+ { nome: 'requirements.md', conteudo: gerarRequirements(dados), msg: 'Gerando requirements.md...' },
60
+ { nome: 'users.md', conteudo: gerarUsers(dados), msg: 'Gerando users.md...' },
61
+ { nome: 'risks.md', conteudo: gerarRisks(dados), msg: 'Gerando risks.md...' },
62
+ { nome: 'architecture.md', conteudo: gerarArchitecture(dados), msg: 'Gerando architecture.md...' },
63
+ { nome: 'INDEX.md', conteudo: gerarIndex(dados), msg: 'Gerando INDEX.md...' },
64
+ { nome: 'changelog.md', conteudo: gerarChangelog(dados), msg: 'Gerando changelog.md...' },
65
+ { nome: 'rules.md', conteudo: gerarRules(dados), msg: 'Gerando rules.md...' },
66
+ ];
67
+ for (const arq of arquivos) {
68
+ const s = (0, ora_1.default)(arq.msg).start();
69
+ await sleep(400);
70
+ fs.writeFileSync(path.join(specDir, arq.nome), arq.conteudo, 'utf-8');
71
+ s.succeed(arq.nome + ' gerado');
72
+ }
73
+ // tasks
74
+ const tasksDir = path.join(specDir, 'tasks');
75
+ if (!fs.existsSync(tasksDir))
76
+ fs.mkdirSync(tasksDir, { recursive: true });
77
+ const sActive = (0, ora_1.default)('Gerando tasks/active.md...').start();
78
+ await sleep(400);
79
+ fs.writeFileSync(path.join(tasksDir, 'active.md'), gerarTasksActive(dados), 'utf-8');
80
+ fs.writeFileSync(path.join(tasksDir, 'backlog.md'), gerarTasksBacklog(dados), 'utf-8');
81
+ fs.writeFileSync(path.join(tasksDir, 'done.md'), gerarTasksDone(dados), 'utf-8');
82
+ sActive.succeed('tasks/ gerado');
83
+ console.log(chalk_1.default.green.bold('\n✅ Spec gerado com sucesso!\n'));
84
+ console.log(chalk_1.default.gray(' Arquivos criados em ') + chalk_1.default.white('.spec/\n'));
85
+ console.log(chalk_1.default.gray(' Próximo passo: ') + chalk_1.default.white('vireum-spec setup') + chalk_1.default.gray(' para configurar stack e protocolo\n'));
86
+ }
87
+ // ─── PARSER ───────────────────────────────────────────────────────────────────
88
+ function extrairSecao(briefing, titulo) {
89
+ const regex = new RegExp(`## [^#]*${titulo}[\\s\\S]*?(?=\n## |$)`, 'i');
90
+ const match = briefing.match(regex);
91
+ return match ? match[0].trim() : '';
92
+ }
93
+ function extrairCampo(briefing, campo) {
94
+ const regex = new RegExp(`\\*\\*${campo}:\\*\\*\\s*(.+)`, 'i');
95
+ const match = briefing.match(regex);
96
+ if (match)
97
+ return match[1].trim();
98
+ const regex2 = new RegExp(`- \\*\\*${campo}:\\*\\*\\s*(.+)`, 'i');
99
+ const match2 = briefing.match(regex2);
100
+ return match2 ? match2[1].trim() : '';
101
+ }
102
+ function extrairLista(secao, prefixo) {
103
+ const linhas = secao.split('\n').filter(l => l.includes('- [ ]') || (prefixo && l.startsWith(prefixo)));
104
+ return linhas.map(l => l.replace('- [ ]', '').replace(prefixo, '').trim()).filter(Boolean);
105
+ }
106
+ function parseBriefing(briefing) {
107
+ const secaoGeral = extrairSecao(briefing, 'Informações Gerais');
108
+ const secaoObjetivo = extrairSecao(briefing, 'Objetivo');
109
+ const secaoEscopo = extrairSecao(briefing, 'Escopo Funcional');
110
+ const secaoRegras = extrairSecao(briefing, 'Regras de Negócio');
111
+ const secaoPlat = extrairSecao(briefing, 'Plataforma');
112
+ const secaoSeg = extrairSecao(briefing, 'Segurança');
113
+ const secaoEscala = extrairSecao(briefing, 'Escala');
114
+ const secaoRiscos = extrairSecao(briefing, 'Riscos');
115
+ const secaoFinan = extrairSecao(briefing, 'Financeiro');
116
+ const secaoMetricas = extrairSecao(briefing, 'Métricas');
117
+ const featuresObrig = extrairLista(secaoEscopo, '- [ ]');
118
+ const featuresDesej = extrairLista(secaoEscopo, '- [ ]');
119
+ return {
120
+ projeto: extrairCampo(secaoGeral, 'Projeto'),
121
+ cliente: extrairCampo(secaoGeral, 'Cliente'),
122
+ responsavel: extrairCampo(secaoGeral, 'Responsável'),
123
+ data: extrairCampo(secaoGeral, 'Data da reunião'),
124
+ objetivo: extrairCampo(secaoObjetivo, 'Objetivo principal') || extrairSecao(briefing, 'Objetivo').split('\n')[1] || '',
125
+ problema: extrairCampo(secaoObjetivo, 'Problema que o sistema resolve'),
126
+ prioridade: extrairCampo(secaoObjetivo, 'Prioridade do cliente'),
127
+ usuarios: extrairCampo(secaoEscopo, 'Quem usará') || '',
128
+ multiUsuario: secaoEscopo.includes('Diferentes tipos de usuários: Sim'),
129
+ niveisAcesso: secaoEscopo.includes('Níveis de acesso: Sim'),
130
+ integracoes: extrairCampo(secaoEscopo, 'Integrações Externas') || '',
131
+ features: featuresObrig,
132
+ regras: secaoRegras,
133
+ plataforma: extrairCampo(secaoPlat, 'Tipo'),
134
+ hospedagem: extrairCampo(secaoPlat, 'Hospedagem'),
135
+ cicd: secaoPlat.includes('CI/CD desde o início: Sim'),
136
+ staging: secaoPlat.includes('Ambiente de staging: Sim'),
137
+ lgpd: secaoSeg.includes('LGPD: Sim'),
138
+ dadosSensiveis: secaoSeg.includes('Dados sensíveis: Sim'),
139
+ pagamento: secaoFinan.includes('Integração com pagamento: Sim'),
140
+ cobranca: extrairCampo(secaoFinan, 'Modelo de cobrança'),
141
+ dashboard: secaoMetricas.includes('Dashboard administrativo: Sim'),
142
+ kpis: extrairCampo(secaoMetricas, 'KPIs'),
143
+ usuariosLanc: extrairCampo(secaoEscala, 'Usuários no lançamento'),
144
+ riscTecnico: extrairCampo(secaoRiscos, 'Risco técnico'),
145
+ riscJuridico: extrairCampo(secaoRiscos, 'Risco jurídico'),
146
+ riscOperacional: extrairCampo(secaoRiscos, 'Risco operacional'),
147
+ };
148
+ }
149
+ // ─── GERADORES ────────────────────────────────────────────────────────────────
150
+ function gerarRequirements(d) {
151
+ const features = d.features.length
152
+ ? d.features.map((f, i) => `- [ ] R-${String(i + 1).padStart(3, '0')} ${f}`).join('\n')
153
+ : '- [ ] R-001 (a definir)';
154
+ return `# Requirements — ${d.projeto}
155
+
156
+ > Gerado automaticamente a partir do briefing em ${d.data}
157
+ > Fonte: .spec/briefing.md
158
+
159
+ ## Objetivo
160
+ ${d.objetivo}
161
+
162
+ **Problema:** ${d.problema}
163
+
164
+ **Prioridade do cliente:** ${d.prioridade}
165
+
166
+ ## Funcionalidades — MVP
167
+
168
+ ${features}
169
+
170
+ ## Funcionalidades — Fase 2
171
+ > Preencher após vireum-spec prioritize
172
+
173
+ ## Regras de Negócio
174
+ ${d.regras || '> A extrair do briefing'}
175
+
176
+ ## Integrações
177
+ ${d.integracoes || 'Nenhuma identificada'}
178
+
179
+ ## Financeiro
180
+ ${d.pagamento ? `- Integração com pagamento: Sim\n- Modelo: ${d.cobranca}` : '- Sem integração financeira'}
181
+
182
+ ## Métricas
183
+ - KPIs: ${d.kpis || 'A definir'}
184
+ - Dashboard: ${d.dashboard ? 'Sim' : 'Não'}
185
+ `;
186
+ }
187
+ function gerarUsers(d) {
188
+ return `# Users — ${d.projeto}
189
+
190
+ > Gerado automaticamente a partir do briefing em ${d.data}
191
+
192
+ ## Público Principal
193
+ ${d.usuarios || 'A definir'}
194
+
195
+ ## Tipos de Usuário
196
+ ${d.multiUsuario ? '> Definir perfis após vireum-spec setup' : '- Usuário único (sem diferenciação de perfis)'}
197
+
198
+ ## Níveis de Acesso
199
+ ${d.niveisAcesso ? '> Detalhar níveis em vireum-spec setup' : '- Acesso único (sem controle de permissões diferenciado)'}
200
+
201
+ ## Jornadas Principais
202
+ > A detalhar durante o desenvolvimento — referenciar requirements.md
203
+ `;
204
+ }
205
+ function gerarRisks(d) {
206
+ return `# Risks — ${d.projeto}
207
+
208
+ > Gerado automaticamente a partir do briefing em ${d.data}
209
+
210
+ ## Riscos Identificados no Briefing
211
+
212
+ ### Técnico
213
+ ${d.riscTecnico || 'Nenhum identificado'}
214
+
215
+ ### Jurídico
216
+ ${d.riscJuridico || 'Nenhum identificado'}
217
+ ${d.lgpd ? '\n> ⚠️ LGPD requerida — atenção ao tratamento de dados pessoais' : ''}
218
+
219
+ ### Operacional
220
+ ${d.riscOperacional || 'Nenhum identificado'}
221
+
222
+ ## Riscos Identificados Durante o Desenvolvimento
223
+ > A IA deve adicionar entradas aqui ao identificar novos riscos
224
+
225
+ | Data | Risco | Impacto | Mitigação |
226
+ |------|-------|---------|-----------|
227
+ | | | | |
228
+ `;
229
+ }
230
+ function gerarArchitecture(d) {
231
+ return `# Architecture — ${d.projeto}
232
+
233
+ > Gerado automaticamente a partir do briefing em ${d.data}
234
+ > Completar com vireum-spec setup
235
+
236
+ ## Stack
237
+ > A definir em vireum-spec setup
238
+
239
+ - Frontend:
240
+ - Backend:
241
+ - Banco de dados:
242
+ - Cache:
243
+ - Auth:
244
+
245
+ ## Infraestrutura
246
+ - Plataforma: ${d.plataforma || 'A definir'}
247
+ - Hospedagem: ${d.hospedagem || 'A definir'}
248
+ - CI/CD: ${d.cicd ? 'Sim' : 'Não definido'}
249
+ - Staging: ${d.staging ? 'Sim' : 'Não'}
250
+
251
+ ## Decisões Arquiteturais
252
+ > A IA deve registrar aqui cada decisão técnica relevante
253
+
254
+ | Data | Decisão | Alternativas descartadas | Motivo |
255
+ |------|---------|--------------------------|--------|
256
+ | | | | |
257
+
258
+ ## MCPs Ativos
259
+ > A configurar em vireum-spec setup
260
+ `;
261
+ }
262
+ function gerarIndex(d) {
263
+ return `# INDEX — ${d.projeto}
264
+
265
+ > Arquivo de entrada. Sempre carregado pela IA no início de cada sessão.
266
+ > Arquivos pesados são carregados por referência — apenas quando a tarefa exigir.
267
+
268
+ ## Projeto
269
+ - **Cliente:** ${d.cliente}
270
+ - **Responsável:** ${d.responsavel}
271
+ - **Fase atual:** MVP
272
+ - **Iniciado em:** ${d.data}
273
+
274
+ ## Estado do MVP
275
+ > Atualizar conforme o desenvolvimento avança
276
+
277
+ - [ ] Nenhuma task concluída ainda
278
+
279
+ ## Arquivos de contexto disponíveis
280
+ Não carregue esses arquivos automaticamente.
281
+ Carregue apenas quando a tarefa exigir:
282
+
283
+ - Escopo e features → leia \`.spec/requirements.md\`
284
+ - Decisões técnicas → leia \`.spec/architecture.md\`
285
+ - Perfis e permissões → leia \`.spec/users.md\`
286
+ - Riscos → leia \`.spec/risks.md\`
287
+ - Tarefas ativas → leia \`.spec/tasks/active.md\`
288
+ - Histórico de decisões → leia \`.spec/changelog.md\`
289
+
290
+ ## Resumo do Objetivo
291
+ ${d.objetivo}
292
+ `;
293
+ }
294
+ function gerarChangelog(d) {
295
+ return `# Changelog — ${d.projeto}
296
+
297
+ > Registro de decisões, mudanças e causa raiz de bugs.
298
+ > A IA deve adicionar entradas aqui ao tomar decisões relevantes ou resolver bugs.
299
+ > Formato: data, tipo, descrição.
300
+
301
+ ## ${d.data} — Projeto iniciado
302
+ - Briefing realizado com ${d.cliente}
303
+ - Spec gerado via vireum-spec distill
304
+ `;
305
+ }
306
+ function gerarRules(d) {
307
+ const extras = [];
308
+ if (d.lgpd)
309
+ extras.push('- Nunca armazenar dados pessoais sem consentimento explícito (LGPD)');
310
+ if (d.dadosSensiveis)
311
+ extras.push('- Dados sensíveis devem ser encriptados em repouso e em trânsito');
312
+ if (d.pagamento)
313
+ extras.push('- Nunca logar dados de pagamento (cartão, CVV, etc.)');
314
+ if (d.niveisAcesso)
315
+ extras.push('- Toda rota deve validar o nível de acesso do usuário');
316
+ return `# Rules — ${d.projeto}
317
+
318
+ > Regras específicas deste projeto geradas a partir do briefing.
319
+ > Estas regras complementam as regras globais em .vireum/rules.md
320
+ > Em caso de conflito, as regras globais prevalecem.
321
+
322
+ ## Regras do Projeto
323
+ ${extras.length ? extras.join('\n') : '- Nenhuma regra específica identificada no briefing'}
324
+
325
+ ## Regras a Definir
326
+ > Completar após vireum-spec setup com decisões técnicas do projeto
327
+ `;
328
+ }
329
+ function gerarTasksActive(d) {
330
+ return `# Tasks — Active
331
+
332
+ > Tarefas em desenvolvimento ativo.
333
+ > Formato: [FEATURE-TIPO_NUMERO] Descrição
334
+ > A IA só implementa tasks desta lista.
335
+ > Ao concluir, mover para done.md
336
+
337
+ ## Como usar
338
+ - [FEATURE-T001] Descrição da task
339
+ - Origem: requirements.md → R-001
340
+ - Critérios: o que deve passar para considerar done
341
+ - Camada: Backend | Frontend | Integração
342
+
343
+ ---
344
+
345
+ > Nenhuma task ativa ainda.
346
+ > Execute vireum-spec prioritize para gerar as tasks do MVP.
347
+ `;
348
+ }
349
+ function gerarTasksBacklog(d) {
350
+ const items = d.features.length
351
+ ? d.features.map((f, i) => `- [ ] [FEATURE-T${String(i + 1).padStart(3, '0')}] ${f} [PENDING: aguarda prioritize]`).join('\n')
352
+ : '- [ ] (nenhuma feature mapeada ainda)';
353
+ return `# Tasks — Backlog
354
+
355
+ > Features mapeadas aguardando priorização.
356
+ > Execute vireum-spec prioritize para classificar em MVP ou Fase 2.
357
+
358
+ ${items}
359
+ `;
360
+ }
361
+ function gerarTasksDone(d) {
362
+ return `# Tasks — Done
363
+
364
+ > Tasks concluídas. Arquivo de referência — raramente entra no contexto ativo.
365
+
366
+ ## ${d.data}
367
+ - Projeto iniciado — spec gerado via vireum-spec distill
368
+ `;
369
+ }
370
+ function sleep(ms) {
371
+ return new Promise(r => setTimeout(r, ms));
372
+ }