@liriraid/agentflow-ai 1.0.19 β 1.0.21
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 +205 -52
- package/orchestrator.js +109 -10
- package/package.json +1 -1
- package/src/ink/index.mjs +8 -3
- package/templates/en/.claude/hooks/notify-check.js +20 -0
- package/templates/en/.claude/settings.json +24 -0
- package/templates/en/.claude/skills/orchestrator-explore/SKILL.md +7 -4
- package/templates/en/.claude/skills/orchestrator-queue-planning/SKILL.md +24 -5
- package/templates/en/AGENT-CONFIG.md +3 -2
- package/templates/en/ORCHESTRATOR.md +30 -18
- package/templates/en/README.md +182 -137
- package/templates/en/agents/OPENCODE.md +68 -23
- package/templates/es/.claude/hooks/notify-check.js +20 -0
- package/templates/es/.claude/settings.json +24 -0
- package/templates/es/.claude/skills/orchestrator-explore/SKILL.md +7 -4
- package/templates/es/.claude/skills/orchestrator-queue-planning/SKILL.md +25 -8
- package/templates/es/AGENT-CONFIG.md +3 -2
- package/templates/es/ORCHESTRATOR.md +32 -20
- package/templates/es/README.md +222 -29
- package/templates/es/agents/OPENCODE.md +63 -18
- package/templates/es/orchestrator.config.json +1 -1
package/README.md
CHANGED
|
@@ -1,79 +1,232 @@
|
|
|
1
|
-
# agentflow
|
|
1
|
+
# agentflow-ai
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Multi-Agent Orchestration System for AI-Powered Development**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
A reusable workspace orchestrator that coordinates multiple AI coding agents (Claude, Codex, OpenCode, etc.) to work **in parallel** on real projects, while keeping the project repository **completely clean** of orchestrator files.
|
|
6
6
|
|
|
7
7
|
```text
|
|
8
|
-
|
|
9
|
-
my-
|
|
10
|
-
orchestrator-my-
|
|
8
|
+
project-workspace/
|
|
9
|
+
my-project/ # Real project (stays clean)
|
|
10
|
+
orchestrator-my-project/ # Orchestrator workspace (generated)
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
##
|
|
14
|
-
|
|
13
|
+
## π― What It Does
|
|
14
|
+
|
|
15
|
+
- **Coordinates multiple AI agents** (Claude, Codex, OpenCode, Gemini, Cursor, Abacus) to work simultaneously on your project.
|
|
16
|
+
- **Real-time monitoring** with a modern TUI (Terminal User Interface) that shows live agent status, queue, and progress.
|
|
17
|
+
- **Automatic task delegation** based on agent specialization (analysis, implementation, code review).
|
|
18
|
+
- **Persistent memory** using Engram to maintain context across sessions.
|
|
19
|
+
- **Spec-Driven Development (SDD)** support with OpenSpec for large, multi-phase changes.
|
|
20
|
+
- **Fallback system** that automatically reassigns tasks when an agent fails or reaches rate limits.
|
|
21
|
+
- **Multi-language support** (English and Spanish) for all templates and documentation.
|
|
22
|
+
|
|
23
|
+
## β¨ Key Features
|
|
24
|
+
|
|
25
|
+
### 1. **Sibling Workspace Model**
|
|
26
|
+
- The orchestrator creates a **separate workspace** next to your real project.
|
|
27
|
+
- Your project repository **stays completely clean** (no `QUEUE.md`, `logs/`, or orchestrator files).
|
|
28
|
+
- Agents work on the real project files via absolute paths configured in `orchestrator.config.json`.
|
|
29
|
+
|
|
30
|
+
### 2. **Multi-Agent Coordination**
|
|
31
|
+
| Agent | Role | Priority | Notes |
|
|
32
|
+
|-------|------|----------|-------|
|
|
33
|
+
| **Claude-Orchestrator** | Session coordinator | - | Never implements code directly; delegates to workers |
|
|
34
|
+
| **Codex** | Primary implementation | 1st choice | Structured tasks, tests, docs |
|
|
35
|
+
| **OpenCode** | Analysis + Implementation | 2nd choice | Uses Mistral Medium 3.5 128B for coding |
|
|
36
|
+
| **Claude-Worker** (Backend/Frontend) | Fallback implementation | 3rd choice | Takes over when Codex/OpenCode fail |
|
|
37
|
+
| **Gemini** | Code review/audits | Optional | Disabled by default |
|
|
38
|
+
| **Cursor/Abacus** | Mechanical tasks | Optional | Disabled by default |
|
|
39
|
+
|
|
40
|
+
### 3. **Real-Time Operation**
|
|
41
|
+
- **fs.watch on QUEUE.md**: Detects changes in **~1-2 seconds** (Linux/macOS: direct file watch; Windows: directory watch fallback).
|
|
42
|
+
- **Live TUI updates**: The dashboard refreshes automatically when tasks are added, started, or completed.
|
|
43
|
+
- **Instant notifications**: Claude-Orchestrator receives alerts in `INBOX.md` and `NOTIFY.md` when tasks finish.
|
|
44
|
+
|
|
45
|
+
### 4. **Smart Task Delegation**
|
|
46
|
+
- **Analysis tasks** β Always assigned to **OpenCode**.
|
|
47
|
+
- **Implementation tasks** β Assigned to **Codex** (1st) β **OpenCode** (2nd, if using Mistral Medium 3.5 128B) β **Claude-Worker** (3rd).
|
|
48
|
+
- **Fallback chain**: `Codex β OpenCode β Claude-Worker` (automatic).
|
|
49
|
+
|
|
50
|
+
### 5. **Persistent Memory & SDD**
|
|
51
|
+
- **Engram**: Stores decisions, bugs, and findings across sessions.
|
|
52
|
+
- **OpenSpec**: Supports `proposal.md`, `spec.md`, `design.md`, `tasks.md`, and `verify-report.md` for large changes.
|
|
53
|
+
- **Handoffs**: Session summaries for continuity.
|
|
54
|
+
|
|
55
|
+
## π Installation
|
|
56
|
+
|
|
57
|
+
### Global CLI (Recommended)
|
|
15
58
|
```bash
|
|
16
|
-
npm i -g @liriraid/agentflow
|
|
59
|
+
npm i -g @liriraid/agentflow-ai
|
|
17
60
|
```
|
|
18
61
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
Interactive language selection:
|
|
22
|
-
|
|
62
|
+
### Local Development
|
|
23
63
|
```bash
|
|
24
|
-
|
|
64
|
+
git clone https://github.com/LiriRaid/agentflow-ai.git
|
|
65
|
+
cd agentflow-ai
|
|
66
|
+
npm install
|
|
25
67
|
```
|
|
26
68
|
|
|
27
|
-
|
|
69
|
+
## π οΈ Quick Start
|
|
28
70
|
|
|
71
|
+
### 1. Create an Orchestrator Workspace
|
|
29
72
|
```bash
|
|
73
|
+
# Interactive (asks for language)
|
|
74
|
+
agentflow init-workspace C:/code/my-project
|
|
75
|
+
|
|
76
|
+
# Direct (English)
|
|
30
77
|
agentflow init-workspace C:/code/my-project --lang en
|
|
78
|
+
|
|
79
|
+
# Direct (Spanish)
|
|
31
80
|
agentflow init-workspace C:/code/mi-proyecto --lang es
|
|
32
81
|
```
|
|
82
|
+
This creates a sibling workspace (e.g., `orchestrator-my-project/`) with all configuration files.
|
|
83
|
+
|
|
84
|
+
### 2. Configure Repositories
|
|
85
|
+
Edit `orchestrator.config.json` in the generated workspace:
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"repos": {
|
|
89
|
+
"backend": "C:/code/my-backend",
|
|
90
|
+
"frontend": "C:/code/my-frontend"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
33
94
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- `ORCHESTRATOR.md`
|
|
37
|
-
- `CLAUDE.md`
|
|
38
|
-
- `QUEUE.md`
|
|
39
|
-
- `PROJECT.md`
|
|
40
|
-
- `README.md`
|
|
41
|
-
- `agents/`
|
|
42
|
-
- `docs/`
|
|
43
|
-
- `openspec/`
|
|
44
|
-
- `.claude/`
|
|
45
|
-
- `.codex/`
|
|
46
|
-
- `.opencode/`
|
|
47
|
-
- `.atl/`
|
|
48
|
-
- `orchestrator.config.json`
|
|
49
|
-
|
|
50
|
-
## Runtime
|
|
51
|
-
|
|
52
|
-
The package runtime remains language-aware through `workspaceLanguage` in the generated `orchestrator.config.json`.
|
|
53
|
-
|
|
54
|
-
Start the TUI from the generated orchestrator workspace:
|
|
55
|
-
|
|
95
|
+
### 3. Start the TUI
|
|
56
96
|
```bash
|
|
57
|
-
cd
|
|
97
|
+
cd orchestrator-my-project
|
|
58
98
|
agentflow ink --paused
|
|
59
99
|
```
|
|
100
|
+
**Controls:**
|
|
101
|
+
- `S`: Start/Resume
|
|
102
|
+
- `P`: Pause
|
|
103
|
+
- `R`: Reload queue
|
|
104
|
+
- `Q`: Quit (stops all agents)
|
|
105
|
+
|
|
106
|
+
### 4. Launch Claude Code
|
|
107
|
+
Open a second terminal in the **orchestrator workspace** (not the real project):
|
|
108
|
+
```bash
|
|
109
|
+
cd orchestrator-my-project
|
|
110
|
+
claude
|
|
111
|
+
```
|
|
112
|
+
Then run:
|
|
113
|
+
```
|
|
114
|
+
Read ORCHESTRATOR.md and start.
|
|
115
|
+
```
|
|
60
116
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
117
|
+
### 5. Request Work
|
|
118
|
+
Examples:
|
|
119
|
+
- `"Explore this project"` β OpenCode analyzes and reports.
|
|
120
|
+
- `"Add JWT authentication"` β OpenCode analyzes, then Codex/OpenCode implement.
|
|
121
|
+
- `"Refactor the API layer"` β OpenCode explores, then workers implement in parallel.
|
|
64
122
|
|
|
65
|
-
|
|
123
|
+
## π Workspace Structure
|
|
66
124
|
|
|
67
|
-
|
|
68
|
-
- `src/`
|
|
69
|
-
- `scripts/`
|
|
70
|
-
- `templates/en/`
|
|
71
|
-
- `templates/es/`
|
|
72
|
-
- `orchestrator.js`
|
|
73
|
-
- `LICENSE`
|
|
125
|
+
The generated workspace includes:
|
|
74
126
|
|
|
75
|
-
|
|
127
|
+
```text
|
|
128
|
+
orchestrator-my-project/
|
|
129
|
+
βββ ORCHESTRATOR.md # Core rules for the orchestrator session
|
|
130
|
+
βββ CLAUDE.md # Routing rules for Claude
|
|
131
|
+
βββ QUEUE.md # Active task queue (TASK-NNN | title | agent | ...)
|
|
132
|
+
βββ ENGRAM.md # Persistent memory conventions
|
|
133
|
+
βββ orchestrator.config.json # Repos, agents, models, and profiles
|
|
134
|
+
βββ agents/ # Agent-specific instructions
|
|
135
|
+
β βββ BACKEND.md
|
|
136
|
+
β βββ FRONTEND.md
|
|
137
|
+
β βββ CODEX.md
|
|
138
|
+
β βββ OPENCODE.md
|
|
139
|
+
βββ .claude/ # Local Claude skills and config
|
|
140
|
+
β βββ skills/ # Orchestrator skills (init, explore, etc.)
|
|
141
|
+
βββ .codex/ # Codex config
|
|
142
|
+
βββ .opencode/ # OpenCode config
|
|
143
|
+
βββ openspec/ # SDD artifacts
|
|
144
|
+
β βββ changes/
|
|
145
|
+
β βββ templates/
|
|
146
|
+
βββ docs/ # Documentation
|
|
147
|
+
βββ logs/ # Execution logs
|
|
148
|
+
βββ handoffs/ # Session handoffs
|
|
149
|
+
βββ progress/ # Agent progress reports
|
|
150
|
+
```
|
|
76
151
|
|
|
77
|
-
##
|
|
152
|
+
## ποΈ Configuration
|
|
153
|
+
|
|
154
|
+
### Agent Configuration (`orchestrator.config.json`)
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"projectName": "My Project",
|
|
158
|
+
"workspaceLanguage": "es",
|
|
159
|
+
"maxConcurrent": 5,
|
|
160
|
+
"pollIntervalSeconds": 5, // Fallback polling (realtime uses fs.watch)
|
|
161
|
+
"taskTimeoutMinutes": 30,
|
|
162
|
+
"repos": {
|
|
163
|
+
"backend": "C:/code/my-backend",
|
|
164
|
+
"frontend": "C:/code/my-frontend"
|
|
165
|
+
},
|
|
166
|
+
"agentProfiles": {
|
|
167
|
+
"claude": { "enabled": true, "localConfigDir": ".claude" },
|
|
168
|
+
"codex": { "enabled": true, "localConfigDir": ".codex" },
|
|
169
|
+
"opencode": { "enabled": true, "localConfigDir": ".opencode" }
|
|
170
|
+
},
|
|
171
|
+
"agents": {
|
|
172
|
+
"Backend": { "cli": "claude", "defaultRepo": "backend", "model": "sonnet" },
|
|
173
|
+
"Frontend": { "cli": "claude", "defaultRepo": "frontend", "model": "sonnet" },
|
|
174
|
+
"Codex": { "cli": "codex", "defaultRepo": "backend", "model": "gpt-5.5" },
|
|
175
|
+
"OpenCode": { "cli": "opencode", "defaultRepo": "frontend", "model": "auto" }
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
```
|
|
78
179
|
|
|
79
|
-
|
|
180
|
+
### Model Selection
|
|
181
|
+
- Use `"model": "auto"` to let the agent use your default configured model (e.g., Mistral Medium 3.5 128B for OpenCode).
|
|
182
|
+
- Specify a model explicitly (e.g., `"model": "gpt-5.5"`) to override.
|
|
183
|
+
|
|
184
|
+
## π Workflow Example
|
|
185
|
+
|
|
186
|
+
1. **User Request**: `"Add user authentication to the backend."`
|
|
187
|
+
2. **Claude-Orchestrator**:
|
|
188
|
+
- Creates `TASK-001` (OpenCode): `"Analyze current auth system"`
|
|
189
|
+
- Waits for OpenCode's report in `progress/PROGRESS-OpenCode.md`
|
|
190
|
+
3. **OpenCode**:
|
|
191
|
+
- Analyzes the codebase.
|
|
192
|
+
- Writes findings to `progress/PROGRESS-OpenCode.md` and `INBOX.md`.
|
|
193
|
+
4. **Claude-Orchestrator**:
|
|
194
|
+
- Reads OpenCode's report.
|
|
195
|
+
- Creates `TASK-002` (Codex): `"Implement JWT auth"` (depends on TASK-001).
|
|
196
|
+
5. **Codex**:
|
|
197
|
+
- Implements the feature.
|
|
198
|
+
- Reports completion in `progress/PROGRESS-Codex.md`.
|
|
199
|
+
6. **TUI**:
|
|
200
|
+
- Shows real-time updates (task status, agent activity, costs).
|
|
201
|
+
|
|
202
|
+
## π Supported Agents & Models
|
|
203
|
+
|
|
204
|
+
| Agent | CLI | Default Model | Implementation? | Notes |
|
|
205
|
+
|-------|-----|----------------|----------------|-------|
|
|
206
|
+
| Backend | `claude` | sonnet | β
Yes | Claude-Worker for backend tasks |
|
|
207
|
+
| Frontend | `claude` | sonnet | β
Yes | Claude-Worker for frontend tasks |
|
|
208
|
+
| Codex | `codex` | gpt-5.5 | β
Yes | Primary implementation |
|
|
209
|
+
| OpenCode | `opencode` | auto | β
**Yes** (with Mistral Medium 3.5 128B) | Secondary implementation |
|
|
210
|
+
| Gemini | `gemini` | auto | β No | Audits/reviews only |
|
|
211
|
+
| Cursor | `cursor` | auto | β No | Bulk edits only |
|
|
212
|
+
| Abacus | `abacusai` | auto | β No | Small focused tasks |
|
|
213
|
+
|
|
214
|
+
## π‘οΈ Safety & Best Practices
|
|
215
|
+
|
|
216
|
+
- **No auto-commits**: Agents never run `git commit` or `git push`.
|
|
217
|
+
- **No YOLO by default**: Safe permissions mode is enabled unless `--yolo` is used.
|
|
218
|
+
- **Claude as reviewer**: Claude-Orchestrator validates all work before user approval.
|
|
219
|
+
- **Clean repos**: Project files stay untouched; orchestrator files live in the sibling workspace.
|
|
220
|
+
- **Fallback safety**: Tasks are automatically reassigned if an agent fails.
|
|
221
|
+
|
|
222
|
+
## π Documentation
|
|
223
|
+
|
|
224
|
+
- **Core Rules**: See `ORCHESTRATOR.md` in the generated workspace.
|
|
225
|
+
- **Agent Routing**: See `CLAUDE.md`.
|
|
226
|
+
- **Architecture**: See `docs/architecture.md`.
|
|
227
|
+
- **OpenSpec**: See `openspec/FLOW.md`.
|
|
228
|
+
|
|
229
|
+
## π€ Acknowledgements
|
|
230
|
+
|
|
231
|
+
Inspired by [Orquestador-AI](https://github.com/ariellontero/Orquestador-AI) by Ariel Lontero (MIT).
|
|
232
|
+
Built from scratch with a modern architecture: **Ink TUI + React, npm package, real-time fs.watch, multi-language templates, and multi-agent coordination**.
|
package/orchestrator.js
CHANGED
|
@@ -122,6 +122,7 @@ const config = JSON.parse(fs.readFileSync(CONFIG_FILE, "utf-8"));
|
|
|
122
122
|
|
|
123
123
|
const QUEUE_FILE = path.join(WORKSPACE, "QUEUE.md");
|
|
124
124
|
const INBOX_FILE = path.join(WORKSPACE, "INBOX.md");
|
|
125
|
+
const NOTIFY_FILE = path.join(WORKSPACE, "NOTIFY.md");
|
|
125
126
|
const AWAY_MODE_FILE = path.join(WORKSPACE, ".away-mode");
|
|
126
127
|
const LOG_DIR = path.join(WORKSPACE, "logs");
|
|
127
128
|
|
|
@@ -266,6 +267,11 @@ const TEXT = {
|
|
|
266
267
|
inboxReasonLabel: "- **Motivo:**",
|
|
267
268
|
inboxNewAgentLabel: "- **Nuevo agente:**",
|
|
268
269
|
inboxFailAction: "- **AcciΓ³n:** La TUI reasignΓ³ automΓ‘ticamente. Verifica en QUEUE.md o espera la siguiente notificaciΓ³n de completada.",
|
|
270
|
+
// NOTIFY.md β notificaciΓ³n concisa a la sesiΓ³n interactiva de Claude
|
|
271
|
+
notifyComplete: (ts, id, agent, dur) => `π [${ts}] ${id} completada por ${agent} (${dur}).\nRevisa INBOX.md y crea la siguiente tarea de implementaciΓ³n en QUEUE.md si aΓΊn no existe.`,
|
|
272
|
+
notifyFailed: (ts, id, from, to, reason) => `β οΈ [${ts}] ${id} fallΓ³ en ${from} β reasignada a ${to}.\nMotivo: ${reason}\nRevisa INBOX.md para el contexto.`,
|
|
273
|
+
notifyPermanentFail: (ts, id, agent) => `π¨ [${ts}] ${id} fallΓ³ permanentemente en ${agent} (sin mΓ‘s reintentos).\nDecide si eliminar, reasignar o escalar la tarea en QUEUE.md.`,
|
|
274
|
+
notifyRateLimit: (ts, id, agent, resetStr, retries, max) => `β³ [${ts}] ${id} β ${agent} alcanzΓ³ el lΓmite de tokens (reintento ${retries}/${max}, ${resetStr}).\nSi quieres reasignar ahora: asigna a Claude-Worker (Frontend). Si esperas, reintentarΓ‘ automΓ‘ticamente.`,
|
|
269
275
|
},
|
|
270
276
|
en: {
|
|
271
277
|
configExists:
|
|
@@ -370,6 +376,11 @@ const TEXT = {
|
|
|
370
376
|
inboxReasonLabel: "- **Reason:**",
|
|
371
377
|
inboxNewAgentLabel: "- **New agent:**",
|
|
372
378
|
inboxFailAction: "- **Action:** TUI reassigned automatically. Check QUEUE.md or wait for the next completion notification.",
|
|
379
|
+
// NOTIFY.md β concise notification to the interactive Claude session
|
|
380
|
+
notifyComplete: (ts, id, agent, dur) => `π [${ts}] ${id} completed by ${agent} (${dur}).\nCheck INBOX.md and create the next implementation task in QUEUE.md if it does not exist yet.`,
|
|
381
|
+
notifyFailed: (ts, id, from, to, reason) => `β οΈ [${ts}] ${id} failed on ${from} β reassigned to ${to}.\nReason: ${reason}\nCheck INBOX.md for context.`,
|
|
382
|
+
notifyPermanentFail: (ts, id, agent) => `π¨ [${ts}] ${id} permanently failed on ${agent} (no more retries).\nDecide whether to remove, reassign, or escalate the task in QUEUE.md.`,
|
|
383
|
+
notifyRateLimit: (ts, id, agent, resetStr, retries, max) => `β³ [${ts}] ${id} β ${agent} hit token/rate limit (retry ${retries}/${max}, ${resetStr}).\nTo reassign now: assign to Claude-Worker (Frontend). Otherwise it will retry automatically.`,
|
|
373
384
|
},
|
|
374
385
|
};
|
|
375
386
|
const L = TEXT[WORKSPACE_LANGUAGE];
|
|
@@ -999,6 +1010,36 @@ function parseCompletedFromFile() {
|
|
|
999
1010
|
}
|
|
1000
1011
|
|
|
1001
1012
|
const loggedUnknownAgents = new Set();
|
|
1013
|
+
let queueWatcher = null;
|
|
1014
|
+
|
|
1015
|
+
function setupQueueWatcher() {
|
|
1016
|
+
if (!fs.existsSync(QUEUE_FILE)) return;
|
|
1017
|
+
|
|
1018
|
+
if (queueWatcher) {
|
|
1019
|
+
try { queueWatcher.close(); } catch {}
|
|
1020
|
+
queueWatcher = null;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
try {
|
|
1024
|
+
queueWatcher = fs.watch(QUEUE_FILE, { persistent: false }, (eventType, filename) => {
|
|
1025
|
+
if (eventType === 'change' && filename === path.basename(QUEUE_FILE)) {
|
|
1026
|
+
log('DEBUG', `QUEUE.md changed, reloading queue immediately`);
|
|
1027
|
+
reloadQueue();
|
|
1028
|
+
scheduleNext();
|
|
1029
|
+
renderDashboard();
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
queueWatcher.on('error', (err) => {
|
|
1033
|
+
log('WARN', `fs.watch failed for QUEUE.md: ${err.message}, falling back to polling`);
|
|
1034
|
+
queueWatcher = null;
|
|
1035
|
+
});
|
|
1036
|
+
log('INFO', 'Realtime QUEUE.md watcher enabled');
|
|
1037
|
+
} catch (err) {
|
|
1038
|
+
log('WARN', `Failed to setup QUEUE.md watcher: ${err.message}, using polling`);
|
|
1039
|
+
queueWatcher = null;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1002
1043
|
function reloadQueue() {
|
|
1003
1044
|
state.queue = parseQueue();
|
|
1004
1045
|
const activeIds = new Set([
|
|
@@ -1064,6 +1105,15 @@ function writeInboxFailureNotification(task, failedAgent, newAgent, reason) {
|
|
|
1064
1105
|
} catch {}
|
|
1065
1106
|
}
|
|
1066
1107
|
|
|
1108
|
+
// Escribe una notificaciΓ³n concisa en NOTIFY.md para la sesiΓ³n interactiva de Claude.
|
|
1109
|
+
// Los hooks de .claude/settings.json leen y limpian este archivo automΓ‘ticamente.
|
|
1110
|
+
function writeNotifyFile(message) {
|
|
1111
|
+
try {
|
|
1112
|
+
const sep = fs.existsSync(NOTIFY_FILE) ? '\n---\n' : '';
|
|
1113
|
+
fs.appendFileSync(NOTIFY_FILE, sep + message + '\n', 'utf-8');
|
|
1114
|
+
} catch {}
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1067
1117
|
// GAP 2 β Move a task line from ## Pending to ## In Progress when it starts
|
|
1068
1118
|
function moveTaskToInProgress(task) {
|
|
1069
1119
|
if (!fs.existsSync(QUEUE_FILE)) return;
|
|
@@ -1523,6 +1573,7 @@ function completeTask(task, agentName) {
|
|
|
1523
1573
|
ag.lastLine = L.lastCompleted(task.id);
|
|
1524
1574
|
updateQueueFile(task);
|
|
1525
1575
|
writeInboxNotification(task, agentName, elapsed);
|
|
1576
|
+
writeNotifyFile(L.notifyComplete(timestamp(), task.id, agentName, formatDuration(elapsed)));
|
|
1526
1577
|
scheduleNext();
|
|
1527
1578
|
renderDashboard();
|
|
1528
1579
|
}
|
|
@@ -1591,6 +1642,7 @@ function failTask(task, agentName, code) {
|
|
|
1591
1642
|
`{yellow-fg}${escBl(L.agentRateLimit(resetStr))}{/yellow-fg}`,
|
|
1592
1643
|
true,
|
|
1593
1644
|
);
|
|
1645
|
+
writeNotifyFile(L.notifyRateLimit(timestamp(), task.id, agentName, resetStr, retries, maxRetries));
|
|
1594
1646
|
} else {
|
|
1595
1647
|
log("FAIL", L.logFail(agentName, task.id, code, retries, maxRetries));
|
|
1596
1648
|
appendToAgent(
|
|
@@ -1622,6 +1674,7 @@ function failTask(task, agentName, code) {
|
|
|
1622
1674
|
: L.reasonPersistent;
|
|
1623
1675
|
if (tryFallbackToAlternative(task, agentName, reason)) {
|
|
1624
1676
|
writeInboxFailureNotification(task, agentName, task.agent, reason);
|
|
1677
|
+
writeNotifyFile(L.notifyFailed(timestamp(), task.id, agentName, task.agent, reason));
|
|
1625
1678
|
setTimeout(() => {
|
|
1626
1679
|
scheduleNext();
|
|
1627
1680
|
safeRenderDashboard();
|
|
@@ -1634,6 +1687,7 @@ function failTask(task, agentName, code) {
|
|
|
1634
1687
|
task.status = "failed";
|
|
1635
1688
|
ag.lastLine = L.lastFailed(task.id);
|
|
1636
1689
|
log("ERROR", L.logPermanentFail(task.id, retries));
|
|
1690
|
+
writeNotifyFile(L.notifyPermanentFail(timestamp(), task.id, agentName));
|
|
1637
1691
|
} else {
|
|
1638
1692
|
task.status = "pending";
|
|
1639
1693
|
let retryDelay = rl.isRateLimit
|
|
@@ -1817,7 +1871,6 @@ function getClaudeFallbackAgent(task) {
|
|
|
1817
1871
|
}
|
|
1818
1872
|
|
|
1819
1873
|
function getAlternativeSupportAgent(failedAgentName) {
|
|
1820
|
-
if (failedAgentName === "Codex") return "OpenCode";
|
|
1821
1874
|
if (failedAgentName === "OpenCode") return "Codex";
|
|
1822
1875
|
return null;
|
|
1823
1876
|
}
|
|
@@ -1825,7 +1878,7 @@ function getAlternativeSupportAgent(failedAgentName) {
|
|
|
1825
1878
|
function tryFallbackToAlternative(task, failedAgentName, reason) {
|
|
1826
1879
|
if (!["Codex", "OpenCode"].includes(failedAgentName)) return false;
|
|
1827
1880
|
|
|
1828
|
-
// Step 1: try sibling support agent (
|
|
1881
|
+
// Step 1: try sibling support agent (OpenCode β Codex only; Codex falls through directly)
|
|
1829
1882
|
const siblingAgent = getAlternativeSupportAgent(failedAgentName);
|
|
1830
1883
|
const siblingAvailable =
|
|
1831
1884
|
siblingAgent &&
|
|
@@ -1931,16 +1984,59 @@ setInterval(() => {
|
|
|
1931
1984
|
if (command) applyControlCommand(command);
|
|
1932
1985
|
}, 1000);
|
|
1933
1986
|
|
|
1934
|
-
// Real-time queue detection
|
|
1935
|
-
//
|
|
1987
|
+
// Real-time queue detection β watches QUEUE.md directly with fallback to WORKSPACE directory
|
|
1988
|
+
// fs.watch on the file itself works reliably on Linux/macOS; fallback to WORKSPACE for Windows
|
|
1936
1989
|
let _queueWatchDebounce = null;
|
|
1990
|
+
let _queueWatcher = null;
|
|
1991
|
+
|
|
1937
1992
|
function startQueueWatcher() {
|
|
1938
|
-
if (
|
|
1993
|
+
if (_queueWatcher) {
|
|
1994
|
+
try { _queueWatcher.close(); } catch {}
|
|
1995
|
+
_queueWatcher = null;
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
try {
|
|
1999
|
+
// Try to watch the file directly first (best for Linux/macOS)
|
|
2000
|
+
_queueWatcher = fs.watch(QUEUE_FILE, { persistent: false }, (eventType, filename) => {
|
|
2001
|
+
if (_queueWatchDebounce) clearTimeout(_queueWatchDebounce);
|
|
2002
|
+
_queueWatchDebounce = setTimeout(() => {
|
|
2003
|
+
if (!fs.existsSync(QUEUE_FILE)) return;
|
|
2004
|
+
const prevCount = state.queue.length;
|
|
2005
|
+
reloadQueue();
|
|
2006
|
+
if (!state.paused) scheduleNext();
|
|
2007
|
+
renderDashboard();
|
|
2008
|
+
if (state.queue.length > prevCount)
|
|
2009
|
+
log("INFO", WORKSPACE_LANGUAGE === "es"
|
|
2010
|
+
? `Nueva tarea detectada en QUEUE.md (realtime)`
|
|
2011
|
+
: `New task detected in QUEUE.md (realtime)`);
|
|
2012
|
+
}, 10); // Faster debounce for direct file watch
|
|
2013
|
+
});
|
|
2014
|
+
_queueWatcher.on('error', () => {
|
|
2015
|
+
// Fallback to WORKSPACE directory watch if direct file watch fails (Windows)
|
|
2016
|
+
log('WARN', WORKSPACE_LANGUAGE === "es"
|
|
2017
|
+
? `No se pudo verificar QUEUE.md directamente, usando watcher de directorio`
|
|
2018
|
+
: `Could not watch QUEUE.md directly, falling back to directory watcher`);
|
|
2019
|
+
setupFallbackQueueWatcher();
|
|
2020
|
+
});
|
|
2021
|
+
} catch (err) {
|
|
2022
|
+
log('WARN', `Queue watcher error: ${err.message}, using fallback`);
|
|
2023
|
+
setupFallbackQueueWatcher();
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
function setupFallbackQueueWatcher() {
|
|
2028
|
+
if (_queueWatcher) {
|
|
2029
|
+
try { _queueWatcher.close(); } catch {}
|
|
2030
|
+
_queueWatcher = null;
|
|
2031
|
+
}
|
|
2032
|
+
|
|
1939
2033
|
try {
|
|
1940
|
-
const
|
|
1941
|
-
|
|
2034
|
+
const watchName = path.basename(QUEUE_FILE);
|
|
2035
|
+
_queueWatcher = fs.watch(WORKSPACE, { persistent: false }, (eventType, filename) => {
|
|
2036
|
+
if (filename !== watchName) return;
|
|
1942
2037
|
if (_queueWatchDebounce) clearTimeout(_queueWatchDebounce);
|
|
1943
2038
|
_queueWatchDebounce = setTimeout(() => {
|
|
2039
|
+
if (!fs.existsSync(QUEUE_FILE)) return;
|
|
1944
2040
|
const prevCount = state.queue.length;
|
|
1945
2041
|
reloadQueue();
|
|
1946
2042
|
if (!state.paused) scheduleNext();
|
|
@@ -1951,10 +2047,12 @@ function startQueueWatcher() {
|
|
|
1951
2047
|
: `New task detected in QUEUE.md`);
|
|
1952
2048
|
}, 50);
|
|
1953
2049
|
});
|
|
1954
|
-
|
|
2050
|
+
_queueWatcher.on('error', () => {});
|
|
1955
2051
|
} catch {}
|
|
1956
2052
|
}
|
|
2053
|
+
|
|
1957
2054
|
startQueueWatcher();
|
|
2055
|
+
setupQueueWatcher();
|
|
1958
2056
|
|
|
1959
2057
|
// Slow fallback (5 min) β only runs if there is actually pending work or busy agents
|
|
1960
2058
|
// fs.watch handles real-time; this is just a safety net
|
|
@@ -2042,8 +2140,9 @@ function startInboxWatcher() {
|
|
|
2042
2140
|
try { fs.writeFileSync(INBOX_FILE, '', 'utf-8'); } catch {}
|
|
2043
2141
|
}
|
|
2044
2142
|
try {
|
|
2045
|
-
const
|
|
2046
|
-
|
|
2143
|
+
const watchName = path.basename(INBOX_FILE);
|
|
2144
|
+
const watcher = fs.watch(WORKSPACE, {persistent: false}, (eventType, filename) => {
|
|
2145
|
+
if (filename !== watchName) return;
|
|
2047
2146
|
if (_inboxDebounce) clearTimeout(_inboxDebounce);
|
|
2048
2147
|
_inboxDebounce = setTimeout(dispatchInboxClaude, 100);
|
|
2049
2148
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@liriraid/agentflow-ai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.21",
|
|
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
|
@@ -393,11 +393,16 @@ function shutdown() {
|
|
|
393
393
|
}
|
|
394
394
|
|
|
395
395
|
// Reactivo: dispara un refresh inmediato cuando el engine escribe el STATE_FILE.
|
|
396
|
-
//
|
|
396
|
+
// Vigila el directorio logs/ (no el archivo directamente) porque en Windows
|
|
397
|
+
// fs.watch sobre un archivo es poco confiable β el patrΓ³n estable es vigilar
|
|
398
|
+
// el directorio padre y filtrar por nombre de archivo.
|
|
397
399
|
function ensureStateWatcher() {
|
|
398
|
-
|
|
400
|
+
const logsDir = path.dirname(STATE_FILE);
|
|
401
|
+
const stateFileName = path.basename(STATE_FILE);
|
|
402
|
+
if (stateWatcher || !fs.existsSync(logsDir)) return;
|
|
399
403
|
try {
|
|
400
|
-
stateWatcher = fs.watch(
|
|
404
|
+
stateWatcher = fs.watch(logsDir, {persistent: false}, (eventType, filename) => {
|
|
405
|
+
if (filename !== stateFileName) return;
|
|
401
406
|
if (stateWatchDebounce) clearTimeout(stateWatchDebounce);
|
|
402
407
|
stateWatchDebounce = setTimeout(() => {
|
|
403
408
|
if (quitRequested) return;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
// Reads NOTIFY.md from the workspace and writes its content to stdout so the
|
|
4
|
+
// Claude Code hook injects it into the interactive session.
|
|
5
|
+
// The file is deleted after reading to avoid repeating the notification.
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
const notifyFile = path.join(process.cwd(), 'NOTIFY.md');
|
|
10
|
+
if (!fs.existsSync(notifyFile)) process.exit(0);
|
|
11
|
+
|
|
12
|
+
const content = fs.readFileSync(notifyFile, 'utf8').trim();
|
|
13
|
+
if (!content) {
|
|
14
|
+
try { fs.unlinkSync(notifyFile); } catch {}
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
try { fs.unlinkSync(notifyFile); } catch {}
|
|
19
|
+
|
|
20
|
+
process.stdout.write('\n' + content + '\n');
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"Stop": [
|
|
4
|
+
{
|
|
5
|
+
"hooks": [
|
|
6
|
+
{
|
|
7
|
+
"type": "command",
|
|
8
|
+
"command": "node .claude/hooks/notify-check.js"
|
|
9
|
+
}
|
|
10
|
+
]
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"UserPromptSubmit": [
|
|
14
|
+
{
|
|
15
|
+
"hooks": [
|
|
16
|
+
{
|
|
17
|
+
"type": "command",
|
|
18
|
+
"command": "node .claude/hooks/notify-check.js"
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -5,7 +5,7 @@ description: >
|
|
|
5
5
|
license: MIT
|
|
6
6
|
metadata:
|
|
7
7
|
owner: agentflow
|
|
8
|
-
version: "1.
|
|
8
|
+
version: "1.1"
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# Skill: orchestrator-explore
|
|
@@ -18,12 +18,15 @@ Gather useful context before creating TASKs or OpenSpec artifacts.
|
|
|
18
18
|
|
|
19
19
|
- Understand the user's exact scope first.
|
|
20
20
|
- Prefer exploration before implementation when context is unclear.
|
|
21
|
-
- Use OpenCode as the
|
|
21
|
+
- Use **ONLY OpenCode** as the exploration agent when deep codebase analysis is needed β its role is **EXCLUSIVELY analysis**, **NEVER implementation**.
|
|
22
|
+
- When delegating exploration to OpenCode, include in the brief exactly what it must report: flows, dependencies, architecture findings, inconsistencies, etc.
|
|
22
23
|
- Do not fill `QUEUE.md` with implementation tasks until enough context exists.
|
|
23
24
|
- Summarize findings in actionable terms: what exists, what is missing, what risks exist, and what tasks follow.
|
|
24
25
|
- If the change is large or multi-phase, move toward OpenSpec.
|
|
25
|
-
- If work is clear, convert findings into concrete TASKs
|
|
26
|
+
- If work is clear, convert findings into concrete TASKs using `orchestrator-queue-planning`.
|
|
27
|
+
- **STRICT RULE: When OpenCode delivers its report in INBOX.md, use THOSE findings to create implementation TASKs (assigned to Codex or Claude-Worker). Under NO circumstances should Claude-Orchestrator re-analyze the code itself if OpenCode has already done so. Read the report in `progress/PROGRESS-OpenCode.md` or INBOX.md and base your decisions on that analysis.**
|
|
28
|
+
- If OpenCode's report is insufficient, ask OpenCode to deepen analysis on a specific area with a new analysis TASK, but **DO NOT do it yourself**.
|
|
26
29
|
|
|
27
30
|
## Expected Result
|
|
28
31
|
|
|
29
|
-
The orchestrator can decide whether to plan TASKs or continue investigation.
|
|
32
|
+
The orchestrator can decide whether to plan TASKs or continue investigation. **The final result MUST be one or more TASKs in QUEUE.md assigned to Codex or Claude-Worker for implementation, NOT more analysis by Claude-Orchestrator.**
|
|
@@ -5,7 +5,7 @@ description: >
|
|
|
5
5
|
license: MIT
|
|
6
6
|
metadata:
|
|
7
7
|
owner: agentflow
|
|
8
|
-
version: "1.
|
|
8
|
+
version: "1.1"
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# Skill: orchestrator-queue-planning
|
|
@@ -14,17 +14,36 @@ metadata:
|
|
|
14
14
|
|
|
15
15
|
Turn the user's request into executable queue work for the TUI.
|
|
16
16
|
|
|
17
|
+
## Agent Assignment Rules
|
|
18
|
+
|
|
19
|
+
### OpenCode β analysis only
|
|
20
|
+
- Use for exploration, audits, context reading, and structured reports.
|
|
21
|
+
- **Do not assign implementation** β OpenCode does not modify project files.
|
|
22
|
+
- If work needs prior analysis, create an OpenCode TASK first, then a Codex TASK with `> after:TASK-NNN`.
|
|
23
|
+
|
|
24
|
+
### Codex β primary implementation
|
|
25
|
+
- Use for implementation, code changes, tests, and docs when the spec is clear.
|
|
26
|
+
- It is the primary execution agent.
|
|
27
|
+
- If Codex fails persistently, the TUI automatically reassigns to Claude-Worker (Frontend/Backend).
|
|
28
|
+
|
|
29
|
+
### Claude-Worker (Frontend / Backend)
|
|
30
|
+
- Automatic fallback when Codex fails.
|
|
31
|
+
- Also takes overflow work when both Codex and OpenCode are busy and more tasks are pending.
|
|
32
|
+
- Frontend-only projects: always use `Frontend`; backend work: use `Backend`.
|
|
33
|
+
|
|
17
34
|
## Critical Rules
|
|
18
35
|
|
|
19
36
|
- Create small, concrete, executable TASKs.
|
|
20
37
|
- Every TASK must include agent, priority, repo, and a clear description.
|
|
21
38
|
- Use `> after:TASK-NNN` for dependencies.
|
|
22
39
|
- Do not implement the task directly as Claude-Orchestrator.
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
40
|
+
- Distribution by task count:
|
|
41
|
+
- **1 analysis task**: OpenCode
|
|
42
|
+
- **1 implementation task**: Codex
|
|
43
|
+
- **2 parallel tasks**: OpenCode (analysis) + Codex (implementation when spec is clear)
|
|
44
|
+
- **3+ tasks** with Codex busy: overflow goes to `Frontend` (FE repo) or `Backend` (BE repo)
|
|
27
45
|
- Keep `QUEUE.md` aligned with the user's current objective.
|
|
46
|
+
- **Never assign implementation to OpenCode.**
|
|
28
47
|
|
|
29
48
|
## Expected Result
|
|
30
49
|
|