elsabro 5.2.0 → 6.0.1

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.
@@ -69,6 +69,31 @@ CHECKPOINT (preguntar):
69
69
  ```
70
70
  </critical_rules>
71
71
 
72
+ <skill_awareness>
73
+ ## Skill Awareness
74
+
75
+ ### Si recibes skills en tu contexto (seccion "SKILLS DE REFERENCIA"):
76
+
77
+ 1. **Lee las guias ANTES de implementar** — los skills contienen patrones verificados
78
+ 2. **Sigue la estructura de archivos sugerida** en `<project_structure>`
79
+ 3. **Usa los pre-requisitos** de `<pre_requisites>` para verificar dependencias
80
+ 4. **Aplica los patrones de codigo** de las secciones de implementacion
81
+ 5. **En tu output, menciona que skill usaste** para cada decision
82
+
83
+ ### Si NO recibes skills pero la tarea parece relevante:
84
+
85
+ 1. Busca en `skills/` con Glob("skills/*.md") por archivos relevantes
86
+ 2. Lee el archivo con Read() y usa como referencia
87
+ 3. Prioriza skills de: auth, database, testing, api
88
+
89
+ ### Keywords que activan busqueda de skills:
90
+ - auth/login/registro → skills/auth-setup.md
91
+ - database/db/prisma → skills/database-setup.md
92
+ - test/testing/jest → skills/testing-setup.md
93
+ - api/endpoint/rest → skills/api-setup.md
94
+ - payments/stripe → skills/payments-setup.md
95
+ </skill_awareness>
96
+
72
97
  <execution_flow>
73
98
  ## Flujo de Ejecución
74
99
 
@@ -46,6 +46,16 @@ Cada tarea debe:
46
46
  - Generar un commit significativo
47
47
  </critical_rules>
48
48
 
49
+ <skill_awareness>
50
+ ## Skill Awareness (Planner)
51
+
52
+ Si recibes skills en tu contexto:
53
+ 1. Referencia los skills relevantes en el PLAN.md que crees
54
+ 2. Usa las estructuras de archivos sugeridas por los skills
55
+ 3. Incorpora los pre-requisitos como dependencias del plan
56
+ 4. Menciona que skill aplica a cada tarea del plan
57
+ </skill_awareness>
58
+
49
59
  <research_first>
50
60
  ## Protocolo: Research-Before-Plan
51
61
 
package/bin/install.js CHANGED
@@ -239,8 +239,8 @@ if (hasUpdate) {
239
239
  console.log(` ${yellow}⚠${reset} No se pudo verificar la última versión`);
240
240
  console.log(` ${dim}Intentando actualizar de todas formas...${reset}\n`);
241
241
  } else if (latestVersion === pkg.version) {
242
- console.log(` ${green}✓${reset} Ya tienes la última versión (${pkg.version})\n`);
243
- process.exit(0);
242
+ console.log(` ${green}✓${reset} Versión actual: ${pkg.version} (ya es la última)`);
243
+ console.log(` ${dim}Reinstalando archivos locales...${reset}\n`);
244
244
  } else {
245
245
  console.log(` ${cyan}Nueva versión disponible: ${latestVersion}${reset}`);
246
246
  console.log(` ${dim}Versión actual: ${pkg.version}${reset}\n`);
@@ -357,7 +357,9 @@ async function install() {
357
357
  { src: 'templates', dest: 'templates', name: 'Templates' },
358
358
  { src: 'workflows', dest: 'workflows', name: 'Workflows' },
359
359
  { src: 'references', dest: 'references', name: 'Referencias' },
360
- { src: 'hooks/dist', dest: 'hooks', name: 'Hooks' }
360
+ { src: 'hooks/dist', dest: 'hooks', name: 'Hooks' },
361
+ { src: 'flows', dest: 'flows', name: 'Flows' },
362
+ { src: 'flow-engine', dest: 'flow-engine', name: 'Flow Engine' }
361
363
  ];
362
364
 
363
365
  const failures = [];
@@ -465,7 +467,9 @@ async function uninstall() {
465
467
  'skills',
466
468
  'templates',
467
469
  'workflows',
468
- 'references'
470
+ 'references',
471
+ 'flows',
472
+ 'flow-engine'
469
473
  ];
470
474
 
471
475
  for (const item of toRemove) {
@@ -162,6 +162,51 @@ Write(".elsabro/state.json", JSON.stringify(state, null, 2));
162
162
  Los skills proveen patrones de código verificados, configuraciones de setup, y guides de integración durante la implementación.
163
163
  </skill_discovery>
164
164
 
165
+ <skill_injection>
166
+ ## Skill Injection (OBLIGATORIO despues de Discovery)
167
+
168
+ **Si skill discovery encontro skills relevantes, CARGAR su contenido como contexto.**
169
+
170
+ ```javascript
171
+ // 1. Obtener skills recomendados
172
+ const recommendedSkills = state.context.available_skills || [];
173
+
174
+ if (recommendedSkills.length > 0) {
175
+ // 2. Cargar contenido de los top-3 skills con match
176
+ const loadedSkills = [];
177
+ for (const skill of recommendedSkills.slice(0, 3)) {
178
+ const content = Read(`skills/${skill.id}.md`);
179
+ if (content) {
180
+ loadedSkills.push({
181
+ name: skill.id,
182
+ content: content
183
+ });
184
+ }
185
+ }
186
+
187
+ // 3. Guardar skills cargados en state
188
+ state.context.loaded_skills = loadedSkills;
189
+ Write(".elsabro/state.json", JSON.stringify(state, null, 2));
190
+
191
+ // 4. Informar al usuario
192
+ output(`Skills cargados para esta ejecucion:`);
193
+ loadedSkills.forEach(s => output(` - ${s.name}`));
194
+ }
195
+ ```
196
+
197
+ **GATE**: Cuando lances agentes executors, INCLUIR los skills en el prompt:
198
+ ```
199
+ Si state.context.loaded_skills tiene contenido, agregar al prompt de CADA executor:
200
+
201
+ "SKILLS DE REFERENCIA:
202
+ [Para cada skill cargado:]
203
+ ### Skill: {name}
204
+ {contenido del archivo .md}
205
+
206
+ Usa estos skills como guia de patrones, estructura y mejores practicas."
207
+ ```
208
+ </skill_injection>
209
+
165
210
  <objective>
166
211
  Ejecutar planes de una fase con:
167
212
  - **Sistema de Tasks para tracking real** de waves y dependencias
@@ -528,6 +573,41 @@ Para cada wave:
528
573
  TaskUpdate({ taskId: "wave-W-id", status: "in_progress" })
529
574
  ```
