agentic-kdd 3.1.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.
@@ -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.1.1",
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": {