agentic-kdd 3.2.1 → 3.2.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/bin/akdd.js +12 -0
- package/package.json +1 -1
- package/session-guard.cjs +265 -0
- package/src/dashboard-template.cjs +292 -27
package/bin/akdd.js
CHANGED
|
@@ -66,6 +66,10 @@ const HELP = `
|
|
|
66
66
|
akdd creative apply <id> Apply a suggestion
|
|
67
67
|
akdd creative wins View applied creative improvements
|
|
68
68
|
|
|
69
|
+
Session continuity:
|
|
70
|
+
akdd historial Resume context in a new chat — paste output at start
|
|
71
|
+
akdd checkpoint Save checkpoint now (auto-runs every 5 cycles)
|
|
72
|
+
|
|
69
73
|
Memory Governance (v3.2):
|
|
70
74
|
akdd cure Run MemCurator — TTL, dedup, conflicts, scores
|
|
71
75
|
akdd cure report Preview what curation would do (no changes)
|
|
@@ -263,6 +267,14 @@ switch (command) {
|
|
|
263
267
|
break;
|
|
264
268
|
}
|
|
265
269
|
|
|
270
|
+
// ── v3.3: Session Guard ────────────────────────────────────────────────────
|
|
271
|
+
case 'historial':
|
|
272
|
+
runModule('session-guard.cjs', 'historial');
|
|
273
|
+
break;
|
|
274
|
+
case 'checkpoint':
|
|
275
|
+
runModule('session-guard.cjs', 'checkpoint');
|
|
276
|
+
break;
|
|
277
|
+
|
|
266
278
|
// ── v3.2: MemCurator ───────────────────────────────────────────────────────
|
|
267
279
|
case 'cure': {
|
|
268
280
|
const sub = arg1 || 'run';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-kdd",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.2",
|
|
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": {
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agentic KDD — Session Guard v1.0
|
|
3
|
+
*
|
|
4
|
+
* Problema que resuelve:
|
|
5
|
+
* - Cursor se cierra y pierdes el hilo
|
|
6
|
+
* - Cambias de PC y empiezas chat nuevo
|
|
7
|
+
* - No recuerdas en qué ibas exactamente
|
|
8
|
+
*
|
|
9
|
+
* Solución:
|
|
10
|
+
* - Cada 5 ciclos aa: guarda un checkpoint en .agentic/checkpoint.md
|
|
11
|
+
* - akdd historial muestra el checkpoint listo para pegar en el chat nuevo
|
|
12
|
+
* - El chat nuevo recupera el contexto exacto de donde se quedó
|
|
13
|
+
*
|
|
14
|
+
* Uso:
|
|
15
|
+
* node session-guard.cjs checkpoint — genera checkpoint ahora
|
|
16
|
+
* node session-guard.cjs historial — muestra el último checkpoint
|
|
17
|
+
* node session-guard.cjs status — cuántos ciclos hasta el próximo checkpoint
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
'use strict';
|
|
21
|
+
|
|
22
|
+
const path = require('path');
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
|
|
25
|
+
const CHECKPOINT_EVERY = 5;
|
|
26
|
+
const CHECKPOINT_PATH = '.agentic/checkpoint.md';
|
|
27
|
+
const COUNTER_KEY = 'session_guard_cycle_count';
|
|
28
|
+
|
|
29
|
+
// ─── DB ───────────────────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
function openDB(projectRoot) {
|
|
32
|
+
const dbPath = path.join(projectRoot, '.agentic/memoria.db');
|
|
33
|
+
try { return new (require('better-sqlite3'))(dbPath); } catch {}
|
|
34
|
+
try { const { DatabaseSync } = require('node:sqlite'); return new DatabaseSync(dbPath); } catch {}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ─── LEER CICLOS RECIENTES ────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
function getRecentCycles(db, limit = 5) {
|
|
41
|
+
try {
|
|
42
|
+
return db.prepare(`
|
|
43
|
+
SELECT ciclo_id, descripcion, tipo, estado, fecha_inicio, fecha_fin,
|
|
44
|
+
tests_corridos, patrones_aplicados, errores_encontrados, fases_completadas
|
|
45
|
+
FROM ciclos
|
|
46
|
+
ORDER BY fecha_inicio DESC
|
|
47
|
+
LIMIT ?
|
|
48
|
+
`).all(limit);
|
|
49
|
+
} catch { return []; }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getProjectStats(db) {
|
|
53
|
+
try {
|
|
54
|
+
const nodes = db.prepare("SELECT COUNT(*) as n FROM nodos WHERE estado='ACTIVO'").get()?.n || 0;
|
|
55
|
+
const high = db.prepare("SELECT COUNT(*) as n FROM nodos WHERE confianza='ALTA' AND estado='ACTIVO'").get()?.n || 0;
|
|
56
|
+
const cycles = db.prepare("SELECT COUNT(*) as n FROM ciclos").get()?.n || 0;
|
|
57
|
+
const stops = db.prepare("SELECT COUNT(*) as n FROM ciclos WHERE estado='STOP'").get()?.n || 0;
|
|
58
|
+
const errors = db.prepare("SELECT COUNT(*) as n FROM nodos WHERE tipo='error' AND estado='ACTIVO'").get()?.n || 0;
|
|
59
|
+
return { nodes, high, cycles, stops, errors };
|
|
60
|
+
} catch { return { nodes: 0, high: 0, cycles: 0, stops: 0, errors: 0 }; }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function getLastModifiedFiles(db, cicloId) {
|
|
64
|
+
try {
|
|
65
|
+
const trail = db.prepare(`
|
|
66
|
+
SELECT descripcion FROM decisiones
|
|
67
|
+
WHERE ciclo_id = ?
|
|
68
|
+
AND (descripcion LIKE '%.ts%' OR descripcion LIKE '%.js%'
|
|
69
|
+
OR descripcion LIKE '%.tsx%' OR descripcion LIKE '%.jsx%'
|
|
70
|
+
OR descripcion LIKE '%.py%')
|
|
71
|
+
LIMIT 5
|
|
72
|
+
`).all(cicloId);
|
|
73
|
+
return trail.map(t => t.descripcion?.substring(0, 60)).filter(Boolean);
|
|
74
|
+
} catch { return []; }
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ─── GENERAR CHECKPOINT ───────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
function generateCheckpoint(projectRoot) {
|
|
80
|
+
projectRoot = projectRoot || process.cwd();
|
|
81
|
+
const db = openDB(projectRoot);
|
|
82
|
+
if (!db) return null;
|
|
83
|
+
|
|
84
|
+
const cycles = getRecentCycles(db, 5);
|
|
85
|
+
const stats = getProjectStats(db);
|
|
86
|
+
const now = new Date();
|
|
87
|
+
const dateStr = now.toLocaleDateString('es-ES', { day:'2-digit', month:'2-digit', year:'numeric' });
|
|
88
|
+
const timeStr = now.toTimeString().substring(0, 5);
|
|
89
|
+
|
|
90
|
+
if (cycles.length === 0) {
|
|
91
|
+
db.close();
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const latest = cycles[0];
|
|
96
|
+
const prev = cycles.slice(1);
|
|
97
|
+
|
|
98
|
+
// Parsear fases completadas del último ciclo
|
|
99
|
+
let fasesInfo = '';
|
|
100
|
+
try {
|
|
101
|
+
const fases = JSON.parse(latest.fases_completadas || '[]');
|
|
102
|
+
if (fases.length > 0) fasesInfo = ` — ${fases.length} fases completadas`;
|
|
103
|
+
} catch {}
|
|
104
|
+
|
|
105
|
+
// Parsear patrones del último ciclo
|
|
106
|
+
let patronesInfo = '';
|
|
107
|
+
try {
|
|
108
|
+
const pats = JSON.parse(latest.patrones_aplicados || '[]');
|
|
109
|
+
if (pats.length > 0) patronesInfo = `, ${pats.length} patrones aplicados`;
|
|
110
|
+
} catch {}
|
|
111
|
+
|
|
112
|
+
// Archivos del último ciclo
|
|
113
|
+
const files = getLastModifiedFiles(db, latest.ciclo_id);
|
|
114
|
+
const filesStr = files.length > 0 ? `\nArchivos: ${files.join(', ')}` : '';
|
|
115
|
+
|
|
116
|
+
// Construir checkpoint
|
|
117
|
+
const lines = [
|
|
118
|
+
`# Checkpoint Agentic KDD — ${dateStr} ${timeStr}`,
|
|
119
|
+
'',
|
|
120
|
+
`> Proyecto: ${path.basename(projectRoot)}`,
|
|
121
|
+
`> Ciclos totales: ${stats.cycles}`,
|
|
122
|
+
'',
|
|
123
|
+
'---',
|
|
124
|
+
'',
|
|
125
|
+
`## Última tarea (ciclo ${stats.cycles})`,
|
|
126
|
+
`**${latest.descripcion || 'Sin descripción'}**`,
|
|
127
|
+
`Estado: ${latest.estado || 'completado'}${fasesInfo}${patronesInfo}${filesStr}`,
|
|
128
|
+
'',
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
if (prev.length > 0) {
|
|
132
|
+
lines.push('## Las anteriores');
|
|
133
|
+
prev.forEach((c, i) => {
|
|
134
|
+
const fecha = c.fecha_inicio ? new Date(c.fecha_inicio).toLocaleDateString('es-ES', {day:'2-digit', month:'2-digit'}) : '';
|
|
135
|
+
lines.push(`${i + 1}. ${fecha ? `[${fecha}] ` : ''}${c.descripcion?.substring(0, 80) || 'Sin descripción'}`);
|
|
136
|
+
});
|
|
137
|
+
lines.push('');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
lines.push('## Estado del proyecto');
|
|
141
|
+
lines.push(`- ${stats.nodes} nodos en memoria · ${stats.high} reglas HIGH`);
|
|
142
|
+
lines.push(`- ${stats.cycles} ciclos completados · ${stats.stops} stops`);
|
|
143
|
+
lines.push(`- ${stats.errors} errores documentados`);
|
|
144
|
+
lines.push('');
|
|
145
|
+
lines.push('## Para retomar en un chat nuevo');
|
|
146
|
+
lines.push('```');
|
|
147
|
+
lines.push('akdd historial');
|
|
148
|
+
lines.push('```');
|
|
149
|
+
lines.push('Pega el output al inicio del nuevo chat y escribe:');
|
|
150
|
+
lines.push('```');
|
|
151
|
+
lines.push(`aa: continúa — ${latest.descripcion?.substring(0, 60) || 'la última tarea'}`);
|
|
152
|
+
lines.push('```');
|
|
153
|
+
lines.push('');
|
|
154
|
+
lines.push(`---`);
|
|
155
|
+
lines.push(`*Generado automáticamente por Session Guard · ${dateStr} ${timeStr}*`);
|
|
156
|
+
|
|
157
|
+
const checkpointContent = lines.join('\n');
|
|
158
|
+
const checkpointPath = path.join(projectRoot, CHECKPOINT_PATH);
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
fs.writeFileSync(checkpointPath, checkpointContent);
|
|
162
|
+
} catch {}
|
|
163
|
+
|
|
164
|
+
db.close();
|
|
165
|
+
return checkpointContent;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ─── MOSTRAR HISTORIAL (para pegar en chat nuevo) ─────────────────────────────
|
|
169
|
+
|
|
170
|
+
function showHistorial(projectRoot) {
|
|
171
|
+
projectRoot = projectRoot || process.cwd();
|
|
172
|
+
const checkpointPath = path.join(projectRoot, CHECKPOINT_PATH);
|
|
173
|
+
|
|
174
|
+
if (!fs.existsSync(checkpointPath)) {
|
|
175
|
+
// Generar uno ahora si no existe
|
|
176
|
+
const generated = generateCheckpoint(projectRoot);
|
|
177
|
+
if (!generated) {
|
|
178
|
+
console.log('\n[SESSION] Sin historial todavía — corre ciclos aa: primero.\n');
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
return generated;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const content = fs.readFileSync(checkpointPath, 'utf8');
|
|
185
|
+
|
|
186
|
+
console.log('\n' + '═'.repeat(60));
|
|
187
|
+
console.log(' 📋 HISTORIAL — Pegar al inicio del chat nuevo');
|
|
188
|
+
console.log('═'.repeat(60));
|
|
189
|
+
console.log('\n' + content);
|
|
190
|
+
console.log('═'.repeat(60));
|
|
191
|
+
console.log('\n Copia todo lo de arriba y pégalo en el nuevo chat.\n');
|
|
192
|
+
|
|
193
|
+
return content;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ─── CHECK Y AUTO-CHECKPOINT ──────────────────────────────────────────────────
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Llamar al final de cada ciclo desde grafo.cjs.
|
|
200
|
+
* Genera checkpoint cada CHECKPOINT_EVERY ciclos.
|
|
201
|
+
*/
|
|
202
|
+
function maybeCheckpoint(projectRoot, currentCycleCount) {
|
|
203
|
+
if (currentCycleCount % CHECKPOINT_EVERY === 0) {
|
|
204
|
+
generateCheckpoint(projectRoot);
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ─── STATUS ───────────────────────────────────────────────────────────────────
|
|
211
|
+
|
|
212
|
+
function getStatus(projectRoot) {
|
|
213
|
+
projectRoot = projectRoot || process.cwd();
|
|
214
|
+
const db = openDB(projectRoot);
|
|
215
|
+
if (!db) return { cycles: 0, next_checkpoint: CHECKPOINT_EVERY };
|
|
216
|
+
|
|
217
|
+
const cycles = db.prepare("SELECT COUNT(*) as n FROM ciclos").get()?.n || 0;
|
|
218
|
+
db.close();
|
|
219
|
+
|
|
220
|
+
const remaining = CHECKPOINT_EVERY - (cycles % CHECKPOINT_EVERY);
|
|
221
|
+
const checkpointExists = fs.existsSync(path.join(projectRoot, CHECKPOINT_PATH));
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
cycles,
|
|
225
|
+
checkpoint_every: CHECKPOINT_EVERY,
|
|
226
|
+
cycles_until_next: remaining === CHECKPOINT_EVERY ? 0 : remaining,
|
|
227
|
+
last_checkpoint: checkpointExists
|
|
228
|
+
? fs.statSync(path.join(projectRoot, CHECKPOINT_PATH)).mtime.toLocaleDateString('es-ES')
|
|
229
|
+
: 'nunca',
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ─── CLI ──────────────────────────────────────────────────────────────────────
|
|
234
|
+
|
|
235
|
+
if (require.main === module) {
|
|
236
|
+
const [,, cmd] = process.argv;
|
|
237
|
+
const projectRoot = process.cwd();
|
|
238
|
+
|
|
239
|
+
switch (cmd) {
|
|
240
|
+
case 'checkpoint':
|
|
241
|
+
const c = generateCheckpoint(projectRoot);
|
|
242
|
+
if (c) console.log(`\n✅ Checkpoint guardado en ${CHECKPOINT_PATH}\n`);
|
|
243
|
+
else console.log('\n❌ Sin ciclos para guardar todavía.\n');
|
|
244
|
+
break;
|
|
245
|
+
|
|
246
|
+
case 'historial':
|
|
247
|
+
showHistorial(projectRoot);
|
|
248
|
+
break;
|
|
249
|
+
|
|
250
|
+
case 'status': {
|
|
251
|
+
const s = getStatus(projectRoot);
|
|
252
|
+
console.log(`\nSession Guard Status:`);
|
|
253
|
+
console.log(` Ciclos totales: ${s.cycles}`);
|
|
254
|
+
console.log(` Checkpoint cada: ${s.checkpoint_every} ciclos`);
|
|
255
|
+
console.log(` Ciclos hasta próximo: ${s.cycles_until_next || CHECKPOINT_EVERY}`);
|
|
256
|
+
console.log(` Último checkpoint: ${s.last_checkpoint}\n`);
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
default:
|
|
261
|
+
console.log('Uso: node session-guard.cjs [checkpoint | historial | status]');
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
module.exports = { generateCheckpoint, showHistorial, maybeCheckpoint, getStatus };
|
|
@@ -9,13 +9,26 @@ const { execSync } = require('child_process');
|
|
|
9
9
|
const PORT = 3847;
|
|
10
10
|
const projectPath = process.cwd();
|
|
11
11
|
const dbPath = path.join(projectPath, '.agentic', 'memoria.db');
|
|
12
|
-
const grafoPath = path.join(projectPath, '.agentic', 'grafo', 'grafo.
|
|
12
|
+
const grafoPath = fs.existsSync(path.join(projectPath, '.agentic', 'grafo', 'grafo.cjs'))
|
|
13
|
+
? path.join(projectPath, '.agentic', 'grafo', 'grafo.cjs')
|
|
14
|
+
: path.join(projectPath, '.agentic', 'grafo', 'grafo.js');
|
|
13
15
|
const configPath = path.join(projectPath, '.agentic', 'config.md');
|
|
14
16
|
const memoriaPath = path.join(projectPath, '.agentic', 'memoria');
|
|
15
17
|
|
|
16
18
|
if (!fs.existsSync(configPath)) { console.log('\n Agentic KDD not installed.\n'); process.exit(1); }
|
|
17
19
|
if (fs.existsSync(grafoPath)) { try { process.stdout.write(' Syncing... '); execSync(`node "${grafoPath}" sync`, { stdio: 'pipe', cwd: projectPath }); console.log('✓'); } catch {} }
|
|
18
20
|
|
|
21
|
+
function escHtml(str) {
|
|
22
|
+
if (!str) return '';
|
|
23
|
+
return String(str)
|
|
24
|
+
.replace(/&/g, '&')
|
|
25
|
+
.replace(/</g, '<')
|
|
26
|
+
.replace(/>/g, '>')
|
|
27
|
+
.replace(/"/g, '"')
|
|
28
|
+
.replace(/`/g, '`')
|
|
29
|
+
.replace(/\$/g, '$');
|
|
30
|
+
}
|
|
31
|
+
|
|
19
32
|
function readConfig() {
|
|
20
33
|
try {
|
|
21
34
|
const c = fs.readFileSync(configPath, 'utf8');
|
|
@@ -274,6 +287,69 @@ function getGraphData() {
|
|
|
274
287
|
} catch { return { nodes: [], edges: [], ciclos: [], fases: [] }; }
|
|
275
288
|
}
|
|
276
289
|
|
|
290
|
+
|
|
291
|
+
// ─── v3.3: CONTRACT GUARD DATA ────────────────────────────────────────────────
|
|
292
|
+
function getContractData() {
|
|
293
|
+
try {
|
|
294
|
+
if (!fs.existsSync(dbPath)) return { total:0, protected:0, verified:0, candidate:0, violations:0, recent:[] };
|
|
295
|
+
const BS3 = require('better-sqlite3');
|
|
296
|
+
const _db = new BS3(dbPath, { readonly: true });
|
|
297
|
+
let result = { total:0, protected:0, verified:0, candidate:0, invalidated:0, violations:0, recent:[] };
|
|
298
|
+
try {
|
|
299
|
+
result.total = _db.prepare("SELECT COUNT(*) as n FROM verified_contracts").get()?.n || 0;
|
|
300
|
+
result.protected = _db.prepare("SELECT COUNT(*) as n FROM verified_contracts WHERE status='protected'").get()?.n || 0;
|
|
301
|
+
result.verified = _db.prepare("SELECT COUNT(*) as n FROM verified_contracts WHERE status='verified'").get()?.n || 0;
|
|
302
|
+
result.candidate = _db.prepare("SELECT COUNT(*) as n FROM verified_contracts WHERE status='candidate'").get()?.n || 0;
|
|
303
|
+
result.invalidated= _db.prepare("SELECT COUNT(*) as n FROM verified_contracts WHERE status='invalidated'").get()?.n || 0;
|
|
304
|
+
result.violations= _db.prepare("SELECT COUNT(*) as n FROM contract_violations WHERE recovered=0").get()?.n || 0;
|
|
305
|
+
result.recent = _db.prepare("SELECT id, module, name, status, verification_count, failure_count FROM verified_contracts ORDER BY updated_at DESC LIMIT 8").all();
|
|
306
|
+
} catch {}
|
|
307
|
+
_db.close();
|
|
308
|
+
return result;
|
|
309
|
+
} catch { return { total:0, protected:0, verified:0, candidate:0, violations:0, recent:[] }; }
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ─── v3.3: CREATIVE ENGINE DATA ───────────────────────────────────────────────
|
|
313
|
+
function getCreativeData() {
|
|
314
|
+
try {
|
|
315
|
+
if (!fs.existsSync(dbPath)) return { level:1, suggestions:0, wins:0, auto_applicable:0, recent_suggestions:[] };
|
|
316
|
+
const BS3 = require('better-sqlite3');
|
|
317
|
+
const _db = new BS3(dbPath, { readonly: true });
|
|
318
|
+
let result = { level:1, suggestions:0, wins:0, auto_applicable:0, recent_suggestions:[] };
|
|
319
|
+
try {
|
|
320
|
+
result.suggestions = _db.prepare("SELECT COUNT(*) as n FROM creative_suggestions WHERE applied=0 AND dismissed=0").get()?.n || 0;
|
|
321
|
+
result.wins = _db.prepare("SELECT COUNT(*) as n FROM creative_wins").get()?.n || 0;
|
|
322
|
+
result.auto_applicable = _db.prepare("SELECT COUNT(*) as n FROM creative_suggestions WHERE auto_applicable=1 AND applied=0 AND dismissed=0").get()?.n || 0;
|
|
323
|
+
// Determine level from protected contracts
|
|
324
|
+
const protected_count = _db.prepare("SELECT COUNT(*) as n FROM verified_contracts WHERE status IN ('protected','verified')").get()?.n || 0;
|
|
325
|
+
result.level = protected_count >= 10 ? 2 : 1;
|
|
326
|
+
result.protected_for_level2 = protected_count;
|
|
327
|
+
result.recent_suggestions = _db.prepare("SELECT id, type, title, risk_level, module, auto_applicable FROM creative_suggestions WHERE applied=0 AND dismissed=0 ORDER BY created_at DESC LIMIT 5").all();
|
|
328
|
+
} catch {}
|
|
329
|
+
_db.close();
|
|
330
|
+
return result;
|
|
331
|
+
} catch { return { level:1, suggestions:0, wins:0, auto_applicable:0, recent_suggestions:[] }; }
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// ─── v3.3: MEM CURATOR DATA ───────────────────────────────────────────────────
|
|
335
|
+
function getCuratorData() {
|
|
336
|
+
try {
|
|
337
|
+
const logPath = path.join(projectPath, '.agentic', 'curator.log');
|
|
338
|
+
let lastRun = 'nunca', actions = 0;
|
|
339
|
+
if (fs.existsSync(logPath)) {
|
|
340
|
+
const lines = fs.readFileSync(logPath, 'utf8').trim().split('\n').filter(Boolean);
|
|
341
|
+
if (lines.length > 0) {
|
|
342
|
+
const last = lines[lines.length-1];
|
|
343
|
+
const match = last.match(/\[([^\]]+)\]/);
|
|
344
|
+
if (match) lastRun = match[1].split('T')[0];
|
|
345
|
+
actions = lines.filter(l => l.includes('mergeados') || l.includes('comprimidos') || l.includes('resueltos')).length;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return { lastRun, actions };
|
|
349
|
+
} catch { return { lastRun: 'nunca', actions: 0 }; }
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
|
|
277
353
|
const { nodes, edges, ciclos: ciclosDB, fases: fasesDB } = getGraphData();
|
|
278
354
|
|
|
279
355
|
// Calcular grado de conexiones por nodo (como Graphify — nodos divinos)
|
|
@@ -324,6 +400,9 @@ function parseModulos(text) {
|
|
|
324
400
|
return results;
|
|
325
401
|
}
|
|
326
402
|
|
|
403
|
+
const contractData = getContractData();
|
|
404
|
+
const creativeData = getCreativeData();
|
|
405
|
+
const curatorData = getCuratorData();
|
|
327
406
|
const modulosImpl = parseModulos(config.implementados);
|
|
328
407
|
const specsData = readSpecs();
|
|
329
408
|
const logsData = readLogs();
|
|
@@ -526,7 +605,12 @@ body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSy
|
|
|
526
605
|
.graph-legend{position:absolute;top:10px;left:10px;background:rgba(17,21,32,.9);border:1px solid var(--border);border-radius:8px;padding:8px 12px;display:flex;gap:10px;backdrop-filter:blur(4px)}
|
|
527
606
|
.lg-item{display:flex;align-items:center;gap:5px;font-size:10px;color:var(--text2)}
|
|
528
607
|
.lg-dot{width:8px;height:8px;border-radius:50%;flex-shrink:0}
|
|
529
|
-
.graph-controls{position:absolute;bottom:12px;left:12px;display:flex;gap:6px}
|
|
608
|
+
.graph-controls{position:absolute;bottom:12px;left:12px;display:flex;gap:6px;flex-wrap:wrap;align-items:center;max-width:480px}
|
|
609
|
+
.gc-slider-wrap{display:flex;align-items:center;gap:4px;background:rgba(17,21,32,.9);border:1px solid var(--border);border-radius:6px;padding:3px 8px}
|
|
610
|
+
.gc-slider-label{font-size:13px;color:var(--text2)}
|
|
611
|
+
.gc-slider{-webkit-appearance:none;width:80px;height:3px;border-radius:2px;background:var(--border);outline:none;cursor:pointer}
|
|
612
|
+
.gc-slider::-webkit-slider-thumb{-webkit-appearance:none;width:12px;height:12px;border-radius:50%;background:#8b5cf6;cursor:pointer}
|
|
613
|
+
.node-pinned{stroke:#ffffff !important;stroke-width:2px !important;stroke-dasharray:3,2}
|
|
530
614
|
.gc-btn{background:rgba(17,21,32,.9);border:1px solid var(--border);color:var(--text2);border-radius:6px;padding:5px 9px;font-size:10px;cursor:pointer;backdrop-filter:blur(4px)}
|
|
531
615
|
.gc-btn:hover{border-color:var(--purple);color:var(--pl)}
|
|
532
616
|
|
|
@@ -551,7 +635,7 @@ body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSy
|
|
|
551
635
|
|
|
552
636
|
/* ════════ PROJECT DOCS MODE ════════ */
|
|
553
637
|
#mode-docs{flex:1;display:none;overflow:hidden}
|
|
554
|
-
.docs-layout{display:flex;height:100%;overflow:hidden}
|
|
638
|
+
.docs-layout{display:flex;height:100%;width:100%;overflow:hidden;flex:1;min-width:0}
|
|
555
639
|
.docs-nav{width:210px;flex-shrink:0;background:var(--bg2);border-right:1px solid var(--border);overflow-y:auto;padding:12px}
|
|
556
640
|
.docs-nav::-webkit-scrollbar{width:3px}
|
|
557
641
|
.docs-nav::-webkit-scrollbar-thumb{background:var(--border)}
|
|
@@ -561,7 +645,7 @@ body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSy
|
|
|
561
645
|
.nav-item:hover{background:var(--bg3);color:var(--text)}
|
|
562
646
|
.nav-item.active{background:rgba(139,92,246,.12);color:var(--pl);border-left:2px solid var(--purple);padding-left:6px}
|
|
563
647
|
.nav-count{font-size:10px;color:var(--text3);margin-left:auto;background:var(--bg3);border-radius:10px;padding:1px 5px}
|
|
564
|
-
.docs-main{flex:1;overflow-y:auto;padding:24px 28px}
|
|
648
|
+
.docs-main{flex:1;min-width:0;overflow-y:auto;padding:24px 28px}
|
|
565
649
|
.docs-main::-webkit-scrollbar{width:4px}
|
|
566
650
|
.docs-main::-webkit-scrollbar-thumb{background:var(--border);border-radius:2px}
|
|
567
651
|
.docs-section{display:none}.docs-section.active{display:block}#doc-modules.active{display:flex}
|
|
@@ -766,6 +850,13 @@ body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSy
|
|
|
766
850
|
<button class="gc-btn" onclick="resetGraph()" data-i="btn_reset">⟳ Reset</button>
|
|
767
851
|
<button class="gc-btn" onclick="centerGraph()" data-i="btn_center">⊙ Center</button>
|
|
768
852
|
<button class="gc-btn" onclick="toggleLabels()" id="label-btn" data-i="btn_labels">Labels OFF</button>
|
|
853
|
+
<button class="gc-btn" onclick="spreadGraph()" title="Spread nodes apart">⊹ Spread</button>
|
|
854
|
+
<button class="gc-btn" onclick="releaseAll()" title="Release all pinned nodes">⊠ Unpin all</button>
|
|
855
|
+
<div class="gc-slider-wrap" title="Node repulsion">
|
|
856
|
+
<span class="gc-slider-label">⊷</span>
|
|
857
|
+
<input type="range" class="gc-slider" id="repulsion-slider" min="50" max="800" value="320"
|
|
858
|
+
oninput="setRepulsion(this.value)" title="Repulsion force">
|
|
859
|
+
</div>
|
|
769
860
|
</div>
|
|
770
861
|
<div class="detail-panel" id="detail-panel">
|
|
771
862
|
<div class="dp-header">
|
|
@@ -925,13 +1016,13 @@ body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSy
|
|
|
925
1016
|
<div class="docs-section" id="doc-patterns">
|
|
926
1017
|
<div class="docs-h1">Patterns</div>
|
|
927
1018
|
<div class="docs-sub">Rules the system learned from this project. HIGH = permanent rule applied automatically.</div>
|
|
928
|
-
${patrones.length ? patrones.sort((a,b) => {const w={ALTA:3,MEDIA:2,BAJA:1}; return (w[b.confianza]||0)-(w[a.confianza]||0);}).map(p => {
|
|
1019
|
+
${patrones.length ? patrones.filter(p => p.titulo && p.titulo !== 'Nombre del patrón' && p.titulo.length > 5).sort((a,b) => {const w={ALTA:3,MEDIA:2,BAJA:1}; return (w[b.confianza]||0)-(w[a.confianza]||0);}).map(p => {
|
|
929
1020
|
const maxUse = Math.max(...patrones.map(x => x.aplicado), 1);
|
|
930
1021
|
return `<div class="pattern-card ${p.confianza==='ALTA'?'high':''}">
|
|
931
1022
|
<div class="pc-top">
|
|
932
|
-
<div class="pc-title">${p.titulo}</div>
|
|
933
|
-
<span class="mb c${p.confianza}">${p.confianza}</span>
|
|
934
|
-
<span class="ab">${p.area}</span>
|
|
1023
|
+
<div class="pc-title">${escHtml(p.titulo)}</div>
|
|
1024
|
+
<span class="mb c${p.confianza}">${escHtml(p.confianza)}</span>
|
|
1025
|
+
<span class="ab">${escHtml(p.area)}</span>
|
|
935
1026
|
</div>
|
|
936
1027
|
${p.aplicado > 0 ? `<div style="font-size:10px;color:var(--text3);margin-bottom:4px">Applied ${p.aplicado} times · ${p.util} useful</div><div class="usage-bar"><div class="usage-fill" style="width:${Math.round(p.aplicado/maxUse*100)}%"></div></div>` : ''}
|
|
937
1028
|
</div>`;
|
|
@@ -949,8 +1040,8 @@ body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSy
|
|
|
949
1040
|
<div class="docs-section" id="doc-errors">
|
|
950
1041
|
<div class="docs-h1">Known Error Patterns</div>
|
|
951
1042
|
<div class="docs-sub">Errors the system has already learned to avoid automatically.</div>
|
|
952
|
-
${errores.length ? errores.sort((a,b)=>b.aplicado-a.aplicado).map(e => `<div class="pattern-card" style="border-left:3px solid var(--red)">
|
|
953
|
-
<div class="pc-top"><div class="pc-title">${e.titulo}</div><span class="mb c${e.confianza}">${e.confianza}</span><span class="ab">${e.area}</span></div>
|
|
1043
|
+
${errores.length ? errores.filter(e => e.titulo && e.titulo !== 'Nombre del patrón' && e.titulo.length > 5).sort((a,b)=>b.aplicado-a.aplicado).map(e => `<div class="pattern-card" style="border-left:3px solid var(--red)">
|
|
1044
|
+
<div class="pc-top"><div class="pc-title">${escHtml(e.titulo)}</div><span class="mb c${e.confianza}">${e.confianza}</span><span class="ab">${escHtml(e.area)}</span></div>
|
|
954
1045
|
${e.aplicado > 0 ? `<div style="font-size:10px;color:var(--text3)">Resolved ${e.aplicado} times</div>` : ''}
|
|
955
1046
|
</div>`).join('') : '<div class="empty-state">No errors recorded yet</div>'}
|
|
956
1047
|
</div>
|
|
@@ -1172,19 +1263,6 @@ body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSy
|
|
|
1172
1263
|
</div>
|
|
1173
1264
|
|
|
1174
1265
|
<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
|
-
});
|
|
1188
1266
|
const NODES = ${JSON.stringify(nodes)};
|
|
1189
1267
|
const EDGES = ${JSON.stringify(edges)};
|
|
1190
1268
|
const M_NODES = ${JSON.stringify(mNodes)};
|
|
@@ -1438,6 +1516,43 @@ function centerGraph(){
|
|
|
1438
1516
|
simulation.force('center',d3.forceCenter(c.clientWidth/2,c.clientHeight/2)).alpha(0.3).restart();
|
|
1439
1517
|
}
|
|
1440
1518
|
|
|
1519
|
+
// ─── Graph interaction helpers ────────────────────────────────
|
|
1520
|
+
function updatePinIndicator(el, pinned){
|
|
1521
|
+
if(!el)return;
|
|
1522
|
+
d3.select(el).classed('node-pinned', pinned);
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
function unpinNode(d, el){
|
|
1526
|
+
d.fx=null; d.fy=null;
|
|
1527
|
+
if(el) d3.select(el).classed('node-pinned', false);
|
|
1528
|
+
simulation.alpha(0.2).restart();
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
function releaseAll(){
|
|
1532
|
+
if(!nodeSel)return;
|
|
1533
|
+
NODES.forEach(n=>{n.fx=null;n.fy=null;});
|
|
1534
|
+
nodeSel.classed('node-pinned', false);
|
|
1535
|
+
simulation.alpha(0.5).restart();
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
function spreadGraph(){
|
|
1539
|
+
if(!simulation)return;
|
|
1540
|
+
simulation.force('charge',d3.forceManyBody().strength(d=>(DEGREE_MAP[d.id]||0)>=GOD_THRESHOLD?-1200:-700));
|
|
1541
|
+
simulation.alpha(0.8).restart();
|
|
1542
|
+
setTimeout(()=>{
|
|
1543
|
+
const repVal = parseInt(document.getElementById('repulsion-slider')?.value||320);
|
|
1544
|
+
simulation.force('charge',d3.forceManyBody().strength(d=>(DEGREE_MAP[d.id]||0)>=GOD_THRESHOLD?-(repVal*2):-(repVal)));
|
|
1545
|
+
simulation.alpha(0.1).restart();
|
|
1546
|
+
}, 1800);
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
function setRepulsion(val){
|
|
1550
|
+
val = parseInt(val);
|
|
1551
|
+
if(!simulation)return;
|
|
1552
|
+
simulation.force('charge',d3.forceManyBody().strength(d=>(DEGREE_MAP[d.id]||0)>=GOD_THRESHOLD?-(val*2):-(val)));
|
|
1553
|
+
simulation.alpha(0.3).restart();
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1441
1556
|
// ─── D3 Knowledge Graph ───────────────────────────────────────
|
|
1442
1557
|
function renderGraph(){
|
|
1443
1558
|
if(!NODES.length)return;
|
|
@@ -1466,7 +1581,7 @@ function renderGraph(){
|
|
|
1466
1581
|
const sd=DEGREE_MAP[d.source.id]||0, td=DEGREE_MAP[d.target.id]||0;
|
|
1467
1582
|
return sd>=GOD_THRESHOLD||td>=GOD_THRESHOLD?120:90;
|
|
1468
1583
|
}))
|
|
1469
|
-
.force('charge',d3.forceManyBody().strength(d=>(DEGREE_MAP[d.id]||0)>=GOD_THRESHOLD?-
|
|
1584
|
+
.force('charge',d3.forceManyBody().strength(d=>(DEGREE_MAP[d.id]||0)>=GOD_THRESHOLD?-600:-320))
|
|
1470
1585
|
.force('center',d3.forceCenter(W/2,H/2))
|
|
1471
1586
|
.force('collision',d3.forceCollide(d=>getNodeRadius(d)+4));
|
|
1472
1587
|
|
|
@@ -1488,8 +1603,9 @@ function renderGraph(){
|
|
|
1488
1603
|
.style('cursor','pointer')
|
|
1489
1604
|
.call(d3.drag()
|
|
1490
1605
|
.on('start',(ev,d)=>{if(!ev.active)simulation.alphaTarget(0.3).restart();d.fx=d.x;d.fy=d.y;})
|
|
1491
|
-
.on('drag',(ev,d)=>{d.fx=ev.x;d.fy=ev.y;})
|
|
1492
|
-
.on('end',(ev,d)=>{if(!ev.active)simulation.alphaTarget(0)
|
|
1606
|
+
.on('drag',(ev,d)=>{d.fx=ev.x;d.fy=ev.y;updatePinIndicator(ev.currentTarget,true);})
|
|
1607
|
+
.on('end',(ev,d)=>{if(!ev.active)simulation.alphaTarget(0);/* node stays PINNED — dblclick to release */}))
|
|
1608
|
+
.on('dblclick',(ev,d)=>{ev.stopPropagation();unpinNode(d,ev.currentTarget);})
|
|
1493
1609
|
.on('click',(ev,d)=>{ev.stopPropagation();selectNode(d.id);})
|
|
1494
1610
|
.on('mouseover',(ev,d)=>{
|
|
1495
1611
|
const tt=document.getElementById('gtt');
|
|
@@ -1706,7 +1822,156 @@ function copyMarkdown(){
|
|
|
1706
1822
|
renderNodeList();
|
|
1707
1823
|
renderGraph();
|
|
1708
1824
|
</script>
|
|
1709
|
-
|
|
1825
|
+
|
|
1826
|
+
<!-- ───────────────────────────────────────────────────────────────────
|
|
1827
|
+
v3.3 PANELS: CONTRACT GUARD + CREATIVE ENGINE + CURATOR
|
|
1828
|
+
─────────────────────────────────────────────────────────────────────── -->
|
|
1829
|
+
|
|
1830
|
+
<style>
|
|
1831
|
+
.v33-section { margin: 0 auto 32px; max-width: 1100px; padding: 0 24px; }
|
|
1832
|
+
.v33-title { font-size: 11px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: #8d99ae; margin: 0 0 14px; }
|
|
1833
|
+
.v33-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; }
|
|
1834
|
+
.v33-card { background: #1e2535; border: 1px solid #2e3550; border-radius: 12px; padding: 18px 20px; }
|
|
1835
|
+
.v33-card-header { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
|
|
1836
|
+
.v33-card-icon { width: 32px; height: 32px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 16px; flex-shrink: 0; }
|
|
1837
|
+
.icon-purple { background: rgba(127,119,221,0.15); }
|
|
1838
|
+
.icon-green { background: rgba(29,158,117,0.15); }
|
|
1839
|
+
.icon-amber { background: rgba(239,159,39,0.15); }
|
|
1840
|
+
.v33-card-title { font-size: 14px; font-weight: 700; color: #e2e8f0; }
|
|
1841
|
+
.v33-card-sub { font-size: 11px; color: #64748b; margin-top: 1px; }
|
|
1842
|
+
.v33-stat-row { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; margin-bottom: 14px; }
|
|
1843
|
+
.v33-stat { background: rgba(255,255,255,0.03); border-radius: 8px; padding: 10px 12px; text-align: center; }
|
|
1844
|
+
.v33-stat-val { font-size: 22px; font-weight: 800; line-height: 1.1; }
|
|
1845
|
+
.v33-stat-label { font-size: 10px; color: #64748b; margin-top: 2px; }
|
|
1846
|
+
.val-purple { color: #9f99e8; }
|
|
1847
|
+
.val-green { color: #34d399; }
|
|
1848
|
+
.val-amber { color: #fbbf24; }
|
|
1849
|
+
.val-red { color: #f87171; }
|
|
1850
|
+
.val-gray { color: #94a3b8; }
|
|
1851
|
+
.contract-list { display: flex; flex-direction: column; gap: 6px; }
|
|
1852
|
+
.contract-row { display: flex; align-items: center; gap: 8px; padding: 7px 10px; background: rgba(255,255,255,0.03); border-radius: 7px; font-size: 12px; }
|
|
1853
|
+
.contract-badge { font-size: 9px; font-weight: 700; padding: 2px 6px; border-radius: 10px; white-space: nowrap; flex-shrink: 0; }
|
|
1854
|
+
.badge-protected { background: rgba(127,119,221,0.2); color: #9f99e8; }
|
|
1855
|
+
.badge-verified { background: rgba(29,158,117,0.2); color: #34d399; }
|
|
1856
|
+
.badge-candidate { background: rgba(239,159,39,0.2); color: #fbbf24; }
|
|
1857
|
+
.badge-invalid { background: rgba(248,113,113,0.2); color: #f87171; }
|
|
1858
|
+
.contract-name { flex: 1; color: #cbd5e1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1859
|
+
.contract-module { font-size: 10px; color: #475569; }
|
|
1860
|
+
.suggestion-row { display: flex; align-items: flex-start; gap: 8px; padding: 7px 10px; background: rgba(255,255,255,0.03); border-radius: 7px; font-size: 12px; margin-bottom: 5px; }
|
|
1861
|
+
.sug-type { font-size: 9px; font-weight: 700; padding: 2px 6px; border-radius: 10px; white-space: nowrap; flex-shrink: 0; background: rgba(239,159,39,0.15); color: #fbbf24; }
|
|
1862
|
+
.sug-auto { background: rgba(29,158,117,0.15); color: #34d399; }
|
|
1863
|
+
.sug-text { color: #94a3b8; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1864
|
+
.level-bar { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
|
|
1865
|
+
.level-indicator { height: 6px; flex: 1; background: rgba(255,255,255,0.06); border-radius: 3px; overflow: hidden; }
|
|
1866
|
+
.level-fill { height: 100%; border-radius: 3px; transition: width .6s; }
|
|
1867
|
+
.curator-row { display: flex; justify-content: space-between; align-items: center; padding: 6px 0; border-bottom: 1px solid rgba(255,255,255,0.05); font-size: 12px; }
|
|
1868
|
+
.curator-row:last-child { border-bottom: none; }
|
|
1869
|
+
.curator-key { color: #64748b; }
|
|
1870
|
+
.curator-val { color: #94a3b8; font-weight: 600; }
|
|
1871
|
+
.obsidian-panel { background: #1e2535; border: 1px solid #3730a3; border-radius: 12px; padding: 16px 20px; }
|
|
1872
|
+
.obsidian-header { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
|
|
1873
|
+
.obsidian-badge { font-size: 10px; padding: 2px 8px; border-radius: 10px; background: rgba(99,91,255,0.15); color: #818cf8; font-weight: 600; }
|
|
1874
|
+
.obsidian-text { font-size: 13px; color: #94a3b8; line-height: 1.5; }
|
|
1875
|
+
.obsidian-cmd { font-family: monospace; font-size: 11px; background: rgba(255,255,255,0.05); color: #a5b4fc; padding: 6px 10px; border-radius: 6px; margin-top: 8px; display: block; }
|
|
1876
|
+
@media(max-width:600px){ .v33-stat-row { grid-template-columns: repeat(2,1fr); } }
|
|
1877
|
+
</style>
|
|
1878
|
+
|
|
1879
|
+
<div class="v33-section">
|
|
1880
|
+
<div class="v33-title">Preservation Intelligence Layer — v3.3</div>
|
|
1881
|
+
<div class="v33-grid">
|
|
1882
|
+
|
|
1883
|
+
<!-- CONTRACT GUARD -->
|
|
1884
|
+
<div class="v33-card">
|
|
1885
|
+
<div class="v33-card-header">
|
|
1886
|
+
<div class="v33-card-icon icon-purple">🛡️</div>
|
|
1887
|
+
<div><div class="v33-card-title">Contract Guard</div><div class="v33-card-sub">Lo que no se puede romper</div></div>
|
|
1888
|
+
</div>
|
|
1889
|
+
<div class="v33-stat-row">
|
|
1890
|
+
<div class="v33-stat"><div class="v33-stat-val val-purple">${contractData.protected}</div><div class="v33-stat-label">Protected</div></div>
|
|
1891
|
+
<div class="v33-stat"><div class="v33-stat-val val-green">${contractData.verified}</div><div class="v33-stat-label">Verified</div></div>
|
|
1892
|
+
<div class="v33-stat"><div class="v33-stat-val val-amber">${contractData.candidate}</div><div class="v33-stat-label">Candidate</div></div>
|
|
1893
|
+
<div class="v33-stat"><div class="v33-stat-val ${contractData.violations > 0 ? 'val-red' : 'val-gray'}">${contractData.violations}</div><div class="v33-stat-label">Violations</div></div>
|
|
1894
|
+
</div>
|
|
1895
|
+
${contractData.recent && contractData.recent.length > 0 ? `
|
|
1896
|
+
<div class="contract-list">
|
|
1897
|
+
${contractData.recent.map(c => `
|
|
1898
|
+
<div class="contract-row">
|
|
1899
|
+
<span class="contract-badge badge-${c.status}">${c.status.toUpperCase()}</span>
|
|
1900
|
+
<span class="contract-name" title="${escHtml(c.name)}">${escHtml(c.name.substring(0,35))}</span>
|
|
1901
|
+
<span class="contract-module">${escHtml(c.module)}</span>
|
|
1902
|
+
</div>
|
|
1903
|
+
`).join('')}
|
|
1904
|
+
</div>` : `<div style="font-size:12px;color:#475569;text-align:center;padding:12px 0">Sin contratos todavía — corre más ciclos aa: para generarlos automáticamente</div>`}
|
|
1905
|
+
</div>
|
|
1906
|
+
|
|
1907
|
+
<!-- CREATIVE ENGINE -->
|
|
1908
|
+
<div class="v33-card">
|
|
1909
|
+
<div class="v33-card-header">
|
|
1910
|
+
<div class="v33-card-icon icon-amber">✨</div>
|
|
1911
|
+
<div><div class="v33-card-title">Creative Engine</div><div class="v33-card-sub">Autonomía creativa dirigida</div></div>
|
|
1912
|
+
</div>
|
|
1913
|
+
<div class="level-bar">
|
|
1914
|
+
<span style="font-size:11px;color:#64748b;white-space:nowrap">Nivel ${creativeData.level}</span>
|
|
1915
|
+
<div class="level-indicator"><div class="level-fill" style="width:${(creativeData.level / 3 * 100).toFixed(0)}%;background:${creativeData.level >= 2 ? '#34d399' : '#fbbf24'}"></div></div>
|
|
1916
|
+
<span style="font-size:11px;color:${creativeData.level >= 2 ? '#34d399' : '#fbbf24'};white-space:nowrap">${creativeData.level >= 2 ? 'CREATIVO' : 'ASISTIDO'}</span>
|
|
1917
|
+
</div>
|
|
1918
|
+
${creativeData.level < 2 ? `<div style="font-size:11px;color:#475569;margin-bottom:10px">Faltan ${10 - (creativeData.protected_for_level2 || 0)} contratos verificados para Nivel 2</div>` : ''}
|
|
1919
|
+
<div class="v33-stat-row">
|
|
1920
|
+
<div class="v33-stat"><div class="v33-stat-val val-amber">${creativeData.suggestions}</div><div class="v33-stat-label">Pendientes</div></div>
|
|
1921
|
+
<div class="v33-stat"><div class="v33-stat-val val-green">${creativeData.wins}</div><div class="v33-stat-label">Aplicadas</div></div>
|
|
1922
|
+
</div>
|
|
1923
|
+
${creativeData.recent_suggestions && creativeData.recent_suggestions.length > 0 ? `
|
|
1924
|
+
<div>
|
|
1925
|
+
${creativeData.recent_suggestions.map(s => `
|
|
1926
|
+
<div class="suggestion-row">
|
|
1927
|
+
<span class="sug-type ${s.auto_applicable ? 'sug-auto' : ''}">${s.type}</span>
|
|
1928
|
+
<span class="sug-text" title="${escHtml(s.title)}">${escHtml(s.title.substring(0,50))}</span>
|
|
1929
|
+
</div>
|
|
1930
|
+
`).join('')}
|
|
1931
|
+
</div>` : `<div style="font-size:12px;color:#475569;text-align:center;padding:8px 0">Sin sugerencias — se generan automáticamente cada ciclo</div>`}
|
|
1932
|
+
</div>
|
|
1933
|
+
|
|
1934
|
+
<!-- MEM CURATOR -->
|
|
1935
|
+
<div class="v33-card">
|
|
1936
|
+
<div class="v33-card-icon icon-green" style="margin-bottom:14px;width:auto;height:auto;padding:0;display:flex;gap:10px;align-items:center;background:none">
|
|
1937
|
+
<div style="width:32px;height:32px;border-radius:8px;background:rgba(29,158,117,0.15);display:flex;align-items:center;justify-content:center;font-size:16px">🔬</div>
|
|
1938
|
+
<div><div class="v33-card-title">MemCurator</div><div class="v33-card-sub">Gobernanza autónoma de memoria</div></div>
|
|
1939
|
+
</div>
|
|
1940
|
+
<div class="curator-row"><span class="curator-key">Última curation</span><span class="curator-val">${curatorData.lastRun}</span></div>
|
|
1941
|
+
<div class="curator-row"><span class="curator-key">Ciclos para auto-run</span><span class="curator-val">cada 10</span></div>
|
|
1942
|
+
<div class="curator-row"><span class="curator-key">TTL episódico</span><span class="curator-val">30 días</span></div>
|
|
1943
|
+
<div class="curator-row"><span class="curator-key">Límite nodos activos</span><span class="curator-val">1,000</span></div>
|
|
1944
|
+
<div class="curator-row"><span class="curator-key">Dedup threshold</span><span class="curator-val">92% Jaccard</span></div>
|
|
1945
|
+
<div style="margin-top:12px;padding:10px;background:rgba(255,255,255,0.03);border-radius:8px;font-size:11px;color:#64748b">
|
|
1946
|
+
<code style="color:#a5b4fc">akdd cure</code> — curation manual<br>
|
|
1947
|
+
<code style="color:#a5b4fc">akdd cure report</code> — preview sin cambios
|
|
1948
|
+
</div>
|
|
1949
|
+
</div>
|
|
1950
|
+
|
|
1951
|
+
</div>
|
|
1952
|
+
</div>
|
|
1953
|
+
|
|
1954
|
+
<!-- OBSIDIAN MCP OPTIONAL CONNECTOR -->
|
|
1955
|
+
<div class="v33-section">
|
|
1956
|
+
<div class="v33-title">Conector opcional — Obsidian MCP</div>
|
|
1957
|
+
<div class="obsidian-panel">
|
|
1958
|
+
<div class="obsidian-header">
|
|
1959
|
+
<span style="font-size:20px">🗂️</span>
|
|
1960
|
+
<div style="flex:1">
|
|
1961
|
+
<span style="font-size:14px;font-weight:700;color:#e2e8f0">Obsidian como fuente humana</span>
|
|
1962
|
+
<span class="obsidian-badge" style="margin-left:8px">OPCIONAL</span>
|
|
1963
|
+
</div>
|
|
1964
|
+
</div>
|
|
1965
|
+
<div class="obsidian-text">
|
|
1966
|
+
Si usas Obsidian, el plugin MCP conecta tu vault directamente con Agentic KDD.<br>
|
|
1967
|
+
Tus notas personales, decisiones y gotchas fluyen al grafo automáticamente — sin hacer <code style="color:#a5b4fc">akdd knowledge</code> manual.
|
|
1968
|
+
</div>
|
|
1969
|
+
<code class="obsidian-cmd">1. Instalar plugin "Obsidian MCP Server" en Obsidian<br>2. En Claude Code/Cursor: agrega el servidor MCP del plugin<br>3. La herramienta obsidian_read_notes queda disponible en el chat</code>
|
|
1970
|
+
<div style="font-size:11px;color:#475569;margin-top:8px">Sin Obsidian instalado: usa <code style="color:#a5b4fc">akdd knowledge</code> normalmente — mismo resultado.</div>
|
|
1971
|
+
</div>
|
|
1972
|
+
</div>
|
|
1973
|
+
|
|
1974
|
+
</body>
|
|
1710
1975
|
</html>`;
|
|
1711
1976
|
|
|
1712
1977
|
const server = http.createServer((req, res) => {
|