530
575
 
576
+ <IMPERATIVO_AGENT_TEAMS>
577
+ ## ⛔ STOP — LEER ANTES DE LANZAR AGENTES PARALELOS
578
+
579
+ **REGLA NO NEGOCIABLE (Rule 8):** Si vas a lanzar 2+ Task() para implementacion o review:
580
+
581
+ 1. **PRIMERO** → `TeamCreate({ team_name: "elsabro-impl", description: "..." })`
582
+ 2. **DESPUES** → `Task({ ..., team_name: "elsabro-impl", name: "executor-1" })`
583
+ 3. **AL FINAL** → `SendMessage({ type: "shutdown_request", ... })` para CADA teammate
584
+ 4. **ULTIMO** → `TeamDelete()`
585
+
586
+ **Si ya lanzaste Task() sin TeamCreate: DETENTE AHORA. No continúes.**
587
+ Vuelve arriba, crea el team, y relanza los agentes CON team_name.
588
+
589
+ **NUNCA hagas esto:**
590
+ ```javascript
591
+ // ⛔ PROHIBIDO — bare Task() sin team
592
+ Task({ subagent_type: "elsabro-executor", prompt: "..." })
593
+ Task({ subagent_type: "elsabro-qa", prompt: "..." })
594
+ ```
595
+
596
+ **SIEMPRE haz esto:**
597
+ ```javascript
598
+ // ✅ CORRECTO — con Agent Team
599
+ TeamCreate({ team_name: "elsabro-impl", description: "Implementation" })
600
+ Task({ subagent_type: "elsabro-executor", team_name: "elsabro-impl", name: "executor-1", prompt: "..." })
601
+ Task({ subagent_type: "elsabro-qa", team_name: "elsabro-impl", name: "qa-1", prompt: "..." })
602
+ // ... al completar ...
603
+ SendMessage({ type: "shutdown_request", recipient: "executor-1", content: "Done" })
604
+ SendMessage({ type: "shutdown_request", recipient: "qa-1", content: "Done" })
605
+ TeamDelete()
606
+ ```
607
+
608
+ **Excepcion UNICA:** Exploracion read-only con HAIKU (Paso 0) puede usar subagents sueltos.
609
+ </IMPERATIVO_AGENT_TEAMS>
610
+
531
611
  ### Si hay múltiples planes en la wave → Paralelo (OPUS)
