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.
Files changed (46) hide show
  1. package/README.md +114 -44
  2. package/dist/boilerplate/commands/executar-task.md +133 -0
  3. package/dist/boilerplate/commands/gerar-contexto.md +1462 -0
  4. package/dist/boilerplate/commands/gerar-prd.md +289 -0
  5. package/dist/boilerplate/commands/gerar-tasks.md +168 -0
  6. package/dist/boilerplate/commands/gerar-techspec.md +1467 -0
  7. package/dist/boilerplate/commands/gerar-visao.md +731 -0
  8. package/dist/boilerplate/commands/realizar-codereview.md +288 -0
  9. package/dist/boilerplate/opencode-commands/executar-task.md +55 -48
  10. package/dist/boilerplate/opencode-commands/gerar-contexto.md +1462 -0
  11. package/dist/boilerplate/opencode-commands/gerar-prd.md +232 -40
  12. package/dist/boilerplate/opencode-commands/gerar-tasks.md +112 -31
  13. package/dist/boilerplate/opencode-commands/gerar-techspec.md +1464 -80
  14. package/dist/boilerplate/opencode-commands/gerar-visao.md +731 -0
  15. package/dist/boilerplate/opencode-commands/realizar-codereview.md +288 -0
  16. package/dist/boilerplate/skills/product-manager/SKILL.md +32 -0
  17. package/dist/boilerplate/skills/techspec-generator/SKILL.md +489 -0
  18. package/dist/boilerplate/skills/techspec-generator/references/api-contracts.md +421 -0
  19. package/dist/boilerplate/skills/techspec-generator/references/architecture-patterns.md +316 -0
  20. package/dist/boilerplate/skills/techspec-generator/references/database-modeling.md +436 -0
  21. package/dist/boilerplate/skills/techspec-generator/references/observability-testing.md +436 -0
  22. package/dist/boilerplate/skills/techspec-generator/references/security-hardening.md +238 -0
  23. package/dist/boilerplate/skills/techspec-generator/references/ux-ui-accessibility.md +511 -0
  24. package/dist/boilerplate/specs-templates/architecture-template.md +736 -0
  25. package/dist/boilerplate/specs-templates/codereview-template.md +95 -0
  26. package/dist/boilerplate/specs-templates/prd-template.md +101 -19
  27. package/dist/boilerplate/specs-templates/product_vision-template.md +284 -0
  28. package/dist/boilerplate/specs-templates/task-template.md +64 -18
  29. package/dist/boilerplate/specs-templates/tasks-template.md +12 -4
  30. package/dist/boilerplate/specs-templates/techspec-template.md +1227 -89
  31. package/dist/boilerplate/templates/architecture-template.md +736 -0
  32. package/dist/boilerplate/templates/codereview-template.md +95 -0
  33. package/dist/boilerplate/templates/prd-template.md +167 -0
  34. package/dist/boilerplate/templates/product_vision-template.md +284 -0
  35. package/dist/boilerplate/templates/task-template.md +169 -0
  36. package/dist/boilerplate/templates/tasks-template.md +15 -0
  37. package/dist/boilerplate/templates/techspec-template.md +1306 -0
  38. package/dist/commands/help.js +33 -11
  39. package/dist/commands/init.js +39 -43
  40. package/dist/tools-mapping.json +32 -0
  41. package/dist/types/init.d.ts +14 -17
  42. package/dist/utils/file-service.d.ts +5 -3
  43. package/dist/utils/file-service.js +168 -56
  44. package/dist/utils/message-formatter.d.ts +2 -1
  45. package/dist/utils/message-formatter.js +39 -22
  46. package/package.json +1 -1
@@ -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. Geração de PRD:'));
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('3. Geração de Tech Spec:'));
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('4. Geração de Tarefas:'));
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('5. Execução de Tarefas:'));
29
- console.log(chalk.white(' /executar-task [caminho da task] [prd] [tech spec]'));
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 times'));
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 the version number'));
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
  }
