siesa-agents 2.1.23 → 2.1.24-dev.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.
@@ -1,32 +1,43 @@
1
1
  # Backend Development Standards
2
2
 
3
- > **Note**: For architecture patterns and principles (Hexagonal Architecture, DDD, folder structure), see [architecture-patterns.md](./architecture-patterns.md)
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
4
17
 
5
18
  ## Technology Stack Standards
6
19
 
7
20
  ### Core Technologies
8
21
  - **NestJS**: 10+ with TypeScript and decorators
9
22
  - **TypeScript**: Strict mode enabled, no `any` types
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
23
+ - **Prisma**: ORM for database operations (no raw queries)
24
+ - **Jest**: Unit and integration testing
25
+ - **Class-validator**: Request validation and transformation
13
26
 
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)
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
20
32
 
21
33
  ### Development Tools
22
- - **Nx**: MonoRepo management and build orchestration
34
+ - **Nx**: MonoRepo management and build system
23
35
  - **ESLint + Prettier**: Code quality and formatting
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
36
+ - **Husky**: Git hooks for pre-commit validation
37
+ - **Winston**: Structured logging
38
+ - **Redis**: Caching and message transport
28
39
 
29
- ## Domain-Driven Design Implementation
40
+ ## Domain-Driven Design Standards
30
41
 
31
42
  ### Entity Structure
32
43
  ```typescript
@@ -35,6 +46,7 @@ export class UserEntity extends AggregateRoot {
35
46
  public readonly id: UserId,
36
47
  private _email: EmailValueObject,
37
48
  private _name: NameValueObject,
49
+ private _createdAt: Date,
38
50
  ) {
39
51
  super();
40
52
  }
@@ -44,6 +56,7 @@ export class UserEntity extends AggregateRoot {
44
56
  UserId.generate(),
45
57
  EmailValueObject.create(props.email),
46
58
  NameValueObject.create(props.name),
59
+ new Date(),
47
60
  );
48
61
  user.addDomainEvent(new UserCreatedEvent(user.id));
49
62
  return user;
@@ -78,6 +91,11 @@ export class EmailValueObject {
78
91
  }
79
92
  }
80
93
 
94
+ private isValidEmail(email: string): boolean {
95
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
96
+ return emailRegex.test(email);
97
+ }
98
+
81
99
  equals(other: EmailValueObject): boolean {
82
100
  return this.value === other.value;
83
101
  }
@@ -88,45 +106,15 @@ export class EmailValueObject {
88
106
  }
89
107
  ```
90
108
 
91
- ### Repository Pattern
109
+ ### Repository Interface Pattern
92
110
  ```typescript
93
- // Interface (in application/ports/repositories)
94
111
  export interface UserRepositoryInterface {
95
112
  save(user: UserEntity): Promise<UserEntity>;
96
113
  findById(id: UserId): Promise<UserEntity | null>;
97
114
  findByEmail(email: EmailValueObject): Promise<UserEntity | null>;
115
+ findAll(criteria: FindUsersCriteria): Promise<UserEntity[]>;
98
116
  delete(id: UserId): Promise<void>;
99
117
  }
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
- }
130
118
  ```
131
119
 
132
120
  ## Use Case Standards
