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.
- package/agents/elsabro-orchestrator.md +113 -0
- package/commands/elsabro/execute.md +223 -46
- package/commands/elsabro/start.md +34 -0
- package/commands/elsabro/verify-work.md +29 -0
- package/hooks/confirm-destructive.sh +145 -0
- package/hooks/hooks-config.json +81 -0
- package/hooks/lint-check.sh +238 -0
- package/hooks/post-edit-test.sh +189 -0
- package/package.json +3 -2
- package/references/error-contracts-tests.md +1171 -0
- package/references/error-contracts.md +3102 -0
- package/references/parallel-worktrees.md +293 -0
- package/scripts/setup-parallel-worktrees.sh +319 -0
- package/skills/memory-update.md +207 -0
- package/skills/review.md +331 -0
- package/skills/techdebt.md +289 -0
- package/skills/tutor.md +219 -0
- package/templates/.planning/notes/.gitkeep +0 -0
- package/templates/CLAUDE.md.template +48 -0
- package/templates/error-handling-config.json +79 -2
- package/templates/mistakes.md.template +52 -0
- package/templates/patterns.md.template +114 -0
|
@@ -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
|
-
|
|
636
|
+
**IMPORTAR**: Este comando DEBE seguir `/references/error-contracts.md`.
|
|
595
637
|
|
|
596
|
-
|
|
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
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
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
|
-
|
|
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
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
627
|
-
|
|
628
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
|
824
|
+
| Situacion | Severity | Accion |
|
|
648
825
|
|-----------|----------|--------|
|
|
649
826
|
| Bug que impide continuar | HIGH | Auto-fix (3 intentos) |
|
|
650
|
-
| Falta
|
|
827
|
+
| Falta validacion critica | HIGH | Auto-add |
|
|
651
828
|
| Falta dependencia | HIGH | Auto-install |
|
|
652
|
-
| Cambio
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
|
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 "$@"
|