532
612
  ```javascript
533
613
  // Marcar todos los planes como in_progress
@@ -548,13 +628,20 @@ Write(".elsabro/state.json", JSON.stringify(state, null, 2));
548
628
 
549
629
  // Lanzar teammates OPUS en paralelo (UN SOLO MENSAJE)
550
630
  // MODELO: OPUS porque escriben código
631
+ // SKILLS: Incluir loaded_skills en CADA prompt de executor
632
+ const skillContext = state.context.loaded_skills?.length > 0
633
+ ? '\n\n## SKILLS DE REFERENCIA\n' + state.context.loaded_skills.map(s =>
634
+ '### Skill: ' + s.name + '\n' + s.content
635
+ ).join('\n\n---\n\n') + '\n\nUsa los skills como referencia de patrones y estructura de archivos.'
636
+ : '';
637
+
551
638
  Task({
552
639
  subagent_type: "elsabro-executor",
553
640
  model: "opus", // ← OPUS para implementación
554
641
  team_name: "elsabro-impl",
555
642
  name: "executor-1",
556
643
  description: "Ejecutar Plan A",
557
- prompt: "Implementa el Plan A siguiendo TDD..."
644
+ prompt: `Implementa el Plan A siguiendo TDD.${skillContext}`
558
645
  }) |
559
646
  Task({
560
647
  subagent_type: "elsabro-executor",
@@ -562,7 +649,7 @@ Task({
562
649
  team_name: "elsabro-impl",
563
650
  name: "executor-2",
564
651
  description: "Ejecutar Plan B",
565
- prompt: "Implementa el Plan B siguiendo TDD..."
652
+ prompt: `Implementa el Plan B siguiendo TDD.${skillContext}`
566
653
  }) |
567
654
  Task({
568
655
  subagent_type: "elsabro-executor",
@@ -570,7 +657,7 @@ Task({
570
657
  team_name: "elsabro-impl",
571
658
  name: "executor-3",
572
659
  description: "Ejecutar Plan C",
573
- prompt: "Implementa el Plan C siguiendo TDD..."
660
+ prompt: `Implementa el Plan C siguiendo TDD.${skillContext}`
574
661
  })
575
662
 
576
663
  // Al completar cada plan
@@ -763,6 +850,29 @@ Cuando termines, dime si funcionó o qué problemas encontraste.
763
850
 
764
851
  </process>
765
852
 
853
+ <agent_teams_cleanup>
854
+ ## Agent Teams Cleanup (OBLIGATORIO)
855
+
856
+ **Al completar TODAS las fases de implementacion y review:**
857
+
858
+ ```javascript
859
+ // 1. Shutdown CADA teammate por nombre
860
+ const teammates = ["executor-1", "executor-2", "executor-3", "reviewer-1", "reviewer-2", "reviewer-3"];
861
+ for (const name of teammates) {
862
+ SendMessage({ type: "shutdown_request", recipient: name, content: "All phases complete" });
863
+ }
864
+
865
+ // 2. Eliminar team
866
+ TeamDelete();
867
+
868
+ // 3. Registrar en state
869
+ state.current_flow.agent_teams_cleanup = true;
870
+ Write(".elsabro/state.json", JSON.stringify(state, null, 2));
871
+ ```
872
+
873
+ **GATE**: No mostrar "Siguiente Paso" si agent_teams_cleanup !== true y hubo 2+ agentes.
874
+ </agent_teams_cleanup>
875
+
766
876
  <code_review_gate>
767
877
  ## Code Review Gate (OBLIGATORIO - NO NEGOCIABLE)
768
878
 
@@ -786,6 +896,30 @@ Cuando termines, dime si funcionó o qué problemas encontraste.
786
896
  <siguiente_paso>
787
897
  ## Siguiente Paso
788
898
 
899
+ ### Self-Check (OBLIGATORIO antes de reportar resultado)
900
+
901
+ Antes de mostrar "Siguiente Paso", verifica CADA UNO de estos items:
902
+
903
+ ```
904
+ [ ] Si lance 2+ Task() en paralelo → ¿use TeamCreate primero?
905
+ SI: Continuar
906
+ NO: DETENERME. Crear team y relanzar.
907
+
908
+ [ ] Si descubri skills relevantes → ¿los cargue con Read() y los pase al executor?
909
+ SI: Continuar
910
+ NO: DETENERME. Cargar skills y re-ejecutar.
911
+
912
+ [ ] Si escribi/modifique codigo → ¿ejecute code review?
913
+ SI: Continuar
914
+ NO: DETENERME. Ejecutar code review.
915
+
916
+ [ ] Si use Agent Teams → ¿ejecute SendMessage(shutdown) + TeamDelete?
917
+ SI: Continuar
918
+ NO: DETENERME. Hacer cleanup.
919
+ ```
920
+
921
+ **Si alguna respuesta es NO: NO mostrar siguiente paso. Corregir primero.**
922
+
789
923
  Al completar, establecer en state.json:
790
924
  ```javascript
