siesa-agents 2.1.24-dev.0 → 2.1.25

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 CHANGED
@@ -1,4 +1,4 @@
1
- # SIESA BMAD Agents
1
+ # SIESA Agents
2
2
 
3
3
  Paquete para instalar y configurar agentes SIESA en tu proyecto.
4
4
 
@@ -52,6 +52,7 @@ El paquete instala las siguientes carpetas en tu directorio actual:
52
52
  - **`.bmad-core/`** - Archivos principales del sistema BMAD
53
53
  - **`.vscode/`** - Configuración de Visual Studio Code
54
54
  - **`.github/`** - Configuración de GitHub Actions y workflows
55
+ - **`.claude/`** - Configuración de Claude Code Commands y workflows
55
56
 
56
57
  ## Características
57
58
 
@@ -70,22 +71,17 @@ El paquete instala las siguientes carpetas en tu directorio actual:
70
71
 
71
72
  Para actualizar una instalación existente, simplemente ejecuta el comando nuevamente:
72
73
 
73
- **Versión estable (recomendado):**
74
74
  ```bash
75
75
  npx siesa-agents
76
76
  ```
77
77
 
78
- **Versión de desarrollo:**
79
- ```bash
80
- npx siesa-agents@dev
81
- ```
82
-
83
78
  El sistema detectará automáticamente que ya existe una instalación y la actualizará.
84
79
 
85
80
  ## Requisitos
86
81
 
87
82
  - Node.js >= 14.0.0
88
83
  - npm >= 6.0.0
84
+ - Python >= 3.7 (requerido para hooks de Claude Code)
89
85
 
90
86
  ## Estructura de archivos instalados
91
87
 
@@ -99,8 +95,6 @@ tu-proyecto/
99
95
  └── [workflows y configuración de GitHub]
