elsabro 4.0.0 → 4.2.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/README.md +61 -7
- package/agents/elsabro-orchestrator.md +70 -20
- package/agents/elsabro-ux-designer.md +70 -0
- package/commands/elsabro/add-phase.md +20 -0
- package/commands/elsabro/add-todo.md +30 -0
- package/commands/elsabro/audit-milestone.md +20 -0
- package/commands/elsabro/check-todos.md +29 -0
- package/commands/elsabro/complete-milestone.md +20 -0
- package/commands/elsabro/debug.md +76 -1
- package/commands/elsabro/design-ui.md +407 -0
- package/commands/elsabro/discuss-phase.md +20 -0
- package/commands/elsabro/execute.md +166 -30
- package/commands/elsabro/exit.md +97 -0
- package/commands/elsabro/help.md +7 -1
- package/commands/elsabro/insert-phase.md +20 -0
- package/commands/elsabro/list-phase-assumptions.md +20 -0
- package/commands/elsabro/map-codebase.md +30 -0
- package/commands/elsabro/new-milestone.md +23 -0
- package/commands/elsabro/new.md +64 -0
- package/commands/elsabro/pause-work.md +28 -5
- package/commands/elsabro/plan-milestone-gaps.md +20 -0
- package/commands/elsabro/plan.md +20 -0
- package/commands/elsabro/progress.md +8 -0
- package/commands/elsabro/quick.md +58 -3
- package/commands/elsabro/remove-phase.md +20 -0
- package/commands/elsabro/research-phase.md +20 -0
- package/commands/elsabro/resume-work.md +21 -0
- package/commands/elsabro/set-profile.md +38 -0
- package/commands/elsabro/settings.md +38 -0
- package/commands/elsabro/start.md +31 -1
- package/commands/elsabro/update.md +28 -0
- package/commands/elsabro/verify-work.md +94 -7
- package/commands/elsabro/verify.md +30 -0
- package/hooks/elsabro-mode.sh +102 -0
- package/hooks/hooks-config-updated.json +43 -7
- package/hooks/skill-discovery.sh +38 -8
- package/package.json +5 -5
- package/references/SYSTEM_INDEX.md +9 -0
- package/references/agent-teams-integration.md +99 -36
- package/references/enforcement-rules.md +177 -62
- package/references/flow-orchestration.md +127 -32
- package/references/next-step-engine.md +134 -0
- package/skills/stitch-ui-design.md +242 -0
- package/templates/session-state.json +3 -1
|
@@ -12,6 +12,9 @@ allowed-tools:
|
|
|
12
12
|
- TaskUpdate
|
|
13
13
|
- TaskList
|
|
14
14
|
- TaskGet
|
|
15
|
+
- TeamCreate
|
|
16
|
+
- TeamDelete
|
|
17
|
+
- SendMessage
|
|
15
18
|
- AskUserQuestion
|
|
16
19
|
sync:
|
|
17
20
|
reads: [".elsabro/state.json"]
|
|
@@ -24,6 +27,9 @@ dispatcher:
|
|
|
24
27
|
model: opus
|
|
25
28
|
parallel: true
|
|
26
29
|
min_agents: 3
|
|
30
|
+
method: "agent-team"
|
|
31
|
+
blocking: true
|
|
32
|
+
max_review_iterations: 5
|
|
27
33
|
---
|
|
28
34
|
|
|
29
35
|
# /elsabro:verify-work
|
|
@@ -50,6 +56,7 @@ TaskUpdate(id, status: "in_progress")
|
|
|
50
56
|
const state = Read(".elsabro/state.json") || createInitialState();
|
|
51
57
|
|
|
52
58
|
// 3. Verificar contexto de execute (archivos a verificar)
|
|
59
|
+
state.context = state.context || {};
|
|
53
60
|
if (state.context.changed_files && state.context.changed_files.length > 0) {
|
|
54
61
|
console.log("Archivos a verificar:", state.context.changed_files);
|
|
55
62
|
console.log("Commits a revisar:", state.context.commits);
|
|
@@ -60,6 +67,7 @@ if (state.context.changed_files && state.context.changed_files.length > 0) {
|
|
|
60
67
|
|
|
61
68
|
// 4. Actualizar estado
|
|
62
69
|
state.current_flow = { command: "verify-work", phase: "initializing", started_at: new Date().toISOString() };
|
|
70
|
+
state.elsabro_mode = true;
|
|
63
71
|
Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
64
72
|
|
|
65
73
|
// 5. Completar task de inicialización
|
|
@@ -90,12 +98,13 @@ state.history.push({
|
|
|
90
98
|
});
|
|
91
99
|
|
|
92
100
|
// Actualizar contexto
|
|
101
|
+
state.context = state.context || {};
|
|
93
102
|
state.context.verification_result = verificationResult;
|
|
94
103
|
state.context.ready_for_merge = verificationResult === "PASS";
|
|
95
104
|
|
|
96
105
|
// Limpiar flujo y sugerir siguiente
|
|
97
106
|
state.current_flow = null;
|
|
98
|
-
state.suggested_next = verificationResult === "PASS" ?
|
|
107
|
+
state.suggested_next = verificationResult === "PASS" ? "plan" : "execute"; // plan = next phase
|
|
99
108
|
|
|
100
109
|
Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
101
110
|
|
|
@@ -133,6 +142,26 @@ Además de los checks estándar, verificar contra:
|
|
|
133
142
|
- `.planning/CLAUDE.md` - ¿Se respetaron las reglas del proyecto?
|
|
134
143
|
</review_integration>
|
|
135
144
|
|
|
145
|
+
<agent_teams_gate>
|
|
146
|
+
## Agent Teams Gate (OBLIGATORIO)
|
|
147
|
+
|
|
148
|
+
verify-work usa 3 agentes OPUS en paralelo → Agent Teams es OBLIGATORIO.
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
¿3 agentes de verificación necesarios?
|
|
152
|
+
│
|
|
153
|
+
└─ SÍ → TeamCreate("elsabro-verify")
|
|
154
|
+
Task(team_name: "elsabro-verify", name: "reviewer-1")
|
|
155
|
+
Task(team_name: "elsabro-verify", name: "reviewer-2")
|
|
156
|
+
Task(team_name: "elsabro-verify", name: "reviewer-3")
|
|
157
|
+
... verificación ...
|
|
158
|
+
SendMessage(shutdown_request) x3
|
|
159
|
+
TeamDelete()
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**VIOLACIÓN CRÍTICA**: Lanzar 3 verificadores sin Agent Teams = ABORTAR
|
|
163
|
+
</agent_teams_gate>
|
|
164
|
+
|
|
136
165
|
## Propósito
|
|
137
166
|
|
|
138
167
|
Verificar que el trabajo completado cumple con los requirements, pasa todos los tests, y está listo para merge/deploy.
|
|
@@ -322,27 +351,50 @@ TaskUpdate({ taskId: "functional-id", status: "in_progress" })
|
|
|
322
351
|
TaskUpdate({ taskId: "quality-id", status: "in_progress" })
|
|
323
352
|
TaskUpdate({ taskId: "security-id", status: "in_progress" })
|
|
324
353
|
|
|
325
|
-
// 2.
|
|
354
|
+
// 2. OBLIGATORIO: Crear Agent Team para verificación paralela (Rule 8)
|
|
355
|
+
TeamCreate({
|
|
356
|
+
team_name: "elsabro-verify",
|
|
357
|
+
description: "Verification team for phase " + phaseNumber
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
// MARCAR: Agent Teams fue usado correctamente (para GATE CHECK en siguiente_paso)
|
|
361
|
+
state.current_flow.parallel_agents_used = 3;
|
|
362
|
+
state.current_flow.agent_teams_used = true;
|
|
363
|
+
Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
364
|
+
|
|
365
|
+
// 3. Spawn 3 teammates OPUS EN PARALELO (un solo mensaje)
|
|
326
366
|
Task({
|
|
327
367
|
subagent_type: "pr-review-toolkit:code-reviewer",
|
|
328
|
-
model: "opus",
|
|
368
|
+
model: "opus",
|
|
369
|
+
team_name: "elsabro-verify",
|
|
370
|
+
name: "reviewer-1",
|
|
329
371
|
description: "Code review completo",
|
|
330
372
|
prompt: "Revisa los cambios buscando bugs, code smells, mejores prácticas, seguridad..."
|
|
331
373
|
}) |
|
|
332
374
|
Task({
|
|
333
375
|
subagent_type: "pr-review-toolkit:silent-failure-hunter",
|
|
334
|
-
model: "opus",
|
|
376
|
+
model: "opus",
|
|
377
|
+
team_name: "elsabro-verify",
|
|
378
|
+
name: "reviewer-2",
|
|
335
379
|
description: "Buscar errores ocultos",
|
|
336
380
|
prompt: "Busca errores silenciosos, try/catch que ocultan errores, fallbacks problemáticos..."
|
|
337
381
|
}) |
|
|
338
382
|
Task({
|
|
339
383
|
subagent_type: "pr-review-toolkit:pr-test-analyzer",
|
|
340
|
-
model: "opus",
|
|
384
|
+
model: "opus",
|
|
385
|
+
team_name: "elsabro-verify",
|
|
386
|
+
name: "reviewer-3",
|
|
341
387
|
description: "Analizar cobertura de tests",
|
|
342
388
|
prompt: "Verifica que los tests cubren los cambios, busca casos críticos sin test..."
|
|
343
389
|
})
|
|
344
390
|
|
|
345
|
-
//
|
|
391
|
+
// 4. Al completar verificación, shutdown teammates y cleanup
|
|
392
|
+
SendMessage({ type: "shutdown_request", recipient: "reviewer-1", content: "Verification complete" })
|
|
393
|
+
SendMessage({ type: "shutdown_request", recipient: "reviewer-2", content: "Verification complete" })
|
|
394
|
+
SendMessage({ type: "shutdown_request", recipient: "reviewer-3", content: "Verification complete" })
|
|
395
|
+
TeamDelete()
|
|
396
|
+
|
|
397
|
+
// 5. Al completar cada uno, marcar completed
|
|
346
398
|
TaskUpdate({
|
|
347
399
|
taskId: "functional-id",
|
|
348
400
|
status: "completed",
|
|
@@ -434,8 +486,9 @@ Si cualquier verificador falla:
|
|
|
434
486
|
║ Opciones: ║
|
|
435
487
|
║ [d] /elsabro:debug - Investigar fallos ║
|
|
436
488
|
║ [f] Fix y re-verificar ║
|
|
437
|
-
║ [s] Saltar verificación (no recomendado) ║
|
|
438
489
|
║ [a] Abortar ║
|
|
490
|
+
║ ║
|
|
491
|
+
║ (NO hay opción "saltar" — v4.0.0 policy) ║
|
|
439
492
|
╚══════════════════════════════════════════════════╝
|
|
440
493
|
```
|
|
441
494
|
|
|
@@ -555,3 +608,37 @@ TaskCreate({
|
|
|
555
608
|
}
|
|
556
609
|
```
|
|
557
610
|
</parallel_coordination>
|
|
611
|
+
|
|
612
|
+
<siguiente_paso>
|
|
613
|
+
## Siguiente Paso
|
|
614
|
+
|
|
615
|
+
Al completar, establecer en state.json:
|
|
616
|
+
```javascript
|
|
617
|
+
// GATE CHECK: No proceder sin Agent Teams para fases con 2+ agentes
|
|
618
|
+
if (state.current_flow?.parallel_agents_used >= 2 && !state.current_flow?.agent_teams_used) {
|
|
619
|
+
throw new Error("AGENT_TEAMS_GATE: Cannot proceed without Agent Teams for 2+ agents");
|
|
620
|
+
}
|
|
621
|
+
if (verificationResult === "PASS") {
|
|
622
|
+
state.suggested_next = "plan"; // next phase
|
|
623
|
+
} else {
|
|
624
|
+
state.suggested_next = "execute"; // re-execute to fix
|
|
625
|
+
}
|
|
626
|
+
Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
Si PASS:
|
|
630
|
+
```
|
|
631
|
+
## Siguiente Paso
|
|
632
|
+
|
|
633
|
+
→ /elsabro:plan [N+1] — planificar la siguiente fase
|
|
634
|
+
→ /elsabro:progress — ver el estado general
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
Si FAIL/PARTIAL:
|
|
638
|
+
```
|
|
639
|
+
## Siguiente Paso
|
|
640
|
+
|
|
641
|
+
→ /elsabro:execute — re-ejecutar para corregir los gaps
|
|
642
|
+
→ /elsabro:debug — investigar problemas encontrados
|
|
643
|
+
```
|
|
644
|
+
</siguiente_paso>
|
|
@@ -14,6 +14,18 @@ argument-hint: "[número de fase o 'manual']"
|
|
|
14
14
|
|
|
15
15
|
# ELSABRO: Verify
|
|
16
16
|
|
|
17
|
+
<state_sync>
|
|
18
|
+
## SINCRONIZACIÓN DE ESTADO
|
|
19
|
+
|
|
20
|
+
### Al Iniciar
|
|
21
|
+
- Leer `.elsabro/state.json` (si existe)
|
|
22
|
+
- Activar persistent mode: `state.elsabro_mode = true`
|
|
23
|
+
|
|
24
|
+
### Al Completar
|
|
25
|
+
- Registrar en `history`
|
|
26
|
+
- Establecer `state.suggested_next = "progress"`
|
|
27
|
+
</state_sync>
|
|
28
|
+
|
|
17
29
|
<objective>
|
|
18
30
|
Verificar que una fase o feature cumple con sus objetivos usando:
|
|
19
31
|
- Tests automáticos
|
|
@@ -205,3 +217,21 @@ gaps:
|
|
|
205
217
|
| medium | Documentar para después |
|
|
206
218
|
| low | Opcional arreglar |
|
|
207
219
|
</gap_handling>
|
|
220
|
+
|
|
221
|
+
<siguiente_paso>
|
|
222
|
+
## Siguiente Paso
|
|
223
|
+
|
|
224
|
+
Al completar la verificación, establecer en state.json:
|
|
225
|
+
```javascript
|
|
226
|
+
state.suggested_next = "progress";
|
|
227
|
+
Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Mostrar al usuario:
|
|
231
|
+
```
|
|
232
|
+
## Siguiente Paso
|
|
233
|
+
|
|
234
|
+
→ /elsabro:progress — ver el estado general del proyecto
|
|
235
|
+
→ /elsabro:plan [N+1] — planificar la siguiente fase
|
|
236
|
+
```
|
|
237
|
+
</siguiente_paso>
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ELSABRO Persistent Mode Hook - UserPromptSubmit
|
|
3
|
+
# Runs on every user message. Fast path: 2 checks then exit 0.
|
|
4
|
+
# When active: outputs routing instructions for Claude to classify intent.
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
STATE_FILE=".elsabro/state.json"
|
|
9
|
+
|
|
10
|
+
# ─── Dependency Check ─────────────────────────────────────────────────
|
|
11
|
+
|
|
12
|
+
if ! command -v jq &>/dev/null; then
|
|
13
|
+
echo "[ELSABRO] 'jq' no instalado. Persistent mode requiere jq. Instalar: brew install jq (macOS) o apt install jq (Linux)" >&2
|
|
14
|
+
exit 0
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
# ─── Consume stdin (required - hook protocol sends JSON on stdin) ─────
|
|
18
|
+
|
|
19
|
+
INPUT_JSON=$(cat)
|
|
20
|
+
|
|
21
|
+
# ─── Fast Path (< 5ms) ───────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
# 1. No state file? Not in ELSABRO → pass through
|
|
24
|
+
if [ ! -f "$STATE_FILE" ]; then
|
|
25
|
+
exit 0
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
# 2. Check elsabro_mode (with validation for empty/corrupt files)
|
|
29
|
+
ELSABRO_MODE=$(jq -r '.elsabro_mode // false' "$STATE_FILE" 2>/dev/null || echo "false")
|
|
30
|
+
if [ -z "$ELSABRO_MODE" ]; then
|
|
31
|
+
echo "[ELSABRO] state.json existe pero no se pudo leer elsabro_mode. Archivo corrupto?" >&2
|
|
32
|
+
exit 0
|
|
33
|
+
fi
|
|
34
|
+
if [ "$ELSABRO_MODE" != "true" ]; then
|
|
35
|
+
exit 0
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# 3. Parse user message from stdin JSON (only when active - avoids jq on fast path)
|
|
39
|
+
USER_MESSAGE=$(echo "$INPUT_JSON" | jq -r '.prompt // ""' 2>/dev/null || echo "")
|
|
40
|
+
|
|
41
|
+
# 4. Slash command? Match /command or /command:sub pattern, not file paths like /api/users
|
|
42
|
+
# Slash commands: /commit, /elsabro:plan, /help (single token, no embedded /)
|
|
43
|
+
# NOT slash commands: /api/users is broken, /usr/bin/node
|
|
44
|
+
if [[ "$USER_MESSAGE" =~ ^/[a-zA-Z][a-zA-Z0-9_:-]*($|[[:space:]]) ]]; then
|
|
45
|
+
exit 0
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# ─── Active Path: ELSABRO is ON ──────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
# Read session context for routing
|
|
51
|
+
# Use single jq call to extract all fields at once (performance + atomic read)
|
|
52
|
+
CONTEXT_JSON=$(jq -r '{
|
|
53
|
+
cmd: (.current_flow.command // "none"),
|
|
54
|
+
ptype: (.context.project_type // "unknown"),
|
|
55
|
+
stack: ((.context.tech_stack // []) | if type == "array" then join(", ") elif type == "string" then . else "" end)
|
|
56
|
+
}' "$STATE_FILE" 2>/dev/null || echo '{"cmd":"none","ptype":"unknown","stack":""}')
|
|
57
|
+
|
|
58
|
+
CURRENT_COMMAND=$(echo "$CONTEXT_JSON" | jq -r '.cmd // "none"' 2>/dev/null || echo "none")
|
|
59
|
+
PROJECT_TYPE=$(echo "$CONTEXT_JSON" | jq -r '.ptype // "unknown"' 2>/dev/null || echo "unknown")
|
|
60
|
+
TECH_STACK=$(echo "$CONTEXT_JSON" | jq -r '.stack // ""' 2>/dev/null || echo "")
|
|
61
|
+
|
|
62
|
+
# Output routing instructions (quoted heredoc prevents shell expansion for security)
|
|
63
|
+
cat <<'ROUTING_HEADER'
|
|
64
|
+
ELSABRO PERSISTENT MODE ACTIVE
|
|
65
|
+
|
|
66
|
+
You are in ELSABRO persistent mode. The user's message should be routed to the appropriate ELSABRO command based on intent classification.
|
|
67
|
+
|
|
68
|
+
## Current Session
|
|
69
|
+
ROUTING_HEADER
|
|
70
|
+
# Variable lines use printf to avoid shell expansion attacks from state.json content
|
|
71
|
+
printf -- '- Active command: %s\n' "$CURRENT_COMMAND"
|
|
72
|
+
printf -- '- Project type: %s\n' "$PROJECT_TYPE"
|
|
73
|
+
printf -- '- Tech stack: %s\n' "$TECH_STACK"
|
|
74
|
+
cat <<'ROUTING_BODY'
|
|
75
|
+
|
|
76
|
+
## Intent Routing Table
|
|
77
|
+
|
|
78
|
+
Classify the user's message and invoke the matching ELSABRO skill using the Skill tool:
|
|
79
|
+
|
|
80
|
+
| If the user wants to... | Invoke |
|
|
81
|
+
|--------------------------------------------------|---------------------------------------|
|
|
82
|
+
| Fix a bug, error, crash, or investigate a problem | Skill("elsabro:debug") |
|
|
83
|
+
| Create a new feature, add something, build | Skill("elsabro:plan") |
|
|
84
|
+
| Understand, explain, or explore the codebase | Skill("elsabro:map-codebase") |
|
|
85
|
+
| Make a quick/small/simple change | Skill("elsabro:quick") |
|
|
86
|
+
| Refactor, improve, or clean up code | Skill("elsabro:plan") |
|
|
87
|
+
| Verify, test, check, or validate work | Skill("elsabro:verify-work") |
|
|
88
|
+
| Resume, continue, or pick up where they left off | Skill("elsabro:resume-work") |
|
|
89
|
+
| Design UI, create mockup, wireframe, or screen | Skill("elsabro:design-ui") |
|
|
90
|
+
| Exit, leave, stop, or deactivate ELSABRO | Skill("elsabro:exit") |
|
|
91
|
+
| See progress, status, or what's next | Skill("elsabro:progress") |
|
|
92
|
+
| Pause work or take a break | Skill("elsabro:pause-work") |
|
|
93
|
+
|
|
94
|
+
## Rules
|
|
95
|
+
|
|
96
|
+
1. ALWAYS route through a Skill() call - never answer directly without invoking the appropriate ELSABRO command
|
|
97
|
+
2. If the intent is ambiguous, ask the user to clarify with 2-3 options
|
|
98
|
+
3. If the user is clearly having a casual conversation (greeting, thanks, etc.), respond naturally but remind them ELSABRO mode is active
|
|
99
|
+
4. Pass the user's original message as the argument to the skill when relevant
|
|
100
|
+
ROUTING_BODY
|
|
101
|
+
|
|
102
|
+
exit 0
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"description": "ELSABRO Hooks Configuration v3.
|
|
4
|
-
"version": "3.
|
|
3
|
+
"description": "ELSABRO Hooks Configuration v3.2 (hook config version) for ELSABRO v4.0.0 - Agent Teams + Blocking Review + Skill Discovery + Persistent Mode",
|
|
4
|
+
"version": "3.2.0",
|
|
5
5
|
|
|
6
6
|
"hooks": {
|
|
7
7
|
"PreFlow": [
|
|
@@ -14,10 +14,8 @@
|
|
|
14
14
|
"command": "bash ./hooks/skill-discovery.sh \"{{inputs.task}}\" \"{{inputs.complexity}}\"",
|
|
15
15
|
"timeout": 30000,
|
|
16
16
|
"enabled": true,
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
"inject_into": ["state.discoveredSkills", "state.skillContext"]
|
|
20
|
-
},
|
|
17
|
+
"outputFormat": "json",
|
|
18
|
+
"outputNote": "stdout JSON is consumed by flow engine to populate state.discoveredSkills and state.skillContext",
|
|
21
19
|
"errorPolicy": "continue",
|
|
22
20
|
"cacheable": true,
|
|
23
21
|
"cache_ttl": 3600
|
|
@@ -58,6 +56,23 @@
|
|
|
58
56
|
}
|
|
59
57
|
],
|
|
60
58
|
|
|
59
|
+
"UserPromptSubmit": [
|
|
60
|
+
{
|
|
61
|
+
"id": "elsabro-persistent-mode",
|
|
62
|
+
"name": "elsabro-persistent-mode",
|
|
63
|
+
"description": "Ruteo automático de mensajes a comandos ELSABRO cuando persistent mode está activo",
|
|
64
|
+
"command": "bash \"$CLAUDE_PROJECT_DIR\"/hooks/elsabro-mode.sh",
|
|
65
|
+
"timeout": 5000,
|
|
66
|
+
"enabled": true,
|
|
67
|
+
"errorPolicy": "continue",
|
|
68
|
+
"errorPolicyRationale": "Hook debe fallar silenciosamente para no bloquear input del usuario. Errores se reportan via stderr.",
|
|
69
|
+
"fastPath": {
|
|
70
|
+
"description": "< 5ms cuando inactivo: no state.json, elsabro_mode=false, o mensaje empieza con /",
|
|
71
|
+
"checks": ["state_file_exists", "elsabro_mode_true", "not_slash_command"]
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
|
|
61
76
|
"Notification": [
|
|
62
77
|
{
|
|
63
78
|
"id": "task-complete-notify",
|
|
@@ -112,11 +127,32 @@
|
|
|
112
127
|
},
|
|
113
128
|
|
|
114
129
|
"teams": {
|
|
115
|
-
"description": "Perfil para Agent Teams -
|
|
130
|
+
"description": "Perfil para Agent Teams - coordinacion peer-to-peer con TeamCreate/TeamDelete/SendMessage",
|
|
131
|
+
"auto-test-on-edit": true,
|
|
132
|
+
"lint-on-save": true,
|
|
133
|
+
"confirm-destructive": true,
|
|
134
|
+
"skill-discovery": true,
|
|
135
|
+
"agent-teams": true,
|
|
136
|
+
"blocking-review": true,
|
|
137
|
+
"max-review-iterations": 5
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
"persistent-mode": {
|
|
141
|
+
"description": "Perfil con persistent mode habilitado - ELSABRO siempre activo, ruteo automático de mensajes",
|
|
142
|
+
"auto-test-on-edit": true,
|
|
143
|
+
"lint-on-save": true,
|
|
144
|
+
"confirm-destructive": true,
|
|
145
|
+
"skill-discovery": true,
|
|
146
|
+
"elsabro-persistent-mode": true
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
"persistent-teams": {
|
|
150
|
+
"description": "Perfil completo - persistent mode + Agent Teams + blocking review",
|
|
116
151
|
"auto-test-on-edit": true,
|
|
117
152
|
"lint-on-save": true,
|
|
118
153
|
"confirm-destructive": true,
|
|
119
154
|
"skill-discovery": true,
|
|
155
|
+
"elsabro-persistent-mode": true,
|
|
120
156
|
"agent-teams": true,
|
|
121
157
|
"blocking-review": true,
|
|
122
158
|
"max-review-iterations": 5
|
package/hooks/skill-discovery.sh
CHANGED
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
# 3. npx skills find <query> (vercel-labs/skills registry)
|
|
14
14
|
#
|
|
15
15
|
# Requiere: bash 4+, jq, node/npm (para npx)
|
|
16
|
+
#
|
|
17
|
+
# NOTA: Este script NO es safe para ejecución paralela del mismo archivo.
|
|
18
|
+
# Si se ejecuta concurrentemente, las escrituras a CACHE_FILE pueden colisionar.
|
|
19
|
+
# El flow engine debe serializar llamadas a este script.
|
|
16
20
|
|
|
17
21
|
set -euo pipefail
|
|
18
22
|
|
|
@@ -53,8 +57,12 @@ log_error() { echo -e "${RED}${PREFIX}${NC} ✗ $1" >&2; }
|
|
|
53
57
|
# ============================================================================
|
|
54
58
|
|
|
55
59
|
ensure_dirs() {
|
|
56
|
-
mkdir -p "$CACHE_DIR" 2>/dev/null
|
|
57
|
-
|
|
60
|
+
if ! mkdir -p "$CACHE_DIR" 2>/dev/null; then
|
|
61
|
+
log_warn "Could not create cache directory: $CACHE_DIR"
|
|
62
|
+
fi
|
|
63
|
+
if ! mkdir -p "$GLOBAL_SKILLS_DIR" 2>/dev/null; then
|
|
64
|
+
log_warn "Could not create skills directory: $GLOBAL_SKILLS_DIR"
|
|
65
|
+
fi
|
|
58
66
|
}
|
|
59
67
|
|
|
60
68
|
has_command() {
|
|
@@ -80,7 +88,13 @@ is_cache_valid() {
|
|
|
80
88
|
|
|
81
89
|
get_from_cache() {
|
|
82
90
|
local cache_key="$1"
|
|
83
|
-
|
|
91
|
+
local data
|
|
92
|
+
data=$(jq --arg key "$cache_key" '.[$key].data' "$CACHE_FILE" 2>/dev/null)
|
|
93
|
+
# Guard against null/empty data from corrupt cache
|
|
94
|
+
if [ -z "$data" ] || [ "$data" = "null" ]; then
|
|
95
|
+
return 1
|
|
96
|
+
fi
|
|
97
|
+
echo "$data"
|
|
84
98
|
}
|
|
85
99
|
|
|
86
100
|
save_to_cache() {
|
|
@@ -89,6 +103,9 @@ save_to_cache() {
|
|
|
89
103
|
ensure_dirs
|
|
90
104
|
local now
|
|
91
105
|
now=$(date +%s)
|
|
106
|
+
# NOTE: PID-unique tmp file mitigates but doesn't eliminate the read-modify-write
|
|
107
|
+
# race condition on concurrent writes. This is acceptable because skill discovery
|
|
108
|
+
# runs sequentially in the flow engine. Worst case: one cache entry is lost.
|
|
92
109
|
local tmp_file="${CACHE_FILE}.tmp.$$"
|
|
93
110
|
if [ -f "$CACHE_FILE" ] && jq empty "$CACHE_FILE" 2>/dev/null; then
|
|
94
111
|
jq --arg key "$cache_key" --argjson data "$data" --argjson ts "$now" \
|
|
@@ -208,7 +225,7 @@ scan_elsabro_skills() {
|
|
|
208
225
|
|
|
209
226
|
# Extraer metadata básica
|
|
210
227
|
local description=""
|
|
211
|
-
description=$(sed -n '/^description:/s/^description: *//p' "$skill_file" | head -1
|
|
228
|
+
description=$(sed -n '/^description:/s/^description: *//p' "$skill_file" | head -1)
|
|
212
229
|
description="${description:0:100}"
|
|
213
230
|
|
|
214
231
|
local tags=""
|
|
@@ -319,6 +336,7 @@ check_skill_updates() {
|
|
|
319
336
|
|
|
320
337
|
local check_output=""
|
|
321
338
|
check_output=$(timeout "$NPX_TIMEOUT" npx -y skills check 2>/dev/null) || {
|
|
339
|
+
log_warn "npx skills check failed or timed out"
|
|
322
340
|
echo "[]"
|
|
323
341
|
return
|
|
324
342
|
}
|
|
@@ -341,7 +359,11 @@ check_skill_updates() {
|
|
|
341
359
|
local name
|
|
342
360
|
name=$(echo "$line" | grep -oE '[a-z][a-z0-9_-]+' | head -1)
|
|
343
361
|
[ -z "$name" ] && continue
|
|
344
|
-
|
|
362
|
+
# Use jq for safe JSON construction (avoids injection from special chars)
|
|
363
|
+
local update_obj
|
|
364
|
+
update_obj=$(jq -n --arg id "$name" --arg cmd "npx skills update $name" \
|
|
365
|
+
'{id: $id, update_available: true, update_cmd: $cmd}')
|
|
366
|
+
updates+=("$update_obj")
|
|
345
367
|
done <<< "$check_output"
|
|
346
368
|
|
|
347
369
|
if [ ${#updates[@]} -eq 0 ]; then
|
|
@@ -484,6 +506,12 @@ main() {
|
|
|
484
506
|
local task="${1:-}"
|
|
485
507
|
local complexity="${2:-medium}"
|
|
486
508
|
|
|
509
|
+
# Check required dependencies
|
|
510
|
+
if ! has_command jq; then
|
|
511
|
+
echo '{"status":"error","message":"jq is required but not installed. Install with: brew install jq (macOS) or apt-get install jq (Linux)","timestamp":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}'
|
|
512
|
+
exit 1
|
|
513
|
+
fi
|
|
514
|
+
|
|
487
515
|
# Validate complexity
|
|
488
516
|
case "$complexity" in
|
|
489
517
|
low|medium|high) ;;
|
|
@@ -506,7 +534,7 @@ main() {
|
|
|
506
534
|
|
|
507
535
|
# --- Fase 2: Cache check ---
|
|
508
536
|
local cache_key
|
|
509
|
-
cache_key=$(echo -n "${task}|${complexity}" | md5 2>/dev/null || echo -n "${task}|${complexity}" | md5sum 2>/dev/null | awk '{print $1}' || echo "
|
|
537
|
+
cache_key=$(echo -n "${task}|${complexity}" | md5 2>/dev/null || echo -n "${task}|${complexity}" | md5sum 2>/dev/null | awk '{print $1}' || echo -n "${task}|${complexity}" | cksum | awk '{print $1}')
|
|
510
538
|
|
|
511
539
|
if is_cache_valid "$cache_key"; then
|
|
512
540
|
log_success "Cache hit"
|
|
@@ -550,8 +578,10 @@ main() {
|
|
|
550
578
|
local output
|
|
551
579
|
output=$(generate_output "$keywords" "$installed" "$elsabro" "$external" "$recommended" "$updates")
|
|
552
580
|
|
|
553
|
-
# Guardar en cache
|
|
554
|
-
save_to_cache "$cache_key" "$output"
|
|
581
|
+
# Guardar en cache (log warning on failure, don't mask exit code)
|
|
582
|
+
if ! save_to_cache "$cache_key" "$output"; then
|
|
583
|
+
log_warn "Failed to save results to cache"
|
|
584
|
+
fi
|
|
555
585
|
|
|
556
586
|
# Output final a STDOUT (esto captura el Flow Engine)
|
|
557
587
|
echo "$output"
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "elsabro",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"description": "Sistema de desarrollo AI-powered para Claude Code - Agent Teams, blocking code review, orquestación avanzada con checkpointing, memoria multi-nivel y flows declarativos",
|
|
3
|
+
"version": "4.2.0",
|
|
4
|
+
"description": "Sistema de desarrollo AI-powered para Claude Code - Next Step Suggestions, Stitch UI Design, Agent Teams, blocking code review, orquestación avanzada con checkpointing, memoria multi-nivel y flows declarativos",
|
|
5
5
|
"bin": {
|
|
6
6
|
"elsabro": "bin/install.js"
|
|
7
7
|
},
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"license": "MIT",
|
|
33
33
|
"repository": {
|
|
34
34
|
"type": "git",
|
|
35
|
-
"url": "git+https://github.com/goat1990/elsabro.git"
|
|
35
|
+
"url": "git+https://github.com/goat1990/elsabro-gsd.git"
|
|
36
36
|
},
|
|
37
|
-
"homepage": "https://github.com/goat1990/elsabro",
|
|
37
|
+
"homepage": "https://github.com/goat1990/elsabro-gsd",
|
|
38
38
|
"bugs": {
|
|
39
|
-
"url": "https://github.com/goat1990/elsabro/issues"
|
|
39
|
+
"url": "https://github.com/goat1990/elsabro-gsd/issues"
|
|
40
40
|
},
|
|
41
41
|
"engines": {
|
|
42
42
|
"node": ">=18.0.0"
|
|
@@ -425,6 +425,15 @@
|
|
|
425
425
|
- API endpoint: `https://add-skill.vercel.sh/check-updates`
|
|
426
426
|
- Comandos: /elsabro:skills search|install|update|list|config
|
|
427
427
|
|
|
428
|
+
### 41. `next-step-engine.md` (v4.2)
|
|
429
|
+
**Propósito**: Decision matrix para sugerencias de siguiente paso
|
|
430
|
+
**Contiene**:
|
|
431
|
+
- Decision Matrix (28 commands mapped to suggested_next)
|
|
432
|
+
- Contextual overrides (blocked tasks, TODOs, verification results)
|
|
433
|
+
- Output template for "Siguiente Paso" blocks
|
|
434
|
+
- Fallback rule (progress as safe default)
|
|
435
|
+
- Integration with persistent mode routing
|
|
436
|
+
|
|
428
437
|
---
|
|
429
438
|
|
|
430
439
|
## ESTADO UNIFICADO
|