elsabro 2.2.0 → 2.3.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.
@@ -150,6 +150,119 @@ Para cada story sin dependencias:
150
150
  ```
151
151
  </execution_pattern>
152
152
 
153
+ <error_contracts>
154
+ ## Contratos de Error en Ejecución Paralela
155
+
156
+ **IMPORTAR**: Este agente DEBE seguir `/references/error-contracts.md` para manejo de errores.
157
+
158
+ ### Integración Obligatoria
159
+
160
+ Antes de ejecutar cualquier wave, el orquestador DEBE:
161
+
162
+ 1. **Validar Registry** (ContractRegistryValidator)
163
+ ```javascript
164
+ const validator = new ContractRegistryValidator("./agents");
165
+ const batch = await validator.validateBatch(agentsNeeded);
166
+ if (!batch.canProceed) {
167
+ displayError({
168
+ severity: "CRITICAL",
169
+ message: `${batch.invalid} agentes faltantes`,
170
+ options: ["update", "help", "abort"]
171
+ });
172
+ return;
173
+ }
174
+ ```
175
+
176
+ 2. **Validar Sesión** (ContractSessionValidator)
177
+ ```javascript
178
+ const sessionValidator = new ContractSessionValidator();
179
+ const session = await sessionValidator.load();
180
+ if (!session.success) {
181
+ // Intentar reparar o escalar a usuario
182
+ }
183
+ ```
184
+
185
+ 3. **Iniciar Timeout** (ContractTimeoutHandler)
186
+ ```javascript
187
+ const timeoutHandler = new ContractTimeoutHandler();
188
+ timeoutHandler.startTimeout(waveId, 30 * 60 * 1000);
189
+ ```
190
+
191
+ ### Durante Ejecución Paralela
192
+
193
+ ```
194
+ ┌─────────────────────────────────────────────────────────────────────┐
195
+ │ ERROR HANDLING EN WAVES PARALELAS │
196
+ ├─────────────────────────────────────────────────────────────────────┤
197
+ │ │
198
+ │ Wave N ejecutando: │
199
+ │ ├─ Agent 1 ──→ SUCCESS ──┐ │
200
+ │ ├─ Agent 2 ──→ FAILED ───┼──→ ErrorAggregator.addResult() │
201
+ │ └─ Agent 3 ──→ SUCCESS ──┘ │
202
+ │ ↓ │
203
+ │ ErrorAggregator.aggregate() │
204
+ │ ↓ │
205
+ │ ┌────────────┴────────────┐ │
206
+ │ │ Aplicar Policy (quorum) │ │
207
+ │ └────────────┬────────────┘ │
208
+ │ ↓ │
209
+ │ ┌──────────────────────┴──────────────────────┐ │
210
+ │ │ 2/3 = 66% > 50% │ 1/3 = 33% < 50% │ │
211
+ │ │ → CONTINUE │ → STOP │ │
212
+ │ │ → Notificar warning │ → Escalar a usuario │ │
213
+ │ └──────────────────────┴──────────────────────┘ │
214
+ │ │
215
+ └─────────────────────────────────────────────────────────────────────┘
216
+ ```
217
+
218
+ ### Retry Automático
219
+
220
+ Para errores transitorios (timeout, network):
221
+
222
+ ```javascript
223
+ const retryPolicy = new ContractRetryPolicy({
224
+ maxAttempts: 3,
225
+ baseDelayMs: 1000,
226
+ backoffMultiplier: 2
227
+ });
228
+
229
+ const result = await retryPolicy.executeWithRetry(
230
+ operationId,
231
+ async () => executeAgent(agent),
232
+ { wave: waveId }
233
+ );
234
+ ```
235
+
236
+ ### Notificación al Usuario
237
+
238
+ **INMEDIATA para CRITICAL:**
239
+ ```
240
+ ╔════════════════════════════════════════════════════╗
241
+ ║ 🔴 CRITICAL: Wave 2 Agent Failed ║
242
+ ╠════════════════════════════════════════════════════╣
243
+ ║ Agent: elsabro-verifier ║
244
+ ║ Error: Timeout after 30 minutes ║
245
+ ║ ║
246
+ ║ [r] Retry now [s] Skip [a] Abort ║
247
+ ╚════════════════════════════════════════════════════╝
248
+ ```
249
+
250
+ **AL FINAL para otros:**
251
+ ```
252
+ ╔════════════════════════════════════════════════════╗
253
+ ║ PARALLEL EXECUTION SUMMARY ║
254
+ ╠════════════════════════════════════════════════════╣
255
+ ║ Waves: 3/3 completed ║
256
+ ║ Agents: 8/9 succeeded (89%) ║
257
+ ║ ║
258
+ ║ Errors: ║
259
+ ║ └─ Wave 2: elsabro-qa timeout (retried 2x) ║
260
+ ║ ║
261
+ ║ Status: SUCCESS (quorum met) ║
262
+ ╚════════════════════════════════════════════════════╝
263
+ ```
264
+ </error_contracts>
265
+
153
266
  <conflict_resolution>
154
267
  ## Resolución de Conflictos
155
268
 
@@ -588,74 +588,251 @@ TaskUpdate({
588
588
  ```
