karajan-code 1.36.0 → 1.37.0

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/docs/README.es.md CHANGED
@@ -23,39 +23,40 @@
23
23
 
24
24
  ## Que es Karajan Code?
25
25
 
26
- Karajan Code (`kj`) orquesta multiples agentes de IA a traves de un pipeline automatizado: generacion de codigo, analisis estatico, revision de codigo, testing y auditorias de seguridad todo en un solo comando.
26
+ Karajan Code (`kj`) orquesta multiples agentes de IA a traves de un pipeline automatizado: generacion de codigo, analisis estatico, revision de codigo, testing y auditorias de seguridad. Todo en un solo comando.
27
27
 
28
28
  En lugar de ejecutar un agente de IA y revisar manualmente su output, `kj` encadena agentes con quality gates. El coder escribe codigo, SonarQube lo analiza, el reviewer lo revisa, y si hay problemas, el coder recibe otra oportunidad. Este bucle se repite hasta que el codigo es aprobado o se alcanza el limite de iteraciones.
29
29
 
30
30
  **Caracteristicas principales:**
31
31
  - **Pipeline multi-agente** con 11 roles configurables
32
32
  - **5 agentes de IA soportados**: Claude, Codex, Gemini, Aider, OpenCode
33
- - **Servidor MCP** con 20 herramientas usa `kj` desde Claude, Codex o cualquier host compatible con MCP sin salir de tu agente. [Ver configuracion MCP](#servidor-mcp)
34
- - **Bootstrap obligatorio** valida prerequisitos del entorno (git, remote, config, agentes, SonarQube) antes de cada ejecucion. Si algo falta, para con instrucciones claras
35
- - **TDD obligatorio** se exigen cambios en tests cuando se modifican ficheros fuente
36
- - **Integracion con SonarQube** analisis estatico con quality gates (requiere [Docker](#requisitos))
37
- - **Perfiles de revision** standard, strict, relaxed, paranoid
38
- - **Tracking de presupuesto** monitorizacion de tokens y costes por sesion con `--trace`
39
- - **Automatizacion Git** auto-commit, auto-push, auto-PR tras aprobacion
40
- - **Gestion de sesiones** pausa/reanudacion con deteccion fail-fast y limpieza automatica de sesiones expiradas
41
- - **Sistema de plugins** extiende con agentes custom via `.karajan/plugins/`
42
- - **Checkpoints interactivos** — en lugar de matar tareas largas, pausa cada 5 minutos con un informe de progreso y te deja decidir: continuar, parar o ajustar el tiempo
43
- - **Descomposicion de tareas** triage detecta cuando una tarea debe dividirse y recomienda subtareas; con integracion Planning Game, crea cards vinculadas con bloqueo secuencial
44
- - **Retry con backoff** recuperacion automatica ante errores transitorios de API (429, 5xx) con backoff exponencial y jitter
45
- - **Pipeline stage tracker** vista de progreso acumulativo durante `kj_run` mostrando que stages estan completadas, en ejecucion o pendientes tanto en CLI como via eventos MCP para renderizado en tiempo real en el host
46
- - **Guardarrailes de observabilidad del planner** telemetria continua de heartbeat/stall, proteccion configurable por silencio maximo (`session.max_agent_silence_minutes`) y limite duro de ejecucion (`session.max_planner_minutes`) para evitar bloqueos prolongados en `kj_plan`/planner
47
- - **Standby por rate-limit** cuando un agente alcanza limites de uso, Karajan parsea el tiempo de espera, espera con backoff exponencial y reanuda automaticamente en vez de fallar
48
- - **Preflight handshake** `kj_preflight` requiere confirmacion humana de la configuracion de agentes antes de ejecutar, previniendo que la IA cambie asignaciones silenciosamente
49
- - **Config de 3 niveles** sesion > proyecto > global con scoping de `kj_agents`
50
- - **Mediacion inteligente del reviewer** — el scope filter difiere automaticamente issues del reviewer fuera de scope (ficheros no presentes en el diff) como deuda tecnica rastreada en vez de bloquear; Solomon media reviews estancados; el contexto diferido se inyecta en el prompt del coder
51
- - **Integracion con Planning Game** combina opcionalmente con [Planning Game](https://github.com/AgenteIA-Geniova/planning-game) para gestion agil de proyectos (tareas, sprints, estimacion) como Jira, pero open-source y nativo XP
52
-
53
- > **Mejor con MCP** — Karajan Code esta disenado para usarse como servidor MCP dentro de tu agente de IA (Claude, Codex, etc.). El agente envia tareas a `kj_run`, recibe notificaciones de progreso en tiempo real, y obtiene resultados estructurados — sin copiar y pegar.
33
+ - **Servidor MCP** con 20 herramientas. Usa `kj` desde Claude, Codex o cualquier host compatible con MCP sin salir de tu agente. [Ver configuracion MCP](#servidor-mcp)
34
+ - **Bootstrap obligatorio.** Valida prerequisitos del entorno (git, remote, config, agentes, SonarQube) antes de cada ejecucion. Si algo falta, para con instrucciones claras
35
+ - **Guardia anti-inyeccion.** Escanea diffs antes de pasarlos a la IA: detecta directivas de override, Unicode invisible y payloads ocultos en comentarios. Tambien como GitHub Action en cada PR
36
+ - **TDD obligatorio.** Se exigen cambios en tests cuando se modifican ficheros fuente
37
+ - **Integracion con SonarQube.** Analisis estatico con quality gates (requiere [Docker](#requisitos))
38
+ - **Perfiles de revision.** standard, strict, relaxed, paranoid
39
+ - **Tracking de presupuesto.** Monitorizacion de tokens y costes por sesion con `--trace`
40
+ - **Automatizacion Git.** Auto-commit, auto-push, auto-PR tras aprobacion
41
+ - **Gestion de sesiones.** Pausa/reanudacion con deteccion fail-fast y limpieza automatica de sesiones expiradas
42
+ - **Sistema de plugins.** Extiende con agentes custom via `.karajan/plugins/`
43
+ - **Checkpoints interactivos.** En lugar de matar tareas largas, pausa cada 5 minutos con un informe de progreso y te deja decidir: continuar, parar o ajustar el tiempo
44
+ - **Descomposicion de tareas.** Triage detecta cuando una tarea debe dividirse y recomienda subtareas; con integracion Planning Game, crea cards vinculadas con bloqueo secuencial
45
+ - **Retry con backoff.** Recuperacion automatica ante errores transitorios de API (429, 5xx) con backoff exponencial y jitter
46
+ - **Pipeline stage tracker.** Vista de progreso acumulativo durante `kj_run` mostrando que stages estan completadas, en ejecucion o pendientes, tanto en CLI como via eventos MCP para renderizado en tiempo real en el host
47
+ - **Guardarrailes de observabilidad del planner.** Telemetria continua de heartbeat/stall, proteccion configurable por silencio maximo (`session.max_agent_silence_minutes`) y limite duro de ejecucion (`session.max_planner_minutes`) para evitar bloqueos prolongados en `kj_plan`/planner
48
+ - **Standby por rate-limit.** Cuando un agente alcanza limites de uso, Karajan parsea el tiempo de espera, espera con backoff exponencial y reanuda automaticamente en vez de fallar
49
+ - **Preflight handshake.** `kj_preflight` requiere confirmacion humana de la configuracion de agentes antes de ejecutar, previniendo que la IA cambie asignaciones silenciosamente
50
+ - **Config de 3 niveles.** Sesion > proyecto > global con scoping de `kj_agents`
51
+ - **Mediacion inteligente del reviewer.** El scope filter difiere automaticamente issues del reviewer fuera de scope (ficheros no presentes en el diff) como deuda tecnica rastreada en vez de bloquear; Solomon media reviews estancados; el contexto diferido se inyecta en el prompt del coder
52
+ - **Integracion con Planning Game.** Combina opcionalmente con [Planning Game](https://github.com/AgenteIA-Geniova/planning-game) para gestion agil de proyectos (tareas, sprints, estimacion). Como Jira, pero open-source y nativo XP
53
+
54
+ > **Mejor con MCP.** Karajan Code esta disenado para usarse como servidor MCP dentro de tu agente de IA (Claude, Codex, etc.). El agente envia tareas a `kj_run`, recibe notificaciones de progreso en tiempo real, y obtiene resultados estructurados. Sin copiar y pegar.
54
55
 
55
56
  ## Requisitos
56
57
 
57
58
  - **Node.js** >= 18
58
- - **Docker** necesario para el analisis estatico con SonarQube. Si no tienes Docker o no necesitas SonarQube, desactivalo con `--no-sonar` o `sonarqube.enabled: false` en la config
59
+ - **Docker** (necesario para SonarQube). Si no lo necesitas, desactivalo con `--no-sonar` o `sonarqube.enabled: false`
59
60
  - Al menos un agente de IA instalado: Claude, Codex, Gemini o Aider
60
61
 
61
62
  ## Pipeline
@@ -66,7 +67,7 @@ triage? ─> researcher? ─> planner? ─> coder ─> refactorer? ─> sonar?
66
67
 
67
68
  | Rol | Descripcion | Por defecto |
68
69
  |-----|-------------|-------------|
69
- | **triage** | Director de pipeline analiza la complejidad y activa roles dinamicamente | **On** |
70
+ | **triage** | Director de pipeline: analiza la complejidad y activa roles dinamicamente | **On** |
70
71
  | **researcher** | Investiga el contexto del codebase antes de planificar | Off |
71
72
  | **planner** | Genera planes de implementacion estructurados | Off |
72
73
  | **coder** | Escribe codigo y tests siguiendo metodologia TDD | **Siempre activo** |
@@ -75,7 +76,7 @@ triage? ─> researcher? ─> planner? ─> coder ─> refactorer? ─> sonar?
75
76
  | **reviewer** | Revision de codigo con perfiles de exigencia configurables | **Siempre activo** |
76
77
  | **tester** | Quality gate de tests y verificacion de cobertura | **On** |
77
78
  | **security** | Auditoria de seguridad OWASP | **On** |
78
- | **solomon** | Supervisor de sesion monitoriza salud de iteraciones con 5 reglas (incl. reviewer overreach), media reviews estancados, escala ante anomalias | **On** |
79
+ | **solomon** | Supervisor de sesion: monitoriza salud de iteraciones con 5 reglas (incl. reviewer overreach), media reviews estancados, escala ante anomalias | **On** |
79
80
  | **commiter** | Automatizacion de git commit, push y PR tras aprobacion | Off |
80
81
 
81
82
  Los roles marcados con `?` son opcionales y se pueden activar por ejecucion o via config.
@@ -129,33 +130,135 @@ Guias completas: [`docs/multi-instance.md`](multi-instance.md) | [`docs/install-
129
130
 
130
131
  `kj init` auto-detecta los agentes instalados. Si solo hay uno disponible, se asigna a todos los roles automaticamente.
131
132
 
132
- ## Inicio rapido
133
+ ## Tres formas de usar Karajan
134
+
135
+ Karajan instala **tres comandos**: `kj`, `kj-tail` y `karajan-mcp`.
136
+
137
+ ### 1. CLI: directamente desde terminal
133
138
 
134
139
  ```bash
135
- # Ejecutar una tarea con defaults (claude=coder, codex=reviewer, TDD)
136
140
  kj run "Implementar autenticacion de usuario con JWT"
137
-
138
- # Solo coder (sin revision)
139
141
  kj code "Anadir validacion de inputs al formulario de registro"
140
-
141
- # Solo reviewer (revisar diff actual)
142
142
  kj review "Revisar los cambios de autenticacion"
143
+ kj plan "Refactorizar la capa de base de datos"
144
+ ```
143
145
 
144
- # Generar un plan de implementacion
145
- kj plan "Refactorizar la capa de base de datos para usar connection pooling"
146
+ ### 2. MCP: dentro de tu agente de IA
146
147
 
147
- # Pipeline completo con todas las opciones
148
- kj run "Corregir inyeccion SQL critica en el endpoint de busqueda" \
149
- --coder claude \
150
- --reviewer codex \
151
- --reviewer-fallback claude \
152
- --methodology tdd \
153
- --enable-triage \
154
- --enable-tester \
155
- --enable-security \
156
- --auto-commit \
157
- --auto-push \
158
- --max-iterations 5
148
+ El caso de uso principal. Karajan corre como servidor MCP dentro de Claude Code, Codex o Gemini. El agente tiene acceso a 20 herramientas (`kj_run`, `kj_code`, `kj_review`, etc.) y delega el trabajo pesado al pipeline de Karajan.
149
+
150
+ ```
151
+ Tu → Claude Code → kj_run (via MCP) → triage → coder → sonar → reviewer tester → security
152
+ ```
153
+
154
+ **El problema**: cuando Karajan corre dentro de un agente de IA, pierdes visibilidad. El agente te muestra el resultado final, pero no las etapas del pipeline, iteraciones o decisiones de Solomon en tiempo real.
155
+
156
+ ### 3. kj-tail: monitorizar desde otro terminal
157
+
158
+ **La herramienta companera.** Abre un segundo terminal en el **mismo directorio del proyecto** donde esta trabajando tu agente de IA:
159
+
160
+ ```bash
161
+ kj-tail
162
+ ```
163
+
164
+ Veras la salida del pipeline en vivo (etapas, resultados, iteraciones, errores) tal como ocurren.
165
+
166
+ ```bash
167
+ kj-tail # Seguir pipeline en tiempo real (por defecto)
168
+ kj-tail -v # Verbose: incluir heartbeats de agente y presupuesto
169
+ kj-tail -t # Mostrar timestamps
170
+ kj-tail -s # Snapshot: mostrar log actual y salir
171
+ kj-tail -n 50 # Mostrar ultimas 50 lineas y seguir
172
+ kj-tail --help # Todas las opciones
173
+ ```
174
+
175
+ > **Importante**: `kj-tail` debe ejecutarse desde el mismo directorio donde el agente de IA esta trabajando. Lee `<proyecto>/.kj/run.log`, que se crea cuando Karajan arranca un pipeline via MCP.
176
+
177
+ **Flujo tipico:**
178
+
179
+ ```
180
+ ┌──────────────────────────┐ ┌──────────────────────────┐
181
+ │ Terminal 1 │ │ Terminal 2 │
182
+ │ │ │ │
183
+ │ $ claude │ │ $ kj-tail │
184
+ │ > implementa la │ │ │
185
+ │ siguiente tarea │ │ ├─ 📋 Triage: medium │
186
+ │ prioritaria │ │ ├─ 🔬 Researcher ✅ │
187
+ │ │ │ ├─ 🧠 Planner ✅ │
188
+ │ (Claude llama a kj_run │ │ ├─ 🔨 Coder ✅ │
189
+ │ via MCP, solo ves │ │ ├─ 🔍 Sonar: OK │
190
+ │ el resultado final) │ │ ├─ 👁️ Reviewer ❌ │
191
+ │ │ │ ├─ ⚖️ Solomon: 2 cond. │
192
+ │ │ │ ├─ 🔨 Coder (iter 2) ✅ │
193
+ │ │ │ ├─ ✅ Review: APPROVED │
194
+ │ │ │ ├─ 🧪 Tester: passed │
195
+ │ │ │ └─ 🏁 Result: APPROVED │
196
+ └──────────────────────────┘ └──────────────────────────┘
197
+ ```
198
+
199
+ **Ejemplo con pipeline completo**, tarea compleja con todos los roles:
200
+
201
+ ```
202
+ ┌─ Terminal 1 ─────────────────────────────────────────────────────────────────┐
203
+ │ │
204
+ │ $ claude │
205
+ │ │
206
+ │ > Construye una API REST para un sistema de reservas. Requisitos: │
207
+ │ > - Express + TypeScript con validacion Zod en cada endpoint │
208
+ │ > - Endpoints: POST /bookings, GET /bookings/:id, │
209
+ │ > PATCH /bookings/:id/cancel │
210
+ │ > - Una reserva tiene: id, guestName, roomType (standard|suite|penthouse), │
211
+ │ > checkIn, checkOut, status (confirmed|cancelled) │
212
+ │ > - Validar: checkOut posterior a checkIn, sin fechas pasadas, │
213
+ │ > roomType debe ser un valor valido del enum │
214
+ │ > - Cancelar devuelve 409 si ya esta cancelada │
215
+ │ > - Usa TDD. Ejecutalo con Karajan con architect y planner activos, │
216
+ │ > modo paranoid. Coder claude, reviewer codex. │
217
+ │ │
218
+ │ Claude llama a kj_run via MCP con: │
219
+ │ --enable-architect --enable-researcher --enable-planner --mode paranoid │
220
+ │ │
221
+ └──────────────────────────────────────────────────────────────────────────────┘
222
+
223
+ ┌─ Terminal 2: kj-tail ────────────────────────────────────────────────────────┐
224
+ │ │
225
+ │ kj-tail v1.37.0 — .kj/run.log │
226
+ │ │
227
+ │ ├─ 📋 Triage: medium (sw) — activando researcher, architect, planner │
228
+ │ ├─ ⚙️ Preflight passed — all checks OK │
229
+ │ ├─ 🔬 Researcher: 8 ficheros, 3 patrones, 5 restricciones │
230
+ │ ├─ 🏗️ Architect: diseno 3 capas (routes → service → validators) │
231
+ │ ├─ 🧠 Planner: 6 pasos — tests primero, luego rutas, servicio, validadores │
232
+ │ │ │
233
+ │ ▶ Iteracion 1/5 │
234
+ │ ├─ 🔨 Coder (claude): 3 endpoints + 18 tests │
235
+ │ ├─ 📋 TDD: PASS (3 src, 2 test) │
236
+ │ ├─ 🔍 Sonar: Quality gate OK │
237
+ │ ├─ 👁️ Reviewer (codex): REJECTED (2 blocking) │
238
+ │ │ "Falta 404 para GET booking inexistente" │
239
+ │ │ "Endpoint cancel sin test de idempotencia" │
240
+ │ ├─ ⚖️ Solomon: approve_with_conditions (2 condiciones) │
241
+ │ │ "Anadir respuesta 404 y test para GET /bookings/:id con id desconocido" │
242
+ │ │ "Anadir test: cancelar reserva ya cancelada devuelve 409, no 500" │
243
+ │ │ │
244
+ │ ▶ Iteracion 2/5 │
245
+ │ ├─ 🔨 Coder (claude): corregido — 22 tests │
246
+ │ ├─ 📋 TDD: PASS │
247
+ │ ├─ 🔍 Sonar: OK │
248
+ │ ├─ 👁️ Reviewer (codex): APPROVED │
249
+ │ ├─ 🧪 Tester: passed — cobertura 94%, 22 tests │
250
+ │ ├─ 🔒 Security: passed — 0 criticos, 1 bajo (helmet recomendado) │
251
+ │ ├─ 📊 Audit: CERTIFIED (3 advertencias) │
252
+ │ │ │
253
+ │ 🏁 Resultado: APPROVED │
254
+ │ 🔬 Investigacion: 8 ficheros, 3 patrones │
255
+ │ 🗺 Plan: 6 pasos (tests primero) │
256
+ │ 🧪 Cobertura: 94%, 22 tests │
257
+ │ 🔒 Seguridad: OK │
258
+ │ 🔍 Sonar: OK │
259
+ │ 💰 Presupuesto: $0.42 (claude: $0.38, codex: $0.04) │
260
+ │ │
261
+ └──────────────────────────────────────────────────────────────────────────────┘
159
262
  ```
160
263
 
161
264
  ## Comandos CLI
@@ -230,7 +333,7 @@ Cada rol tiene un template `.md` con instrucciones que el agente de IA sigue. Lo
230
333
 
231
334
  Usa `kj roles show <rol>` para inspeccionar cualquier template. Crea un override de proyecto para personalizar el comportamiento por proyecto.
232
335
 
233
- **Variantes de revision**: `reviewer-strict`, `reviewer-relaxed`, `reviewer-paranoid` seleccionables via flag `--mode` o config `review_mode`.
336
+ **Variantes de revision**: `reviewer-strict`, `reviewer-relaxed`, `reviewer-paranoid`, seleccionables via flag `--mode` o config `review_mode`.
234
337
 
235
338
  ## Contribuir
236
339
 
@@ -238,7 +341,7 @@ Usa `kj roles show <rol>` para inspeccionar cualquier template. Crea un override
238
341
  git clone https://github.com/manufosela/karajan-code.git
239
342
  cd karajan-code
240
343
  npm install
241
- npm test # Ejecutar 1040+ tests con Vitest
344
+ npm test # Ejecutar 2044 tests con Vitest
242
345
  npm run test:watch # Modo watch
243
346
  npm run validate # Lint + test
244
347
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "karajan-code",
3
- "version": "1.36.0",
3
+ "version": "1.37.0",
4
4
  "description": "Local multi-agent coding orchestrator with TDD, SonarQube, and code review pipeline",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0",
@@ -38,6 +38,7 @@
38
38
  ],
39
39
  "bin": {
40
40
  "kj": "bin/kj.js",
41
+ "kj-tail": "bin/kj-tail",
41
42
  "karajan-mcp": "bin/karajan-mcp.js"
42
43
  },
43
44
  "scripts": {
@@ -21,7 +21,7 @@ export async function runCoderStage({ coderRoleInstance, coderRole, config, logg
21
21
  emitter,
22
22
  makeEvent("coder:start", { ...eventBase, stage: "coder" }, {
23
23
  message: `Coder (${coderRole.provider}) running`,
24
- detail: { coder: coderRole.provider }
24
+ detail: { coder: coderRole.provider, provider: coderRole.provider, executorType: "agent" }
25
25
  })
26
26
  );
27
27
 
@@ -89,7 +89,8 @@ export async function runCoderStage({ coderRoleInstance, coderRole, config, logg
89
89
  emitProgress(
90
90
  emitter,
91
91
  makeEvent("coder:end", { ...eventBase, stage: "coder" }, {
92
- message: `Coder completed (fallback: ${fallbackCoder})`
92
+ message: `Coder completed (fallback: ${fallbackCoder})`,
93
+ detail: { provider: fallbackCoder, executorType: "agent" }
93
94
  })
94
95
  );
95
96
  return;
@@ -114,7 +115,8 @@ export async function runCoderStage({ coderRoleInstance, coderRole, config, logg
114
115
  emitter,
115
116
  makeEvent("coder:end", { ...eventBase, stage: "coder" }, {
116
117
  status: "fail",
117
- message: `Coder failed: ${details}`
118
+ message: `Coder failed: ${details}`,
119
+ detail: { provider: coderRole.provider, executorType: "agent" }
118
120
  })
119
121
  );
120
122
  throw new Error(`Coder failed: ${details}`);
@@ -124,7 +126,8 @@ export async function runCoderStage({ coderRoleInstance, coderRole, config, logg
124
126
  emitProgress(
125
127
  emitter,
126
128
  makeEvent("coder:end", { ...eventBase, stage: "coder" }, {
127
- message: "Coder completed"
129
+ message: "Coder completed",
130
+ detail: { provider: coderRole.provider, executorType: "agent" }
128
131
  })
129
132
  );
130
133
  }
@@ -135,7 +138,7 @@ export async function runRefactorerStage({ refactorerRole, config, logger, emitt
135
138
  emitter,
136
139
  makeEvent("refactorer:start", { ...eventBase, stage: "refactorer" }, {
137
140
  message: `Refactorer (${refactorerRole.provider}) running`,
138
- detail: { refactorer: refactorerRole.provider }
141
+ detail: { refactorer: refactorerRole.provider, provider: refactorerRole.provider, executorType: "agent" }
139
142
  })
140
143
  );
141
144
  const refactorerOnOutput = ({ stream, line }) => {
@@ -184,7 +187,8 @@ export async function runRefactorerStage({ refactorerRole, config, logger, emitt
184
187
  emitter,
185
188
  makeEvent("refactorer:end", { ...eventBase, stage: "refactorer" }, {
186
189
  status: "fail",
187
- message: `Refactorer failed: ${details}`
190
+ message: `Refactorer failed: ${details}`,
191
+ detail: { provider: refactorerRole.provider, executorType: "agent" }
188
192
  })
189
193
  );
190
194
  throw new Error(`Refactorer failed: ${details}`);
@@ -193,7 +197,8 @@ export async function runRefactorerStage({ refactorerRole, config, logger, emitt
193
197
  emitProgress(
194
198
  emitter,
195
199
  makeEvent("refactorer:end", { ...eventBase, stage: "refactorer" }, {
196
- message: "Refactorer completed"
200
+ message: "Refactorer completed",
201
+ detail: { provider: refactorerRole.provider, executorType: "agent" }
197
202
  })
198
203
  );
199
204
  }
@@ -287,7 +292,8 @@ export async function runTddCheckStage({ config, logger, emitter, eventBase, ses
287
292
  ok: tddEval.ok,
288
293
  reason: tddEval.reason,
289
294
  sourceFiles: tddEval.sourceFiles?.length || 0,
290
- testFiles: tddEval.testFiles?.length || 0
295
+ testFiles: tddEval.testFiles?.length || 0,
296
+ executorType: "local"
291
297
  }
292
298
  })
293
299
  );
@@ -386,7 +392,8 @@ export async function runSonarStage({ config, logger, emitter, eventBase, sessio
386
392
  emitProgress(
387
393
  emitter,
388
394
  makeEvent("sonar:start", { ...eventBase, stage: "sonar" }, {
389
- message: "SonarQube scanning"
395
+ message: "SonarQube scanning",
396
+ detail: { provider: "sonarqube", executorType: "local" }
390
397
  })
391
398
  );
392
399
 
@@ -470,7 +477,7 @@ export async function runSonarStage({ config, logger, emitter, eventBase, sessio
470
477
  makeEvent("sonar:end", { ...eventBase, stage: "sonar" }, {
471
478
  status: sonarResult.blocking ? "fail" : "ok",
472
479
  message: `Quality gate: ${sonarResult.gateStatus}`,
473
- detail: { projectKey: sonarResult.projectKey, gateStatus: sonarResult.gateStatus, openIssues: sonarResult.openIssuesTotal }
480
+ detail: { projectKey: sonarResult.projectKey, gateStatus: sonarResult.gateStatus, openIssues: sonarResult.openIssuesTotal, provider: "sonarqube", executorType: "local" }
474
481
  })
475
482
  );
476
483
 
@@ -498,7 +505,8 @@ export async function runSonarCloudStage({ config, logger, emitter, eventBase, s
498
505
  emitProgress(
499
506
  emitter,
500
507
  makeEvent("sonarcloud:start", { ...eventBase, stage: "sonarcloud" }, {
501
- message: "SonarCloud scanning"
508
+ message: "SonarCloud scanning",
509
+ detail: { provider: "sonarcloud", executorType: "local" }
502
510
  })
503
511
  );
504
512
 
@@ -532,7 +540,7 @@ export async function runSonarCloudStage({ config, logger, emitter, eventBase, s
532
540
  makeEvent("sonarcloud:end", { ...eventBase, stage: "sonarcloud" }, {
533
541
  status,
534
542
  message,
535
- detail: { projectKey: result.projectKey, exitCode: result.exitCode }
543
+ detail: { projectKey: result.projectKey, exitCode: result.exitCode, provider: "sonarcloud", executorType: "local" }
536
544
  })
537
545
  );
538
546
 
@@ -681,7 +689,7 @@ export async function runReviewerStage({ reviewerRole, config, logger, emitter,
681
689
  emitter,
682
690
  makeEvent("reviewer:start", { ...eventBase, stage: "reviewer" }, {
683
691
  message: `Reviewer (${reviewerRole.provider}) running`,
684
- detail: { reviewer: reviewerRole.provider }
692
+ detail: { reviewer: reviewerRole.provider, provider: reviewerRole.provider, executorType: "agent" }
685
693
  })
686
694
  );
687
695
 
@@ -692,6 +700,30 @@ export async function runReviewerStage({ reviewerRole, config, logger, emitter,
692
700
  logger.warn(`Review diff generation failed: ${err.message}`);
693
701
  return { approved: false, blocking_issues: [{ description: `Diff generation failed: ${err.message}` }], non_blocking_suggestions: [], summary: `Reviewer failed: cannot generate diff — ${err.message}`, confidence: 0 };
694
702
  }
703
+
704
+ // Injection guard: scan diff before sending to AI reviewer
705
+ const { scanDiff } = await import("../utils/injection-guard.js");
706
+ const guardResult = scanDiff(diff);
707
+ if (!guardResult.clean) {
708
+ logger.warn(`Injection guard: ${guardResult.summary}`);
709
+ emitProgress(emitter, makeEvent("guard:injection", { ...eventBase, stage: "reviewer" }, {
710
+ message: `Injection guard blocked review: ${guardResult.summary}`,
711
+ detail: { findings: guardResult.findings, summary: guardResult.summary }
712
+ }));
713
+ return {
714
+ approved: false,
715
+ blocking_issues: guardResult.findings.map((f) => ({
716
+ id: `INJECTION_${f.type.toUpperCase()}`,
717
+ severity: "critical",
718
+ description: `Potential prompt injection (${f.type}): ${f.snippet}`,
719
+ line: f.line,
720
+ })),
721
+ non_blocking_suggestions: [],
722
+ summary: `Review blocked by injection guard: ${guardResult.summary}`,
723
+ confidence: 1,
724
+ };
725
+ }
726
+
695
727
  const reviewerOnOutput = ({ stream, line }) => {
696
728
  emitProgress(emitter, makeEvent("agent:output", { ...eventBase, stage: "reviewer" }, {
697
729
  message: line,
@@ -754,7 +786,8 @@ export async function runReviewerStage({ reviewerRole, config, logger, emitter,
754
786
  emitter,
755
787
  makeEvent("reviewer:end", { ...eventBase, stage: "reviewer" }, {
756
788
  status: "fail",
757
- message: `Reviewer failed: ${details}`
789
+ message: `Reviewer failed: ${details}`,
790
+ detail: { provider: reviewerRole.provider, executorType: "agent" }
758
791
  })
759
792
  );
760
793
  throw new Error(`Reviewer failed: ${details}`);
@@ -836,7 +869,9 @@ export async function runReviewerStage({ reviewerRole, config, logger, emitter,
836
869
  blockingCount: review.blocking_issues.length,
837
870
  issues: review.blocking_issues.map(
838
871
  (x) => `${x.id || "ISSUE"}: ${x.description || "Missing description"}`
839
- )
872
+ ),
873
+ provider: reviewerRole.provider,
874
+ executorType: "agent"
840
875
  }
841
876
  })
842
877
  );
@@ -98,7 +98,8 @@ export async function runTesterStage({ config, logger, emitter, eventBase, sessi
98
98
  emitProgress(
99
99
  emitter,
100
100
  makeEvent("tester:start", { ...eventBase, stage: "tester" }, {
101
- message: "Tester evaluating test quality"
101
+ message: "Tester evaluating test quality",
102
+ detail: { provider: config?.roles?.tester?.provider || config?.roles?.coder?.provider || config?.coder || "claude", executorType: "agent" }
102
103
  })
103
104
  );
104
105
 
@@ -126,11 +127,13 @@ export async function runTesterStage({ config, logger, emitter, eventBase, sessi
126
127
  attempts: attempts.length > 1 ? attempts : undefined
127
128
  });
128
129
 
130
+ const testerProvider = provider || coderRole.provider;
129
131
  emitProgress(
130
132
  emitter,
131
133
  makeEvent("tester:end", { ...eventBase, stage: "tester" }, {
132
134
  status: testerOutput.ok ? "ok" : "fail",
133
- message: testerOutput.ok ? "Tester passed" : `Tester: ${testerOutput.summary}`
135
+ message: testerOutput.ok ? "Tester passed" : `Tester: ${testerOutput.summary}`,
136
+ detail: { ok: testerOutput.ok, summary: testerOutput.summary, provider: testerProvider, executorType: "agent" }
134
137
  })
135
138
  );
136
139
 
@@ -143,7 +146,7 @@ export async function runTesterStage({ config, logger, emitter, eventBase, sessi
143
146
  makeEvent("tester:auto-continue", { ...eventBase, stage: "tester" }, {
144
147
  status: "warn",
145
148
  message: `Tester issues are advisory (reviewer approved), continuing: ${testerOutput.summary}`,
146
- detail: { summary: testerOutput.summary, auto_continued: true }
149
+ detail: { summary: testerOutput.summary, auto_continued: true, provider: testerProvider, executorType: "agent" }
147
150
  })
148
151
  );
149
152
  return { action: "ok", stageResult: { ok: false, summary: testerOutput.summary || "Tester issues (advisory)", auto_continued: true } };
@@ -158,7 +161,8 @@ export async function runSecurityStage({ config, logger, emitter, eventBase, ses
158
161
  emitProgress(
159
162
  emitter,
160
163
  makeEvent("security:start", { ...eventBase, stage: "security" }, {
161
- message: "Security auditing code"
164
+ message: "Security auditing code",
165
+ detail: { provider: config?.roles?.security?.provider || config?.roles?.coder?.provider || config?.coder || "claude", executorType: "agent" }
162
166
  })
163
167
  );
164
168
 
@@ -186,11 +190,13 @@ export async function runSecurityStage({ config, logger, emitter, eventBase, ses
186
190
  attempts: attempts.length > 1 ? attempts : undefined
187
191
  });
188
192
 
193
+ const securityProvider = provider || coderRole.provider;
189
194
  emitProgress(
190
195
  emitter,
191
196
  makeEvent("security:end", { ...eventBase, stage: "security" }, {
192
197
  status: securityOutput.ok ? "ok" : "fail",
193
- message: securityOutput.ok ? "Security audit passed" : `Security: ${securityOutput.summary}`
198
+ message: securityOutput.ok ? "Security audit passed" : `Security: ${securityOutput.summary}`,
199
+ detail: { ok: securityOutput.ok, summary: securityOutput.summary, provider: securityProvider, executorType: "agent" }
194
200
  })
195
201
  );
196
202
 
@@ -246,7 +252,8 @@ export async function runImpeccableStage({ config, logger, emitter, eventBase, s
246
252
  emitProgress(
247
253
  emitter,
248
254
  makeEvent("impeccable:start", { ...eventBase, stage: "impeccable" }, {
249
- message: "Impeccable auditing frontend design quality"
255
+ message: "Impeccable auditing frontend design quality",
256
+ detail: { provider: config?.roles?.impeccable?.provider || coderRole.provider, executorType: "agent" }
250
257
  })
251
258
  );
252
259
 
@@ -277,13 +284,15 @@ export async function runImpeccableStage({ config, logger, emitter, eventBase, s
277
284
  });
278
285
 
279
286
  const verdict = impeccableOutput.result?.verdict || "APPROVED";
287
+ const impeccableProvider = config?.roles?.impeccable?.provider || coderRole.provider;
280
288
  emitProgress(
281
289
  emitter,
282
290
  makeEvent("impeccable:end", { ...eventBase, stage: "impeccable" }, {
283
291
  status: impeccableOutput.ok ? "ok" : "fail",
284
292
  message: impeccableOutput.ok
285
293
  ? (verdict === "IMPROVED" ? "Impeccable applied design fixes" : "Impeccable audit passed")
286
- : `Impeccable: ${impeccableOutput.summary}`
294
+ : `Impeccable: ${impeccableOutput.summary}`,
295
+ detail: { provider: impeccableProvider, executorType: "agent" }
287
296
  })
288
297
  );
289
298
 
@@ -296,7 +305,8 @@ export async function runFinalAuditStage({ config, logger, emitter, eventBase, s
296
305
  emitProgress(
297
306
  emitter,
298
307
  makeEvent("audit:start", { ...eventBase, stage: "audit" }, {
299
- message: "Final audit — verifying code quality"
308
+ message: "Final audit — verifying code quality",
309
+ detail: { provider: config?.roles?.audit?.provider || config?.roles?.coder?.provider || config?.coder || "claude", executorType: "agent" }
300
310
  })
301
311
  );
302
312
 
@@ -324,6 +334,7 @@ export async function runFinalAuditStage({ config, logger, emitter, eventBase, s
324
334
  attempts: attempts.length > 1 ? attempts : undefined
325
335
  });
326
336
 
337
+ const auditProvider = provider || coderRole.provider;
327
338
  if (!auditOutput.ok) {
328
339
  // Audit agent failed to run — treat as advisory, don't block pipeline
329
340
  logger.warn(`Audit agent error (advisory): ${auditOutput.summary}`);
@@ -331,7 +342,8 @@ export async function runFinalAuditStage({ config, logger, emitter, eventBase, s
331
342
  emitter,
332
343
  makeEvent("audit:end", { ...eventBase, stage: "audit" }, {
333
344
  status: "warn",
334
- message: `Audit: agent error (advisory), continuing — ${auditOutput.summary}`
345
+ message: `Audit: agent error (advisory), continuing — ${auditOutput.summary}`,
346
+ detail: { provider: auditProvider, executorType: "agent" }
335
347
  })
336
348
  );
337
349
  return { action: "ok", stageResult: { ok: false, summary: auditOutput.summary || "Audit agent error (advisory)", auto_continued: true } };
@@ -374,7 +386,8 @@ export async function runFinalAuditStage({ config, logger, emitter, eventBase, s
374
386
  emitter,
375
387
  makeEvent("audit:end", { ...eventBase, stage: "audit" }, {
376
388
  status: "fail",
377
- message: `Audit: ${criticalCount + highCount} issue(s) found, sending back to coder`
389
+ message: `Audit: ${criticalCount + highCount} issue(s) found, sending back to coder`,
390
+ detail: { provider: auditProvider, executorType: "agent" }
378
391
  })
379
392
  );
380
393
 
@@ -392,7 +405,8 @@ export async function runFinalAuditStage({ config, logger, emitter, eventBase, s
392
405
  emitter,
393
406
  makeEvent("audit:end", { ...eventBase, stage: "audit" }, {
394
407
  status: "ok",
395
- message: certifiedMsg
408
+ message: certifiedMsg,
409
+ detail: { provider: auditProvider, executorType: "agent" }
396
410
  })
397
411
  );
398
412