@liriraid/agentflow-ai 1.0.13 → 1.0.14

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/bin/agentflow.mjs CHANGED
@@ -84,7 +84,7 @@ Ejemplos:
84
84
  agentflow init . --project-name "Mi Proyecto" --lang es
85
85
  agentflow init-workspace C:/code/mi-proyecto --lang en
86
86
  agentflow tui --paused
87
- agentflow ink
87
+ agentflow ink --yolo
88
88
  `);
89
89
  }
90
90
 
package/orchestrator.js CHANGED
@@ -170,8 +170,7 @@ const TEXT = {
170
170
  empty: "(vacía)",
171
171
  after: "después de",
172
172
  quotaLimit: "LÍMITE DE CUOTA",
173
- retryAt: (time, remaining) =>
174
- `reintenta a las ${time} (${remaining} min)`,
173
+ retryAt: (time, remaining) => `reintenta a las ${time} (${remaining} min)`,
175
174
  log: "REGISTRO",
176
175
  controls: "Seguir Pausa Recargar Quitar",
177
176
  },
@@ -221,6 +220,16 @@ const TEXT = {
221
220
  };
222
221
  const L = TEXT[WORKSPACE_LANGUAGE];
223
222
 
223
+ let lastRenderTime = 0;
224
+ const RENDER_DEBOUNCE_MS = 500;
225
+
226
+ function safeRenderDashboard() {
227
+ const now = Date.now();
228
+ if (now - lastRenderTime < RENDER_DEBOUNCE_MS) return;
229
+ lastRenderTime = now;
230
+ renderDashboard();
231
+ }
232
+
224
233
  // CLI args
225
234
  const argv = process.argv.slice(2);
226
235
  const CLI = {
@@ -258,8 +267,7 @@ ${L.keyboard}:
258
267
  const MAX_CONCURRENT = config.maxConcurrent || Object.keys(AGENTS).length;
259
268
  const POLL_INTERVAL_MS = (config.pollIntervalSeconds || 30) * 1000;
260
269
  const TASK_TIMEOUT_MS = (config.taskTimeoutMinutes || 30) * 60 * 1000;
261
- const SKIP_PERMISSIONS =
262
- process.env.SKIP_PERMISSIONS === "true" || CLI.yolo;
270
+ const SKIP_PERMISSIONS = process.env.SKIP_PERMISSIONS === "true" || CLI.yolo;
263
271
  const PERMISSION_FLAGS = SKIP_PERMISSIONS
264
272
  ? ["--dangerously-skip-permissions"]
265
273
  : ["--permission-mode", "default"];
@@ -401,7 +409,10 @@ const dashboard =
401
409
 
402
410
  const agentNames = Object.keys(AGENTS);
403
411
  const agentBoxes = {};
404
- const panelWidth = Math.max(1, Math.floor(100 / Math.max(1, agentNames.length)));
412
+ const panelWidth = Math.max(
413
+ 1,
414
+ Math.floor(100 / Math.max(1, agentNames.length)),
415
+ );
405
416
 
406
417
  if (!CLI.headless && screen) {
407
418
  agentNames.forEach((name, i) => {
@@ -480,7 +491,11 @@ function persistState() {
480
491
  ]),
481
492
  ),
482
493
  };
483
- fs.writeFileSync(STATE_FILE, JSON.stringify(snapshot, null, 2) + "\n", "utf-8");
494
+ fs.writeFileSync(
495
+ STATE_FILE,
496
+ JSON.stringify(snapshot, null, 2) + "\n",
497
+ "utf-8",
498
+ );
484
499
  }
485
500
 
486
501
  function consumeControlCommand() {
@@ -590,7 +605,9 @@ function renderDashboard() {
590
605
  ? `{yellow-fg}${L.paused}{/yellow-fg}`
591
606
  : `{green-fg}${L.running}{/green-fg}`;
592
607
 
593
- lines.push(` ${datestamp()} ${timestamp()} ${WORKSPACE_LANGUAGE === "es" ? "activo" : "active"} ${up} ${cost} ${mode}`);
608
+ lines.push(
609
+ ` ${datestamp()} ${timestamp()} ${WORKSPACE_LANGUAGE === "es" ? "activo" : "active"} ${up} ${cost} ${mode}`,
610
+ );
594
611
  lines.push("");
595
612
 
596
613
  for (const [name, ag] of Object.entries(state.agents)) {
@@ -668,9 +685,7 @@ function renderDashboard() {
668
685
  lines.push(` {gray-fg}${escBl(entry)}{/gray-fg}`);
669
686
  }
670
687
  lines.push("");
671
- lines.push(
672
- ` {cyan-fg}S{/cyan-fg} ${L.controls}`,
673
- );
688
+ lines.push(` {cyan-fg}S{/cyan-fg} ${L.controls}`);
674
689
 
675
690
  dashboard.setContent(lines.join("\n"));
676
691
 
@@ -705,7 +720,10 @@ function parseQueue() {
705
720
  section = "pending";
706
721
  continue;
707
722
  }
708
- if (line.startsWith("## In Progress") || line.startsWith("## En progreso")) {
723
+ if (
724
+ line.startsWith("## In Progress") ||
725
+ line.startsWith("## En progreso")
726
+ ) {
709
727
  section = "inprogress";
710
728
  continue;
711
729
  }
@@ -747,7 +765,10 @@ function parseCompletedFromFile() {
747
765
  section = "pending";
748
766
  continue;
749
767
  }
750
- if (line.startsWith("## In Progress") || line.startsWith("## En progreso")) {
768
+ if (
769
+ line.startsWith("## In Progress") ||
770
+ line.startsWith("## En progreso")
771
+ ) {
751
772
  section = "inprogress";
752
773
  continue;
753
774
  }
@@ -892,7 +913,13 @@ function generateBrief(task) {
892
913
  }
893
914
  }
894
915
 
895
- const repoDir = REPOS[task.repo] || REPOS[agentCfg.defaultRepo] || ".";
916
+ const hasBackend = REPOS.backend && fs.existsSync(REPOS.backend);
917
+ const hasFrontend = REPOS.frontend && fs.existsSync(REPOS.frontend);
918
+ const isSingleRepo = (hasBackend && hasFrontend &&
919
+ path.resolve(REPOS.backend) === path.resolve(REPOS.frontend)) ||
920
+ (!hasBackend && hasFrontend) || (hasBackend && !hasFrontend);
921
+ const effectiveRepo = isSingleRepo ? "frontend" : (task.repo || agentCfg.defaultRepo);
922
+ const repoDir = REPOS[effectiveRepo] || REPOS[task.repo] || REPOS[agentCfg.defaultRepo] || ".";
896
923
  const progressFile = path.join(
897
924
  WORKSPACE,
898
925
  "progress",
@@ -902,7 +929,7 @@ function generateBrief(task) {
902
929
  return `
