specifica-br 1.2.2 → 1.5.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 +114 -44
- package/dist/boilerplate/commands/executar-task.md +133 -0
- package/dist/boilerplate/commands/gerar-contexto.md +1462 -0
- package/dist/boilerplate/commands/gerar-prd.md +289 -0
- package/dist/boilerplate/commands/gerar-tasks.md +168 -0
- package/dist/boilerplate/commands/gerar-techspec.md +1467 -0
- package/dist/boilerplate/commands/gerar-visao.md +731 -0
- package/dist/boilerplate/commands/realizar-codereview.md +288 -0
- package/dist/boilerplate/opencode-commands/executar-task.md +55 -48
- package/dist/boilerplate/opencode-commands/gerar-contexto.md +1462 -0
- package/dist/boilerplate/opencode-commands/gerar-prd.md +232 -40
- package/dist/boilerplate/opencode-commands/gerar-tasks.md +112 -31
- package/dist/boilerplate/opencode-commands/gerar-techspec.md +1464 -80
- package/dist/boilerplate/opencode-commands/gerar-visao.md +731 -0
- package/dist/boilerplate/opencode-commands/realizar-codereview.md +288 -0
- package/dist/boilerplate/skills/product-manager/SKILL.md +32 -0
- package/dist/boilerplate/skills/techspec-generator/SKILL.md +489 -0
- package/dist/boilerplate/skills/techspec-generator/references/api-contracts.md +421 -0
- package/dist/boilerplate/skills/techspec-generator/references/architecture-patterns.md +316 -0
- package/dist/boilerplate/skills/techspec-generator/references/database-modeling.md +436 -0
- package/dist/boilerplate/skills/techspec-generator/references/observability-testing.md +436 -0
- package/dist/boilerplate/skills/techspec-generator/references/security-hardening.md +238 -0
- package/dist/boilerplate/skills/techspec-generator/references/ux-ui-accessibility.md +511 -0
- package/dist/boilerplate/specs-templates/architecture-template.md +736 -0
- package/dist/boilerplate/specs-templates/codereview-template.md +95 -0
- package/dist/boilerplate/specs-templates/prd-template.md +101 -19
- package/dist/boilerplate/specs-templates/product_vision-template.md +284 -0
- package/dist/boilerplate/specs-templates/task-template.md +64 -18
- package/dist/boilerplate/specs-templates/tasks-template.md +12 -4
- package/dist/boilerplate/specs-templates/techspec-template.md +1227 -89
- package/dist/boilerplate/templates/architecture-template.md +736 -0
- package/dist/boilerplate/templates/codereview-template.md +95 -0
- package/dist/boilerplate/templates/prd-template.md +167 -0
- package/dist/boilerplate/templates/product_vision-template.md +284 -0
- package/dist/boilerplate/templates/task-template.md +169 -0
- package/dist/boilerplate/templates/tasks-template.md +15 -0
- package/dist/boilerplate/templates/techspec-template.md +1306 -0
- package/dist/commands/help.js +33 -11
- package/dist/commands/init.js +39 -43
- package/dist/tools-mapping.json +32 -0
- package/dist/types/init.d.ts +14 -17
- package/dist/utils/file-service.d.ts +5 -3
- package/dist/utils/file-service.js +168 -56
- package/dist/utils/message-formatter.d.ts +2 -1
- package/dist/utils/message-formatter.js +39 -22
- package/package.json +1 -1
package/dist/commands/help.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { updateNotifierMiddleware } from '../utils/update-notifier-middleware.js';
|
|
4
|
-
/**
|
|
5
|
-
* Exibe o workflow completo de Spec Driven Development (SDD).
|
|
6
|
-
* Mostra o fluxo detalhado desde a inicialização até a execução de tarefas.
|
|
7
|
-
*/
|
|
8
4
|
function showCompleteHelp() {
|
|
9
5
|
console.log('');
|
|
10
6
|
console.log(chalk.cyan.bold('Workflow Completo de Spec Driven Development (SDD)'));
|
|
@@ -13,27 +9,50 @@ function showCompleteHelp() {
|
|
|
13
9
|
console.log(chalk.white(' $ specifica init'));
|
|
14
10
|
console.log(chalk.gray(' Cria estrutura de diretórios e templates no projeto.'));
|
|
15
11
|
console.log('');
|
|
16
|
-
console.log(chalk.yellow('2.
|
|
12
|
+
console.log(chalk.yellow('2. Definição do Contexto:'));
|
|
13
|
+
console.log(chalk.gray(' Escolha baseada no tipo de projeto:'));
|
|
14
|
+
console.log('');
|
|
15
|
+
console.log(chalk.white(' Projeto Novo (Green Field):'));
|
|
16
|
+
console.log(chalk.white(' /gerar-visao [sua ideia]'));
|
|
17
|
+
console.log(chalk.gray(' Define Fundação (Contexto + Stack) - Role: Product Manager + Tech Lead - Foco: Visão macro'));
|
|
18
|
+
console.log('');
|
|
19
|
+
console.log(chalk.white(' Projeto em Desenvolvimento (Brown Field):'));
|
|
20
|
+
console.log(chalk.white(' /gerar-contexto'));
|
|
21
|
+
console.log(chalk.gray(' Analisa código e INFERE (Contexto + Stack) - Role: Engenheiro Senior + Arquiteto - Foco: Análise automática'));
|
|
22
|
+
console.log('');
|
|
23
|
+
console.log(chalk.yellow('3. Geração de PRD:'));
|
|
17
24
|
console.log(chalk.white(' /gerar-prd [sua ideia]'));
|
|
18
25
|
console.log(chalk.gray(' Define requisitos funcionais e regras de negócio da feature.'));
|
|
19
26
|
console.log('');
|
|
20
|
-
console.log(chalk.yellow('
|
|
27
|
+
console.log(chalk.yellow('4. Geração de Tech Spec:'));
|
|
21
28
|
console.log(chalk.white(' /gerar-techspec [caminho do prd]'));
|
|
22
29
|
console.log(chalk.gray(' Define arquitetura técnica, componentes e plano de implementação.'));
|
|
23
30
|
console.log('');
|
|
24
|
-
console.log(chalk.yellow('
|
|
31
|
+
console.log(chalk.yellow('5. Geração de Tarefas:'));
|
|
25
32
|
console.log(chalk.white(' /gerar-tasks [caminho do prd] [caminho do tech spec]'));
|
|
26
33
|
console.log(chalk.gray(' Decompõe o plano técnico em tarefas executáveis.'));
|
|
27
34
|
console.log('');
|
|
28
|
-
console.log(chalk.yellow('
|
|
29
|
-
console.log(chalk.white(' /executar-task [caminho da task]
|
|
35
|
+
console.log(chalk.yellow('6. Execução de Tarefas:'));
|
|
36
|
+
console.log(chalk.white(' /executar-task [caminho da task]'));
|
|
30
37
|
console.log(chalk.gray(' Implementa cada tarefa individualmente seguindo a especificação.'));
|
|
31
38
|
console.log('');
|
|
39
|
+
console.log(chalk.yellow('7. Code Review:'));
|
|
40
|
+
console.log(chalk.white(' /realizar-codereview'));
|
|
41
|
+
console.log(chalk.gray(' Realiza code review do código implementado.'));
|
|
42
|
+
console.log('');
|
|
43
|
+
console.log(chalk.cyan.bold('Ferramentas de IA suportadas:'));
|
|
44
|
+
console.log('');
|
|
45
|
+
console.log(chalk.white(' • ClaudeCode'));
|
|
46
|
+
console.log(chalk.white(' • Cursor'));
|
|
47
|
+
console.log(chalk.white(' • Gemini CLI'));
|
|
48
|
+
console.log(chalk.white(' • Kiro'));
|
|
49
|
+
console.log(chalk.white(' • OpenCode'));
|
|
50
|
+
console.log('');
|
|
32
51
|
console.log(chalk.cyan.bold('Benefícios do SDD:'));
|
|
33
52
|
console.log('');
|
|
34
53
|
console.log(chalk.white(' • Documentação antes do código'));
|
|
35
54
|
console.log(chalk.white(' • Redução de retrabalho'));
|
|
36
|
-
console.log(chalk.white(' • Comunicação alinhada entre
|
|
55
|
+
console.log(chalk.white(' • Comunicação alinhada entre vezes'));
|
|
37
56
|
console.log('');
|
|
38
57
|
}
|
|
39
58
|
/**
|
|
@@ -50,7 +69,7 @@ function runHelpCommand(options) {
|
|
|
50
69
|
console.log(chalk.white(' specifica-br [options] [command]'));
|
|
51
70
|
console.log('');
|
|
52
71
|
console.log(chalk.cyan.bold('Options:'));
|
|
53
|
-
console.log(chalk.white(' -V, --version output
|
|
72
|
+
console.log(chalk.white(' -V, --version output version number'));
|
|
54
73
|
console.log(chalk.white(' -h, --help display help for command'));
|
|
55
74
|
console.log('');
|
|
56
75
|
console.log(chalk.cyan.bold('Commands:'));
|
|
@@ -58,6 +77,9 @@ function runHelpCommand(options) {
|
|
|
58
77
|
console.log(chalk.white(' help [command] display help for command'));
|
|
59
78
|
console.log(chalk.white(' upgrade Atualiza templates e comandos (em breve)'));
|
|
60
79
|
console.log('');
|
|
80
|
+
console.log(chalk.cyan.bold('Ferramentas de IA suportadas:'));
|
|
81
|
+
console.log(chalk.white(' OpenCode, ClaudeCode, Cursor, Gemini CLI, Kiro'));
|
|
82
|
+
console.log('');
|
|
61
83
|
console.log(chalk.gray('Para ajuda detalhada, use: specifica-br help --completo'));
|
|
62
84
|
console.log('');
|
|
63
85
|
}
|
package/dist/commands/init.js
CHANGED
|
@@ -15,60 +15,56 @@ async function runInitCommand() {
|
|
|
15
15
|
const fileService = new FileService();
|
|
16
16
|
try {
|
|
17
17
|
process.on('SIGINT', handleSigInt);
|
|
18
|
-
const
|
|
18
|
+
const toolsMapping = await fileService.loadToolsMapping();
|
|
19
|
+
const choices = toolsMapping.map(tool => ({
|
|
20
|
+
title: tool.name,
|
|
21
|
+
value: tool.name
|
|
22
|
+
}));
|
|
23
|
+
const toolResponse = await prompts({
|
|
19
24
|
type: 'select',
|
|
20
|
-
name: '
|
|
21
|
-
message: 'Selecione a
|
|
22
|
-
choices
|
|
23
|
-
{ title: 'Recomendado (OpenCode)', value: 'opencode', description: 'Cria diretório .opencode/commands/ para uso com OpenCode' },
|
|
24
|
-
{ title: 'Agnóstico (Specifica-BR)', value: 'specifica-br', description: 'Cria diretório specifica-br/commands/ para uso em qualquer IDE/IA' }
|
|
25
|
-
],
|
|
25
|
+
name: 'toolName',
|
|
26
|
+
message: 'Selecione a ferramenta de IA:',
|
|
27
|
+
choices,
|
|
26
28
|
initial: 0
|
|
27
29
|
});
|
|
28
|
-
if (!
|
|
30
|
+
if (!toolResponse.toolName) {
|
|
29
31
|
showErrorMessage('Operação cancelada pelo usuário.');
|
|
30
32
|
process.exit(1);
|
|
31
33
|
}
|
|
34
|
+
const selectedTool = toolsMapping.find(tool => tool.name === toolResponse.toolName);
|
|
35
|
+
if (!selectedTool) {
|
|
36
|
+
showErrorMessage('Erro: Ferramenta não encontrada no mapeamento.');
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
32
39
|
const answers = {
|
|
33
|
-
|
|
40
|
+
toolName: toolResponse.toolName
|
|
34
41
|
};
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
initial: 0
|
|
57
|
-
});
|
|
58
|
-
if (!modelResponse.model) {
|
|
59
|
-
showErrorMessage('Operação cancelada pelo usuário.');
|
|
60
|
-
process.exit(1);
|
|
61
|
-
}
|
|
62
|
-
answers.tool = toolResponse.tool;
|
|
63
|
-
answers.model = modelResponse.model;
|
|
64
|
-
console.log('');
|
|
65
|
-
showInfoMessage(`Ferramenta: ${answers.tool} | Modelo: ${answers.model}`);
|
|
42
|
+
console.log('');
|
|
43
|
+
showInfoMessage('Os seguintes diretórios serão criados:');
|
|
44
|
+
if (selectedTool.commands) {
|
|
45
|
+
console.log(` Commands: ${selectedTool.commands} (7 arquivos)`);
|
|
46
|
+
}
|
|
47
|
+
if (selectedTool.skills) {
|
|
48
|
+
console.log(` Skills: ${selectedTool.skills} (2 skills com referências)`);
|
|
49
|
+
}
|
|
50
|
+
if (selectedTool.templates) {
|
|
51
|
+
console.log(` Templates: ${selectedTool.templates} (7 arquivos)`);
|
|
52
|
+
}
|
|
53
|
+
console.log('');
|
|
54
|
+
const confirmResponse = await prompts({
|
|
55
|
+
type: 'confirm',
|
|
56
|
+
name: 'confirm',
|
|
57
|
+
message: 'Deseja continuar?',
|
|
58
|
+
initial: true
|
|
59
|
+
});
|
|
60
|
+
if (!confirmResponse.confirm) {
|
|
61
|
+
showErrorMessage('Operação cancelada pelo usuário.');
|
|
62
|
+
process.exit(1);
|
|
66
63
|
}
|
|
67
64
|
console.log('');
|
|
68
65
|
showInfoMessage('Criando estrutura de diretórios...');
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
showSuccessMessage(answers.directoryConvention);
|
|
66
|
+
const copyResult = await fileService.createStructure(selectedTool);
|
|
67
|
+
showSuccessMessage(copyResult);
|
|
72
68
|
showMissionMessage();
|
|
73
69
|
}
|
|
74
70
|
catch (error) {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "ClaudeCode",
|
|
4
|
+
"commands": ".claude/commands/",
|
|
5
|
+
"skills": ".claude/skills/",
|
|
6
|
+
"templates": "specs/templates/"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"name": "Cursor",
|
|
10
|
+
"commands": ".cursor/commands/",
|
|
11
|
+
"skills": ".cursor/skills/",
|
|
12
|
+
"templates": "specs/templates/"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "Gemini CLI",
|
|
16
|
+
"commands": ".gemini/commands/",
|
|
17
|
+
"skills": ".agents/skills/",
|
|
18
|
+
"templates": "specs/templates/"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "Kiro",
|
|
22
|
+
"commands": ".kiro/commands/",
|
|
23
|
+
"skills": ".kiro/skills/",
|
|
24
|
+
"templates": "specs/templates/"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"name": "OpenCode",
|
|
28
|
+
"commands": ".opencode/commands/",
|
|
29
|
+
"skills": ".agents/skills/",
|
|
30
|
+
"templates": "specs/templates/"
|
|
31
|
+
}
|
|
32
|
+
]
|
package/dist/types/init.d.ts
CHANGED
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
export interface ToolMapping {
|
|
2
|
+
name: string;
|
|
3
|
+
commands?: string;
|
|
4
|
+
skills?: string;
|
|
5
|
+
templates?: string;
|
|
6
|
+
}
|
|
5
7
|
export interface InitAnswers {
|
|
6
|
-
|
|
7
|
-
tool?: string;
|
|
8
|
-
model?: string;
|
|
8
|
+
toolName: string;
|
|
9
9
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
model: string;
|
|
18
|
-
opencodeCommands: string[];
|
|
19
|
-
};
|
|
10
|
+
export interface CopyResult {
|
|
11
|
+
commandsDir: string;
|
|
12
|
+
commandsCopied: string[];
|
|
13
|
+
skillsDir?: string;
|
|
14
|
+
skillsCopied: string[];
|
|
15
|
+
templatesDir: string;
|
|
16
|
+
templatesCopied: string[];
|
|
20
17
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import type { ToolMapping, CopyResult } from '../types/init.js';
|
|
1
2
|
export declare class FileService {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
loadToolsMapping(): Promise<ToolMapping[]>;
|
|
4
|
+
createStructure(toolMapping: ToolMapping): Promise<CopyResult>;
|
|
5
|
+
copyDirectoryContents(sourceDir: string, destDir: string): Promise<string[]>;
|
|
6
|
+
copySkillsRecursively(sourceDir: string, destDir: string): Promise<string[]>;
|
|
5
7
|
}
|
|
@@ -3,72 +3,184 @@ import path from 'path';
|
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
5
5
|
export class FileService {
|
|
6
|
-
async
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
async loadToolsMapping() {
|
|
7
|
+
const mappingPath = path.join(__dirname, '..', 'tools-mapping.json');
|
|
8
|
+
try {
|
|
9
|
+
await fs.access(mappingPath);
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
throw new Error('Erro: Arquivo de mapeamento de ferramentas (tools-mapping.json) não encontrado ou inválido.');
|
|
13
|
+
}
|
|
14
|
+
let mappingContent;
|
|
15
|
+
try {
|
|
16
|
+
mappingContent = await fs.readFile(mappingPath, 'utf-8');
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
throw new Error('Erro: Arquivo de mapeamento de ferramentas (tools-mapping.json) não encontrado ou inválido.');
|
|
20
|
+
}
|
|
21
|
+
let mappings;
|
|
22
|
+
try {
|
|
23
|
+
mappings = JSON.parse(mappingContent);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
throw new Error('Erro: Arquivo tools-mapping.json contém JSON inválido.');
|
|
27
|
+
}
|
|
28
|
+
if (!Array.isArray(mappings)) {
|
|
29
|
+
throw new Error('Erro: Arquivo tools-mapping.json com formato inválido.');
|
|
30
|
+
}
|
|
31
|
+
if (mappings.length !== 5) {
|
|
32
|
+
throw new Error('Erro: Arquivo tools-mapping.json com formato inválido.');
|
|
33
|
+
}
|
|
34
|
+
for (const mapping of mappings) {
|
|
35
|
+
if (typeof mapping !== 'object' || mapping === null) {
|
|
36
|
+
throw new Error('Erro: Arquivo tools-mapping.json com formato inválido.');
|
|
37
|
+
}
|
|
38
|
+
const toolMapping = mapping;
|
|
39
|
+
if (!toolMapping.name || typeof toolMapping.name !== 'string' || toolMapping.name.trim().length === 0) {
|
|
40
|
+
throw new Error('Erro: Arquivo tools-mapping.json com formato inválido.');
|
|
41
|
+
}
|
|
42
|
+
if (toolMapping.commands !== undefined && (typeof toolMapping.commands !== 'string' || toolMapping.commands.trim().length === 0)) {
|
|
43
|
+
throw new Error('Erro: Arquivo tools-mapping.json com formato inválido.');
|
|
44
|
+
}
|
|
45
|
+
if (toolMapping.skills !== undefined && (typeof toolMapping.skills !== 'string' || toolMapping.skills.trim().length === 0)) {
|
|
46
|
+
throw new Error('Erro: Arquivo tools-mapping.json com formato inválido.');
|
|
47
|
+
}
|
|
48
|
+
if (toolMapping.templates !== undefined && (typeof toolMapping.templates !== 'string' || toolMapping.templates.trim().length === 0)) {
|
|
49
|
+
throw new Error('Erro: Arquivo tools-mapping.json com formato inválido.');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return mappings;
|
|
13
53
|
}
|
|
14
|
-
async
|
|
54
|
+
async createStructure(toolMapping) {
|
|
55
|
+
const boilerplateRoot = path.join(__dirname, '..', 'boilerplate');
|
|
56
|
+
const commandsCopied = [];
|
|
57
|
+
const skillsCopied = [];
|
|
58
|
+
const templatesCopied = [];
|
|
59
|
+
if (toolMapping.commands) {
|
|
60
|
+
const sourceDir = path.join(boilerplateRoot, 'commands');
|
|
61
|
+
const destDir = path.join(process.cwd(), toolMapping.commands);
|
|
62
|
+
const exists = await fs.pathExists(sourceDir);
|
|
63
|
+
if (!exists) {
|
|
64
|
+
throw new Error('Erro: Diretorio de commands não encontrado no boilerplate.');
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
await fs.ensureDir(destDir);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (error instanceof Error && 'code' in error && error.code === 'EACCES') {
|
|
71
|
+
throw new Error('Erro: Sem permissão para criar diretorios.');
|
|
72
|
+
}
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
const files = await this.copyDirectoryContents(sourceDir, destDir);
|
|
76
|
+
commandsCopied.push(...files);
|
|
77
|
+
}
|
|
78
|
+
if (toolMapping.skills) {
|
|
79
|
+
const sourceDir = path.join(boilerplateRoot, 'skills');
|
|
80
|
+
const destDir = path.join(process.cwd(), toolMapping.skills);
|
|
81
|
+
const exists = await fs.pathExists(sourceDir);
|
|
82
|
+
if (!exists) {
|
|
83
|
+
throw new Error('Erro: Diretorio de skills não encontrado no boilerplate.');
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
await fs.ensureDir(destDir);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
if (error instanceof Error && 'code' in error && error.code === 'EACCES') {
|
|
90
|
+
throw new Error('Erro: Sem permissão para criar diretorios.');
|
|
91
|
+
}
|
|
92
|
+
throw error;
|
|
93
|
+
}
|
|
94
|
+
const files = await this.copySkillsRecursively(sourceDir, destDir);
|
|
95
|
+
skillsCopied.push(...files);
|
|
96
|
+
}
|
|
97
|
+
if (toolMapping.templates) {
|
|
98
|
+
const sourceDir = path.join(boilerplateRoot, 'templates');
|
|
99
|
+
const destDir = path.join(process.cwd(), toolMapping.templates);
|
|
100
|
+
const exists = await fs.pathExists(sourceDir);
|
|
101
|
+
if (!exists) {
|
|
102
|
+
throw new Error('Erro: Diretorio de templates não encontrado no boilerplate.');
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
await fs.ensureDir(destDir);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
if (error instanceof Error && 'code' in error && error.code === 'EACCES') {
|
|
109
|
+
throw new Error('Erro: Sem permissão para criar diretorios.');
|
|
110
|
+
}
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
const files = await this.copyDirectoryContents(sourceDir, destDir);
|
|
114
|
+
templatesCopied.push(...files);
|
|
115
|
+
}
|
|
116
|
+
const result = {
|
|
117
|
+
commandsDir: toolMapping.commands ? path.join(process.cwd(), toolMapping.commands) : '',
|
|
118
|
+
commandsCopied,
|
|
119
|
+
skillsDir: toolMapping.skills ? path.join(process.cwd(), toolMapping.skills) : undefined,
|
|
120
|
+
skillsCopied,
|
|
121
|
+
templatesDir: toolMapping.templates ? path.join(process.cwd(), toolMapping.templates) : '',
|
|
122
|
+
templatesCopied
|
|
123
|
+
};
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
async copyDirectoryContents(sourceDir, destDir) {
|
|
127
|
+
const copiedFiles = [];
|
|
15
128
|
try {
|
|
16
|
-
await fs.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
129
|
+
const files = await fs.readdir(sourceDir);
|
|
130
|
+
for (const file of files) {
|
|
131
|
+
const sourcePath = path.join(sourceDir, file);
|
|
132
|
+
const destPath = path.join(destDir, file);
|
|
133
|
+
const stat = await fs.stat(sourcePath);
|
|
134
|
+
if (stat.isFile()) {
|
|
135
|
+
try {
|
|
136
|
+
await fs.copy(sourcePath, destPath, { overwrite: true });
|
|
137
|
+
copiedFiles.push(file);
|
|
138
|
+
console.log(`✓ Arquivo copiado: ${file}`);
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
if (error instanceof Error && 'code' in error && error.code === 'EACCES') {
|
|
142
|
+
throw new Error('Erro: Sem permissão para copiar arquivos.');
|
|
143
|
+
}
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
20
148
|
}
|
|
21
149
|
catch (error) {
|
|
22
150
|
if (error instanceof Error && 'code' in error && error.code === 'EACCES') {
|
|
23
|
-
throw new Error('Erro: Sem permissão para
|
|
151
|
+
throw new Error('Erro: Sem permissão para copiar arquivos.');
|
|
24
152
|
}
|
|
25
153
|
throw error;
|
|
26
154
|
}
|
|
155
|
+
return copiedFiles;
|
|
27
156
|
}
|
|
28
|
-
async
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
{
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
source: path.join(distRoot, 'specs-templates', 'techspec-template.md'),
|
|
53
|
-
dest: path.join(process.cwd(), 'specs', 'templates', 'techspec-template.md')
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
source: path.join(distRoot, 'specs-templates', 'task-template.md'),
|
|
57
|
-
dest: path.join(process.cwd(), 'specs', 'templates', 'task-template.md')
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
source: path.join(distRoot, 'specs-templates', 'tasks-template.md'),
|
|
61
|
-
dest: path.join(process.cwd(), 'specs', 'templates', 'tasks-template.md')
|
|
62
|
-
}
|
|
63
|
-
];
|
|
64
|
-
for (const { source, dest } of templateFiles) {
|
|
65
|
-
if (await fs.pathExists(source)) {
|
|
66
|
-
await fs.copy(source, dest, { overwrite: true });
|
|
67
|
-
console.log(`✓ Arquivo copiado: ${path.basename(dest)}`);
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
throw new Error(`Erro: Diretório de templates não encontrado em ${source}`);
|
|
157
|
+
async copySkillsRecursively(sourceDir, destDir) {
|
|
158
|
+
const copiedFiles = [];
|
|
159
|
+
try {
|
|
160
|
+
await fs.copy(sourceDir, destDir, { overwrite: true });
|
|
161
|
+
const collectFiles = async (dir, baseDir) => {
|
|
162
|
+
const items = await fs.readdir(dir);
|
|
163
|
+
for (const item of items) {
|
|
164
|
+
const itemPath = path.join(dir, item);
|
|
165
|
+
const stat = await fs.stat(itemPath);
|
|
166
|
+
const relativePath = path.relative(baseDir, itemPath);
|
|
167
|
+
if (stat.isFile()) {
|
|
168
|
+
copiedFiles.push(relativePath);
|
|
169
|
+
console.log(`✓ Arquivo copiado: ${relativePath}`);
|
|
170
|
+
}
|
|
171
|
+
else if (stat.isDirectory()) {
|
|
172
|
+
await collectFiles(itemPath, baseDir);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
await collectFiles(destDir, destDir);
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
if (error instanceof Error && 'code' in error && error.code === 'EACCES') {
|
|
180
|
+
throw new Error('Erro: Sem permissão para copiar arquivos.');
|
|
71
181
|
}
|
|
182
|
+
throw error;
|
|
72
183
|
}
|
|
184
|
+
return copiedFiles;
|
|
73
185
|
}
|
|
74
186
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import type { CopyResult } from '../types/init.js';
|
|
2
|
+
export declare function showSuccessMessage(copyResult: CopyResult): void;
|
|
2
3
|
export declare function showMissionMessage(): void;
|
|
3
4
|
export declare function showErrorMessage(message: string): void;
|
|
4
5
|
export declare function showInfoMessage(message: string): void;
|
|
@@ -1,30 +1,47 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
? '.opencode/commands/'
|
|
5
|
-
: 'specifica-br/commands/';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
export function showSuccessMessage(copyResult) {
|
|
6
4
|
console.log('');
|
|
7
5
|
console.log(chalk.green.bold('✓ Estrutura SDD criada com sucesso!'));
|
|
8
6
|
console.log('');
|
|
9
7
|
console.log(chalk.gray('Diretórios criados:'));
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
console.log(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
8
|
+
if (copyResult.commandsDir) {
|
|
9
|
+
console.log(chalk.gray(` • ${copyResult.commandsDir}`));
|
|
10
|
+
}
|
|
11
|
+
if (copyResult.skillsDir) {
|
|
12
|
+
console.log(chalk.gray(` • ${copyResult.skillsDir}`));
|
|
13
|
+
}
|
|
14
|
+
if (copyResult.templatesDir) {
|
|
15
|
+
console.log(chalk.gray(` • ${copyResult.templatesDir}`));
|
|
16
|
+
}
|
|
17
|
+
console.log('');
|
|
18
|
+
if (copyResult.commandsCopied.length > 0) {
|
|
19
|
+
console.log(chalk.gray('Arquivos de comandos copiados:'));
|
|
20
|
+
copyResult.commandsCopied.forEach(file => {
|
|
21
|
+
console.log(chalk.gray(` • ${file}`));
|
|
22
|
+
});
|
|
23
|
+
console.log('');
|
|
24
|
+
}
|
|
25
|
+
if (copyResult.skillsCopied.length > 0) {
|
|
26
|
+
console.log(chalk.gray('Arquivos de skills copiados:'));
|
|
27
|
+
copyResult.skillsCopied.forEach(file => {
|
|
28
|
+
console.log(chalk.gray(` • ${file}`));
|
|
29
|
+
});
|
|
30
|
+
console.log('');
|
|
31
|
+
}
|
|
32
|
+
if (copyResult.templatesCopied.length > 0) {
|
|
33
|
+
console.log(chalk.gray('Arquivos de templates copiados:'));
|
|
34
|
+
copyResult.templatesCopied.forEach(file => {
|
|
35
|
+
console.log(chalk.gray(` • ${file}`));
|
|
36
|
+
});
|
|
37
|
+
console.log('');
|
|
38
|
+
}
|
|
39
|
+
if (copyResult.commandsDir) {
|
|
40
|
+
const rootDir = copyResult.commandsDir.split(path.sep)[0];
|
|
41
|
+
console.log(chalk.gray('Para começar, navegue até o diretório de comandos:'));
|
|
42
|
+
console.log(chalk.gray(` cd ${rootDir}`));
|
|
43
|
+
console.log('');
|
|
44
|
+
}
|
|
28
45
|
}
|
|
29
46
|
export function showMissionMessage() {
|
|
30
47
|
console.log('');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specifica-br",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Ferramenta de automação para desenvolvimento guiado por especificações (Spec Driven Development - SDD) com IA. Otimizado para o ecossistema brasileiro.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|