100
96
  ```
101
97
 
102
-
103
-
104
98
  ## Soporte
105
99
 
106
100
  Si encuentras algún problema durante la instalación, por favor verifica:
@@ -115,4 +109,7 @@ MIT
115
109
 
116
110
  ## Autor
117
111
 
118
- SIESA - Sistemas de Información Empresarial
112
+ SIESA - Sistemas de Información Empresarial
113
+
114
+ ---
115
+ *Versión actualizada automáticamente por CI/CD*
package/bin/install.js CHANGED
@@ -15,9 +15,18 @@ class SiesaBmadInstaller {
15
15
  { source: 'kiro', target: '.kiro' },
16
16
  { source: 'resources', target: '.resources' }
17
17
  ];
18
+
19
+ // Lista de archivos que se preservan automáticamente (no se crean backups)
20
+ this.ignoredFiles = [
21
+ 'data/technical-preferences.md'
22
+ ];
23
+
18
24
  this.targetDir = process.cwd();
19
25
  // Intentar múltiples ubicaciones posibles para el paquete
20
26
  this.packageDir = this.findPackageDir();
27
+
28
+ // Almacenamiento temporal para contenido de archivos ignorados
29
+ this.preservedContent = new Map();
21
30
  }
22
31
 
23
32
  showBanner() {
@@ -28,8 +37,7 @@ class SiesaBmadInstaller {
28
37
  console.log('╚════██║██║██╔══╝ ╚════██║██╔══██║ ██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ╚════██║');
29
38
  console.log('███████║██║███████╗███████║██║ ██║ ██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ███████║');
30
39
  console.log('╚══════╝╚═╝╚══════╝╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝');
31
- console.log('Version de Desarrollador');
32
- console.log('\n');
40
+ console.log('');
33
41
  }
34
42
 
35
43
  findPackageDir() {
@@ -126,7 +134,8 @@ class SiesaBmadInstaller {
126
134
  modifiedFiles.push({
127
135
  folder: mapping.target,
128
136
  file: relativePath,
129
- fullPath: targetFile
137
+ fullPath: targetFile,
138
+ is_ignored: this.ignoredFiles.includes(relativePath)
130
139
  });
131
140
  }
132
141
  } catch (error) {
@@ -138,7 +147,8 @@ class SiesaBmadInstaller {
138
147
  modifiedFiles.push({
139
148
  folder: mapping.target,
140
149
  file: relativePath,
141
- fullPath: targetFile
150
+ fullPath: targetFile,
151
+ is_ignored: this.ignoredFiles.includes(relativePath)
142
152
  });
143
153
  }
144
154
  }
@@ -169,6 +179,10 @@ class SiesaBmadInstaller {
169
179
  }
170
180
 
171
181
  async promptUser(modifiedFiles) {
182
+
183
+ const hasNonIgnoredFiles = modifiedFiles.some(file => file.is_ignored == false)
184
+ if (!hasNonIgnoredFiles) return '2'
185
+
172
186
  console.log('\n⚠️ Se detectaron archivos modificados:');
173
187
 
174
188
  // Agrupar por carpeta
@@ -209,6 +223,12 @@ class SiesaBmadInstaller {
209
223
  console.log('\n🔄 Creando backup de archivos modificados...');
210
224
 
211
225
  for (const item of modifiedFiles) {
226
+ // No crear backup de archivos ignorados
227
+ if (item.is_ignored) {
228
+ console.log(`✓ Preservando: ${item.file} (sin backup)`);
229
+ continue;
230
+ }
231
+
212
232
  const originalPath = item.fullPath;
213
233
  const backupPath = this.getBackupPath(originalPath);
214
234
 
@@ -272,11 +292,20 @@ class SiesaBmadInstaller {
272
292
  async copyWithBackupPreservation(sourcePath, targetPath) {
273
293
  // Obtener todos los archivos backup existentes
274
294
  const backupFiles = await this.findBackupFiles(targetPath);
275
-
276
- // Copiar la carpeta completa sobrescribiendo
295
+
296
+ // Copiar la carpeta preservando technical-preferences.md
277
297
  await fs.copy(sourcePath, targetPath, {
278
298
  overwrite: true,
279
- recursive: true
299
+ recursive: true,
300
+ filter: (src) => {
301
+ const relativePath = path.relative(sourcePath, src);
302
+ // No sobrescribir archivos ignorados si ya existen
303
+ if (this.ignoredFiles.includes(relativePath)) {
304
+ const targetFile = path.join(targetPath, relativePath);
305
+ return !fs.existsSync(targetFile);
306
+ }
307
+ return true;
308
+ }
280
309
  });
281
310
 
282
311
  // Restaurar los archivos backup
@@ -331,7 +360,16 @@ class SiesaBmadInstaller {
331
360
  if (fs.existsSync(sourcePath)) {
332
361
  await fs.copy(sourcePath, targetPath, {
333
362
  overwrite: true,
334
- recursive: true
363
+ recursive: true,
364
+ filter: (src) => {
365
+ const relativePath = path.relative(sourcePath, src);
366
+ // No sobrescribir archivos ignorados si ya existen
367
+ if (this.ignoredFiles.includes(relativePath)) {
368
+ const targetFile = path.join(targetPath, relativePath);
369
+ return !fs.existsSync(targetFile);
370
+ }
371
+ return true;
372
+ }
335
373
  });
336
374
  } else {
337
375
  console.warn(`⚠️ Carpeta ${mapping.source} no encontrada en el paquete`);
@@ -367,6 +405,9 @@ class SiesaBmadInstaller {
367
405
  await this.performUpdateWithBackups();
368
406
  } else {
369
407
  // Si no hay backups, hacer actualización normal (remover y copiar)
408
+ // Pero primero preservar archivos ignorados
409
+ await this.preserveIgnoredFiles();
410
+
370
411
  for (const mapping of this.folderMappings) {
371
412
  const targetPath = path.join(this.targetDir, mapping.target);
372
413
 
@@ -377,7 +418,64 @@ class SiesaBmadInstaller {
377
418
 
378
419
  // Realizar instalación nueva
379
420
  await this.performInstallation();
421
+
422
+ // Restaurar archivos ignorados
423
+ await this.restoreIgnoredFiles();
424
+ }
425
+ }
426
+
427
+ async preserveIgnoredFiles() {
428
+ console.log('🔒 Preservando archivos de configuración...');
429
+
430
+ for (const mapping of this.folderMappings) {
431
+ const targetFolderPath = path.join(this.targetDir, mapping.target);
432
+
433
+ if (!fs.existsSync(targetFolderPath)) {
434
+ continue;
435
+ }
436
+
437
+ for (const ignoredFile of this.ignoredFiles) {
438
+ const filePath = path.join(targetFolderPath, ignoredFile);
439
+
440
+ if (fs.existsSync(filePath)) {
441
+ try {
442
+ const content = await fs.readFile(filePath, 'utf8');
443
+ const key = `${mapping.target}/${ignoredFile}`;
444
+ this.preservedContent.set(key, content);
445
+ console.log(`✓ Preservando: ${ignoredFile}`);
446
+ } catch (error) {
447
+ console.warn(`⚠️ Error leyendo ${ignoredFile}: ${error.message}`);
448
+ }
449
+ }
450
+ }
451
+ }
452
+ }
453
+
454
+ async restoreIgnoredFiles() {
455
+ if (this.preservedContent.size === 0) {
456
+ return;
457
+ }
458
+
459
+ console.log('🔄 Restaurando archivos de configuración...');
460
+
461
+ for (const [key, content] of this.preservedContent) {
462
+ const [targetFolder, ...filePathParts] = key.split('/');
463
+ const filePath = path.join(this.targetDir, targetFolder, ...filePathParts);
464
+
465
+ try {
466
+ // Asegurar que el directorio existe
467
+ await fs.ensureDir(path.dirname(filePath));
468
+
469
+ // Restaurar el contenido
470
+ await fs.writeFile(filePath, content, 'utf8');
471
+ console.log(`✓ Restaurado: ${filePathParts.join('/')}`);
472
+ } catch (error) {
473
+ console.warn(`⚠️ Error restaurando ${filePathParts.join('/')}: ${error.message}`);
474
+ }
380
475
  }
476
+
477
+ // Limpiar el mapa después de restaurar
478
+ this.preservedContent.clear();
381
479
  }
382
480
 
383
481
  showPostInstallMessage() {
@@ -81,6 +81,8 @@ dependencies:
81
81
  - architect-checklist.md
82
82
  data:
83
83
  - technical-preferences.md
84
+ - technology-stack.md
85
+ - architecture-patterns.md
84
86
  tasks:
85
87
  - create-deep-research-prompt.md
86
88
  - create-doc.md
@@ -60,6 +60,7 @@ commands:
60
60
  dependencies:
61
61
  data:
62
62
  - technical-preferences.md
63
+ - technology-stack.md
63
64
  - technical-preferences-ux.md
64
65
  tasks:
65
66
  - create-doc.md
@@ -0,0 +1,261 @@
1
+ # Architecture Patterns & Design Decisions
2
+
3
+ ## Frontend Architecture
4
+
5
+ ### Architecture Style
6
+ - **Clean Architecture** + **Domain-Driven Design (DDD)**
7
+
8
+ ### Folder Structure
9
+
10
+ Next.js 15 App Router Structure with Clean Architecture + DDD:
11
+
12
+ ```
13
+ ├── app/ # Next.js App Router directory
14
+ │ ├── (dashboard)/ # Route groups for dashboard
15
+ │ ├── sales/ # Routes for sales module
16
+ │ │ ├── quotes/ # Quote management pages
17
+ │ │ └── invoices/ # Invoice pages
18
+ │ ├── inventory/ # Inventory routes
19
+ │ ├── globals.css # Global styles
20
+ │ ├── layout.tsx # Root layout component
21
+ │ ├── page.tsx # Home page
22
+ │ ├── loading.tsx # Global loading UI
23
+ │ └── not-found.tsx # 404 page
24
+
25
+ ├── src/
26
+ │ ├── modules/ # Business modules following DDD
27
+ │ │ ├── sales/ # Sales module
28
+ │ │ │ ├── quotes/ # Quote domain
29
+ │ │ │ │ ├── cart/ # Shopping cart feature
30
+ │ │ │ │ │ ├── domain/
31
+ │ │ │ │ │ │ ├── entities/
32
+ │ │ │ │ │ │ ├── repositories/
33
+ │ │ │ │ │ │ ├── services/
34
+ │ │ │ │ │ │ └── types/
35
+ │ │ │ │ │ ├── application/
36
+ │ │ │ │ │ │ ├── use-cases/
37
+ │ │ │ │ │ │ ├── hooks/
38
+ │ │ │ │ │ │ └── store/
39
+ │ │ │ │ │ ├── infrastructure/
40
+ │ │ │ │ │ │ ├── repositories/
41
+ │ │ │ │ │ │ ├── api/
42
+ │ │ │ │ │ │ └── adapters/
43
+ │ │ │ │ │ └── presentation/
44
+ │ │ │ │ │ └── components/ # Only components, pages in app/
45
+ │ │ │ │ └── products/ # Products feature
46
+ │ │ │ └── billing/ # Billing domain
47
+ │ │ ├── inventory/ # Inventory module
48
+ │ │ └── users/ # User module
49
+ │ │
50
+ │ ├── shared/
51
+ │ │ ├── components/ # Reusable UI components
52
+ │ │ ├── hooks/ # Shared hooks
53
+ │ │ ├── utils/ # Utility functions
54
+ │ │ ├── types/ # Common TypeScript types
55
+ │ │ └── constants/ # App constants
56
+ │ │
57
+ │ ├── providers/ # React context providers
58
+ │ ├── store/ # Global Zustand stores
59
+ │ └── middleware.ts # Next.js middleware
60
+
61
+ ├── lib/ # Next.js utilities and configurations
62
+ ├── components/ # Global UI components (alternative to src/shared)
63
+ ├── public/ # Static assets and PWA manifest
64
+ └── styles/ # Additional stylesheets
65
+ ```
66
+
67
+ ### Core Principles
68
+
69
+ #### Clean Architecture First
70
+ Strict separation of:
71
+ - **Domain layer** - Business entities, repositories interfaces, domain services, and types
72
+ - **Application layer** - Use cases, hooks, and state management (Zustand stores)
73
+ - **Infrastructure layer** - Repository implementations, API clients, and adapters
74
+ - **Presentation layer** - UI components (pages go in `app/`, components in `presentation/`)
75
+
76
+ #### Domain-Driven Design
77
+ Business logic drives architecture decisions. Organize by business modules and domains, not technical layers.
78
+
79
+ #### Component Composition
80
+ Build complex UIs from simple, reusable components.
81
+
82
+ #### Type Safety
83
+ Leverage TypeScript for compile-time safety and developer experience.
84
+
85
+ #### Performance by Design
86
+ - Lazy loading
87
+ - Memoization
88
+ - Bundle optimization
89
+
90
+ #### Accessibility as Standard
91
+ WCAG 2.1 AA compliance in all components.
92
+
93
+ #### Test-Driven Development
94
+ Unit tests for all use cases and components.
95
+
96
+ #### Progressive Web App
97
+ Offline-first approach with service workers.
98
+
99
+ #### Minimal and Functional
100
+ Only build what's explicitly requested, nothing more.
101
+
102
+ #### User-Centered Design
103
+ Start with user needs and work backward to implementation.
104
+
105
+ #### MCP Shadcn Available
106
+ Use MCP to install Shadcn components instead of creating manually.
107
+
108
+ ### Framework Selection Rules
109
+
110
+ **Default**: Always use Next.js 15 with App Router unless explicitly told otherwise.
111
+
112
+ **Exceptions**: Only use pure React + Vite when user specifically mentions offline-first functionality or requests non-Next.js setup.
113
+
114
+ **Reasoning**: Next.js provides better developer experience, built-in optimization, and easier deployment while maintaining PWA capabilities.
115
+
116
+ ## Backend Architecture
117
+
118
+ ### Architecture Style
119
+ - **Hexagonal Architecture** (Ports & Adapters) + **Domain-Driven Design (DDD)**
120
+
121
+ ### Folder Structure
122
+
123
+ MonoRepo Structure with Hexagonal Architecture + DDD:
124
+
125
+ ```
126
+ ├── apps/ # Microservices applications
127
+ │ ├── sales-service/ # Sales domain microservice
128
+ │ │ ├── src/
129
+ │ │ │ ├── modules/
130
+ │ │ │ │ ├── quotes/ # Quote bounded context
131
+ │ │ │ │ │ ├── application/
132
+ │ │ │ │ │ │ ├── ports/ # Interfaces (secondary ports)
133
+ │ │ │ │ │ │ │ ├── repositories/
134
+ │ │ │ │ │ │ │ └── services/
135
+ │ │ │ │ │ │ ├── use-cases/ # Primary ports
136
+ │ │ │ │ │ │ ├── commands/
137
+ │ │ │ │ │ │ ├── queries/
138
+ │ │ │ │ │ │ └── dto/
139
+ │ │ │ │ │ ├── domain/
140
+ │ │ │ │ │ │ ├── entities/
141
+ │ │ │ │ │ │ ├── value-objects/
142
+ │ │ │ │ │ │ ├── aggregates/
143
+ │ │ │ │ │ │ ├── events/
144
+ │ │ │ │ │ │ └── services/ # Domain services
145
+ │ │ │ │ │ └── infrastructure/ # Adapters (secondary adapters)
146
+ │ │ │ │ │ ├── repositories/ # Prisma implementations
147
+ │ │ │ │ │ ├── services/ # External service adapters
148
+ │ │ │ │ │ └── events/
149
+ │ │ │ │ └── products/ # Product bounded context
150
+ │ │ │ ├── api/ # Primary adapters
151
+ │ │ │ │ ├── controllers/
152
+ │ │ │ │ ├── guards/
153
+ │ │ │ │ ├── middlewares/
154
+ │ │ │ │ └── filters/
155
+ │ │ │ ├── config/
156
+ │ │ │ ├── main.ts
157
+ │ │ │ └── app.module.ts
158
+ │ │ ├── test/
159
+ │ │ ├── prisma/
160
+ │ │ │ ├── schema.prisma
161
+ │ │ │ └── migrations/
162
+ │ │ └── package.json
163
+ │ │
164
+ │ ├── inventory-service/ # Inventory domain microservice
165
+ │ └── user-service/ # User domain microservice
166
+
167
+ ├── libs/ # Shared libraries
168
+ │ ├── common/ # Common utilities
169
+ │ │ ├── src/
170
+ │ │ │ ├── decorators/
171
+ │ │ │ ├── filters/
172
+ │ │ │ ├── guards/
173
+ │ │ │ ├── interceptors/
174
+ │ │ │ ├── pipes/
175
+ │ │ │ ├── types/
176
+ │ │ │ └── utils/
177
+ │ │ └── package.json
178
+ │ │
179
+ │ ├── domain-core/ # Shared domain concepts
180
+ │ │ ├── src/
181
+ │ │ │ ├── base/
182
+ │ │ │ │ ├── aggregate-root.ts
183
+ │ │ │ │ ├── entity.ts
184
+ │ │ │ │ ├── value-object.ts
185
+ │ │ │ │ └── domain-event.ts
186
+ │ │ │ ├── interfaces/
187
+ │ │ │ └── exceptions/
188
+ │ │ └── package.json
189
+ │ │
190
+ │ └── database/ # Shared database utilities
191
+ │ ├── src/
192
+ │ │ ├── base-repository.ts
193
+ │ │ ├── transaction.decorator.ts
194
+ │ │ └── prisma.service.ts
195
+ │ └── package.json
196
+
197
+ ├── tools/ # Development tools
198
+ ├── nx.json # Nx workspace configuration
199
+ ├── package.json # Root package.json
200
+ └── tsconfig.base.json # Base TypeScript config
201
+ ```
202
+
203
+ ### Core Principles
204
+
205
+ #### Hexagonal Architecture First
206
+ Strict separation of concerns following ports & adapters pattern:
207
+ - **Domain Layer**: Pure business logic with entities, value objects, aggregates, and domain services
208
+ - **Application Layer**: Use cases orchestrating domain logic, defining ports (interfaces)
209
+ - **Infrastructure Layer**: Adapters implementing ports (Prisma repos, HTTP clients, message publishers)
210
+ - **API Layer**: Primary adapters exposing application via REST/GraphQL (controllers, resolvers)
211
+
212
+ #### Domain-Driven Design
213
+ Business logic drives all architectural decisions:
214
+ - **Bounded Contexts**: Each module represents a bounded context (quotes, products, billing)
215
+ - **Ubiquitous Language**: Code reflects business terminology
216
+ - **Aggregates**: Consistency boundaries for domain entities
217
+ - **Domain Events**: Communicate changes across bounded contexts
218
+ - **Repository Pattern**: Abstract data access behind interfaces
219
+
220
+ #### Dependency Rules
221
+ - Domain layer has zero dependencies on frameworks or external libraries
222
+ - All dependencies point inward toward the domain core
223
+ - Use dependency inversion for all external concerns (databases, APIs, messaging)
224
+ - Interfaces defined in application layer, implementations in infrastructure layer
225
+
226
+ #### Microservices Independence
227
+ - Each microservice has its own database (no shared databases)
228
+ - Shared code through libraries only (common, domain-core, database)
229
+ - Independent deployment pipelines per service
230
+ - Service-to-service communication via events (async messaging)
231
+ - No direct database access between services
232
+
233
+ #### Test-Driven Development
234
+ - Unit tests for domain entities, value objects, and use cases
235
+ - Integration tests for repository implementations
236
+ - E2E tests for complete API workflows
237
+ - TDD approach: write tests before implementation
238
+
239
+ #### Type Safety & Validation
240
+ - Leverage TypeScript strict mode for compile-time safety
241
+ - Domain validation in value objects and entities
242
+ - DTO validation at API boundaries with class-validator
243
+ - No `any` types allowed
244
+
245
+ #### Security by Design
246
+ - Authentication and authorization at every layer
247
+ - Input validation on all endpoints
248
+ - OWASP Top 10 compliance
249
+ - Audit logging for critical operations
250
+
251
+ ### Framework Selection Rules
252
+
253
+ **Default**: Always use NestJS 10+ with TypeScript for backend services.
254
+
255
+ **Database**: Prisma ORM only - no raw SQL queries allowed.
256
+
257
+ **Testing**: Jest + Supertest with TDD approach.
258
+
259
+ **Documentation**: Swagger/OpenAPI auto-generated from decorators.
260
+
261
+ **Reasoning**: NestJS provides excellent DI container, decorator-based development, and native support for microservices patterns while enforcing SOLID principles.
@@ -1,43 +1,32 @@
1
1
  # Backend Development Standards
2
2
 
3
- ## Architecture Principles
4
-
5
- ### Hexagonal Architecture Implementation
6
- - **Application Core**: Domain entities, value objects, aggregates, and domain services
7
- - **Primary Ports**: Use cases, commands, queries, and application services
8
- - **Primary Adapters**: REST controllers, GraphQL resolvers, message handlers
9
- - **Secondary Ports**: Repository interfaces, external service interfaces
10
- - **Secondary Adapters**: Prisma repositories, HTTP clients, message publishers
11
-
12
- ### Dependency Rules
13
- - Application core must not depend on external frameworks
14
- - All dependencies point inward toward the domain
15
- - Use dependency inversion for all external concerns
16
- - Interfaces defined in application layer, implementations in infrastructure
3
+ > **Note**: For architecture patterns and principles (Hexagonal Architecture, DDD, folder structure), see [architecture-patterns.md](./architecture-patterns.md)
17
4
 
18
5
  ## Technology Stack Standards
19
6
 
20
7
  ### Core Technologies
21
8
  - **NestJS**: 10+ with TypeScript and decorators
22
9
  - **TypeScript**: Strict mode enabled, no `any` types
23
- - **Prisma**: ORM for database operations (no raw queries)
24
- - **Jest**: Unit and integration testing
25
- - **Class-validator**: Request validation and transformation
10
+ - **Prisma**: ORM for database operations (no raw queries allowed)
11
+ - **Jest + Supertest**: Unit, integration, and E2E testing
12
+ - **Class-validator + Class-transformer**: DTO validation
26
13
 
27
- ### Framework Selection Rules
28
- - **Default**: Always use NestJS 10+ with TypeScript
29
- - **Database**: Prisma ORM only - no raw SQL queries allowed
30
- - **Testing**: TDD approach with Jest and Supertest
31
- - **Documentation**: Swagger/OpenAPI for all endpoints
14
+ ### Framework Standards
15
+ - **Default Framework**: NestJS 10+ with TypeScript
16
+ - **Database**: Prisma ORM only - no raw SQL queries
17
+ - **Testing**: TDD approach with comprehensive test coverage
18
+ - **Documentation**: Swagger/OpenAPI auto-generated from decorators
19
+ - **Messaging**: NestJS Microservices (Redis, RabbitMQ, or gRPC)
32
20
 
33
21
  ### Development Tools
34
- - **Nx**: MonoRepo management and build system
22
+ - **Nx**: MonoRepo management and build orchestration
35
23
  - **ESLint + Prettier**: Code quality and formatting
36
- - **Husky**: Git hooks for pre-commit validation
37
- - **Winston**: Structured logging
38
- - **Redis**: Caching and message transport
24
+ - **Husky**: Pre-commit hooks for quality gates
25
+ - **Winston**: Structured logging with log levels
26
+ - **Redis**: Caching, session storage, and message transport
27
+ - **Passport + JWT**: Authentication and authorization
39
28
 
40
- ## Domain-Driven Design Standards
29
+ ## Domain-Driven Design Implementation
41
30
 
42
31
  ### Entity Structure
43
32
  ```typescript