903
930
  # Agent: ${task.agent}
904
931
  # Task: ${task.id} — ${task.title}
905
- # Repository: ${task.repo}
932
+ # Repository: ${effectiveRepo}
906
933
  # CWD: ${repoDir}
907
934
  # Priority: ${task.priority}
908
935
  # Workspace: ${WORKSPACE}
@@ -971,25 +998,17 @@ function buildCliCommand(agentCfg, task, prompt) {
971
998
  case "codex":
972
999
  return {
973
1000
  cmd: "codex",
974
- args: [
975
- "exec",
976
- ...(agentCfg.model ? ["--model", agentCfg.model] : []),
977
- ...(CLI.yolo ? ["--dangerously-bypass-approvals-and-sandbox"] : []),
978
- "--add-dir",
979
- WORKSPACE,
980
- "-",
981
- ],
1001
+ args: ["exec", "--yolo", "--add-dir", WORKSPACE, "-"],
982
1002
  };
983
1003
  case "opencode":
984
1004
  return {
985
1005
  cmd: "opencode",
986
1006
  args: [
987
1007
  "run",
988
- ...(agentCfg.model ? ["--model", agentCfg.model] : []),
989
1008
  "--format",
990
1009
  "json",
991
1010
  "--pure",
992
- ...(CLI.yolo ? ["--dangerously-skip-permissions"] : []),
1011
+ "--dangerously-skip-permissions",
993
1012
  ],
994
1013
  };
995
1014
  case "gemini":
@@ -1023,7 +1042,7 @@ function buildCliCommand(agentCfg, task, prompt) {
1023
1042
  cmd: "cmd",
1024
1043
  args: [
1025
1044
  "/c",
1026
- `type "${promptFile}" | abacusai -p --output-format stream-json --permission-mode ${CLI.yolo ? "yolo" : "default"} ${CLI.yolo ? "--dangerously-skip-permissions --auto-accept-edits" : ""} --add-dir "${WORKSPACE}"`,
1045
+ `type "${promptFile}" | abacusai -p --output-format stream-json --permission-mode yolo --dangerously-skip-permissions --auto-accept-edits --add-dir "${WORKSPACE}"`,
1027
1046
  ],
1028
1047
  };
1029
1048
  }
