siesa-agents 2.1.1 → 2.1.3

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 (145) hide show
  1. package/README.md +83 -83
  2. package/bin/install.js +400 -399
  3. package/bin/prepare-publish.js +26 -26
  4. package/bin/restore-folders.js +26 -26
  5. package/bmad-core/agent-teams/team-all.yaml +15 -15
  6. package/bmad-core/agent-teams/team-fullstack.yaml +19 -19
  7. package/bmad-core/agent-teams/team-ide-minimal.yaml +11 -11
  8. package/bmad-core/agent-teams/team-no-ui.yaml +14 -14
  9. package/bmad-core/agents/analyst.md +84 -84
  10. package/bmad-core/agents/architect.md +94 -94
  11. package/bmad-core/agents/backend-agent.md +189 -189
  12. package/bmad-core/agents/bmad-master.md +110 -110
  13. package/bmad-core/agents/bmad-orchestrator.md +147 -147
  14. package/bmad-core/agents/dev.md +81 -81
  15. package/bmad-core/agents/frontend-agent.md +168 -168
  16. package/bmad-core/agents/pm.md +84 -84
  17. package/bmad-core/agents/po.md +79 -79
  18. package/bmad-core/agents/qa.md +91 -91
  19. package/bmad-core/agents/sm.md +65 -65
  20. package/bmad-core/agents/ux-expert.md +69 -69
  21. package/bmad-core/checklists/architect-checklist.md +440 -440
  22. package/bmad-core/checklists/backend-checklist.md +142 -142
  23. package/bmad-core/checklists/change-checklist.md +184 -184
  24. package/bmad-core/checklists/frontend-checklist.md +105 -105
  25. package/bmad-core/checklists/pm-checklist.md +372 -372
  26. package/bmad-core/checklists/po-master-checklist.md +434 -434
  27. package/bmad-core/checklists/story-dod-checklist.md +96 -96
  28. package/bmad-core/checklists/story-draft-checklist.md +155 -155
  29. package/bmad-core/core-config.yaml +22 -22
  30. package/bmad-core/data/backend-standards.md +439 -439
  31. package/bmad-core/data/bmad-kb.md +809 -809
  32. package/bmad-core/data/brainstorming-techniques.md +38 -38
  33. package/bmad-core/data/elicitation-methods.md +156 -156
  34. package/bmad-core/data/frontend-standards.md +323 -323
  35. package/bmad-core/data/technical-preferences.md +5 -5
  36. package/bmad-core/data/test-levels-framework.md +148 -148
  37. package/bmad-core/data/test-priorities-matrix.md +174 -174
  38. package/bmad-core/enhanced-ide-development-workflow.md +248 -248
  39. package/bmad-core/install-manifest.yaml +230 -230
  40. package/bmad-core/tasks/advanced-elicitation.md +119 -119
  41. package/bmad-core/tasks/apply-qa-fixes.md +150 -150
  42. package/bmad-core/tasks/brownfield-create-epic.md +162 -162
  43. package/bmad-core/tasks/brownfield-create-story.md +149 -149
  44. package/bmad-core/tasks/correct-course.md +72 -72
  45. package/bmad-core/tasks/create-brownfield-story.md +314 -314
  46. package/bmad-core/tasks/create-component.md +102 -102
  47. package/bmad-core/tasks/create-deep-research-prompt.md +280 -280
  48. package/bmad-core/tasks/create-doc.md +103 -103
  49. package/bmad-core/tasks/create-entity.md +132 -132
  50. package/bmad-core/tasks/create-feature.md +90 -90
  51. package/bmad-core/tasks/create-next-story.md +114 -114
  52. package/bmad-core/tasks/create-service.md +117 -117
  53. package/bmad-core/tasks/create-use-case.md +140 -140
  54. package/bmad-core/tasks/document-project.md +345 -345
  55. package/bmad-core/tasks/execute-checklist.md +88 -88
  56. package/bmad-core/tasks/facilitate-brainstorming-session.md +138 -138
  57. package/bmad-core/tasks/generate-ai-frontend-prompt.md +53 -53
  58. package/bmad-core/tasks/index-docs.md +175 -175
  59. package/bmad-core/tasks/kb-mode-interaction.md +77 -77
  60. package/bmad-core/tasks/nfr-assess.md +345 -345
  61. package/bmad-core/tasks/qa-gate.md +163 -163
  62. package/bmad-core/tasks/review-story.md +316 -316
  63. package/bmad-core/tasks/risk-profile.md +355 -355
  64. package/bmad-core/tasks/scaffold-backend.md +110 -110
  65. package/bmad-core/tasks/scaffold-frontend.md +78 -78
  66. package/bmad-core/tasks/shard-doc.md +187 -187
  67. package/bmad-core/tasks/test-design.md +176 -176
  68. package/bmad-core/tasks/trace-requirements.md +266 -266
  69. package/bmad-core/tasks/validate-next-story.md +136 -136
  70. package/bmad-core/templates/architecture-tmpl.yaml +662 -662
  71. package/bmad-core/templates/brainstorming-output-tmpl.yaml +156 -156
  72. package/bmad-core/templates/brownfield-architecture-tmpl.yaml +477 -477
  73. package/bmad-core/templates/brownfield-prd-tmpl.yaml +281 -281
  74. package/bmad-core/templates/competitor-analysis-tmpl.yaml +307 -307
  75. package/bmad-core/templates/front-end-architecture-tmpl.yaml +258 -258
  76. package/bmad-core/templates/front-end-spec-tmpl.yaml +350 -350
  77. package/bmad-core/templates/fullstack-architecture-tmpl.yaml +824 -824
  78. package/bmad-core/templates/market-research-tmpl.yaml +253 -253
  79. package/bmad-core/templates/prd-tmpl.yaml +203 -203
  80. package/bmad-core/templates/project-brief-tmpl.yaml +222 -222
  81. package/bmad-core/templates/qa-gate-tmpl.yaml +103 -103
  82. package/bmad-core/templates/story-tmpl.yaml +138 -138
  83. package/bmad-core/user-guide.md +530 -530
  84. package/bmad-core/utils/bmad-doc-template.md +327 -327
  85. package/bmad-core/utils/workflow-management.md +71 -71
  86. package/bmad-core/workflows/brownfield-fullstack.yaml +298 -298
  87. package/bmad-core/workflows/brownfield-service.yaml +188 -188
  88. package/bmad-core/workflows/brownfield-ui.yaml +198 -198
  89. package/bmad-core/workflows/greenfield-fullstack.yaml +241 -241
  90. package/bmad-core/workflows/greenfield-service.yaml +207 -207
  91. package/bmad-core/workflows/greenfield-ui.yaml +236 -236
  92. package/bmad-core/working-in-the-brownfield.md +606 -606
  93. package/claude/commands/BMad/agents/analyst.md +88 -0
  94. package/claude/commands/BMad/agents/architect.md +89 -0
  95. package/claude/commands/BMad/agents/backend.md +188 -0
  96. package/claude/commands/BMad/agents/bmad-master.md +114 -0
  97. package/claude/commands/BMad/agents/bmad-orchestrator.md +151 -0
  98. package/claude/commands/BMad/agents/dev.md +85 -0
  99. package/claude/commands/BMad/agents/frontend.md +151 -0
  100. package/claude/commands/BMad/agents/pm.md +88 -0
  101. package/claude/commands/BMad/agents/po.md +83 -0
  102. package/claude/commands/BMad/agents/qa.md +95 -0
  103. package/claude/commands/BMad/agents/sm.md +69 -0
  104. package/claude/commands/BMad/agents/ux-expert.md +73 -0
  105. package/claude/commands/BMad/tasks/advanced-elicitation.md +123 -0
  106. package/claude/commands/BMad/tasks/apply-qa-fixes.md +154 -0
  107. package/claude/commands/BMad/tasks/brownfield-create-epic.md +166 -0
  108. package/claude/commands/BMad/tasks/brownfield-create-story.md +153 -0
  109. package/claude/commands/BMad/tasks/correct-course.md +76 -0
  110. package/claude/commands/BMad/tasks/create-brownfield-story.md +318 -0
  111. package/claude/commands/BMad/tasks/create-deep-research-prompt.md +284 -0
  112. package/claude/commands/BMad/tasks/create-doc.md +107 -0
  113. package/claude/commands/BMad/tasks/create-next-story.md +118 -0
  114. package/claude/commands/BMad/tasks/document-project.md +349 -0
  115. package/claude/commands/BMad/tasks/execute-checklist.md +92 -0
  116. package/claude/commands/BMad/tasks/facilitate-brainstorming-session.md +142 -0
  117. package/claude/commands/BMad/tasks/generate-ai-frontend-prompt.md +57 -0
  118. package/claude/commands/BMad/tasks/index-docs.md +179 -0
  119. package/claude/commands/BMad/tasks/kb-mode-interaction.md +81 -0
  120. package/claude/commands/BMad/tasks/nfr-assess.md +349 -0
  121. package/claude/commands/BMad/tasks/qa-gate.md +167 -0
  122. package/claude/commands/BMad/tasks/review-story.md +320 -0
  123. package/claude/commands/BMad/tasks/risk-profile.md +359 -0
  124. package/claude/commands/BMad/tasks/shard-doc.md +191 -0
  125. package/claude/commands/BMad/tasks/test-design.md +180 -0
  126. package/claude/commands/BMad/tasks/trace-requirements.md +270 -0
  127. package/claude/commands/BMad/tasks/validate-next-story.md +140 -0
  128. package/claude/settings.local.json +20 -0
  129. package/github/b-mad-expert.md +742 -742
  130. package/github/chatmodes/analyst.chatmode.md +89 -89
  131. package/github/chatmodes/architect.chatmode.md +97 -97
  132. package/github/chatmodes/backend.chatmode.md +194 -194
  133. package/github/chatmodes/bmad-master.chatmode.md +115 -115
  134. package/github/chatmodes/bmad-orchestrator.chatmode.md +152 -152
  135. package/github/chatmodes/dev.chatmode.md +86 -86
  136. package/github/chatmodes/frontend.chatmode.md +157 -157
  137. package/github/chatmodes/pm.chatmode.md +89 -89
  138. package/github/chatmodes/po.chatmode.md +84 -84
  139. package/github/chatmodes/qa.chatmode.md +96 -96
  140. package/github/chatmodes/sm.chatmode.md +70 -70
  141. package/github/chatmodes/ux-expert.chatmode.md +74 -74
  142. package/index.js +9 -9
  143. package/package.json +37 -36
  144. package/vscode/mcp.json +11 -11
  145. package/vscode/settings.json +12 -12
