agent-mp 0.5.31 → 0.5.34

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.
@@ -855,6 +855,9 @@ function cmdHelp(fi) {
855
855
  { key: '/run rev <id>', value: 'Run only reviewer' },
856
856
  { key: '/run explorer [task]', value: 'Run only explorer' },
857
857
  { key: '/explorer [task]', value: 'Run explorer (shortcut)' },
858
+ { key: '/orch <feature-id>', value: 'Lanza SOLO orchestrator — genera plan.json' },
859
+ { key: '/impl <task-id>', value: 'Lanza SOLO implementor — ejecuta plan existente' },
860
+ { key: '/rev <task-id>', value: 'Lanza SOLO reviewer — valida implementación' },
858
861
  { key: '/model', value: 'Cambiar modelo del coordinador' },
859
862
  { key: '/provider', value: 'Cambiar proveedor activo (Gemini / Qwen)' },
860
863
  { key: '/login', value: 'Configurar API key (Qwen o Gemini)' },
@@ -1196,6 +1199,59 @@ export async function runRepl(resumeSession) {
1196
1199
  });
1197
1200
  break;
1198
1201
  }
1202
+ // Comandos cortos para lanzar roles directamente
1203
+ case 'orch': {
1204
+ // /orch <feature-id> — lanza solo orchestrator sin preguntas
1205
+ const featureId = args.join(' ').trim();
1206
+ if (!featureId) {
1207
+ fi.println(chalk.red(' Uso: /orch <feature-id>'));
1208
+ break;
1209
+ }
1210
+ await withEngine(async (engine) => {
1211
+ const result = await engine.runOrchestrator(`.agent/docs/${featureId}`, undefined, featureId);
1212
+ fi.println(chalk.green(` ✓ Task ID: ${result.taskId}`));
1213
+ fi.println(chalk.green(` ✓ Plan: .agent/tasks/${result.taskId}/plan.json`));
1214
+ });
1215
+ break;
1216
+ }
1217
+ case 'impl': {
1218
+ // /impl <task-id> — lanza solo implementor con plan existente
1219
+ const taskId = args.join(' ').trim();
1220
+ if (!taskId) {
1221
+ fi.println(chalk.red(' Uso: /impl <task-id>'));
1222
+ break;
1223
+ }
1224
+ await withEngine(async (engine) => {
1225
+ const taskDir = path.join(engine['projectDir'], '.agent', 'tasks', taskId);
1226
+ if (!await fileExists(taskDir)) {
1227
+ fi.println(chalk.red(` ✗ Task "${taskId}" no existe`));
1228
+ return;
1229
+ }
1230
+ const plan = await readJson(path.join(taskDir, 'plan.json'));
1231
+ await engine.runImplementor(taskId, plan);
1232
+ fi.println(chalk.green(` ✓ Implementación completada`));
1233
+ });
1234
+ break;
1235
+ }
1236
+ case 'rev': {
1237
+ // /rev <task-id> — lanza solo reviewer
1238
+ const taskId = args.join(' ').trim();
1239
+ if (!taskId) {
1240
+ fi.println(chalk.red(' Uso: /rev <task-id>'));
1241
+ break;
1242
+ }
1243
+ await withEngine(async (engine) => {
1244
+ const taskDir = path.join(engine['projectDir'], '.agent', 'tasks', taskId);
1245
+ if (!await fileExists(taskDir)) {
1246
+ fi.println(chalk.red(` ✗ Task "${taskId}" no existe`));
1247
+ return;
1248
+ }
1249
+ const plan = await readJson(path.join(taskDir, 'plan.json'));
1250
+ const progress = await readJson(path.join(taskDir, 'progress.json'));
1251
+ await engine.runReviewer(taskId, plan, progress);
1252
+ });
1253
+ break;
1254
+ }
1199
1255
  case 'models':
1200
1256
  case 'model':
1201
1257
  await withRl((rl) => cmdModels(args[0], fi, rl));