@@ -46,7 +35,6 @@ export class UserEntity extends AggregateRoot {
46
35
  public readonly id: UserId,
47
36
  private _email: EmailValueObject,
48
37
  private _name: NameValueObject,
49
- private _createdAt: Date,
50
38
  ) {
51
39
  super();
52
40
  }
@@ -56,7 +44,6 @@ export class UserEntity extends AggregateRoot {
56
44
  UserId.generate(),
57
45
  EmailValueObject.create(props.email),
58
46
  NameValueObject.create(props.name),
59
- new Date(),
60
47
  );
61
48
  user.addDomainEvent(new UserCreatedEvent(user.id));
62
49
  return user;
@@ -91,11 +78,6 @@ export class EmailValueObject {
91
78
  }
92
79
  }
93
80
 
94
- private isValidEmail(email: string): boolean {
95
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
96
- return emailRegex.test(email);
97
- }
98
-
99
81
  equals(other: EmailValueObject): boolean {
100
82
  return this.value === other.value;
101
83
  }
@@ -106,15 +88,45 @@ export class EmailValueObject {
106
88
  }
107
89
  ```
108
90
 
109
- ### Repository Interface Pattern
91
+ ### Repository Pattern
110
92
  ```typescript
93
+ // Interface (in application/ports/repositories)
111
94
  export interface UserRepositoryInterface {
112
95
  save(user: UserEntity): Promise<UserEntity>;
113
96
  findById(id: UserId): Promise<UserEntity | null>;
114
97
  findByEmail(email: EmailValueObject): Promise<UserEntity | null>;
115
- findAll(criteria: FindUsersCriteria): Promise<UserEntity[]>;
116
98
  delete(id: UserId): Promise<void>;
117
99
  }