@@ -15,60 +15,56 @@ async function runInitCommand() {
15
15
  const fileService = new FileService();
16
16
  try {
17
17
  process.on('SIGINT', handleSigInt);
18
- const directoryConventionResponse = await prompts({
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: 'directoryConvention',
21
- message: 'Selecione a convenção de diretórios para comandos:',
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 (!directoryConventionResponse.directoryConvention) {
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
- directoryConvention: directoryConventionResponse.directoryConvention
40
+ toolName: toolResponse.toolName
34
41
  };
35
- if (answers.directoryConvention === 'opencode') {
36
- const toolResponse = await prompts({
37
- type: 'select',
38
- name: 'tool',
39
- message: 'Selecione a ferramenta de IA:',
40
- choices: [
41
- { title: 'OpenCode', value: 'opencode' }
42
- ],
43
- initial: 0
44
- });
45
- if (!toolResponse.tool) {
46
- showErrorMessage('Operação cancelada pelo usuário.');
47
- process.exit(1);
48
- }
49
- const modelResponse = await prompts({
50
- type: 'select',
51
- name: 'model',
52
- message: 'Selecione o modelo de IA:',
53
- choices: [
54
- { title: 'GLM 4.7', value: 'glm-4.7' }
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
- console.log(`• Convenção selecionada: ${answers.directoryConvention}`);
70
- await fileService.createStructure(answers.directoryConvention);
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
+ ]
@@ -1,20 +1,17 @@
1
- /**
2
- * Interface para as respostas do comando init.
3
- * Representa as escolhas do usuário durante o fluxo interativo.
4
- */
1
+ export interface ToolMapping {
2
+ name: string;
3
+ commands?: string;
4
+ skills?: string;
5
+ templates?: string;
6
+ }
5
7
  export interface InitAnswers {
6
- directoryConvention: 'opencode' | 'specifica-br';
7
- tool?: string;
8
- model?: string;
8
+ toolName: string;
9
9
  }
10
- /**
11
- * Interface para configuração de templates.
12
- * Mapeia ferramentas e modelos para os paths de arquivos de comando.
13
- */
14
- export interface TemplateConfig {
15
- [key: string]: {
16
- tool: string;
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
- createStructure(directoryConvention: 'opencode' | 'specifica-br'): Promise<void>;
3
- private createDirectories;
4
- private copyTemplates;
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 createStructure(directoryConvention) {
7
- const commandsDestDir = directoryConvention === 'opencode'
8
- ? path.join(process.cwd(), '.opencode', 'commands')
9
- : path.join(process.cwd(), 'specifica-br', 'commands');
10
- console.log(`• Criando diretórios com convenção: ${directoryConvention}`);
11
- await this.createDirectories(commandsDestDir, path.join(process.cwd(), 'specs', 'templates'));
12
- await this.copyTemplates(commandsDestDir);
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 createDirectories(commandsDestDir, templatesDestDir) {
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.ensureDir(commandsDestDir);
17
- console.log(`✓ Diretório criado: ${commandsDestDir}`);
18
- await fs.ensureDir(templatesDestDir);
19
- console.log(`✓ Diretório criado: ${templatesDestDir}`);
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 criar diretórios.');
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 copyTemplates(commandsDestDir) {
29
- const distRoot = path.resolve(__dirname, '..', 'boilerplate');
30
- const templateFiles = [
31
- {
32
- source: path.join(distRoot, 'opencode-commands', 'gerar-prd.md'),
33
- dest: path.join(commandsDestDir, 'gerar-prd.md')
34
- },
35
- {
36
- source: path.join(distRoot, 'opencode-commands', 'gerar-techspec.md'),
37
- dest: path.join(commandsDestDir, 'gerar-techspec.md')
38
- },
39
- {
40
- source: path.join(distRoot, 'opencode-commands', 'gerar-tasks.md'),
41
- dest: path.join(commandsDestDir, 'gerar-tasks.md')
42
- },
43
- {
44
- source: path.join(distRoot, 'opencode-commands', 'executar-task.md'),
45
- dest: path.join(commandsDestDir, 'executar-task.md')
46
- },
47
- {
48
- source: path.join(distRoot, 'specs-templates', 'prd-template.md'),
49
- dest: path.join(process.cwd(), 'specs', 'templates', 'prd-template.md')
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
- export declare function showSuccessMessage(directoryConvention: 'opencode' | 'specifica-br'): void;
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
- export function showSuccessMessage(directoryConvention) {
3
- const commandDir = directoryConvention === 'opencode'
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
- console.log(chalk.gray(` • ${commandDir}`));
11
- console.log(chalk.gray('specs/templates/'));
12
- console.log('');
13
- console.log(chalk.gray('Arquivos de comandos copiados:'));
14
- console.log(chalk.gray('gerar-prd.md'));
15
- console.log(chalk.gray(' • gerar-techspec.md'));
16
- console.log(chalk.gray(' • gerar-tasks.md'));
17
- console.log(chalk.gray('executar-task.md'));
18
- console.log('');
19
- console.log(chalk.gray('Arquivos de templates copiados:'));
20
- console.log(chalk.gray(' • prd-template.md'));
21
- console.log(chalk.gray(' techspec-template.md'));
22
- console.log(chalk.gray(' • task-template.md'));
23
- console.log(chalk.gray('tasks-template.md'));
24
- console.log('');
25
- console.log(chalk.gray('Para começar, navegue até o diretório de comandos e execute:'));
26
- console.log(chalk.gray(` cd ${commandDir.split('/')[0]}`));
27
- console.log('');
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.2.2",
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",