589
589
  </parallel_execution>
590
590
 
591
+ <worktrees_integration>
592
+ ## Integración con Worktrees Paralelos
593
+
594
+ **IMPORTAR**: En waves con múltiples agentes, usar worktrees para aislamiento.
595
+
596
+ ### Cuándo Usar Worktrees
597
+
598
+ - Profile "careful": SIEMPRE usar worktrees
599
+ - Más de 2 agentes en wave: Considerar worktrees
600
+ - Archivos compartidos entre agentes: OBLIGATORIO usar worktrees
601
+
602
+ ### Flujo con Worktrees
603
+
604
+ ```javascript
605
+ // Antes de ejecutar wave paralela
606
+ if (shouldUseWorktrees(wave, profile)) {
607
+ const agents = wave.plans.map(p => p.agent);
608
+
609
+ // Crear worktrees
610
+ Bash(`./scripts/setup-parallel-worktrees.sh create ${agents.join(' ')}`);
611
+
612
+ // Ejecutar agentes en sus worktrees
613
+ for (const agent of agents) {
614
+ Task({
615
+ subagent_type: agent,
616
+ prompt: `Trabaja en worktree: ../elsabro-worktrees/${agent}-wt/`
617
+ });
618
+ }
619
+
620
+ // Merge y cleanup al completar
621
+ Bash(`./scripts/setup-parallel-worktrees.sh complete ${agents.join(' ')}`);
622
+ }
623
+ ```
624
+
625
+ ### Resolución de Conflictos
626
+
627
+ Si hay conflictos en el merge:
628
+ 1. Pausar ejecución
629
+ 2. Mostrar conflictos al usuario
630
+ 3. Opciones: resolver manual, descartar branch, o abortar
631
+ </worktrees_integration>
632
+
591
633
  <error_handling>
592
- ## Manejo de Errores
634
+ ## Manejo de Errores en Ejecucion Paralela
593
635
 
594
- ### Política de Ejecución Paralela
636
+ **IMPORTAR**: Este comando DEBE seguir `/references/error-contracts.md`.
595
637
 
596
- Este comando usa **policy: quorum** por defecto.
638
+ ### Inicializacion con Contratos
597
639
 
640
+ ```javascript
641
+ // Antes de cualquier wave, inicializar contratos
642
+ const registryValidator = new ContractRegistryValidator();
643
+ const sessionValidator = new ContractSessionValidator();
644
+ const timeoutHandler = new ContractTimeoutHandler();
645
+ const retryPolicy = new ContractRetryPolicy({
646
+ maxAttempts: 3,
647
+ baseDelayMs: 1000,
648
+ backoffMultiplier: 2
649
+ });
650
+ const errorAggregator = new ContractErrorAggregator("quorum");
651
+ const severityClassifier = new ContractSeverityClassifier();
652
+
653
+ // 1. Validar registry
654
+ const agentsNeeded = dispatcher.exploration.agents;
655
+ const batch = await registryValidator.validateBatch(agentsNeeded);
656
+ if (!batch.canProceed) {
657
+ return handleCriticalError("REGISTRY_MISSING", batch.details);
658
+ }
659
+
660
+ // 2. Validar sesion
661
+ const session = await sessionValidator.load();
662
+ if (!session.success) {
663
+ return handleCriticalError("SESSION_INVALID", session);
664
+ }
598
665
  ```
