@liriraid/agentflow-ai 1.0.14 → 1.0.16

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.14",
3
+ "version": "1.0.16",
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",
@@ -22,7 +22,6 @@
22
22
  "bin",
23
23
  "src",
24
24
  "templates",
25
- "scripts",
26
25
  "orchestrator.js",
27
26
  "LICENSE"
28
27
  ],
@@ -31,9 +30,6 @@
31
30
  "start:paused": "node orchestrator.js --paused",
32
31
  "start:ink": "node src/ink/index.mjs",
33
32
  "start:ink:paused": "node src/ink/index.mjs --paused",
34
- "skills:registry": "node scripts/update-skill-registry.mjs",
35
- "openspec:new": "node scripts/scaffold-openspec-change.mjs",
36
- "agent-config:init": "node scripts/scaffold-agent-configs.mjs",
37
33
  "cli:help": "node bin/agentflow.mjs --help"
38
34
  },
39
35
  "keywords": [
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
@@ -219,14 +219,33 @@ function buildSnapshot() {
219
219
 
220
220
  const agents = Object.entries(config.agents || {}).map(([name]) => {
221
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
+
222
240
  return {
223
241
  name,
224
- status: agent?.status === 'busy' ? 'busy' : 'idle',
242
+ status,
225
243
  task: agent?.task ? `${agent.task.id} · ${agent.task.title}` : null,
226
244
  detail:
227
245
  agent?.status === 'busy'
228
246
  ? `${agent.task?.priority || 'P?'} · ${agent.task?.repo || 'repo'}`
229
- : agent?.lastLine || text.ready
247
+ : lastLine || text.ready,
248
+ totalCost: agent?.totalCost || 0
230
249
  };
231
250
  });
232
251
 
@@ -350,7 +369,7 @@ function requestAction(action) {
350
369
  }
351
370
 
352
371
  function shutdown() {
353
- if (refreshTimer) clearInterval(refreshTimer);
372
+ if (refreshTimer) clearTimeout(refreshTimer);
354
373
  if (resizeTimer) clearTimeout(resizeTimer);
355
374
  if (spawnedEngine && !spawnedEngine.killed && quitRequested) {
356
375
  try {
@@ -364,10 +383,21 @@ function shutdown() {
364
383
  try { fs.unlinkSync(STATE_FILE); } catch {}
365
384
  }
366
385
 
386
+ function scheduleRefresh() {
387
+ const engineState = readEngineState();
388
+ const busy = engineState && Object.values(engineState.agents || {}).some(a => a.status === 'busy');
389
+ const hasWork = engineState && ((engineState.queue || []).length > 0 || (engineState.inProgress || []).length > 0);
390
+ const ms = (busy || hasWork) ? 1000 : 4000;
391
+ refreshTimer = setTimeout(() => {
392
+ refresh();
393
+ scheduleRefresh();
394
+ }, ms);
395
+ }
396
+
367
397
  function mount() {
368
398
  ensureEngine();
369
399
  refresh();
370
- refreshTimer = setInterval(refresh, 1000);
400
+ scheduleRefresh();
371
401
 
372
402
  if (process.stdout.isTTY) {
373
403
  process.stdout.on('resize', () => {
@@ -64,44 +64,56 @@ 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 automation:** The orchestrator uses `fs.watch` (Node.js real-time watching). No Task Scheduler needed. The TUI stays running in a terminal and detects changes immediately.
68
+ 4. Read `<projectName>-plan.md`, `PLAN.md`, or `plan.md` if present.
69
+ 5. Read the newest `handoffs/HANDOFF-*.md` if the folder exists.
70
+ 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.).
71
+ 7. Read `QUEUE.md` to understand pending, active, and completed work.
72
+ 8. Read all `progress/PROGRESS-*.md` files if present.
73
+ 9. Read `ENGRAM.md` and follow the memory rules.
74
+ 10. Use `openspec/` for large or multi-phase changes.
75
+ 11. Tell the user the orchestrator is ready and ask what to prioritize.
75
76
 
76
77
  **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
78
 
79
+ **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.
80
+
81
+ **ACTIONS rule:** If `ACTIONS.md` exists, read it too — it contains automatic monitoring actions (completed tasks needing follow-up, failed tasks, stuck tasks, etc).
82
+
78
83
  Startup is context loading only. Do not create project code changes during startup.
79
84
 
80
85
  ## Away Mode
81
86
 
82
87
  If the user says something like:
83
88
 
89
+ - `I will be away for 1 hour`
84
90
  - `I will be away for 2 hours`
85
- - `monitor while I am gone`
86
- - `keep checking`
87
- - `continue while I am away`
91
+ - `going out for a while`
92
+ - `activate monitoring`
88
93
 
89
- enter **Away Mode** for that session.
90
-
91
- In Away Mode:
94
+ **Activate Away Mode:**
95
+ ```bash
96
+ echo away > .away-mode
97
+ ```
92
98
 
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.
99
+ **Away Mode checks every 5 minutes** and will:
100
+ - Completed tasks without follow-up
101
+ - Failed tasks
102
+ - Stuck tasks (>10 min)
103
+ - Pending tasks with no agent assigned assign them automatically
104
+ - Write to ACTIONS.md
98
105
 
99
- Away Mode limits:
106
+ **Auto-deactivate:**
107
+ When there are NO pending tasks AND NO agents working AND all tasks are completed:
108
+ - Away Mode removes .away-mode automatically
109
+ - Away Mode deactivates by itself
110
+ - When you return and say "I'm back" → Claude responds normally
100
111
 
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.
112
+ **Deactivate Away Mode:**
113
+ ```bash
114
+ # Delete indicator file
115
+ del .away-mode
116
+ ```
105
117
 
106
118
  ## Fallback Policy
107
119
 
@@ -142,7 +154,13 @@ Default agent summary:
142
154
 
143
155
  ## How To Assign Work
144
156
 
145
- Write TASKs in `QUEUE.md` with this format:
157
+ 1. **When the user asks for a change or new task** → **NEVER analyze directly yourself**
158
+ - **First**: Create a TASK in `QUEUE.md` assigned to **OpenCode** to analyze the context
159
+ - **Second**: Wait for OpenCode to finish its analysis (check INBOX.md or progress/)
160
+ - **Third**: You receive the analysis → create new TASK to implement (Codex or OpenCode)
161
+ - **Never analyze the project code yourself** - that's OpenCode's job
162
+
163
+ 2. Write TASKs in `QUEUE.md` with this format:
146
164
 
147
165
  ```text
148
166
  TASK-NNN | short title | Agent | P1 | repo | detailed description
@@ -155,7 +173,7 @@ Rules:
155
173
  3. Add `> after:TASK-NNN` at the end of the description for dependencies.
156
174
  4. Use `TASKS.md` under `### TASK-NNN` for longer task specs when needed.
157
175
  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.
176
+ 6. **The TUI starts automatically** - you don't need to press R or S. The TUI detects new tasks and launches them.
159
177
 
160
178
  Routing preferences:
161
179
 
@@ -43,18 +43,23 @@ Cuando necesites entender el proyecto para planificar tareas, **lee archivos des
43
43
  ## Al iniciar la sesión — OBLIGATORIO
44
44
 
45
45
  1. Lee este archivo completo.
46
- 2. Lee `orchestrator.config.json` — identifica las rutas reales en `repos` (frontend, backend). Esas son las rutas del proyecto real donde trabajan los agentes.
47
- 3. Lee `<projectName>-plan.md` (o `PLAN.md` / `plan.md`) si existe; ese es el plan general.
48
- 4. Lee el handoff más reciente en `handoffs/HANDOFF-*.md` si existe la carpeta.
49
- 5. **Lee `INBOX.md` si existe** contiene notificaciones automáticas del TUI de tasks completadas que requieren tu atención (crear siguientes TASKs, leer reportes de agentes, etc.).
50
- 6. Lee `QUEUE.md` para ver trabajo activo y pendiente.
51
- 7. Lee todos los archivos `progress/PROGRESS-*.md` que existan para entender el estado actual de cada agente.
52
- 8. Lee `ENGRAM.md` para respetar la convención de memoria persistente del proyecto.
53
- 9. Si existe `openspec/`, úsalo como capa de artefactos para cambios grandes o de varias fases.
54
- 10. Pregunta al usuario qué quiere priorizar; no planifiques toda la sesión automáticamente.
46
+ 2. Lee `orchestrator.config.json` — identifica las rutas reales en `repos` (frontend, backend). Esas son las rutas del proyecto real donde trabajan los agentees.
47
+ 3. **Verifica la automatización:** El orquestador usa `fs.watch` (realtime de Node.js). No necesita Task Scheduler. La TUI corre en una terminal y detecta cambios inmediatamente.
48
+ 4. Lee `<projectName>-plan.md` (o `PLAN.md` / `plan.md`) si existe; ese es el plan general.
49
+ 5. Lee el handoff más reciente en `handoffs/HANDOFF-*.md` si existe la carpeta.
50
+ 6. **Lee `INBOX.md` si existe** contiene notificaciones automáticas del TUI de tasks completadas que requieren tu atención (crear siguientes TASKs, leer reportes de agentes, etc.).
51
+ 7. Lee `QUEUE.md` para ver trabajo activo y pendiente.
52
+ 8. Lee todos los archivos `progress/PROGRESS-*.md` que existan para entender el estado actual de cada agente.
53
+ 9. Lee `ENGRAM.md` para respetar la convención de memoria persistente del proyecto.
54
+ 10. Si existe `openspec/`, úsalo como capa de artefactos para cambios grandes o de varias fases.
55
+ 11. Pregunta al usuario qué quiere priorizar; no planifiques toda la sesión automáticamente.
55
56
 
56
57
  **Regla de INBOX:** Al inicio de CADA respuesta, si `INBOX.md` tiene entradas nuevas desde tu última lectura, léelo primero antes de responder al usuario. Así sabrás qué agentes terminaron y qué falta crear.
57
58
 
59
+ **Regla de STATUS:** También lee `STATUS.md` al inicio de cada respuesta para tener contexto del estado actual de los agentes y la cola. Este archivo se actualiza automáticamente cada 60 segundos.
60
+
61
+ **Regla de ACTIONS:** Si existe `ACTIONS.md`, léelo también - contiene acciones automáticas del monitoreo (tareas completadas que necesitan seguimiento, tareas fallidas, etc).
62
+
58
63
  ## Restricción operativa por defecto
59
64
 
60
65
  Aunque esta plantilla soporte múltiples agentes, en este proyecto debes operar **solo con estas 3 IA por defecto**:
@@ -71,13 +76,34 @@ Los demás agentes pueden permanecer configurados en `orchestrator.config.json`,
71
76
 
72
77
  Si el usuario dice explícitamente algo como:
73
78
 
79
+ - `estaré ausente 1 hora`
74
80
  - `estaré ausente 2 horas`
75
81
  - `me voy un rato`
76
82
  - `activa monitoreo`
77
- - `quédate revisando`
78
- - `monitorea mientras no estoy`
79
83
 
80
- entonces debes entrar en **Modo Ausencia** durante esa sesión.
84
+ **Activar Modo Ausencia:**
85
+ ```bash
86
+ echo away > .away-mode
87
+ ```
88
+
89
+ **El modo ausente hace revisión cada 5 minutos** y revisará:
90
+ - Tareas completadas sin seguimiento
91
+ - Tareas fallidas
92
+ - Tareas atascadas (>10 min)
93
+ - Tareas pendientes sin agente asignado → las asignará automáticamente
94
+ - Y escribirá en ACTIONS.md
95
+
96
+ **Auto-desactivación:**
97
+ Cuando NO hay tareas pendientes Y NO hay agentes trabajando Y todas las tareas están completadas:
98
+ - El modo elimina .away-mode automáticamente
99
+ - Modo Ausencia se desactiva solo
100
+ - Cuando vuelvas y le digas "ya volví" → Claude responde normalmente
101
+
102
+ **Desactivar Modo Ausencia:**
103
+ ```bash
104
+ # Eliminar archivo indicador
105
+ del .away-mode
106
+ ```
81
107
 
82
108
  ### Qué significa Modo Ausencia
83
109
 
@@ -149,17 +175,23 @@ Revisa `orchestrator.config.json` → `agents`. Cada entrada tiene:
149
175
 
150
176
  ## Cómo asignar trabajo
151
177
 
152
- 1. Escribe TASKs en `QUEUE.md` (formato pipe; la TUI lo lee):
153
- ```
154
- TASK-NNN | titulo corto | Agent | P1 | repo | descripcion larga
155
- ```
156
- Valores válidos de `Agent`: exactamente las keys de `orchestrator.config.json.agents`.
157
- Valores válidos de `repo`: exactamente las keys de `orchestrator.config.json.repos`.
158
- 2. (Opcional) También escribe una spec larga en `TASKS.md` bajo un heading `### TASK-NNN`; se inyecta al brief.
159
- 3. (Opcional) Para un brief muy detallado, crea `briefs/TASK-NNN-BRIEF.md`; también se inyecta.
160
- 4. Dependencias: agrega `> after:TASK-NNN` al final de la descripción para bloquear la tarea.
161
- 5. Dile al usuario que presione **R** en la TUI para recargar la cola, o **S** si está pausada.
162
- 6. **Prioriza Codex y OpenCode** para toda implementación y exploración. Claude-Workers solo cuando hay saturación o fallo total de agentes de soporte.
178
+ 1. **Cuando el usuario pide un cambio o nueva tarea** → **NUNCA analices directamente**
179
+ - **Primero**: Crea una TASK en `QUEUE.md` asignada a **OpenCode** para que analice el contexto
180
+ - **Segundo**: Espera a que OpenCode termine su análisis (revisa INBOX.md o progress/)
181
+ - **Tercero**: Recibes el análisis → creas nueva TASK para implementar (Codex o OpenCode)
182
+ - **Nunca analices el código del proyecto directamente tu mismo** - eso lo hace OpenCode
183
+
184
+ 2. Escribe TASKs en `QUEUE.md` (formato pipe; la TUI lo lee):
185
+ ```
186
+ TASK-NNN | titulo corto | Agent | P1 | repo | descripcion larga
187
+ ```
188
+ Valores válidos de `Agent`: exactamente las keys de `orchestrator.config.json.agents`.
189
+ Valores válidos de `repo`: exactamente las keys de `orchestrator.config.json.repos`.
190
+ 3. (Opcional) También escribe una spec larga en `TASKS.md` bajo un heading `### TASK-NNN`; se inyecta al brief.
191
+ 4. (Opcional) Para un brief muy detallado, crea `briefs/TASK-NNN-BRIEF.md`; también se inyecta.
192
+ 5. Dependencias: agrega `> after:TASK-NNN` al final de la descripción para bloquear la tarea.
193
+ 6. **La TUI inicia automáticamente** - NO necesitas presionar R ni S. La TUI detecta nuevas tasks y las lanza.
194
+ 7. **Prioriza Codex y OpenCode** para toda implementación y exploración. Claude-Workers solo cuando hay saturación o fallo total de agentes de soporte.
163
195
  7. Distribución según cantidad de TASKs independientes:
164
196
  - **1 tarea**: OpenCode (exploración) o Codex (implementación). Nunca Claude-Worker en primera instancia.
165
197
  - **2 tareas**: OpenCode + Codex, una cada uno.
@@ -1,100 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from 'fs';
4
- import path from 'path';
5
-
6
- const ROOT = process.cwd();
7
- const CONFIG_FILE = path.join(ROOT, 'orchestrator.config.json');
8
- const CONFIG = fs.existsSync(CONFIG_FILE)
9
- ? JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'))
10
- : {};
11
- const LANGUAGE = CONFIG.workspaceLanguage === 'en' ? 'en' : 'es';
12
-
13
- const CONTENT = {
14
- es: {
15
- claude: [
16
- '# Claude Local Config',
17
- '',
18
- 'Esta carpeta contiene la configuración local del proyecto para Claude.',
19
- '',
20
- '- `skills/` guarda skills propias del repo',
21
- '- `CLAUDE.md` en la raíz define el routing del proyecto',
22
- '- esta capa local debe priorizarse sobre configuración global del usuario'
23
- ],
24
- codex: [
25
- '# Codex Local Config',
26
- '',
27
- 'Esta carpeta reserva la configuración local del proyecto para Codex.',
28
- '',
29
- '- hoy se usa como base reusable del proyecto',
30
- '- mañana puede alojar prompts, perfiles, reglas o plugins locales',
31
- '- no debe depender solo de configuración global del usuario'
32
- ],
33
- opencode: [
34
- '# OpenCode Local Config',
35
- '',
36
- 'Esta carpeta reserva la configuración local del proyecto para OpenCode.',
37
- '',
38
- '- hoy se usa como base reusable del proyecto',
39
- '- mañana puede alojar reglas, prompts o convenciones específicas',
40
- '- no debe depender solo de configuración global del usuario'
41
- ],
42
- done: 'Configuración local por agente creada o verificada.'
43
- },
44
- en: {
45
- claude: [
46
- '# Claude Local Config',
47
- '',
48
- 'This folder contains project-local Claude configuration.',
49
- '',
50
- '- `skills/` stores repo-specific skills',
51
- '- root `CLAUDE.md` defines project routing',
52
- '- this local layer should take priority over global user config'
53
- ],
54
- codex: [
55
- '# Codex Local Config',
56
- '',
57
- 'This folder reserves project-local configuration for Codex.',
58
- '',
59
- '- today it is used as the reusable local project base',
60
- '- later it can hold prompts, profiles, rules, or local plugins',
61
- '- it should not depend only on global user config'
62
- ],
63
- opencode: [
64
- '# OpenCode Local Config',
65
- '',
66
- 'This folder reserves project-local configuration for OpenCode.',
67
- '',
68
- '- today it is used as the reusable local project base',
69
- '- later it can hold rules, prompts, or specific conventions',
70
- '- it should not depend only on global user config'
71
- ],
72
- done: 'Local agent configuration created or verified.'
73
- }
74
- };
75
- const L = CONTENT[LANGUAGE];
76
-
77
- const files = [
78
- [
79
- '.claude/README.md',
80
- L.claude.join('\n')
81
- ],
82
- [
83
- '.codex/README.md',
84
- L.codex.join('\n')
85
- ],
86
- [
87
- '.opencode/README.md',
88
- L.opencode.join('\n')
89
- ]
90
- ];
91
-
92
- for (const [relativePath, content] of files) {
93
- const absolutePath = path.join(ROOT, relativePath);
94
- fs.mkdirSync(path.dirname(absolutePath), {recursive: true});
95
- if (!fs.existsSync(absolutePath)) {
96
- fs.writeFileSync(absolutePath, `${content}\n`, 'utf8');
97
- }
98
- }
99
-
100
- console.log(L.done);
@@ -1,84 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from 'fs';
4
- import path from 'path';
5
-
6
- const ROOT = process.cwd();
7
- const CHANGE_NAME = process.argv[2];
8
- const CONFIG_FILE = path.join(ROOT, 'orchestrator.config.json');
9
- const CONFIG = fs.existsSync(CONFIG_FILE)
10
- ? JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'))
11
- : {};
12
- const LANGUAGE = CONFIG.workspaceLanguage === 'en' ? 'en' : 'es';
13
- const TEXT = {
14
- es: {
15
- usage: 'Uso: npm run openspec:new -- <change-name>',
16
- readmeIntro: 'Este change fue generado desde las plantillas locales de `openspec/`.',
17
- created: base => `OpenSpec change creado en ${base}`
18
- },
19
- en: {
20
- usage: 'Usage: npm run openspec:new -- <change-name>',
21
- readmeIntro: 'This change was generated from the local `openspec/` templates.',
22
- created: base => `OpenSpec change created at ${base}`
23
- }
24
- };
25
- const L = TEXT[LANGUAGE];
26
-
27
- if (!CHANGE_NAME) {
28
- console.error(L.usage);
29
- process.exit(1);
30
- }
31
-
32
- const base = path.join(ROOT, 'openspec', 'changes', CHANGE_NAME);
33
- const templates = path.join(ROOT, 'openspec', 'templates');
34
-
35
- const files = [
36
- ['proposal.md', 'proposal.md'],
37
- ['design.md', 'design.md'],
38
- ['tasks.md', 'tasks.md'],
39
- ['verify-report.md', 'verify-report.md'],
40
- ['archive-report.md', 'archive-report.md'],
41
- ['.openspec.yaml', 'change-metadata.yaml']
42
- ];
43
-
44
- fs.mkdirSync(base, {recursive: true});
45
- const specsDir = path.join(base, 'specs');
46
- fs.mkdirSync(specsDir, {recursive: true});
47
-
48
- for (const [targetName, templateName] of files) {
49
- const target = path.join(base, targetName);
50
- if (fs.existsSync(target)) continue;
51
- const template = fs.readFileSync(path.join(templates, templateName), 'utf8');
52
- const content = template.replaceAll('<change-name>', CHANGE_NAME);
53
- fs.writeFileSync(target, content.endsWith('\n') ? content : `${content}\n`, 'utf8');
54
- }
55
-
56
- const specTemplate = fs.readFileSync(path.join(templates, 'spec.md'), 'utf8');
57
- const specTarget = path.join(specsDir, 'spec.md');
58
- if (!fs.existsSync(specTarget)) {
59
- const specContent = specTemplate.replaceAll('<change-name>', CHANGE_NAME);
60
- fs.writeFileSync(specTarget, specContent.endsWith('\n') ? specContent : `${specContent}\n`, 'utf8');
61
- }
62
-
63
- const readme = [
64
- `# ${CHANGE_NAME}`,
65
- '',
66
- L.readmeIntro,
67
- '',
68
- '## Files',
69
- '',
70
- '- `proposal.md`',
71
- '- `design.md`',
72
- '- `tasks.md`',
73
- '- `verify-report.md`',
74
- '- `archive-report.md`',
75
- '- `.openspec.yaml`',
76
- '- `specs/spec.md`'
77
- ].join('\n');
78
-
79
- const changeReadme = path.join(base, 'README.md');
80
- if (!fs.existsSync(changeReadme)) {
81
- fs.writeFileSync(changeReadme, `${readme}\n`, 'utf8');
82
- }
83
-
84
- console.log(L.created(base));