@@ -416,35 +416,17 @@ Los archivos raw YA ESTÁN en el CONTEXTO DEL PROYECTO arriba.
416
416
 
417
417
  REGLAS CRÍTICAS:
418
418
  1. ANTES DE ESCRIBIR CUALQUIER COSA: leé completo el CONTEXTO DEL PROYECTO, incluyendo la sección "TAREA EXISTENTE" si está presente.
419
- 2. SI EXISTE "TAREA EXISTENTE" en el contexto:
420
- - Si hay SPEC.MD EXISTENTE: NO lo regeneres. Usalo como referencia.
419
+ 2. SI EXISTE SPEC.MD en el contexto (bajo "SPEC.MD EXISTENTE"):
420
+ - **NO lo regeneres desde cero** — ese documento es VIVO y lo edita el humano.
421
+ - Identificá qué secciones ya están completas (Objetivo, Alcance, Criterios, etc.).
422
+ - Identificá qué información FALTA o está incompleta.
423
+ - Hacé SOLO las preguntas necesarias para completar lo que falta.
424
+ - Si el spec.md ya está completo, NO hagas preguntas — usá el existente.
425
+ 3. SI EXISTE "TAREA EXISTENTE" en el contexto:
421
426
  - Si hay RESULTADO PREVIO: FAIL: informá al usuario qué falló y preguntá si quiere corregir eso específico.
422
- - Si ya hay plan.json: NO generes otro spec — la tarea ya está definida.
423
- 3. NO preguntes sobre información que ya está en los archivos raw o en TAREA EXISTENTE.
424
- 4. Solo hacé preguntas sobre información que GENUINAMENTE falta y que no podés inferir del contexto.
425
- 5. Si los archivos ya tienen suficiente información para entender el objetivo, NO hagas preguntas — usá el spec existente.
426
- 6. Cuando tengas todo claro (o si ya hay spec), generá el spec.md SOLO si NO existe uno previo:
427
-
428
- === SPEC.MD ===
429
- # Feature: ${activeFeatureId}
430
-
431
- ## Objetivo
432
- [descripción clara del objetivo]
433
-
434
- ## Alcance
435
- [qué incluye y qué no incluye]
436
-
437
- ## Historias de Usuario / Requerimientos
438
- [detalle de cada requerimiento]
439
-
440
- ## Criterios de Aceptación
441
- [lista de criterios verificables]
442
-
443
- ## Notas Técnicas
444
- [consideraciones de implementación]
445
- === END SPEC.MD ===
446
-
447
- 7. Cuando el spec esté aprobado (o si ya existe), escribí EXACTAMENTE al final: ${READY_SENTINEL}` : `
427
+ - Si ya hay plan.json: la tarea ya está definida — NO generes spec.
428
+ 4. NUNCA reemplaces el spec.md existente si necesitás actualizarlo, solo sugerí los cambios puntuales.
429
+ 5. Cuando tengas todo claro (o si ya hay spec), escribí EXACTAMENTE al final: ${READY_SENTINEL}` : `
448
430
  MODO TAREA LIBRE — No se especificó feature todavía.
449
431
  ${featureListBlock}
450
432
  REGLAS:
@@ -468,9 +450,10 @@ ${conversationHistory}
468
450
  INSTRUCCIONES GENERALES:
469
451
  - Hablá en forma NATURAL, como un compañero de equipo experimentado.
470
452
  - ANTES DE RESPONDER: leé todo el CONTEXTO DEL PROYECTO, incluyendo "TAREA EXISTENTE" si está presente.
453
+ - **SPEC.MD ES DOCUMENTO VIVO**: Si ya existe spec.md, NO lo regeneres — es un documento que el humano va construyendo y editando. Solo identificá qué falta y preguntá para completar.
471
454
  - Si hay una TAREA EXISTENTE con RESULTADO PREVIO: FAIL, informá al usuario qué falló y preguntá si quiere corregir eso específico.
472
455
  - Si ya hay SPEC.MD o plan.json para la tarea, NO los regeneres — usá los existentes.