599
- ┌────────────────────────────────────────────────┐
600
- │ PARALLEL EXECUTION POLICY: quorum │
601
- ├────────────────────────────────────────────────┤
602
- │ Si >50% de agentes tienen éxito → continuar │
603
- │ Si ≤50% de agentes fallan → STOP │
604
- └────────────────────────────────────────────────┘
666
+
667
+ ### Error Handling por Fase
668
+
669
+ #### Fase Exploracion (HAIKU x3)
670
+ ```javascript
671
+ // Policy: continue_all (exploracion es best-effort)
672
+ errorAggregator.setPolicy("continue_all");
673
+
674
+ const results = await Promise.all([
675
+ executeWithRetry("explore-1", () => Task(Explore)),
676
+ executeWithRetry("explore-2", () => Task(CodeExplorer)),
677
+ executeWithRetry("explore-3", () => Task(Plan))
678
+ ]);
679
+
680
+ const summary = await errorAggregator.aggregate("exploration", results);
681
+ // continue_all: siempre continua, reporta al final
605
682
  ```
606
683
 
607
- ### Clasificación de Errores
684
+ #### Fase Implementacion (OPUS x2 por wave)
685
+ ```javascript
686
+ // Policy: quorum (>50% debe tener exito)
687
+ errorAggregator.setPolicy("quorum");
688
+
689
+ for (const wave of waves) {
690
+ timeoutHandler.startTimeout(wave.id, 60 * 60 * 1000); // 60min por wave
691
+
692
+ const results = await Promise.all(
693
+ wave.plans.map(plan =>
694
+ executeWithRetry(plan.id, () => Task(elsabro-executor, plan))
695
+ )
696
+ );
697
+
698
+ const summary = await errorAggregator.aggregate(wave.id, results);
699
+
700
+ if (summary.decision === "STOP") {
701
+ // Quorum no alcanzado
702
+ await notifyUserImmediately({
703
+ severity: "CRITICAL",
704
+ message: `Wave ${wave.id} fallo: ${summary.reason}`,
705
+ options: ["retry", "debug", "abort"]
706
+ });
707
+ return;
708
+ }
608
709
 
609
- | Severity | Emoji | Acción |
610
- |----------|-------|--------|
611
- | 🔴 CRITICAL | Parar | No puede continuar de ninguna forma |
612
- | 🟠 HIGH | Preguntar | Ofrecer opciones: fix/skip/abort |
613
- | 🟡 MEDIUM | Warning | Mostrar y continuar |
614
- | ⬜ LOW | Info | Log y continuar |
710
+ timeoutHandler.completeTimeout(wave.id);
711
+ }
712
+ ```
713
+
714
+ #### Fase Verificacion (OPUS x3)
715
+ ```javascript
716
+ // Policy: fail_fast (verificacion debe pasar completa)
717
+ errorAggregator.setPolicy("fail_fast");
718
+
719
+ const verificationResults = await Promise.all([
720
+ executeWithRetry("verify-code", () => Task(CodeReviewer)),
721
+ executeWithRetry("verify-silent", () => Task(SilentFailureHunter)),
722
+ executeWithRetry("verify-tests", () => Task(TestAnalyzer))
723
+ ]);
724
+
725
+ const summary = await errorAggregator.aggregate("verification", verificationResults);
726
+
727
+ if (summary.decision === "STOP") {
728
+ // Cualquier fallo en verificacion es critico
729
+ await displayVerificationFailure(summary);
730
+ return;
731
+ }
732
+ ```
733
+
734
+ ### Escalamiento de Errores
735
+
736
+ ```
737
+ ┌─────────────────────────────────────────────────────────────────────┐
738
+ │ ESCALAMIENTO DE ERRORES │
739
+ ├─────────────────────────────────────────────────────────────────────┤
740
+ │ │
741
+ │ Error detectado │
742
+ │ ↓ │
743
+ │ SeverityClassifier.classify(error) │
744
+ │ ↓ │
745
+ │ ┌─────────────────────────────────────────────────────────────┐ │
746
+ │ │ CRITICAL: Notificar inmediatamente, STOP │ │
747
+ │ │ HIGH: Notificar, ofrecer opciones [fix/skip/abort] │ │
748
+ │ │ MEDIUM: Agregar a resumen, continuar │ │
749
+ │ │ LOW: Log para debugging, continuar │ │
750
+ │ └─────────────────────────────────────────────────────────────┘ │
751
+ │ │
752
+ └─────────────────────────────────────────────────────────────────────┘
753
+ ```
615
754
 