100
+
101
+ // Implementation (in infrastructure/repositories)
102
+ @Injectable()
103
+ export class PrismaUserRepository implements UserRepositoryInterface {
104
+ constructor(private readonly prisma: PrismaService) {}
105
+
106
+ async save(user: UserEntity): Promise<UserEntity> {
107
+ const data = {
108
+ id: user.id.toString(),
109
+ email: user.email.toString(),
110
+ name: user.name.toString(),
111
+ };
112
+
113
+ const savedUser = await this.prisma.user.upsert({
114
+ where: { id: data.id },
115
+ update: data,
116
+ create: data,
117
+ });
118
+
119
+ return this.toDomain(savedUser);
120
+ }
121
+
122
+ private toDomain(prismaUser: User): UserEntity {
123
+ return UserEntity.reconstitute({
124
+ id: UserId.create(prismaUser.id),
125
+ email: EmailValueObject.create(prismaUser.email),
126
+ name: NameValueObject.create(prismaUser.name),
127
+ });
128
+ }
129
+ }
118
130
  ```
119
131
 
120
132
  ## Use Case Standards
@@ -133,20 +145,19 @@ export class CreateUserUseCase {
133
145
  async execute(command: CreateUserCommand): Promise<UserResponseDto> {
134
146
  // 1. Validate business rules
135
147
  await this.validateUserDoesNotExist(command.email);
136
-
148
+
137
149
  // 2. Create domain entity
138
150
  const user = UserEntity.create({
139
151
  email: command.email,
140
152
  name: command.name,
141
153
  });
142
-
154
+
143
155
  // 3. Persist entity
144
156
  const savedUser = await this.userRepository.save(user);
145
-
157
+
146
158
  // 4. Publish domain events
147
159
  await this.eventBus.publishAll(savedUser.getUncommittedEvents());
148
- savedUser.markEventsAsCommitted();
149
-
160
+
150
161
  // 5. Return response DTO
151
162
  return UserResponseDto.fromEntity(savedUser);
152
163
  }
@@ -172,20 +183,16 @@ export class CreateUserCommand {
172
183
  @IsNotEmpty()
173
184
  @Length(2, 50)
174
185
  readonly name: string;
175
-
176
- @IsOptional()
177
- @IsString()
178
- readonly organizationId?: string;
179
186
  }
180
187
  ```
