vireum-spec-cli 0.4.2 → 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/README.md +259 -0
- package/dist/commands/brief.js +0 -4
- package/dist/commands/distill.js +64 -301
- package/dist/commands/enrich.js +139 -0
- package/dist/commands/health.js +0 -4
- package/dist/commands/init/ai.js +186 -0
- package/dist/commands/init/api.js +195 -0
- package/dist/commands/init/automation.js +166 -0
- package/dist/commands/init/mobile.js +155 -0
- package/dist/commands/init/system.js +307 -0
- package/dist/commands/init/web.js +165 -0
- package/dist/commands/init.js +21 -253
- package/dist/commands/prioritize.js +82 -34
- package/dist/commands/retrofit.js +14 -400
- package/dist/commands/setup.js +56 -488
- package/dist/commands/skills.js +0 -6
- package/dist/commands/verify-mcps.js +0 -4
- package/dist/index.js +44 -2
- package/dist/lib/analyzer.js +194 -0
- package/dist/lib/generators-retrofit.js +259 -0
- package/dist/lib/generators-retrofit.test.js +112 -0
- package/dist/lib/generators-setup.js +764 -0
- package/dist/lib/generators-setup.test.js +118 -0
- package/dist/lib/generators.js +593 -0
- package/dist/lib/parsers/ai.js +54 -0
- package/dist/lib/parsers/api.js +63 -0
- package/dist/lib/parsers/automation.js +52 -0
- package/dist/lib/parsers/mobile.js +60 -0
- package/dist/lib/parsers/system.js +66 -0
- package/dist/lib/parsers/web.js +70 -0
- package/dist/lib/types.js +2 -0
- package/docs/COMO_USAR.md +322 -0
- package/docs/DOCUMENTACAO_FRAMEWORK.md +568 -0
- package/package.json +9 -3
|
@@ -42,6 +42,8 @@ const prompts_1 = require("@inquirer/prompts");
|
|
|
42
42
|
const ora_1 = __importDefault(require("ora"));
|
|
43
43
|
const fs = __importStar(require("fs"));
|
|
44
44
|
const path = __importStar(require("path"));
|
|
45
|
+
const analyzer_1 = require("../lib/analyzer");
|
|
46
|
+
const gen = __importStar(require("../lib/generators-retrofit"));
|
|
45
47
|
async function runRetrofit() {
|
|
46
48
|
console.log(chalk_1.default.hex('#2D7DD2').bold('\n Vireum Spec — Retrofit\n'));
|
|
47
49
|
console.log(chalk_1.default.gray(' Gera o spec a partir de um projeto ja em andamento\n'));
|
|
@@ -57,8 +59,7 @@ async function runRetrofit() {
|
|
|
57
59
|
}
|
|
58
60
|
// ── Analise automatica ─────────────────────────────────────────────────────
|
|
59
61
|
const spinner = (0, ora_1.default)('Analisando projeto...').start();
|
|
60
|
-
|
|
61
|
-
const analise = analisarProjeto(cwd);
|
|
62
|
+
const analise = (0, analyzer_1.analisarProjeto)(cwd);
|
|
62
63
|
spinner.succeed(`Projeto analisado — ${analise.arquivos} arquivos encontrados`);
|
|
63
64
|
console.log('');
|
|
64
65
|
console.log(chalk_1.default.hex('#2D7DD2').bold(' O que foi detectado automaticamente:\n'));
|
|
@@ -139,26 +140,24 @@ async function runRetrofit() {
|
|
|
139
140
|
if (!fs.existsSync(tasksDir))
|
|
140
141
|
fs.mkdirSync(tasksDir, { recursive: true });
|
|
141
142
|
const arquivos = [
|
|
142
|
-
{ nome: 'briefing.md', conteudo: gerarBriefing(dados), msg: 'Gerando briefing.md...' },
|
|
143
|
-
{ nome: 'requirements.md', conteudo: gerarRequirements(dados), msg: 'Gerando requirements.md...' },
|
|
144
|
-
{ nome: 'architecture.md', conteudo: gerarArchitecture(dados), msg: 'Gerando architecture.md...' },
|
|
145
|
-
{ nome: 'users.md', conteudo: gerarUsers(dados), msg: 'Gerando users.md...' },
|
|
146
|
-
{ nome: 'risks.md', conteudo: gerarRisks(dados), msg: 'Gerando risks.md...' },
|
|
147
|
-
{ nome: 'rules.md', conteudo: gerarRules(dados), msg: 'Gerando rules.md...' },
|
|
148
|
-
{ nome: 'INDEX.md', conteudo: gerarIndex(dados), msg: 'Gerando INDEX.md...' },
|
|
149
|
-
{ nome: 'changelog.md', conteudo: gerarChangelog(dados), msg: 'Gerando changelog.md...' },
|
|
143
|
+
{ nome: 'briefing.md', conteudo: gen.gerarBriefing(dados), msg: 'Gerando briefing.md...' },
|
|
144
|
+
{ nome: 'requirements.md', conteudo: gen.gerarRequirements(dados), msg: 'Gerando requirements.md...' },
|
|
145
|
+
{ nome: 'architecture.md', conteudo: gen.gerarArchitecture(dados), msg: 'Gerando architecture.md...' },
|
|
146
|
+
{ nome: 'users.md', conteudo: gen.gerarUsers(dados), msg: 'Gerando users.md...' },
|
|
147
|
+
{ nome: 'risks.md', conteudo: gen.gerarRisks(dados), msg: 'Gerando risks.md...' },
|
|
148
|
+
{ nome: 'rules.md', conteudo: gen.gerarRules(dados), msg: 'Gerando rules.md...' },
|
|
149
|
+
{ nome: 'INDEX.md', conteudo: gen.gerarIndex(dados), msg: 'Gerando INDEX.md...' },
|
|
150
|
+
{ nome: 'changelog.md', conteudo: gen.gerarChangelog(dados), msg: 'Gerando changelog.md...' },
|
|
150
151
|
];
|
|
151
152
|
for (const arq of arquivos) {
|
|
152
153
|
const s = (0, ora_1.default)(arq.msg).start();
|
|
153
|
-
await sleep(400);
|
|
154
154
|
fs.writeFileSync(path.join(specDir, arq.nome), arq.conteudo, 'utf-8');
|
|
155
155
|
s.succeed(arq.nome);
|
|
156
156
|
}
|
|
157
157
|
const s1 = (0, ora_1.default)('Gerando tasks...').start();
|
|
158
|
-
|
|
159
|
-
fs.writeFileSync(path.join(tasksDir, '
|
|
160
|
-
fs.writeFileSync(path.join(tasksDir, '
|
|
161
|
-
fs.writeFileSync(path.join(tasksDir, 'done.md'), gerarTasksDone(dados), 'utf-8');
|
|
158
|
+
fs.writeFileSync(path.join(tasksDir, 'active.md'), gen.gerarTasksActive(dados), 'utf-8');
|
|
159
|
+
fs.writeFileSync(path.join(tasksDir, 'backlog.md'), gen.gerarTasksBacklog(dados), 'utf-8');
|
|
160
|
+
fs.writeFileSync(path.join(tasksDir, 'done.md'), gen.gerarTasksDone(dados), 'utf-8');
|
|
162
161
|
s1.succeed('tasks/ gerado');
|
|
163
162
|
console.log(chalk_1.default.green.bold('\n✅ Retrofit concluido!\n'));
|
|
164
163
|
console.log(chalk_1.default.gray(' Spec gerado em .spec/ a partir do projeto existente\n'));
|
|
@@ -167,388 +166,3 @@ async function runRetrofit() {
|
|
|
167
166
|
console.log(chalk_1.default.gray(' 2. Execute: ') + chalk_1.default.white('vireum-spec setup') + chalk_1.default.gray(' para configurar o protocolo da IA'));
|
|
168
167
|
console.log(chalk_1.default.gray(' 3. Execute: ') + chalk_1.default.white('vireum-spec prioritize') + chalk_1.default.gray(' para organizar as tasks pendentes\n'));
|
|
169
168
|
}
|
|
170
|
-
// ── ANALISADOR ────────────────────────────────────────────────────────────────
|
|
171
|
-
function analisarProjeto(cwd) {
|
|
172
|
-
const resultado = {
|
|
173
|
-
nome: '',
|
|
174
|
-
descricao: '',
|
|
175
|
-
arquivos: 0,
|
|
176
|
-
readme: false,
|
|
177
|
-
features: [],
|
|
178
|
-
stack: {
|
|
179
|
-
frontend: '',
|
|
180
|
-
backend: '',
|
|
181
|
-
banco: '',
|
|
182
|
-
orm: '',
|
|
183
|
-
auth: '',
|
|
184
|
-
cache: '',
|
|
185
|
-
},
|
|
186
|
-
};
|
|
187
|
-
const pkgPath = path.join(cwd, 'package.json');
|
|
188
|
-
if (fs.existsSync(pkgPath)) {
|
|
189
|
-
try {
|
|
190
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
191
|
-
resultado.nome = pkg.name || '';
|
|
192
|
-
resultado.descricao = pkg.description || '';
|
|
193
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
194
|
-
if (deps['next'])
|
|
195
|
-
resultado.stack.frontend = 'Next.js';
|
|
196
|
-
else if (deps['react'])
|
|
197
|
-
resultado.stack.frontend = 'React';
|
|
198
|
-
else if (deps['vue'])
|
|
199
|
-
resultado.stack.frontend = 'Vue';
|
|
200
|
-
else if (deps['nuxt'])
|
|
201
|
-
resultado.stack.frontend = 'Nuxt';
|
|
202
|
-
if (deps['fastify'])
|
|
203
|
-
resultado.stack.backend = 'Node.js + Fastify';
|
|
204
|
-
else if (deps['express'])
|
|
205
|
-
resultado.stack.backend = 'Node.js + Express';
|
|
206
|
-
else if (deps['@nestjs/core'])
|
|
207
|
-
resultado.stack.backend = 'NestJS';
|
|
208
|
-
if (deps['pg'] || deps['postgres'])
|
|
209
|
-
resultado.stack.banco = 'PostgreSQL';
|
|
210
|
-
else if (deps['mysql2'])
|
|
211
|
-
resultado.stack.banco = 'MySQL';
|
|
212
|
-
else if (deps['better-sqlite3'])
|
|
213
|
-
resultado.stack.banco = 'SQLite';
|
|
214
|
-
else if (deps['mongoose'])
|
|
215
|
-
resultado.stack.banco = 'MongoDB';
|
|
216
|
-
if (deps['prisma'] || deps['@prisma/client'])
|
|
217
|
-
resultado.stack.orm = 'Prisma';
|
|
218
|
-
else if (deps['typeorm'])
|
|
219
|
-
resultado.stack.orm = 'TypeORM';
|
|
220
|
-
else if (deps['drizzle-orm'])
|
|
221
|
-
resultado.stack.orm = 'Drizzle';
|
|
222
|
-
if (deps['jsonwebtoken'])
|
|
223
|
-
resultado.stack.auth = 'JWT';
|
|
224
|
-
else if (deps['next-auth'])
|
|
225
|
-
resultado.stack.auth = 'NextAuth';
|
|
226
|
-
else if (deps['@clerk/nextjs'])
|
|
227
|
-
resultado.stack.auth = 'Clerk';
|
|
228
|
-
if (deps['bullmq'])
|
|
229
|
-
resultado.stack.cache = 'Redis + BullMQ';
|
|
230
|
-
else if (deps['ioredis'] || deps['redis'])
|
|
231
|
-
resultado.stack.cache = 'Redis';
|
|
232
|
-
}
|
|
233
|
-
catch { }
|
|
234
|
-
}
|
|
235
|
-
const readmePath = path.join(cwd, 'README.md');
|
|
236
|
-
if (fs.existsSync(readmePath)) {
|
|
237
|
-
resultado.readme = true;
|
|
238
|
-
const readme = fs.readFileSync(readmePath, 'utf-8');
|
|
239
|
-
const desc = readme.split('\n').find(l => l.trim() && !l.startsWith('#'));
|
|
240
|
-
if (desc)
|
|
241
|
-
resultado.descricao = resultado.descricao || desc.trim();
|
|
242
|
-
}
|
|
243
|
-
const pastasCodigo = ['src', 'app', 'pages', 'components', 'server', 'api'];
|
|
244
|
-
let totalArquivos = 0;
|
|
245
|
-
const todasRotas = [];
|
|
246
|
-
for (const pasta of pastasCodigo) {
|
|
247
|
-
const dir = path.join(cwd, pasta);
|
|
248
|
-
if (fs.existsSync(dir)) {
|
|
249
|
-
todasRotas.push(...extrairRotas(dir));
|
|
250
|
-
totalArquivos += contarArquivos(dir);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
resultado.features = [...new Set(todasRotas)].slice(0, 20);
|
|
254
|
-
resultado.arquivos = totalArquivos;
|
|
255
|
-
return resultado;
|
|
256
|
-
}
|
|
257
|
-
function extrairRotas(dir) {
|
|
258
|
-
const rotas = [];
|
|
259
|
-
const exts = ['.ts', '.tsx', '.js', '.jsx'];
|
|
260
|
-
function walk(d) {
|
|
261
|
-
if (!fs.existsSync(d))
|
|
262
|
-
return;
|
|
263
|
-
for (const entry of fs.readdirSync(d)) {
|
|
264
|
-
const full = path.join(d, entry);
|
|
265
|
-
const stat = fs.statSync(full);
|
|
266
|
-
if (stat.isDirectory() && !['node_modules', '.next', 'dist', '.git'].includes(entry)) {
|
|
267
|
-
walk(full);
|
|
268
|
-
}
|
|
269
|
-
else if (exts.includes(path.extname(entry))) {
|
|
270
|
-
try {
|
|
271
|
-
const content = fs.readFileSync(full, 'utf-8');
|
|
272
|
-
const apiMatches = content.match(/\.(get|post|put|patch|delete|route)\(['"`]([^'"`]+)/gi);
|
|
273
|
-
if (apiMatches) {
|
|
274
|
-
apiMatches.forEach(m => {
|
|
275
|
-
const rota = m.replace(/\.(get|post|put|patch|delete|route)\(['"`]/i, '').trim();
|
|
276
|
-
if (rota && !rotas.includes(rota))
|
|
277
|
-
rotas.push(rota);
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
if (entry === 'page.tsx' || entry === 'page.ts') {
|
|
281
|
-
const relativo = path.relative(dir, full)
|
|
282
|
-
.replace(/\\/g, '/')
|
|
283
|
-
.replace('/page.tsx', '')
|
|
284
|
-
.replace('/page.ts', '');
|
|
285
|
-
if (relativo && !rotas.includes(relativo))
|
|
286
|
-
rotas.push(`page: /${relativo}`);
|
|
287
|
-
}
|
|
288
|
-
if (d.includes('pages') && (entry.endsWith('.tsx') || entry.endsWith('.ts')) && !entry.startsWith('_')) {
|
|
289
|
-
const pagina = entry.replace('.tsx', '').replace('.ts', '');
|
|
290
|
-
if (!rotas.includes(pagina))
|
|
291
|
-
rotas.push(`page: ${pagina}`);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
catch { }
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
walk(dir);
|
|
299
|
-
return rotas;
|
|
300
|
-
}
|
|
301
|
-
function contarArquivos(dir) {
|
|
302
|
-
let count = 0;
|
|
303
|
-
function walk(d) {
|
|
304
|
-
if (!fs.existsSync(d))
|
|
305
|
-
return;
|
|
306
|
-
for (const entry of fs.readdirSync(d)) {
|
|
307
|
-
const full = path.join(d, entry);
|
|
308
|
-
if (fs.statSync(full).isDirectory() && !['node_modules', '.next', 'dist', '.git'].includes(entry)) {
|
|
309
|
-
walk(full);
|
|
310
|
-
}
|
|
311
|
-
else
|
|
312
|
-
count++;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
walk(dir);
|
|
316
|
-
return count;
|
|
317
|
-
}
|
|
318
|
-
// ── GERADORES ─────────────────────────────────────────────────────────────────
|
|
319
|
-
function gerarBriefing(d) {
|
|
320
|
-
return `# Briefing — ${d.projeto}
|
|
321
|
-
|
|
322
|
-
> Gerado via vireum-spec retrofit em ${d.data}
|
|
323
|
-
> Projeto pre-existente — informacoes inferidas e complementadas
|
|
324
|
-
|
|
325
|
-
## Informacoes Gerais
|
|
326
|
-
- **Projeto:** ${d.projeto}
|
|
327
|
-
- **Cliente:** ${d.cliente}
|
|
328
|
-
- **Data do retrofit:** ${d.data}
|
|
329
|
-
- **Fase:** ${d.fase}
|
|
330
|
-
|
|
331
|
-
## Objetivo
|
|
332
|
-
**Objetivo principal:** ${d.objetivo}
|
|
333
|
-
**Problema que resolve:** ${d.problema}
|
|
334
|
-
**Estado atual / prioridade:** ${d.prioridade}
|
|
335
|
-
|
|
336
|
-
## Usuarios
|
|
337
|
-
${d.usuarios}
|
|
338
|
-
|
|
339
|
-
## Funcionalidades Implementadas
|
|
340
|
-
${d.featuresAtivas.map((f) => `- [x] ${f}`).join('\n') || '- (nao informado)'}
|
|
341
|
-
|
|
342
|
-
## Funcionalidades Pendentes
|
|
343
|
-
${d.featuresPendentes.map((f) => `- [ ] ${f}`).join('\n') || '- (nao informado)'}
|
|
344
|
-
|
|
345
|
-
## Regras de Negocio
|
|
346
|
-
${d.regras || '(nao informado)'}
|
|
347
|
-
|
|
348
|
-
## Stack Detectada
|
|
349
|
-
- Frontend: ${d.analise.stack.frontend || 'nao detectado'}
|
|
350
|
-
- Backend: ${d.analise.stack.backend || 'nao detectado'}
|
|
351
|
-
- Banco: ${d.analise.stack.banco || 'nao detectado'}
|
|
352
|
-
- ORM: ${d.analise.stack.orm || 'nao detectado'}
|
|
353
|
-
- Auth: ${d.analise.stack.auth || 'nao detectado'}
|
|
354
|
-
|
|
355
|
-
## Riscos
|
|
356
|
-
${d.riscos || '(nenhum identificado)'}
|
|
357
|
-
|
|
358
|
-
## Conformidade
|
|
359
|
-
- Multi-tenant: ${d.multiTenant ? 'Sim' : 'Nao'}
|
|
360
|
-
- LGPD: ${d.lgpd ? 'Sim' : 'Nao'}
|
|
361
|
-
`;
|
|
362
|
-
}
|
|
363
|
-
function gerarRequirements(d) {
|
|
364
|
-
const implementadas = d.featuresAtivas.map((f, i) => `- [x] R-${String(i + 1).padStart(3, '0')} ${f} — JA IMPLEMENTADO`).join('\n');
|
|
365
|
-
const pendentes = d.featuresPendentes.map((f, i) => `- [ ] R-${String(d.featuresAtivas.length + i + 1).padStart(3, '0')} ${f}`).join('\n');
|
|
366
|
-
return `# Requirements — ${d.projeto}
|
|
367
|
-
|
|
368
|
-
> Gerado via vireum-spec retrofit em ${d.data}
|
|
369
|
-
|
|
370
|
-
## Objetivo
|
|
371
|
-
${d.objetivo}
|
|
372
|
-
|
|
373
|
-
## Funcionalidades Implementadas
|
|
374
|
-
${implementadas || '- (nao informado)'}
|
|
375
|
-
|
|
376
|
-
## Funcionalidades Pendentes (MVP)
|
|
377
|
-
${pendentes || '- (nao informado)'}
|
|
378
|
-
|
|
379
|
-
## Funcionalidades Fase 2
|
|
380
|
-
> Preencher apos vireum-spec prioritize
|
|
381
|
-
|
|
382
|
-
## Regras de Negocio
|
|
383
|
-
${d.regras || '> A detalhar'}
|
|
384
|
-
|
|
385
|
-
## Rotas Detectadas Automaticamente
|
|
386
|
-
${d.analise.features.map((r) => `- ${r}`).join('\n') || '- (nenhuma detectada)'}
|
|
387
|
-
`;
|
|
388
|
-
}
|
|
389
|
-
function gerarArchitecture(d) {
|
|
390
|
-
const tenant = d.multiTenant
|
|
391
|
-
? '\n## Multi-Tenancy\n- Ativo — nunca fazer query sem filtro de tenantId' : '';
|
|
392
|
-
return `# Architecture — ${d.projeto}
|
|
393
|
-
|
|
394
|
-
> Gerado via vireum-spec retrofit em ${d.data}
|
|
395
|
-
> Stack inferida automaticamente — revisar e complementar
|
|
396
|
-
|
|
397
|
-
## Stack
|
|
398
|
-
- Frontend: ${d.analise.stack.frontend || 'A confirmar'}
|
|
399
|
-
- Backend: ${d.analise.stack.backend || 'A confirmar'}
|
|
400
|
-
- Banco de dados: ${d.analise.stack.banco || 'A confirmar'}
|
|
401
|
-
- ORM: ${d.analise.stack.orm || 'A confirmar'}
|
|
402
|
-
- Cache / Filas: ${d.analise.stack.cache || 'A confirmar'}
|
|
403
|
-
- Auth: ${d.analise.stack.auth || 'A confirmar'}
|
|
404
|
-
${tenant}
|
|
405
|
-
|
|
406
|
-
## Decisoes Arquiteturais
|
|
407
|
-
> Registre aqui decisoes tomadas antes do retrofit
|
|
408
|
-
|
|
409
|
-
| Data | Decisao | Alternativas descartadas | Motivo |
|
|
410
|
-
|------|---------|--------------------------|--------|
|
|
411
|
-
| | | | |
|
|
412
|
-
|
|
413
|
-
## MCPs Ativos
|
|
414
|
-
> Configurar via vireum-spec setup
|
|
415
|
-
`;
|
|
416
|
-
}
|
|
417
|
-
function gerarUsers(d) {
|
|
418
|
-
return `# Users — ${d.projeto}
|
|
419
|
-
|
|
420
|
-
> Gerado via vireum-spec retrofit em ${d.data}
|
|
421
|
-
|
|
422
|
-
## Publico Principal
|
|
423
|
-
${d.usuarios}
|
|
424
|
-
|
|
425
|
-
## Tipos de Usuario e Acesso
|
|
426
|
-
> Detalhar apos analise do codigo existente
|
|
427
|
-
|
|
428
|
-
## Jornadas Principais
|
|
429
|
-
> A mapear com base nas rotas detectadas
|
|
430
|
-
`;
|
|
431
|
-
}
|
|
432
|
-
function gerarRisks(d) {
|
|
433
|
-
return `# Risks — ${d.projeto}
|
|
434
|
-
|
|
435
|
-
> Gerado via vireum-spec retrofit em ${d.data}
|
|
436
|
-
|
|
437
|
-
## Riscos Identificados
|
|
438
|
-
- Tecnico: ${d.riscos || 'Nenhum informado'}
|
|
439
|
-
${d.lgpd ? '- LGPD requerida — revisar tratamento de dados pessoais existente' : ''}
|
|
440
|
-
${d.multiTenant ? '- Multi-tenant — auditar queries existentes para garantir isolamento' : ''}
|
|
441
|
-
|
|
442
|
-
## Riscos de Retrofit
|
|
443
|
-
- Spec gerado pode nao refletir 100% da realidade do codigo — revisar arquivos gerados
|
|
444
|
-
- Funcionalidades nao listadas no briefing podem existir no codigo — auditar
|
|
445
|
-
|
|
446
|
-
## Riscos Identificados Durante o Desenvolvimento
|
|
447
|
-
| Data | Risco | Impacto | Mitigacao |
|
|
448
|
-
|------|-------|---------|-----------|
|
|
449
|
-
| | | | |
|
|
450
|
-
`;
|
|
451
|
-
}
|
|
452
|
-
function gerarRules(d) {
|
|
453
|
-
const extras = [];
|
|
454
|
-
if (d.lgpd)
|
|
455
|
-
extras.push('- Nunca armazenar dados pessoais sem consentimento (LGPD)');
|
|
456
|
-
if (d.multiTenant)
|
|
457
|
-
extras.push('- Nunca fazer query sem filtro de tenantId');
|
|
458
|
-
return `# Rules — ${d.projeto}
|
|
459
|
-
|
|
460
|
-
> Gerado via vireum-spec retrofit em ${d.data}
|
|
461
|
-
> Complementar com regras especificas do projeto apos revisao do codigo
|
|
462
|
-
|
|
463
|
-
## Regras do Projeto
|
|
464
|
-
${extras.length ? extras.join('\n') : '- A definir apos revisao do codigo existente'}
|
|
465
|
-
`;
|
|
466
|
-
}
|
|
467
|
-
function gerarIndex(d) {
|
|
468
|
-
const pendentes = d.featuresPendentes.map((f, i) => {
|
|
469
|
-
const prefix = f.replace(/\s+/g, '_').toUpperCase().slice(0, 8);
|
|
470
|
-
return `- [ ] [${prefix}-T${String(i + 1).padStart(3, '0')}] ${f}`;
|
|
471
|
-
}).join('\n');
|
|
472
|
-
return `# INDEX — ${d.projeto}
|
|
473
|
-
|
|
474
|
-
> Gerado via vireum-spec retrofit em ${d.data}
|
|
475
|
-
> Arquivo de entrada — sempre carregado pela IA no inicio de cada sessao
|
|
476
|
-
|
|
477
|
-
## Projeto
|
|
478
|
-
- **Cliente:** ${d.cliente}
|
|
479
|
-
- **Fase atual:** ${d.fase}
|
|
480
|
-
- **Retrofit em:** ${d.data}
|
|
481
|
-
|
|
482
|
-
## Estado Atual
|
|
483
|
-
${pendentes || '- Nenhuma task pendente mapeada ainda'}
|
|
484
|
-
|
|
485
|
-
## Arquivos de contexto disponiveis
|
|
486
|
-
Nao carregue automaticamente. Carregue apenas quando a task exigir:
|
|
487
|
-
|
|
488
|
-
- Escopo e features → leia \`.spec/requirements.md\`
|
|
489
|
-
- Decisoes tecnicas → leia \`.spec/architecture.md\`
|
|
490
|
-
- Perfis e permissoes → leia \`.spec/users.md\`
|
|
491
|
-
- Riscos → leia \`.spec/risks.md\`
|
|
492
|
-
- Tarefas ativas → leia \`.spec/tasks/active.md\`
|
|
493
|
-
- Historico → leia \`.spec/changelog.md\`
|
|
494
|
-
|
|
495
|
-
## Resumo do Objetivo
|
|
496
|
-
${d.objetivo}
|
|
497
|
-
`;
|
|
498
|
-
}
|
|
499
|
-
function gerarChangelog(d) {
|
|
500
|
-
return `# Changelog — ${d.projeto}
|
|
501
|
-
|
|
502
|
-
> Registro de decisoes, mudancas e causa raiz de bugs.
|
|
503
|
-
|
|
504
|
-
## ${d.data} — Retrofit realizado
|
|
505
|
-
- Spec gerado a partir de projeto pre-existente via vireum-spec retrofit
|
|
506
|
-
- Stack detectada automaticamente: ${[
|
|
507
|
-
d.analise.stack.frontend,
|
|
508
|
-
d.analise.stack.backend,
|
|
509
|
-
d.analise.stack.banco,
|
|
510
|
-
].filter(Boolean).join(', ') || 'a confirmar'}
|
|
511
|
-
- Fase no momento do retrofit: ${d.fase}
|
|
512
|
-
`;
|
|
513
|
-
}
|
|
514
|
-
function gerarTasksActive(d) {
|
|
515
|
-
if (d.featuresPendentes.length === 0) {
|
|
516
|
-
return `# Tasks — Active\n\n> Nenhuma task pendente mapeada no retrofit.\n> Execute vireum-spec prioritize para organizar.\n`;
|
|
517
|
-
}
|
|
518
|
-
const tasks = d.featuresPendentes.map((f, i) => {
|
|
519
|
-
const prefix = f.replace(/\s+/g, '_').toUpperCase().slice(0, 8);
|
|
520
|
-
const num = String(i + 1).padStart(3, '0');
|
|
521
|
-
return `## [${prefix}-T${num}] ${f}
|
|
522
|
-
- **Origem:** retrofit — funcionalidade pendente
|
|
523
|
-
- **Camada:** A definir
|
|
524
|
-
- **Complexidade:** A definir
|
|
525
|
-
- **Criterios de aceitacao:**
|
|
526
|
-
- (definir antes de implementar)
|
|
527
|
-
- **Status:** [ ] Nao iniciada
|
|
528
|
-
`;
|
|
529
|
-
}).join('\n');
|
|
530
|
-
return `# Tasks — Active
|
|
531
|
-
|
|
532
|
-
> Tasks pendentes identificadas no retrofit.
|
|
533
|
-
> Revise e complemente os criterios de aceitacao antes de comecar.
|
|
534
|
-
|
|
535
|
-
---
|
|
536
|
-
|
|
537
|
-
${tasks}`;
|
|
538
|
-
}
|
|
539
|
-
function gerarTasksBacklog(d) {
|
|
540
|
-
return `# Tasks — Backlog\n\n> Preencher apos priorizacao via vireum-spec prioritize.\n`;
|
|
541
|
-
}
|
|
542
|
-
function gerarTasksDone(d) {
|
|
543
|
-
const done = d.featuresAtivas.map((f) => `- [x] ${f} — pre-existente ao retrofit`).join('\n');
|
|
544
|
-
return `# Tasks — Done
|
|
545
|
-
|
|
546
|
-
> Funcionalidades ja implementadas antes do retrofit.
|
|
547
|
-
|
|
548
|
-
## ${d.data} — Retrofit
|
|
549
|
-
${done || '- (nenhuma listada)'}
|
|
550
|
-
`;
|
|
551
|
-
}
|
|
552
|
-
function sleep(ms) {
|
|
553
|
-
return new Promise(r => setTimeout(r, ms));
|
|
554
|
-
}
|