@@ -145,19 +133,20 @@ export class CreateUserUseCase {
145
133
  async execute(command: CreateUserCommand): Promise<UserResponseDto> {
146
134
  // 1. Validate business rules
147
135
  await this.validateUserDoesNotExist(command.email);
148
-
136
+
149
137
  // 2. Create domain entity
150
138
  const user = UserEntity.create({
151
139
  email: command.email,
152
140
  name: command.name,
153
141
  });
154
-
142
+
155
143
  // 3. Persist entity
156
144
  const savedUser = await this.userRepository.save(user);
157
-
145
+
158
146
  // 4. Publish domain events
159
147
  await this.eventBus.publishAll(savedUser.getUncommittedEvents());
160
-
148
+ savedUser.markEventsAsCommitted();
149
+
161
150
  // 5. Return response DTO
162
151
  return UserResponseDto.fromEntity(savedUser);
163
152
  }
@@ -183,16 +172,20 @@ export class CreateUserCommand {
183
172
  @IsNotEmpty()
184
173
  @Length(2, 50)
185
174
  readonly name: string;
175
+
176
+ @IsOptional()
177
+ @IsString()
178
+ readonly organizationId?: string;
186
179
  }
187
180
  ```
188
181
 
189
182
  ## Testing Standards
190
183
 
191
184
  ### Testing Strategy
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
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
196
189
 
197
190
  ### Test Structure
198
191
  ```typescript
@@ -205,8 +198,19 @@ describe('CreateUserUseCase', () => {
205
198
  const module = await Test.createTestingModule({
206
199
  providers: [
207
200
  CreateUserUseCase,
208
- { provide: USER_REPOSITORY, useValue: { save: jest.fn(), findByEmail: jest.fn() } },
209
- { provide: EVENT_BUS, useValue: { publishAll: jest.fn() } },
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
+ },
210
214
  ],
211
215
  }).compile();
212
216
 
@@ -215,33 +219,46 @@ describe('CreateUserUseCase', () => {
215
219
  eventBus = module.get(EVENT_BUS);
216
220
  });
217
221
 
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';
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';
223
228
 
224
- userRepository.findByEmail.mockResolvedValue(null);
225
- userRepository.save.mockResolvedValue(UserEntity.create(command));
229
+ const expectedUser = UserEntity.create({
230
+ email: command.email,
231
+ name: command.name,
232
+ });
226
233
 
227
- // Act
228
- const result = await useCase.execute(command);
234
+ userRepository.findByEmail.mockResolvedValue(null);
235
+ userRepository.save.mockResolvedValue(expectedUser);
229
236
 
230
- // Assert
231
- expect(result.email).toBe(command.email);
232
- expect(userRepository.save).toHaveBeenCalledWith(expect.any(UserEntity));
233
- expect(eventBus.publishAll).toHaveBeenCalled();
234
- });
237
+ // Act
238
+ const result = await useCase.execute(command);
235
239
 
236
- it('should throw error when user already exists', async () => {
237
- // Arrange
238
- const command = new CreateUserCommand();
239
- command.email = 'existing@example.com';
240
+ // Assert
241
+ expect(result.email).toBe(command.email);
242
+ expect(userRepository.save).toHaveBeenCalledWith(expect.any(UserEntity));
243
+ expect(eventBus.publishAll).toHaveBeenCalled();
244
+ });
240
245
 
241
- userRepository.findByEmail.mockResolvedValue(UserEntity.create(command));
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';
242
251
 
243
- // Act & Assert
244
- await expect(useCase.execute(command)).rejects.toThrow(UserAlreadyExistsException);
252
+ const existingUser = UserEntity.create({
253
+ email: command.email,
254
+ name: 'Existing User',
255
+ });
256
+
257
+ userRepository.findByEmail.mockResolvedValue(existingUser);
258
+
259
+ // Act & Assert
260
+ await expect(useCase.execute(command)).rejects.toThrow(UserAlreadyExistsException);
261
+ });
245
262
  });
246
263
  });
247
264
  ```
@@ -260,20 +277,23 @@ export class UserController {
260
277
 
261
278
  @Post()
262
279
  @ApiOperation({ summary: 'Create a new user' })
263
- @ApiResponse({ status: 201, type: UserResponseDto })
280
+ @ApiResponse({ status: 201, description: 'User created successfully', type: UserResponseDto })
264
281
  @ApiResponse({ status: 400, description: 'Bad request' })
265
- async createUser(@Body() dto: CreateUserDto): Promise<UserResponseDto> {
282
+ @ApiResponse({ status: 409, description: 'User already exists' })
283
+ async createUser(@Body() createUserDto: CreateUserDto): Promise<UserResponseDto> {
266
284
  const command = new CreateUserCommand();
267
- Object.assign(command, dto);
285
+ Object.assign(command, createUserDto);
268
286
  return this.createUserUseCase.execute(command);
269
287
  }
270
288
 
271
289
  @Get(':id')
272
290
  @ApiOperation({ summary: 'Get user by ID' })
273
- @ApiResponse({ status: 200, type: UserResponseDto })
291
+ @ApiParam({ name: 'id', description: 'User ID' })
292
+ @ApiResponse({ status: 200, description: 'User found', type: UserResponseDto })
274
293
  @ApiResponse({ status: 404, description: 'User not found' })
275
294
  async getUser(@Param('id', ParseUUIDPipe) id: string): Promise<UserResponseDto> {
276
- return this.getUserUseCase.execute(new GetUserQuery(id));
295
+ const query = new GetUserQuery(id);
296
+ return this.getUserUseCase.execute(query);
277
297
  }
278
298
  }
279
299
  ```