181
188
 
182
189
  ## Testing Standards
183
190
 
184
191
  ### Testing Strategy
185
- - **Unit Tests**: Domain entities, value objects, use cases
186
- - **Integration Tests**: Repository implementations, external services
187
- - **E2E Tests**: Complete API workflows
188
- - **Contract Tests**: External service integrations
192
+ - **Unit Tests**: Domain entities, value objects, use cases (isolated logic)
193
+ - **Integration Tests**: Repository implementations, database operations
194
+ - **E2E Tests**: Complete API workflows with Supertest
195
+ - **TDD Approach**: Write tests before implementation
189
196
 
190
197
  ### Test Structure
191
198
  ```typescript
@@ -198,19 +205,8 @@ describe('CreateUserUseCase', () => {
198
205
  const module = await Test.createTestingModule({
199
206
  providers: [
200
207
  CreateUserUseCase,
201
- {
202
- provide: USER_REPOSITORY,
203
- useValue: {
204
- save: jest.fn(),
205
- findByEmail: jest.fn(),
206
- },
207
- },
208
- {
209
- provide: EVENT_BUS,
210
- useValue: {
211
- publishAll: jest.fn(),
212
- },
213
- },
208
+ { provide: USER_REPOSITORY, useValue: { save: jest.fn(), findByEmail: jest.fn() } },
209
+ { provide: EVENT_BUS, useValue: { publishAll: jest.fn() } },
214
210
  ],
215
211
  }).compile();
216
212
 
@@ -219,46 +215,33 @@ describe('CreateUserUseCase', () => {
219
215
  eventBus = module.get(EVENT_BUS);
220
216
  });
221
217
 
222
- describe('execute', () => {
223
- it('should create user successfully', async () => {
224
- // Arrange
225
- const command = new CreateUserCommand();
226
- command.email = 'test@example.com';
227
- command.name = 'Test User';
228
-
229
- const expectedUser = UserEntity.create({
230
- email: command.email,
231
- name: command.name,
232
- });
233
-
234
- userRepository.findByEmail.mockResolvedValue(null);
235
- userRepository.save.mockResolvedValue(expectedUser);
218
+ it('should create user successfully', async () => {
219
+ // Arrange
220
+ const command = new CreateUserCommand();
221
+ command.email = 'test@example.com';
222
+ command.name = 'Test User';
236
223
 
237
- // Act
238
- const result = await useCase.execute(command);
224
+ userRepository.findByEmail.mockResolvedValue(null);
225
+ userRepository.save.mockResolvedValue(UserEntity.create(command));
239
226
 
240
- // Assert
241
- expect(result.email).toBe(command.email);
242
- expect(userRepository.save).toHaveBeenCalledWith(expect.any(UserEntity));
243
- expect(eventBus.publishAll).toHaveBeenCalled();
244
- });
227
+ // Act
228
+ const result = await useCase.execute(command);
245
229
 
246
- it('should throw error when user already exists', async () => {
247
- // Arrange
248
- const command = new CreateUserCommand();
249
- command.email = 'existing@example.com';
250
- command.name = 'Test User';
230
+ // Assert
231
+ expect(result.email).toBe(command.email);
232
+ expect(userRepository.save).toHaveBeenCalledWith(expect.any(UserEntity));
233
+ expect(eventBus.publishAll).toHaveBeenCalled();
234
+ });
251
235
 
252
- const existingUser = UserEntity.create({
253
- email: command.email,
254
- name: 'Existing User',
255
- });
236
+ it('should throw error when user already exists', async () => {
237
+ // Arrange
238
+ const command = new CreateUserCommand();
239
+ command.email = 'existing@example.com';
256
240
 
257
- userRepository.findByEmail.mockResolvedValue(existingUser);
241
+ userRepository.findByEmail.mockResolvedValue(UserEntity.create(command));
258
242
 
259
- // Act & Assert
260
- await expect(useCase.execute(command)).rejects.toThrow(UserAlreadyExistsException);
261
- });
243
+ // Act & Assert
244
+ await expect(useCase.execute(command)).rejects.toThrow(UserAlreadyExistsException);
262
245
  });
263
246
  });
264
247
  ```