@@ -1031,7 +1050,7 @@ function buildCliCommand(agentCfg, task, prompt) {
1031
1050
  cmd: "sh",
1032
1051
  args: [
1033
1052
  "-c",
1034
- `cat "${promptFile}" | abacusai -p --output-format stream-json --permission-mode ${CLI.yolo ? "yolo" : "default"} ${CLI.yolo ? "--dangerously-skip-permissions --auto-accept-edits" : ""} --add-dir "${WORKSPACE}"`,
1053
+ `cat "${promptFile}" | abacusai -p --output-format stream-json --permission-mode yolo --dangerously-skip-permissions --auto-accept-edits --add-dir "${WORKSPACE}"`,
1035
1054
  ],
1036
1055
  };
1037
1056
  }
@@ -1358,9 +1377,8 @@ function failTask(task, agentName, code) {
1358
1377
  writeInboxFailureNotification(task, agentName, task.agent, reason);
1359
1378
  setTimeout(() => {
1360
1379
  scheduleNext();
1361
- renderDashboard();
1380
+ safeRenderDashboard();
1362
1381
  }, 3000);
1363
- renderDashboard();
1364
1382
  return;
1365
1383
  }
1366
1384
  }
@@ -1392,7 +1410,7 @@ function failTask(task, agentName, code) {
1392
1410
  setTimeout(
1393
1411
  () => {
1394
1412
  scheduleNext();
1395
- renderDashboard();
1413
+ safeRenderDashboard();
1396
1414
  },
1397
1415
  Math.max(
1398
1416
  Math.min(task._retryAfter - Date.now() + 5000, 3600_000),
@@ -1402,10 +1420,9 @@ function failTask(task, agentName, code) {
1402
1420
  } else {
1403
1421
  setTimeout(() => {
1404
1422
  scheduleNext();
1405
- renderDashboard();
1423
+ safeRenderDashboard();
1406
1424
  }, 3000);
1407
1425
  }
1408
- renderDashboard();
1409
1426
  }
1410
1427
 
1411
1428
  // ============================================================================
@@ -1447,7 +1464,11 @@ function updateQueueFile(completedTask) {
1447
1464
  `^${completedTask.id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(\\s|$|\\|)`,
1448
1465
  );
1449
1466
  const filtered = lines.filter((l) => !idMatcher.test(l.trim()));
1450
- const idx = filtered.findIndex((l) => l.trim().startsWith("## Completed") || l.trim().startsWith("## Completadas"));
1467
+ const idx = filtered.findIndex(
1468
+ (l) =>
1469
+ l.trim().startsWith("## Completed") ||
1470
+ l.trim().startsWith("## Completadas"),
1471
+ );
1451
1472
  if (idx >= 0)
1452
1473
  filtered.splice(
1453
1474
  idx + 1,
@@ -1515,17 +1536,24 @@ function detectSupportAgentFailure(agentName) {
1515
1536
  }
1516
1537
 
1517
1538
  function getClaudeFallbackAgent(task) {
1518
- // If both repos point to the same resolved path (frontend-only project), always prefer Frontend
1519
- if (
1520
- REPOS.backend &&
1521
- REPOS.frontend &&
1522
- path.resolve(REPOS.backend) === path.resolve(REPOS.frontend)
1523
- ) {
1539
+ const hasBackend = REPOS.backend && fs.existsSync(REPOS.backend);
1540
+ const hasFrontend = REPOS.frontend && fs.existsSync(REPOS.frontend);
1541
+ const isSameRepo = hasBackend && hasFrontend &&
1542
+ path.resolve(REPOS.backend) === path.resolve(REPOS.frontend);
1543
+
1544
+ if (isSameRepo || !hasFrontend) {
1545
+ if (AGENTS["Frontend"]?.cli === "claude") return "Frontend";
1546
+ if (AGENTS["Backend"]?.cli === "claude") return "Backend";
1547
+ }
1548
+ if (!hasBackend && hasFrontend) {
1524
1549
  if (AGENTS["Frontend"]?.cli === "claude") return "Frontend";
1525
1550
  }
1551
+
1526
1552
  const preferred = task.repo === "frontend" ? "Frontend" : "Backend";
1527
1553
  if (AGENTS[preferred]?.cli === "claude") return preferred;
1528
- return Object.keys(AGENTS).find((name) => AGENTS[name]?.cli === "claude") || null;
1554
+ return (
1555
+ Object.keys(AGENTS).find((name) => AGENTS[name]?.cli === "claude") || null
1556
+ );
1529
1557
  }
1530
1558
 
1531
1559
  function getAlternativeSupportAgent(failedAgentName) {
@@ -1546,7 +1574,9 @@ function tryFallbackToAlternative(task, failedAgentName, reason) {
1546
1574
  !rateLimitedAgents.has(siblingAgent);
1547
1575
 
1548
1576
  // Step 2: if sibling is also unavailable, fall back to Claude worker (prefer Frontend)
1549
- const targetAgent = siblingAvailable ? siblingAgent : getClaudeFallbackAgent(task);
1577
+ const targetAgent = siblingAvailable
1578
+ ? siblingAgent
1579
+ : getClaudeFallbackAgent(task);
1550
1580
  if (!targetAgent || targetAgent === failedAgentName) return false;
1551
1581
 
1552
1582
  const queueUpdated = updateQueueTaskAgent(task.id, targetAgent);
@@ -1556,14 +1586,20 @@ function tryFallbackToAlternative(task, failedAgentName, reason) {
1556
1586
  failedTasks.set(task.id, 0);
1557
1587
  state.queue.push(task);
1558
1588
 
1559
- log("FALLBACK", `${task.id} reasignada de ${failedAgentName} a ${targetAgent} (${reason})`);
1589
+ log(
1590
+ "FALLBACK",
1591
+ `${task.id} reasignada de ${failedAgentName} a ${targetAgent} (${reason})`,
1592
+ );
1560
1593
  appendToAgent(
1561
1594
  failedAgentName,
1562
1595
  `{yellow-fg}=== REASIGNADA A ${escBl(targetAgent)} (${escBl(reason)}) ==={/yellow-fg}`,
1563
1596
  true,
1564
1597
  );
1565
1598
  if (!queueUpdated) {
1566
- log("WARN", `${task.id} reasignada a ${targetAgent}, pero QUEUE.md no pudo actualizarse`);
1599
+ log(
1600
+ "WARN",
1601
+ `${task.id} reasignada a ${targetAgent}, pero QUEUE.md no pudo actualizarse`,
1602
+ );
1567
1603
  }
1568
1604
  return true;
1569
1605
  }
@@ -1582,17 +1618,17 @@ if (!CLI.headless && screen) {
1582
1618
  log("INFO", "Reanudado");
1583
1619
  }
1584
1620
  scheduleNext();
1585
- renderDashboard();
1621
+ safeRenderDashboard();
1586
1622
  });
1587
1623
  screen.key("p", () => {
1588
1624
  state.paused = !state.paused;
1589
1625
  log("INFO", state.paused ? L.paused : L.resumed);
1590
- renderDashboard();
1626
+ safeRenderDashboard();
1591
1627
  });
1592
1628
  screen.key("r", () => {
1593
1629
  reloadQueue();
1594
1630
  log("INFO", L.queueReloaded(state.queue.length));
1595
- renderDashboard();
1631
+ safeRenderDashboard();
1596
1632
  });
1597
1633
  }
1598
1634
 
@@ -1601,10 +1637,7 @@ if (!CLI.headless && screen) {
1601
1637
  // ============================================================================
1602
1638
  log("INFO", L.starting(PROJECT_NAME));
1603
1639
  state.completed = parseCompletedFromFile();
1604
- log(
1605
- "INFO",
1606
- L.loadedCompleted(state.completed.length),
1607
- );
1640
+ log("INFO", L.loadedCompleted(state.completed.length));
1608
1641
  reloadQueue();
1609
1642
  log("INFO", `${L.queue}: ${state.queue.length} ${L.tasks}`);
1610
1643
  renderDashboard();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liriraid/agentflow-ai",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "Multi-agent workspace orchestrator with TUI. Coordinates AI coding agents over your real frontend and backend projects.",
5
5
  "author": "LiriRaid",
6
6
  "homepage": "https://github.com/LiriRaid/agentflow-ai#readme",
package/src/ink/index.mjs CHANGED
@@ -20,6 +20,7 @@ const CONTROL_FILE = path.join(ROOT, 'logs', 'orchestrator-control.json');
20
20
 
21
21
  const argv = process.argv.slice(2);
22
22
  const startPaused = argv.includes('--paused');
23
+ const startYolo = argv.includes('--yolo');
23
24
  const TEXT = {
24
25
  es: {
25
26
  configMissing: root =>
@@ -266,6 +267,7 @@ function ensureEngine() {
266
267
 
267
268
  const childArgs = [ENGINE_FILE, '--headless'];
268
269
  if (startPaused) childArgs.push('--paused');
270
+ if (startYolo) childArgs.push('--yolo');
269
271
 
270
272
  pushLocalEvent(
271
273
  startPaused ? text.startPaused : text.startRunning
@@ -71,14 +71,14 @@
71
71
  "cli": "gemini",
72
72
  "profile": "gemini",
73
73
  "defaultRepo": "frontend",
74
- "model": "gemini-3-pro-preview",
74
+ "model": "auto",
75
75
  "instructionsFile": "agents/GEMINI.md"
76
76
  },
77
77
  "OpenCode": {
78
78
  "cli": "opencode",
79
79
  "profile": "opencode",
80
80
  "defaultRepo": "frontend",
81
- "model": "opencode/glm-5-free",
81
+ "model": "auto",
82
82
  "instructionsFile": "agents/OPENCODE.md"
83
83
  },
84
84
  "Cursor": {
@@ -92,7 +92,7 @@
92
92
  "cli": "abacusai",
93
93
  "profile": "abacusai",
94
94
  "defaultRepo": "backend",
95
- "model": "abacus-ai-agent",
95
+ "model": "auto",
96
96
  "instructionsFile": "agents/ABACUS.md"
97
97
  }
98
98
  }
@@ -67,18 +67,18 @@
67
67
  "model": "gpt-5.5",
68
68
  "instructionsFile": "agents/CODEX.md"
69
69
  },
70
- "Gemini": {
70
+ "Gemini": {
71
71
  "cli": "gemini",
72
72
  "profile": "gemini",
73
73
  "defaultRepo": "frontend",
74
- "model": "gemini-3-pro-preview",
74
+ "model": "auto",
75
75
  "instructionsFile": "agents/GEMINI.md"
76
- },
76
+ },
77
77
  "OpenCode": {
78
78
  "cli": "opencode",
79
79
  "profile": "opencode",
80
80
  "defaultRepo": "frontend",
81
- "model": "opencode/glm-5-free",
81
+ "model": "auto",
82
82
  "instructionsFile": "agents/OPENCODE.md"
83
83
  },
84
84
  "Cursor": {
@@ -92,7 +92,7 @@
92
92
  "cli": "abacusai",
93
93
  "profile": "abacusai",
94
94
  "defaultRepo": "backend",
95
- "model": "abacus-ai-agent",
95
+ "model": "auto",
96
96
  "instructionsFile": "agents/ABACUS.md"
97
97
  }
98
98
  }