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,148 @@
|
|
|
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.runVerifyMcps = runVerifyMcps;
|
|
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
|
+
const os = __importStar(require("os"));
|
|
45
|
+
async function runVerifyMcps() {
|
|
46
|
+
console.log(chalk_1.default.hex('#2D7DD2').bold('\n Vireum Spec — Verify MCPs\n'));
|
|
47
|
+
const specDir = path.join(process.cwd(), '.spec');
|
|
48
|
+
const archPath = path.join(specDir, 'architecture.md');
|
|
49
|
+
if (!fs.existsSync(archPath)) {
|
|
50
|
+
console.log(chalk_1.default.red('\n❌ architecture.md não encontrado.'));
|
|
51
|
+
console.log(chalk_1.default.gray(' Execute primeiro: ') + chalk_1.default.white('vireum-spec setup\n'));
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
const spinner = (0, ora_1.default)('Lendo MCPs do projeto...').start();
|
|
55
|
+
await sleep(400);
|
|
56
|
+
const arch = fs.readFileSync(archPath, 'utf-8');
|
|
57
|
+
const mcpsProjeto = extrairMcpsProjeto(arch);
|
|
58
|
+
spinner.stop();
|
|
59
|
+
if (mcpsProjeto.length === 0) {
|
|
60
|
+
console.log(chalk_1.default.yellow('\n⚠️ Nenhum MCP configurado neste projeto.\n'));
|
|
61
|
+
console.log(chalk_1.default.gray(' Execute: ') + chalk_1.default.white('vireum-spec setup\n'));
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
console.log(chalk_1.default.gray(` MCPs necessários: ${mcpsProjeto.join(', ')}\n`));
|
|
65
|
+
// ── Detectar configs dos clientes ─────────────────────────────────────────
|
|
66
|
+
const home = os.homedir();
|
|
67
|
+
const clientes = [
|
|
68
|
+
{
|
|
69
|
+
nome: 'Claude Code',
|
|
70
|
+
paths: [
|
|
71
|
+
path.join(home, '.claude', 'settings.json'),
|
|
72
|
+
path.join(home, '.claude.json'),
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
nome: 'Cursor',
|
|
77
|
+
paths: [
|
|
78
|
+
path.join(home, '.cursor', 'mcp.json'),
|
|
79
|
+
path.join(home, 'AppData', 'Roaming', 'Cursor', 'User', 'globalStorage', 'mcp.json'),
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
nome: 'Codex CLI',
|
|
84
|
+
paths: [
|
|
85
|
+
path.join(home, '.codex', 'config.json'),
|
|
86
|
+
path.join(home, '.config', 'codex', 'config.json'),
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
];
|
|
90
|
+
// ── Checar cada cliente ────────────────────────────────────────────────────
|
|
91
|
+
for (const cliente of clientes) {
|
|
92
|
+
console.log(chalk_1.default.white.bold(` ${cliente.nome}`));
|
|
93
|
+
const configPath = cliente.paths.find(p => fs.existsSync(p));
|
|
94
|
+
if (!configPath) {
|
|
95
|
+
console.log(chalk_1.default.gray(` Config não encontrada — cliente pode não estar instalado\n`));
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
let config = {};
|
|
99
|
+
try {
|
|
100
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
console.log(chalk_1.default.yellow(` ⚠ Erro ao ler config em ${configPath}\n`));
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const mcpsInstalados = extrairMcpsInstalados(config);
|
|
107
|
+
for (const mcp of mcpsProjeto) {
|
|
108
|
+
const instalado = mcpsInstalados.some(m => m.toLowerCase().includes(mcp.toLowerCase()));
|
|
109
|
+
if (instalado) {
|
|
110
|
+
console.log(chalk_1.default.green(` ✓ ${mcp}`));
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
console.log(chalk_1.default.red(` ✗ ${mcp} — não encontrado`));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
console.log('');
|
|
117
|
+
}
|
|
118
|
+
// ── Instruções ─────────────────────────────────────────────────────────────
|
|
119
|
+
console.log(chalk_1.default.gray(' Para instalar MCPs faltantes consulte: ') + chalk_1.default.white('.spec/mcp-setup.md\n'));
|
|
120
|
+
}
|
|
121
|
+
// ─── HELPERS ──────────────────────────────────────────────────────────────────
|
|
122
|
+
function extrairMcpsProjeto(arch) {
|
|
123
|
+
const idx = arch.indexOf('## MCPs Ativos');
|
|
124
|
+
if (idx === -1)
|
|
125
|
+
return [];
|
|
126
|
+
const secao = arch.slice(idx + '## MCPs Ativos'.length);
|
|
127
|
+
const linhas = secao.split('\n').filter(l => l.startsWith('- '));
|
|
128
|
+
return linhas.map(l => l.replace('- ', '').trim()).filter(Boolean);
|
|
129
|
+
}
|
|
130
|
+
function extrairMcpsInstalados(config) {
|
|
131
|
+
const nomes = [];
|
|
132
|
+
// Claude Code formato: { mcpServers: { "nome": {...} } }
|
|
133
|
+
if (config.mcpServers) {
|
|
134
|
+
nomes.push(...Object.keys(config.mcpServers));
|
|
135
|
+
}
|
|
136
|
+
// Cursor formato: { mcpServers: { "nome": {...} } }
|
|
137
|
+
if (config.mcp?.servers) {
|
|
138
|
+
nomes.push(...Object.keys(config.mcp.servers));
|
|
139
|
+
}
|
|
140
|
+
// Codex formato: { tools: [...] }
|
|
141
|
+
if (config.tools && Array.isArray(config.tools)) {
|
|
142
|
+
nomes.push(...config.tools.map((t) => t.name || t));
|
|
143
|
+
}
|
|
144
|
+
return nomes;
|
|
145
|
+
}
|
|
146
|
+
function sleep(ms) {
|
|
147
|
+
return new Promise(r => setTimeout(r, ms));
|
|
148
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const init_1 = require("./commands/init");
|
|
10
|
+
const distill_1 = require("./commands/distill");
|
|
11
|
+
const setup_1 = require("./commands/setup");
|
|
12
|
+
const prioritize_1 = require("./commands/prioritize");
|
|
13
|
+
const health_1 = require("./commands/health");
|
|
14
|
+
const brief_1 = require("./commands/brief");
|
|
15
|
+
const verify_mcps_1 = require("./commands/verify-mcps");
|
|
16
|
+
const skills_1 = require("./commands/skills");
|
|
17
|
+
const program = new commander_1.Command();
|
|
18
|
+
program
|
|
19
|
+
.name('vireum-spec')
|
|
20
|
+
.description('Spec Driven Development framework by Vireum Desenvolvimento')
|
|
21
|
+
.version('0.1.0');
|
|
22
|
+
program
|
|
23
|
+
.command('init')
|
|
24
|
+
.description('Inicia um novo projeto com briefing interativo')
|
|
25
|
+
.action(async () => { await (0, init_1.runInit)(); });
|
|
26
|
+
program
|
|
27
|
+
.command('distill')
|
|
28
|
+
.description('Gera os arquivos de spec a partir do briefing')
|
|
29
|
+
.action(async () => { await (0, distill_1.runDistill)(); });
|
|
30
|
+
program
|
|
31
|
+
.command('setup')
|
|
32
|
+
.description('Configura stack, infra, MCPs e protocolo da IA')
|
|
33
|
+
.action(async () => { await (0, setup_1.runSetup)(); });
|
|
34
|
+
program
|
|
35
|
+
.command('prioritize')
|
|
36
|
+
.description('Classifica features em MVP, Fase 2 ou fora do escopo')
|
|
37
|
+
.action(async () => { await (0, prioritize_1.runPrioritize)(); });
|
|
38
|
+
program
|
|
39
|
+
.command('health')
|
|
40
|
+
.description('Verifica inconsistencias nos arquivos de spec')
|
|
41
|
+
.action(async () => { await (0, health_1.runHealth)(); });
|
|
42
|
+
program
|
|
43
|
+
.command('brief')
|
|
44
|
+
.description('Gera resumo do estado atual do projeto')
|
|
45
|
+
.action(async () => { await (0, brief_1.runBrief)(); });
|
|
46
|
+
program
|
|
47
|
+
.command('verify-mcps')
|
|
48
|
+
.description('Verifica se os MCPs do projeto estao instalados')
|
|
49
|
+
.action(async () => { await (0, verify_mcps_1.runVerifyMcps)(); });
|
|
50
|
+
program
|
|
51
|
+
.command('skills')
|
|
52
|
+
.description('Instala as skills do framework no cliente de IA')
|
|
53
|
+
.action(async () => { await (0, skills_1.runSkills)(); });
|
|
54
|
+
console.log(chalk_1.default.hex('#2D7DD2').bold('\n Vireum Spec Framework\n'));
|
|
55
|
+
program.parse(process.argv);
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vireum-spec-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Spec Driven Development framework by Vireum Desenvolvimento",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"vireum-spec": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"dev": "ts-node src/index.ts",
|
|
12
|
+
"start": "node dist/index.js",
|
|
13
|
+
"prepublishOnly": "npm run build"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist/",
|
|
17
|
+
"src/skills/"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"spec",
|
|
21
|
+
"cli",
|
|
22
|
+
"development",
|
|
23
|
+
"spec-driven",
|
|
24
|
+
"vireum"
|
|
25
|
+
],
|
|
26
|
+
"author": "Vireum Desenvolvimento",
|
|
27
|
+
"license": "ISC",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@inquirer/prompts": "^7.0.0",
|
|
30
|
+
"chalk": "^5.3.0",
|
|
31
|
+
"commander": "^12.0.0",
|
|
32
|
+
"ora": "^8.0.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^20.0.0",
|
|
36
|
+
"ts-node": "^10.9.0",
|
|
37
|
+
"typescript": "^5.0.0"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Skill — Corrigir Bug
|
|
2
|
+
|
|
3
|
+
Quando o dev reportar um erro ou bug:
|
|
4
|
+
|
|
5
|
+
1. Crie um hotfix em `.spec/tasks/active.md`:
|
|
6
|
+
- Formato: `## [FEATURE-H001] Fix: descrição do bug`
|
|
7
|
+
- Tag: [H] — prioridade crítica
|
|
8
|
+
- Origem: bug reportado em data atual
|
|
9
|
+
2. Identifique a causa raiz antes de corrigir
|
|
10
|
+
3. Corrija o bug
|
|
11
|
+
4. Registre em `.spec/changelog.md`:
|
|
12
|
+
- Data, descrição do bug, causa raiz, solução aplicada
|
|
13
|
+
5. Verifique se o bug afeta outras tasks em active.md
|
|
14
|
+
6. Marque o hotfix como done e mova para tasks/done.md
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Skill — Contrato de Interface
|
|
2
|
+
|
|
3
|
+
Antes de implementar qualquer feature que envolva frontend E backend:
|
|
4
|
+
|
|
5
|
+
1. Defina o contrato da API primeiro:
|
|
6
|
+
- Método e rota: `POST /auth/login`
|
|
7
|
+
- Request body com tipos
|
|
8
|
+
- Response de sucesso com tipos
|
|
9
|
+
- Responses de erro possíveis
|
|
10
|
+
2. Registre o contrato em `.spec/architecture.md`
|
|
11
|
+
3. Só depois implemente backend e frontend
|
|
12
|
+
4. Frontend e backend podem rodar em paralelo após contrato definido
|
|
13
|
+
|
|
14
|
+
## Formato do contrato
|
|
15
|
+
```
|
|
16
|
+
### [FEATURE] Nome da feature
|
|
17
|
+
- Rota: METHOD /path
|
|
18
|
+
- Body: { campo: tipo }
|
|
19
|
+
- Response 200: { campo: tipo }
|
|
20
|
+
- Response 4xx: { error: string }
|
|
21
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Skill — Nova Demanda do Cliente
|
|
2
|
+
|
|
3
|
+
Quando o dev mencionar que o cliente pediu algo novo:
|
|
4
|
+
|
|
5
|
+
1. Verifique se já existe em `.spec/requirements.md`
|
|
6
|
+
2. Se não existir:
|
|
7
|
+
- Crie task com tag [PENDING] em `.spec/tasks/backlog.md`
|
|
8
|
+
- Formato: `- [ ] [FEATURE-TXXX] nome — [PENDING: aguarda priorização]`
|
|
9
|
+
- Estime o impacto: quais tasks existentes são afetadas?
|
|
10
|
+
3. Informe o dev:
|
|
11
|
+
- O que foi criado no backlog
|
|
12
|
+
- Quais tasks existentes são impactadas
|
|
13
|
+
- Aguarde decisão antes de implementar
|
|
14
|
+
4. NUNCA implemente demanda nova sem aprovação explícita do dev
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Skill — Verificar Escopo
|
|
2
|
+
|
|
3
|
+
Antes de implementar qualquer coisa, verificar:
|
|
4
|
+
|
|
5
|
+
1. A solicitação está em `.spec/requirements.md`?
|
|
6
|
+
- Sim → prosseguir normalmente
|
|
7
|
+
- Não → PARAR e avisar o dev (escopo creep)
|
|
8
|
+
|
|
9
|
+
2. A solicitação está em `.spec/tasks/active.md`?
|
|
10
|
+
- Sim → prosseguir
|
|
11
|
+
- Não → verificar se está no backlog ou é demanda nova
|
|
12
|
+
|
|
13
|
+
3. Se escopo creep identificado, informar:
|
|
14
|
+
- O que foi solicitado
|
|
15
|
+
- Por que está fora do escopo atual
|
|
16
|
+
- Sugerir criar task [PENDING] no backlog
|
|
17
|
+
|
|
18
|
+
## Nunca
|
|
19
|
+
- Implementar silenciosamente algo fora do spec
|
|
20
|
+
- Assumir que o dev "já sabe" que é escopo extra
|
|
21
|
+
- Deixar para avisar depois de implementar
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Skill — Atualizar Spec
|
|
2
|
+
|
|
3
|
+
Quando e como atualizar cada arquivo de spec:
|
|
4
|
+
|
|
5
|
+
## INDEX.md
|
|
6
|
+
- Atualizar após cada task concluída
|
|
7
|
+
- Atualizar estado do MVP (checkboxes)
|
|
8
|
+
- Manter enxuto — máximo 50 linhas
|
|
9
|
+
|
|
10
|
+
## tasks/active.md
|
|
11
|
+
- Marcar task como [x] ao concluir
|
|
12
|
+
- Adicionar hotfixes [H] quando bug identificado
|
|
13
|
+
- Mover tasks concluídas para done.md
|
|
14
|
+
|
|
15
|
+
## tasks/backlog.md
|
|
16
|
+
- Adicionar [PENDING] quando nova demanda chega
|
|
17
|
+
- Nunca mover para active sem aprovação humana
|
|
18
|
+
|
|
19
|
+
## architecture.md
|
|
20
|
+
- Registrar TODA decisão técnica relevante
|
|
21
|
+
- Formato: Data | Decisão | Alternativas | Motivo
|
|
22
|
+
- Nunca só registrar o quê — sempre o porquê
|
|
23
|
+
|
|
24
|
+
## risks.md
|
|
25
|
+
- Adicionar risco novo antes de continuar implementação
|
|
26
|
+
- Formato: Data | Risco | Impacto | Mitigação
|
|
27
|
+
|
|
28
|
+
## changelog.md
|
|
29
|
+
- Registrar: decisões importantes, causa raiz de bugs, mudanças de escopo
|
|
30
|
+
- Formato: ## DATA — Descrição
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Skill — Implementar Task
|
|
2
|
+
|
|
3
|
+
Quando o dev pedir para implementar uma task:
|
|
4
|
+
|
|
5
|
+
1. Leia `.spec/tasks/active.md` e identifique a task solicitada
|
|
6
|
+
2. Leia `.spec/requirements.md` para contexto da feature
|
|
7
|
+
3. Leia `.spec/architecture.md` para decisões técnicas já tomadas
|
|
8
|
+
4. Verifique os critérios de aceitação da task antes de começar
|
|
9
|
+
5. Implemente seguindo a camada definida (Backend / Frontend / Integração)
|
|
10
|
+
6. Ao concluir:
|
|
11
|
+
- Marque o status como [x] em active.md
|
|
12
|
+
- Mova a task para tasks/done.md
|
|
13
|
+
- Atualize o INDEX.md
|
|
14
|
+
- Se tomou decisão arquitetural, registre em architecture.md
|