package/bin/install.js CHANGED
@@ -1,400 +1,401 @@
1
- #!/usr/bin/env node
2
-
3
- const fs = require('fs-extra');
4
- const path = require('path');
5
- const readline = require('readline');
6
-
7
- class SiesaBmadInstaller {
8
- constructor() {
9
- // Definir las carpetas primero (nombres en el paquete vs nombres finales)
10
- this.folderMappings = [
11
- { source: 'bmad-core', target: '.bmad-core' },
12
- { source: 'vscode', target: '.vscode' },
13
- { source: 'github', target: '.github' }
14
- ];
15
- this.targetDir = process.cwd();
16
- // Intentar múltiples ubicaciones posibles para el paquete
17
- this.packageDir = this.findPackageDir();
18
- }
19
-
20
- showBanner() {
21
- console.log('\n');
22
- console.log('███████╗██╗███████╗███████╗ █████╗ █████╗ ██████╗ ███████╗███╗ ██╗████████╗███████╗');
23
- console.log('██╔════╝██║██╔════╝██╔════╝██╔══██╗ ██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝██╔════╝');
24
- console.log('███████╗██║█████╗ ███████╗███████║ ███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║ ███████╗');
25
- console.log('╚════██║██║██╔══╝ ╚════██║██╔══██║ ██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ╚════██║');
26
- console.log('███████║██║███████╗███████║██║ ██║ ██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ███████║');
27
- console.log('╚══════╝╚═╝╚══════╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝');
28
- console.log('');
29
- }
30
-
31
- findPackageDir() {
32
- // Opción 1: directorio padre del bin (instalación normal)
33
- let packageDir = path.dirname(__dirname);
34
-
35
- // Opción 2: si estamos en npx, buscar en node_modules
36
- if (!this.hasRequiredFolders(packageDir)) {
37
- // Para npm 6.14+ y npx, intentar ubicaciones alternativas
38
- const possiblePaths = [
39
- path.join(__dirname, '..'), // bin -> paquete
40
- path.resolve(__dirname, '..'), // alternativa con resolve
41
- path.resolve(__dirname, '..', '..', 'siesa-bmad-agents'), // desde node_modules
42
- // Intentar buscar basado en __dirname específicamente para npx
43
- path.resolve(__dirname.replace(/\\bin$|\/bin$/, '')),
44
- process.cwd(), // directorio actual como último recurso
45
- ];
46
-
47
- for (const possiblePath of possiblePaths) {
48
- if (this.hasRequiredFolders(possiblePath)) {
49
- packageDir = possiblePath;
50
- break;
51
- }
52
- }
53
- }
54
-
55
- return packageDir;
56
- }
57
-
58
- hasRequiredFolders(dir) {
59
- return this.folderMappings.some(mapping => {
60
- const folderPath = path.join(dir, mapping.source);
61
- return fs.existsSync(folderPath);
62
- });
63
- }
64
-
65
- async install() {
66
- this.showBanner();
67
- console.log(' Instalando SIESA Agents...');
68
-
69
- try {
70
- // Verificar si ya existe una instalación
71
- const hasExistingInstallation = this.checkExistingInstallation();
72
-
73
- if (hasExistingInstallation) {
74
- console.log('🔄 Instalación existente detectada. Actualizando...');
75
- await this.update();
76
- } else {
77
- console.log('✨ Nueva instalación...');
78
- await this.performInstallation();
79
- }
80
-
81
- console.log('✅ SIESA Agents instalado correctamente!');
82
- this.showPostInstallMessage();
83
-
84
- } catch (error) {
85
- console.error('❌ Error durante la instalación:', error.message);
86
- process.exit(1);
87
- }
88
- }
89
-
90
- checkExistingInstallation() {
91
- return this.folderMappings.some(mapping => {
92
- const targetPath = path.join(this.targetDir, mapping.target);
93
- return fs.existsSync(targetPath);
94
- });
95
- }
96
-
97
- async checkModifiedFiles() {
98
- const modifiedFiles = [];
99
-
100
- for (const mapping of this.folderMappings) {
101
- const sourcePath = path.join(this.packageDir, mapping.source);
102
- const targetPath = path.join(this.targetDir, mapping.target);
103
-
104
- if (!fs.existsSync(sourcePath) || !fs.existsSync(targetPath)) {
105
- continue;
106
- }
107
-
108
- // Obtener todos los archivos recursivamente
109
- const sourceFiles = await this.getAllFiles(sourcePath);
110
-
111
- for (const sourceFile of sourceFiles) {
112
- const relativePath = path.relative(sourcePath, sourceFile);
113
- const targetFile = path.join(targetPath, relativePath);
114
-
115
- if (fs.existsSync(targetFile)) {
116
- try {
117
- // Comparar contenido de archivos
118
- const sourceContent = await fs.readFile(sourceFile, 'utf8');
119
- const targetContent = await fs.readFile(targetFile, 'utf8');
120
-
121
- if (sourceContent !== targetContent) {
122
- modifiedFiles.push({
123
- folder: mapping.target,
124
- file: relativePath,
125
- fullPath: targetFile
126
- });
127
- }
128
- } catch (error) {
129
- // Si no se puede leer como texto, comparar como buffer (archivos binarios)
130
- const sourceBuffer = await fs.readFile(sourceFile);
131
- const targetBuffer = await fs.readFile(targetFile);
132
-
133
- if (!sourceBuffer.equals(targetBuffer)) {
134
- modifiedFiles.push({
135
- folder: mapping.target,
136
- file: relativePath,
137
- fullPath: targetFile
138
- });
139
- }
140
- }
141
- }
142
- }
143
- }
144
-
145
- return modifiedFiles;
146
- }
147
-
148
- async getAllFiles(dir) {
149
- const files = [];
150
- const items = await fs.readdir(dir);
151
-
152
- for (const item of items) {
153
- const fullPath = path.join(dir, item);
154
- const stat = await fs.stat(fullPath);
155
-
156
- if (stat.isDirectory()) {
157
- const subFiles = await this.getAllFiles(fullPath);
158
- files.push(...subFiles);
159
- } else {
160
- files.push(fullPath);
161
- }
162
- }
163
-
164
- return files;
165
- }
166
-
167
- async promptUser(modifiedFiles) {
168
- console.log('\n⚠️ Se detectaron archivos modificados:');
169
-
170
- // Agrupar por carpeta
171
- const filesByFolder = {};
172
- modifiedFiles.forEach(item => {
173
- if (!filesByFolder[item.folder]) {
174
- filesByFolder[item.folder] = [];
175
- }
176
- filesByFolder[item.folder].push(item.file);
177
- });
178
-
179
- // Mostrar archivos modificados por carpeta
180
- Object.keys(filesByFolder).forEach(folder => {
181
- console.log(`\n📁 ${folder}:`);
182
- filesByFolder[folder].forEach(file => {
183
- console.log(` - ${file}`);
184
- });
185
- });
186
-
187
- console.log('\n¿Qué deseas hacer?');
188
- console.log('1. Reemplazar todos los archivos (se perderán las modificaciones)');
189
- console.log('2. Hacer backup de archivos modificados (se agregarán con sufijo _bk)');
190
-
191
- const rl = readline.createInterface({
192
- input: process.stdin,
193
- output: process.stdout
194
- });
195
-
196
- return new Promise((resolve) => {
197
- rl.question('\nElige una opción (1 o 2): ', (answer) => {
198
- rl.close();
199
- resolve(answer.trim());
200
- });
201
- });
202
- }
203
-
204
- async backupModifiedFiles(modifiedFiles) {
205
- console.log('\n🔄 Creando backup de archivos modificados...');
206
-
207
- for (const item of modifiedFiles) {
208
- const originalPath = item.fullPath;
209
- const backupPath = this.getBackupPath(originalPath);
210
-
211
- try {
212
- await fs.copy(originalPath, backupPath);
213
-
214
- // Determinar tipo de backup para mostrar mensaje apropiado
215
- const backupName = path.basename(backupPath);
216
- const isVersionedBackup = backupName.includes('_bk_');
217
-
218
- if (isVersionedBackup) {
219
- console.log(`✓ Backup versionado: ${path.relative(this.targetDir, backupPath)}`);
220
- } else {
221
- console.log(`✓ Backup creado: ${path.relative(this.targetDir, backupPath)}`);
222
- }
223
- } catch (error) {
224
- console.warn(`⚠️ Error creando backup de ${item.file}: ${error.message}`);
225
- }
226
- }
227
- }
228
-
229
- getBackupPath(filePath) {
230
- const dir = path.dirname(filePath);
231
- const ext = path.extname(filePath);
232
- const name = path.basename(filePath, ext);
233
-
234
- // Primer intento: archivo_bk.ext
235
- const basicBackupPath = path.join(dir, `${name}_bk${ext}`);
236
-
237
- // Si no existe, usar el nombre básico
238
- if (!fs.existsSync(basicBackupPath)) {
239
- return basicBackupPath;
240
- }
241
-
242
- // Si ya existe _bk, crear versión con timestamp
243
- const now = new Date();
244
- const timestamp = now.getFullYear().toString() +
245
- (now.getMonth() + 1).toString().padStart(2, '0') +
246
- now.getDate().toString().padStart(2, '0') + '_' +
247
- now.getHours().toString().padStart(2, '0') +
248
- now.getMinutes().toString().padStart(2, '0') +
249
- now.getSeconds().toString().padStart(2, '0');
250
-
251
- return path.join(dir, `${name}_bk_${timestamp}${ext}`);
252
- }
253
-
254
- async performUpdateWithBackups() {
255
- for (const mapping of this.folderMappings) {
256
- const sourcePath = path.join(this.packageDir, mapping.source);
257
- const targetPath = path.join(this.targetDir, mapping.target);
258
-
259
- if (fs.existsSync(sourcePath)) {
260
- // Copiar archivos selectivamente, preservando los _bk
261
- await this.copyWithBackupPreservation(sourcePath, targetPath);
262
- } else {
263
- console.warn(`⚠️ Carpeta ${mapping.source} no encontrada en el paquete`);
264
- }
265
- }
266
- }
267
-
268
- async copyWithBackupPreservation(sourcePath, targetPath) {
269
- // Obtener todos los archivos backup existentes
270
- const backupFiles = await this.findBackupFiles(targetPath);
271
-
272
- // Copiar la carpeta completa sobrescribiendo
273
- await fs.copy(sourcePath, targetPath, {
274
- overwrite: true,
275
- recursive: true
276
- });
277
-
278
- // Restaurar los archivos backup
279
- for (const backupFile of backupFiles) {
280
- const backupSourcePath = backupFile.tempPath;
281
- const backupTargetPath = backupFile.originalPath;
282
-
283
- try {
284
- await fs.copy(backupSourcePath, backupTargetPath);
285
- // Limpiar archivo temporal
286
- await fs.remove(backupSourcePath);
287
- } catch (error) {
288
- console.warn(`⚠️ Error restaurando backup ${backupFile.relativePath}: ${error.message}`);
289
- }
290
- }
291
- }
292
-
293
- async findBackupFiles(targetPath) {
294
- if (!fs.existsSync(targetPath)) {
295
- return [];
296
- }
297
-
298
- const backupFiles = [];
299
- const allFiles = await this.getAllFiles(targetPath);
300
-
301
- for (const filePath of allFiles) {
302
- const fileName = path.basename(filePath);
303
- // Detectar archivos _bk y _bk_timestamp
304
- if (fileName.includes('_bk')) {
305
- const tempPath = path.join(require('os').tmpdir(), `backup_${Date.now()}_${fileName}`);
306
- const relativePath = path.relative(targetPath, filePath);
307
-
308
- // Crear copia temporal del backup
309
- await fs.copy(filePath, tempPath);
310
-
311
- backupFiles.push({
312
- originalPath: filePath,
313
- tempPath: tempPath,
314
- relativePath: relativePath
315
- });
316
- }
317
- }
318
-
319
- return backupFiles;
320
- }
321
-
322
- async performInstallation() {
323
- for (const mapping of this.folderMappings) {
324
- const sourcePath = path.join(this.packageDir, mapping.source);
325
- const targetPath = path.join(this.targetDir, mapping.target);
326
-
327
- if (fs.existsSync(sourcePath)) {
328
- await fs.copy(sourcePath, targetPath, {
329
- overwrite: true,
330
- recursive: true
331
- });
332
- } else {
333
- console.warn(`⚠️ Carpeta ${mapping.source} no encontrada en el paquete`);
334
- }
335
- }
336
- }
337
-
338
- async update() {
339
- // Verificar archivos modificados
340
- console.log('🔍 Verificando archivos modificados...');
341
- const modifiedFiles = await this.checkModifiedFiles();
342
-
343
- let hasBackups = false;
344
- if (modifiedFiles.length > 0) {
345
- const userChoice = await this.promptUser(modifiedFiles);
346
-
347
- if (userChoice === '2') {
348
- // Crear backup de archivos modificados
349
- await this.backupModifiedFiles(modifiedFiles);
350
- hasBackups = true;
351
- } else if (userChoice !== '1') {
352
- console.log('❌ Opción no válida. Cancelando actualización.');
353
- return;
354
- }
355
-
356
- console.log('\n🔄 Procediendo con la actualización...');
357
- } else {
358
- console.log('✓ No se detectaron archivos modificados.');
359
- }
360
-
361
- // Si hay backups, hacer actualización preservando backups
362
- if (hasBackups) {
363
- await this.performUpdateWithBackups();
364
- } else {
365
- // Si no hay backups, hacer actualización normal (remover y copiar)
366
- for (const mapping of this.folderMappings) {
367
- const targetPath = path.join(this.targetDir, mapping.target);
368
-
369
- if (fs.existsSync(targetPath)) {
370
- await fs.remove(targetPath);
371
- }
372
- }
373
-
374
- // Realizar instalación nueva
375
- await this.performInstallation();
376
- }
377
- }
378
-
379
- showPostInstallMessage() {
380
- console.log('\n📚 Carpetas instaladas:');
381
- this.folderMappings.forEach(mapping => {
382
- const targetPath = path.join(this.targetDir, mapping.target);
383
- if (fs.existsSync(targetPath)) {
384
- console.log(` ✓ ${mapping.target}`);
385
- }
386
- });
387
-
388
- console.log('\n🎉 ¡Instalación completada!');
389
- console.log('💡 Las carpetas han sido instaladas en tu directorio actual.');
390
- console.log('🔧 Puedes ejecutar "npx siesa-agents" nuevamente para actualizar.');
391
- }
392
- }
393
-
394
- // Ejecutar instalación si el script es llamado directamente
395
- if (require.main === module) {
396
- const installer = new SiesaBmadInstaller();
397
- installer.install();
398
- }
399
-
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs-extra');
4
+ const path = require('path');
5
+ const readline = require('readline');
6
+
7
+ class SiesaBmadInstaller {
8
+ constructor() {
9
+ // Definir las carpetas primero (nombres en el paquete vs nombres finales)
10
+ this.folderMappings = [
11
+ { source: 'bmad-core', target: '.bmad-core' },
12
+ { source: 'vscode', target: '.vscode' },
13
+ { source: 'github', target: '.github' },
14
+ { source: 'claude', target: '.claude' }
15
+ ];
16
+ this.targetDir = process.cwd();
17
+ // Intentar múltiples ubicaciones posibles para el paquete
18
+ this.packageDir = this.findPackageDir();
19
+ }
20
+
21
+ showBanner() {
22
+ console.log('\n');
23
+ console.log('███████╗██╗███████╗███████╗ █████╗ █████╗ ██████╗ ███████╗███╗ ██╗████████╗███████╗');
24
+ console.log('██╔════╝██║██╔════╝██╔════╝██╔══██╗ ██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝██╔════╝');
25
+ console.log('███████╗██║█████╗ ███████╗███████║ ███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║ ███████╗');
26
+ console.log('╚════██║██║██╔══╝ ╚════██║██╔══██║ ██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ╚════██║');
27
+ console.log('███████║██║███████╗███████║██║ ██║ ██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ███████║');
28
+ console.log('╚══════╝╚═╝╚══════╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝');
29
+ console.log('');
30
+ }
31
+
32
+ findPackageDir() {
33
+ // Opción 1: directorio padre del bin (instalación normal)
34
+ let packageDir = path.dirname(__dirname);
35
+
36
+ // Opción 2: si estamos en npx, buscar en node_modules
37
+ if (!this.hasRequiredFolders(packageDir)) {
38
+ // Para npm 6.14+ y npx, intentar ubicaciones alternativas
39
+ const possiblePaths = [
40
+ path.join(__dirname, '..'), // bin -> paquete
41
+ path.resolve(__dirname, '..'), // alternativa con resolve
42
+ path.resolve(__dirname, '..', '..', 'siesa-bmad-agents'), // desde node_modules
43
+ // Intentar buscar basado en __dirname específicamente para npx
44
+ path.resolve(__dirname.replace(/\\bin$|\/bin$/, '')),
45
+ process.cwd(), // directorio actual como último recurso
46
+ ];
47
+
48
+ for (const possiblePath of possiblePaths) {
49
+ if (this.hasRequiredFolders(possiblePath)) {
50
+ packageDir = possiblePath;
51
+ break;
52
+ }
53
+ }
54
+ }
55
+
56
+ return packageDir;
57
+ }
58
+
59
+ hasRequiredFolders(dir) {
60
+ return this.folderMappings.some(mapping => {
61
+ const folderPath = path.join(dir, mapping.source);
62
+ return fs.existsSync(folderPath);
63
+ });
64
+ }
65
+
66
+ async install() {
67
+ this.showBanner();
68
+ console.log(' Instalando SIESA Agents...');
69
+
70
+ try {
71
+ // Verificar si ya existe una instalación
72
+ const hasExistingInstallation = this.checkExistingInstallation();
73
+
74
+ if (hasExistingInstallation) {
75
+ console.log('🔄 Instalación existente detectada. Actualizando...');
76
+ await this.update();
77
+ } else {
78
+ console.log('✨ Nueva instalación...');
79
+ await this.performInstallation();
80
+ }
81
+
82
+ console.log('✅ SIESA Agents instalado correctamente!');
83
+ this.showPostInstallMessage();
84
+
85
+ } catch (error) {
86
+ console.error('❌ Error durante la instalación:', error.message);
87
+ process.exit(1);
88
+ }
89
+ }
90
+
91
+ checkExistingInstallation() {
92
+ return this.folderMappings.some(mapping => {
93
+ const targetPath = path.join(this.targetDir, mapping.target);
94
+ return fs.existsSync(targetPath);
95
+ });
96
+ }
97
+
98
+ async checkModifiedFiles() {
99
+ const modifiedFiles = [];
100
+
101
+ for (const mapping of this.folderMappings) {
102
+ const sourcePath = path.join(this.packageDir, mapping.source);
103
+ const targetPath = path.join(this.targetDir, mapping.target);
104
+
105
+ if (!fs.existsSync(sourcePath) || !fs.existsSync(targetPath)) {
106
+ continue;
107
+ }
108
+
109
+ // Obtener todos los archivos recursivamente
110
+ const sourceFiles = await this.getAllFiles(sourcePath);
111
+
112
+ for (const sourceFile of sourceFiles) {
113
+ const relativePath = path.relative(sourcePath, sourceFile);
114
+ const targetFile = path.join(targetPath, relativePath);
115
+
116
+ if (fs.existsSync(targetFile)) {
117
+ try {
118
+ // Comparar contenido de archivos
119
+ const sourceContent = await fs.readFile(sourceFile, 'utf8');
120
+ const targetContent = await fs.readFile(targetFile, 'utf8');
121
+
122
+ if (sourceContent !== targetContent) {
123
+ modifiedFiles.push({
124
+ folder: mapping.target,
125
+ file: relativePath,
126
+ fullPath: targetFile
127
+ });
128
+ }
129
+ } catch (error) {
130
+ // Si no se puede leer como texto, comparar como buffer (archivos binarios)
131
+ const sourceBuffer = await fs.readFile(sourceFile);
132
+ const targetBuffer = await fs.readFile(targetFile);
133
+
134
+ if (!sourceBuffer.equals(targetBuffer)) {
135
+ modifiedFiles.push({
136
+ folder: mapping.target,
137
+ file: relativePath,
138
+ fullPath: targetFile
139
+ });
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+
146
+ return modifiedFiles;
147
+ }
148
+
149
+ async getAllFiles(dir) {
150
+ const files = [];
151
+ const items = await fs.readdir(dir);
152
+
153
+ for (const item of items) {
154
+ const fullPath = path.join(dir, item);
155
+ const stat = await fs.stat(fullPath);
156
+
157
+ if (stat.isDirectory()) {
158
+ const subFiles = await this.getAllFiles(fullPath);
159
+ files.push(...subFiles);
160
+ } else {
161
+ files.push(fullPath);
162
+ }
163
+ }
164
+
165
+ return files;
166
+ }
167
+
168
+ async promptUser(modifiedFiles) {
169
+ console.log('\n⚠️ Se detectaron archivos modificados:');
170
+
171
+ // Agrupar por carpeta
172
+ const filesByFolder = {};
173
+ modifiedFiles.forEach(item => {
174
+ if (!filesByFolder[item.folder]) {
175
+ filesByFolder[item.folder] = [];
176
+ }
177
+ filesByFolder[item.folder].push(item.file);
178
+ });
179
+
180
+ // Mostrar archivos modificados por carpeta
181
+ Object.keys(filesByFolder).forEach(folder => {
182
+ console.log(`\n📁 ${folder}:`);
183
+ filesByFolder[folder].forEach(file => {
184
+ console.log(` - ${file}`);
185
+ });
186
+ });
187
+
188
+ console.log('\n¿Qué deseas hacer?');
189
+ console.log('1. Reemplazar todos los archivos (se perderán las modificaciones)');
190
+ console.log('2. Hacer backup de archivos modificados (se agregarán con sufijo _bk)');
191
+
192
+ const rl = readline.createInterface({
193
+ input: process.stdin,
194
+ output: process.stdout
195
+ });
196
+
197
+ return new Promise((resolve) => {
198
+ rl.question('\nElige una opción (1 o 2): ', (answer) => {
199
+ rl.close();
200
+ resolve(answer.trim());
201
+ });
202
+ });
203
+ }
204
+
205
+ async backupModifiedFiles(modifiedFiles) {
206
+ console.log('\n🔄 Creando backup de archivos modificados...');
207
+
208
+ for (const item of modifiedFiles) {
209
+ const originalPath = item.fullPath;
210
+ const backupPath = this.getBackupPath(originalPath);
211
+
212
+ try {
213
+ await fs.copy(originalPath, backupPath);
214
+
215
+ // Determinar tipo de backup para mostrar mensaje apropiado
216
+ const backupName = path.basename(backupPath);
217
+ const isVersionedBackup = backupName.includes('_bk_');
218
+
219
+ if (isVersionedBackup) {
220
+ console.log(`✓ Backup versionado: ${path.relative(this.targetDir, backupPath)}`);
221
+ } else {
222
+ console.log(`✓ Backup creado: ${path.relative(this.targetDir, backupPath)}`);
223
+ }
224
+ } catch (error) {
225
+ console.warn(`⚠️ Error creando backup de ${item.file}: ${error.message}`);
226
+ }
227
+ }
228
+ }
229
+
230
+ getBackupPath(filePath) {
231
+ const dir = path.dirname(filePath);
232
+ const ext = path.extname(filePath);
233
+ const name = path.basename(filePath, ext);
234
+
235
+ // Primer intento: archivo_bk.ext
236
+ const basicBackupPath = path.join(dir, `${name}_bk${ext}`);
237
+
238
+ // Si no existe, usar el nombre básico
239
+ if (!fs.existsSync(basicBackupPath)) {
240
+ return basicBackupPath;
241
+ }
242
+
243
+ // Si ya existe _bk, crear versión con timestamp
244
+ const now = new Date();
245
+ const timestamp = now.getFullYear().toString() +
246
+ (now.getMonth() + 1).toString().padStart(2, '0') +
247
+ now.getDate().toString().padStart(2, '0') + '_' +
248
+ now.getHours().toString().padStart(2, '0') +
249
+ now.getMinutes().toString().padStart(2, '0') +
250
+ now.getSeconds().toString().padStart(2, '0');
251
+
252
+ return path.join(dir, `${name}_bk_${timestamp}${ext}`);
253
+ }
254
+
255
+ async performUpdateWithBackups() {
256
+ for (const mapping of this.folderMappings) {
257
+ const sourcePath = path.join(this.packageDir, mapping.source);
258
+ const targetPath = path.join(this.targetDir, mapping.target);
259
+
260
+ if (fs.existsSync(sourcePath)) {
261
+ // Copiar archivos selectivamente, preservando los _bk
262
+ await this.copyWithBackupPreservation(sourcePath, targetPath);
263
+ } else {
264
+ console.warn(`⚠️ Carpeta ${mapping.source} no encontrada en el paquete`);
265
+ }
266
+ }
267
+ }
268
+
269
+ async copyWithBackupPreservation(sourcePath, targetPath) {
270
+ // Obtener todos los archivos backup existentes
271
+ const backupFiles = await this.findBackupFiles(targetPath);
272
+
273
+ // Copiar la carpeta completa sobrescribiendo
274
+ await fs.copy(sourcePath, targetPath, {
275
+ overwrite: true,
276
+ recursive: true
277
+ });
278
+
279
+ // Restaurar los archivos backup
280
+ for (const backupFile of backupFiles) {
281
+ const backupSourcePath = backupFile.tempPath;
282
+ const backupTargetPath = backupFile.originalPath;
283
+
284
+ try {
285
+ await fs.copy(backupSourcePath, backupTargetPath);
286
+ // Limpiar archivo temporal
287
+ await fs.remove(backupSourcePath);
288
+ } catch (error) {
289
+ console.warn(`⚠️ Error restaurando backup ${backupFile.relativePath}: ${error.message}`);
290
+ }
291
+ }
292
+ }
293
+
294
+ async findBackupFiles(targetPath) {
295
+ if (!fs.existsSync(targetPath)) {
296
+ return [];
297
+ }
298
+
299
+ const backupFiles = [];
300
+ const allFiles = await this.getAllFiles(targetPath);
301
+
302
+ for (const filePath of allFiles) {
303
+ const fileName = path.basename(filePath);
304
+ // Detectar archivos _bk y _bk_timestamp
305
+ if (fileName.includes('_bk')) {
306
+ const tempPath = path.join(require('os').tmpdir(), `backup_${Date.now()}_${fileName}`);
307
+ const relativePath = path.relative(targetPath, filePath);
308
+
309
+ // Crear copia temporal del backup
310
+ await fs.copy(filePath, tempPath);
311
+
312
+ backupFiles.push({
313
+ originalPath: filePath,
314
+ tempPath: tempPath,
315
+ relativePath: relativePath
316
+ });
317
+ }
318
+ }
319
+
320
+ return backupFiles;
321
+ }
322
+
323
+ async performInstallation() {
324
+ for (const mapping of this.folderMappings) {
325
+ const sourcePath = path.join(this.packageDir, mapping.source);
326
+ const targetPath = path.join(this.targetDir, mapping.target);
327
+
328
+ if (fs.existsSync(sourcePath)) {
329
+ await fs.copy(sourcePath, targetPath, {
330
+ overwrite: true,
331
+ recursive: true
332
+ });
333
+ } else {
334
+ console.warn(`⚠️ Carpeta ${mapping.source} no encontrada en el paquete`);
335
+ }
336
+ }
337
+ }
338
+
339
+ async update() {
340
+ // Verificar archivos modificados
341
+ console.log('🔍 Verificando archivos modificados...');
342
+ const modifiedFiles = await this.checkModifiedFiles();
343
+
344
+ let hasBackups = false;
345
+ if (modifiedFiles.length > 0) {
346
+ const userChoice = await this.promptUser(modifiedFiles);
347
+
348
+ if (userChoice === '2') {
349
+ // Crear backup de archivos modificados
350
+ await this.backupModifiedFiles(modifiedFiles);
351
+ hasBackups = true;
352
+ } else if (userChoice !== '1') {
353
+ console.log('❌ Opción no válida. Cancelando actualización.');
354
+ return;
355
+ }
356
+
357
+ console.log('\n🔄 Procediendo con la actualización...');
358
+ } else {
359
+ console.log('✓ No se detectaron archivos modificados.');
360
+ }
361
+
362
+ // Si hay backups, hacer actualización preservando backups
363
+ if (hasBackups) {
364
+ await this.performUpdateWithBackups();
365
+ } else {
366
+ // Si no hay backups, hacer actualización normal (remover y copiar)
367
+ for (const mapping of this.folderMappings) {
368
+ const targetPath = path.join(this.targetDir, mapping.target);
369
+
370
+ if (fs.existsSync(targetPath)) {
371
+ await fs.remove(targetPath);
372
+ }
373
+ }
374
+
375
+ // Realizar instalación nueva
376
+ await this.performInstallation();
377
+ }
378
+ }
379
+
380
+ showPostInstallMessage() {
381
+ console.log('\n📚 Carpetas instaladas:');
382
+ this.folderMappings.forEach(mapping => {
383
+ const targetPath = path.join(this.targetDir, mapping.target);
384
+ if (fs.existsSync(targetPath)) {
385
+ console.log(` ✓ ${mapping.target}`);
386
+ }
387
+ });
388
+
389
+ console.log('\n🎉 ¡Instalación completada!');
390
+ console.log('💡 Las carpetas han sido instaladas en tu directorio actual.');
391
+ console.log('🔧 Puedes ejecutar "npx siesa-agents" nuevamente para actualizar.');
392
+ }
393
+ }
394
+
395
+ // Ejecutar instalación si el script es llamado directamente
396
+ if (require.main === module) {
397
+ const installer = new SiesaBmadInstaller();
398
+ installer.install();
399
+ }
400
+
400
401
  module.exports = SiesaBmadInstaller;