791
925
  // GATE CHECK 1: No escribir suggested_next sin code review
@@ -1237,3 +1371,22 @@ Al completar un plan:
1237
1371
  - [x] Tests: npm run test
1238
1372
  ```
1239
1373
  </summary_creation>
1374
+
1375
+ <engine_integration>
1376
+ ## Flow Engine (v6.0.0)
1377
+
1378
+ The flow engine runtime is available at `flow-engine/src/index.js`.
1379
+ It can parse and execute `flows/development-flow.json`.
1380
+
1381
+ Current status: Engine handles implemented nodes (19/42).
1382
+ Not-implemented nodes throw NotImplementedError with gap descriptions.
1383
+
1384
+ For full engine-driven execution (future):
1385
+ ```javascript
1386
+ const { FlowEngine } = require('./flow-engine/src/index.js');
1387
+ const flow = require('./flows/development-flow.json');
1388
+ const engine = new FlowEngine({ callbacks: { onAgent, onBash, onInterrupt, onCheckpoint, onParallel, onTeamRequired } });
1389
+ engine.loadFlow(flow);
1390
+ const result = await engine.run({ task, profile, complexity }, callbacks);
1391
+ ```
1392
+ </engine_integration>
@@ -11,6 +11,9 @@ allowed-tools:
11
11
  - TaskCreate
12
12
  - TaskUpdate
13
13
  - TaskList
14
+ - TeamCreate
15
+ - TeamDelete
16
+ - SendMessage
14
17
  - WebSearch
15
18
  - AskUserQuestion
16
19
  - mcp__plugin_context7_context7__*
@@ -157,6 +160,35 @@ Write(".elsabro/state.json", JSON.stringify(state, null, 2));
157
160
  Los skills descubiertos informan la fase de research (Context7 queries) y la creación del PLAN.md.
158
161
  </skill_discovery>
159
162
 
163
+ <skill_injection>
164
+ ## Skill Injection (Para Planificacion)
165
+
166
+ Despues de skill discovery, cargar skills relevantes para informar al planner:
167
+
168
+ ```javascript
169
+ const recommendedSkills = state.context.available_skills || [];
170
+ if (recommendedSkills.length > 0) {
171
+ const loadedSkills = [];
172
+ for (const skill of recommendedSkills.slice(0, 3)) {
173
+ const content = Read(`skills/${skill.id}.md`);
174
+ if (content) {
175
+ loadedSkills.push({ name: skill.id, content: content });
176
+ }
177
+ }
178
+ state.context.loaded_skills = loadedSkills;
179
+ Write(".elsabro/state.json", JSON.stringify(state, null, 2));
180
+ }
181
+ ```
182
+
183
+ **Al lanzar el planner OPUS**: Incluir skills como contexto:
184
+ ```
185
+ "Skills disponibles para esta tarea:
186
+ ${loadedSkills.map(s => '- ' + s.name + ': ' + s.content.slice(0, 200)).join('\n')}
187
+
188
+ Referencia estos skills en tu plan cuando aplique."
189
+ ```
190
+ </skill_injection>
191
+
160
192
  <objective>
161
193
  Crear un plan ejecutable para una fase o feature, con investigación previa usando Context7 y WebSearch.
162
194
 
@@ -42,6 +42,27 @@ Write(".elsabro/state.json", JSON.stringify(state, null, 2));
42
42
  ```