473
- - NO preguntés cosas que ya están respondidas en los archivos raw, TAREA EXISTENTE o la conversación.
456
+ - NO preguntés cosas que ya están respondidas en los archivos raw, TAREA EXISTENTE, SPEC.MD o la conversación.
474
457
  - Hacé como máximo UNA pregunta por turno.
475
458
  - NO uses JSON, hablá normalmente.
476
459
  - Sé directo y eficiente — respetá el tiempo del programador.${specInstructions}`;
@@ -621,12 +604,38 @@ INSTRUCCIONES GENERALES:
621
604
  const confirm = await ask(' ¿Confirmás lanzar el orchestrator? (y/n): ', this.rl);
622
605
  if (confirm.toLowerCase() === 'y' || confirm.toLowerCase() === 's') {
623
606
  let specPath;
624
- if (activeFeatureId && pendingSpecContent) {
607
+ if (activeFeatureId) {
625
608
  const specDir = path.join(this.projectDir, '.agent', 'docs', activeFeatureId);
626
609
  const specFile = path.join(specDir, 'spec.md');
627
- await writeFile(specFile, pendingSpecContent);
628
- log.ok(`spec.md guardado en ${path.relative(this.projectDir, specFile)}`);
629
- specPath = specFile;
610
+ // Si ya existe spec.md y hay contenido nuevo, ACTUALIZAR incrementalmente
611
+ if (await fileExists(specFile) && pendingSpecContent) {
612
+ const existingSpec = await readFile(specFile);
613
+ // Si el contenido es sustancialmente diferente, preguntar al usuario
614
+ if (existingSpec.trim() !== pendingSpecContent.trim()) {
615
+ console.log(chalk.yellow('\n ⚠️ Ya existe un spec.md con este contenido:'));
616
+ console.log(chalk.dim(' ' + existingSpec.split('\n').slice(0, 10).join('\n ')));
617
+ const action = await ask('\n ¿Qué querés hacer? (r=reemplazar / m=mantener el existente / e=editar): ', this.rl);
618
+ if (action.toLowerCase() === 'r') {
619
+ await writeFile(specFile, pendingSpecContent);
620
+ log.ok(`spec.md reemplazado`);
621
+ }
622
+ else if (action.toLowerCase() === 'e') {
623
+ console.log(chalk.dim(' Editá el archivo spec.md manualmente y luego volvé a ejecutar.'));
624
+ return { task: conversationHistory, specPath: undefined, featureId: activeFeatureId };
625
+ }
626
+ else {
627
+ log.ok(`spec.md existente mantenido`);
628
+ }
629
+ }
630
+ }
631
+ else if (pendingSpecContent) {
632
+ // No existe spec.md — crearlo
633
+ await writeFile(specFile, pendingSpecContent);
634
+ log.ok(`spec.md guardado en ${path.relative(this.projectDir, specFile)}`);
635
+ }
636
+ if (await fileExists(specFile)) {
637
+ specPath = specFile;
638
+ }
630
639
  }
631
640
  return { task: conversationHistory, specPath, featureId: activeFeatureId };
632
641
  }
@@ -1034,6 +1043,31 @@ REGLA: Al terminar, reporta todo lo que encontraste de forma clara y estructurad
1034
1043
  const taskId = await this.generateTaskId(task, featureId);
1035
1044
  const taskDir = path.join(this.projectDir, '.agent', 'tasks', taskId);
1036
1045
  log.phase(1, 'Planificacion', this.config.roles.orchestrator.cli, this.config.roles.orchestrator.model);
1046
+ // VERIFICAR SI YA EXISTE UN PLAN PARA ESTA TAREA
1047
+ const existingPlanPath = path.join(taskDir, 'plan.json');
1048
+ if (await fileExists(existingPlanPath)) {
1049
+ const existingPlan = await readJson(existingPlanPath);
1050
+ console.log(chalk.yellow('\n ⚠️ Ya existe un plan para esta tarea:'));
1051
+ console.log(chalk.dim(` Descripción: ${existingPlan.description?.slice(0, 100) || 'N/A'}`));
1052
+ console.log(chalk.dim(` Steps: ${existingPlan.steps?.length || 0}`));
1053
+ const action = await ask('\n ¿Qué querés hacer? (v=ver plan completo / r=regenerar / m=mantener y continuar): ', this.rl);
1054
+ if (action.toLowerCase() === 'v') {
1055
+ console.log('');
1056
+ console.log(chalk.cyan(' === plan.json ==='));
1057
+ console.log(chalk.white(JSON.stringify(existingPlan, null, 2).slice(0, 2000)));
1058
+ console.log(chalk.cyan(' ================='));
1059
+ const retry = await ask(' ¿Regenerar igual? (y/n): ', this.rl);
1060
+ if (retry.toLowerCase() !== 'y') {
1061
+ log.info('Usando plan existente.');
1062
+ return { taskId, plan: existingPlan };
1063
+ }
1064
+ }
1065
+ else if (action.toLowerCase() === 'm') {
1066
+ log.info('Usando plan existente.');
1067
+ return { taskId, plan: existingPlan };
1068
+ }
1069
+ // Si es 'r', continúa generando nuevo plan
1070
+ }
1037
1071
  const context = await this.buildOrchestratorContext();
1038
1072
  let specContent = '';
1039
1073
  if (specPath && await fileExists(specPath)) {
@@ -1051,10 +1085,15 @@ ${context}
1051
1085
  INSTRUCCIONES CRÍTICAS:
1052
1086
  1. Si hay SPEC.MD, ESE ES EL DOCUMENTO PRIMARIO — define completamente la tarea. Los archivos raw son solo contexto histórico, NO los uses para definir la implementación.
1053
1087
  2. Ignorá cualquier información que NO tenga relación directa con la tarea del spec.md (ej: listas de modelos, ejemplos genéricos, etc.).
1054
- 3. Generá steps CONCRETOS y DIRECTOS para implementar lo que dice el spec.md sin sobre-analizar.
1055
- 4. Si el spec.md dice "script que muestra la hora", los steps son: (1) crear el script, (2) hacerlo ejecutable. No más.
1056
- 5. Definí acceptance_criteria medibles leyendo archivos.
1057
- 6. Responde SOLO con este JSON exacto, sin texto adicional:
1088
+ 3. **GENERÁ STEPS DE IMPLEMENTACIÓN CONCRETOS** NO steps sobre "leer", "analizar", "identificar". El implementor ya sabe leer.
1089
+ - MAL: "Leer spec.md para identificar requerimientos"
1090
+ - MAL: "Analizar archivos raw como contexto"
1091
+ - BIEN: "Crear script clock.sh con formato HH:MM:SS, sleep 1, trap SIGINT"
1092
+ - ✅ BIEN: "Hacer ejecutable el script con chmod +x"
1093
+ 4. Cada step debe tener una descripción de **ACCIÓN** (crear, modificar, instalar, configurar) y los archivos específicos que se van a crear/modificar.
1094
+ 5. Si el spec.md dice "script que muestra la hora", los steps son: (1) crear el script, (2) hacerlo ejecutable. No más.
1095
+ 6. Definí acceptance_criteria medibles leyendo archivos.
1096
+ 7. Responde SOLO con este JSON exacto, sin texto adicional:
1058
1097
 
1059
1098
  {"plan":{"task_id":"${taskId}","description":"${task}","steps":[{"num":1,"description":"descripcion concreta del paso","files":["archivo/a/crear.ts"],"status":"pending"}],"acceptance_criteria":["criterio verificable"],"deliberation":{"needed":false,"question":""}}}`;
1060
1099
  const res = await this.runWithFallback('orchestrator', prompt, 'Planificacion');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-mp",
3
- "version": "0.5.31",
3
+ "version": "0.5.34",
4
4
  "description": "Deterministic multi-agent CLI orchestrator — plan, code, review",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",