agentic-kdd 3.2.2 → 3.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/akdd.js +8 -0
- package/effectiveness-report.cjs +402 -0
- package/package.json +1 -1
package/bin/akdd.js
CHANGED
|
@@ -66,6 +66,9 @@ const HELP = `
|
|
|
66
66
|
akdd creative apply <id> Apply a suggestion
|
|
67
67
|
akdd creative wins View applied creative improvements
|
|
68
68
|
|
|
69
|
+
Effectiveness:
|
|
70
|
+
akdd report Real data — before vs after comparison across all cycles
|
|
71
|
+
|
|
69
72
|
Session continuity:
|
|
70
73
|
akdd historial Resume context in a new chat — paste output at start
|
|
71
74
|
akdd checkpoint Save checkpoint now (auto-runs every 5 cycles)
|
|
@@ -267,6 +270,11 @@ switch (command) {
|
|
|
267
270
|
break;
|
|
268
271
|
}
|
|
269
272
|
|
|
273
|
+
// ── v3.3: Effectiveness Report ──────────────────────────────────────────────
|
|
274
|
+
case 'report':
|
|
275
|
+
runModule('effectiveness-report.cjs');
|
|
276
|
+
break;
|
|
277
|
+
|
|
270
278
|
// ── v3.3: Session Guard ────────────────────────────────────────────────────
|
|
271
279
|
case 'historial':
|
|
272
280
|
runModule('session-guard.cjs', 'historial');
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agentic KDD — Effectiveness Report v1.0
|
|
3
|
+
*
|
|
4
|
+
* Genera datos reales de efectividad comparando primeros ciclos vs últimos.
|
|
5
|
+
* Todo viene de SQLite — cero estimaciones, cero inventados.
|
|
6
|
+
*
|
|
7
|
+
* Métricas:
|
|
8
|
+
* - Error recurrence rate: ¿El mismo error ocurre dos veces?
|
|
9
|
+
* - Stop rate: ¿Cuántas veces el TDD gate paró el ciclo?
|
|
10
|
+
* - First-try test pass rate: ¿Tests pasan en el primer intento?
|
|
11
|
+
* - Rework index: ¿Cuántos ciclos tocan los mismos archivos?
|
|
12
|
+
* - Pattern velocity: ¿Qué tan rápido suben patrones a HIGH?
|
|
13
|
+
* - Memory growth: ¿Cuánto conocimiento acumula por ciclo?
|
|
14
|
+
*
|
|
15
|
+
* Uso:
|
|
16
|
+
* node effectiveness-report.cjs → reporte completo
|
|
17
|
+
* node effectiveness-report.cjs --json → output JSON para dashboards
|
|
18
|
+
* node effectiveness-report.cjs --short → resumen de una línea por métrica
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
'use strict';
|
|
22
|
+
|
|
23
|
+
const path = require('path');
|
|
24
|
+
const fs = require('fs');
|
|
25
|
+
|
|
26
|
+
// ─── DB ───────────────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
function openDB(projectRoot) {
|
|
29
|
+
const dbPath = path.join(projectRoot, '.agentic/memoria.db');
|
|
30
|
+
try { return new (require('better-sqlite3'))(dbPath, { readonly: true }); } catch {}
|
|
31
|
+
try { const { DatabaseSync } = require('node:sqlite'); return new DatabaseSync(dbPath); } catch {}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ─── CALCULAR MÉTRICAS POR VENTANA DE CICLOS ─────────────────────────────────
|
|
36
|
+
|
|
37
|
+
function calcWindowMetrics(db, cycleIds) {
|
|
38
|
+
if (!cycleIds || cycleIds.length === 0) {
|
|
39
|
+
return { stops: 0, stop_rate: 0, errors_found: 0, errors_per_cycle: 0,
|
|
40
|
+
tests_total: 0, tests_passed: 0, first_try_rate: 0,
|
|
41
|
+
patterns_applied: 0, patterns_per_cycle: 0, cycles: 0 };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const placeholders = cycleIds.map(() => '?').join(',');
|
|
45
|
+
const safe = (fn) => { try { return fn(); } catch { return null; } };
|
|
46
|
+
|
|
47
|
+
const stops = safe(() =>
|
|
48
|
+
db.prepare(`SELECT COUNT(*) as n FROM ciclos WHERE estado='STOP' AND ciclo_id IN (${placeholders})`).get(...cycleIds)?.n
|
|
49
|
+
) || 0;
|
|
50
|
+
|
|
51
|
+
const errorsFound = safe(() => {
|
|
52
|
+
const rows = db.prepare(`SELECT errores_encontrados FROM ciclos WHERE ciclo_id IN (${placeholders})`).all(...cycleIds);
|
|
53
|
+
return rows.reduce((s, r) => s + (parseInt(r.errores_encontrados) || 0), 0);
|
|
54
|
+
}) || 0;
|
|
55
|
+
|
|
56
|
+
// Tests: sumar de todos los ciclos
|
|
57
|
+
let testsTotal = 0, testsPassed = 0;
|
|
58
|
+
safe(() => {
|
|
59
|
+
const rows = db.prepare(`SELECT tests_corridos FROM ciclos WHERE ciclo_id IN (${placeholders})`).all(...cycleIds);
|
|
60
|
+
rows.forEach(r => {
|
|
61
|
+
try {
|
|
62
|
+
const t = JSON.parse(r.tests_corridos || '{}');
|
|
63
|
+
testsTotal += (t.total || parseInt(r.tests_corridos) || 0);
|
|
64
|
+
testsPassed += (t.passed || parseInt(r.tests_corridos) || 0);
|
|
65
|
+
} catch {
|
|
66
|
+
const n = parseInt(r.tests_corridos) || 0;
|
|
67
|
+
testsTotal += n; testsPassed += n;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Patrones aplicados
|
|
73
|
+
const patternsApplied = safe(() => {
|
|
74
|
+
const rows = db.prepare(`SELECT patrones_aplicados FROM ciclos WHERE ciclo_id IN (${placeholders})`).all(...cycleIds);
|
|
75
|
+
return rows.reduce((s, r) => {
|
|
76
|
+
try { return s + JSON.parse(r.patrones_aplicados || '[]').length; }
|
|
77
|
+
catch { return s; }
|
|
78
|
+
}, 0);
|
|
79
|
+
}) || 0;
|
|
80
|
+
|
|
81
|
+
const n = cycleIds.length;
|
|
82
|
+
return {
|
|
83
|
+
cycles: n,
|
|
84
|
+
stops,
|
|
85
|
+
stop_rate: n > 0 ? Math.round((stops / n) * 100) : 0,
|
|
86
|
+
errors_found: errorsFound,
|
|
87
|
+
errors_per_cycle: n > 0 ? Math.round((errorsFound / n) * 10) / 10 : 0,
|
|
88
|
+
tests_total: testsTotal,
|
|
89
|
+
tests_passed: testsPassed,
|
|
90
|
+
first_try_rate: testsTotal > 0 ? Math.round((testsPassed / testsTotal) * 100) : 100,
|
|
91
|
+
patterns_applied: patternsApplied,
|
|
92
|
+
patterns_per_cycle: n > 0 ? Math.round((patternsApplied / n) * 10) / 10 : 0,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ─── ERROR RECURRENCE RATE ───────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
function calcErrorRecurrence(db, allCycleIds, splitAt) {
|
|
99
|
+
const safe = (fn) => { try { return fn(); } catch { return 0; } };
|
|
100
|
+
|
|
101
|
+
// Errores únicos en primera mitad
|
|
102
|
+
const firstHalf = allCycleIds.slice(0, splitAt);
|
|
103
|
+
const secondHalf = allCycleIds.slice(splitAt);
|
|
104
|
+
|
|
105
|
+
if (firstHalf.length === 0 || secondHalf.length === 0) return { rate: 0, recurred: 0, total_first: 0 };
|
|
106
|
+
|
|
107
|
+
const ph1 = firstHalf.map(() => '?').join(',');
|
|
108
|
+
const ph2 = secondHalf.map(() => '?').join(',');
|
|
109
|
+
|
|
110
|
+
// Patrones de error en primera mitad
|
|
111
|
+
const firstErrors = safe(() =>
|
|
112
|
+
db.prepare(`SELECT titulo FROM nodos WHERE tipo='error' AND fecha_creacion IN (
|
|
113
|
+
SELECT fecha_inicio FROM ciclos WHERE ciclo_id IN (${ph1})
|
|
114
|
+
)`).all(...firstHalf).map(r => r.titulo)
|
|
115
|
+
) || [];
|
|
116
|
+
|
|
117
|
+
// Mismos patrones vistos en segunda mitad
|
|
118
|
+
let recurred = 0;
|
|
119
|
+
if (firstErrors.length > 0 && secondHalf.length > 0) {
|
|
120
|
+
firstErrors.forEach(titulo => {
|
|
121
|
+
if (!titulo) return;
|
|
122
|
+
const found = safe(() =>
|
|
123
|
+
db.prepare(`SELECT COUNT(*) as n FROM nodos WHERE tipo='error' AND titulo=? AND fecha_creacion IN (
|
|
124
|
+
SELECT fecha_inicio FROM ciclos WHERE ciclo_id IN (${ph2})
|
|
125
|
+
)`).get(titulo, ...secondHalf)?.n
|
|
126
|
+
);
|
|
127
|
+
if (found > 0) recurred++;
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
rate: firstErrors.length > 0 ? Math.round((recurred / firstErrors.length) * 100) : 0,
|
|
133
|
+
recurred,
|
|
134
|
+
total_first: firstErrors.length,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ─── PATTERN VELOCITY ────────────────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
function calcPatternVelocity(db) {
|
|
141
|
+
const safe = (fn) => { try { return fn(); } catch { return null; } };
|
|
142
|
+
|
|
143
|
+
const highNodes = safe(() =>
|
|
144
|
+
db.prepare(`SELECT aplicado, fecha_creacion, fecha_update FROM nodos
|
|
145
|
+
WHERE confianza='ALTA' AND estado='ACTIVO' AND tipo='patron'`).all()
|
|
146
|
+
) || [];
|
|
147
|
+
|
|
148
|
+
if (highNodes.length === 0) return { avg_cycles_to_high: null, total_high: 0 };
|
|
149
|
+
|
|
150
|
+
// Promedio de aplicaciones para llegar a HIGH (proxy: campo aplicado)
|
|
151
|
+
const avgApplied = highNodes.reduce((s, n) => s + (n.aplicado || 0), 0) / highNodes.length;
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
avg_cycles_to_high: Math.round(avgApplied * 10) / 10,
|
|
155
|
+
total_high: highNodes.length,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ─── REWORK INDEX ────────────────────────────────────────────────────────────
|
|
160
|
+
|
|
161
|
+
function calcReworkIndex(db, cycleIds) {
|
|
162
|
+
if (!cycleIds || cycleIds.length < 2) return { index: 0, reworked_areas: 0 };
|
|
163
|
+
|
|
164
|
+
const safe = (fn) => { try { return fn(); } catch { return null; } };
|
|
165
|
+
const ph = cycleIds.map(() => '?').join(',');
|
|
166
|
+
|
|
167
|
+
// Áreas que aparecen en más de un ciclo = rework
|
|
168
|
+
const areas = safe(() =>
|
|
169
|
+
db.prepare(`SELECT n.area, COUNT(DISTINCT c.ciclo_id) as cycles
|
|
170
|
+
FROM nodos n
|
|
171
|
+
JOIN ciclos c ON n.fecha_creacion >= c.fecha_inicio AND n.fecha_creacion <= COALESCE(c.fecha_fin, datetime('now'))
|
|
172
|
+
WHERE c.ciclo_id IN (${ph})
|
|
173
|
+
GROUP BY n.area HAVING cycles > 1`).all(...cycleIds)
|
|
174
|
+
) || [];
|
|
175
|
+
|
|
176
|
+
const totalAreas = safe(() =>
|
|
177
|
+
db.prepare(`SELECT COUNT(DISTINCT area) as n FROM nodos WHERE estado='ACTIVO'`).get()?.n
|
|
178
|
+
) || 1;
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
index: areas.length,
|
|
182
|
+
reworked_areas:areas.length,
|
|
183
|
+
total_areas: totalAreas,
|
|
184
|
+
rate: Math.round((areas.length / totalAreas) * 100),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ─── REPORTE COMPLETO ─────────────────────────────────────────────────────────
|
|
189
|
+
|
|
190
|
+
function generateReport(projectRoot, options = {}) {
|
|
191
|
+
projectRoot = projectRoot || process.cwd();
|
|
192
|
+
const db = openDB(projectRoot);
|
|
193
|
+
if (!db) return { error: 'DB no disponible' };
|
|
194
|
+
|
|
195
|
+
const safe = (fn) => { try { return fn(); } catch { return null; } };
|
|
196
|
+
|
|
197
|
+
// Obtener todos los ciclos en orden cronológico
|
|
198
|
+
const allCycles = safe(() =>
|
|
199
|
+
db.prepare(`SELECT ciclo_id, descripcion, fecha_inicio, estado FROM ciclos ORDER BY fecha_inicio ASC`).all()
|
|
200
|
+
) || [];
|
|
201
|
+
|
|
202
|
+
if (allCycles.length < 2) {
|
|
203
|
+
db.close();
|
|
204
|
+
return { error: `Solo ${allCycles.length} ciclo(s) — se necesitan al menos 2 para comparar`, cycles: allCycles.length };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const n = allCycles.length;
|
|
208
|
+
const splitAt = Math.max(1, Math.floor(n / 2));
|
|
209
|
+
const firstIds = allCycles.slice(0, splitAt).map(c => c.ciclo_id);
|
|
210
|
+
const lastIds = allCycles.slice(splitAt).map(c => c.ciclo_id);
|
|
211
|
+
const last3Ids = allCycles.slice(-3).map(c => c.ciclo_id);
|
|
212
|
+
const first3Ids = allCycles.slice(0, 3).map(c => c.ciclo_id);
|
|
213
|
+
|
|
214
|
+
// Calcular métricas
|
|
215
|
+
const first = calcWindowMetrics(db, firstIds);
|
|
216
|
+
const last = calcWindowMetrics(db, lastIds);
|
|
217
|
+
const first3 = calcWindowMetrics(db, first3Ids);
|
|
218
|
+
const last3 = calcWindowMetrics(db, last3Ids);
|
|
219
|
+
|
|
220
|
+
const recurrence = calcErrorRecurrence(db, allCycles.map(c => c.ciclo_id), splitAt);
|
|
221
|
+
const velocity = calcPatternVelocity(db);
|
|
222
|
+
const rework = calcReworkIndex(db, last3Ids);
|
|
223
|
+
|
|
224
|
+
// Memoria evolution
|
|
225
|
+
const memFirst = safe(() =>
|
|
226
|
+
db.prepare(`SELECT COUNT(*) as n FROM nodos WHERE fecha_creacion <= (
|
|
227
|
+
SELECT fecha_inicio FROM ciclos ORDER BY fecha_inicio ASC LIMIT 1 OFFSET ?
|
|
228
|
+
)`).get(Math.max(0, splitAt - 1))?.n
|
|
229
|
+
) || 0;
|
|
230
|
+
const memNow = safe(() =>
|
|
231
|
+
db.prepare(`SELECT COUNT(*) as n FROM nodos WHERE estado='ACTIVO'`).get()?.n
|
|
232
|
+
) || 0;
|
|
233
|
+
|
|
234
|
+
// Delta helpers
|
|
235
|
+
const delta = (before, after) => {
|
|
236
|
+
if (before === 0 && after === 0) return { val: 0, pct: 0, dir: '→' };
|
|
237
|
+
if (before === 0) return { val: after, pct: 100, dir: '↑' };
|
|
238
|
+
const pct = Math.round(((after - before) / before) * 100);
|
|
239
|
+
return { val: after - before, pct: Math.abs(pct), dir: pct > 0 ? '↑' : pct < 0 ? '↓' : '→' };
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const report = {
|
|
243
|
+
generated: new Date().toISOString(),
|
|
244
|
+
project: path.basename(projectRoot),
|
|
245
|
+
total_cycles: n,
|
|
246
|
+
window: {
|
|
247
|
+
first_half: { cycles: firstIds.length, label: `ciclos 1-${firstIds.length}` },
|
|
248
|
+
second_half: { cycles: lastIds.length, label: `ciclos ${firstIds.length + 1}-${n}` },
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
metrics: {
|
|
252
|
+
stop_rate: {
|
|
253
|
+
label: 'Stop rate (TDD gate)',
|
|
254
|
+
description: 'Ciclos donde el harness tuvo que parar',
|
|
255
|
+
first: `${first.stop_rate}%`,
|
|
256
|
+
last: `${last.stop_rate}%`,
|
|
257
|
+
delta: delta(first.stop_rate, last.stop_rate),
|
|
258
|
+
trend: last.stop_rate < first.stop_rate ? 'improving' : last.stop_rate > first.stop_rate ? 'degrading' : 'stable',
|
|
259
|
+
raw: { first: first.stop_rate, last: last.stop_rate },
|
|
260
|
+
},
|
|
261
|
+
errors_per_cycle: {
|
|
262
|
+
label: 'Errores por ciclo',
|
|
263
|
+
description: 'Promedio de errores nuevos encontrados por ciclo',
|
|
264
|
+
first: first.errors_per_cycle,
|
|
265
|
+
last: last.errors_per_cycle,
|
|
266
|
+
delta: delta(first.errors_per_cycle, last.errors_per_cycle),
|
|
267
|
+
trend: last.errors_per_cycle < first.errors_per_cycle ? 'improving' : 'stable',
|
|
268
|
+
raw: { first: first.errors_per_cycle, last: last.errors_per_cycle },
|
|
269
|
+
},
|
|
270
|
+
first_try_rate: {
|
|
271
|
+
label: 'Tests en primer intento',
|
|
272
|
+
description: '% de tests que pasan sin self-healing',
|
|
273
|
+
first: `${first.first_try_rate}%`,
|
|
274
|
+
last: `${last.first_try_rate}%`,
|
|
275
|
+
delta: delta(first.first_try_rate, last.first_try_rate),
|
|
276
|
+
trend: last.first_try_rate >= first.first_try_rate ? 'improving' : 'degrading',
|
|
277
|
+
raw: { first: first.first_try_rate, last: last.first_try_rate },
|
|
278
|
+
},
|
|
279
|
+
error_recurrence: {
|
|
280
|
+
label: 'Recurrencia de errores',
|
|
281
|
+
description: '% de errores de la primera mitad que volvieron a ocurrir',
|
|
282
|
+
value: `${recurrence.rate}%`,
|
|
283
|
+
recurred: recurrence.recurred,
|
|
284
|
+
total: recurrence.total_first,
|
|
285
|
+
trend: recurrence.rate < 20 ? 'good' : recurrence.rate < 50 ? 'moderate' : 'needs_work',
|
|
286
|
+
},
|
|
287
|
+
pattern_velocity: {
|
|
288
|
+
label: 'Velocidad de patrones',
|
|
289
|
+
description: 'Promedio de aplicaciones para llegar a HIGH confidence',
|
|
290
|
+
avg_to_high: velocity.avg_cycles_to_high,
|
|
291
|
+
total_high: velocity.total_high,
|
|
292
|
+
trend: velocity.total_high > 5 ? 'good' : 'building',
|
|
293
|
+
},
|
|
294
|
+
memory_growth: {
|
|
295
|
+
label: 'Crecimiento de memoria',
|
|
296
|
+
description: 'Nodos de conocimiento acumulados',
|
|
297
|
+
at_start: memFirst,
|
|
298
|
+
now: memNow,
|
|
299
|
+
delta: memNow - memFirst,
|
|
300
|
+
per_cycle: n > 0 ? Math.round(((memNow - memFirst) / n) * 10) / 10 : 0,
|
|
301
|
+
trend: memNow > memFirst ? 'growing' : 'stable',
|
|
302
|
+
},
|
|
303
|
+
patterns_per_cycle: {
|
|
304
|
+
label: 'Patrones aplicados por ciclo',
|
|
305
|
+
description: 'Cuántos patrones de memoria usa el agente en cada ciclo',
|
|
306
|
+
first: first.patterns_per_cycle,
|
|
307
|
+
last: last.patterns_per_cycle,
|
|
308
|
+
delta: delta(first.patterns_per_cycle, last.patterns_per_cycle),
|
|
309
|
+
trend: last.patterns_per_cycle >= first.patterns_per_cycle ? 'improving' : 'stable',
|
|
310
|
+
raw: { first: first.patterns_per_cycle, last: last.patterns_per_cycle },
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
|
|
314
|
+
summary: {
|
|
315
|
+
improving: [],
|
|
316
|
+
stable: [],
|
|
317
|
+
needs_work:[],
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// Clasificar métricas en el summary
|
|
322
|
+
Object.entries(report.metrics).forEach(([key, m]) => {
|
|
323
|
+
if (!m.trend) return;
|
|
324
|
+
if (['improving','good','growing'].includes(m.trend)) report.summary.improving.push(m.label);
|
|
325
|
+
else if (['degrading','needs_work'].includes(m.trend)) report.summary.needs_work.push(m.label);
|
|
326
|
+
else report.summary.stable.push(m.label);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
db.close();
|
|
330
|
+
return report;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// ─── PRINT REPORT ─────────────────────────────────────────────────────────────
|
|
334
|
+
|
|
335
|
+
function printReport(report) {
|
|
336
|
+
if (report.error) {
|
|
337
|
+
console.log(`\n[REPORT] ${report.error}\n`);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const arrow = (trend) => trend === 'improving' || trend === 'good' || trend === 'growing' ? '✅' :
|
|
342
|
+
trend === 'degrading' || trend === 'needs_work' ? '⚠️' : '→';
|
|
343
|
+
|
|
344
|
+
console.log('\n' + '═'.repeat(60));
|
|
345
|
+
console.log(' Agentic KDD — Effectiveness Report');
|
|
346
|
+
console.log(` ${report.project} · ${report.total_cycles} ciclos totales`);
|
|
347
|
+
console.log('═'.repeat(60));
|
|
348
|
+
console.log(`\n Comparando: ${report.window.first_half.label} vs ${report.window.second_half.label}\n`);
|
|
349
|
+
|
|
350
|
+
const m = report.metrics;
|
|
351
|
+
|
|
352
|
+
console.log(' ① Stop rate (TDD gate paró el ciclo)');
|
|
353
|
+
console.log(` Antes: ${m.stop_rate.first} → Ahora: ${m.stop_rate.last} ${arrow(m.stop_rate.trend)}`);
|
|
354
|
+
if (m.stop_rate.delta.pct > 0) console.log(` ${m.stop_rate.delta.dir} ${m.stop_rate.delta.pct}% ${m.stop_rate.trend}`);
|
|
355
|
+
|
|
356
|
+
console.log('\n ② Errores por ciclo');
|
|
357
|
+
console.log(` Antes: ${m.errors_per_cycle.first} → Ahora: ${m.errors_per_cycle.last} ${arrow(m.errors_per_cycle.trend)}`);
|
|
358
|
+
|
|
359
|
+
console.log('\n ③ Tests pasan en primer intento');
|
|
360
|
+
console.log(` Antes: ${m.first_try_rate.first} → Ahora: ${m.first_try_rate.last} ${arrow(m.first_try_rate.trend)}`);
|
|
361
|
+
|
|
362
|
+
console.log('\n ④ Recurrencia de errores');
|
|
363
|
+
console.log(` ${m.error_recurrence.recurred}/${m.error_recurrence.total} errores volvieron a ocurrir = ${m.error_recurrence.value} ${arrow(m.error_recurrence.trend)}`);
|
|
364
|
+
|
|
365
|
+
console.log('\n ⑤ Memoria acumulada');
|
|
366
|
+
console.log(` Inicio: ${m.memory_growth.at_start} nodos → Ahora: ${m.memory_growth.now} nodos (+${m.memory_growth.delta})`);
|
|
367
|
+
console.log(` ${m.memory_growth.per_cycle} nodos nuevos por ciclo promedio`);
|
|
368
|
+
|
|
369
|
+
console.log('\n ⑥ Patrones de memoria aplicados por ciclo');
|
|
370
|
+
console.log(` Antes: ${m.patterns_per_cycle.first} → Ahora: ${m.patterns_per_cycle.last} ${arrow(m.patterns_per_cycle.trend)}`);
|
|
371
|
+
|
|
372
|
+
console.log('\n ⑦ Patrones con confianza HIGH');
|
|
373
|
+
console.log(` ${m.pattern_velocity.total_high} patrones promovidos a HIGH`);
|
|
374
|
+
if (m.pattern_velocity.avg_to_high) console.log(` Promedio de ${m.pattern_velocity.avg_to_high} aplicaciones para llegar a HIGH`);
|
|
375
|
+
|
|
376
|
+
if (report.summary.improving.length > 0) {
|
|
377
|
+
console.log(`\n ✅ Mejorando: ${report.summary.improving.join(' · ')}`);
|
|
378
|
+
}
|
|
379
|
+
if (report.summary.needs_work.length > 0) {
|
|
380
|
+
console.log(` ⚠️ Atención: ${report.summary.needs_work.join(' · ')}`);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
console.log('\n' + '═'.repeat(60));
|
|
384
|
+
console.log(' Generado:', new Date(report.generated).toLocaleString('es-ES'));
|
|
385
|
+
console.log('═'.repeat(60) + '\n');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// ─── CLI ──────────────────────────────────────────────────────────────────────
|
|
389
|
+
|
|
390
|
+
if (require.main === module) {
|
|
391
|
+
const args = process.argv.slice(2);
|
|
392
|
+
const projectRoot = process.cwd();
|
|
393
|
+
const report = generateReport(projectRoot);
|
|
394
|
+
|
|
395
|
+
if (args.includes('--json')) {
|
|
396
|
+
console.log(JSON.stringify(report, null, 2));
|
|
397
|
+
} else {
|
|
398
|
+
printReport(report);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
module.exports = { generateReport, printReport };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-kdd",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.3",
|
|
4
4
|
"description": "Autonomous development pipeline — aa: · ag: · audit: · AST graph · Harness · Specs · Impact analysis · Decision trail · Metrics · MCP server. Works with Cursor and Claude Code.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|