616
- ### Si una tarea falla
755
+ ### Retry Automatico con Backoff
617
756
 
757
+ ```javascript
758
+ async function executeWithRetry(operationId, operation) {
759
+ return await retryPolicy.executeWithRetry(
760
+ operationId,
761
+ operation,
762
+ {
763
+ onAttempt: (attempt) => {
764
+ console.log(`[RETRY] ${operationId}: Intento ${attempt}/3`);
765
+ },
766
+ onSuccess: (result, attempts) => {
767
+ if (attempts > 1) {
768
+ console.log(`[RECOVERED] ${operationId}: Exitoso en intento ${attempts}`);
769
+ }
770
+ },
771
+ onFailure: (error, attempts) => {
772
+ console.error(`[FAILED] ${operationId}: Fallo despues de ${attempts} intentos`);
773
+
774
+ // Clasificar para decidir siguiente accion
775
+ const classification = severityClassifier.classify(error.message);
776
+ if (classification.severity === "CRITICAL") {
777
+ throw new CriticalError(error, classification);
778
+ }
779
+ }
780
+ }
781
+ );
782
+ }
618
783
  ```
619
- 1. CLASIFICAR ERROR:
620
- - Tests fallan → HIGH
621
- - Build falla → HIGH
622
- - Dependencia falta → HIGH (auto-fixable)
623
- - Lint warnings → MEDIUM
624
- - File not found crítico → CRITICAL
625
784
 
626
- 2. APLICAR RETRY (si es transitorio):
627
- - Attempt 1: Ejecutar
628
- - Attempt 2: Esperar 1s, reintentar
629
- - Attempt 3: Esperar 2s, reintentar
630
- - Máximo 3 intentos, timeout total 2 min
785
+ ### Guardar Estado de Errores
786
+
787
+ Al completar cada wave (exito o fallo):
631
788
 
632
- 3. SI RETRY FALLA → Escalar según severidad
789
+ ```javascript
790
+ // Actualizar state.json con errores para recovery
791
+ state.errors = {
792
+ count: allErrors.length,
793
+ lastError: allErrors[allErrors.length - 1],
794
+ byWave: errorsByWave,
795
+ bySeverity: {
796
+ CRITICAL: criticalErrors,
797
+ HIGH: highErrors,
798
+ MEDIUM: mediumErrors,
799
+ LOW: lowErrors
800
+ }
801
+ };
802
+
803
+ state.parallelExecution = {
804
+ policy: currentPolicy,
805
+ activeAgents: [],
806
+ completedAgents: completedAgentIds,
807
+ failedAgents: failedAgentIds
808
+ };
633
809
 
634
- 4. MOSTRAR ERROR formateado:
635
- ╔══════════════════════════════════════════╗
636
- ║ 🟠 ERROR: TESTS_FAILED ║
637
- ║ Severity: HIGH ║
638
- ╠══════════════════════════════════════════╣
639
- ║ [Detalles del error] ║
640
- ╠══════════════════════════════════════════╣
641
- ║ [d] Debug [s] Skip [a] Abort ║
642
- ╚══════════════════════════════════════════╝
810
+ await sessionValidator.save(state);
643
811
  ```
644
812
 