43
43
  </skill_discovery>
44
44
 
45
+ <skill_injection>
46
+ ## Skill Injection (Quick)
47
+
48
+ Si discovery encontro skills relevantes, cargar el top-1 skill como referencia rapida:
49
+
50
+ ```javascript
51
+ const skills = state.context.available_skills || [];
52
+ if (skills.length > 0) {
53
+ const topSkill = Read(`skills/${skills[0].id}.md`);
54
+ if (topSkill) {
55
+ state.context.loaded_skills = [{ name: skills[0].id, content: topSkill }];
56
+ }
57
+ }
58
+ ```
59
+
60
+ Incluir en el prompt del agente quick:
61
+ ```
62
+ "Skill de referencia: ${topSkill ? topSkill.name : 'ninguno'}"
63
+ ```
64
+ </skill_injection>
65
+
45
66
  <command-name>quick</command-name>
46
67
 
47
68
  ## Propósito
@@ -324,6 +324,19 @@ Agrega verificaciones extra:
324
324
  - Compatible con CI/CD pipelines
325
325
  - Genera reporte en `.planning/VERIFICATION-REPORT.md`
326
326
 
327
+ <IMPERATIVO_AGENT_TEAMS>
328
+ ## ⛔ STOP — ANTES DE LANZAR REVIEWERS PARALELOS
329
+
330
+ **REGLA NO NEGOCIABLE (Rule 8):** Los 3 reviewers DEBEN ejecutarse como Agent Team.
331
+
332
+ 1. **PRIMERO** → `TeamCreate({ team_name: "elsabro-verify", description: "Verification team" })`
333
+ 2. **DESPUES** → `Task({ ..., team_name: "elsabro-verify", name: "reviewer-N" })` x3
334
+ 3. **AL FINAL** → `SendMessage({ type: "shutdown_request", ... })` x3
335
+ 4. **ULTIMO** → `TeamDelete()`
336
+
337
+ **NUNCA lances 3 Task() de review sin TeamCreate previo.**
338
+ </IMPERATIVO_AGENT_TEAMS>
339
+
327
340
  <parallel_coordination>
328
341
  ## Coordinación de Verificación Paralela
329
342
 
