agentic-kdd 3.1.1 → 3.2.1
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 +35 -0
- package/contract-guard.cjs +851 -0
- package/creative-engine.cjs +560 -0
- package/package.json +1 -1
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agentic KDD — Creative Engine v1.0
|
|
3
|
+
* Directed Creative Autonomy (DCA)
|
|
4
|
+
*
|
|
5
|
+
* El agente no solo ejecuta lo pedido — interpreta el proyecto y aporta valor
|
|
6
|
+
* adicional dentro de los límites del libreto.
|
|
7
|
+
*
|
|
8
|
+
* ┌─────────────────────────────────────────────────────────────────────────┐
|
|
9
|
+
* │ NIVELES DE AUTONOMÍA: │
|
|
10
|
+
* │ │
|
|
11
|
+
* │ NIVEL 0 — ESTRICTO │
|
|
12
|
+
* │ Ejecuta exactamente lo pedido. Sin sugerencias. │
|
|
13
|
+
* │ Activar para: cambios sensibles, bugs críticos, reglas de negocio │
|
|
14
|
+
* │ │
|
|
15
|
+
* │ NIVEL 1 — ASISTIDO (DEFAULT) │
|
|
16
|
+
* │ Ejecuta y sugiere mejoras al final del ciclo. │
|
|
17
|
+
* │ NUNCA aplica sugerencias sin permiso explícito. │
|
|
18
|
+
* │ Activo desde el primer ciclo. │
|
|
19
|
+
* │ │
|
|
20
|
+
* │ NIVEL 2 — CREATIVO CONTROLADO (auto-eleva con 10+ contratos) │
|
|
21
|
+
* │ Puede aplicar mejoras locales si: │
|
|
22
|
+
* │ - blast_radius == LOW (≤ 3 contratos) │
|
|
23
|
+
* │ - No rompe ningún contrato VERIFIED o PROTECTED │
|
|
24
|
+
* │ - La mejora está respaldada por memoria del proyecto │
|
|
25
|
+
* │ - El impacto es local (< 3 archivos) │
|
|
26
|
+
* │ │
|
|
27
|
+
* │ NIVEL 3 — EXPLORATORIO (manual, solo para prototipos) │
|
|
28
|
+
* │ Para proyectos con poca documentación. Infiere más, propone más. │
|
|
29
|
+
* │ Activo solo con: aa: explore <tarea> │
|
|
30
|
+
* └─────────────────────────────────────────────────────────────────────────┘
|
|
31
|
+
*
|
|
32
|
+
* GUARDRAILS (siempre activos independientemente del nivel):
|
|
33
|
+
* 1. Nunca toca contratos PROTECTED
|
|
34
|
+
* 2. Blast radius CRITICAL → bloquea cualquier acción creativa
|
|
35
|
+
* 3. Toda acción creativa queda auditada y es reversible
|
|
36
|
+
* 4. Mejoras creativas se guardan como creative_win en memoria
|
|
37
|
+
*
|
|
38
|
+
* Uso:
|
|
39
|
+
* node creative-engine.cjs level — ver nivel actual
|
|
40
|
+
* node creative-engine.cjs suggest <area> — ver sugerencias pendientes
|
|
41
|
+
* node creative-engine.cjs apply <id> — aplicar una sugerencia
|
|
42
|
+
* node creative-engine.cjs dismiss <id> — descartar una sugerencia
|
|
43
|
+
* node creative-engine.cjs wins — ver mejoras creativas previas
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
'use strict';
|
|
47
|
+
|
|
48
|
+
const path = require('path');
|
|
49
|
+
const fs = require('fs');
|
|
50
|
+
|
|
51
|
+
// ─── CONSTANTES ───────────────────────────────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
const LEVEL_NAMES = {
|
|
54
|
+
0: 'STRICT',
|
|
55
|
+
1: 'ASSISTED',
|
|
56
|
+
2: 'CREATIVE_CONTROLLED',
|
|
57
|
+
3: 'EXPLORATORY',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const LEVEL_2_MIN_CONTRACTS = 10; // contratos protegidos mínimos para auto-elevar a nivel 2
|
|
61
|
+
const MAX_SUGGESTIONS_STORED = 50; // máx sugerencias pendientes en DB
|
|
62
|
+
|
|
63
|
+
// Tipos de sugerencia con metadata
|
|
64
|
+
const SUGGESTION_TYPES = {
|
|
65
|
+
SIMPLIFICATION: { label: 'Simplification', risk: 'LOW', auto_apply_at: 2 },
|
|
66
|
+
ABSTRACTION: { label: 'Abstraction', risk: 'MEDIUM', auto_apply_at: 3 },
|
|
67
|
+
REFACTOR: { label: 'Refactor', risk: 'MEDIUM', auto_apply_at: 3 },
|
|
68
|
+
PATTERN: { label: 'Pattern', risk: 'LOW', auto_apply_at: 2 },
|
|
69
|
+
FRAGILITY: { label: 'Fragility warning', risk: 'HIGH', auto_apply_at: null }, // nunca auto-aplica
|
|
70
|
+
DEAD_CODE: { label: 'Dead code', risk: 'LOW', auto_apply_at: 2 },
|
|
71
|
+
MISSING_TEST: { label: 'Missing test', risk: 'MEDIUM', auto_apply_at: null },
|
|
72
|
+
OPPORTUNITY: { label: 'Opportunity', risk: 'LOW', auto_apply_at: 2 },
|
|
73
|
+
ARCHITECTURE: { label: 'Architecture', risk: 'HIGH', auto_apply_at: null },
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// ─── DB ───────────────────────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
function openDB(projectRoot) {
|
|
79
|
+
const dbPath = path.join(projectRoot, '.agentic/memoria.db');
|
|
80
|
+
try { return new (require('better-sqlite3'))(dbPath); } catch {}
|
|
81
|
+
try { const { DatabaseSync } = require('node:sqlite'); return new DatabaseSync(dbPath); } catch {}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ─── SCHEMA ───────────────────────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
function migrateSchema(db) {
|
|
88
|
+
db.exec(`
|
|
89
|
+
CREATE TABLE IF NOT EXISTS creative_suggestions (
|
|
90
|
+
id TEXT PRIMARY KEY,
|
|
91
|
+
type TEXT NOT NULL,
|
|
92
|
+
title TEXT NOT NULL,
|
|
93
|
+
description TEXT,
|
|
94
|
+
file TEXT,
|
|
95
|
+
module TEXT,
|
|
96
|
+
area TEXT DEFAULT 'global',
|
|
97
|
+
risk_level TEXT DEFAULT 'LOW',
|
|
98
|
+
blast_radius INTEGER DEFAULT 0,
|
|
99
|
+
evidence TEXT,
|
|
100
|
+
auto_applicable INTEGER DEFAULT 0,
|
|
101
|
+
applied INTEGER DEFAULT 0,
|
|
102
|
+
dismissed INTEGER DEFAULT 0,
|
|
103
|
+
ciclo_id TEXT,
|
|
104
|
+
created_at TEXT DEFAULT (datetime('now')),
|
|
105
|
+
applied_at TEXT,
|
|
106
|
+
result TEXT
|
|
107
|
+
)
|
|
108
|
+
`);
|
|
109
|
+
|
|
110
|
+
db.exec(`
|
|
111
|
+
CREATE TABLE IF NOT EXISTS creative_wins (
|
|
112
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
113
|
+
suggestion_id TEXT,
|
|
114
|
+
description TEXT NOT NULL,
|
|
115
|
+
type TEXT NOT NULL,
|
|
116
|
+
module TEXT,
|
|
117
|
+
file TEXT,
|
|
118
|
+
impact TEXT,
|
|
119
|
+
ciclo_id TEXT,
|
|
120
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
121
|
+
)
|
|
122
|
+
`);
|
|
123
|
+
|
|
124
|
+
try { db.exec("CREATE INDEX IF NOT EXISTS idx_suggestions_module ON creative_suggestions(module)"); } catch {}
|
|
125
|
+
try { db.exec("CREATE INDEX IF NOT EXISTS idx_suggestions_applied ON creative_suggestions(applied, dismissed)"); } catch {}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ─── NIVEL ACTUAL ─────────────────────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Determina el nivel de autonomía creativa basado en:
|
|
132
|
+
* - config.md (override manual del dev)
|
|
133
|
+
* - cantidad de contratos protegidos en DB
|
|
134
|
+
*/
|
|
135
|
+
function getCurrentLevel(db, projectRoot) {
|
|
136
|
+
// 1. Verificar override manual en config.md
|
|
137
|
+
const configPath = path.join(projectRoot, '.agentic/config.md');
|
|
138
|
+
if (fs.existsSync(configPath)) {
|
|
139
|
+
const config = fs.readFileSync(configPath, 'utf8');
|
|
140
|
+
const levelMatch = config.match(/creative_mode:\s*(\d+)/i);
|
|
141
|
+
if (levelMatch) {
|
|
142
|
+
const manualLevel = parseInt(levelMatch[1]);
|
|
143
|
+
return {
|
|
144
|
+
level: manualLevel,
|
|
145
|
+
name: LEVEL_NAMES[manualLevel] || 'CUSTOM',
|
|
146
|
+
source: 'manual (config.md)',
|
|
147
|
+
auto_apply: manualLevel >= 2,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
// Modo strict explícito
|
|
151
|
+
if (/creative_mode:\s*strict/i.test(config)) {
|
|
152
|
+
return { level: 0, name: 'STRICT', source: 'manual (config.md)', auto_apply: false };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 2. Auto-determinar por cantidad de contratos protegidos
|
|
157
|
+
if (db) {
|
|
158
|
+
try {
|
|
159
|
+
const protectedCount = db.prepare(
|
|
160
|
+
"SELECT COUNT(*) as n FROM verified_contracts WHERE status IN ('protected','verified')"
|
|
161
|
+
).get()?.n || 0;
|
|
162
|
+
|
|
163
|
+
if (protectedCount >= LEVEL_2_MIN_CONTRACTS) {
|
|
164
|
+
return {
|
|
165
|
+
level: 2,
|
|
166
|
+
name: 'CREATIVE_CONTROLLED',
|
|
167
|
+
source: `auto (${protectedCount} verified contracts)`,
|
|
168
|
+
auto_apply: true,
|
|
169
|
+
protected_contracts: protectedCount,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
level: 1,
|
|
175
|
+
name: 'ASSISTED',
|
|
176
|
+
source: `auto (${protectedCount}/${LEVEL_2_MIN_CONTRACTS} contracts for level 2)`,
|
|
177
|
+
auto_apply: false,
|
|
178
|
+
protected_contracts: protectedCount,
|
|
179
|
+
contracts_needed: LEVEL_2_MIN_CONTRACTS - protectedCount,
|
|
180
|
+
};
|
|
181
|
+
} catch {}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return { level: 1, name: 'ASSISTED', source: 'default', auto_apply: false };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ─── REGISTRAR SUGERENCIA ─────────────────────────────────────────────────────
|
|
188
|
+
|
|
189
|
+
function addSuggestion(db, suggestion, cicloId) {
|
|
190
|
+
if (!db) return null;
|
|
191
|
+
|
|
192
|
+
const id = require('crypto')
|
|
193
|
+
.createHash('md5')
|
|
194
|
+
.update(`${suggestion.type}:${suggestion.title}:${suggestion.file || ''}`)
|
|
195
|
+
.digest('hex')
|
|
196
|
+
.substring(0, 8);
|
|
197
|
+
|
|
198
|
+
const typeConfig = SUGGESTION_TYPES[suggestion.type] || SUGGESTION_TYPES.OPPORTUNITY;
|
|
199
|
+
const currentLevel = getCurrentLevel(db, process.cwd());
|
|
200
|
+
const autoApplicable = currentLevel.auto_apply &&
|
|
201
|
+
typeConfig.auto_apply_at !== null &&
|
|
202
|
+
currentLevel.level >= typeConfig.auto_apply_at &&
|
|
203
|
+
(suggestion.blast_radius || 0) <= 3;
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
db.prepare(`
|
|
207
|
+
INSERT OR IGNORE INTO creative_suggestions
|
|
208
|
+
(id, type, title, description, file, module, area, risk_level,
|
|
209
|
+
blast_radius, evidence, auto_applicable, ciclo_id)
|
|
210
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
211
|
+
`).run(
|
|
212
|
+
id, suggestion.type, suggestion.title,
|
|
213
|
+
suggestion.description || '', suggestion.file || null,
|
|
214
|
+
suggestion.module || 'global', suggestion.area || 'global',
|
|
215
|
+
typeConfig.risk, suggestion.blast_radius || 0,
|
|
216
|
+
JSON.stringify(suggestion.evidence || []),
|
|
217
|
+
autoApplicable ? 1 : 0, cicloId
|
|
218
|
+
);
|
|
219
|
+
} catch {}
|
|
220
|
+
|
|
221
|
+
return { id, auto_applicable: autoApplicable };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ─── DETECTAR OPORTUNIDADES ───────────────────────────────────────────────────
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Analiza el proyecto y detecta oportunidades de mejora.
|
|
228
|
+
* Llamar después de cada ciclo completado.
|
|
229
|
+
*/
|
|
230
|
+
function detectOpportunities(db, projectRoot, cicloId, context = {}) {
|
|
231
|
+
const suggestions = [];
|
|
232
|
+
|
|
233
|
+
if (!db) return suggestions;
|
|
234
|
+
|
|
235
|
+
// ── 1. Código sin tests (missing test coverage) ───────────────────────────
|
|
236
|
+
try {
|
|
237
|
+
const highRiskNodes = db.prepare(`
|
|
238
|
+
SELECT titulo, area, contenido FROM nodos
|
|
239
|
+
WHERE tipo = 'error'
|
|
240
|
+
AND confianza IN ('ALTA', 'MEDIA')
|
|
241
|
+
AND estado = 'ACTIVO'
|
|
242
|
+
AND (vigencia_tipo = 'VIGENTE' OR vigencia_tipo IS NULL)
|
|
243
|
+
`).all();
|
|
244
|
+
|
|
245
|
+
for (const node of highRiskNodes.slice(0, 5)) {
|
|
246
|
+
// Verificar si existe un contrato que cubra esta área
|
|
247
|
+
const contractExists = db.prepare(`
|
|
248
|
+
SELECT COUNT(*) as n FROM verified_contracts
|
|
249
|
+
WHERE module = ? AND status IN ('verified', 'protected')
|
|
250
|
+
`).get(node.area)?.n || 0;
|
|
251
|
+
|
|
252
|
+
if (!contractExists) {
|
|
253
|
+
const s = addSuggestion(db, {
|
|
254
|
+
type: 'MISSING_TEST',
|
|
255
|
+
title: `No verified contract for error area: ${node.area}`,
|
|
256
|
+
description: `Error "${node.titulo}" has no verified contract protecting it. Consider adding tests.`,
|
|
257
|
+
module: node.area,
|
|
258
|
+
risk_level: 'MEDIUM',
|
|
259
|
+
evidence: [{ type: 'error_node', titulo: node.titulo }],
|
|
260
|
+
}, cicloId);
|
|
261
|
+
if (s) suggestions.push(s);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
} catch {}
|
|
265
|
+
|
|
266
|
+
// ── 2. Módulos con alta tasa de regresiones ───────────────────────────────
|
|
267
|
+
try {
|
|
268
|
+
const regressionProne = db.prepare(`
|
|
269
|
+
SELECT module, COUNT(*) as fails
|
|
270
|
+
FROM verified_contracts
|
|
271
|
+
WHERE failure_count > 0
|
|
272
|
+
GROUP BY module
|
|
273
|
+
HAVING fails >= 2
|
|
274
|
+
ORDER BY fails DESC
|
|
275
|
+
LIMIT 5
|
|
276
|
+
`).all();
|
|
277
|
+
|
|
278
|
+
for (const mod of regressionProne) {
|
|
279
|
+
const s = addSuggestion(db, {
|
|
280
|
+
type: 'FRAGILITY',
|
|
281
|
+
title: `Module "${mod.module}" has ${mod.fails} contract failures — fragility detected`,
|
|
282
|
+
description: `This module has had repeated contract violations. Consider architecture review.`,
|
|
283
|
+
module: mod.module,
|
|
284
|
+
risk_level: 'HIGH',
|
|
285
|
+
evidence: [{ type: 'regression_count', count: mod.fails }],
|
|
286
|
+
}, cicloId);
|
|
287
|
+
if (s) suggestions.push(s);
|
|
288
|
+
}
|
|
289
|
+
} catch {}
|
|
290
|
+
|
|
291
|
+
// ── 3. Patrones repetidos sin abstracción ─────────────────────────────────
|
|
292
|
+
try {
|
|
293
|
+
const patterns = db.prepare(`
|
|
294
|
+
SELECT titulo, area, aplicado FROM nodos
|
|
295
|
+
WHERE tipo = 'patron'
|
|
296
|
+
AND confianza = 'ALTA'
|
|
297
|
+
AND aplicado >= 5
|
|
298
|
+
AND (vigencia_tipo = 'VIGENTE' OR vigencia_tipo IS NULL)
|
|
299
|
+
ORDER BY aplicado DESC LIMIT 5
|
|
300
|
+
`).all();
|
|
301
|
+
|
|
302
|
+
for (const p of patterns) {
|
|
303
|
+
const s = addSuggestion(db, {
|
|
304
|
+
type: 'ABSTRACTION',
|
|
305
|
+
title: `Pattern "${p.titulo}" applied ${p.aplicado}× — abstraction opportunity`,
|
|
306
|
+
description: `This pattern appears frequently in ${p.area}. Consider extracting it into a shared utility.`,
|
|
307
|
+
module: p.area,
|
|
308
|
+
risk_level: 'LOW',
|
|
309
|
+
evidence: [{ type: 'pattern', titulo: p.titulo, count: p.aplicado }],
|
|
310
|
+
}, cicloId);
|
|
311
|
+
if (s) suggestions.push(s);
|
|
312
|
+
}
|
|
313
|
+
} catch {}
|
|
314
|
+
|
|
315
|
+
// ── 4. Causal edges de regresiones → oportunidad de refactor ─────────────
|
|
316
|
+
try {
|
|
317
|
+
const regressions = db.prepare(`
|
|
318
|
+
SELECT desde_entidad, COUNT(*) as n
|
|
319
|
+
FROM relaciones_semanticas
|
|
320
|
+
WHERE tipo = 'regressed_by'
|
|
321
|
+
AND (invalid_at IS NULL OR invalid_at = '')
|
|
322
|
+
GROUP BY desde_entidad
|
|
323
|
+
HAVING n >= 2
|
|
324
|
+
LIMIT 5
|
|
325
|
+
`).all();
|
|
326
|
+
|
|
327
|
+
for (const r of regressions) {
|
|
328
|
+
const s = addSuggestion(db, {
|
|
329
|
+
type: 'REFACTOR',
|
|
330
|
+
title: `"${r.desde_entidad}" regressed ${r.n} times — refactor candidate`,
|
|
331
|
+
description: `This component has caused ${r.n} regressions. A refactor could reduce coupling and fragility.`,
|
|
332
|
+
file: r.desde_entidad,
|
|
333
|
+
module: r.desde_entidad.split('/')[0] || 'global',
|
|
334
|
+
risk_level: 'MEDIUM',
|
|
335
|
+
evidence: [{ type: 'regression_edge', file: r.desde_entidad, count: r.n }],
|
|
336
|
+
}, cicloId);
|
|
337
|
+
if (s) suggestions.push(s);
|
|
338
|
+
}
|
|
339
|
+
} catch {}
|
|
340
|
+
|
|
341
|
+
return suggestions;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// ─── APLICAR SUGERENCIA ───────────────────────────────────────────────────────
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Aplica una sugerencia si es auto_aplicable y el nivel lo permite.
|
|
348
|
+
* Level 2: aplica si blast_radius ≤ 3 y no toca contratos PROTECTED.
|
|
349
|
+
*/
|
|
350
|
+
function applySuggestion(db, suggestionId, projectRoot, cicloId) {
|
|
351
|
+
if (!db) return { applied: false, reason: 'DB unavailable' };
|
|
352
|
+
|
|
353
|
+
const suggestion = db.prepare('SELECT * FROM creative_suggestions WHERE id = ?').get(suggestionId);
|
|
354
|
+
if (!suggestion) return { applied: false, reason: 'Suggestion not found' };
|
|
355
|
+
if (suggestion.applied) return { applied: false, reason: 'Already applied' };
|
|
356
|
+
if (suggestion.dismissed) return { applied: false, reason: 'Dismissed' };
|
|
357
|
+
|
|
358
|
+
const level = getCurrentLevel(db, projectRoot);
|
|
359
|
+
|
|
360
|
+
// Verificar que el nivel permite auto-aplicar
|
|
361
|
+
if (level.level < 2 && !suggestion.auto_applicable) {
|
|
362
|
+
return { applied: false, reason: `Level ${level.level} (${level.name}) — apply manually or upgrade to Level 2` };
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Verificar blast radius
|
|
366
|
+
if (suggestion.blast_radius > 3) {
|
|
367
|
+
return { applied: false, reason: `Blast radius ${suggestion.blast_radius} > 3 — requires manual review` };
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Marcar como aplicada
|
|
371
|
+
db.prepare(`
|
|
372
|
+
UPDATE creative_suggestions SET applied = 1, applied_at = datetime('now')
|
|
373
|
+
WHERE id = ?
|
|
374
|
+
`).run(suggestionId);
|
|
375
|
+
|
|
376
|
+
// Registrar como creative_win
|
|
377
|
+
db.prepare(`
|
|
378
|
+
INSERT INTO creative_wins (suggestion_id, description, type, module, file, impact, ciclo_id)
|
|
379
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
380
|
+
`).run(
|
|
381
|
+
suggestionId, suggestion.title, suggestion.type,
|
|
382
|
+
suggestion.module, suggestion.file,
|
|
383
|
+
`Applied at level ${level.level}`, cicloId
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
// Registrar en memoria como patrón
|
|
387
|
+
try {
|
|
388
|
+
db.prepare(`
|
|
389
|
+
INSERT OR IGNORE INTO nodos (tipo, titulo, contenido, area, confianza, estado, vigencia_tipo, fecha_creacion, fecha_update)
|
|
390
|
+
VALUES ('creative_win', ?, ?, ?, 'MEDIA', 'ACTIVO', 'VIGENTE', datetime('now'), datetime('now'))
|
|
391
|
+
`).run(
|
|
392
|
+
suggestion.title,
|
|
393
|
+
`Creative improvement applied at level ${level.level}: ${suggestion.description}`,
|
|
394
|
+
suggestion.module
|
|
395
|
+
);
|
|
396
|
+
} catch {}
|
|
397
|
+
|
|
398
|
+
return { applied: true, suggestion_id: suggestionId, level_used: level.level };
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// ─── REPORTE DE SUGERENCIAS ───────────────────────────────────────────────────
|
|
402
|
+
|
|
403
|
+
function getSuggestions(db, module) {
|
|
404
|
+
if (!db) return [];
|
|
405
|
+
try {
|
|
406
|
+
const query = module
|
|
407
|
+
? `SELECT * FROM creative_suggestions WHERE dismissed=0 AND applied=0 AND module=? ORDER BY created_at DESC LIMIT 20`
|
|
408
|
+
: `SELECT * FROM creative_suggestions WHERE dismissed=0 AND applied=0 ORDER BY created_at DESC LIMIT 20`;
|
|
409
|
+
return module ? db.prepare(query).all(module) : db.prepare(query).all();
|
|
410
|
+
} catch { return []; }
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function getCreativeWins(db, limit = 10) {
|
|
414
|
+
if (!db) return [];
|
|
415
|
+
try {
|
|
416
|
+
return db.prepare(`SELECT * FROM creative_wins ORDER BY created_at DESC LIMIT ?`).all(limit);
|
|
417
|
+
} catch { return []; }
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// ─── INTEGRACIÓN CON CICLO ────────────────────────────────────────────────────
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Punto de entrada principal. Llamar al final de cada ciclo completado.
|
|
424
|
+
* Detecta oportunidades y aplica las auto-aplicables según el nivel.
|
|
425
|
+
*/
|
|
426
|
+
function runCreativePass(db, projectRoot, cicloId, context = {}) {
|
|
427
|
+
if (!db) return { level: 1, suggestions: [], auto_applied: [] };
|
|
428
|
+
|
|
429
|
+
migrateSchema(db);
|
|
430
|
+
|
|
431
|
+
const level = getCurrentLevel(db, projectRoot);
|
|
432
|
+
const suggestions = detectOpportunities(db, projectRoot, cicloId, context);
|
|
433
|
+
|
|
434
|
+
const autoApplied = [];
|
|
435
|
+
|
|
436
|
+
// En nivel 2+, aplicar auto-aplicables
|
|
437
|
+
if (level.level >= 2) {
|
|
438
|
+
const autoApplicable = db.prepare(`
|
|
439
|
+
SELECT id FROM creative_suggestions
|
|
440
|
+
WHERE auto_applicable = 1 AND applied = 0 AND dismissed = 0
|
|
441
|
+
AND blast_radius <= 3
|
|
442
|
+
ORDER BY created_at DESC LIMIT 5
|
|
443
|
+
`).all();
|
|
444
|
+
|
|
445
|
+
for (const s of autoApplicable) {
|
|
446
|
+
const result = applySuggestion(db, s.id, projectRoot, cicloId);
|
|
447
|
+
if (result.applied) autoApplied.push(s.id);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return {
|
|
452
|
+
level: level.level,
|
|
453
|
+
level_name: level.name,
|
|
454
|
+
level_source: level.source,
|
|
455
|
+
new_suggestions: suggestions.length,
|
|
456
|
+
auto_applied: autoApplied.length,
|
|
457
|
+
contracts_needed_for_level2: level.contracts_needed || 0,
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// ─── CLI ──────────────────────────────────────────────────────────────────────
|
|
462
|
+
|
|
463
|
+
if (require.main === module) {
|
|
464
|
+
const [,, cmd, ...args] = process.argv;
|
|
465
|
+
const projectRoot = process.cwd();
|
|
466
|
+
|
|
467
|
+
const db = openDB(projectRoot);
|
|
468
|
+
if (db) migrateSchema(db);
|
|
469
|
+
|
|
470
|
+
switch (cmd) {
|
|
471
|
+
case 'level': {
|
|
472
|
+
const level = getCurrentLevel(db, projectRoot);
|
|
473
|
+
console.log('\n══════════════════════════════════════════════');
|
|
474
|
+
console.log(' Creative Engine — Current Level');
|
|
475
|
+
console.log('══════════════════════════════════════════════');
|
|
476
|
+
console.log(` Level: ${level.level} — ${level.name}`);
|
|
477
|
+
console.log(` Source: ${level.source}`);
|
|
478
|
+
console.log(` Auto-apply: ${level.auto_apply ? '✅ Yes (LOW risk, LOCAL changes)' : '❌ No — suggestions only'}`);
|
|
479
|
+
if (level.contracts_needed > 0) {
|
|
480
|
+
console.log(` → Need ${level.contracts_needed} more verified contracts to reach Level 2`);
|
|
481
|
+
console.log(` → Run more cycles and let contract-guard build the base`);
|
|
482
|
+
}
|
|
483
|
+
console.log('══════════════════════════════════════════════\n');
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
case 'suggest': {
|
|
488
|
+
const suggestions = getSuggestions(db, args[0]);
|
|
489
|
+
const icons = { FRAGILITY: '⚠️', MISSING_TEST: '🧪', REFACTOR: '🔧', PATTERN: '📐', OPPORTUNITY: '💡', ABSTRACTION: '🏗️', DEAD_CODE: '🗑️', SIMPLIFICATION: '✂️', ARCHITECTURE: '🏛️' };
|
|
490
|
+
console.log(`\nCreative Suggestions${args[0] ? ` [${args[0]}]` : ''} (${suggestions.length}):\n`);
|
|
491
|
+
suggestions.forEach(s => {
|
|
492
|
+
const icon = icons[s.type] || '💡';
|
|
493
|
+
const autoTag = s.auto_applicable ? ' [AUTO]' : '';
|
|
494
|
+
console.log(` ${icon} [${s.id}]${autoTag} ${s.title}`);
|
|
495
|
+
console.log(` Type: ${s.type} | Risk: ${s.risk_level} | Module: ${s.module}`);
|
|
496
|
+
if (s.description) console.log(` ${s.description.substring(0, 100)}`);
|
|
497
|
+
console.log();
|
|
498
|
+
});
|
|
499
|
+
if (suggestions.length === 0) console.log(' No suggestions — run a few cycles first.\n');
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
case 'apply': {
|
|
504
|
+
const id = args[0];
|
|
505
|
+
if (!id) { console.error('Uso: creative-engine.cjs apply <suggestion_id>'); break; }
|
|
506
|
+
const result = applySuggestion(db, id, projectRoot, `manual-${Date.now()}`);
|
|
507
|
+
console.log(result.applied
|
|
508
|
+
? `✅ Applied suggestion ${id}`
|
|
509
|
+
: `❌ Not applied: ${result.reason}`
|
|
510
|
+
);
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
case 'dismiss': {
|
|
515
|
+
const id = args[0];
|
|
516
|
+
if (!id) { console.error('Uso: creative-engine.cjs dismiss <suggestion_id>'); break; }
|
|
517
|
+
if (db) db.prepare('UPDATE creative_suggestions SET dismissed=1 WHERE id=?').run(id);
|
|
518
|
+
console.log(`✅ Dismissed suggestion ${id}`);
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
case 'wins': {
|
|
523
|
+
const wins = getCreativeWins(db);
|
|
524
|
+
console.log(`\nCreative Wins (${wins.length}):\n`);
|
|
525
|
+
wins.forEach(w => {
|
|
526
|
+
console.log(` ✨ [${w.type}] ${w.description}`);
|
|
527
|
+
console.log(` Module: ${w.module} | ${w.created_at?.split('T')[0]}`);
|
|
528
|
+
});
|
|
529
|
+
if (wins.length === 0) console.log(' No creative wins yet.\n');
|
|
530
|
+
break;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
case 'run': {
|
|
534
|
+
const result = runCreativePass(db, projectRoot, `manual-${Date.now()}`);
|
|
535
|
+
console.log(`\nCreative Engine — Level ${result.level} (${result.level_name})`);
|
|
536
|
+
console.log(`New suggestions: ${result.new_suggestions}`);
|
|
537
|
+
console.log(`Auto-applied: ${result.auto_applied}`);
|
|
538
|
+
if (result.contracts_needed_for_level2 > 0) {
|
|
539
|
+
console.log(`Level 2 in: ${result.contracts_needed_for_level2} more verified contracts`);
|
|
540
|
+
}
|
|
541
|
+
break;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
default:
|
|
545
|
+
console.log('Uso: node creative-engine.cjs [level | suggest [module] | apply <id> | dismiss <id> | wins | run]');
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
module.exports = {
|
|
550
|
+
migrateSchema,
|
|
551
|
+
getCurrentLevel,
|
|
552
|
+
detectOpportunities,
|
|
553
|
+
addSuggestion,
|
|
554
|
+
applySuggestion,
|
|
555
|
+
runCreativePass,
|
|
556
|
+
getSuggestions,
|
|
557
|
+
getCreativeWins,
|
|
558
|
+
LEVEL_NAMES,
|
|
559
|
+
SUGGESTION_TYPES,
|
|
560
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-kdd",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.1",
|
|
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": {
|