elsabro 7.3.0 → 7.3.2

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 CHANGED
@@ -1,15 +1,16 @@
1
- # ELSABRO v7.1.0
1
+ # ELSABRO v7.3.1
2
2
 
3
3
  **Tu asistente AI para crear apps increibles** — Orquestacion de agentes con flow engine, checkpointing y ejecucion paralela.
4
4
 
5
5
  ELSABRO es un sistema de desarrollo AI-powered para Claude Code que te guia paso a paso en la creacion de aplicaciones. Incluye un flow engine con 44 nodos, 12 agentes especializados, un CLI para control de flujo, y un sistema de checkpoints para recuperacion automatica.
6
6
 
7
- ## Que Hay de Nuevo en v7.1.0
7
+ ## Que Hay de Nuevo en v7.3
8
8
 
9
- - **Flow Engine Runtime** — Motor de ejecucion basado en grafos con 9 tipos de nodos, resolución de templates, y checkpointing automatico
10
- - **CLI de 6 comandos** validate, dry-run, init, step, complete, status
9
+ - **Execute.md reescrito** — Reducido 89% (1624 182 lineas) como thin wrapper del flow engine
10
+ - **Observabilidad** 4 senales de telemetria JSONL: node_skip_counter, gate_bypass_detector, execution_time_anomaly, diff_size_vs_review_ratio
11
+ - **Limpieza de codigo fantasma** — Eliminados phantom classes, worktrees inexistentes, 5 secciones duplicadas de Agent Teams
12
+ - **Flow Engine Runtime** — Motor de ejecucion basado en grafos con 9 tipos de nodos, checkpointing automatico
11
13
  - **PartyEngine** — Discusiones multi-agente con seleccion automatica de participantes
12
- - **CallbackProtocol** — Composicion automatica de Agent Teams (minimo 5 miembros)
13
14
  - **279 tests** — Zero dependencias externas, Node 18+
14
15
  - **Design-UI Routing** — Integracion con Stitch AI para generar pantallas desde texto
15
16
 
@@ -245,10 +246,46 @@ O desde Claude Code:
245
246
  /elsabro:update
246
247
  ```
247
248
 
249
+ ## Desinstalacion
250
+
251
+ Si deseas eliminar ELSABRO completamente, puedes hacerlo de dos formas:
252
+
253
+ ### Con el CLI (Recomendado)
254
+
255
+ ```bash
256
+ # Desinstalar instalacion global
257
+ npx elsabro --global --uninstall
258
+
259
+ # Desinstalar instalacion local
260
+ npx elsabro --local --uninstall
261
+ ```
262
+
263
+ Esto elimina todos los componentes del framework: comandos, agentes, skills, templates, workflows, referencias, flows y el flow engine.
264
+
265
+ ### Manual
266
+
267
+ Si prefieres eliminar los archivos manualmente:
268
+
269
+ ```bash
270
+ # 1. Eliminar componentes del framework (instalacion global)
271
+ cd ~/.claude
272
+ rm -rf commands/elsabro agents skills templates workflows references flows flow-engine
273
+
274
+ # 2. Eliminar datos de sesion del proyecto (desde la raiz de tu proyecto)
275
+ rm -rf .elsabro .planning
276
+
277
+ # 3. Limpiar la entrada de ELSABRO en settings.json (opcional)
278
+ # Editar ~/.claude/settings.json y eliminar la clave "elsabro"
279
+ ```
280
+
281
+ > **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.
282
+
248
283
  ## Version History
249
284
 
250
285
  | Version | Milestone | Cambio Principal |
251
286
  |---------|-----------|-----------------|
287
+ | 7.3.1 | M7 | Documentacion de desinstalacion en README |
288
+ | 7.3.0 | M7 | Execute.md reescrito (-89%), observabilidad JSONL, limpieza codigo fantasma |
252
289
  | 7.1.0 | M5 + M6 | AT compliance, design-ui routing, 279 failure mode tests |
253
290
  | 7.0.0 | M4 | Feature completeness — 42/42 nodos, CLI, 257 tests |
254
291
  | 6.0.0 | M3 | Flow engine runtime — 6 source files, 109 tests |
@@ -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
- node flow-engine/src/cli.js init --flow "$FLOW" --task "$TASK" --profile "$PROFILE"
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
- if (step.action === "bash") Bash(step.command)
132
- if (step.action === "agent") Task({ subagent_type: step.agent, prompt: step.inputs })
133
- if (step.action === "read_files") Read(step.files)
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
- // result: { steps: stepOutputs }
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
- ¡Hola! Veo que ya tienes un proyecto con [tech_stack].
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
- ¿Qué te gustaría hacer?
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
- 1) Agregar una nueva funcionalidad
222
- 2) 🐛 Arreglar algo que no funciona
223
- 3) 📖 Entender cómo funciona el código
224
- 4) 🔄 Refactorizar o mejorar algo
225
- 5) 🎨 Diseñar o rediseñar la interfaz
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
- mkdir -p .elsabro
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 de estado. ¿Puedes verificar los permisos?
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
- Mientras tanto, puedo ayudarte sin guardar estado, pero no podré
523
- recordar el progreso entre sesiones.
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
- ¿Continuar de todas formas?
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
  ```