elsabro 7.3.0 → 7.3.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.
- package/README.md +34 -0
- package/agents/elsabro-orchestrator.md +2 -0
- package/commands/elsabro/execute.md +322 -5
- package/commands/elsabro/quick.md +11 -11
- package/commands/elsabro/start.md +156 -14
- package/flow-engine/src/callbacks.js +79 -0
- package/flow-engine/src/checkpoint.js +41 -0
- package/flow-engine/src/cli.js +214 -6
- package/flow-engine/src/executors.js +134 -3
- package/flow-engine/src/graph.js +30 -2
- package/flow-engine/src/template.js +152 -72
- package/flow-engine/tests/checkpoint.test.js +476 -0
- package/flow-engine/tests/execute-dispatcher.test.js +738 -0
- package/flow-engine/tests/executors-complex.test.js +259 -0
- package/flow-engine/tests/graph.test.js +193 -0
- package/flow-engine/tests/skill-install.test.js +254 -0
- package/flow-engine/tests/validation.test.js +137 -0
- package/flows/development-flow.json +3 -3
- package/hooks/skill-discovery.sh +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -245,6 +245,40 @@ O desde Claude Code:
|
|
|
245
245
|
/elsabro:update
|
|
246
246
|
```
|
|
247
247
|
|
|
248
|
+
## Desinstalacion
|
|
249
|
+
|
|
250
|
+
Si deseas eliminar ELSABRO completamente, puedes hacerlo de dos formas:
|
|
251
|
+
|
|
252
|
+
### Con el CLI (Recomendado)
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
# Desinstalar instalacion global
|
|
256
|
+
npx elsabro --global --uninstall
|
|
257
|
+
|
|
258
|
+
# Desinstalar instalacion local
|
|
259
|
+
npx elsabro --local --uninstall
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Esto elimina todos los componentes del framework: comandos, agentes, skills, templates, workflows, referencias, flows y el flow engine.
|
|
263
|
+
|
|
264
|
+
### Manual
|
|
265
|
+
|
|
266
|
+
Si prefieres eliminar los archivos manualmente:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
# 1. Eliminar componentes del framework (instalacion global)
|
|
270
|
+
cd ~/.claude
|
|
271
|
+
rm -rf commands/elsabro agents skills templates workflows references flows flow-engine
|
|
272
|
+
|
|
273
|
+
# 2. Eliminar datos de sesion del proyecto (desde la raiz de tu proyecto)
|
|
274
|
+
rm -rf .elsabro .planning
|
|
275
|
+
|
|
276
|
+
# 3. Limpiar la entrada de ELSABRO en settings.json (opcional)
|
|
277
|
+
# Editar ~/.claude/settings.json y eliminar la clave "elsabro"
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
> **Nota:** Los directorios `.elsabro/` y `.planning/` contienen el estado de sesion, checkpoints y planes de tu proyecto. El CLI no los elimina automaticamente porque son datos de tu trabajo. Revisalos antes de borrarlos si tienes informacion que quieras conservar.
|
|
281
|
+
|
|
248
282
|
## Version History
|
|
249
283
|
|
|
250
284
|
| Version | Milestone | Cambio Principal |
|
|
@@ -387,6 +387,8 @@ Unlike parallel execution (waves), Party Mode is **sequential deliberation**:
|
|
|
387
387
|
<agent_teams>
|
|
388
388
|
## Agent Teams Integration (v4.2.0 — Mandatory)
|
|
389
389
|
|
|
390
|
+
**CLARIFICATION**: The orchestrator agent uses Agent Teams (TeamCreate, TeamDelete, SendMessage) for parallel execution coordination. This is **NOT related** to the elsabro-scrum-master agent, which is a separate utility agent for sprint planning and process management.
|
|
391
|
+
|
|
390
392
|
### Cuándo Usar Agent Teams
|
|
391
393
|
|
|
392
394
|
Como orquestador, Agent Teams es **OBLIGATORIO** para todo trabajo paralelo con 2+ agentes (Rule 8):
|
|
@@ -38,12 +38,99 @@ Verificar flujo en progreso. Actualizar phase a "stepping".
|
|
|
38
38
|
FLOW="flows/development-flow.json"
|
|
39
39
|
TASK="[descripcion de la tarea del usuario]"
|
|
40
40
|
PROFILE="[default|yolo|careful|teams|bmad]"
|
|
41
|
+
COMPLEXITY="[low|medium|high]"
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
# Initialize flow and create checkpoint
|
|
44
|
+
node flow-engine/src/cli.js init --flow "$FLOW" --task "$TASK" --profile "$PROFILE" --complexity "$COMPLEXITY"
|
|
43
45
|
```
|
|
44
46
|
|
|
45
47
|
Guardar el flowId del resultado para todos los comandos siguientes.
|
|
46
48
|
|
|
49
|
+
**Note on Skill Auto-Install:**
|
|
50
|
+
The development-flow.json includes a `skill_discovery` node (second node after entry) that automatically:
|
|
51
|
+
1. Calls `hooks/skill-discovery.sh` with task description and complexity
|
|
52
|
+
2. Discovers installed skills and recommends relevant ones from the Vercel Agent Skills Registry
|
|
53
|
+
3. Provides install commands for recommended skills
|
|
54
|
+
4. All discovery output is captured and available in `context.steps.discovery`
|
|
55
|
+
|
|
56
|
+
## 1.1. Stitch MCP Auto-Detection (Visual Projects)
|
|
57
|
+
|
|
58
|
+
**AFTER initialization**, check if this is a visual project and suggest Stitch MCP if UI work is needed:
|
|
59
|
+
|
|
60
|
+
```javascript
|
|
61
|
+
// Detectar frameworks visuales y verificar si Stitch está configurado
|
|
62
|
+
let isVisualProject = false;
|
|
63
|
+
let detectedFrameworks = [];
|
|
64
|
+
let hasStitchDesigns = false;
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const packageJson = Read("package.json");
|
|
68
|
+
const pkg = JSON.parse(packageJson);
|
|
69
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
70
|
+
|
|
71
|
+
// Frameworks visuales
|
|
72
|
+
const visualFrameworks = [
|
|
73
|
+
{ name: "react", display: "React" },
|
|
74
|
+
{ name: "next", display: "Next.js" },
|
|
75
|
+
{ name: "vue", display: "Vue" },
|
|
76
|
+
{ name: "nuxt", display: "Nuxt" },
|
|
77
|
+
{ name: "@angular/core", display: "Angular" },
|
|
78
|
+
{ name: "svelte", display: "Svelte" }
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
for (const framework of visualFrameworks) {
|
|
82
|
+
if (allDeps[framework.name]) {
|
|
83
|
+
detectedFrameworks.push(framework.display);
|
|
84
|
+
isVisualProject = true;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Verificar si ya hay diseños de Stitch
|
|
89
|
+
try {
|
|
90
|
+
const stitchDesigns = Read(".planning/ui-designs/UI-DESIGNS.md");
|
|
91
|
+
hasStitchDesigns = !!stitchDesigns;
|
|
92
|
+
} catch (e) {
|
|
93
|
+
hasStitchDesigns = false;
|
|
94
|
+
}
|
|
95
|
+
} catch (e) {
|
|
96
|
+
// No package.json - continue without detection
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Si es proyecto visual sin diseños, sugerir Stitch MCP
|
|
100
|
+
if (isVisualProject && !hasStitchDesigns) {
|
|
101
|
+
state.context = state.context || {};
|
|
102
|
+
state.context.is_visual_project = true;
|
|
103
|
+
state.context.visual_frameworks = detectedFrameworks;
|
|
104
|
+
|
|
105
|
+
// Actualizar estado
|
|
106
|
+
Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
107
|
+
|
|
108
|
+
// Sugerir al usuario
|
|
109
|
+
output(`\n🎨 Proyecto ${detectedFrameworks.join("/")} detectado`);
|
|
110
|
+
output(`💡 Tip: Usa /elsabro:design-ui para generar diseños visuales con Stitch MCP antes de implementar\n`);
|
|
111
|
+
|
|
112
|
+
// Preguntar si quieren diseñar primero
|
|
113
|
+
const answer = AskUserQuestion({
|
|
114
|
+
questions: [{
|
|
115
|
+
question: "¿Quieres diseñar la UI visualmente antes de implementar?",
|
|
116
|
+
header: "Stitch MCP",
|
|
117
|
+
options: [
|
|
118
|
+
{ label: "Continuar con ejecución", description: "Implementar sin diseño visual previo" },
|
|
119
|
+
{ label: "Diseñar UI primero (Recomendado)", description: "Genera screens con Stitch MCP antes de codificar" }
|
|
120
|
+
],
|
|
121
|
+
multiSelect: false
|
|
122
|
+
}]
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (answer.includes("Diseñar UI primero")) {
|
|
126
|
+
// Interrumpir execute y redirigir a design-ui
|
|
127
|
+
output("→ Redirigiendo a /elsabro:design-ui...\n");
|
|
128
|
+
Skill("elsabro:design-ui", TASK);
|
|
129
|
+
return; // Exit execute
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
47
134
|
## 2. Loop Principal
|
|
48
135
|
|
|
49
136
|
Ejecutar el siguiente loop hasta que `step` retorne `{ finished: true }`:
|
|
@@ -67,8 +154,125 @@ Ejecutar el siguiente loop hasta que `step` retorne `{ finished: true }`:
|
|
|
67
154
|
|
|
68
155
|
6. Volver al paso 1.
|
|
69
156
|
|
|
157
|
+
## 2.1. Error Handling: Runtime Status Validation
|
|
158
|
+
|
|
159
|
+
The flow engine validates all nodes before execution via `checkRuntimeStatus()`. This catches:
|
|
160
|
+
|
|
161
|
+
### NotImplementedError
|
|
162
|
+
Thrown when a node has `runtime_status: "not_implemented"`:
|
|
163
|
+
```javascript
|
|
164
|
+
{
|
|
165
|
+
"id": "bmad_solution",
|
|
166
|
+
"type": "sequence",
|
|
167
|
+
"runtime_status": "not_implemented",
|
|
168
|
+
"gaps": ["BMAD solutioning logic not yet implemented", "Needs ADR generation"]
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Error format:**
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"error": "NotImplementedError",
|
|
176
|
+
"nodeId": "bmad_solution",
|
|
177
|
+
"message": "Node \"bmad_solution\" is not yet implemented. Gaps: BMAD solutioning logic not yet implemented; Needs ADR generation",
|
|
178
|
+
"gaps": ["BMAD solutioning logic not yet implemented", "Needs ADR generation"]
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Dispatcher action:**
|
|
183
|
+
- STOP execution immediately
|
|
184
|
+
- Display error to user with gap details
|
|
185
|
+
- Offer options:
|
|
186
|
+
1. "Skip this node" (only if node has `optional: true`)
|
|
187
|
+
2. "Abort workflow"
|
|
188
|
+
3. "Report bug" (opens issue template with node info)
|
|
189
|
+
|
|
190
|
+
### DeprecatedNodeError
|
|
191
|
+
Thrown when a node has `runtime_status: "deprecated"`:
|
|
192
|
+
```javascript
|
|
193
|
+
{
|
|
194
|
+
"id": "legacy_team_node",
|
|
195
|
+
"type": "team",
|
|
196
|
+
"runtime_status": "deprecated",
|
|
197
|
+
"deprecated_reason": "Use parallel nodes with Agent Teams instead (Rule 8)"
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Error format:**
|
|
202
|
+
```json
|
|
203
|
+
{
|
|
204
|
+
"error": "DeprecatedNodeError",
|
|
205
|
+
"nodeId": "legacy_team_node",
|
|
206
|
+
"message": "Node \"legacy_team_node\" is deprecated: Use parallel nodes with Agent Teams instead (Rule 8)",
|
|
207
|
+
"reason": "Use parallel nodes with Agent Teams instead (Rule 8)"
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Dispatcher action:**
|
|
212
|
+
- STOP execution immediately
|
|
213
|
+
- Display deprecation warning with reason
|
|
214
|
+
- Suggest migration path if available in error
|
|
215
|
+
- Offer "Abort workflow"
|
|
216
|
+
|
|
217
|
+
### When to Continue vs Abort
|
|
218
|
+
|
|
219
|
+
**Continue (skip node) only if:**
|
|
220
|
+
- Node has `optional: true` explicitly set
|
|
221
|
+
- AND user explicitly chooses "Skip" option
|
|
222
|
+
|
|
223
|
+
**Always abort for:**
|
|
224
|
+
- Deprecated nodes (never optional)
|
|
225
|
+
- Not-implemented nodes without `optional: true`
|
|
226
|
+
- Any runtime status error in critical path nodes (entry, exit, condition, router)
|
|
227
|
+
|
|
228
|
+
### Telemetry Signal
|
|
229
|
+
|
|
230
|
+
When runtime status errors occur, emit:
|
|
231
|
+
```json
|
|
232
|
+
{
|
|
233
|
+
"ts": "2025-02-09T12:34:56Z",
|
|
234
|
+
"signal": "runtime_status_error",
|
|
235
|
+
"nodeId": "bmad_solution",
|
|
236
|
+
"error": "NotImplementedError",
|
|
237
|
+
"gaps": ["..."],
|
|
238
|
+
"action": "abort"
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
70
242
|
## 3. Dispatch por Tipo de Instruccion
|
|
71
243
|
|
|
244
|
+
### type: "entry"
|
|
245
|
+
```javascript
|
|
246
|
+
// instruction: { type: "entry", nodeId, next }
|
|
247
|
+
// Entry nodes are auto-resolved by the engine - no dispatcher action needed
|
|
248
|
+
// The engine automatically transitions to the next node
|
|
249
|
+
// result: { next: instruction.next }
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### type: "exit"
|
|
253
|
+
```javascript
|
|
254
|
+
// instruction: { type: "exit", nodeId, outputs, status }
|
|
255
|
+
// Exit nodes are auto-resolved by the engine - no dispatcher action needed
|
|
256
|
+
// The engine resolves output templates and terminates the flow
|
|
257
|
+
// result: { outputs: resolvedOutputs, status: instruction.status || "completed" }
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### type: "condition"
|
|
261
|
+
```javascript
|
|
262
|
+
// instruction: { type: "condition", nodeId, conditionResult, next }
|
|
263
|
+
// Condition nodes are auto-resolved by the engine - no dispatcher action needed
|
|
264
|
+
// The engine evaluates the condition and determines the next node (true/false branch)
|
|
265
|
+
// result: { next: instruction.next, conditionResult: instruction.conditionResult }
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### type: "router"
|
|
269
|
+
```javascript
|
|
270
|
+
// instruction: { type: "router", nodeId, routeKey, next }
|
|
271
|
+
// Router nodes are auto-resolved by the engine - no dispatcher action needed
|
|
272
|
+
// The engine evaluates the condition, matches against routes, and determines next node
|
|
273
|
+
// result: { next: instruction.next, routeKey: instruction.routeKey }
|
|
274
|
+
```
|
|
275
|
+
|
|
72
276
|
### type: "agent"
|
|
73
277
|
```javascript
|
|
74
278
|
// instruction: { type: "agent", nodeId, agent, model, inputs }
|
|
@@ -115,6 +319,45 @@ TeamDelete()
|
|
|
115
319
|
// result: team outputs aggregated
|
|
116
320
|
```
|
|
117
321
|
|
|
322
|
+
### type: "team"
|
|
323
|
+
```javascript
|
|
324
|
+
// instruction: { type: "team", nodeId, team, coordinator, workers }
|
|
325
|
+
// Dedicated team node with coordinator-worker pattern
|
|
326
|
+
|
|
327
|
+
TeamCreate({ team_name: instruction.team.name, description: instruction.team.description })
|
|
328
|
+
|
|
329
|
+
// Spawn coordinator
|
|
330
|
+
Task({
|
|
331
|
+
subagent_type: instruction.coordinator.agent,
|
|
332
|
+
model: instruction.coordinator.model,
|
|
333
|
+
team_name: instruction.team.name,
|
|
334
|
+
name: instruction.coordinator.name,
|
|
335
|
+
prompt: `Role: Coordinator. ${instruction.coordinator.instructions}`
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
// Spawn workers
|
|
339
|
+
for (const worker of instruction.workers) {
|
|
340
|
+
Task({
|
|
341
|
+
subagent_type: worker.agent,
|
|
342
|
+
model: worker.model,
|
|
343
|
+
team_name: instruction.team.name,
|
|
344
|
+
name: worker.name,
|
|
345
|
+
prompt: `Role: ${worker.role}. ${worker.instructions}`
|
|
346
|
+
})
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Coordinator sends messages to workers via SendMessage
|
|
350
|
+
// (handled internally by the agent - dispatcher just spawns them)
|
|
351
|
+
|
|
352
|
+
// Al completar:
|
|
353
|
+
SendMessage({ type: "shutdown_request", recipient: instruction.coordinator.name, content: "Done" })
|
|
354
|
+
for (const worker of instruction.workers) {
|
|
355
|
+
SendMessage({ type: "shutdown_request", recipient: worker.name, content: "Done" })
|
|
356
|
+
}
|
|
357
|
+
TeamDelete()
|
|
358
|
+
// result: { coordinatorOutput, workerOutputs }
|
|
359
|
+
```
|
|
360
|
+
|
|
118
361
|
### type: "interrupt"
|
|
119
362
|
```javascript
|
|
120
363
|
// instruction: { type: "interrupt", nodeId, display, routes }
|
|
@@ -127,12 +370,86 @@ const answer = AskUserQuestion({
|
|
|
127
370
|
### type: "sequence"
|
|
128
371
|
```javascript
|
|
129
372
|
// instruction: { type: "sequence", nodeId, steps }
|
|
373
|
+
// Execute each step sequentially, capturing outputs
|
|
374
|
+
|
|
375
|
+
const stepOutputs = {};
|
|
376
|
+
const context = getCurrentContext(); // From checkpoint
|
|
377
|
+
|
|
130
378
|
for (const step of instruction.steps) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
379
|
+
const stepLabel = step.as || step.action || 'unnamed';
|
|
380
|
+
|
|
381
|
+
switch (step.action) {
|
|
382
|
+
case 'bash': {
|
|
383
|
+
// Resolve template variables in command (e.g., {{inputs.task}})
|
|
384
|
+
const resolvedCommand = resolveTemplate(step.command, context);
|
|
385
|
+
|
|
386
|
+
// Execute bash command with timeout
|
|
387
|
+
const bashResult = Bash({
|
|
388
|
+
command: resolvedCommand,
|
|
389
|
+
timeout: step.timeout || 120000,
|
|
390
|
+
description: `Execute ${stepLabel} - ${step.action}`
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
stepOutputs[stepLabel] = {
|
|
394
|
+
output: bashResult.stdout,
|
|
395
|
+
exitCode: bashResult.exitCode || 0,
|
|
396
|
+
stderr: bashResult.stderr || ''
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
// Special handling for skill-discovery: parse JSON output
|
|
400
|
+
if (resolvedCommand.includes('skill-discovery.sh')) {
|
|
401
|
+
try {
|
|
402
|
+
const discoveryData = JSON.parse(bashResult.stdout);
|
|
403
|
+
stepOutputs[stepLabel].discovered = discoveryData.discovered || [];
|
|
404
|
+
stepOutputs[stepLabel].recommended = discoveryData.recommended || {};
|
|
405
|
+
|
|
406
|
+
// Log skill discovery execution
|
|
407
|
+
logInfo(`Skill discovery found ${discoveryData.discovered?.length || 0} installed skills`);
|
|
408
|
+
if (discoveryData.recommended?.skills?.length > 0) {
|
|
409
|
+
logInfo(`Recommended skills: ${discoveryData.recommended.skills.join(', ')}`);
|
|
410
|
+
}
|
|
411
|
+
} catch (e) {
|
|
412
|
+
logWarn(`Failed to parse skill-discovery output: ${e.message}`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
case 'agent': {
|
|
419
|
+
const resolvedInputs = resolveTemplate(step.inputs || {}, context);
|
|
420
|
+
const agentResult = Task({
|
|
421
|
+
subagent_type: step.agent,
|
|
422
|
+
model: step.model || 'sonnet',
|
|
423
|
+
prompt: JSON.stringify(resolvedInputs)
|
|
424
|
+
});
|
|
425
|
+
stepOutputs[stepLabel] = { result: agentResult };
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
case 'read_files': {
|
|
430
|
+
const files = resolveTemplate(step.files, context);
|
|
431
|
+
const fileContents = [];
|
|
432
|
+
for (const filePath of files) {
|
|
433
|
+
try {
|
|
434
|
+
const content = Read({ file_path: filePath });
|
|
435
|
+
fileContents.push({ path: filePath, content });
|
|
436
|
+
} catch (e) {
|
|
437
|
+
if (!step.optional) throw e;
|
|
438
|
+
fileContents.push({ path: filePath, content: null, error: e.message });
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
stepOutputs[stepLabel] = { content: fileContents };
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
default:
|
|
446
|
+
// Unimplemented step actions: log warning
|
|
447
|
+
logWarn(`Step action "${step.action}" not implemented in dispatcher, skipping`);
|
|
448
|
+
stepOutputs[stepLabel] = { skipped: true, reason: `Action not implemented` };
|
|
449
|
+
}
|
|
134
450
|
}
|
|
135
|
-
|
|
451
|
+
|
|
452
|
+
// result: stepOutputs with all step execution results
|
|
136
453
|
```
|
|
137
454
|
|
|
138
455
|
## 4. Observabilidad (4 Senales)
|
|
@@ -169,17 +169,6 @@ Task({
|
|
|
169
169
|
})
|
|
170
170
|
```
|
|
171
171
|
|
|
172
|
-
## Output
|
|
173
|
-
|
|
174
|
-
```
|
|
175
|
-
✓ Quick complete
|
|
176
|
-
Changed: src/components/RegisterForm.tsx
|
|
177
|
-
Added: src/components/__tests__/RegisterForm.email.test.ts
|
|
178
|
-
Tests: 1 passed
|
|
179
|
-
|
|
180
|
-
Commit? (y/n/edit message)
|
|
181
|
-
```
|
|
182
|
-
|
|
183
172
|
<code_review_gate>
|
|
184
173
|
## Code Review Gate (OBLIGATORIO - NO NEGOCIABLE)
|
|
185
174
|
|
|
@@ -203,6 +192,17 @@ Task({
|
|
|
203
192
|
**VIOLACIÓN CRÍTICA**: Reportar resultado sin code review = ABORTAR OPERACIÓN
|
|
204
193
|
</code_review_gate>
|
|
205
194
|
|
|
195
|
+
## Output
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
✓ Quick complete
|
|
199
|
+
Changed: src/components/RegisterForm.tsx
|
|
200
|
+
Added: src/components/__tests__/RegisterForm.email.test.ts
|
|
201
|
+
Tests: 1 passed
|
|
202
|
+
|
|
203
|
+
Commit? (y/n/edit message)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
206
|
<siguiente_paso>
|
|
207
207
|
## Siguiente Paso
|
|
208
208
|
|
|
@@ -72,6 +72,62 @@ Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
|
72
72
|
Los skills descubiertos se pasan al comando siguiente (plan, execute, etc.) para informar patrones y configuraciones.
|
|
73
73
|
</skill_discovery>
|
|
74
74
|
|
|
75
|
+
<stitch_mcp_detection>
|
|
76
|
+
## Stitch MCP Auto-Detection (Visual Projects)
|
|
77
|
+
|
|
78
|
+
**DESPUÉS de skill discovery**, detectar si el proyecto usa frameworks visuales y sugerir Stitch MCP:
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
// Detectar frameworks visuales en package.json
|
|
82
|
+
let isVisualProject = false;
|
|
83
|
+
let detectedFrameworks = [];
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const packageJson = Read("package.json");
|
|
87
|
+
const pkg = JSON.parse(packageJson);
|
|
88
|
+
const allDeps = {
|
|
89
|
+
...pkg.dependencies,
|
|
90
|
+
...pkg.devDependencies
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// Frameworks visuales a detectar
|
|
94
|
+
const visualFrameworks = [
|
|
95
|
+
{ name: "react", display: "React" },
|
|
96
|
+
{ name: "next", display: "Next.js" },
|
|
97
|
+
{ name: "vue", display: "Vue" },
|
|
98
|
+
{ name: "nuxt", display: "Nuxt" },
|
|
99
|
+
{ name: "@angular/core", display: "Angular" },
|
|
100
|
+
{ name: "svelte", display: "Svelte" }
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
for (const framework of visualFrameworks) {
|
|
104
|
+
if (allDeps[framework.name]) {
|
|
105
|
+
detectedFrameworks.push(framework.display);
|
|
106
|
+
isVisualProject = true;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} catch (e) {
|
|
110
|
+
// No package.json or parse error - not a visual project
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Guardar en estado
|
|
114
|
+
if (isVisualProject) {
|
|
115
|
+
state.context = state.context || {};
|
|
116
|
+
state.context.is_visual_project = true;
|
|
117
|
+
state.context.visual_frameworks = detectedFrameworks;
|
|
118
|
+
state.context.stitch_available = true; // Flag para sugerir Stitch MCP
|
|
119
|
+
|
|
120
|
+
Write(".elsabro/state.json", JSON.stringify(state, null, 2));
|
|
121
|
+
|
|
122
|
+
// Informar al usuario
|
|
123
|
+
output(`🎨 Proyecto visual detectado: ${detectedFrameworks.join(", ")}`);
|
|
124
|
+
output(`💡 Stitch MCP disponible para diseño de UI`);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Integración con greeting**: Si `state.context.stitch_available === true`, agregar opción de diseño UI prominentemente en el saludo.
|
|
129
|
+
</stitch_mcp_detection>
|
|
130
|
+
|
|
75
131
|
<objective>
|
|
76
132
|
Eres el **orquestador principal** de ELSABRO. Tu trabajo es:
|
|
77
133
|
|
|
@@ -110,6 +166,47 @@ Si existe, leerlo:
|
|
|
110
166
|
Read(".elsabro/state.json")
|
|
111
167
|
```
|
|
112
168
|
|
|
169
|
+
### 0.2.1 Verificar permisos de .elsabro/
|
|
170
|
+
|
|
171
|
+
**OBLIGATORIO**: Antes de continuar, verificar permisos de lectura/escritura:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
# Si .elsabro/ existe, verificar permisos
|
|
175
|
+
if [ -d .elsabro ]; then
|
|
176
|
+
# Intentar crear archivo de prueba
|
|
177
|
+
touch .elsabro/.permission_test 2>/dev/null
|
|
178
|
+
if [ $? -ne 0 ]; then
|
|
179
|
+
echo "ERROR: No hay permisos de escritura en .elsabro/"
|
|
180
|
+
exit 1
|
|
181
|
+
fi
|
|
182
|
+
rm -f .elsabro/.permission_test
|
|
183
|
+
|
|
184
|
+
# Verificar permisos de lectura si existe state.json
|
|
185
|
+
if [ -f .elsabro/state.json ]; then
|
|
186
|
+
cat .elsabro/state.json >/dev/null 2>&1
|
|
187
|
+
if [ $? -ne 0 ]; then
|
|
188
|
+
echo "ERROR: No hay permisos de lectura en .elsabro/state.json"
|
|
189
|
+
exit 1
|
|
190
|
+
fi
|
|
191
|
+
fi
|
|
192
|
+
fi
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Si hay error de permisos, informar al usuario:
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
⚠️ No puedo acceder a la carpeta .elsabro/ por falta de permisos.
|
|
199
|
+
|
|
200
|
+
Opciones:
|
|
201
|
+
1) Corregir permisos: chmod 755 .elsabro
|
|
202
|
+
2) Continuar sin guardar estado (no recomendado)
|
|
203
|
+
3) Salir y revisar el problema
|
|
204
|
+
|
|
205
|
+
¿Qué prefieres?
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Usar `AskUserQuestion` con estas opciones.
|
|
209
|
+
|
|
113
210
|
### 0.3 Detectar contexto con agentes paralelos
|
|
114
211
|
|
|
115
212
|
**OBLIGATORIO**: Lanzar 2 agentes HAIKU en paralelo:
|
|
@@ -213,17 +310,33 @@ Veo que estás en una carpeta vacía. ¿Qué te gustaría crear?
|
|
|
213
310
|
```
|
|
214
311
|
|
|
215
312
|
### Para Brownfield (código existente):
|
|
216
|
-
```
|
|
217
|
-
|
|
313
|
+
```javascript
|
|
314
|
+
// Personalizar saludo basado en detección de framework visual
|
|
315
|
+
const greeting = `¡Hola! Veo que ya tienes un proyecto con ${tech_stack.join(", ")}.`;
|
|
316
|
+
const options = [
|
|
317
|
+
{ label: "➕ Agregar una nueva funcionalidad", value: "add_feature" },
|
|
318
|
+
{ label: "🐛 Arreglar algo que no funciona", value: "debug" },
|
|
319
|
+
{ label: "📖 Entender cómo funciona el código", value: "understand" },
|
|
320
|
+
{ label: "🔄 Refactorizar o mejorar algo", value: "refactor" }
|
|
321
|
+
];
|
|
218
322
|
|
|
219
|
-
|
|
323
|
+
// Si es proyecto visual, promover opción de Stitch MCP
|
|
324
|
+
if (state.context.is_visual_project) {
|
|
325
|
+
options.splice(1, 0, {
|
|
326
|
+
label: `🎨 Diseñar UI con Stitch MCP (${state.context.visual_frameworks.join("/")})`,
|
|
327
|
+
value: "design_ui",
|
|
328
|
+
description: "Genera screens visuales profesionales desde descripciones"
|
|
329
|
+
});
|
|
330
|
+
output(`${greeting}\n\n🎨 Detectado: ${state.context.visual_frameworks.join(", ")} - Stitch MCP disponible para diseño visual\n`);
|
|
331
|
+
} else {
|
|
332
|
+
options.push({ label: "🎨 Diseñar o rediseñar la interfaz", value: "design_ui" });
|
|
333
|
+
}
|
|
220
334
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
6) ❓ Otra cosa (cuéntame)
|
|
335
|
+
options.push({ label: "❓ Otra cosa (cuéntame)", value: "other" });
|
|
336
|
+
|
|
337
|
+
// Mostrar opciones
|
|
338
|
+
output("¿Qué te gustaría hacer?\n");
|
|
339
|
+
options.forEach((opt, i) => output(`${i + 1}) ${opt.label}`));
|
|
227
340
|
```
|
|
228
341
|
|
|
229
342
|
### Para Continuation (proyecto ELSABRO existente):
|
|
@@ -409,7 +522,22 @@ Al final de cada respuesta, agregar:
|
|
|
409
522
|
### Crear .elsabro/ si no existe
|
|
410
523
|
|
|
411
524
|
```bash
|
|
412
|
-
|
|
525
|
+
# Crear directorio con verificación de permisos
|
|
526
|
+
mkdir -p .elsabro 2>/dev/null
|
|
527
|
+
|
|
528
|
+
# Verificar que se creó correctamente
|
|
529
|
+
if [ ! -d .elsabro ]; then
|
|
530
|
+
echo "ERROR: No se pudo crear .elsabro/ - verifica permisos del directorio actual"
|
|
531
|
+
exit 1
|
|
532
|
+
fi
|
|
533
|
+
|
|
534
|
+
# Verificar permisos de escritura
|
|
535
|
+
touch .elsabro/.permission_test 2>/dev/null
|
|
536
|
+
if [ $? -ne 0 ]; then
|
|
537
|
+
echo "ERROR: .elsabro/ existe pero no tiene permisos de escritura"
|
|
538
|
+
exit 1
|
|
539
|
+
fi
|
|
540
|
+
rm -f .elsabro/.permission_test
|
|
413
541
|
```
|
|
414
542
|
|
|
415
543
|
### Estructura de .elsabro/
|
|
@@ -517,14 +645,28 @@ Y actualizar automáticamente `.planning/mistakes.md` o `.planning/patterns.md`.
|
|
|
517
645
|
### Si no hay permisos para crear .elsabro/
|
|
518
646
|
|
|
519
647
|
```
|
|
520
|
-
No puedo crear la carpeta
|
|
648
|
+
⚠️ No puedo crear o acceder a la carpeta .elsabro/ por permisos insuficientes.
|
|
649
|
+
|
|
650
|
+
Diagnóstico:
|
|
651
|
+
- Directorio actual: [pwd]
|
|
652
|
+
- Permisos del directorio: [ls -ld .]
|
|
653
|
+
- Usuario actual: [whoami]
|
|
521
654
|
|
|
522
|
-
|
|
523
|
-
|
|
655
|
+
Soluciones:
|
|
656
|
+
1) Corregir permisos: chmod 755 .elsabro (si existe)
|
|
657
|
+
2) Cambiar propietario: sudo chown $(whoami) .elsabro (si es necesario)
|
|
658
|
+
3) Continuar sin guardar estado (no recordaré progreso entre sesiones)
|
|
524
659
|
|
|
525
|
-
¿
|
|
660
|
+
¿Qué prefieres hacer?
|
|
526
661
|
```
|
|
527
662
|
|
|
663
|
+
Usar `AskUserQuestion` para elegir solución.
|
|
664
|
+
|
|
665
|
+
Si elige continuar sin estado:
|
|
666
|
+
- Establecer flag `state.no_persistence = true`
|
|
667
|
+
- Advertir en cada transición que no se guardará progreso
|
|
668
|
+
- Sugerir corregir permisos al finalizar
|
|
669
|
+
|
|
528
670
|
### Si state.json está corrupto
|
|
529
671
|
|
|
530
672
|
```
|