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
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.runInitWeb = runInitWeb;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
9
|
+
const system_1 = require("./system");
|
|
10
|
+
async function runInitWeb() {
|
|
11
|
+
console.log(chalk_1.default.hex('#2D7DD2').bold('\n Vireum Spec — Briefing Interativo (Web)\n'));
|
|
12
|
+
console.log(chalk_1.default.gray(' Responda as perguntas abaixo. As respostas serão salvas em .spec/briefing.md\n'));
|
|
13
|
+
// ── 1. Informações Gerais ──────────────────────────────────────────────────
|
|
14
|
+
console.log(chalk_1.default.hex('#2D7DD2').bold('📌 1. Informações Gerais\n'));
|
|
15
|
+
const geral = {
|
|
16
|
+
projeto: await (0, prompts_1.input)({ message: 'Nome do projeto:' }),
|
|
17
|
+
cliente: await (0, prompts_1.input)({ message: 'Nome do cliente:' }),
|
|
18
|
+
responsavel: await (0, prompts_1.input)({ message: 'Responsável pelo projeto (Vireum):' }),
|
|
19
|
+
data: await (0, prompts_1.input)({ message: 'Data da reunião:', default: new Date().toLocaleDateString('pt-BR') }),
|
|
20
|
+
};
|
|
21
|
+
// ── 2. Objetivo ────────────────────────────────────────────────────────────
|
|
22
|
+
console.log(chalk_1.default.hex('#2D7DD2').bold('\n🎯 2. Objetivo e Público\n'));
|
|
23
|
+
const objetivo = {
|
|
24
|
+
objetivo: await (0, prompts_1.input)({ message: 'Qual é o objetivo principal do site?' }),
|
|
25
|
+
cta_principal: await (0, prompts_1.input)({ message: 'Qual é o CTA principal? (ex: agendar consulta, comprar, cadastrar)' }),
|
|
26
|
+
publico: await (0, prompts_1.input)({ message: 'Público-alvo do site:' }),
|
|
27
|
+
subtipo: await (0, prompts_1.select)({
|
|
28
|
+
message: 'Tipo de site:',
|
|
29
|
+
choices: [
|
|
30
|
+
{ name: 'Landing Page — foco em conversão, CTA único', value: 'landing' },
|
|
31
|
+
{ name: 'Institucional — múltiplas páginas, apresentação da empresa', value: 'institucional' },
|
|
32
|
+
{ name: 'Blog / Conteúdo — foco em artigos e SEO', value: 'blog' },
|
|
33
|
+
{ name: 'Portfólio — showcase de trabalhos', value: 'portfolio' },
|
|
34
|
+
{ name: 'One Page — scroll contínuo, evento ou produto', value: 'onepage' },
|
|
35
|
+
],
|
|
36
|
+
}),
|
|
37
|
+
prazo_mvp: await (0, prompts_1.input)({ message: 'Prazo esperado para entrega:' }),
|
|
38
|
+
};
|
|
39
|
+
// ── 3. Conteúdo ────────────────────────────────────────────────────────────
|
|
40
|
+
console.log(chalk_1.default.hex('#2D7DD2').bold('\n🧩 3. Conteúdo e Seções\n'));
|
|
41
|
+
const conteudo = {
|
|
42
|
+
secoes: await (0, prompts_1.input)({ message: 'Seções da página (separadas por vírgula, ex: hero, sobre, serviços, depoimentos, contato):' }),
|
|
43
|
+
formulario: await (0, prompts_1.confirm)({ message: 'Tem formulário de contato ou captura de leads?', default: false }),
|
|
44
|
+
campos_form: objetivo.subtipo !== 'blog'
|
|
45
|
+
? await (0, prompts_1.input)({ message: 'Campos do formulário (separados por vírgula):' })
|
|
46
|
+
: '',
|
|
47
|
+
blog_categorias: objetivo.subtipo === 'blog'
|
|
48
|
+
? await (0, prompts_1.input)({ message: 'Categorias do blog:' })
|
|
49
|
+
: '',
|
|
50
|
+
cms: await (0, prompts_1.confirm)({ message: 'O cliente vai gerenciar o conteúdo sozinho (precisa de CMS)?', default: false }),
|
|
51
|
+
};
|
|
52
|
+
// ── 4. Integrações ─────────────────────────────────────────────────────────
|
|
53
|
+
console.log(chalk_1.default.hex('#2D7DD2').bold('\n🔗 4. Integrações\n'));
|
|
54
|
+
const integracoes = {
|
|
55
|
+
analytics: await (0, prompts_1.confirm)({ message: 'Precisa de analytics (Google Analytics, Hotjar)?', default: false }),
|
|
56
|
+
chat: await (0, prompts_1.confirm)({ message: 'Precisa de chat ao vivo (Intercom, Tawk, etc)?', default: false }),
|
|
57
|
+
crm: await (0, prompts_1.input)({ message: 'Integração com CRM ou e-mail marketing? (HubSpot, RD Station, Mailchimp — deixe em branco se não):' }),
|
|
58
|
+
pixel: await (0, prompts_1.confirm)({ message: 'Precisa de pixel de rastreamento (Meta, Google Ads)?', default: false }),
|
|
59
|
+
};
|
|
60
|
+
// ── 5. Design ──────────────────────────────────────────────────────────────
|
|
61
|
+
console.log(chalk_1.default.hex('#2D7DD2').bold('\n🎨 5. Design e Identidade Visual\n'));
|
|
62
|
+
const design = {
|
|
63
|
+
tem_identidade: await (0, prompts_1.confirm)({ message: 'Já existe identidade visual (logo, cores, tipografia)?', default: false }),
|
|
64
|
+
referencia: await (0, prompts_1.input)({ message: 'Site de referência ou inspiração (URL ou descrição):' }),
|
|
65
|
+
design_system: await (0, prompts_1.select)({
|
|
66
|
+
message: 'Framework de UI:',
|
|
67
|
+
choices: [
|
|
68
|
+
{ name: 'Tailwind CSS', value: 'Tailwind CSS' },
|
|
69
|
+
{ name: 'Shadcn/ui', value: 'Shadcn/ui' },
|
|
70
|
+
{ name: 'Bootstrap', value: 'Bootstrap' },
|
|
71
|
+
{ name: 'CSS puro', value: 'CSS puro' },
|
|
72
|
+
{ name: 'A definir', value: 'A definir' },
|
|
73
|
+
],
|
|
74
|
+
}),
|
|
75
|
+
responsivo: await (0, prompts_1.confirm)({ message: 'Precisa ser responsivo (mobile-first)?', default: true }),
|
|
76
|
+
};
|
|
77
|
+
// ── 6. Deploy e SEO ────────────────────────────────────────────────────────
|
|
78
|
+
console.log(chalk_1.default.hex('#2D7DD2').bold('\n🚀 6. Deploy e SEO\n'));
|
|
79
|
+
const deploy = {
|
|
80
|
+
plataforma: await (0, prompts_1.select)({
|
|
81
|
+
message: 'Plataforma de deploy:',
|
|
82
|
+
choices: [
|
|
83
|
+
{ name: 'Vercel', value: 'Vercel' },
|
|
84
|
+
{ name: 'Netlify', value: 'Netlify' },
|
|
85
|
+
{ name: 'GitHub Pages', value: 'GitHub Pages' },
|
|
86
|
+
{ name: 'VPS / servidor próprio', value: 'VPS' },
|
|
87
|
+
{ name: 'A definir', value: 'A definir' },
|
|
88
|
+
],
|
|
89
|
+
}),
|
|
90
|
+
custo_operacional: await (0, prompts_1.select)({
|
|
91
|
+
message: 'Custo operacional mensal estimado:',
|
|
92
|
+
choices: [
|
|
93
|
+
{ name: 'Até R$ 200 — projeto leve, baixo tráfego', value: 'Até R$ 200' },
|
|
94
|
+
{ name: 'R$ 200–800 — projeto moderado', value: 'R$ 200–800' },
|
|
95
|
+
{ name: 'R$ 800–3k — projeto robusto', value: 'R$ 800–3k' },
|
|
96
|
+
{ name: 'Acima de R$ 3k — missão crítica / alta escala', value: 'Acima de R$ 3k' },
|
|
97
|
+
{ name: 'A definir com o cliente', value: 'A definir' },
|
|
98
|
+
],
|
|
99
|
+
}),
|
|
100
|
+
dominio: await (0, prompts_1.confirm)({ message: 'Já tem domínio configurado?', default: false }),
|
|
101
|
+
seo: await (0, prompts_1.confirm)({ message: 'SEO é requisito (meta tags, sitemap, Open Graph)?', default: true }),
|
|
102
|
+
performance: await (0, prompts_1.confirm)({ message: 'Performance é crítica (Core Web Vitals)?', default: false }),
|
|
103
|
+
};
|
|
104
|
+
const briefing = gerarBriefingWeb({ geral, objetivo, conteudo, integracoes, design, deploy });
|
|
105
|
+
(0, system_1.salvarBriefing)(briefing, 'web');
|
|
106
|
+
}
|
|
107
|
+
function gerarBriefingWeb(data) {
|
|
108
|
+
const { geral, objetivo, conteudo, integracoes, design, deploy } = data;
|
|
109
|
+
const secoes = conteudo.secoes
|
|
110
|
+
.split(',').map((s) => `- ${s.trim()}`).join('\n');
|
|
111
|
+
const subtipoLabel = {
|
|
112
|
+
landing: 'Landing Page',
|
|
113
|
+
institucional: 'Institucional',
|
|
114
|
+
blog: 'Blog / Conteúdo',
|
|
115
|
+
portfolio: 'Portfólio',
|
|
116
|
+
onepage: 'One Page',
|
|
117
|
+
};
|
|
118
|
+
return `# Briefing — ${geral.projeto}
|
|
119
|
+
|
|
120
|
+
## 📌 1. Informações Gerais
|
|
121
|
+
- **Projeto:** ${geral.projeto}
|
|
122
|
+
- **Cliente:** ${geral.cliente}
|
|
123
|
+
- **Responsável:** ${geral.responsavel}
|
|
124
|
+
- **Data da reunião:** ${geral.data}
|
|
125
|
+
- **Tipo:** Web — ${subtipoLabel[objetivo.subtipo] || objetivo.subtipo}
|
|
126
|
+
- **Versão:** 1.0
|
|
127
|
+
|
|
128
|
+
## 🎯 2. Objetivo e Público
|
|
129
|
+
- **Objetivo principal:** ${objetivo.objetivo}
|
|
130
|
+
- **CTA principal:** ${objetivo.cta_principal}
|
|
131
|
+
- **Público-alvo:** ${objetivo.publico}
|
|
132
|
+
- **Tipo de site:** ${subtipoLabel[objetivo.subtipo]}
|
|
133
|
+
- **Prazo de entrega:** ${objetivo.prazo_mvp}
|
|
134
|
+
|
|
135
|
+
## 🧩 3. Conteúdo e Seções
|
|
136
|
+
|
|
137
|
+
### Seções da página
|
|
138
|
+
${secoes}
|
|
139
|
+
|
|
140
|
+
### Formulário
|
|
141
|
+
- Formulário de contato/captura: ${conteudo.formulario ? 'Sim' : 'Não'}
|
|
142
|
+
${conteudo.campos_form ? `- Campos: ${conteudo.campos_form}` : ''}
|
|
143
|
+
${conteudo.blog_categorias ? `- Categorias do blog: ${conteudo.blog_categorias}` : ''}
|
|
144
|
+
- CMS para gestão de conteúdo: ${conteudo.cms ? 'Sim' : 'Não'}
|
|
145
|
+
|
|
146
|
+
## 🔗 4. Integrações
|
|
147
|
+
- Analytics: ${integracoes.analytics ? 'Sim' : 'Não'}
|
|
148
|
+
- Chat ao vivo: ${integracoes.chat ? 'Sim' : 'Não'}
|
|
149
|
+
- CRM / E-mail marketing: ${integracoes.crm || 'Não'}
|
|
150
|
+
- Pixel de rastreamento: ${integracoes.pixel ? 'Sim' : 'Não'}
|
|
151
|
+
|
|
152
|
+
## 🎨 5. Design e Identidade Visual
|
|
153
|
+
- Identidade visual existente: ${design.tem_identidade ? 'Sim' : 'Não'}
|
|
154
|
+
- Referência: ${design.referencia || 'Não informada'}
|
|
155
|
+
- Framework de UI: ${design.design_system}
|
|
156
|
+
- Responsivo: ${design.responsivo ? 'Sim' : 'Não'}
|
|
157
|
+
|
|
158
|
+
## 🚀 6. Deploy e SEO
|
|
159
|
+
- Plataforma de deploy: ${deploy.plataforma}
|
|
160
|
+
- Custo operacional estimado: ${deploy.custo_operacional}
|
|
161
|
+
- Domínio configurado: ${deploy.dominio ? 'Sim' : 'Não'}
|
|
162
|
+
- SEO (meta tags, sitemap, OG): ${deploy.seo ? 'Sim' : 'Não'}
|
|
163
|
+
- Performance / Core Web Vitals: ${deploy.performance ? 'Crítico' : 'Normal'}
|
|
164
|
+
`;
|
|
165
|
+
}
|
package/dist/commands/init.js
CHANGED
|
@@ -1,261 +1,29 @@
|
|
|
1
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
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
4
|
};
|
|
38
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
6
|
exports.runInit = runInit;
|
|
40
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
longo_prazo: await (0, prompts_1.input)({ message: 'Resultado esperado no longo prazo:' }),
|
|
63
|
-
prioridade_cliente: await (0, prompts_1.input)({ message: 'O que o cliente precisa que funcione primeiro?' }),
|
|
64
|
-
};
|
|
65
|
-
// ── 3. Escopo Funcional ────────────────────────────────────────────────────
|
|
66
|
-
console.log(chalk_1.default.hex('#2D7DD2').bold('\n🧩 3. Escopo Funcional\n'));
|
|
67
|
-
const escopo = {
|
|
68
|
-
usuarios: await (0, prompts_1.input)({ message: 'Quem usará o sistema?' }),
|
|
69
|
-
multi_usuario: await (0, prompts_1.confirm)({ message: 'Existem diferentes tipos de usuários?', default: false }),
|
|
70
|
-
niveis_acesso: await (0, prompts_1.confirm)({ message: 'Existem níveis de acesso diferentes?', default: false }),
|
|
71
|
-
features_obrigatorias: await (0, prompts_1.input)({ message: 'Funcionalidades obrigatórias (separadas por vírgula):' }),
|
|
72
|
-
features_desejaveis: await (0, prompts_1.input)({ message: 'Funcionalidades desejáveis no MVP (separadas por vírgula):' }),
|
|
73
|
-
integracoes: await (0, prompts_1.input)({ message: 'Integrações externas? (CRM, ERP, WhatsApp, etc.)' }),
|
|
74
|
-
};
|
|
75
|
-
// ── 4. Regras de Negócio ───────────────────────────────────────────────────
|
|
76
|
-
console.log(chalk_1.default.hex('#2D7DD2').bold('\n🏗️ 4. Regras de Negócio\n'));
|
|
77
|
-
const regras = {
|
|
78
|
-
regras: await (0, prompts_1.input)({ message: 'Principais regras que o sistema deve obedecer:' }),
|
|
79
|
-
excecoes: await (0, prompts_1.input)({ message: 'Exceções importantes:' }),
|
|
80
|
-
};
|
|
81
|
-
// ── 5. Financeiro ──────────────────────────────────────────────────────────
|
|
82
|
-
console.log(chalk_1.default.hex('#2D7DD2').bold('\n💰 5. Modelo Financeiro\n'));
|
|
83
|
-
const financeiro = {
|
|
84
|
-
pagamento: await (0, prompts_1.confirm)({ message: 'Haverá integração com meio de pagamento?', default: false }),
|
|
85
|
-
cobranca: await (0, prompts_1.select)({
|
|
86
|
-
message: 'Modelo de cobrança:',
|
|
87
|
-
choices: [
|
|
88
|
-
{ name: 'Não aplicável', value: 'Não aplicável' },
|
|
89
|
-
{ name: 'Assinatura recorrente', value: 'Assinatura recorrente' },
|
|
90
|
-
{ name: 'Compra única', value: 'Compra única' },
|
|
91
|
-
{ name: 'Ambos', value: 'Ambos' },
|
|
92
|
-
],
|
|
93
|
-
}),
|
|
94
|
-
estorno: await (0, prompts_1.confirm)({ message: 'Existem regras de estorno ou cancelamento?', default: false }),
|
|
95
|
-
};
|
|
96
|
-
// ── 6. Métricas ────────────────────────────────────────────────────────────
|
|
97
|
-
console.log(chalk_1.default.hex('#2D7DD2').bold('\n📊 6. Métricas e Indicadores\n'));
|
|
98
|
-
const metricas = {
|
|
99
|
-
kpis: await (0, prompts_1.input)({ message: 'Quais KPIs devem ser acompanhados?' }),
|
|
100
|
-
dashboard: await (0, prompts_1.confirm)({ message: 'Precisa de dashboard administrativo?', default: false }),
|
|
101
|
-
relatorios: await (0, prompts_1.confirm)({ message: 'Precisa exportar relatórios?', default: false }),
|
|
102
|
-
};
|
|
103
|
-
// ── 7. Plataforma e DevOps ─────────────────────────────────────────────────
|
|
104
|
-
console.log(chalk_1.default.hex('#2D7DD2').bold('\n📱 7. Plataforma e Infraestrutura\n'));
|
|
105
|
-
const plataforma = {
|
|
106
|
-
tipo: await (0, prompts_1.select)({
|
|
107
|
-
message: 'O sistema será:',
|
|
108
|
-
choices: [
|
|
109
|
-
{ name: 'Apenas Web', value: 'Apenas Web' },
|
|
110
|
-
{ name: 'Web + App', value: 'Web + App' },
|
|
111
|
-
{ name: 'Apenas App', value: 'Apenas App' },
|
|
112
|
-
],
|
|
113
|
-
}),
|
|
114
|
-
app_futuro: await (0, prompts_1.confirm)({ message: 'Pretende lançar aplicativo futuramente?', default: false }),
|
|
115
|
-
push: await (0, prompts_1.confirm)({ message: 'Precisa de notificações push?', default: false }),
|
|
116
|
-
hospedagem: await (0, prompts_1.input)({ message: 'Hospedagem preferida ou orçamento de infra:' }),
|
|
117
|
-
cicd: await (0, prompts_1.confirm)({ message: 'Precisa de CI/CD desde o início?', default: false }),
|
|
118
|
-
staging: await (0, prompts_1.confirm)({ message: 'Precisa de ambiente de staging?', default: false }),
|
|
119
|
-
dominio: await (0, prompts_1.confirm)({ message: 'Já tem domínio/DNS configurado?', default: false }),
|
|
120
|
-
backup_sla: await (0, prompts_1.confirm)({ message: 'Backups e SLA são requisito do cliente?', default: false }),
|
|
121
|
-
};
|
|
122
|
-
// ── 8. Segurança e Jurídico ────────────────────────────────────────────────
|
|
123
|
-
console.log(chalk_1.default.hex('#2D7DD2').bold('\n🔐 8. Segurança e Jurídico\n'));
|
|
124
|
-
const seguranca = {
|
|
125
|
-
dados_sensiveis: await (0, prompts_1.confirm)({ message: 'Dados sensíveis serão armazenados?', default: false }),
|
|
126
|
-
lgpd: await (0, prompts_1.confirm)({ message: 'Precisa de conformidade com LGPD?', default: true }),
|
|
127
|
-
termos: await (0, prompts_1.confirm)({ message: 'Precisa de termos de uso?', default: false }),
|
|
128
|
-
acesso_avancado: await (0, prompts_1.confirm)({ message: 'Haverá controle de acesso avançado?', default: false }),
|
|
129
|
-
};
|
|
130
|
-
// ── 9. Escala ──────────────────────────────────────────────────────────────
|
|
131
|
-
console.log(chalk_1.default.hex('#2D7DD2').bold('\n📈 9. Escala e Crescimento\n'));
|
|
132
|
-
const escala = {
|
|
133
|
-
usuarios_lancamento: await (0, prompts_1.input)({ message: 'Número esperado de usuários no lançamento:' }),
|
|
134
|
-
crescimento_1ano: await (0, prompts_1.input)({ message: 'Crescimento estimado em 1 ano:' }),
|
|
135
|
-
uso: await (0, prompts_1.select)({
|
|
136
|
-
message: 'Padrão de uso:',
|
|
137
|
-
choices: [
|
|
138
|
-
{ name: 'Diário intenso', value: 'Diário intenso' },
|
|
139
|
-
{ name: 'Diário moderado', value: 'Diário moderado' },
|
|
140
|
-
{ name: 'Esporádico', value: 'Esporádico' },
|
|
141
|
-
],
|
|
142
|
-
}),
|
|
143
|
-
};
|
|
144
|
-
// ── 10. Operacional ────────────────────────────────────────────────────────
|
|
145
|
-
console.log(chalk_1.default.hex('#2D7DD2').bold('\n⚙️ 10. Operacional\n'));
|
|
146
|
-
const operacional = {
|
|
147
|
-
operador: await (0, prompts_1.input)({ message: 'Quem vai operar o sistema?' }),
|
|
148
|
-
equipe: await (0, prompts_1.confirm)({ message: 'Haverá equipe interna dedicada?', default: false }),
|
|
149
|
-
treinamento: await (0, prompts_1.confirm)({ message: 'Necessita treinamento?', default: false }),
|
|
150
|
-
suporte: await (0, prompts_1.confirm)({ message: 'Necessita suporte mensal?', default: false }),
|
|
151
|
-
};
|
|
152
|
-
// ── 11. Riscos ─────────────────────────────────────────────────────────────
|
|
153
|
-
console.log(chalk_1.default.hex('#2D7DD2').bold('\n🧠 11. Riscos Identificados\n'));
|
|
154
|
-
const riscos = {
|
|
155
|
-
risco_tecnico: await (0, prompts_1.input)({ message: 'Risco técnico identificado:' }),
|
|
156
|
-
risco_juridico: await (0, prompts_1.input)({ message: 'Risco jurídico identificado:' }),
|
|
157
|
-
risco_operacional: await (0, prompts_1.input)({ message: 'Risco operacional identificado:' }),
|
|
158
|
-
};
|
|
159
|
-
// ── Gerar briefing.md ──────────────────────────────────────────────────────
|
|
160
|
-
const briefing = gerarBriefing({ geral, objetivo, escopo, regras, financeiro, metricas, plataforma, seguranca, escala, operacional, riscos });
|
|
161
|
-
const specDir = path.join(process.cwd(), '.spec');
|
|
162
|
-
if (!fs.existsSync(specDir))
|
|
163
|
-
fs.mkdirSync(specDir, { recursive: true });
|
|
164
|
-
const tasksDir = path.join(specDir, 'tasks');
|
|
165
|
-
if (!fs.existsSync(tasksDir))
|
|
166
|
-
fs.mkdirSync(tasksDir, { recursive: true });
|
|
167
|
-
fs.writeFileSync(path.join(specDir, 'briefing.md'), briefing, 'utf-8');
|
|
168
|
-
console.log(chalk_1.default.green.bold('\n✅ Briefing salvo em .spec/briefing.md\n'));
|
|
169
|
-
console.log(chalk_1.default.gray(' Próximo passo: ') + chalk_1.default.white('vireum-spec distill') + chalk_1.default.gray(' para gerar os arquivos de spec\n'));
|
|
170
|
-
}
|
|
171
|
-
function gerarBriefing(data) {
|
|
172
|
-
const { geral, objetivo, escopo, regras, financeiro, metricas, plataforma, seguranca, escala, operacional, riscos } = data;
|
|
173
|
-
const features = escopo.features_obrigatorias
|
|
174
|
-
.split(',').map((f) => `- [ ] ${f.trim()}`).join('\n');
|
|
175
|
-
const featuresDesejaveis = escopo.features_desejaveis
|
|
176
|
-
.split(',').map((f) => `- [ ] ${f.trim()}`).join('\n');
|
|
177
|
-
return `# Briefing — ${geral.projeto}
|
|
178
|
-
|
|
179
|
-
## 📌 1. Informações Gerais
|
|
180
|
-
- **Projeto:** ${geral.projeto}
|
|
181
|
-
- **Cliente:** ${geral.cliente}
|
|
182
|
-
- **Responsável:** ${geral.responsavel}
|
|
183
|
-
- **Data da reunião:** ${geral.data}
|
|
184
|
-
- **Versão:** 1.0
|
|
185
|
-
|
|
186
|
-
## 🎯 2. Objetivo do Projeto
|
|
187
|
-
**Objetivo principal:** ${objetivo.objetivo}
|
|
188
|
-
|
|
189
|
-
**Problema que o sistema resolve:** ${objetivo.problema}
|
|
190
|
-
|
|
191
|
-
**Resultados esperados:**
|
|
192
|
-
- Curto prazo: ${objetivo.curto_prazo}
|
|
193
|
-
- Médio prazo: ${objetivo.medio_prazo}
|
|
194
|
-
- Longo prazo: ${objetivo.longo_prazo}
|
|
195
|
-
|
|
196
|
-
**Prioridade do cliente (o que precisa funcionar primeiro):** ${objetivo.prioridade_cliente}
|
|
197
|
-
|
|
198
|
-
## 🧩 3. Escopo Funcional
|
|
199
|
-
|
|
200
|
-
### Público do Sistema
|
|
201
|
-
- Quem usará: ${escopo.usuarios}
|
|
202
|
-
- Diferentes tipos de usuários: ${escopo.multi_usuario ? 'Sim' : 'Não'}
|
|
203
|
-
- Níveis de acesso: ${escopo.niveis_acesso ? 'Sim' : 'Não'}
|
|
204
|
-
|
|
205
|
-
### Funcionalidades Obrigatórias
|
|
206
|
-
${features}
|
|
207
|
-
|
|
208
|
-
### Funcionalidades Desejáveis (não obrigatórias no MVP)
|
|
209
|
-
${featuresDesejaveis}
|
|
210
|
-
|
|
211
|
-
### Integrações Externas
|
|
212
|
-
${escopo.integracoes || 'Nenhuma identificada'}
|
|
213
|
-
|
|
214
|
-
## 🏗️ 4. Regras de Negócio
|
|
215
|
-
${regras.regras}
|
|
216
|
-
|
|
217
|
-
**Exceções:** ${regras.excecoes || 'Nenhuma'}
|
|
218
|
-
|
|
219
|
-
## 💰 5. Modelo Financeiro
|
|
220
|
-
- Integração com pagamento: ${financeiro.pagamento ? 'Sim' : 'Não'}
|
|
221
|
-
- Modelo de cobrança: ${financeiro.cobranca}
|
|
222
|
-
- Regras de estorno/cancelamento: ${financeiro.estorno ? 'Sim' : 'Não'}
|
|
223
|
-
|
|
224
|
-
## 📊 6. Métricas e Indicadores
|
|
225
|
-
- KPIs: ${metricas.kpis}
|
|
226
|
-
- Dashboard administrativo: ${metricas.dashboard ? 'Sim' : 'Não'}
|
|
227
|
-
- Exportação de relatórios: ${metricas.relatorios ? 'Sim' : 'Não'}
|
|
228
|
-
|
|
229
|
-
## 📱 7. Plataforma e Infraestrutura
|
|
230
|
-
- Tipo: ${plataforma.tipo}
|
|
231
|
-
- App futuro: ${plataforma.app_futuro ? 'Sim' : 'Não'}
|
|
232
|
-
- Notificações push: ${plataforma.push ? 'Sim' : 'Não'}
|
|
233
|
-
- Hospedagem: ${plataforma.hospedagem || 'A definir'}
|
|
234
|
-
- CI/CD desde o início: ${plataforma.cicd ? 'Sim' : 'Não'}
|
|
235
|
-
- Ambiente de staging: ${plataforma.staging ? 'Sim' : 'Não'}
|
|
236
|
-
- Domínio configurado: ${plataforma.dominio ? 'Sim' : 'Não'}
|
|
237
|
-
- Backup e SLA: ${plataforma.backup_sla ? 'Sim' : 'Não'}
|
|
238
|
-
|
|
239
|
-
## 🔐 8. Segurança e Jurídico
|
|
240
|
-
- Dados sensíveis: ${seguranca.dados_sensiveis ? 'Sim' : 'Não'}
|
|
241
|
-
- LGPD: ${seguranca.lgpd ? 'Sim' : 'Não'}
|
|
242
|
-
- Termos de uso: ${seguranca.termos ? 'Sim' : 'Não'}
|
|
243
|
-
- Controle de acesso avançado: ${seguranca.acesso_avancado ? 'Sim' : 'Não'}
|
|
244
|
-
|
|
245
|
-
## 📈 9. Escala e Crescimento
|
|
246
|
-
- Usuários no lançamento: ${escala.usuarios_lancamento}
|
|
247
|
-
- Crescimento em 1 ano: ${escala.crescimento_1ano}
|
|
248
|
-
- Padrão de uso: ${escala.uso}
|
|
249
|
-
|
|
250
|
-
## ⚙️ 10. Operacional
|
|
251
|
-
- Operador: ${operacional.operador}
|
|
252
|
-
- Equipe interna: ${operacional.equipe ? 'Sim' : 'Não'}
|
|
253
|
-
- Treinamento: ${operacional.treinamento ? 'Sim' : 'Não'}
|
|
254
|
-
- Suporte mensal: ${operacional.suporte ? 'Sim' : 'Não'}
|
|
255
|
-
|
|
256
|
-
## 🧠 11. Riscos Identificados
|
|
257
|
-
- Risco técnico: ${riscos.risco_tecnico || 'Nenhum identificado'}
|
|
258
|
-
- Risco jurídico: ${riscos.risco_juridico || 'Nenhum identificado'}
|
|
259
|
-
- Risco operacional: ${riscos.risco_operacional || 'Nenhum identificado'}
|
|
260
|
-
`;
|
|
8
|
+
const system_1 = require("./init/system");
|
|
9
|
+
const web_1 = require("./init/web");
|
|
10
|
+
const api_1 = require("./init/api");
|
|
11
|
+
const mobile_1 = require("./init/mobile");
|
|
12
|
+
const automation_1 = require("./init/automation");
|
|
13
|
+
const ai_1 = require("./init/ai");
|
|
14
|
+
const TIPOS_VALIDOS = ['system', 'web', 'api', 'mobile', 'automation', 'ai'];
|
|
15
|
+
async function runInit(type = 'system') {
|
|
16
|
+
if (!TIPOS_VALIDOS.includes(type)) {
|
|
17
|
+
console.log(chalk_1.default.red(`\n Tipo inválido: "${type}"\n`));
|
|
18
|
+
console.log(chalk_1.default.gray(' Tipos disponíveis: ') + chalk_1.default.white(TIPOS_VALIDOS.join(', ')) + '\n');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
switch (type) {
|
|
22
|
+
case 'web': return (0, web_1.runInitWeb)();
|
|
23
|
+
case 'api': return (0, api_1.runInitApi)();
|
|
24
|
+
case 'mobile': return (0, mobile_1.runInitMobile)();
|
|
25
|
+
case 'automation': return (0, automation_1.runInitAutomation)();
|
|
26
|
+
case 'ai': return (0, ai_1.runInitAi)();
|
|
27
|
+
default: return (0, system_1.runInitSystem)();
|
|
28
|
+
}
|
|
261
29
|
}
|
|
@@ -50,13 +50,17 @@ async function runPrioritize() {
|
|
|
50
50
|
const backlogPath = path.join(tasksDir, 'backlog.md');
|
|
51
51
|
const activePath = path.join(tasksDir, 'active.md');
|
|
52
52
|
const indexPath = path.join(specDir, 'INDEX.md');
|
|
53
|
+
const requirementsPath = path.join(specDir, 'requirements.md');
|
|
53
54
|
if (!fs.existsSync(backlogPath)) {
|
|
54
55
|
console.log(chalk_1.default.red('\n❌ tasks/backlog.md não encontrado.'));
|
|
55
56
|
console.log(chalk_1.default.gray(' Execute primeiro: ') + chalk_1.default.white('vireum-spec distill\n'));
|
|
56
57
|
process.exit(1);
|
|
57
58
|
}
|
|
58
59
|
const backlog = fs.readFileSync(backlogPath, 'utf-8');
|
|
59
|
-
const
|
|
60
|
+
const requirements = fs.existsSync(requirementsPath) ? fs.readFileSync(requirementsPath, 'utf-8') : '';
|
|
61
|
+
const featuresFromBacklog = extrairFeaturesBacklog(backlog);
|
|
62
|
+
const featuresFromReqs = extrairFeaturesRequirements(requirements);
|
|
63
|
+
const features = mergeDuplicateFeatures(featuresFromReqs, featuresFromBacklog);
|
|
60
64
|
if (features.length === 0) {
|
|
61
65
|
console.log(chalk_1.default.yellow('\n⚠️ Nenhuma feature encontrada no backlog.\n'));
|
|
62
66
|
process.exit(0);
|
|
@@ -113,15 +117,13 @@ async function runPrioritize() {
|
|
|
113
117
|
console.log('');
|
|
114
118
|
}
|
|
115
119
|
const s1 = (0, ora_1.default)('Gerando tasks/active.md...').start();
|
|
116
|
-
|
|
117
|
-
fs.writeFileSync(activePath, gerarActive(mvp), 'utf-8');
|
|
120
|
+
const maxExistingId = extrairMaxTaskId(activePath);
|
|
121
|
+
fs.writeFileSync(activePath, gerarActive(mvp, maxExistingId), 'utf-8');
|
|
118
122
|
s1.succeed('tasks/active.md gerado');
|
|
119
123
|
const s2 = (0, ora_1.default)('Atualizando tasks/backlog.md...').start();
|
|
120
|
-
await sleep(400);
|
|
121
124
|
fs.writeFileSync(backlogPath, gerarBacklog(fase2), 'utf-8');
|
|
122
125
|
s2.succeed('tasks/backlog.md atualizado');
|
|
123
126
|
const s3 = (0, ora_1.default)('Atualizando INDEX.md...').start();
|
|
124
|
-
await sleep(400);
|
|
125
127
|
atualizarIndex(indexPath, mvp);
|
|
126
128
|
s3.succeed('INDEX.md atualizado');
|
|
127
129
|
console.log(chalk_1.default.green.bold('\n✅ Priorização concluída!\n'));
|
|
@@ -136,41 +138,80 @@ async function runPrioritize() {
|
|
|
136
138
|
}
|
|
137
139
|
}
|
|
138
140
|
// ─── HELPERS ──────────────────────────────────────────────────────────────────
|
|
139
|
-
function
|
|
141
|
+
function extrairFeaturesBacklog(backlog) {
|
|
140
142
|
const linhas = backlog.split('\n').filter(l => l.includes('[FEATURE-'));
|
|
141
143
|
return linhas.map(linha => {
|
|
142
144
|
const idMatch = linha.match(/\[FEATURE-(T\d+)\]/);
|
|
143
|
-
const nomeMatch = linha.match(/\[FEATURE-T\d+\]
|
|
145
|
+
const nomeMatch = linha.match(/\[FEATURE-T\d+\]\s+([^\[]+)/);
|
|
144
146
|
return {
|
|
145
147
|
id: idMatch ? idMatch[1] : 'T000',
|
|
146
148
|
nome: nomeMatch ? nomeMatch[1].trim() : 'feature',
|
|
147
149
|
};
|
|
148
|
-
}).filter((f) => f.nome);
|
|
150
|
+
}).filter((f) => f.nome && f.id !== 'T000');
|
|
149
151
|
}
|
|
150
|
-
function
|
|
152
|
+
function extrairFeaturesRequirements(requirements) {
|
|
153
|
+
const mvpSection = requirements.split('## Funcionalidades — MVP')[1] || '';
|
|
154
|
+
if (!mvpSection)
|
|
155
|
+
return [];
|
|
156
|
+
const linhas = mvpSection.split('\n').filter(l => l.includes('R-') && (l.includes('- [ ]') || l.includes('- [x]')));
|
|
157
|
+
return linhas.map(linha => {
|
|
158
|
+
const idMatch = linha.match(/R-(\d+)/);
|
|
159
|
+
const nomeMatch = linha.match(/R-\d+\s+([^\[]+)/);
|
|
160
|
+
return {
|
|
161
|
+
id: idMatch ? `R${idMatch[1]}` : 'R000',
|
|
162
|
+
nome: nomeMatch ? nomeMatch[1].trim() : 'feature',
|
|
163
|
+
};
|
|
164
|
+
}).filter((f) => f.nome && f.id !== 'R000');
|
|
165
|
+
}
|
|
166
|
+
function mergeDuplicateFeatures(reqs, backlog) {
|
|
167
|
+
const nomeMap = new Map();
|
|
168
|
+
reqs.forEach(f => nomeMap.set(f.nome.toLowerCase(), f));
|
|
169
|
+
backlog.forEach(f => {
|
|
170
|
+
if (!nomeMap.has(f.nome.toLowerCase())) {
|
|
171
|
+
nomeMap.set(f.nome.toLowerCase(), f);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
return Array.from(nomeMap.values());
|
|
175
|
+
}
|
|
176
|
+
function extrairMaxTaskId(activePath) {
|
|
177
|
+
if (!fs.existsSync(activePath))
|
|
178
|
+
return 0;
|
|
179
|
+
const content = fs.readFileSync(activePath, 'utf-8');
|
|
180
|
+
const matches = content.match(/\[.+-T(\d+)\]/g) || [];
|
|
181
|
+
if (matches.length === 0)
|
|
182
|
+
return 0;
|
|
183
|
+
return Math.max(...matches.map(m => {
|
|
184
|
+
const numMatch = m.match(/T(\d+)/);
|
|
185
|
+
return numMatch ? parseInt(numMatch[1], 10) : 0;
|
|
186
|
+
}));
|
|
187
|
+
}
|
|
188
|
+
function gerarActive(mvp, startFromId = 0) {
|
|
151
189
|
if (mvp.length === 0) {
|
|
152
190
|
return `# Tasks — Active\n\n> Nenhuma task no MVP ainda.\n> Execute vireum-spec prioritize para classificar as features.\n`;
|
|
153
191
|
}
|
|
154
192
|
const tasks = mvp.map((item, i) => {
|
|
155
193
|
const prefix = item.nome.replace(/\s+/g, '_').toUpperCase().slice(0, 8);
|
|
156
|
-
const num = String(i + 1).padStart(3, '0');
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
- **
|
|
162
|
-
|
|
163
|
-
- **
|
|
194
|
+
const num = String(startFromId + i + 1).padStart(3, '0');
|
|
195
|
+
const criteriosArray = item.criterios
|
|
196
|
+
? item.criterios.split(',').map((c) => c.trim()).filter(Boolean)
|
|
197
|
+
: [];
|
|
198
|
+
return `## [${prefix}-T${num}] ${item.nome}
|
|
199
|
+
- **Origem:** FEATURE-${item.id}
|
|
200
|
+
- **Camada:** ${item.camada}
|
|
201
|
+
- **Complexidade:** ${item.complexidade}
|
|
202
|
+
- **Critérios de aceitação:**
|
|
203
|
+
${criteriosArray.length > 0 ? criteriosArray.map((c) => ` - ${c}`).join('\n') : ' - (definir antes de implementar)'}
|
|
204
|
+
- **Status:** [ ] Não iniciada
|
|
164
205
|
`;
|
|
165
206
|
}).join('\n');
|
|
166
|
-
return `# Tasks — Active
|
|
167
|
-
|
|
168
|
-
> Tarefas do MVP prontas para desenvolvimento.
|
|
169
|
-
> A IA só implementa tasks desta lista.
|
|
170
|
-
> Ao concluir uma task: marcar status, mover para done.md, atualizar INDEX.md
|
|
171
|
-
|
|
172
|
-
---
|
|
173
|
-
|
|
207
|
+
return `# Tasks — Active
|
|
208
|
+
|
|
209
|
+
> Tarefas do MVP prontas para desenvolvimento.
|
|
210
|
+
> A IA só implementa tasks desta lista.
|
|
211
|
+
> Ao concluir uma task: marcar status, mover para done.md, atualizar INDEX.md
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
174
215
|
${tasks}`;
|
|
175
216
|
}
|
|
176
217
|
function gerarBacklog(fase2) {
|
|
@@ -178,25 +219,32 @@ function gerarBacklog(fase2) {
|
|
|
178
219
|
return `# Tasks — Backlog\n\n> Nenhuma feature na Fase 2 no momento.\n`;
|
|
179
220
|
}
|
|
180
221
|
const items = fase2.map(item => `- [ ] [FEATURE-${item.id}] ${item.nome} — ${item.complexidade} — ${item.camada} [FASE 2]`).join('\n');
|
|
181
|
-
return `# Tasks — Backlog
|
|
182
|
-
|
|
183
|
-
> Features da Fase 2. Aguardando conclusão do MVP para priorização.
|
|
184
|
-
|
|
185
|
-
${items}
|
|
222
|
+
return `# Tasks — Backlog
|
|
223
|
+
|
|
224
|
+
> Features da Fase 2. Aguardando conclusão do MVP para priorização.
|
|
225
|
+
|
|
226
|
+
${items}
|
|
186
227
|
`;
|
|
187
228
|
}
|
|
188
229
|
function atualizarIndex(indexPath, mvp) {
|
|
189
230
|
if (!fs.existsSync(indexPath))
|
|
190
231
|
return;
|
|
191
232
|
const content = fs.readFileSync(indexPath, 'utf-8');
|
|
233
|
+
const maxId = extrairMaxTaskIdFromContent(content);
|
|
192
234
|
const tasks = mvp.map((item, i) => {
|
|
193
235
|
const prefix = item.nome.replace(/\s+/g, '_').toUpperCase().slice(0, 8);
|
|
194
|
-
const num = String(i + 1).padStart(3, '0');
|
|
236
|
+
const num = String(maxId + i + 1).padStart(3, '0');
|
|
195
237
|
return `- [ ] [${prefix}-T${num}] ${item.nome}`;
|
|
196
238
|
}).join('\n');
|
|
197
|
-
const updated = content.replace(/## Estado do MVP[\s\S]*?(?=\n## )/, `## Estado
|
|
239
|
+
const updated = content.replace(/## Estado(?: do MVP)?[\s\S]*?(?=\n## |$)/, `## Estado\n${tasks}\n\n`);
|
|
198
240
|
fs.writeFileSync(indexPath, updated, 'utf-8');
|
|
199
241
|
}
|
|
200
|
-
function
|
|
201
|
-
|
|
242
|
+
function extrairMaxTaskIdFromContent(content) {
|
|
243
|
+
const matches = content.match(/\[.+-T(\d+)\]/g) || [];
|
|
244
|
+
if (matches.length === 0)
|
|
245
|
+
return 0;
|
|
246
|
+
return Math.max(...matches.map(m => {
|
|
247
|
+
const numMatch = m.match(/T(\d+)/);
|
|
248
|
+
return numMatch ? parseInt(numMatch[1], 10) : 0;
|
|
249
|
+
}));
|
|
202
250
|
}
|