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.
- package/dist/commands/brief.js +150 -0
- package/dist/commands/distill.js +372 -0
- package/dist/commands/health.js +172 -0
- package/dist/commands/init.js +261 -0
- package/dist/commands/prioritize.js +202 -0
- package/dist/commands/setup.js +436 -0
- package/dist/commands/skills.js +147 -0
- package/dist/commands/verify-mcps.js +148 -0
- package/dist/index.js +55 -0
- package/package.json +39 -0
- package/src/skills/bug-fix.md +14 -0
- package/src/skills/contract-first.md +21 -0
- package/src/skills/new-demand.md +14 -0
- package/src/skills/scope-check.md +21 -0
- package/src/skills/spec-update.md +30 -0
- package/src/skills/task-implement.md +14 -0
|
@@ -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
|
+
}
|