645
- ### Reglas de Desviación
813
+ ### Clasificacion de Errores (Referencia Rapida)
814
+
815
+ | Severity | Emoji | Accion |
816
+ |----------|-------|--------|
817
+ | CRITICAL | Parar | No puede continuar de ninguna forma |
818
+ | HIGH | Preguntar | Ofrecer opciones: fix/skip/abort |
819
+ | MEDIUM | Warning | Mostrar y continuar |
820
+ | LOW | Info | Log y continuar |
821
+
822
+ ### Reglas de Desviacion
646
823
 
647
- | Situación | Severity | Acción |
824
+ | Situacion | Severity | Accion |
648
825
  |-----------|----------|--------|
649
826
  | Bug que impide continuar | HIGH | Auto-fix (3 intentos) |
650
- | Falta validación crítica | HIGH | Auto-add |
827
+ | Falta validacion critica | HIGH | Auto-add |
651
828
  | Falta dependencia | HIGH | Auto-install |
652
- | Cambio arquitectónico | HIGH | CHECKPOINT |
829
+ | Cambio arquitectonico | HIGH | CHECKPOINT |
653
830
  | Tests fallan | HIGH | Debug o skip |
654
831
  | Lint warnings | MEDIUM | Continuar |
655
832
 
656
833
  ### Actualizar Estado con Tasks
657
834
 
658
- Después de errores, actualizar tanto SESSION-STATE.json como Tasks:
835
+ Despues de errores, actualizar tanto SESSION-STATE.json como Tasks:
659
836
 
660
837
  ```javascript
661
838
  // 1. Actualizar task con metadata de error
@@ -681,13 +858,13 @@ TaskCreate({
681
858
  metadata: { type: "bugfix", parentPlan: "plan-X-id" }
682
859
  }) // → bugfix-id
683
860
 
684
- // 3. Bloquear verificación hasta que bugfix complete
861
+ // 3. Bloquear verificacion hasta que bugfix complete
685
862
  TaskUpdate({
686
863
  taskId: "verify-aggregate-id",
687
864
  addBlockedBy: ["bugfix-id"]
688
865
  })
689
866
 
690
- // 4. También actualizar SESSION-STATE.json para compatibilidad
867
+ // 4. Tambien actualizar SESSION-STATE.json para compatibilidad
691
868
  ```
692
869
 
693
870
  ### Rollback de Estado con Tasks
@@ -711,7 +888,7 @@ TaskUpdate({ taskId: "plan-X-id", status: "completed" })
711
888
  // Listar todas las tasks
712
889
  TaskList()
713
890
 
714
- // Ver detalles de una task específica
891
+ // Ver detalles de una task especifica
715
892
  TaskGet({ taskId: "plan-X-id" })
716
893
  ```
717
894
  </error_handling>
@@ -418,6 +418,40 @@ Ejecuta `/elsabro:start` o el comando específico.
418
418
  ```
419
419
  </state_management>
420
420
 
421
+ <memory_integration>
422
+ ## Integración con Memoria Persistente
423
+
424
+ **IMPORTAR**: Este comando lee la memoria del proyecto al iniciar.
425
+
426
+ ### Al Iniciar Sesión
427
+
428
+ ```javascript
429
+ // Leer memoria si existe
430
+ const memoryFiles = [
431
+ ".planning/CLAUDE.md",
432
+ ".planning/mistakes.md",
433
+ ".planning/patterns.md"
434
+ ];
435
+
436
+ for (const file of memoryFiles) {
437
+ const content = Read(file);
438
+ if (content) {
439
+ // Aplicar reglas de memoria en toda la sesión
440
+ applyMemoryRules(content);
441
+ }
442
+ }
443
+ ```
444
+
445
+ ### Detección de Correcciones
446
+
447
+ Durante la sesión, detectar triggers de corrección:
448
+ - "No, hazlo así..."
449
+ - "Siempre usa X en lugar de Y"
450
+ - "Guarda esto en tu memoria"
451
+
452
+ Y actualizar automáticamente `.planning/mistakes.md` o `.planning/patterns.md`.
453
+ </memory_integration>
454
+
421
455
  <error_handling>
422
456
  ## Manejo de Errores
423
457
 
