@liriraid/agentflow-ai 1.0.25 → 1.0.27
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 +0 -0
- package/orchestrator.js +33 -154
- package/package.json +64 -65
- package/templates/en/.claude/hooks/watch-notify.js +33 -0
- package/templates/en/.claude/settings.json +5 -0
- package/templates/en/.claude/skills/orchestrator-explore/SKILL.md +1 -1
- package/templates/en/ORCHESTRATOR.md +34 -13
- package/templates/es/.claude/hooks/watch-notify.js +33 -0
- package/templates/es/.claude/settings.json +5 -0
- package/templates/es/.claude/skills/orchestrator-explore/SKILL.md +1 -1
- package/templates/es/ORCHESTRATOR.md +28 -20
package/bin/agentflow.mjs
CHANGED
|
File without changes
|
package/orchestrator.js
CHANGED
|
@@ -9,12 +9,17 @@
|
|
|
9
9
|
const blessed = require("blessed");
|
|
10
10
|
const { spawn } = require("child_process");
|
|
11
11
|
const fs = require("fs");
|
|
12
|
+
const os = require("os");
|
|
12
13
|
const path = require("path");
|
|
13
14
|
|
|
14
15
|
const WORKSPACE = process.env.ORCHESTRATOR_WORKSPACE
|
|
15
16
|
? path.resolve(process.env.ORCHESTRATOR_WORKSPACE)
|
|
16
17
|
: path.resolve(process.cwd());
|
|
17
18
|
|
|
19
|
+
// Global supply chain — the user's ~/.claude dir (rules, skills, CLAUDE.md, etc.)
|
|
20
|
+
// Worker agents are given access to this dir so they follow the same standards as Claude.
|
|
21
|
+
const GLOBAL_CLAUDE_DIR = path.join(os.homedir(), ".claude");
|
|
22
|
+
|
|
18
23
|
// ============================================================================
|
|
19
24
|
// CONFIGURATION — loaded from orchestrator.config.json
|
|
20
25
|
// ============================================================================
|
|
@@ -1008,35 +1013,6 @@ function parseCompletedFromFile() {
|
|
|
1008
1013
|
}
|
|
1009
1014
|
|
|
1010
1015
|
const loggedUnknownAgents = new Set();
|
|
1011
|
-
let queueWatcher = null;
|
|
1012
|
-
|
|
1013
|
-
function setupQueueWatcher() {
|
|
1014
|
-
if (!fs.existsSync(QUEUE_FILE)) return;
|
|
1015
|
-
|
|
1016
|
-
if (queueWatcher) {
|
|
1017
|
-
try { queueWatcher.close(); } catch {}
|
|
1018
|
-
queueWatcher = null;
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
try {
|
|
1022
|
-
queueWatcher = fs.watch(QUEUE_FILE, { persistent: false }, (eventType, filename) => {
|
|
1023
|
-
if (eventType === 'change' && filename === path.basename(QUEUE_FILE)) {
|
|
1024
|
-
log('DEBUG', `QUEUE.md changed, reloading queue immediately`);
|
|
1025
|
-
reloadQueue();
|
|
1026
|
-
scheduleNext();
|
|
1027
|
-
renderDashboard();
|
|
1028
|
-
}
|
|
1029
|
-
});
|
|
1030
|
-
queueWatcher.on('error', (err) => {
|
|
1031
|
-
log('WARN', `fs.watch failed for QUEUE.md: ${err.message}, falling back to polling`);
|
|
1032
|
-
queueWatcher = null;
|
|
1033
|
-
});
|
|
1034
|
-
log('INFO', 'Realtime QUEUE.md watcher enabled');
|
|
1035
|
-
} catch (err) {
|
|
1036
|
-
log('WARN', `Failed to setup QUEUE.md watcher: ${err.message}, using polling`);
|
|
1037
|
-
queueWatcher = null;
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
1016
|
|
|
1041
1017
|
function reloadQueue() {
|
|
1042
1018
|
state.queue = parseQueue();
|
|
@@ -1178,6 +1154,14 @@ function generateBrief(task) {
|
|
|
1178
1154
|
if (taskMatch) taskEntry = taskMatch[0];
|
|
1179
1155
|
}
|
|
1180
1156
|
|
|
1157
|
+
// Global developer standards — read the user's ~/.claude/CLAUDE.md verbatim.
|
|
1158
|
+
// Each developer has their own standards there; we don't parse specific sections.
|
|
1159
|
+
let globalStandards = "";
|
|
1160
|
+
const globalClaude = path.join(GLOBAL_CLAUDE_DIR, "CLAUDE.md");
|
|
1161
|
+
if (fs.existsSync(globalClaude)) {
|
|
1162
|
+
globalStandards = fs.readFileSync(globalClaude, "utf-8").trim();
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1181
1165
|
// Project plan — if `<projectName>-plan.md` or `PLAN.md` exists in the workspace,
|
|
1182
1166
|
// inject it as shared context so every agent sees the big-picture plan.
|
|
1183
1167
|
let projectPlan = "";
|
|
@@ -1217,7 +1201,9 @@ function generateBrief(task) {
|
|
|
1217
1201
|
# Priority: ${task.priority}
|
|
1218
1202
|
# Workspace: ${WORKSPACE}
|
|
1219
1203
|
# Progress file: ${progressFile}
|
|
1204
|
+
# Global supply chain: ${GLOBAL_CLAUDE_DIR}
|
|
1220
1205
|
|
|
1206
|
+
${globalStandards ? `## Global Developer Standards\n> Extracted from ${globalClaude} — apply these rules to ALL code in this task.\n\n${globalStandards}\n` : ""}
|
|
1221
1207
|
${projectPlan ? `## Project Plan (big picture — use as context, don't try to do everything)\n${projectPlan}\n` : ""}
|
|
1222
1208
|
${agentInstructions ? `## Agent Instructions\n${agentInstructions}` : ""}
|
|
1223
1209
|
${protocolRules ? `## Protocol Rules\n${protocolRules}` : ""}
|
|
@@ -1274,6 +1260,7 @@ function buildCliCommand(agentCfg, task, prompt) {
|
|
|
1274
1260
|
...(agentCfg.model ? ["--model", agentCfg.model] : []),
|
|
1275
1261
|
"--add-dir",
|
|
1276
1262
|
WORKSPACE,
|
|
1263
|
+
...(fs.existsSync(GLOBAL_CLAUDE_DIR) ? ["--add-dir", GLOBAL_CLAUDE_DIR] : []),
|
|
1277
1264
|
"--name",
|
|
1278
1265
|
`${task.agent}-${task.id}`,
|
|
1279
1266
|
],
|
|
@@ -1281,7 +1268,14 @@ function buildCliCommand(agentCfg, task, prompt) {
|
|
|
1281
1268
|
case "codex":
|
|
1282
1269
|
return {
|
|
1283
1270
|
cmd: "codex",
|
|
1284
|
-
args: [
|
|
1271
|
+
args: [
|
|
1272
|
+
"exec",
|
|
1273
|
+
"--yolo",
|
|
1274
|
+
"--add-dir",
|
|
1275
|
+
WORKSPACE,
|
|
1276
|
+
...(fs.existsSync(GLOBAL_CLAUDE_DIR) ? ["--add-dir", GLOBAL_CLAUDE_DIR] : []),
|
|
1277
|
+
"-",
|
|
1278
|
+
],
|
|
1285
1279
|
};
|
|
1286
1280
|
case "opencode":
|
|
1287
1281
|
return {
|
|
@@ -1421,7 +1415,7 @@ function launchAgent(task) {
|
|
|
1421
1415
|
if (txt) {
|
|
1422
1416
|
for (const l of txt.split("\n").slice(-3))
|
|
1423
1417
|
appendToAgent(agentName, l.slice(0, 120));
|
|
1424
|
-
ag.lastLine = txt.split("\n").pop().slice(0, 80);
|
|
1418
|
+
ag.lastLine = (txt.split("\n").pop() || "").slice(0, 80);
|
|
1425
1419
|
}
|
|
1426
1420
|
}
|
|
1427
1421
|
if (block.type === "tool_use") {
|
|
@@ -1685,6 +1679,7 @@ function failTask(task, agentName, code) {
|
|
|
1685
1679
|
task.status = "failed";
|
|
1686
1680
|
ag.lastLine = L.lastFailed(task.id);
|
|
1687
1681
|
log("ERROR", L.logPermanentFail(task.id, retries));
|
|
1682
|
+
updateQueueFile(task);
|
|
1688
1683
|
writeNotifyFile(L.notifyPermanentFail(timestamp(), task.id, agentName));
|
|
1689
1684
|
} else {
|
|
1690
1685
|
task.status = "pending";
|
|
@@ -1870,6 +1865,7 @@ function getClaudeFallbackAgent(task) {
|
|
|
1870
1865
|
|
|
1871
1866
|
function getAlternativeSupportAgent(failedAgentName) {
|
|
1872
1867
|
if (failedAgentName === "OpenCode") return "Codex";
|
|
1868
|
+
if (failedAgentName === "Codex") return "OpenCode";
|
|
1873
1869
|
return null;
|
|
1874
1870
|
}
|
|
1875
1871
|
|
|
@@ -1907,45 +1903,9 @@ function tryFallbackToAlternative(task, failedAgentName, reason) {
|
|
|
1907
1903
|
log("WARN", L.logReassignWarn(task.id, targetAgent));
|
|
1908
1904
|
}
|
|
1909
1905
|
|
|
1910
|
-
// Notificar a Claude (sesión principal) cuando hay fallback
|
|
1911
|
-
notifyClaudeOfFallback(task, failedAgentName, targetAgent, reason);
|
|
1912
1906
|
return true;
|
|
1913
1907
|
}
|
|
1914
1908
|
|
|
1915
|
-
// ============================================================================
|
|
1916
|
-
// CLAUDE FALLBACK NOTIFIER — avisa a Claude principal cuando hay reasignación
|
|
1917
|
-
// ============================================================================
|
|
1918
|
-
function notifyClaudeOfFallback(task, fromAgent, toAgent, reason) {
|
|
1919
|
-
const lang = WORKSPACE_LANGUAGE;
|
|
1920
|
-
const prompt = lang === 'es'
|
|
1921
|
-
? `⚠️ FALLBACK: La tarea "${task.id}: ${task.title}" falló en ${fromAgent} (${reason}) y fue reasignada a ${toAgent}.
|
|
1922
|
-
|
|
1923
|
-
Estado actual:
|
|
1924
|
-
- QUEUE.md tiene ahora la tarea asignada a ${toAgent}
|
|
1925
|
-
- El agente ${toAgent} está procediendo automáticamente
|
|
1926
|
-
|
|
1927
|
-
Acción: No necesitas hacer nada — solo toma nota del cambio. El orquestador将继续 automáticamente.
|
|
1928
|
-
Si quieres revisar el progreso, lee INBOX.md o STATUS.md.`
|
|
1929
|
-
: `⚠️ FALLBACK: Task "${task.id}: ${task.title}" failed on ${fromAgent} (${reason}) and was reassigned to ${toAgent}.
|
|
1930
|
-
|
|
1931
|
-
Current state:
|
|
1932
|
-
- QUEUE.md now has the task assigned to ${toAgent}
|
|
1933
|
-
- Agent ${toAgent} is proceeding automatically
|
|
1934
|
-
|
|
1935
|
-
Action: You don't need to do anything — just take note of the change. The orchestrator will continue automatically.
|
|
1936
|
-
If you want to check progress, read INBOX.md or STATUS.md.`;
|
|
1937
|
-
|
|
1938
|
-
const logPath = path.join(LOG_DIR, `fallback-notify-${Date.now()}.log`);
|
|
1939
|
-
try {
|
|
1940
|
-
const logFd = fs.openSync(logPath, 'a');
|
|
1941
|
-
const child = spawn('claude', ['-p', prompt, '--add-dir', WORKSPACE, '--dangerously-skip-permissions'], {
|
|
1942
|
-
cwd: WORKSPACE, stdio: ['ignore', logFd, logFd], shell: true, windowsHide: true, detached: true
|
|
1943
|
-
});
|
|
1944
|
-
fs.closeSync(logFd);
|
|
1945
|
-
child.unref();
|
|
1946
|
-
} catch {}
|
|
1947
|
-
}
|
|
1948
|
-
|
|
1949
1909
|
// ============================================================================
|
|
1950
1910
|
// KEYBOARD
|
|
1951
1911
|
// ============================================================================
|
|
@@ -2050,7 +2010,6 @@ function setupFallbackQueueWatcher() {
|
|
|
2050
2010
|
}
|
|
2051
2011
|
|
|
2052
2012
|
startQueueWatcher();
|
|
2053
|
-
setupQueueWatcher();
|
|
2054
2013
|
|
|
2055
2014
|
// Slow fallback (5 min) — only runs if there is actually pending work or busy agents
|
|
2056
2015
|
// fs.watch handles real-time; this is just a safety net
|
|
@@ -2062,92 +2021,12 @@ setInterval(() => {
|
|
|
2062
2021
|
renderDashboard();
|
|
2063
2022
|
}, 5 * 60 * 1000);
|
|
2064
2023
|
|
|
2065
|
-
//
|
|
2066
|
-
//
|
|
2067
|
-
//
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
let _lastInboxContent = '';
|
|
2071
|
-
let _inboxDispatching = false;
|
|
2072
|
-
|
|
2073
|
-
function dispatchInboxClaude() {
|
|
2074
|
-
if (_inboxDispatching) return;
|
|
2075
|
-
let content = '';
|
|
2076
|
-
try { content = fs.existsSync(INBOX_FILE) ? fs.readFileSync(INBOX_FILE, 'utf-8') : ''; } catch {}
|
|
2077
|
-
if (!content.trim() || content === _lastInboxContent) return;
|
|
2078
|
-
|
|
2079
|
-
_lastInboxContent = content;
|
|
2080
|
-
_inboxDispatching = true;
|
|
2081
|
-
|
|
2082
|
-
const lang = WORKSPACE_LANGUAGE;
|
|
2083
|
-
const prompt = lang === 'es'
|
|
2084
|
-
? `Eres el orquestador de este workspace. Tu única misión ahora es procesar el INBOX.
|
|
2085
|
-
|
|
2086
|
-
Pasos:
|
|
2087
|
-
1. Lee INBOX.md en ${WORKSPACE}
|
|
2088
|
-
2. Lee QUEUE.md en ${WORKSPACE} para ver las tareas existentes (secciones Pendientes, En progreso, Completadas)
|
|
2089
|
-
|
|
2090
|
-
Si en INBOX.md hay análisis completados de un agente (especialmente OpenCode) que aún NO tienen su tarea de implementación en la sección ## Pendientes de QUEUE.md:
|
|
2091
|
-
- Determina el siguiente TASK ID disponible leyendo QUEUE.md
|
|
2092
|
-
- Crea la nueva TASK en QUEUE.md con el formato exacto:
|
|
2093
|
-
TASK-NNN | título corto | Codex | P1 | repo | descripción basada en el análisis
|
|
2094
|
-
|
|
2095
|
-
Si ya existe la tarea correspondiente, o el análisis no está completo, responde solo: "Sin acción necesaria."
|
|
2096
|
-
|
|
2097
|
-
Reglas: No hagas commit ni push. No analices código del proyecto. Solo lee INBOX.md y QUEUE.md, y edita QUEUE.md si hace falta.`
|
|
2098
|
-
: `You are the orchestrator for this workspace. Your only mission now is to process the INBOX.
|
|
2099
|
-
|
|
2100
|
-
Steps:
|
|
2101
|
-
1. Read INBOX.md in ${WORKSPACE}
|
|
2102
|
-
2. Read QUEUE.md in ${WORKSPACE} to see existing tasks (sections Pending, In Progress, Completed)
|
|
2103
|
-
|
|
2104
|
-
If INBOX.md contains completed analyses from an agent (especially OpenCode) that do NOT yet have a corresponding implementation task in the ## Pending section of QUEUE.md:
|
|
2105
|
-
- Determine the next available TASK ID by reading QUEUE.md
|
|
2106
|
-
- Create the new TASK in QUEUE.md with the exact format:
|
|
2107
|
-
TASK-NNN | short title | Codex | P1 | repo | description based on the analysis
|
|
2108
|
-
|
|
2109
|
-
If the corresponding task already exists, or the analysis is not complete, reply only: "No action needed."
|
|
2110
|
-
|
|
2111
|
-
Rules: Do not commit or push. Do not analyze project code. Only read INBOX.md and QUEUE.md, and edit QUEUE.md if necessary.`;
|
|
2112
|
-
|
|
2113
|
-
const logPath = path.join(LOG_DIR, `inbox-trigger-${Date.now()}.log`);
|
|
2114
|
-
try {
|
|
2115
|
-
const logFd = fs.openSync(logPath, 'a');
|
|
2116
|
-
const child = spawn('claude', [
|
|
2117
|
-
'-p', prompt,
|
|
2118
|
-
'--add-dir', WORKSPACE,
|
|
2119
|
-
'--dangerously-skip-permissions'
|
|
2120
|
-
], {
|
|
2121
|
-
cwd: WORKSPACE,
|
|
2122
|
-
stdio: ['ignore', logFd, logFd],
|
|
2123
|
-
shell: true,
|
|
2124
|
-
windowsHide: true,
|
|
2125
|
-
detached: true
|
|
2126
|
-
});
|
|
2127
|
-
fs.closeSync(logFd);
|
|
2128
|
-
child.unref();
|
|
2129
|
-
log('INFO', lang === 'es'
|
|
2130
|
-
? 'INBOX: Claude despachado para procesar notificación'
|
|
2131
|
-
: 'INBOX: Claude dispatched to process notification');
|
|
2132
|
-
} catch {}
|
|
2133
|
-
setTimeout(() => { _inboxDispatching = false; }, 3 * 60 * 1000);
|
|
2134
|
-
}
|
|
2135
|
-
|
|
2136
|
-
function startInboxWatcher() {
|
|
2137
|
-
if (!fs.existsSync(INBOX_FILE)) {
|
|
2138
|
-
try { fs.writeFileSync(INBOX_FILE, '', 'utf-8'); } catch {}
|
|
2139
|
-
}
|
|
2140
|
-
try {
|
|
2141
|
-
const watchName = path.basename(INBOX_FILE);
|
|
2142
|
-
const watcher = fs.watch(WORKSPACE, {persistent: false}, (eventType, filename) => {
|
|
2143
|
-
if (filename !== watchName) return;
|
|
2144
|
-
if (_inboxDebounce) clearTimeout(_inboxDebounce);
|
|
2145
|
-
_inboxDebounce = setTimeout(dispatchInboxClaude, 100);
|
|
2146
|
-
});
|
|
2147
|
-
watcher.on('error', () => {});
|
|
2148
|
-
} catch {}
|
|
2024
|
+
// INBOX.md initialization — create empty file if it doesn't exist so the
|
|
2025
|
+
// Claude session can watch it. Notifications are delivered via NOTIFY.md +
|
|
2026
|
+
// the asyncRewake hook in .claude/settings.json (watch-notify.js).
|
|
2027
|
+
if (!fs.existsSync(INBOX_FILE)) {
|
|
2028
|
+
try { fs.writeFileSync(INBOX_FILE, '', 'utf-8'); } catch {}
|
|
2149
2029
|
}
|
|
2150
|
-
startInboxWatcher();
|
|
2151
2030
|
|
|
2152
2031
|
// ============================================================================
|
|
2153
2032
|
// AWAY MODE WATCHER — monitors .away-mode file; when active runs periodic
|
|
@@ -2169,7 +2048,7 @@ function runAwayModeCheck() {
|
|
|
2169
2048
|
const completedCount = (state.completed || []).length;
|
|
2170
2049
|
const hasWork = pendingTasks.length > 0 || inProgressTasks.length > 0 || busy;
|
|
2171
2050
|
|
|
2172
|
-
if (!hasWork
|
|
2051
|
+
if (!hasWork) {
|
|
2173
2052
|
try { fs.unlinkSync(AWAY_MODE_FILE); } catch {}
|
|
2174
2053
|
deactivateAwayMode();
|
|
2175
2054
|
|
package/package.json
CHANGED
|
@@ -1,65 +1,64 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@liriraid/agentflow-ai",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Multi-agent workspace orchestrator with TUI. Coordinates AI coding agents over your real frontend and backend projects.",
|
|
5
|
-
"author": "LiriRaid",
|
|
6
|
-
"homepage": "https://github.com/LiriRaid/agentflow-ai#readme",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/LiriRaid/agentflow-ai.git"
|
|
10
|
-
},
|
|
11
|
-
"bugs": {
|
|
12
|
-
"url": "https://github.com/LiriRaid/agentflow-ai/issues"
|
|
13
|
-
},
|
|
14
|
-
"publishConfig": {
|
|
15
|
-
"access": "public"
|
|
16
|
-
},
|
|
17
|
-
"main": "orchestrator.js",
|
|
18
|
-
"bin": {
|
|
19
|
-
"agentflow": "bin/agentflow.mjs"
|
|
20
|
-
},
|
|
21
|
-
"files": [
|
|
22
|
-
"bin",
|
|
23
|
-
"src",
|
|
24
|
-
"templates",
|
|
25
|
-
"orchestrator.js",
|
|
26
|
-
"LICENSE",
|
|
27
|
-
"README.md"
|
|
28
|
-
],
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@liriraid/agentflow-ai",
|
|
3
|
+
"version": "1.0.27",
|
|
4
|
+
"description": "Multi-agent workspace orchestrator with TUI. Coordinates AI coding agents over your real frontend and backend projects.",
|
|
5
|
+
"author": "LiriRaid",
|
|
6
|
+
"homepage": "https://github.com/LiriRaid/agentflow-ai#readme",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/LiriRaid/agentflow-ai.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/LiriRaid/agentflow-ai/issues"
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"main": "orchestrator.js",
|
|
18
|
+
"bin": {
|
|
19
|
+
"agentflow": "bin/agentflow.mjs"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"bin",
|
|
23
|
+
"src",
|
|
24
|
+
"templates",
|
|
25
|
+
"orchestrator.js",
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"README.md"
|
|
28
|
+
],
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18.0.0"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"ai",
|
|
34
|
+
"agents",
|
|
35
|
+
"orchestrator",
|
|
36
|
+
"agentflow-ai",
|
|
37
|
+
"claude",
|
|
38
|
+
"codex",
|
|
39
|
+
"gemini",
|
|
40
|
+
"cursor",
|
|
41
|
+
"opencode",
|
|
42
|
+
"abacus",
|
|
43
|
+
"tui",
|
|
44
|
+
"multi-agent",
|
|
45
|
+
"coding-assistant",
|
|
46
|
+
"openspec",
|
|
47
|
+
"engram",
|
|
48
|
+
"skills",
|
|
49
|
+
"agent-config"
|
|
50
|
+
],
|
|
51
|
+
"license": "MIT",
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"blessed": "^0.1.81",
|
|
54
|
+
"ink": "^5.2.1",
|
|
55
|
+
"react": "^18.3.1"
|
|
56
|
+
},
|
|
57
|
+
"scripts": {
|
|
58
|
+
"start": "node orchestrator.js",
|
|
59
|
+
"start:paused": "node orchestrator.js --paused",
|
|
60
|
+
"start:ink": "node src/ink/index.mjs",
|
|
61
|
+
"start:ink:paused": "node src/ink/index.mjs --paused",
|
|
62
|
+
"cli:help": "node bin/agentflow.mjs --help"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
// Background watcher for NOTIFY.md (asyncRewake hook).
|
|
4
|
+
// Launched on every Stop event. Polls until NOTIFY.md appears, then
|
|
5
|
+
// exits with code 2 to wake the Claude session with the notification.
|
|
6
|
+
// If .away-mode exists, exits immediately — Away Mode handles monitoring.
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
const cwd = process.cwd();
|
|
11
|
+
const notifyFile = path.join(cwd, 'NOTIFY.md');
|
|
12
|
+
const awayFile = path.join(cwd, '.away-mode');
|
|
13
|
+
|
|
14
|
+
if (fs.existsSync(awayFile)) process.exit(0);
|
|
15
|
+
if (fs.existsSync(notifyFile)) deliver();
|
|
16
|
+
|
|
17
|
+
const POLL_MS = 10_000;
|
|
18
|
+
const MAX_MS = 30 * 60_000;
|
|
19
|
+
const start = Date.now();
|
|
20
|
+
|
|
21
|
+
const timer = setInterval(() => {
|
|
22
|
+
if (fs.existsSync(awayFile)) { clearInterval(timer); process.exit(0); }
|
|
23
|
+
if (Date.now() - start > MAX_MS) { clearInterval(timer); process.exit(0); }
|
|
24
|
+
if (fs.existsSync(notifyFile)) { clearInterval(timer); deliver(); }
|
|
25
|
+
}, POLL_MS);
|
|
26
|
+
|
|
27
|
+
function deliver() {
|
|
28
|
+
const content = fs.readFileSync(notifyFile, 'utf8').trim();
|
|
29
|
+
try { fs.unlinkSync(notifyFile); } catch {}
|
|
30
|
+
if (!content) process.exit(0);
|
|
31
|
+
process.stdout.write('\n' + content + '\n');
|
|
32
|
+
process.exit(2);
|
|
33
|
+
}
|
|
@@ -16,7 +16,7 @@ Gather useful context before creating TASKs or OpenSpec artifacts.
|
|
|
16
16
|
|
|
17
17
|
## Critical Rules
|
|
18
18
|
|
|
19
|
-
- Understand the user's exact scope first
|
|
19
|
+
- Understand the user's exact scope first — clarify by asking the user directly, **NOT by reading project files yourself**.
|
|
20
20
|
- Prefer exploration before implementation when context is unclear.
|
|
21
21
|
- Use **ONLY OpenCode** as the exploration agent when deep codebase analysis is needed — its role is **EXCLUSIVELY analysis**, **NEVER implementation**.
|
|
22
22
|
- When delegating exploration to OpenCode, include in the brief exactly what it must report: flows, dependencies, architecture findings, inconsistencies, etc.
|
|
@@ -47,6 +47,23 @@ The TUI handles automatic fallback: Codex fails → Claude-Worker directly. You
|
|
|
47
47
|
|
|
48
48
|
The `repo` field determines the working directory: `frontend` for UI/client work, `backend` for API/server work. Codex and OpenCode can work in either repo depending on the task.
|
|
49
49
|
|
|
50
|
+
### Parallel Execution Rule
|
|
51
|
+
|
|
52
|
+
**Never queue all tasks behind one OpenCode analysis if context already exists.**
|
|
53
|
+
|
|
54
|
+
When multiple tasks arrive:
|
|
55
|
+
|
|
56
|
+
- **Check context first**: Are there existing OpenCode reports in `progress/PROGRESS-OpenCode.md` or `INBOX.md` for the area being touched? Is this a long session where the codebase is already well understood?
|
|
57
|
+
- **If context is sufficient** → assign all implementation tasks directly to Codex (and Claude-Worker for overflow) in parallel. Do NOT send them to OpenCode first.
|
|
58
|
+
- **If context is missing for a specific area** → send ONE task to OpenCode for that area. Assign all other independent tasks to Codex in parallel — do not make them wait.
|
|
59
|
+
- **Never serialize work that can run in parallel.** If 5 tasks are independent and context is clear, all 5 go out at once to available agents.
|
|
60
|
+
|
|
61
|
+
**When is context "sufficient"?**
|
|
62
|
+
- OpenCode already reported on this area in the current session.
|
|
63
|
+
- The task is a direct follow-up to a completed task (same files, same pattern).
|
|
64
|
+
- The user explicitly defined what to change (specific files, keys, values) — no exploration needed.
|
|
65
|
+
- Similar tasks were already completed successfully in this session.
|
|
66
|
+
|
|
50
67
|
## This Workspace Is NOT the Real Project
|
|
51
68
|
|
|
52
69
|
This directory (`orchestrator-<name>`) exists **only** for work management:
|
|
@@ -57,7 +74,7 @@ This directory (`orchestrator-<name>`) exists **only** for work management:
|
|
|
57
74
|
- `logs/` — agent output
|
|
58
75
|
|
|
59
76
|
The real project code lives at the paths defined in `orchestrator.config.json → repos`.
|
|
60
|
-
|
|
77
|
+
**Do NOT read real project code files to understand the project.** If codebase analysis is needed to plan tasks, delegate it to OpenCode via a TASK in `QUEUE.md` — that is what OpenCode is for.
|
|
61
78
|
**Never modify real project files directly** — that is exclusively the workers' job.
|
|
62
79
|
|
|
63
80
|
## Startup Checklist
|
|
@@ -119,14 +136,16 @@ del .away-mode
|
|
|
119
136
|
|
|
120
137
|
## Fallback Policy
|
|
121
138
|
|
|
122
|
-
The TUI handles fallback automatically following
|
|
139
|
+
The TUI handles fallback automatically following these chains:
|
|
123
140
|
|
|
141
|
+
**By failure:**
|
|
124
142
|
```
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
Codex fails → Frontend (frontend repo) or Backend (backend repo) directly
|
|
143
|
+
OpenCode fails → Codex → Claude-Worker (Frontend/Backend)
|
|
144
|
+
Codex fails → OpenCode → Claude-Worker (Frontend/Backend)
|
|
128
145
|
```
|
|
129
146
|
|
|
147
|
+
**By availability (when assigning new tasks):** Claude-Orchestrator should check `STATUS.md` before writing to `QUEUE.md`. If the primary agent for a task is already busy or rate-limited, assign directly to the next available idle agent in the chain rather than queuing behind a busy one.
|
|
148
|
+
|
|
130
149
|
As Orchestrator you do **not** need to manually reassign on failure — the TUI does it. Your role is:
|
|
131
150
|
|
|
132
151
|
1. Check `INBOX.md` or `QUEUE.md` to confirm the fallback ran correctly.
|
|
@@ -160,11 +179,12 @@ Default agent summary:
|
|
|
160
179
|
## How To Assign Work
|
|
161
180
|
|
|
162
181
|
1. **When the user asks for a change or new task** → **NEVER analyze directly yourself**
|
|
163
|
-
- **If
|
|
164
|
-
- **
|
|
165
|
-
- **
|
|
182
|
+
- **If context already exists** (OpenCode reports from this session, completed similar tasks, user-specified exact changes): **skip OpenCode** and create implementation TASKs directly assigned to **Codex** or **Claude-Worker**. Assign all independent tasks in parallel.
|
|
183
|
+
- **If prior analysis is needed** (new area, unknown structure, no prior report): Create ONE TASK in `QUEUE.md` assigned to **OpenCode** for that area. Assign all other independent tasks to Codex in parallel — do not make them wait for OpenCode.
|
|
184
|
+
- **Wait for the OpenCode report only for the tasks that depend on it**: OpenCode writes findings to `progress/PROGRESS-OpenCode.md` and notifies in `INBOX.md`
|
|
185
|
+
- **Then implement the dependent task**: **READ OPENCODE'S REPORT** and create the implementation TASK assigned to **Codex**
|
|
166
186
|
- **OpenCode DOES NOT implement** — its TASKs are **ONLY for analysis**; implementation **ALWAYS** goes to Codex or Claude-Worker
|
|
167
|
-
- **NEVER
|
|
187
|
+
- **NEVER analyze the project code yourself (Claude-Orchestrator)** — use OpenCode for that. If a report already exists, **USE THAT CONTEXT** directly.
|
|
168
188
|
|
|
169
189
|
2. Write TASKs in `QUEUE.md` with this format:
|
|
170
190
|
|
|
@@ -202,10 +222,11 @@ Routing preferences:
|
|
|
202
222
|
8. Use Engram for durable decisions, bugs, discoveries, and session summaries.
|
|
203
223
|
9. Use `openspec/changes/<change-name>/` for large changes.
|
|
204
224
|
10. Claude remains the final reviewer before work is considered accepted.
|
|
205
|
-
11. **MANDATORY VERIFICATION:** Before creating
|
|
206
|
-
- There is an OpenCode report in `INBOX.md` or `progress/PROGRESS-OpenCode.md`
|
|
207
|
-
- The
|
|
208
|
-
-
|
|
225
|
+
11. **MANDATORY VERIFICATION:** Before creating implementation TASKs, confirm one of:
|
|
226
|
+
- There is an OpenCode report in `INBOX.md` or `progress/PROGRESS-OpenCode.md` covering the area, OR
|
|
227
|
+
- The user provided explicit enough detail (exact files, values, changes) that no exploration is needed, OR
|
|
228
|
+
- Similar work was already completed this session (context is already established).
|
|
229
|
+
- **YOU (Claude-Orchestrator) HAVE NOT** analyzed the code yourself in any case.
|
|
209
230
|
|
|
210
231
|
## TUI Controls
|
|
211
232
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
// Watcher en background para NOTIFY.md (hook asyncRewake).
|
|
4
|
+
// Se lanza en cada evento Stop. Espera hasta que aparezca NOTIFY.md y
|
|
5
|
+
// sale con código 2 para despertar la sesión de Claude con la notificación.
|
|
6
|
+
// Si existe .away-mode, sale inmediatamente — el Away Mode maneja el monitoreo.
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
const cwd = process.cwd();
|
|
11
|
+
const notifyFile = path.join(cwd, 'NOTIFY.md');
|
|
12
|
+
const awayFile = path.join(cwd, '.away-mode');
|
|
13
|
+
|
|
14
|
+
if (fs.existsSync(awayFile)) process.exit(0);
|
|
15
|
+
if (fs.existsSync(notifyFile)) deliver();
|
|
16
|
+
|
|
17
|
+
const POLL_MS = 10_000;
|
|
18
|
+
const MAX_MS = 30 * 60_000;
|
|
19
|
+
const start = Date.now();
|
|
20
|
+
|
|
21
|
+
const timer = setInterval(() => {
|
|
22
|
+
if (fs.existsSync(awayFile)) { clearInterval(timer); process.exit(0); }
|
|
23
|
+
if (Date.now() - start > MAX_MS) { clearInterval(timer); process.exit(0); }
|
|
24
|
+
if (fs.existsSync(notifyFile)) { clearInterval(timer); deliver(); }
|
|
25
|
+
}, POLL_MS);
|
|
26
|
+
|
|
27
|
+
function deliver() {
|
|
28
|
+
const content = fs.readFileSync(notifyFile, 'utf8').trim();
|
|
29
|
+
try { fs.unlinkSync(notifyFile); } catch {}
|
|
30
|
+
if (!content) process.exit(0);
|
|
31
|
+
process.stdout.write('\n' + content + '\n');
|
|
32
|
+
process.exit(2);
|
|
33
|
+
}
|
|
@@ -17,7 +17,7 @@ Guiar la fase de exploración del orquestador para reunir contexto útil antes d
|
|
|
17
17
|
|
|
18
18
|
## Reglas críticas
|
|
19
19
|
|
|
20
|
-
- Empieza por entender el alcance exacto del pedido del usuario
|
|
20
|
+
- Empieza por entender el alcance exacto del pedido del usuario — acláralo preguntándole directamente, **NO leyendo archivos del proyecto tú mismo**.
|
|
21
21
|
- Si hace falta lectura amplia, prioriza exploración y análisis antes de planear implementación.
|
|
22
22
|
- Usa **SOLO OpenCode** como agente de exploración cuando necesites análisis profundo del codebase — su rol es **EXCLUSIVAMENTE análisis**, **NUNCA implementación**.
|
|
23
23
|
- Al delegar exploración a OpenCode, incluye en el brief exactamente qué debe reportar: flujos, dependencias, hallazgos de arquitectura, inconsistencias, etc.
|
|
@@ -38,7 +38,7 @@ Este directorio (`orchestrator-<nombre>`) existe **únicamente** para gestión d
|
|
|
38
38
|
- `logs/` — salida de los agentes
|
|
39
39
|
|
|
40
40
|
El código real del proyecto vive en las rutas definidas en `orchestrator.config.json → repos`.
|
|
41
|
-
|
|
41
|
+
**NO leas archivos de código del proyecto real para planificar tareas.** Si necesitas análisis del codebase para crear TASKs, delégalo a OpenCode mediante una TASK en `QUEUE.md` — para eso existe OpenCode.
|
|
42
42
|
**Nunca modifiques archivos del proyecto real directamente** — eso es trabajo exclusivo de los agentes workers.
|
|
43
43
|
|
|
44
44
|
## Al iniciar la sesión — OBLIGATORIO
|
|
@@ -132,14 +132,16 @@ del .away-mode
|
|
|
132
132
|
|
|
133
133
|
### Fallback por cuota o indisponibilidad
|
|
134
134
|
|
|
135
|
-
La TUI gestiona el fallback automáticamente siguiendo
|
|
135
|
+
La TUI gestiona el fallback automáticamente siguiendo estas cadenas:
|
|
136
136
|
|
|
137
|
+
**Por fallo:**
|
|
137
138
|
```
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
Codex falla → Frontend (repo FE) o Backend (repo BE) directamente
|
|
139
|
+
OpenCode falla → Codex → Claude-Worker (Frontend/Backend)
|
|
140
|
+
Codex falla → OpenCode → Claude-Worker (Frontend/Backend)
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
+
**Por disponibilidad (al asignar tareas nuevas):** Claude-Orquestador debe revisar `STATUS.md` antes de escribir en `QUEUE.md`. Si el agente primario para una tarea ya está ocupado o tiene rate limit, asigna directamente al siguiente agente idle en la cadena en lugar de dejar la tarea esperando detrás de uno ocupado.
|
|
144
|
+
|
|
143
145
|
Como Orquestador, **no necesitas reasignar manualmente** cuando hay un fallo — la TUI lo hace sola. Tu rol en este caso es:
|
|
144
146
|
|
|
145
147
|
1. Verificar en `INBOX.md` o `QUEUE.md` que el fallback se ejecutó correctamente.
|
|
@@ -180,11 +182,12 @@ Revisa `orchestrator.config.json` → `agents`. Cada entrada tiene:
|
|
|
180
182
|
## Cómo asignar trabajo
|
|
181
183
|
|
|
182
184
|
1. **Cuando el usuario pide un cambio o nueva tarea** → **NUNCA analices directamente**
|
|
183
|
-
- **Si
|
|
184
|
-
- **
|
|
185
|
-
- **
|
|
185
|
+
- **Si ya existe contexto suficiente** (reportes de OpenCode de esta sesión, tareas similares ya completadas, cambios que el usuario especificó exactamente): **omite OpenCode** y crea TASKs de implementación directamente asignadas a **Codex** o **Claude-Worker**. Asigna todas las tareas independientes en paralelo.
|
|
186
|
+
- **Si se necesita análisis previo** (área nueva, estructura desconocida, sin reporte previo): Crea UNA TASK asignada a **OpenCode** para esa área. Asigna todas las demás tareas independientes a Codex en paralelo — no las hagas esperar a OpenCode.
|
|
187
|
+
- **Espera el reporte de OpenCode solo para las tareas que dependen de él**: OpenCode escribe hallazgos en `progress/PROGRESS-OpenCode.md` y notifica en `INBOX.md`
|
|
188
|
+
- **Luego implementa la tarea dependiente**: **LEE EL REPORTE DE OPENCODE** y crea la TASK de implementación asignada a **Codex**
|
|
186
189
|
- **OpenCode NO implementa** — sus TASKs son **SOLO de análisis**; la implementación **SIEMPRE** va a Codex o Claude-Worker
|
|
187
|
-
- **NUNCA
|
|
190
|
+
- **NUNCA analices el código del proyecto tú mismo (Claude-Orquestador)** — usa OpenCode para eso. Si ya existe un reporte, **USA ESE CONTEXTO** directamente.
|
|
188
191
|
|
|
189
192
|
2. Escribe TASKs en `QUEUE.md` (formato pipe; la TUI lo lee):
|
|
190
193
|
```
|
|
@@ -198,12 +201,16 @@ Revisa `orchestrator.config.json` → `agents`. Cada entrada tiene:
|
|
|
198
201
|
6. **La TUI inicia automáticamente** - NO necesitas presionar R ni S. La TUI detecta nuevas tasks y las lanza.
|
|
199
202
|
7. **Codex es la primera opción para implementación; OpenCode es la segunda opción.** Claude-Worker es el fallback automático de Codex/OpenCode y también toma trabajo cuando hay más tareas que agentes disponibles.
|
|
200
203
|
8. **REGLA CRÍTICA SOBRE ANÁLISIS:** Si OpenCode ya analizó algo y escribió su reporte en `INBOX.md` o `progress/PROGRESS-OpenCode.md`, **TÚ (Claude-Orquestador) NO DEBES VOLVER A ANALIZAR EL MISMO CÓDIGO**. Usa el reporte existente para crear tareas de implementación. Si necesitas más detalles, pide a OpenCode que haga un análisis adicional con una nueva TASK, pero **NUNCA lo hagas tú directamente**.
|
|
201
|
-
9.
|
|
202
|
-
- **
|
|
203
|
-
- **
|
|
204
|
-
- **
|
|
205
|
-
|
|
206
|
-
|
|
204
|
+
9. **Regla de ejecución en paralelo — nunca serialices lo que puede correr en paralelo:**
|
|
205
|
+
- **Si el contexto ya existe**: asigna todas las tareas independientes en paralelo a Codex y Claude-Worker a la vez. No las pongas en cola una por una.
|
|
206
|
+
- **Si falta contexto para un área**: manda UNA TASK a OpenCode para esa área. Las demás tareas independientes van a Codex en paralelo — no esperan a OpenCode.
|
|
207
|
+
- **Distribución por volumen:**
|
|
208
|
+
- 1 tarea de análisis → OpenCode
|
|
209
|
+
- 1 tarea de implementación → Codex
|
|
210
|
+
- 2 tareas paralelas → OpenCode (análisis) + Codex (implementación con spec clara)
|
|
211
|
+
- 3+ tareas independientes con contexto claro → Codex + Frontend/Backend en paralelo
|
|
212
|
+
- **¿Cuándo el contexto es suficiente?** OpenCode ya reportó sobre esa área en esta sesión / el usuario especificó exactamente qué cambiar / tareas similares ya se completaron esta sesión.
|
|
213
|
+
10. Si hay más TASKs que agentes disponibles, deja el resto en cola con dependencias claras o prioridad menor; no uses Gemini, Cursor ni Abacus salvo permiso explícito.
|
|
207
214
|
9. El campo `repo` determina en qué directorio trabaja el agente. Usa siempre el valor correcto: `frontend` para trabajo de UI/cliente, `backend` para trabajo de API/servidor. Codex y OpenCode pueden trabajar en ambos repos según lo que indique la task.
|
|
208
215
|
|
|
209
216
|
## Reglas
|
|
@@ -216,12 +223,13 @@ Revisa `orchestrator.config.json` → `agents`. Cada entrada tiene:
|
|
|
216
223
|
6. Al terminar la sesión, escribe un `handoffs/HANDOFF-<fecha>.md` resumiendo qué se hizo y qué sigue.
|
|
217
224
|
7. **Por defecto solo usa Claude, Codex y OpenCode**. No uses Gemini, Cursor ni Abacus salvo instrucción explícita del usuario.
|
|
218
225
|
8. Si el usuario activa **Modo Ausencia**, revisa progreso cada 5 minutos y reasigna nuevas TASKs razonables dentro del alcance actual sin esperar confirmación intermedia.
|
|
219
|
-
9. La TUI gestiona el fallback automáticamente:
|
|
226
|
+
9. La TUI gestiona el fallback automáticamente: OpenCode falla → Codex → Claude-Worker; Codex falla → OpenCode → Claude-Worker. Solo intervén manualmente si la tarea queda marcada `failed`.
|
|
220
227
|
10. Usa Engram para guardar decisiones, hallazgos, bugs y resúmenes de sesión; no dependas solo del contexto corto de la conversación.
|
|
221
|
-
11. **VERIFICACIÓN OBLIGATORIA:** Antes de crear
|
|
222
|
-
- Existe un reporte de OpenCode en `INBOX.md` o `progress/PROGRESS-OpenCode.md`
|
|
223
|
-
-
|
|
224
|
-
-
|
|
228
|
+
11. **VERIFICACIÓN OBLIGATORIA:** Antes de crear TASKs de implementación, confirma una de estas condiciones:
|
|
229
|
+
- Existe un reporte de OpenCode en `INBOX.md` o `progress/PROGRESS-OpenCode.md` que cubre el área, O
|
|
230
|
+
- El usuario especificó con suficiente detalle qué cambiar (archivos exactos, valores, cambios concretos), O
|
|
231
|
+
- Trabajo similar ya se completó esta sesión (el contexto ya está establecido).
|
|
232
|
+
- **NO** has analizado el código tú mismo (Claude-Orquestador) en ningún caso.
|
|
225
233
|
11. Para cambios grandes, usa `openspec/changes/<change-name>/` para proposal, spec, design, tasks y verify; no dejes todo solo en la conversación.
|
|
226
234
|
12. No asumas bypass total o autoaceptación de cambios en los agentes. Claude debe seguir siendo la autoridad final para validar el resultado esperado antes de que el usuario dé la aprobación definitiva.
|
|
227
235
|
|