@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
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(
|
|
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(
|
|
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(
|
|
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 (
|
|
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 (
|
|
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
|
|
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: ${
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
REPOS.frontend
|
|
1522
|
-
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
1626
|
+
safeRenderDashboard();
|
|
1591
1627
|
});
|
|
1592
1628
|
screen.key("r", () => {
|
|
1593
1629
|
reloadQueue();
|
|
1594
1630
|
log("INFO", L.queueReloaded(state.queue.length));
|
|
1595
|
-
|
|
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.
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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
|
-
|
|
70
|
+
"Gemini": {
|
|
71
71
|
"cli": "gemini",
|
|
72
72
|
"profile": "gemini",
|
|
73
73
|
"defaultRepo": "frontend",
|
|
74
|
-
"model": "
|
|
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": "
|
|
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": "
|
|
95
|
+
"model": "auto",
|
|
96
96
|
"instructionsFile": "agents/ABACUS.md"
|
|
97
97
|
}
|
|
98
98
|
}
|