@@ -277,23 +260,20 @@ export class UserController {
277
260
 
278
261
  @Post()
279
262
  @ApiOperation({ summary: 'Create a new user' })
280
- @ApiResponse({ status: 201, description: 'User created successfully', type: UserResponseDto })
263
+ @ApiResponse({ status: 201, type: UserResponseDto })
281
264
  @ApiResponse({ status: 400, description: 'Bad request' })
282
- @ApiResponse({ status: 409, description: 'User already exists' })
283
- async createUser(@Body() createUserDto: CreateUserDto): Promise<UserResponseDto> {
265
+ async createUser(@Body() dto: CreateUserDto): Promise<UserResponseDto> {
284
266
  const command = new CreateUserCommand();
285
- Object.assign(command, createUserDto);
267
+ Object.assign(command, dto);
286
268
  return this.createUserUseCase.execute(command);
287
269
  }
288
270
 
289
271
  @Get(':id')
290
272
  @ApiOperation({ summary: 'Get user by ID' })
291
- @ApiParam({ name: 'id', description: 'User ID' })
292
- @ApiResponse({ status: 200, description: 'User found', type: UserResponseDto })
273
+ @ApiResponse({ status: 200, type: UserResponseDto })
293
274
  @ApiResponse({ status: 404, description: 'User not found' })
294
275
  async getUser(@Param('id', ParseUUIDPipe) id: string): Promise<UserResponseDto> {
295
- const query = new GetUserQuery(id);
296
- return this.getUserUseCase.execute(query);
276
+ return this.getUserUseCase.execute(new GetUserQuery(id));
297
277
  }
298
278
  }
299
279
  ```
@@ -308,9 +288,7 @@ model User {
308
288
  name String
309
289
  createdAt DateTime @default(now())
310
290
  updatedAt DateTime @updatedAt
311
-
312
- // Relationships
313
- orders Order[]
291
+ orders Order[]
314
292
 
315
293
  @@map("users")
316
294
  }
@@ -320,8 +298,6 @@ model Order {
320
298
  total Decimal @db.Decimal(10, 2)
321
299
  status OrderStatus
322
300
  userId String
323
-
324
- // Relationships
325
301
  user User @relation(fields: [userId], references: [id])
326
302
  items OrderItem[]
327
303
 
@@ -337,104 +313,60 @@ enum OrderStatus {
337
313
  }
338
314
  ```
339
315
 
340
- ### Repository Implementation
341
- ```typescript
342
- @Injectable()
343
- export class PrismaUserRepository implements UserRepositoryInterface {
344
- constructor(private readonly prisma: PrismaService) {}
345
-
346
- async save(user: UserEntity): Promise<UserEntity> {
347
- const data = {
348
- id: user.id.toString(),
349
- email: user.email.toString(),
350
- name: user.name.toString(),
351
- };
352
-
353
- const savedUser = await this.prisma.user.upsert({
354
- where: { id: data.id },
355
- update: data,
356
- create: data,
357
- });
358
-
359
- return this.toDomain(savedUser);
360
- }
361
-
362
- async findById(id: UserId): Promise<UserEntity | null> {
363
- const user = await this.prisma.user.findUnique({
364
- where: { id: id.toString() },
365
- });
366
-
367
- return user ? this.toDomain(user) : null;
368
- }
369
-
370
- private toDomain(prismaUser: User): UserEntity {
371
- return UserEntity.reconstitute({
372
- id: UserId.create(prismaUser.id),
373
- email: EmailValueObject.create(prismaUser.email),
374
- name: NameValueObject.create(prismaUser.name),
375
- createdAt: prismaUser.createdAt,
376
- });
377
- }
378
- }
379
- ```
316
+ ### Prisma Best Practices
317
+ - Use enums for fixed value sets
318
+ - Always add indexes on foreign keys
319
+ - Use `@@map` for table naming (plural snake_case)
320
+ - Include `createdAt` and `updatedAt` timestamps
321
+ - Use `cuid()` for primary keys
322
+ - No raw SQL queries - use Prisma Client only
380
323
 
381
324
  ## Security Standards
382
325
 
383
326
  ### Authentication & Authorization
384
- - JWT tokens with proper expiration
385
- - Role-based access control (RBAC)
386
- - Input validation on all endpoints
387
- - Rate limiting for public endpoints
388
- - HTTPS only in production
327
+ - **JWT Tokens**: Proper expiration and refresh token handling
328
+ - **RBAC**: Role-based access control with Guards
329
+ - **Validation**: Input validation on all endpoints (class-validator)
330
+ - **Rate Limiting**: Throttle public endpoints to prevent abuse
331
+ - **Environment**: HTTPS only in production, secrets in env variables
389
332
 
390
333
  ### Data Protection
391
- - Encrypt sensitive data at rest
392
- - Use environment variables for secrets
393
- - Implement audit logging
394
- - Regular security updates
395
- - OWASP compliance
334
+ - Encrypt sensitive data at rest and in transit
335
+ - Never commit secrets to repository
336
+ - Implement audit logging for critical operations
337
+ - OWASP Top 10 compliance
338
+ - Regular dependency security audits
396
339
 
397
340
  ## Performance Standards
398
341
 
399
342
  ### Database Optimization
400
- - Proper indexing strategies
401
- - Connection pooling
402
- - Query optimization
403
- - Pagination for large datasets
404
- - Database monitoring
343
+ - Proper indexing on frequently queried fields
344
+ - Connection pooling via Prisma
345
+ - Pagination for large datasets (cursor-based preferred)
346
+ - Avoid N+1 queries with Prisma `include`
347
+ - Query monitoring and slow query logging
405
348
 
406
349
  ### Caching Strategy
407
- - Redis for session data
408
- - Application-level caching
409
- - HTTP caching headers
410
- - CDN for static assets
411
- - Cache invalidation patterns
350
+ - **Redis**: Session data, rate limiting, and frequently accessed data
351
+ - **Application Cache**: In-memory caching for configuration
352
+ - **TTL Strategy**: Appropriate time-to-live for different data types
353
+ - **Invalidation**: Event-driven cache invalidation
412
354
 
413
- ## MonoRepo Organization
355
+ ## Error Handling
414
356
 
415
- ### Shared Libraries Structure
416
- ```
417
- libs/
418
- ├── common/
419
- │ ├── decorators/
420
- │ ├── filters/
421
- │ ├── guards/
422
- │ ├── interceptors/
423
- │ ├── pipes/
424
- │ └── utils/
425
- ├── domain-core/
426
- │ ├── base/
427
- │ ├── interfaces/
428
- │ └── exceptions/
429
- └── database/
430
- ├── base-repository.ts
431
- ├── transaction.decorator.ts
432
- └── prisma.service.ts
433
- ```
357
+ ### Exception Hierarchy
358
+ - Domain exceptions for business rule violations
359
+ - Application exceptions for use case errors
360
+ - Infrastructure exceptions for external service failures
361
+ - HTTP exception filters for API responses
434
362
 
435
- ### Service Independence
436
- - Each microservice has its own database
437
- - Shared code through libraries only
438
- - Independent deployment pipelines
439
- - Service-to-service communication via events
440
- - No direct database access between services
363
+ ### Error Response Format
364
+ ```typescript
365
+ {
366
+ "statusCode": 400,
367
+ "message": "User with email already exists",
368
+ "error": "UserAlreadyExistsException",
369
+ "timestamp": "2024-01-15T10:30:00Z",
370
+ "path": "/api/users"
371
+ }
372
+ ```
@@ -0,0 +1,81 @@
1
+ # Technology Stack
2
+
3
+ ## Frontend Stack
4
+
5
+ ### Framework
6
+ - **Next.js 15** with TypeScript (App Router)
7
+ - Default framework unless explicitly told otherwise
8
+ - Built-in Turbopack/Webpack for building
9
+ - File-based routing with App Router
10
+ - Exception: Use pure React + Vite only when user specifically mentions offline-first functionality or requests non-Next.js setup
11
+
12
+ ### State Management
13
+ - **Zustand**
14
+
15
+ ### UI Framework & Styling
16
+ - **Shadcn/ui** (component library)
17
+ - **Radix UI** (primitives)
18
+ - **TailwindCSS v4** (styling)
19
+
20
+ ### Architecture
21
+ - **Clean Architecture** + **Domain-Driven Design (DDD)**
22
+
23
+ ### Testing
24
+ - **Vitest** (test runner)
25
+ - **React Testing Library** (component testing)
26
+ - **MSW** (Mock Service Worker - API mocking)
27
+
28
+ ### Forms & Validation
29
+ - **React Hook Form** (form management)
30
+ - **Zod** (schema validation)
31
+
32
+ ### HTTP Client
33
+ - **Axios** with interceptors
34
+
35
+ ### Progressive Web App (PWA)
36
+ - **Next.js PWA plugin**
37
+ - **Workbox** (service worker library)
38
+
39
+ ### Routing
40
+ - **Next.js App Router** (file-based routing)
41
+
42
+ ## Core Principles
43
+
44
+ ### Clean Architecture First
45
+ Strict separation of:
46
+ - Domain layer
47
+ - Application layer
48
+ - Infrastructure layer
49
+ - Presentation layer
50
+
51
+ ### Domain-Driven Design
52
+ Business logic drives architecture decisions
53
+
54
+ ### Component Composition
55
+ Build complex UIs from simple, reusable components
56
+
57
+ ### Type Safety
58
+ Leverage TypeScript for compile-time safety and developer experience
59
+
60
+ ### Performance by Design
61
+ - Lazy loading
62
+ - Memoization
63
+ - Bundle optimization
64
+
65
+ ### Accessibility as Standard
66
+ WCAG 2.1 AA compliance in all components
67
+
68
+ ### Test-Driven Development
69
+ Unit tests for all use cases and components
70
+
71
+ ### Progressive Web App
72
+ Offline-first approach with service workers
73
+
74
+ ### Minimal and Functional
75
+ Only build what's explicitly requested, nothing more
76
+
77
+ ### User-Centered Design
78
+ Start with user needs and work backward to implementation
79
+
80
+ ### MCP Shadcn Available
81
+ Use MCP to install Shadcn components instead of creating manually
@@ -16,41 +16,5 @@
16
16
  ],
