agentic-kdd 2.0.8 → 2.1.2

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,7 +1,7 @@
1
1
  {
2
2
  "name": "agentic-kdd",
3
- "version": "2.0.8",
4
- "description": "Autonomous development pipeline with KDD \u2014 aa: \u00b7 ag: \u00b7 audit: \u00b7 Visual Dashboard. Works with Cursor and Claude Code.",
3
+ "version": "2.1.2",
4
+ "description": "Autonomous development pipeline with KDD aa: · ag: · audit: · Visual Dashboard. Works with Cursor and Claude Code.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
7
7
  "akdd": "bin/akdd.js"
@@ -39,6 +39,9 @@
39
39
  "chalk": "^4.1.2",
40
40
  "ora": "^5.4.1",
41
41
  "inquirer": "^8.2.6",
42
+ "sql.js": "^1.10.3"
43
+ },
44
+ "optionalDependencies": {
42
45
  "better-sqlite3": "^9.4.3"
43
46
  }
44
- }
47
+ }
@@ -69,6 +69,148 @@ function readConfig() {
69
69
 
70
70
  function readMemoria(file) { try { const p = path.join(memoriaPath, file); return fs.existsSync(p) ? fs.readFileSync(p, 'utf8') : ''; } catch { return ''; } }
71
71
 
72
+ function readSpecs() {
73
+ const specsPath = path.join(projectPath, '.agentic', 'specs');
74
+ if (!fs.existsSync(specsPath)) return [];
75
+ try {
76
+ return fs.readdirSync(specsPath).filter(f => f.endsWith('.md')).map(f => {
77
+ const c = fs.readFileSync(path.join(specsPath, f), 'utf8');
78
+ const estado = (c.match(/Estado: (.+)/) || [])[1]?.trim() || 'DESCONOCIDO';
79
+ const fecha = (c.match(/ltima actualización: (.+)/) || [])[1]?.trim() || '—';
80
+ const tests = (c.match(/PASS/g) || []).length;
81
+ return { name: f.replace('.md',''), estado, fecha, tests };
82
+ });
83
+ } catch { return []; }
84
+ }
85
+
86
+ function readLogs() {
87
+ const outputPath = path.join(projectPath, '_output');
88
+ if (!fs.existsSync(outputPath)) return [];
89
+ const logs = [];
90
+ try {
91
+ fs.readdirSync(outputPath).filter(f => f.startsWith('log-') && f.endsWith('.md')).forEach(f => {
92
+ const c = fs.readFileSync(path.join(outputPath, f), 'utf8');
93
+ c.split(/^## /m).filter(s => s.trim() && s.includes('Resultado:')).slice(0,20).forEach(entry => {
94
+ const lines = entry.split('\n');
95
+ const get = (k) => { for (const l of lines) { if (l.startsWith(k+':')) return l.split(':').slice(1).join(':').trim(); } return ''; };
96
+ logs.push({ header: lines[0].trim().slice(0,55), modulo: get('Módulo'), resultado: get('Resultado'), tests: get('Tests'), patrones: get('Patrones KDD aplicados'), errores: get('Errores evitados'), sync: get('Sync grafo') });
97
+ });
98
+ });
99
+ } catch {}
100
+ return logs.slice(0,15);
101
+ }
102
+
103
+ function calcMetrics(logs) {
104
+ if (ciclosDB && ciclosDB.length > 0) {
105
+ const total = ciclosDB.length;
106
+ const completados = ciclosDB.filter(c => c.estado === 'COMPLETADO').length;
107
+ const stops = ciclosDB.filter(c => c.estado === 'STOP').length;
108
+ const goal_attainment = Math.round(completados/total*100);
109
+ const autonomy_ratio = Math.round((total-stops)/total*100);
110
+ const totalFases = ciclosDB.reduce((s,c) => s+(c.fases_total||0), 0);
111
+ const fasesOK = ciclosDB.reduce((s,c) => s+(c.fases_completadas||0), 0);
112
+ const handoff = totalFases>0 ? Math.round(fasesOK/totalFases*100) : 0;
113
+ let patronesTotal=0, erroresTotal=0;
114
+ ciclosDB.forEach(c => {
115
+ try { patronesTotal += JSON.parse(c.patrones_aplicados||'[]').length; } catch(e) {}
116
+ try { erroresTotal += JSON.parse(c.errores_evitados||'[]').length; } catch(e) {}
117
+ });
118
+ const testsGen = ciclosDB.reduce((s,c) => s+(c.tests_generados||0), 0);
119
+ const testsOK = ciclosDB.reduce((s,c) => s+(c.tests_pasando||0), 0);
120
+ const test_rate = testsGen>0 ? Math.round(testsOK/testsGen*100) : 0;
121
+ const totalBlockers = ciclosDB.reduce((s,c) => s+(c.review_blockers||0), 0);
122
+ const drift_index = (totalBlockers/total).toFixed(1);
123
+ const guardrails = ciclosDB.filter(c => c.context_guard === 'STOP').length;
124
+
125
+ // Métrica 4: Tiempo promedio por ciclo (de los que tienen duracion)
126
+ const conDur = ciclosDB.filter(c => c.duracion_ms > 0);
127
+ const avg_duracion_ms = conDur.length>0
128
+ ? Math.round(conDur.reduce((s,c)=>s+c.duracion_ms,0)/conDur.length) : 0;
129
+
130
+ // Métrica 5: Éxito por tipo de tarea
131
+ const tipoMap = {};
132
+ ciclosDB.forEach(c => {
133
+ const t = c.tipo_tarea || 'feature';
134
+ if (!tipoMap[t]) tipoMap[t] = { total:0, ok:0 };
135
+ tipoMap[t].total++;
136
+ if (c.estado==='COMPLETADO') tipoMap[t].ok++;
137
+ });
138
+ const exito_por_tipo = Object.entries(tipoMap).map(([tipo,v]) => ({
139
+ tipo, total:v.total, ok:v.ok, rate: Math.round(v.ok/v.total*100)
140
+ }));
141
+
142
+ // Métrica 6: Evolución de memoria (snapshots antes/después)
143
+ let evolucion_memoria = null;
144
+ const conSnap = ciclosDB.filter(c => c.snapshot_fin);
145
+ if (conSnap.length >= 2) {
146
+ try {
147
+ const primero = JSON.parse(conSnap[conSnap.length-1].snapshot_fin);
148
+ const ultimo = JSON.parse(conSnap[0].snapshot_fin);
149
+ evolucion_memoria = {
150
+ nodos_inicio: primero.totales?.total || 0,
151
+ nodos_ahora: ultimo.totales?.total || 0,
152
+ alta_inicio: primero.totales?.alta || 0,
153
+ alta_ahora: ultimo.totales?.alta || 0,
154
+ crecimiento: (ultimo.totales?.total||0) - (primero.totales?.total||0),
155
+ };
156
+ } catch(e) {}
157
+ }
158
+
159
+ // Reintentos desde fases
160
+ let reintento_rate = 0, avg_fase_ms = 0;
161
+ if (fasesDB && fasesDB.length > 0) {
162
+ const conReintentos = fasesDB.filter(f => f.intentos > 1);
163
+ reintento_rate = Math.round(conReintentos.length/fasesDB.length*100);
164
+ const conFaseDur = fasesDB.filter(f => f.duracion_ms > 0);
165
+ avg_fase_ms = conFaseDur.length>0
166
+ ? Math.round(conFaseDur.reduce((s,f)=>s+f.duracion_ms,0)/conFaseDur.length) : 0;
167
+ }
168
+
169
+ return {
170
+ total, completados, stops,
171
+ goal_attainment, autonomy_ratio,
172
+ handoff_integrity: handoff,
173
+ drift_index, guardrail_violations: guardrails,
174
+ patronesTotal, erroresTotal,
175
+ test_rate, testsGen, testsOK,
176
+ avg_duracion_ms, avg_fase_ms,
177
+ reintento_rate, exito_por_tipo,
178
+ evolucion_memoria,
179
+ source: 'sqlite'
180
+ };
181
+ }
182
+ // Fallback a logs de archivos
183
+ const completados = logs.filter(l => l.resultado&&l.resultado.includes('COMPLETADO')).length;
184
+ const stops = logs.filter(l => l.resultado&&l.resultado.includes('STOP')).length;
185
+ let patronesTotal=0, erroresTotal=0;
186
+ logs.forEach(l => {
187
+ const pm=(l.patrones||'').match(/^(\d+)/); if(pm) patronesTotal+=parseInt(pm[1]);
188
+ const em=(l.errores||'').match(/^(\d+)/); if(em) erroresTotal+=parseInt(em[1]);
189
+ });
190
+ return {
191
+ total:logs.length, completados, stops, patronesTotal, erroresTotal,
192
+ goal_attainment: logs.length>0?Math.round(completados/logs.length*100):0,
193
+ autonomy_ratio:0, handoff_integrity:0, drift_index:'0',
194
+ guardrail_violations:0, test_rate:0, testsGen:0, testsOK:0,
195
+ avg_duracion_ms:0, avg_fase_ms:0, reintento_rate:0,
196
+ exito_por_tipo:[], evolucion_memoria:null, source:'logs'
197
+ };
198
+ }
199
+
200
+ function calcOnboarding(config, mImpl, dec, pat, specsArr) {
201
+ const checks = [
202
+ { label: 'config.md configurado', ok: config.nombre !== '—' && config.tipo !== '—' },
203
+ { label: 'Primer sync del grafo', ok: fs.existsSync(dbPath) },
204
+ { label: 'Módulos documentados', ok: mImpl.length > 0 },
205
+ { label: 'Primera decisión registrada', ok: dec.length > 0 },
206
+ { label: 'Primer patrón registrado', ok: pat.length > 0 },
207
+ { label: 'Primer ciclo aa: completado', ok: readMemoria('trabajo.md').includes('COMPLETADO') },
208
+ { label: 'Specs generadas', ok: specsArr.length > 0 },
209
+ ];
210
+ const done = checks.filter(c => c.ok).length;
211
+ return { checks, done, total: checks.length, pct: Math.round(done/checks.length*100) };
212
+ }
213
+
72
214
  function parseEntries(content) {
73
215
  return content.split(/^## /m)
74
216
  .filter(s => s.trim() && !s.startsWith('<!--') && !s.startsWith('Cómo') && !s.startsWith('Formato') && !s.startsWith('Registro') && !s.startsWith('Patrones') && s.length > 10)
@@ -88,17 +230,51 @@ const errores = parseEntries(readMemoria('errores.md'));
88
230
 
89
231
  function getGraphData() {
90
232
  try {
91
- if (!fs.existsSync(dbPath)) return { nodes: [], edges: [] };
92
- const Database = require('better-sqlite3');
93
- const db = new Database(dbPath, { readonly: true });
94
- const nodes = db.prepare('SELECT * FROM nodos ORDER BY fecha_creacion DESC').all();
95
- const edges = db.prepare('SELECT * FROM relaciones').all();
96
- db.close();
97
- return { nodes, edges };
98
- } catch { return { nodes: [], edges: [] }; }
233
+ if (!fs.existsSync(dbPath)) return { nodes: [], edges: [], ciclos: [], fases: [] };
234
+ let db = null, usingSqlJs = false;
235
+ // Intentar better-sqlite3 primero, fallback a sql.js
236
+ try {
237
+ const BS3 = require('better-sqlite3');
238
+ db = {
239
+ all: (sql, ...p) => BS3(dbPath, {readonly:true}).prepare(sql).all(...p),
240
+ close: () => {}
241
+ };
242
+ const _db = new BS3(dbPath, { readonly: true });
243
+ const nodes = _db.prepare('SELECT * FROM nodos ORDER BY fecha_creacion DESC').all();
244
+ const edges = _db.prepare('SELECT * FROM relaciones').all();
245
+ let ciclos = [], fases = [];
246
+ try { ciclos = _db.prepare('SELECT * FROM ciclos ORDER BY fecha_inicio DESC LIMIT 30').all(); } catch(e) {}
247
+ try { fases = _db.prepare('SELECT * FROM fases ORDER BY fecha_inicio DESC LIMIT 100').all(); } catch(e) {}
248
+ _db.close();
249
+ return { nodes, edges, ciclos, fases };
250
+ } catch(e) {
251
+ // Fallback sql.js
252
+ try {
253
+ const SQL = require('sql.js/dist/sql-wasm.js');
254
+ const buffer = fs.readFileSync(dbPath);
255
+ const _db = new SQL.Database(buffer);
256
+ const allSQL = (sql) => {
257
+ try {
258
+ const stmt = _db.prepare(sql);
259
+ const rows = [];
260
+ while(stmt.step()) rows.push(stmt.getAsObject());
261
+ stmt.free();
262
+ return rows;
263
+ } catch(e) { return []; }
264
+ };
265
+ const nodes = allSQL('SELECT * FROM nodos ORDER BY fecha_creacion DESC');
266
+ const edges = allSQL('SELECT * FROM relaciones');
267
+ const ciclos = allSQL('SELECT * FROM ciclos ORDER BY fecha_inicio DESC LIMIT 30');
268
+ const fases = allSQL('SELECT * FROM fases ORDER BY fecha_inicio DESC LIMIT 100');
269
+ return { nodes, edges, ciclos, fases };
270
+ } catch(e2) {
271
+ return { nodes: [], edges: [], ciclos: [], fases: [] };
272
+ }
273
+ }
274
+ } catch { return { nodes: [], edges: [], ciclos: [], fases: [] }; }
99
275
  }
100
276
 
101
- const { nodes, edges } = getGraphData();
277
+ const { nodes, edges, ciclos: ciclosDB, fases: fasesDB } = getGraphData();
102
278
 
103
279
  // Calcular grado de conexiones por nodo (como Graphify — nodos divinos)
104
280
  const degreeMap = {};
@@ -149,6 +325,10 @@ function parseModulos(text) {
149
325
  }
150
326
 
151
327
  const modulosImpl = parseModulos(config.implementados);
328
+ const specsData = readSpecs();
329
+ const logsData = readLogs();
330
+ const metricsData = calcMetrics(logsData);
331
+ const onboardingData = calcOnboarding(config, modulosImpl, decisiones, patrones, specsData);
152
332
  const modulosPend = parseModulos(config.pendientes);
153
333
  const reglas = (config.reglas || '').split('\n').map(l => l.trim()).filter(l => l && l !== '—' && l.length > 5);
154
334
 
@@ -619,6 +799,9 @@ body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSy
619
799
  <div class="nav-item" onclick="showDoc('decisions',this)">🔵 <span data-i="nav_decisions">Decisions</span> <span class="nav-count">${decisiones.length}</span></div>
620
800
  <div class="nav-item" onclick="showDoc('errors',this)">🔴 <span data-i="nav_errors">Errors</span> <span class="nav-count">${errores.length}</span></div>
621
801
  <div class="nav-item" onclick="showDoc('questions',this)">💡 <span data-i="nav_questions">For New Devs</span></div>
802
+ <div class="nav-item" onclick="showDoc('metrics',this)">📊 <span>Metrics</span></div>
803
+ <div class="nav-item" onclick="showDoc('timeline',this)">🕐 <span>Timeline</span></div>
804
+ <div class="nav-item" onclick="showDoc('onboarding',this)">🚀 <span>Onboarding</span> <span class="nav-count">${onboardingData.pct}%</span></div>
622
805
  </div>
623
806
  </nav>
624
807
 
@@ -793,6 +976,195 @@ body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSy
793
976
  </div>` : ''}
794
977
  </div>
795
978
 
979
+ <!-- METRICS -->
980
+ <div class="docs-section" id="doc-metrics">
981
+ <div class="docs-h1">📊 Metrics</div>
982
+ <div class="docs-sub">Real observability — every aa: cycle tracked. Data from SQLite, not estimates.</div>
983
+ ${metricsData.total === 0 ? `<div class="empty-state" style="padding:48px;text-align:center">
984
+ <div style="font-size:36px;margin-bottom:12px">📊</div>
985
+ <div style="font-size:14px;color:var(--text2);margin-bottom:6px">No cycles recorded yet</div>
986
+ <div style="font-size:12px;color:var(--text3)">Run <code style="background:var(--bg3);padding:2px 6px;border-radius:3px">aa: [task]</code> to start — metrics appear automatically after each cycle</div>
987
+ </div>` : `
988
+ <!-- Fila 1: 4 KPIs principales -->
989
+ <div style="display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:12px">
990
+ <div style="background:linear-gradient(135deg,rgba(139,92,246,.08),rgba(16,185,129,.04));border:1px solid rgba(139,92,246,.2);border-radius:10px;padding:14px;text-align:center">
991
+ <div style="font-size:28px;font-weight:700;color:${metricsData.goal_attainment>=80?'#34d399':metricsData.goal_attainment>=60?'#fbbf24':'#f87171'}">${metricsData.goal_attainment}%</div>
992
+ <div style="font-size:10px;font-weight:600;color:var(--text2);margin-top:3px">Goal Attainment</div>
993
+ <div style="font-size:9px;color:${metricsData.goal_attainment>=80?'#34d399':'var(--text3)'}">target >80%</div>
994
+ </div>
995
+ <div style="background:rgba(6,182,212,.05);border:1px solid rgba(6,182,212,.2);border-radius:10px;padding:14px;text-align:center">
996
+ <div style="font-size:28px;font-weight:700;color:var(--cyan)">${metricsData.autonomy_ratio||0}%</div>
997
+ <div style="font-size:10px;font-weight:600;color:var(--text2);margin-top:3px">Autonomy Ratio</div>
998
+ <div style="font-size:9px;color:var(--text3)">cycles without STOP</div>
999
+ </div>
1000
+ <div style="background:rgba(16,185,129,.04);border:1px solid rgba(16,185,129,.2);border-radius:10px;padding:14px;text-align:center">
1001
+ <div style="font-size:28px;font-weight:700;color:${(metricsData.handoff_integrity||0)>=90?'#34d399':'#fbbf24'}">${metricsData.handoff_integrity||0}%</div>
1002
+ <div style="font-size:10px;font-weight:600;color:var(--text2);margin-top:3px">Handoff Integrity</div>
1003
+ <div style="font-size:9px;color:${(metricsData.handoff_integrity||0)>=90?'#34d399':'var(--text3)'}">target >90%</div>
1004
+ </div>
1005
+ <div style="background:rgba(239,68,68,.04);border:1px solid rgba(239,68,68,.15);border-radius:10px;padding:14px;text-align:center">
1006
+ <div style="font-size:28px;font-weight:700;color:${parseFloat(metricsData.drift_index||0)<=0.5?'#34d399':'#f87171'}">${metricsData.drift_index||'0'}</div>
1007
+ <div style="font-size:10px;font-weight:600;color:var(--text2);margin-top:3px">Drift Index</div>
1008
+ <div style="font-size:9px;color:${parseFloat(metricsData.drift_index||0)<=0.5?'#34d399':'var(--text3)'}">blockers/cycle (0=ideal)</div>
1009
+ </div>
1010
+ </div>
1011
+
1012
+ <!-- Fila 2: 6 stats secundarios -->
1013
+ <div style="display:grid;grid-template-columns:repeat(6,1fr);gap:8px;margin-bottom:12px">
1014
+ <div class="info-card"><div class="ic-label">Cycles</div><div class="ic-val" style="color:var(--pl)">${metricsData.total}</div></div>
1015
+ <div class="info-card"><div class="ic-label">Completed</div><div class="ic-val" style="color:var(--green)">${metricsData.completados}</div></div>
1016
+ <div class="info-card"><div class="ic-label">STOPs</div><div class="ic-val" style="color:var(--red)">${metricsData.stops}</div></div>
1017
+ <div class="info-card"><div class="ic-label">Patterns used</div><div class="ic-val" style="color:var(--amber)">${metricsData.patronesTotal}</div></div>
1018
+ <div class="info-card"><div class="ic-label">Errors avoided</div><div class="ic-val" style="color:var(--cyan)">${metricsData.erroresTotal}</div></div>
1019
+ <div class="info-card"><div class="ic-label">Test pass rate</div><div class="ic-val" style="color:var(--green)">${metricsData.test_rate||0}%</div></div>
1020
+ </div>
1021
+
1022
+ <!-- Métrica extra: tiempo por ciclo y reintentos -->
1023
+ ${metricsData.avg_duracion_ms > 0 || metricsData.reintento_rate > 0 ? `
1024
+ <div style="display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:12px">
1025
+ ${metricsData.avg_duracion_ms>0?`<div class="info-card"><div class="ic-label">Avg cycle time</div><div class="ic-val" style="color:var(--text2);font-size:13px">${metricsData.avg_duracion_ms>60000?Math.round(metricsData.avg_duracion_ms/60000)+'m':metricsData.avg_duracion_ms+'ms'}</div></div>`:''}
1026
+ ${metricsData.avg_fase_ms>0?`<div class="info-card"><div class="ic-label">Avg phase time</div><div class="ic-val" style="color:var(--text2);font-size:13px">${metricsData.avg_fase_ms>60000?Math.round(metricsData.avg_fase_ms/60000)+'m':metricsData.avg_fase_ms+'ms'}</div></div>`:''}
1027
+ ${metricsData.reintento_rate>0?`<div class="info-card"><div class="ic-label">Retry rate</div><div class="ic-val" style="color:${metricsData.reintento_rate>30?'#f87171':'#fbbf24'};font-size:16px">${metricsData.reintento_rate}%</div></div>`:''}
1028
+ </div>` : ''}
1029
+
1030
+ <!-- Guardrail violations -->
1031
+ ${metricsData.guardrail_violations > 0
1032
+ ? `<div style="background:rgba(239,68,68,.06);border:1px solid rgba(239,68,68,.2);border-radius:8px;padding:10px 14px;margin-bottom:12px;font-size:11px;color:#f87171">⚠️ Guardrail violations: ${metricsData.guardrail_violations} — instructions outside project scope</div>`
1033
+ : `<div style="background:rgba(16,185,129,.04);border:1px solid rgba(16,185,129,.15);border-radius:8px;padding:10px 14px;margin-bottom:12px;font-size:11px;color:#34d399">✓ Guardrail violations: 0 — all instructions within project scope</div>`}
1034
+
1035
+ <!-- Éxito por tipo de tarea -->
1036
+ ${metricsData.exito_por_tipo && metricsData.exito_por_tipo.length > 1 ? `
1037
+ <div class="docs-h2">Success rate by task type</div>
1038
+ <div style="display:grid;grid-template-columns:repeat(${Math.min(metricsData.exito_por_tipo.length,4)},1fr);gap:8px;margin-bottom:16px">
1039
+ ${metricsData.exito_por_tipo.map(t => `
1040
+ <div style="background:var(--bg2);border:1px solid var(--border);border-radius:8px;padding:10px;text-align:center">
1041
+ <div style="font-size:18px;font-weight:700;color:${t.rate>=80?'#34d399':t.rate>=60?'#fbbf24':'#f87171'}">${t.rate}%</div>
1042
+ <div style="font-size:10px;color:var(--text2);margin-top:2px">${t.tipo}</div>
1043
+ <div style="font-size:9px;color:var(--text3)">${t.ok}/${t.total}</div>
1044
+ </div>`).join('')}
1045
+ </div>` : ''}
1046
+
1047
+ <!-- Evolución de memoria -->
1048
+ ${metricsData.evolucion_memoria ? `
1049
+ <div class="docs-h2">Memory evolution</div>
1050
+ <div style="background:var(--bg2);border:1px solid var(--border);border-radius:10px;padding:14px;margin-bottom:16px">
1051
+ <div style="display:grid;grid-template-columns:repeat(4,1fr);gap:10px;text-align:center">
1052
+ <div><div style="font-size:18px;font-weight:600;color:var(--pl)">${metricsData.evolucion_memoria.nodos_inicio}</div><div style="font-size:10px;color:var(--text3)">nodes at start</div></div>
1053
+ <div><div style="font-size:18px;font-weight:600;color:var(--pl)">${metricsData.evolucion_memoria.nodos_ahora}</div><div style="font-size:10px;color:var(--text3)">nodes now</div></div>
1054
+ <div><div style="font-size:18px;font-weight:600;color:${metricsData.evolucion_memoria.crecimiento>0?'#34d399':'#94a3b8'}">+${metricsData.evolucion_memoria.crecimiento}</div><div style="font-size:10px;color:var(--text3)">growth</div></div>
1055
+ <div><div style="font-size:18px;font-weight:600;color:var(--amber)">${metricsData.evolucion_memoria.alta_ahora}</div><div style="font-size:10px;color:var(--text3)">HIGH rules now</div></div>
1056
+ </div>
1057
+ </div>` : ''}
1058
+
1059
+ <!-- Ciclos recientes -->
1060
+ <div class="docs-h2">Recent cycles</div>
1061
+ ${(ciclosDB&&ciclosDB.length>0?ciclosDB:logsData).map(l => {
1062
+ const esDB = !!l.ciclo_id;
1063
+ const tarea = (esDB ? l.tarea : l.header)||'';
1064
+ const modulo = l.modulo;
1065
+ const ok = esDB ? l.estado==='COMPLETADO' : (l.resultado&&l.resultado.includes('COMPLETADO'));
1066
+ const fases = esDB && l.fases_total>0 ? l.fases_completadas+'/'+l.fases_total+' phases' : '';
1067
+ const tests = esDB ? (l.tests_pasando||0)+'/'+(l.tests_generados||0)+' tests' : (l.tests||'');
1068
+ const tipo = esDB && l.tipo_tarea ? l.tipo_tarea : '';
1069
+ let pats=0; if(esDB){try{pats=JSON.parse(l.patrones_aplicados||'[]').length;}catch(e){}}
1070
+ return `<div style="background:var(--bg2);border:1px solid var(--border);border-left:3px solid ${ok?'var(--green)':'var(--red)'};border-radius:8px;padding:10px 14px;margin-bottom:6px">
1071
+ <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:5px">
1072
+ <div style="font-size:12px;font-weight:500;color:var(--text);flex:1;margin-right:8px">${tarea.slice(0,65)}</div>
1073
+ <div style="display:flex;gap:6px;align-items:center;flex-shrink:0">
1074
+ ${tipo?`<span style="font-size:9px;background:rgba(139,92,246,.15);color:#a78bfa;border-radius:3px;padding:1px 5px">${tipo}</span>`:''}
1075
+ <span style="font-size:10px;padding:2px 7px;border-radius:4px;background:${ok?'rgba(16,185,129,.15)':'rgba(239,68,68,.15)'};color:${ok?'#34d399':'#f87171'}">${ok?'done':'stop'}</span>
1076
+ </div>
1077
+ </div>
1078
+ <div style="display:flex;gap:10px;font-size:10px;color:var(--text3);flex-wrap:wrap">
1079
+ ${modulo&&modulo!=='global'?`<span>📦 ${modulo}</span>`:''}
1080
+ ${fases?`<span>${fases}</span>`:''}
1081
+ ${tests&&tests!=='0/0'?`<span style="color:#34d399">🧪 ${tests}</span>`:''}
1082
+ ${pats>0?`<span style="color:var(--amber)">★ ${pats} patterns</span>`:''}
1083
+ <span style="margin-left:auto">${(l.fecha_inicio||'').slice(0,16)}</span>
1084
+ </div>
1085
+ </div>`;
1086
+ }).join('')}
1087
+ `}
1088
+ </div>
1089
+
1090
+ <!-- TIMELINE -->
1091
+ <div class="docs-section" id="doc-timeline">
1092
+ <div class="docs-h1">🕐 Decision Timeline</div>
1093
+ <div class="docs-sub">Every architectural decision, when it was made, why, and which modules it affects. The project's living memory.</div>
1094
+ ${decisiones.length === 0 ? '<div class="empty-state" style="padding:40px">No decisions recorded yet — the system logs them automatically as you build</div>' : `
1095
+ <div style="position:relative;padding-left:24px">
1096
+ <div style="position:absolute;left:8px;top:0;bottom:0;width:2px;background:var(--border)"></div>
1097
+ ${decisiones.map((d,i) => `<div style="position:relative;margin-bottom:16px">
1098
+ <div style="position:absolute;left:-20px;top:6px;width:10px;height:10px;border-radius:50%;background:var(--blue);border:2px solid var(--bg)"></div>
1099
+ <div style="background:var(--bg2);border:1px solid var(--border);border-left:3px solid var(--blue);border-radius:8px;padding:12px 14px">
1100
+ <div style="font-size:12px;font-weight:600;color:var(--text);margin-bottom:4px">${d.titulo}</div>
1101
+ <div style="display:flex;gap:8px;margin-bottom:6px">
1102
+ <span style="font-size:10px;background:rgba(59,130,246,.15);color:#60a5fa;border-radius:3px;padding:1px 6px">${d.area}</span>
1103
+ <span style="font-size:10px;color:var(--text3)">${d.confianza}</span>
1104
+ </div>
1105
+ ${d.contenido && d.contenido.split('\n').find(l=>l.startsWith('Razón:')) ? `<div style="font-size:11px;color:var(--text2);line-height:1.5">${d.contenido.split('\n').find(l=>l.startsWith('Razón:')).replace('Razón:','').trim()}</div>` : ''}
1106
+ </div>
1107
+ </div>`).join('')}
1108
+ </div>`}
1109
+ ${specsData.length > 0 ? `
1110
+ <div class="docs-h2" style="margin-top:24px">📋 Module Specs</div>
1111
+ <div style="font-size:12px;color:var(--text3);margin-bottom:12px">Auto-generated specs — updated after every aa: cycle</div>
1112
+ ${specsData.map(s => `<div style="background:var(--bg2);border:1px solid var(--border);border-left:3px solid ${s.estado.includes('IMPLEMENTADO')?'var(--green)':'var(--amber)'};border-radius:8px;padding:10px 14px;margin-bottom:6px;display:flex;align-items:center;gap:10px">
1113
+ <div style="font-size:13px;font-weight:500;color:var(--text);flex:1">${s.name}</div>
1114
+ <span style="font-size:10px;background:${s.estado.includes('IMPLEMENTADO')?'rgba(16,185,129,.15)':'rgba(245,158,11,.15)'};color:${s.estado.includes('IMPLEMENTADO')?'#34d399':'#fbbf24'};border-radius:4px;padding:2px 7px">${s.estado}</span>
1115
+ ${s.tests>0?`<span style="font-size:10px;color:var(--text3)">${s.tests} tests</span>`:''}
1116
+ <span style="font-size:10px;color:var(--text3)">${s.fecha}</span>
1117
+ </div>`).join('')}` : ''}
1118
+ </div>
1119
+
1120
+ <!-- ONBOARDING -->
1121
+ <div class="docs-section" id="doc-onboarding">
1122
+ <div class="docs-h1">🚀 Project Setup</div>
1123
+ <div class="docs-sub">How configured is this project with Agentic KDD. Complete all steps for the full system to work.</div>
1124
+
1125
+ <!-- Progress bar -->
1126
+ <div style="background:var(--bg2);border:1px solid var(--border);border-radius:12px;padding:20px;margin-bottom:20px">
1127
+ <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:12px">
1128
+ <div style="font-size:14px;font-weight:600;color:var(--text)">Setup progress</div>
1129
+ <div style="font-size:24px;font-weight:700;color:${onboardingData.pct===100?'var(--green)':onboardingData.pct>50?'var(--amber)':'var(--red)'}">${onboardingData.pct}%</div>
1130
+ </div>
1131
+ <div style="height:8px;background:var(--bg3);border-radius:4px;overflow:hidden;margin-bottom:16px">
1132
+ <div style="height:100%;width:${onboardingData.pct}%;background:${onboardingData.pct===100?'var(--green)':onboardingData.pct>50?'var(--amber)':'var(--purple)'};border-radius:4px;transition:width .5s"></div>
1133
+ </div>
1134
+ ${onboardingData.checks.map(c => `<div style="display:flex;align-items:center;gap:10px;padding:6px 0;border-bottom:1px solid rgba(255,255,255,.04)">
1135
+ <span style="font-size:16px">${c.ok?'✅':'⬜'}</span>
1136
+ <span style="font-size:12px;color:${c.ok?'var(--text)':'var(--text3)'}">${c.label}</span>
1137
+ ${!c.ok?'<span style="font-size:10px;color:var(--amber);margin-left:auto">pending</span>':'<span style="font-size:10px;color:var(--green);margin-left:auto">done</span>'}
1138
+ </div>`).join('')}
1139
+ </div>
1140
+
1141
+ ${onboardingData.pct < 100 ? `
1142
+ <div class="docs-h2">Next steps</div>
1143
+ ${onboardingData.checks.filter(c=>!c.ok).map(c => {
1144
+ const steps = {
1145
+ 'config.md configurado': 'Open in Cursor/Claude Code and run: aa: configurar',
1146
+ 'Primer sync del grafo': 'Run: node .agentic/grafo/grafo.cjs sync',
1147
+ 'Módulos documentados': 'Run: aa: configurar — describe your modules',
1148
+ 'Primera decisión registrada': 'Run any aa: task — decisions are logged automatically',
1149
+ 'Primer patrón registrado': 'Run any aa: task — patterns are detected automatically',
1150
+ 'Primer ciclo aa: completado': 'Run: aa: [any task]',
1151
+ 'Specs generadas': 'Complete a full module with aa: — specs auto-generate',
1152
+ };
1153
+ return `<div style="background:var(--bg2);border:1px solid rgba(245,158,11,.2);border-radius:8px;padding:12px 14px;margin-bottom:8px;display:flex;gap:10px;align-items:flex-start">
1154
+ <span style="font-size:18px;flex-shrink:0">⏳</span>
1155
+ <div>
1156
+ <div style="font-size:12px;font-weight:600;color:var(--amber);margin-bottom:4px">${c.label}</div>
1157
+ <div style="font-size:11px;color:var(--text2)">${steps[c.label]||'Follow the setup instructions'}</div>
1158
+ </div>
1159
+ </div>`;
1160
+ }).join('')}` : `
1161
+ <div style="background:rgba(16,185,129,.08);border:1px solid rgba(16,185,129,.25);border-radius:12px;padding:20px;text-align:center">
1162
+ <div style="font-size:32px;margin-bottom:8px">🎉</div>
1163
+ <div style="font-size:16px;font-weight:700;color:#34d399;margin-bottom:6px">Fully configured</div>
1164
+ <div style="font-size:12px;color:var(--text2)">This project has Agentic KDD fully set up. The system will keep improving automatically.</div>
1165
+ </div>`}
1166
+ </div>
1167
+
796
1168
  </div>
797
1169
  </div>
798
1170
  </div>
@@ -800,6 +1172,19 @@ body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSy
800
1172
  </div>
801
1173
 
802
1174
  <script>
1175
+ console.log('=== Agentic KDD Dashboard Debug ===');
1176
+ console.log('M_NODES:', ${JSON.stringify(mNodes)}.length, 'nodes');
1177
+ console.log('metricsData:', ${JSON.stringify(metricsData)});
1178
+ console.log('onboardingData pct:', ${JSON.stringify(onboardingData.pct)});
1179
+ console.log('specsData:', ${JSON.stringify(specsData)}.length, 'specs');
1180
+ console.log('logsData:', ${JSON.stringify(logsData)}.length, 'logs');
1181
+ document.addEventListener('DOMContentLoaded', function(){
1182
+ console.log('Nav items:', document.querySelectorAll('.nav-item').length);
1183
+ console.log('Doc sections:', document.querySelectorAll('.docs-section').length);
1184
+ console.log('doc-metrics exists:', !!document.getElementById('doc-metrics'));
1185
+ console.log('doc-timeline exists:', !!document.getElementById('doc-timeline'));
1186
+ console.log('doc-onboarding exists:', !!document.getElementById('doc-onboarding'));
1187
+ });
803
1188
  const NODES = ${JSON.stringify(nodes)};
804
1189
  const EDGES = ${JSON.stringify(edges)};
805
1190
  const M_NODES = ${JSON.stringify(mNodes)};
package/src/graph.js CHANGED
@@ -7,64 +7,55 @@ const { execSync } = require('child_process');
7
7
 
8
8
  async function graph() {
9
9
  const projectPath = process.cwd();
10
- const grafoPath = path.join(projectPath, '.agentic', 'grafo', 'grafo.js');
11
- const dbPath = path.join(projectPath, '.agentic', 'memoria.db');
12
- const memoriaPath = path.join(projectPath, '.agentic', 'memoria');
10
+ const grafoPath = path.join(projectPath, '.agentic', 'grafo', 'grafo.cjs');
11
+ const grafoOld = path.join(projectPath, '.agentic', 'grafo', 'grafo.js');
12
+ const dbPath = path.join(projectPath, '.agentic', 'memoria.db');
13
13
 
14
- // Verificar que Agentic está instalado
15
14
  if (!fs.existsSync(path.join(projectPath, '.agentic', 'config.md'))) {
16
15
  console.log(chalk.yellow('\n Agentic KDD no está instalado en este proyecto.'));
17
16
  console.log(chalk.gray(' Corre akdd init para instalarlo.\n'));
18
17
  return;
19
18
  }
20
19
 
21
- // Verificar que el grafo existe
22
- if (!fs.existsSync(grafoPath)) {
23
- console.log(chalk.yellow('\n El grafo no está disponible en este proyecto.'));
20
+ const grafo = fs.existsSync(grafoPath) ? grafoPath : fs.existsSync(grafoOld) ? grafoOld : null;
21
+ if (!grafo) {
22
+ console.log(chalk.yellow('\n El grafo no está disponible.'));
24
23
  console.log(chalk.gray(' Actualiza con: akdd update\n'));
25
24
  return;
26
25
  }
27
26
 
28
- console.log('\n' + chalk.bold.blue(' Agentic KDD') + chalk.gray(' — grafo de conocimiento\n'));
27
+ console.log('\n' + chalk.bold.hex('#8b5cf6')(' Agentic KDD') + chalk.gray(' — grafo de conocimiento\n'));
29
28
 
30
- // Sincronizar primero si hay archivos de memoria
31
- if (fs.existsSync(memoriaPath)) {
32
- try {
33
- process.stdout.write(chalk.gray(' Sincronizando memoria... '));
34
- execSync(`node "${grafoPath}" sync`, { stdio: 'pipe', cwd: projectPath });
35
- console.log(chalk.green('✓'));
36
- } catch (e) {
37
- console.log(chalk.yellow('⚠ No se pudo sincronizar'));
38
- }
29
+ // Sincronizar primero
30
+ try {
31
+ process.stdout.write(chalk.gray(' Sincronizando memoria... '));
32
+ execSync(`node "${grafo}" sync`, { stdio: 'pipe', cwd: projectPath });
33
+ console.log(chalk.green(''));
34
+ } catch (e) {
35
+ console.log(chalk.yellow('⚠ No se pudo sincronizar'));
39
36
  }
40
37
 
41
- // Mostrar stats
38
+ // Stats
42
39
  if (fs.existsSync(dbPath)) {
43
40
  try {
44
- const output = execSync(`node "${grafoPath}" stats`, {
45
- stdio: 'pipe',
46
- cwd: projectPath
47
- }).toString();
41
+ const output = execSync(`node "${grafo}" stats`, { stdio: 'pipe', cwd: projectPath }).toString();
48
42
  console.log(output);
49
43
  } catch (e) {
50
- console.log(chalk.red(' Error al leer el grafo: ' + e.message));
44
+ console.log(chalk.red(' Error: ' + e.message));
51
45
  }
52
46
  } else {
53
- console.log(chalk.gray(' Sin datos aún — el grafo se llena a medida que usas aa:\n'));
54
- console.log(chalk.gray(' Tip: después de cada tarea con aa:, el sistema actualiza el grafo solo.\n'));
47
+ console.log(chalk.gray(' Sin datos aún — usa aa: para empezar\n'));
55
48
  }
56
49
 
57
- // Mostrar config del proyecto
58
- const configPath = path.join(projectPath, '.agentic', 'config.md');
59
- if (fs.existsSync(configPath)) {
60
- const config = fs.readFileSync(configPath, 'utf8');
61
- const nombre = (config.match(/Nombre: (.+)/) || [])[1] || '—';
62
- const configurado = config.includes('CONFIGURADO: SI') ? chalk.green('✓ Configurado') : chalk.yellow('⚠ Sin configurar');
63
-
64
- console.log(chalk.bold(' Proyecto: ') + nombre);
65
- console.log(chalk.bold(' Estado: ') + configurado);
66
- console.log('');
67
- }
50
+ // Métricas si hay ciclos
51
+ try {
52
+ const m = JSON.parse(execSync(`node "${grafo}" metricas`, { stdio: 'pipe', cwd: projectPath }).toString());
53
+ if (m.total > 0) {
54
+ console.log(chalk.bold(' Métricas del agente:'));
55
+ console.log(` Goal Attainment: ${chalk.green(m.goal_attainment+'%')} | Autonomy: ${chalk.cyan(m.autonomy_ratio+'%')} | Handoff: ${chalk.green(m.handoff_integrity+'%')}`);
56
+ console.log(` Patrones aplicados: ${m.patrones_aplicados} | Errores evitados: ${m.errores_evitados}\n`);
57
+ }
58
+ } catch(e) {}
68
59
  }
69
60
 
70
61
  module.exports = { graph };