@@ -104,6 +104,35 @@ Write(".elsabro/context.md", generateHumanReadableContext(state));
104
104
  ```
105
105
  </state_sync>
106
106
 
107
+ <review_integration>
108
+ ## Integración con Code Review
109
+
110
+ **IMPORTAR**: Este comando puede invocar `/elsabro:review` para verificación detallada.
111
+
112
+ ### Opción: Review Profundo
113
+
114
+ Si el usuario solicita verificación exhaustiva, invocar:
115
+
116
+ ```javascript
117
+ // Verificación con code review profesional
118
+ Task({
119
+ subagent_type: "skill:review",
120
+ prompt: `
121
+ /elsabro:review --staged --save
122
+
123
+ Guarda el resultado en .planning/REVIEW.md
124
+ `
125
+ })
126
+ ```
127
+
128
+ ### Checklist de Verificación Extendido
129
+
130
+ Además de los checks estándar, verificar contra:
131
+ - `.planning/mistakes.md` - ¿Se cometieron errores conocidos?
132
+ - `.planning/patterns.md` - ¿Se siguieron los patrones preferidos?
133
+ - `.planning/CLAUDE.md` - ¿Se respetaron las reglas del proyecto?
134
+ </review_integration>
135
+
107
136
  ## Propósito
108
137
 
109
138
  Verificar que el trabajo completado cumple con los requirements, pasa todos los tests, y está listo para merge/deploy.
@@ -0,0 +1,145 @@
1
+ #!/bin/bash
2
+ # confirm-destructive.sh - ELSABRO Hook: Confirmar antes de operaciones destructivas
3
+ # Uso: Este hook se ejecuta antes de comandos Bash que coinciden con patrones destructivos
4
+ # Patrones: rm -rf, git reset --hard, git clean -fd, git push --force, drop table, truncate
5
+
6
+ # Colores
7
+ RED='\033[0;31m'
8
+ GREEN='\033[0;32m'
9
+ YELLOW='\033[1;33m'
10
+ BLUE='\033[0;34m'
11
+ BOLD='\033[1m'
12
+ NC='\033[0m'
13
+
14
+ # Prefijo ELSABRO
15
+ PREFIX="[ELSABRO:hook]"
16
+
17
+ # Comando que se va a ejecutar (pasado como argumento)
18
+ COMMAND="${1:-}"
19
+
20
+ # Patrones destructivos y su descripcion
21
+ declare -A DESTRUCTIVE_PATTERNS=(
22
+ ["rm -rf"]="Eliminacion recursiva forzada de archivos/directorios"
23
+ ["rm -r"]="Eliminacion recursiva de archivos/directorios"
24
+ ["git reset --hard"]="Descarta TODOS los cambios locales sin posibilidad de recuperacion"
25
+ ["git clean -fd"]="Elimina archivos no rastreados y directorios"
26
+ ["git clean -f"]="Elimina archivos no rastreados"
27
+ ["git push --force"]="Sobreescribe historial remoto (puede perder commits de otros)"
28
+ ["git push -f"]="Sobreescribe historial remoto (puede perder commits de otros)"
29
+ ["DROP TABLE"]="Elimina tabla de base de datos permanentemente"
30
+ ["drop table"]="Elimina tabla de base de datos permanentemente"
31
+ ["TRUNCATE"]="Elimina todos los datos de una tabla"
32
+ ["truncate"]="Elimina todos los datos de una tabla"
33
+ ["DELETE FROM"]="Elimina registros de base de datos"
34
+ ["delete from"]="Elimina registros de base de datos"
35
+ ["> /dev/null"]="Redirige output (potencialmente peligroso si mal usado)"
36
+ ["chmod 777"]="Permisos inseguros - todos pueden leer/escribir/ejecutar"
37
+ ["chmod -R"]="Cambio recursivo de permisos"
38
+ ["dd if="]="Operacion de disco de bajo nivel"
39
+ ["mkfs"]="Formateo de sistema de archivos"
40
+ [":(){ :|:& };:"]="Fork bomb - puede crashear el sistema"
41
+ )
42
+
43
+ # Detectar patron destructivo
44
+ detect_destructive_pattern() {
45
+ local cmd="$1"
46
+
47
+ for pattern in "${!DESTRUCTIVE_PATTERNS[@]}"; do
48
+ if [[ "$cmd" == *"$pattern"* ]]; then
49
+ echo "$pattern"
50
+ return 0
51
+ fi
52
+ done
53
+
54
+ echo ""
55
+ return 1
56
+ }
57
+
58
+ # Mostrar advertencia
59
+ show_warning() {
60
+ local pattern="$1"
61
+ local description="${DESTRUCTIVE_PATTERNS[$pattern]}"
62
+
63
+ echo ""
64
+ echo -e "${RED}${BOLD}========================================${NC}"
65
+ echo -e "${RED}${BOLD} ADVERTENCIA: OPERACION DESTRUCTIVA${NC}"
66
+ echo -e "${RED}${BOLD}========================================${NC}"
67
+ echo ""
68
+ echo -e "${YELLOW}$PREFIX Comando detectado:${NC}"
69
+ echo -e "${BLUE} $COMMAND${NC}"
70
+ echo ""
71
+ echo -e "${YELLOW}$PREFIX Patron peligroso:${NC} ${RED}$pattern${NC}"
72
+ echo -e "${YELLOW}$PREFIX Riesgo:${NC} $description"
73
+ echo ""
74
+ echo -e "${RED}${BOLD}Este comando puede causar perdida de datos irreversible.${NC}"
75
+ echo ""
76
+ }
77
+
78
+ # Solicitar confirmacion
79
+ request_confirmation() {
80
+ # En modo no interactivo (CI/CD), rechazar automaticamente
81
+ if [ ! -t 0 ]; then
82
+ echo -e "${RED}$PREFIX Modo no interactivo detectado - rechazando comando destructivo${NC}"
83
+ echo -e "${YELLOW}$PREFIX Para ejecutar en CI, desactiva el hook 'confirm-destructive'${NC}"
84
+ return 1
85
+ fi
86
+
87
+ echo -e "${YELLOW}Para confirmar, escribe exactamente: ${BOLD}SI CONFIRMO${NC}"
88
+ echo -n "> "
89
+ read -r confirmation
90
+
91
+ if [ "$confirmation" = "SI CONFIRMO" ]; then
92
+ echo -e "${GREEN}$PREFIX Confirmado. Procediendo con el comando...${NC}"
93
+ return 0
94
+ else
95
+ echo -e "${RED}$PREFIX Cancelado. El comando NO se ejecutara.${NC}"
96
+ return 1
97
+ fi
98
+ }
99
+
100
+ # Registrar en log
101
+ log_destructive_attempt() {
102
+ local pattern="$1"
103
+ local confirmed="$2"
104
+ local log_dir=".planning/logs"
105
+ local log_file="$log_dir/destructive-commands.log"
106
+
107
+ # Crear directorio si no existe
108
+ mkdir -p "$log_dir" 2>/dev/null || true
109
+
110
+ local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
111
+ local status="REJECTED"
112
+ [ "$confirmed" = "true" ] && status="CONFIRMED"
113
+
114
+ echo "[$timestamp] $status | Pattern: $pattern | Command: $COMMAND" >> "$log_file" 2>/dev/null || true
115
+ }
116
+
117
+ # Main
118
+ main() {
119
+ if [ -z "$COMMAND" ]; then
120
+ # Sin comando, permitir (no hay nada que verificar)
121
+ exit 0
122
+ fi
123
+
124
+ # Detectar si es destructivo
125
+ local pattern=$(detect_destructive_pattern "$COMMAND")
126
+
127
+ if [ -z "$pattern" ]; then
128
+ # No es destructivo, permitir
129
+ exit 0
130
+ fi
131
+
132
+ # Es destructivo - mostrar advertencia
133
+ show_warning "$pattern"
134
+
135
+ # Solicitar confirmacion
136
+ if request_confirmation; then
137
+ log_destructive_attempt "$pattern" "true"
138
+ exit 0 # Permitir ejecucion
139
+ else
140
+ log_destructive_attempt "$pattern" "false"
141
+ exit 1 # Bloquear ejecucion
142
+ fi
143
+ }
144
+
145
+ main "$@"