17
17
  "deny": [],
18
18
  "ask": []
19
- },
20
- "hooks": {
21
- "UserPromptSubmit": [
22
- {
23
- "matcher": ".*",
24
- "hooks": [
25
- {
26
- "type": "command",
27
- "command": "python .claude/hooks/track-agent.py"
28
- }
29
- ]
30
- }
31
- ],
32
- "PreToolUse": [
33
- {
34
- "matcher": "Write|Edit",
35
- "hooks": [
36
- {
37
- "type": "command",
38
- "command": "python .claude/hooks/file-restriction-hook.py"
39
- }
40
- ]
41
- }
42
- ],
43
- "SessionEnd": [
44
- {
45
- "matcher": ".*",
46
- "hooks": [
47
- {
48
- "type": "command",
49
- "command": "python .claude/hooks/cleanup-agent.py"
50
- }
51
- ]
52
- }
53
- ]
54
- },
55
- "disableAllHooks": false
19
+ }
56
20
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "siesa-agents",
3
- "version": "2.1.24-dev.0",
3
+ "version": "2.1.25",
4
4
  "description": "Paquete para instalar y configurar agentes SIESA en tu proyecto",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -1,51 +0,0 @@
1
- import sys
2
- import json
3
- import os
4
-
5
- try:
6
- # Leer JSON desde stdin
7
- data = json.load(sys.stdin)
8
-
9
- # Obtener información del archivo y sesión
10
- file_path = data.get('tool_input', {}).get('file_path', '')
11
- extension = os.path.splitext(file_path)[1].lower() if file_path else ''
12
- session_id = data.get('session_id', '')
13
- cwd = data.get('cwd', '')
14
-
15
- # Construir ruta relativa al log desde el cwd
16
- log_file = os.path.join(cwd, '.claude', 'logs', 'active_agents.json')
17
-
18
- # Agentes que solo pueden escribir markdown
19
- MARKDOWN_ONLY_AGENTS = ['PO', 'SM', 'PM', 'ANALYST', 'ARCHITECT', 'UX-EXPERT']
20
-
21
- # Verificar si la sesión actual tiene un agente activo
22
- if session_id and os.path.exists(log_file):
23
- try:
24
- with open(log_file, 'r', encoding='utf-8') as f:
25
- active_agents = json.load(f)
26
-
27
- # Si la sesión actual tiene un agente activo
28
- if session_id in active_agents:
29
- agent_type = active_agents[session_id]['agent']
30
-
31
- # Si el agente está en la lista de solo markdown
32
- if agent_type in MARKDOWN_ONLY_AGENTS:
33
- # Solo permitir archivos markdown
34
- if extension != '.md':
35
- result = {
36
- "hookSpecificOutput": {
37
- "hookEventName": "PreToolUse",
38
- "permissionDecision": "deny",
39
- "permissionDecisionReason": f"⛔ El agente de tipo {agent_type} solo puede redactar archivos markdown"
40
- }
41
- }
42
- print(json.dumps(result))
43
- sys.exit(0)
44
- except:
45
- # Si hay error leyendo el log, permitir la operación
46
- pass
47
-
48
- # Si no está bloqueado, permitir la operación (no imprimir nada)
49
- except Exception as e:
50
- # En caso de error, permitir la operación
51
- pass
@@ -1,67 +0,0 @@
1
- import sys
2
- import json
3
- import os
4
- from datetime import datetime
5
-
6
- try:
7
- # Leer JSON desde stdin
8
- data = json.load(sys.stdin)
9
-
10
- session_id = data.get('session_id', '')
11
- prompt = data.get('prompt', '').lower()
12
- cwd = data.get('cwd', '')
13
-
14
- # Construir ruta relativa al log desde el cwd
15
- log_file = os.path.join(cwd, '.claude', 'logs', 'active_agents.json')
16
-
17
- # Crear directorio si no existe
18
- log_dir = os.path.dirname(log_file)
19
- os.makedirs(log_dir, exist_ok=True)
20
-
21
- # Lista completa de agentes disponibles
22
- agent_identifiers = {
23
- 'agents:po': 'PO',
24
- 'agents:sm': 'SM',
25
- 'agents:pm': 'PM',
26
- 'agents:analyst': 'ANALYST',
27
- 'agents:architect': 'ARCHITECT',
28
- 'agents:dev': 'DEV',
29
- 'agents:backend': 'BACKEND',
30
- 'agents:frontend': 'FRONTEND',
31
- 'agents:qa': 'QA',
32
- 'agents:ux-expert': 'UX-EXPERT',
33
- 'agents:bmad-master': 'BMAD-MASTER',
34
- 'agents:bmad-orchestrator': 'BMAD-ORCHESTRATOR'
35
- }
36
-
37
- # Detectar si se está invocando un agente
38
- agent_type = None
39
- for identifier, agent_name in agent_identifiers.items():
40
- if identifier in prompt or f'/bmad:{identifier}' in prompt:
41
- agent_type = agent_name
42
- break
43
-
44
- if agent_type and session_id:
45
- # Leer log existente
46
- active_agents = {}
47
- if os.path.exists(log_file):
48
- try:
49
- with open(log_file, 'r', encoding='utf-8') as f:
50
- active_agents = json.load(f)
51
- except:
52
- active_agents = {}
53
-
54
- # Actualizar o agregar la sesión con el agente actual
55
- active_agents[session_id] = {
56
- 'agent': agent_type,
57
- 'timestamp': datetime.now().isoformat(),
58
- 'last_prompt': prompt[:100] # Guardar inicio del prompt para debug
59
- }
60
-
61
- # Guardar log actualizado
62
- with open(log_file, 'w', encoding='utf-8') as f:
63
- json.dump(active_agents, f, indent=2, ensure_ascii=False)
64
-
65
- except Exception as e:
66
- # En caso de error, no bloquear la operación
67
- pass