@@ -288,7 +308,9 @@ model User {
288
308
  name String
289
309
  createdAt DateTime @default(now())
290
310
  updatedAt DateTime @updatedAt
291
- orders Order[]
311
+
312
+ // Relationships
313
+ orders Order[]
292
314
 
293
315
  @@map("users")
294
316
  }
@@ -298,6 +320,8 @@ model Order {
298
320
  total Decimal @db.Decimal(10, 2)
299
321
  status OrderStatus
300
322
  userId String
323
+
324
+ // Relationships
301
325
  user User @relation(fields: [userId], references: [id])
302
326
  items OrderItem[]
303
327
 
@@ -313,60 +337,104 @@ enum OrderStatus {
313
337
  }
314
338
  ```
315
339
 
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
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
+ ```
323
380
 
324
381
  ## Security Standards
325
382
 
326
383
  ### Authentication & Authorization
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
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
332
389
 
333
390
  ### Data Protection
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
391
+ - Encrypt sensitive data at rest
392
+ - Use environment variables for secrets
393
+ - Implement audit logging
394
+ - Regular security updates
395
+ - OWASP compliance
339
396
 
340
397
  ## Performance Standards
341
398
 
342
399
  ### Database Optimization
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
400
+ - Proper indexing strategies
401
+ - Connection pooling
402
+ - Query optimization
403
+ - Pagination for large datasets
404
+ - Database monitoring
348
405
 
349
406
  ### Caching Strategy
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
407
+ - Redis for session data
408
+ - Application-level caching
409
+ - HTTP caching headers
410
+ - CDN for static assets
411
+ - Cache invalidation patterns
354
412
 
355
- ## Error Handling
413
+ ## MonoRepo Organization
356
414
 
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
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
+ ```
362
434
 
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
- ```
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
@@ -335,4 +335,40 @@ import { Button } from '@/components/ui/button';
335
335
  - Cache-first for static assets
336
336
  - Network-first for dynamic data
337
337
  - Fallback pages for offline scenarios
338
- - Sync when connection restored
338
+ - Sync when connection restored
339
+
340
+ ## Language Standards (Frontend & Backend)
341
+
342
+ ### Spanish for All User-Facing Content (CRITICAL RULE)
343
+
344
+ **MANDATORY: All text visible to end users MUST be in Spanish.**
345
+
346
+ #### ✅ Spanish Required:
347
+ - UI labels, buttons, forms, messages, notifications
348
+ - API responses, validation errors, email templates
349
+ - Any text the user sees (frontend or backend)
350
+
351
+ #### ✅ English Required:
352
+ - Code (variables, functions, classes)
353
+ - Technical logs, comments, git commits
354
+ - Developer documentation
355
+
356
+ #### ❌ Never mix languages in user-facing text
357
+
358
+ **Examples:**
359
+ ```typescript
360
+ // ✅ CORRECT
361
+ <Button>Guardar</Button>
362
+ toast.success("Datos guardados correctamente");
363
+ throw new BadRequestException('No se pudo crear el usuario');
364
+
365
+ // ❌ INCORRECT
366
+ <Button>Save cambios</Button>
367
+ toast.error("Failed al guardar");
368
+ throw new BadRequestException('Invalid datos proporcionados');
369
+ ```
370
+
371
+ **Implementation:**
372
+ - Create message constants in Spanish
373
+ - Validate all user-facing text before story completion
374
+ - Technical logs stay in English, user messages in Spanish
@@ -0,0 +1,51 @@
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
@@ -0,0 +1,67 @@
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
@@ -16,5 +16,41 @@
16
16
  ],
17
17
  "deny": [],
18
18
  "ask": []
19
- }
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
20
56
  }
package/kiro/README.md ADDED
@@ -0,0 +1,13 @@
1
+ # Kiro Configuration
2
+
3
+ Esta carpeta contiene las configuraciones y specs para Kiro IDE.
4
+
5
+ ## Estructura
6
+
7
+ - **specs/** - Especificaciones de features (requirements, design, tasks)
8
+ - **steering/** - Reglas y contexto adicional para el agente
9
+ - **settings/** - Configuraciones de Kiro (MCP, etc.)
10
+
11
+ ## Uso
12
+
13
+ Las specs siguen el workflow: Requirements → Design → Tasks → Implementation