vireum-spec-cli 0.5.0 → 0.7.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.
package/dist/index.js CHANGED
@@ -1,11 +1,46 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
3
36
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
37
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
38
  };
6
39
  Object.defineProperty(exports, "__esModule", { value: true });
7
40
  const commander_1 = require("commander");
8
41
  const chalk_1 = __importDefault(require("chalk"));
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
9
44
  const init_1 = require("./commands/init");
10
45
  const distill_1 = require("./commands/distill");
11
46
  const setup_1 = require("./commands/setup");
@@ -16,15 +51,17 @@ const verify_mcps_1 = require("./commands/verify-mcps");
16
51
  const skills_1 = require("./commands/skills");
17
52
  const retrofit_1 = require("./commands/retrofit");
18
53
  const enrich_1 = require("./commands/enrich");
54
+ const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8'));
19
55
  const program = new commander_1.Command();
20
56
  program
21
57
  .name('vireum-spec')
22
58
  .description('Spec Driven Development framework by Vireum Desenvolvimento')
23
- .version('0.1.0');
59
+ .version(pkg.version);
24
60
  program
25
61
  .command('init')
26
62
  .description('Inicia um novo projeto com briefing interativo')
27
- .action(async () => { await (0, init_1.runInit)(); });
63
+ .option('-t, --type <type>', 'Tipo de projeto: system, web, api, mobile, automation, ai', 'system')
64
+ .action(async (options) => { await (0, init_1.runInit)(options.type); });
28
65
  program
29
66
  .command('distill')
30
67
  .description('Gera os arquivos de spec a partir do briefing')
