@liriraid/agentflow-ai 1.0.13 → 1.0.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liriraid/agentflow-ai",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
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",
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env node
2
+ // ============================================================================
3
+ // Auto-trigger script - Ejecutar cada 60 segundos desde Windows Task Scheduler
4
+ // Detecta nuevo contenido en INBOX.md y dispara Claude headless para procesarlo
5
+ // ============================================================================
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const { spawn } = require('child_process');
10
+
11
+ const WORKSPACE = process.env.ORCHESTRATOR_WORKSPACE || process.cwd();
12
+ const INBOX_FILE = path.join(WORKSPACE, 'INBOX.md');
13
+ const QUEUE_FILE = path.join(WORKSPACE, 'QUEUE.md');
14
+ const LAST_CHECK_FILE = path.join(WORKSPACE, 'logs', 'last-auto-check.json');
15
+
16
+ function timestamp() {
17
+ return new Date().toISOString().replace('T', ' ').slice(0, 19);
18
+ }
19
+
20
+ function detectLanguage() {
21
+ if (!fs.existsSync(QUEUE_FILE)) return 'en';
22
+ try {
23
+ const content = fs.readFileSync(QUEUE_FILE, 'utf-8');
24
+ return (content.includes('## Pendientes') || content.includes('## Completadas')) ? 'es' : 'en';
25
+ } catch { return 'en'; }
26
+ }
27
+
28
+ const lang = detectLanguage();
29
+
30
+ // Leer último hash guardado
31
+ let lastCheck = { time: 0, inboxHash: '' };
32
+ try {
33
+ if (fs.existsSync(LAST_CHECK_FILE)) {
34
+ lastCheck = JSON.parse(fs.readFileSync(LAST_CHECK_FILE, 'utf-8'));
35
+ }
36
+ } catch {}
37
+
38
+ // Leer INBOX actual
39
+ let inboxContent = '';
40
+ let currentHash = '';
41
+ try {
42
+ if (fs.existsSync(INBOX_FILE)) {
43
+ inboxContent = fs.readFileSync(INBOX_FILE, 'utf-8');
44
+ currentHash = inboxContent.slice(0, 500);
45
+ }
46
+ } catch {}
47
+
48
+ // Si no hay contenido o no cambió, salir
49
+ if (!inboxContent.trim() || currentHash === lastCheck.inboxHash) {
50
+ console.log(`[${timestamp()}] Sin cambios en INBOX. Nada que procesar.`);
51
+ process.exit(0);
52
+ }
53
+
54
+ console.log(`[${timestamp()}] Nuevo contenido en INBOX detectado — disparando Claude...`);
55
+
56
+ // Guardar hash para no relanzar en el próximo ciclo de 60s
57
+ lastCheck = { time: Date.now(), inboxHash: currentHash };
58
+ fs.mkdirSync(path.dirname(LAST_CHECK_FILE), { recursive: true });
59
+ fs.writeFileSync(LAST_CHECK_FILE, JSON.stringify(lastCheck), 'utf-8');
60
+
61
+ // Prompt para Claude headless — lee INBOX y crea la task de implementación si aplica
62
+ const prompt = lang === 'es'
63
+ ? `Eres el orquestador de este workspace. Tu única misión ahora es procesar el INBOX.
64
+
65
+ Pasos:
66
+ 1. Lee INBOX.md en ${WORKSPACE}
67
+ 2. Lee QUEUE.md en ${WORKSPACE} para ver las tareas existentes (secciones Pendientes, En progreso, Completadas)
68
+
69
+ 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:
70
+ - Determina el siguiente TASK ID disponible leyendo QUEUE.md
71
+ - Crea la nueva TASK en QUEUE.md con el formato exacto:
72
+ TASK-NNN | título corto | Codex | P1 | repo | descripción basada en el análisis
73
+
74
+ Si ya existe la tarea correspondiente, o el análisis no está completo, responde solo: "Sin acción necesaria."
75
+
76
+ 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.`
77
+ : `You are the orchestrator for this workspace. Your only mission now is to process the INBOX.
78
+
79
+ Steps:
80
+ 1. Read INBOX.md in ${WORKSPACE}
81
+ 2. Read QUEUE.md in ${WORKSPACE} to see existing tasks (sections Pending, In Progress, Completed)
82
+
83
+ 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:
84
+ - Determine the next available TASK ID by reading QUEUE.md
85
+ - Create the new TASK in QUEUE.md with the exact format:
86
+ TASK-NNN | short title | Codex | P1 | repo | description based on the analysis
87
+
88
+ If the corresponding task already exists, or the analysis is not complete, reply only: "No action needed."
89
+
90
+ Rules: Do not commit or push. Do not analyze project code. Only read INBOX.md and QUEUE.md, and edit QUEUE.md if necessary.`;
91
+
92
+ const claude = spawn('claude', [
93
+ '-p', prompt,
94
+ '--add-dir', WORKSPACE,
95
+ '--dangerously-skip-permissions'
96
+ ], {
97
+ cwd: WORKSPACE,
98
+ stdio: ['ignore', 'pipe', 'pipe'],
99
+ shell: true
100
+ });
101
+
102
+ let output = '';
103
+ claude.stdout.on('data', d => { output += d.toString(); });
104
+ claude.stderr.on('data', d => { process.stderr.write(d); });
105
+
106
+ claude.on('close', code => {
107
+ const result = output.trim().slice(0, 300);
108
+ console.log(`[${timestamp()}] Claude completó (exit ${code}): ${result}`);
109
+ });
110
+
111
+ claude.on('error', err => {
112
+ console.error(`[${timestamp()}] Error al lanzar Claude: ${err.message}`);
113
+ process.exit(1);
114
+ });
@@ -0,0 +1,172 @@
1
+ #!/usr/bin/env node
2
+ // ============================================================================
3
+ // Monitor script - Ejecutar cada 5 minutos desde Windows Task Scheduler
4
+ // SOLO corre cuando Modo Ausencia está activado (.away-mode existe)
5
+ // En cada revisión manda un prompt a Claude para que monitoree y tome decisiones
6
+ // ============================================================================
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const { spawn } = require('child_process');
11
+
12
+ const WORKSPACE = process.env.ORCHESTRATOR_WORKSPACE || process.cwd();
13
+ const QUEUE_FILE = path.join(WORKSPACE, 'QUEUE.md');
14
+ const INBOX_FILE = path.join(WORKSPACE, 'INBOX.md');
15
+ const STATE_FILE = path.join(WORKSPACE, 'logs', 'orchestrator-state.json');
16
+ const ACTIONS_FILE = path.join(WORKSPACE, 'ACTIONS.md');
17
+ const AWAY_MODE_FILE = path.join(WORKSPACE, '.away-mode');
18
+
19
+ // Si Away Mode no está activado, salir inmediatamente
20
+ if (!fs.existsSync(AWAY_MODE_FILE)) {
21
+ console.log('Monitor: Away Mode no activado. Saltando.');
22
+ process.exit(0);
23
+ }
24
+
25
+ function timestamp() {
26
+ return new Date().toISOString().replace('T', ' ').slice(0, 19);
27
+ }
28
+
29
+ function detectLanguage() {
30
+ if (!fs.existsSync(QUEUE_FILE)) return 'en';
31
+ try {
32
+ const content = fs.readFileSync(QUEUE_FILE, 'utf-8');
33
+ return (content.includes('## Pendientes') || content.includes('## Completadas')) ? 'es' : 'en';
34
+ } catch { return 'en'; }
35
+ }
36
+
37
+ const lang = detectLanguage();
38
+
39
+ function readQueue() {
40
+ if (!fs.existsSync(QUEUE_FILE)) return { pending: [], inProgress: [], completed: [] };
41
+ const result = { pending: [], inProgress: [], completed: [] };
42
+ let section = '';
43
+ for (const line of fs.readFileSync(QUEUE_FILE, 'utf-8').split('\n')) {
44
+ const trimmed = line.trim();
45
+ if (trimmed.startsWith('## Pending') || trimmed.startsWith('## Pendientes')) { section = 'pending'; continue; }
46
+ if (trimmed.startsWith('## In Progress') || trimmed.startsWith('## En progreso')) { section = 'inProgress'; continue; }
47
+ if (trimmed.startsWith('## Completed') || trimmed.startsWith('## Completadas')) { section = 'completed'; continue; }
48
+ if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('>')) continue;
49
+ if (section && trimmed.includes('|')) result[section].push(trimmed);
50
+ }
51
+ return result;
52
+ }
53
+
54
+ function readState() {
55
+ if (!fs.existsSync(STATE_FILE)) return null;
56
+ try { return JSON.parse(fs.readFileSync(STATE_FILE, 'utf-8')); }
57
+ catch { return null; }
58
+ }
59
+
60
+ function launchClaude(prompt) {
61
+ const claude = spawn('claude', [
62
+ '-p', prompt,
63
+ '--add-dir', WORKSPACE,
64
+ '--dangerously-skip-permissions'
65
+ ], {
66
+ cwd: WORKSPACE,
67
+ stdio: 'inherit',
68
+ shell: true
69
+ });
70
+ claude.on('error', err => console.error(`[${timestamp()}] Error lanzando Claude: ${err.message}`));
71
+ return claude;
72
+ }
73
+
74
+ // ============================================================================
75
+ // RECOPILAR ESTADO ACTUAL
76
+ // ============================================================================
77
+ const queue = readQueue();
78
+ const state = readState();
79
+
80
+ const busyAgents = Object.values(state?.agents || {}).filter(a => a.status === 'busy').length;
81
+ const failedAgents = Object.entries(state?.agents || {})
82
+ .filter(([, ag]) => (ag.lastLine || '').startsWith('FALLÓ:') || (ag.lastLine || '').startsWith('FAILED:'))
83
+ .map(([name, ag]) => `${name}: ${ag.lastLine}`);
84
+ const retryingAgents = Object.entries(state?.agents || {})
85
+ .filter(([, ag]) => {
86
+ const ll = ag.lastLine || '';
87
+ return ll.startsWith('REINTENTO:') || ll.startsWith('LÍMITE:') || ll.startsWith('RETRY:');
88
+ })
89
+ .map(([name, ag]) => `${name}: ${ag.lastLine}`);
90
+
91
+ const hasWork = queue.pending.length > 0 || (state?.inProgress?.length || 0) > 0 || busyAgents > 0;
92
+
93
+ console.log(`[${timestamp()}] Monitor Modo Ausencia:`);
94
+ console.log(` Pendientes: ${queue.pending.length} | En progreso: ${state?.inProgress?.length || 0} | Completadas: ${queue.completed.length}`);
95
+ console.log(` Agentes ocupados: ${busyAgents} | Fallidos: ${failedAgents.length} | Reintentando: ${retryingAgents.length}`);
96
+
97
+ // ============================================================================
98
+ // SI NO HAY TRABAJO → DESACTIVAR Y DAR RESUMEN FINAL
99
+ // ============================================================================
100
+ if (!hasWork && queue.completed.length > 0) {
101
+ console.log(` -> Sin trabajo pendiente. Desactivando Modo Ausencia.`);
102
+
103
+ try { fs.unlinkSync(AWAY_MODE_FILE); } catch {}
104
+ try { if (fs.existsSync(ACTIONS_FILE)) fs.unlinkSync(ACTIONS_FILE); } catch {}
105
+
106
+ const donePrompt = lang === 'es'
107
+ ? `Modo Ausencia terminado. Todas las tareas se completaron mientras estabas ausente.
108
+
109
+ Lee QUEUE.md en ${WORKSPACE} y dame un resumen de todo lo que se logró durante la sesión.
110
+ Luego dime si hay algo que podamos continuar o integrar a partir de lo que ya se hizo, o pregúntame qué quiero priorizar a continuación.`
111
+ : `Away Mode ended. All tasks were completed while you were away.
112
+
113
+ Read QUEUE.md in ${WORKSPACE} and give me a summary of everything accomplished during the session.
114
+ Then tell me if there is anything we can continue or integrate from what was done, or ask me what I want to prioritize next.`;
115
+
116
+ launchClaude(donePrompt);
117
+ process.exit(0);
118
+ }
119
+
120
+ // ============================================================================
121
+ // HAY TRABAJO → MANDAR PROMPT A CLAUDE PARA QUE MONITOREE Y TOME DECISIONES
122
+ // ============================================================================
123
+
124
+ // Construir contexto de estado para incluir en el prompt
125
+ const stateLines = [];
126
+ if (queue.pending.length > 0) {
127
+ stateLines.push(`Tareas pendientes en cola: ${queue.pending.length}`);
128
+ queue.pending.slice(0, 5).forEach(t => stateLines.push(` - ${t.split('|').slice(0, 3).join('|').trim()}`));
129
+ }
130
+ if (state?.inProgress?.length > 0) {
131
+ stateLines.push(`Tareas en progreso: ${state.inProgress.map(t => `${t.id} (${t.agent})`).join(', ')}`);
132
+ }
133
+ if (failedAgents.length > 0) {
134
+ stateLines.push(`Agentes con fallo: ${failedAgents.join(' | ')}`);
135
+ }
136
+ if (retryingAgents.length > 0) {
137
+ stateLines.push(`Agentes reintentando: ${retryingAgents.join(' | ')}`);
138
+ }
139
+ if (queue.completed.length > 0) {
140
+ stateLines.push(`Tareas completadas hasta ahora: ${queue.completed.length}`);
141
+ }
142
+
143
+ const stateContext = stateLines.join('\n');
144
+
145
+ const monitorPrompt = lang === 'es'
146
+ ? `Modo Ausencia activo — revisión automática cada 5 minutos.
147
+
148
+ Estado actual del orquestador:
149
+ ${stateContext}
150
+
151
+ Instrucciones:
152
+ 1. Lee INBOX.md en ${WORKSPACE} — si hay análisis completados de agentes que aún no tienen su tarea de implementación en QUEUE.md, crea la TASK correspondiente
153
+ 2. Lee QUEUE.md en ${WORKSPACE} — si hay tareas fallidas que la TUI no pudo reasignar automáticamente (marcadas como failed), reasígnalas manualmente al siguiente agente disponible
154
+ 3. Si hay agentes idle y tareas pendientes que no se están procesando, revisa si hay un problema de dependencias o bloqueo y resuélvelo
155
+ 4. Si detectas que el trabajo avanza normalmente, no hagas nada y responde brevemente el estado
156
+
157
+ No hagas commit ni push. No inventes tareas nuevas fuera del alcance actual.`
158
+ : `Away Mode active — automatic check every 5 minutes.
159
+
160
+ Current orchestrator state:
161
+ ${stateContext}
162
+
163
+ Instructions:
164
+ 1. Read INBOX.md in ${WORKSPACE} — if there are completed agent analyses without a corresponding implementation task in QUEUE.md, create the TASK
165
+ 2. Read QUEUE.md in ${WORKSPACE} — if there are failed tasks that the TUI could not auto-reassign (marked as failed), manually reassign to the next available agent
166
+ 3. If there are idle agents and pending tasks not being processed, check for dependency or blocking issues and resolve them
167
+ 4. If work is progressing normally, do nothing and briefly report the status
168
+
169
+ Do not commit or push. Do not invent new tasks outside the current scope.`;
170
+
171
+ console.log(` -> Disparando prompt a Claude para monitoreo...`);
172
+ launchClaude(monitorPrompt);
package/src/ink/app.mjs CHANGED
@@ -19,6 +19,8 @@ const TEXT = {
19
19
  es: {
20
20
  busy: 'Ocupado',
21
21
  idle: 'En espera',
22
+ failed: 'Falló',
23
+ retrying: 'Reintentando',
22
24
  noTask: 'Sin tarea activa',
23
25
  ready: 'Listo para trabajar',
24
26
  project: 'Proyecto',
@@ -31,9 +33,7 @@ const TEXT = {
31
33
  paused: 'Pausado',
32
34
  preview: 'Explorando Ink',
33
35
  shortcuts: 'Atajos',
34
- start: 'iniciar/reanudar',
35
36
  pause: 'pausar',
36
- reload: 'recargar QUEUE.md',
37
37
  quit: 'salir y matar agentes',
38
38
  summary: 'Resumen',
39
39
  activeQueue: 'Cola activa',
@@ -43,6 +43,8 @@ const TEXT = {
43
43
  en: {
44
44
  busy: 'Busy',
45
45
  idle: 'Idle',
46
+ failed: 'Failed',
47
+ retrying: 'Retrying',
46
48
  noTask: 'No active task',
47
49
  ready: 'Ready to work',
48
50
  project: 'Project',
@@ -55,9 +57,7 @@ const TEXT = {
55
57
  paused: 'Paused',
56
58
  preview: 'Exploring Ink',
57
59
  shortcuts: 'Shortcuts',
58
- start: 'start/resume',
59
60
  pause: 'pause',
60
- reload: 'reload QUEUE.md',
61
61
  quit: 'quit and stop agents',
62
62
  summary: 'Summary',
63
63
  activeQueue: 'Active Queue',
@@ -82,7 +82,20 @@ const Panel = ({title, width, children}) =>
82
82
  );
83
83
 
84
84
  const AgentCard = ({agent, text}) => {
85
- const statusColor = agent.status === 'busy' ? COLORS.success : COLORS.muted;
85
+ const statusColor =
86
+ agent.status === 'busy' ? COLORS.success :
87
+ agent.status === 'failed' ? 'red' :
88
+ agent.status === 'retrying' ? COLORS.warning :
89
+ COLORS.muted;
90
+
91
+ const statusLabel =
92
+ agent.status === 'busy' ? text.busy :
93
+ agent.status === 'failed' ? text.failed :
94
+ agent.status === 'retrying' ? text.retrying :
95
+ text.idle;
96
+
97
+ const costLine = agent.totalCost > 0 ? `${text.cost}: $${agent.totalCost.toFixed(2)}` : null;
98
+
86
99
  return h(
87
100
  Box,
88
101
  {
@@ -94,13 +107,14 @@ const AgentCard = ({agent, text}) => {
94
107
  flexDirection: 'column'
95
108
  },
96
109
  h(Text, {bold: true, wrap: 'wrap'}, agent.name),
97
- h(Text, {color: statusColor}, agent.status === 'busy' ? text.busy : text.idle),
110
+ h(Text, {color: statusColor, bold: agent.status === 'failed' || agent.status === 'retrying'}, statusLabel),
98
111
  h(
99
112
  Text,
100
113
  {color: COLORS.muted, wrap: 'wrap'},
101
114
  agent.task || text.noTask
102
115
  ),
103
- h(Text, {color: COLORS.muted, wrap: 'wrap'}, agent.detail || text.ready)
116
+ h(Text, {color: COLORS.muted, wrap: 'wrap'}, agent.detail || text.ready),
117
+ ...(costLine ? [h(Text, {color: COLORS.success, wrap: 'wrap'}, costLine)] : [])
104
118
  );
105
119
  };
106
120
 
@@ -123,8 +137,6 @@ export function App({snapshot, paused = false, onAction}) {
123
137
  }
124
138
 
125
139
  const normalized = input.toLowerCase();
126
- if (normalized === 'r') onAction?.('reload');
127
- if (normalized === 's') onAction?.('start');
128
140
  if (normalized === 'p') onAction?.('pause');
129
141
  if (normalized === 'q') onAction?.('quit');
130
142
  });
@@ -173,14 +185,10 @@ export function App({snapshot, paused = false, onAction}) {
173
185
  Box,
174
186
  {marginBottom: 1},
175
187
  h(Text, {color: COLORS.warning}, `${text.shortcuts}: `),
176
- h(Text, {bold: true}, 'S'),
177
- h(Text, {color: COLORS.muted}, truncate(` ${text.start} `, Math.max(0, shortcutWidth - 40))),
178
188
  h(Text, {bold: true}, 'P'),
179
189
  h(Text, {color: COLORS.muted}, truncate(` ${text.pause} `, Math.max(0, shortcutWidth - 55))),
180
- h(Text, {bold: true}, 'R'),
181
- h(Text, {color: COLORS.muted}, truncate(` ${text.reload} `, Math.max(0, shortcutWidth - 70))),
182
190
  h(Text, {bold: true}, 'Q'),
183
- h(Text, {color: COLORS.muted}, truncate(` ${text.quit}`, Math.max(0, shortcutWidth - 90)))
191
+ h(Text, {color: COLORS.muted}, truncate(` ${text.quit}`, Math.max(0, shortcutWidth - 70)))
184
192
  ),
185
193
  h(
186
194
  Box,
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 =>
@@ -218,14 +219,33 @@ function buildSnapshot() {
218
219
 
219
220
  const agents = Object.entries(config.agents || {}).map(([name]) => {
220
221
  const agent = engineState.agents?.[name];
222
+ const lastLine = agent?.lastLine || '';
223
+
224
+ let status;
225
+ if (agent?.status === 'busy') {
226
+ status = 'busy';
227
+ } else if (lastLine.startsWith('FALLÓ:') || lastLine.startsWith('FAILED:')) {
228
+ status = 'failed';
229
+ } else if (
230
+ lastLine.startsWith('REINTENTO:') ||
231
+ lastLine.startsWith('LÍMITE:') ||
232
+ lastLine.startsWith('RETRY:') ||
233
+ lastLine.startsWith('LIMIT:')
234
+ ) {
235
+ status = 'retrying';
236
+ } else {
237
+ status = 'idle';
238
+ }
239
+
221
240
  return {
222
241
  name,
223
- status: agent?.status === 'busy' ? 'busy' : 'idle',
242
+ status,
224
243
  task: agent?.task ? `${agent.task.id} · ${agent.task.title}` : null,
225
244
  detail:
226
245
  agent?.status === 'busy'
227
246
  ? `${agent.task?.priority || 'P?'} · ${agent.task?.repo || 'repo'}`
228
- : agent?.lastLine || text.ready
247
+ : lastLine || text.ready,
248
+ totalCost: agent?.totalCost || 0
229
249
  };
230
250
  });
231
251
 
@@ -266,6 +286,7 @@ function ensureEngine() {
266
286
 
267
287
  const childArgs = [ENGINE_FILE, '--headless'];
268
288
  if (startPaused) childArgs.push('--paused');
289
+ if (startYolo) childArgs.push('--yolo');
269
290
 
270
291
  pushLocalEvent(
271
292
  startPaused ? text.startPaused : text.startRunning
@@ -64,44 +64,63 @@ When the user says something like `Read ORCHESTRATOR.md and start`, do this:
64
64
 
65
65
  1. Read this file completely.
66
66
  2. Read `orchestrator.config.json` — identify the real project paths in `repos` (frontend, backend). Those are the paths where the worker agents operate.
67
- 3. Read `<projectName>-plan.md`, `PLAN.md`, or `plan.md` if present.
68
- 4. Read the newest `handoffs/HANDOFF-*.md` if the folder exists.
69
- 5. **Read `INBOX.md` if it exists** it contains automatic TUI notifications of completed tasks that require your attention (creating next TASKs, reading agent reports, etc.).
70
- 6. Read `QUEUE.md` to understand pending, active, and completed work.
71
- 7. Read all `progress/PROGRESS-*.md` files if present.
72
- 8. Read `ENGRAM.md` and follow the memory rules.
73
- 9. Use `openspec/` for large or multi-phase changes.
74
- 10. Tell the user the orchestrator is ready and ask what to prioritize.
67
+ 3. **Verify script automation:** check if `logs/schedule-configured.json` exists.
68
+ - If it does **NOT exist**: run `agentflow schedule` in the workspace directory to register `auto-trigger.js` (every 1 min) and `monitor-check.js` (every 5 min) in the task scheduler. Then create `logs/schedule-configured.json` with `{"configuredAt": "<date>"}`. Inform the user that automation is ready.
69
+ - If it **already exists**: continue without doing anything.
70
+ 4. Read `<projectName>-plan.md`, `PLAN.md`, or `plan.md` if present.
71
+ 5. Read the newest `handoffs/HANDOFF-*.md` if the folder exists.
72
+ 6. **Read `INBOX.md` if it exists** it contains automatic TUI notifications of completed tasks that require your attention (creating next TASKs, reading agent reports, etc.).
73
+ 7. Read `QUEUE.md` to understand pending, active, and completed work.
74
+ 8. Read all `progress/PROGRESS-*.md` files if present.
75
+ 9. Read `ENGRAM.md` and follow the memory rules.
76
+ 10. Use `openspec/` for large or multi-phase changes.
77
+ 11. Tell the user the orchestrator is ready and ask what to prioritize.
75
78
 
76
79
  **INBOX rule:** At the start of EACH response, if `INBOX.md` has new entries since your last read, check it first. This is how you know when an agent finished and what to create next — without Away Mode active.
77
80
 
81
+ **STATUS rule:** Also read `STATUS.md` at the start of each response to get current context of agents and queue. This file updates automatically every 60 seconds.
82
+
83
+ **ACTIONS rule:** If `ACTIONS.md` exists, read it too — it contains automatic monitoring actions (completed tasks needing follow-up, failed tasks, stuck tasks, etc).
84
+
78
85
  Startup is context loading only. Do not create project code changes during startup.
79
86
 
80
87
  ## Away Mode
81
88
 
82
89
  If the user says something like:
83
90
 
91
+ - `I will be away for 1 hour`
84
92
  - `I will be away for 2 hours`
85
- - `monitor while I am gone`
86
- - `keep checking`
87
- - `continue while I am away`
88
-
89
- enter **Away Mode** for that session.
90
-
91
- In Away Mode:
92
-
93
- 1. Check work state every 5 minutes.
94
- 2. Read `QUEUE.md`, completed tasks, active tasks, idle agents, progress files, and blocked tasks.
95
- 3. Assign new useful TASKs when agents become idle, as long as the work stays within the user's stated goal.
96
- 4. Update `QUEUE.md` and `TASKS.md` when work needs splitting, dependency cleanup, or a next batch.
97
- 5. Keep progress moving without inventing new product scope.
93
+ - `going out for a while`
94
+ - `activate monitoring`
98
95
 
99
- Away Mode limits:
96
+ **Activate Away Mode:**
97
+ ```bash
98
+ echo away > .away-mode
99
+ ```
100
100
 
101
- - Do not change the user's objective.
102
- - Do not open unrelated work streams.
103
- - Do not use Gemini, Cursor, or Abacus without explicit permission.
104
- - If a decision is risky or ambiguous, leave a note in `QUEUE.md` or a handoff instead of guessing.
101
+ **The monitor-check.js script will run every 5 minutes** and check:
102
+ - Completed tasks without follow-up
103
+ - Failed tasks
104
+ - Stuck tasks (>10 min)
105
+ - And write to ACTIONS.md
106
+
107
+ **Auto-deactivate:**
108
+ When there are NO pending tasks AND NO agents working AND all tasks are completed:
109
+ - The script removes .away-mode automatically
110
+ - Away Mode deactivates by itself
111
+ - When you return and say "I'm back" → Clayde responds normally
112
+
113
+ **The monitor-check.js script will run every 5 minutes** and check:
114
+ - Completed tasks without follow-up
115
+ - Failed tasks
116
+ - Stuck tasks (>10 min)
117
+ - And write to ACTIONS.md so when you return, Claude reads it automatically
118
+
119
+ **Deactivate Away Mode:**
120
+ ```bash
121
+ # Delete indicator file
122
+ del .away-mode
123
+ ```
105
124
 
106
125
  ## Fallback Policy
107
126
 
@@ -142,7 +161,13 @@ Default agent summary:
142
161
 
143
162
  ## How To Assign Work
144
163
 
145
- Write TASKs in `QUEUE.md` with this format:
164
+ 1. **When the user asks for a change or new task** → **NEVER analyze directly yourself**
165
+ - **First**: Create a TASK in `QUEUE.md` assigned to **OpenCode** to analyze the context
166
+ - **Second**: Wait for OpenCode to finish its analysis (check INBOX.md or progress/)
167
+ - **Third**: You receive the analysis → create new TASK to implement (Codex or OpenCode)
168
+ - **Never analyze the project code yourself** - that's OpenCode's job
169
+
170
+ 2. Write TASKs in `QUEUE.md` with this format:
146
171
 
147
172
  ```text
148
173
  TASK-NNN | short title | Agent | P1 | repo | detailed description
@@ -155,7 +180,7 @@ Rules:
155
180
  3. Add `> after:TASK-NNN` at the end of the description for dependencies.
156
181
  4. Use `TASKS.md` under `### TASK-NNN` for longer task specs when needed.
157
182
  5. Use `briefs/TASK-NNN-BRIEF.md` for very detailed briefs when needed.
158
- 6. After changing `QUEUE.md`, tell the user to press `R` in the TUI, and `S` if the TUI is paused.
183
+ 6. **The TUI starts automatically** - you don't need to press R or S. The TUI detects new tasks and launches them.
159
184
 
160
185
  Routing preferences:
161
186
 
@@ -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
  }