@@ -612,6 +625,22 @@ TaskCreate({
612
625
  <siguiente_paso>
613
626
  ## Siguiente Paso
614
627
 
628
+ ### Self-Check (OBLIGATORIO antes de reportar resultado)
629
+
630
+ Antes de mostrar "Siguiente Paso", verifica CADA UNO de estos items:
631
+
632
+ ```
633
+ [ ] Si lance 2+ Task() en paralelo → ¿use TeamCreate primero?
634
+ SI: Continuar
635
+ NO: DETENERME. Crear team y relanzar.
636
+
637
+ [ ] Si use Agent Teams → ¿ejecute SendMessage(shutdown) + TeamDelete?
638
+ SI: Continuar
639
+ NO: DETENERME. Hacer cleanup.
640
+ ```
641
+
642
+ **Si alguna respuesta es NO: NO mostrar siguiente paso. Corregir primero.**
643
+
615
644
  Al completar, establecer en state.json:
616
645
  ```javascript
617
646
  // GATE CHECK: No proceder sin Agent Teams para fases con 2+ agentes
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "elsabro-flow-engine",
3
+ "version": "0.1.0",
4
+ "description": "Runtime engine for ELSABRO development-flow.json",
5
+ "main": "src/index.js",
6
+ "scripts": {
7
+ "test": "node --test tests/"
8
+ },
9
+ "engines": {
10
+ "node": ">=18.0.0"
11
+ },
12
+ "license": "MIT"
13
+ }
@@ -0,0 +1,112 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Checkpoint manager for the ELSABRO flow engine.
5
+ *
6
+ * Saves and loads flow execution state to/from the filesystem,
7
+ * enabling resume-from-checkpoint after interrupts or crashes.
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+
13
+ class CheckpointManager {
14
+ /**
15
+ * @param {object} options
16
+ * @param {string} [options.dir] – checkpoint directory (default: .elsabro/checkpoints/)
17
+ */
18
+ constructor(options = {}) {
19
+ this.dir = options.dir || path.join(process.cwd(), '.elsabro', 'checkpoints');
20
+ this._seq = 0;
21
+ }
22
+
23
+ /**
24
+ * Save a checkpoint to disk.
25
+ *
26
+ * @param {string} flowId – flow identifier
27
+ * @param {object} checkpointData – { currentNode, nextNode, context, nodesVisited }
28
+ * @returns {string} checkpoint filename
29
+ */
30
+ save(flowId, checkpointData) {
31
+ fs.mkdirSync(this.dir, { recursive: true });
32
+
33
+ const timestamp = Date.now();
34
+ this._seq++;
35
+ const filename = `${flowId}-${timestamp}-${String(this._seq).padStart(4, '0')}.json`;
36
+ const filepath = path.join(this.dir, filename);
37
+
38
+ const payload = {
39
+ flowId,
40
+ timestamp,
41
+ createdAt: new Date().toISOString(),
42
+ ...checkpointData
43
+ };
44
+
45
+ fs.writeFileSync(filepath, JSON.stringify(payload, null, 2));
46
+ return filename;
47
+ }
48
+
49
+ /**
50
+ * Load the latest checkpoint for a flow.
51
+ *
52
+ * @param {string} flowId
53
+ * @returns {object|null} checkpoint data, or null if none found
54
+ */
55
+ load(flowId) {
56
+ if (!fs.existsSync(this.dir)) return null;
57
+
58
+ const files = fs.readdirSync(this.dir)
59
+ .filter(f => f.startsWith(`${flowId}-`) && f.endsWith('.json'))
60
+ .sort()
61
+ .reverse();
62
+
63
+ if (files.length === 0) return null;
64
+
65
+ const filepath = path.join(this.dir, files[0]);
66
+ const data = JSON.parse(fs.readFileSync(filepath, 'utf8'));
67
+ return data;
68
+ }
69
+
70
+ /**
71
+ * List all checkpoints for a flow.
72
+ *
73
+ * @param {string} flowId
74
+ * @returns {{ filename: string, timestamp: number }[]}
75
+ */
76
+ list(flowId) {
77
+ if (!fs.existsSync(this.dir)) return [];
78
+
79
+ return fs.readdirSync(this.dir)
80
+ .filter(f => f.startsWith(`${flowId}-`) && f.endsWith('.json'))
81
+ .map(filename => {
82
+ const match = filename.match(/-(\d+)-\d+\.json$/);
83
+ return {
84
+ filename,
85
+ timestamp: match ? parseInt(match[1], 10) : 0
86
+ };
87
+ })
88
+ .sort((a, b) => b.timestamp - a.timestamp);
89
+ }
90
+
91
+ /**
92
+ * Remove old checkpoints, keeping only the most recent N.
93
+ *
94
+ * @param {string} flowId
95
+ * @param {number} [keepLast=5]
96
+ */
97
+ clean(flowId, keepLast = 5) {
98
+ const checkpoints = this.list(flowId);
99
+ const toRemove = checkpoints.slice(keepLast);
100
+
101
+ for (const cp of toRemove) {
102
+ const filepath = path.join(this.dir, cp.filename);
103
+ if (fs.existsSync(filepath)) {
104
+ fs.unlinkSync(filepath);
105
+ }
106
+ }
107
+
108
+ return toRemove.length;
109
+ }
110
+ }
111
+
112
+ module.exports = { CheckpointManager };