@@ -0,0 +1,194 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.analisarProjeto = analisarProjeto;
37
+ exports.extrairRotas = extrairRotas;
38
+ exports.contarArquivos = contarArquivos;
39
+ const fs = __importStar(require("fs"));
40
+ const path = __importStar(require("path"));
41
+ function analisarProjeto(cwd) {
42
+ const resultado = {
43
+ nome: '',
44
+ descricao: '',
45
+ arquivos: 0,
46
+ readme: false,
47
+ features: [],
48
+ stack: {
49
+ frontend: '',
50
+ backend: '',
51
+ banco: '',
52
+ orm: '',
53
+ auth: '',
54
+ cache: '',
55
+ },
56
+ };
57
+ const pkgPath = path.join(cwd, 'package.json');
58
+ if (fs.existsSync(pkgPath)) {
59
+ try {
60
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
61
+ resultado.nome = pkg.name || '';
62
+ resultado.descricao = pkg.description || '';
63
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
64
+ if (deps['next'])
65
+ resultado.stack.frontend = 'Next.js';
66
+ else if (deps['react'])
67
+ resultado.stack.frontend = 'React';
68
+ else if (deps['vue'])
69
+ resultado.stack.frontend = 'Vue';
70
+ else if (deps['nuxt'])
71
+ resultado.stack.frontend = 'Nuxt';
72
+ if (deps['fastify'])
73
+ resultado.stack.backend = 'Node.js + Fastify';
74
+ else if (deps['express'])
75
+ resultado.stack.backend = 'Node.js + Express';
76
+ else if (deps['@nestjs/core'])
77
+ resultado.stack.backend = 'NestJS';
78
+ if (deps['pg'] || deps['postgres'])
79
+ resultado.stack.banco = 'PostgreSQL';
80
+ else if (deps['mysql2'])
81
+ resultado.stack.banco = 'MySQL';
82
+ else if (deps['better-sqlite3'])
83
+ resultado.stack.banco = 'SQLite';
84
+ else if (deps['mongoose'])
85
+ resultado.stack.banco = 'MongoDB';
86
+ if (deps['prisma'] || deps['@prisma/client'])
87
+ resultado.stack.orm = 'Prisma';
88
+ else if (deps['typeorm'])
89
+ resultado.stack.orm = 'TypeORM';
90
+ else if (deps['drizzle-orm'])
91
+ resultado.stack.orm = 'Drizzle';
92
+ if (deps['jsonwebtoken'])
93
+ resultado.stack.auth = 'JWT';
94
+ else if (deps['next-auth'])
95
+ resultado.stack.auth = 'NextAuth';
96
+ else if (deps['@clerk/nextjs'])
97
+ resultado.stack.auth = 'Clerk';
98
+ if (deps['bullmq'])
99
+ resultado.stack.cache = 'Redis + BullMQ';
100
+ else if (deps['ioredis'] || deps['redis'])
101
+ resultado.stack.cache = 'Redis';
102
+ }
103
+ catch { }
104
+ }
105
+ const readmePath = path.join(cwd, 'README.md');
106
+ if (fs.existsSync(readmePath)) {
107
+ resultado.readme = true;
108
+ const readme = fs.readFileSync(readmePath, 'utf-8');
109
+ const desc = readme.split('\n').find((l) => l.trim() && !l.startsWith('#'));
110
+ if (desc)
111
+ resultado.descricao = resultado.descricao || desc.trim();
112
+ }
113
+ const pastasCodigo = ['src', 'app', 'pages', 'components', 'server', 'api'];
114
+ let totalArquivos = 0;
115
+ const todasRotas = [];
116
+ for (const pasta of pastasCodigo) {
117
+ const dir = path.join(cwd, pasta);
118
+ if (fs.existsSync(dir)) {
119
+ todasRotas.push(...extrairRotas(dir));
120
+ totalArquivos += contarArquivos(dir);
121
+ }
122
+ }
123
+ resultado.features = [...new Set(todasRotas)].slice(0, 20);
124
+ resultado.arquivos = totalArquivos;
125
+ return resultado;
126
+ }
127
+ function extrairRotas(dir) {
128
+ const rotas = [];
129
+ const exts = ['.ts', '.tsx', '.js', '.jsx'];
130
+ function walk(d) {
131
+ if (!fs.existsSync(d))
132
+ return;
133
+ for (const entry of fs.readdirSync(d)) {
134
+ const full = path.join(d, entry);
135
+ const stat = fs.statSync(full);
136
+ if (stat.isDirectory() &&
137
+ !['node_modules', '.next', 'dist', '.git'].includes(entry)) {
138
+ walk(full);
139
+ }
140
+ else if (exts.includes(path.extname(entry))) {
141
+ try {
142
+ const content = fs.readFileSync(full, 'utf-8');
143
+ const apiMatches = content.match(/\.(get|post|put|patch|delete|route)\(['"`]([^'"`]+)/gi);
144
+ if (apiMatches) {
145
+ apiMatches.forEach((m) => {
146
+ const rota = m
147
+ .replace(/\.(get|post|put|patch|delete|route)\(['"`]/i, '')
148
+ .trim();
149
+ if (rota && !rotas.includes(rota))
150
+ rotas.push(rota);
151
+ });
152
+ }
153
+ if (entry === 'page.tsx' || entry === 'page.ts') {
154
+ const relativo = path
155
+ .relative(dir, full)
156
+ .replace(/\\/g, '/')
157
+ .replace('/page.tsx', '')
158
+ .replace('/page.ts', '');
159
+ if (relativo && !rotas.includes(relativo))
160
+ rotas.push(`page: /${relativo}`);
161
+ }
162
+ if (d.includes('pages') &&
163
+ (entry.endsWith('.tsx') || entry.endsWith('.ts')) &&
164
+ !entry.startsWith('_')) {
165
+ const pagina = entry.replace('.tsx', '').replace('.ts', '');
166
+ if (!rotas.includes(pagina))
167
+ rotas.push(`page: ${pagina}`);
168
+ }
169
+ }
170
+ catch { }
171
+ }
172
+ }
173
+ }
174
+ walk(dir);
175
+ return rotas;
176
+ }
177
+ function contarArquivos(dir) {
178
+ let count = 0;
179
+ function walk(d) {
180
+ if (!fs.existsSync(d))
181
+ return;
182
+ for (const entry of fs.readdirSync(d)) {
183
+ const full = path.join(d, entry);
184
+ if (fs.statSync(full).isDirectory() &&
185
+ !['node_modules', '.next', 'dist', '.git'].includes(entry)) {
186
+ walk(full);
187
+ }
188
+ else
189
+ count++;
190
+ }
191
+ }
192
+ walk(dir);
193
+ return count;
194
+ }
@@ -0,0 +1,259 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.gerarBriefing = gerarBriefing;
4
+ exports.gerarRequirements = gerarRequirements;
5
+ exports.gerarArchitecture = gerarArchitecture;
6
+ exports.gerarUsers = gerarUsers;
7
+ exports.gerarRisks = gerarRisks;
8
+ exports.gerarRules = gerarRules;
9
+ exports.gerarIndex = gerarIndex;
10
+ exports.gerarChangelog = gerarChangelog;
11
+ exports.gerarTasksActive = gerarTasksActive;
12
+ exports.gerarTasksBacklog = gerarTasksBacklog;
13
+ exports.gerarTasksDone = gerarTasksDone;
14
+ function gerarBriefing(d) {
15
+ return `# Briefing — ${d.projeto}
16
+
17
+ > Gerado via vireum-spec retrofit em ${d.data}
18
+ > Projeto pre-existente — informacoes inferidas e complementadas
19
+
20
+ ## Informacoes Gerais
21
+ - **Projeto:** ${d.projeto}
22
+ - **Cliente:** ${d.cliente}
23
+ - **Data do retrofit:** ${d.data}
24
+ - **Fase:** ${d.fase}
25
+
26
+ ## Objetivo
27
+ **Objetivo principal:** ${d.objetivo}
28
+ **Problema que resolve:** ${d.problema}
29
+ **Estado atual / prioridade:** ${d.prioridade}
30
+
31
+ ## Usuarios
32
+ ${d.usuarios}
33
+
34
+ ## Funcionalidades Implementadas
35
+ ${d.featuresAtivas.map((f) => `- [x] ${f}`).join('\n') || '- (nao informado)'}
36
+
37
+ ## Funcionalidades Pendentes
38
+ ${d.featuresPendentes.map((f) => `- [ ] ${f}`).join('\n') || '- (nao informado)'}
39
+
40
+ ## Regras de Negocio
41
+ ${d.regras || '(nao informado)'}
42
+
43
+ ## Stack Detectada
44
+ - Frontend: ${d.analise.stack.frontend || 'nao detectado'}
45
+ - Backend: ${d.analise.stack.backend || 'nao detectado'}
46
+ - Banco: ${d.analise.stack.banco || 'nao detectado'}
47
+ - ORM: ${d.analise.stack.orm || 'nao detectado'}
48
+ - Auth: ${d.analise.stack.auth || 'nao detectado'}
49
+
50
+ ## Riscos
51
+ ${d.riscos || '(nenhum identificado)'}
52
+
53
+ ## Conformidade
54
+ - Multi-tenant: ${d.multiTenant ? 'Sim' : 'Nao'}
55
+ - LGPD: ${d.lgpd ? 'Sim' : 'Nao'}
56
+ `;
57
+ }
58
+ function gerarRequirements(d) {
59
+ const implementadas = d.featuresAtivas
60
+ .map((f, i) => `- [x] R-${String(i + 1).padStart(3, '0')} ${f} — JA IMPLEMENTADO`)
61
+ .join('\n');
62
+ const pendentes = d.featuresPendentes
63
+ .map((f, i) => `- [ ] R-${String(d.featuresAtivas.length + i + 1).padStart(3, '0')} ${f}`)
64
+ .join('\n');
65
+ return `# Requirements — ${d.projeto}
66
+
67
+ > Gerado via vireum-spec retrofit em ${d.data}
68
+
69
+ ## Objetivo
70
+ ${d.objetivo}
71
+
72
+ ## Funcionalidades Implementadas
73
+ ${implementadas || '- (nao informado)'}
74
+
75
+ ## Funcionalidades Pendentes (MVP)
76
+ ${pendentes || '- (nao informado)'}
77
+
78
+ ## Funcionalidades Fase 2
79
+ > Preencher apos vireum-spec prioritize
80
+
81
+ ## Regras de Negocio
82
+ ${d.regras || '> A detalhar'}
83
+
84
+ ## Rotas Detectadas Automaticamente
85
+ ${d.analise.features.map((r) => `- ${r}`).join('\n') || '- (nenhuma detectada)'}
86
+ `;
87
+ }
88
+ function gerarArchitecture(d) {
89
+ const tenant = d.multiTenant
90
+ ? '\n## Multi-Tenancy\n- Ativo — nunca fazer query sem filtro de tenantId'
91
+ : '';
92
+ return `# Architecture — ${d.projeto}
93
+
94
+ > Gerado via vireum-spec retrofit em ${d.data}
95
+ > Stack inferida automaticamente — revisar e complementar
96
+
97
+ ## Stack
98
+ - Frontend: ${d.analise.stack.frontend || 'A confirmar'}
99
+ - Backend: ${d.analise.stack.backend || 'A confirmar'}
100
+ - Banco de dados: ${d.analise.stack.banco || 'A confirmar'}
101
+ - ORM: ${d.analise.stack.orm || 'A confirmar'}
102
+ - Cache / Filas: ${d.analise.stack.cache || 'A confirmar'}
103
+ - Auth: ${d.analise.stack.auth || 'A confirmar'}
104
+ ${tenant}
105
+
106
+ ## Decisoes Arquiteturais
107
+ > Registre aqui decisoes tomadas antes do retrofit
108
+
109
+ | Data | Decisao | Alternativas descartadas | Motivo |
110
+ |------|---------|--------------------------|--------|
111
+ | | | | |
112
+
113
+ ## MCPs Ativos
114
+ > Configurar via vireum-spec setup
115
+ `;
116
+ }
117
+ function gerarUsers(d) {
118
+ return `# Users — ${d.projeto}
119
+
120
+ > Gerado via vireum-spec retrofit em ${d.data}
121
+
122
+ ## Publico Principal
123
+ ${d.usuarios}
124
+
125
+ ## Tipos de Usuario e Acesso
126
+ > Detalhar apos analise do codigo existente
127
+
128
+ ## Jornadas Principais
129
+ > A mapear com base nas rotas detectadas
130
+ `;
131
+ }
132
+ function gerarRisks(d) {
133
+ return `# Risks — ${d.projeto}
134
+
135
+ > Gerado via vireum-spec retrofit em ${d.data}
136
+
137
+ ## Riscos Identificados
138
+ - Tecnico: ${d.riscos || 'Nenhum informado'}
139
+ ${d.lgpd ? '- LGPD requerida — revisar tratamento de dados pessoais existente' : ''}
140
+ ${d.multiTenant ? '- Multi-tenant — auditar queries existentes para garantir isolamento' : ''}
141
+
142
+ ## Riscos de Retrofit
143
+ - Spec gerado pode nao refletir 100% da realidade do codigo — revisar arquivos gerados
144
+ - Funcionalidades nao listadas no briefing podem existir no codigo — auditar
145
+
146
+ ## Riscos Identificados Durante o Desenvolvimento
147
+ | Data | Risco | Impacto | Mitigacao |
148
+ |------|-------|---------|-----------|
149
+ | | | | |
150
+ `;
151
+ }
152
+ function gerarRules(d) {
153
+ const extras = [];
154
+ if (d.lgpd)
155
+ extras.push('- Nunca armazenar dados pessoais sem consentimento (LGPD)');
156
+ if (d.multiTenant)
157
+ extras.push('- Nunca fazer query sem filtro de tenantId');
158
+ return `# Rules — ${d.projeto}
159
+
160
+ > Gerado via vireum-spec retrofit em ${d.data}
161
+ > Complementar com regras especificas do projeto apos revisao do codigo
162
+
163
+ ## Regras do Projeto
164
+ ${extras.length ? extras.join('\n') : '- A definir apos revisao do codigo existente'}
165
+ `;
166
+ }
167
+ function gerarIndex(d) {
168
+ const pendentes = d.featuresPendentes
169
+ .map((f, i) => {
170
+ const prefix = f.replace(/\s+/g, '_').toUpperCase().slice(0, 8);
171
+ return `- [ ] [${prefix}-T${String(i + 1).padStart(3, '0')}] ${f}`;
172
+ })
173
+ .join('\n');
174
+ return `# INDEX — ${d.projeto}
175
+
176
+ > Gerado via vireum-spec retrofit em ${d.data}
177
+ > Arquivo de entrada — sempre carregado pela IA no inicio de cada sessao
178
+
179
+ ## Projeto
180
+ - **Cliente:** ${d.cliente}
181
+ - **Fase atual:** ${d.fase}
182
+ - **Retrofit em:** ${d.data}
183
+
184
+ ## Estado Atual
185
+ ${pendentes || '- Nenhuma task pendente mapeada ainda'}
186
+
187
+ ## Arquivos de contexto disponiveis
188
+ Nao carregue automaticamente. Carregue apenas quando a task exigir:
189
+
190
+ - Escopo e features → leia \`.spec/requirements.md\`
191
+ - Decisoes tecnicas → leia \`.spec/architecture.md\`
192
+ - Perfis e permissoes → leia \`.spec/users.md\`
193
+ - Riscos → leia \`.spec/risks.md\`
194
+ - Tarefas ativas → leia \`.spec/tasks/active.md\`
195
+ - Historico → leia \`.spec/changelog.md\`
196
+
197
+ ## Resumo do Objetivo
198
+ ${d.objetivo}
199
+ `;
200
+ }
201
+ function gerarChangelog(d) {
202
+ return `# Changelog — ${d.projeto}
203
+
204
+ > Registro de decisoes, mudancas e causa raiz de bugs.
205
+
206
+ ## ${d.data} — Retrofit realizado
207
+ - Spec gerado a partir de projeto pre-existente via vireum-spec retrofit
208
+ - Stack detectada automaticamente: ${[
209
+ d.analise.stack.frontend,
210
+ d.analise.stack.backend,
211
+ d.analise.stack.banco,
212
+ ]
213
+ .filter(Boolean)
214
+ .join(', ') || 'a confirmar'}
215
+ - Fase no momento do retrofit: ${d.fase}
216
+ `;
217
+ }
218
+ function gerarTasksActive(d) {
219
+ if (d.featuresPendentes.length === 0) {
220
+ return `# Tasks — Active\n\n> Nenhuma task pendente mapeada no retrofit.\n> Execute vireum-spec prioritize para organizar.\n`;
221
+ }
222
+ const tasks = d.featuresPendentes
223
+ .map((f, i) => {
224
+ const prefix = f.replace(/\s+/g, '_').toUpperCase().slice(0, 8);
225
+ const num = String(i + 1).padStart(3, '0');
226
+ return `## [${prefix}-T${num}] ${f}
227
+ - **Origem:** retrofit — funcionalidade pendente
228
+ - **Camada:** A definir
229
+ - **Complexidade:** A definir
230
+ - **Criterios de aceitacao:**
231
+ - (definir antes de implementar)
232
+ - **Status:** [ ] Nao iniciada
233
+ `;
234
+ })
235
+ .join('\n');
236
+ return `# Tasks — Active
237
+
238
+ > Tasks pendentes identificadas no retrofit.
239
+ > Revise e complemente os criterios de aceitacao antes de comecar.
240
+
241
+ ---
242
+
243
+ ${tasks}`;
244
+ }
245
+ function gerarTasksBacklog(d) {
246
+ return `# Tasks — Backlog\n\n> Preencher apos priorizacao via vireum-spec prioritize.\n`;
247
+ }
248
+ function gerarTasksDone(d) {
249
+ const done = d.featuresAtivas
250
+ .map((f) => `- [x] ${f} — pre-existente ao retrofit`)
251
+ .join('\n');
252
+ return `# Tasks — Done
253
+
254
+ > Funcionalidades ja implementadas antes do retrofit.
255
+
256
+ ## ${d.data} — Retrofit
257
+ ${done || '- (nenhuma listada)'}
258
+ `;
259
+ }
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const generators_retrofit_1 = require("./generators-retrofit");
5
+ const mockData = {
6
+ projeto: 'Retrofit Test',
7
+ cliente: 'Test Client',
8
+ objetivo: 'Test objective',
9
+ problema: 'Test problem',
10
+ prioridade: 'High',
11
+ usuarios: 'Test users',
12
+ regras: 'Test rules',
13
+ fase: 'MVP em desenvolvimento',
14
+ multiTenant: false,
15
+ lgpd: false,
16
+ featuresAtivas: ['Feature 1', 'Feature 2'],
17
+ featuresPendentes: ['Feature 3', 'Feature 4'],
18
+ riscos: 'Test risks',
19
+ data: '01/01/2025',
20
+ analise: {
21
+ nome: 'test-project',
22
+ descricao: 'Test description',
23
+ arquivos: 100,
24
+ readme: true,
25
+ features: ['route /api/users', 'route /api/posts'],
26
+ stack: {
27
+ frontend: 'Next.js',
28
+ backend: 'Express',
29
+ banco: 'PostgreSQL',
30
+ orm: 'Prisma',
31
+ auth: 'JWT',
32
+ cache: 'Redis',
33
+ },
34
+ },
35
+ };
36
+ (0, vitest_1.describe)('Generators — Retrofit', () => {
37
+ (0, vitest_1.describe)('gerarBriefing', () => {
38
+ (0, vitest_1.it)('should generate valid briefing structure', () => {
39
+ const result = (0, generators_retrofit_1.gerarBriefing)(mockData);
40
+ (0, vitest_1.expect)(result).toContain('# Briefing — Retrofit Test');
41
+ (0, vitest_1.expect)(result).toContain('## Informacoes Gerais');
42
+ (0, vitest_1.expect)(result).toContain('## Objetivo');
43
+ (0, vitest_1.expect)(result).toContain('## Stack Detectada');
44
+ (0, vitest_1.expect)(result).toMatchSnapshot();
45
+ });
46
+ (0, vitest_1.it)('should include LGPD warning when applicable', () => {
47
+ const dataWithLGPD = { ...mockData, lgpd: true };
48
+ const result = (0, generators_retrofit_1.gerarBriefing)(dataWithLGPD);
49
+ (0, vitest_1.expect)(result).toContain('LGPD: Sim');
50
+ (0, vitest_1.expect)(result).toMatchSnapshot();
51
+ });
52
+ (0, vitest_1.it)('should include multi-tenant note', () => {
53
+ const dataWithTenant = { ...mockData, multiTenant: true };
54
+ const result = (0, generators_retrofit_1.gerarBriefing)(dataWithTenant);
55
+ (0, vitest_1.expect)(result).toContain('Multi-tenant: Sim');
56
+ (0, vitest_1.expect)(result).toMatchSnapshot();
57
+ });
58
+ });
59
+ (0, vitest_1.describe)('gerarRequirements', () => {
60
+ (0, vitest_1.it)('should generate requirements with MVP and Phase 2', () => {
61
+ const result = (0, generators_retrofit_1.gerarRequirements)(mockData);
62
+ (0, vitest_1.expect)(result).toContain('# Requirements — Retrofit Test');
63
+ (0, vitest_1.expect)(result).toContain('## Objetivo');
64
+ (0, vitest_1.expect)(result).toContain('## Funcionalidades Implementadas');
65
+ (0, vitest_1.expect)(result).toContain('R-001 Feature 1');
66
+ (0, vitest_1.expect)(result).toContain('## Rotas Detectadas Automaticamente');
67
+ (0, vitest_1.expect)(result).toMatchSnapshot();
68
+ });
69
+ });
70
+ (0, vitest_1.describe)('gerarArchitecture', () => {
71
+ (0, vitest_1.it)('should generate architecture document', () => {
72
+ const result = (0, generators_retrofit_1.gerarArchitecture)(mockData);
73
+ (0, vitest_1.expect)(result).toContain('# Architecture — Retrofit Test');
74
+ (0, vitest_1.expect)(result).toContain('## Stack');
75
+ (0, vitest_1.expect)(result).toContain('Next.js');
76
+ (0, vitest_1.expect)(result).toContain('PostgreSQL');
77
+ (0, vitest_1.expect)(result).toMatchSnapshot();
78
+ });
79
+ (0, vitest_1.it)('should include multi-tenant section when applicable', () => {
80
+ const dataWithTenant = { ...mockData, multiTenant: true };
81
+ const result = (0, generators_retrofit_1.gerarArchitecture)(dataWithTenant);
82
+ (0, vitest_1.expect)(result).toContain('## Multi-Tenancy');
83
+ (0, vitest_1.expect)(result).toMatchSnapshot();
84
+ });
85
+ });
86
+ (0, vitest_1.describe)('gerarIndex', () => {
87
+ (0, vitest_1.it)('should generate INDEX.md with pending tasks', () => {
88
+ const result = (0, generators_retrofit_1.gerarIndex)(mockData);
89
+ (0, vitest_1.expect)(result).toContain('# INDEX — Retrofit Test');
90
+ (0, vitest_1.expect)(result).toContain('## Projeto');
91
+ (0, vitest_1.expect)(result).toContain('## Estado Atual');
92
+ (0, vitest_1.expect)(result).toContain('## Arquivos de contexto disponiveis');
93
+ (0, vitest_1.expect)(result).toMatchSnapshot();
94
+ });
95
+ });
96
+ (0, vitest_1.describe)('gerarTasksActive', () => {
97
+ (0, vitest_1.it)('should generate active tasks from pending features', () => {
98
+ const result = (0, generators_retrofit_1.gerarTasksActive)(mockData);
99
+ (0, vitest_1.expect)(result).toContain('# Tasks — Active');
100
+ (0, vitest_1.expect)(result).toContain('Feature 3');
101
+ (0, vitest_1.expect)(result).toContain('Feature 4');
102
+ (0, vitest_1.expect)(result).toContain('Criterios de aceitacao');
103
+ (0, vitest_1.expect)(result).toMatchSnapshot();
104
+ });
105
+ (0, vitest_1.it)('should return empty message when no pending features', () => {
106
+ const dataEmpty = { ...mockData, featuresPendentes: [] };
107
+ const result = (0, generators_retrofit_1.gerarTasksActive)(dataEmpty);
108
+ (0, vitest_1.expect)(result).toContain('Nenhuma task pendente mapeada');
109
+ (0, vitest_1.expect)(result).toMatchSnapshot();
110
+ });
111
+ });
112
+ });