@saulwade/swl-ses 2.2.0 → 2.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +199 -196
- package/README.md +597 -579
- package/agentes/arquitecto-swl.md +0 -5
- package/agentes/backend-python-swl.md +0 -5
- package/agentes/implementador-swl.md +0 -5
- package/agentes/nemesis-auditor-swl.md +0 -5
- package/agentes/orquestador-swl.md +0 -5
- package/agentes/planificador-swl.md +0 -5
- package/agentes/revisor-codigo-swl.md +0 -5
- package/bin/swl-mcp-server.js +1 -1
- package/comandos/swl/adoptar-proyecto.md +253 -258
- package/comandos/swl/aprender.md +823 -828
- package/comandos/swl/claudemd.md +234 -239
- package/comandos/swl/ejecutar-fase.md +0 -5
- package/comandos/swl/nuevo-proyecto.md +200 -205
- package/comandos/swl/release.md +19 -5
- package/comandos/swl/revisar-impacto.md +0 -5
- package/habilidades/agent-browser/SKILL.md +0 -5
- package/habilidades/angular-moderno/SKILL.md +0 -5
- package/habilidades/api-rest-diseno/SKILL.md +0 -5
- package/habilidades/aprendizaje-continuo/SKILL.md +0 -5
- package/habilidades/auth-patrones/SKILL.md +0 -5
- package/habilidades/build-errors-nextjs/SKILL.md +0 -5
- package/habilidades/changelog-generator/SKILL.md +174 -179
- package/habilidades/checklist-seguridad/SKILL.md +0 -5
- package/habilidades/contenedores-docker/SKILL.md +0 -5
- package/habilidades/datos-etl/SKILL.md +0 -5
- package/habilidades/doc-sync/SKILL.md +0 -5
- package/habilidades/extractor-de-aprendizajes/SKILL.md +0 -5
- package/habilidades/fastapi-experto/SKILL.md +0 -5
- package/habilidades/frontend-avanzado/SKILL.md +0 -5
- package/habilidades/iam-secretos/SKILL.md +0 -5
- package/habilidades/manejo-errores/SKILL.md +0 -5
- package/habilidades/mapear-codebase/SKILL.md +0 -5
- package/habilidades/meta-skills-estandar/SKILL.md +0 -5
- package/habilidades/monitoring-alertas/SKILL.md +0 -5
- package/habilidades/nextjs-experto/SKILL.md +0 -5
- package/habilidades/nextjs-testing/SKILL.md +0 -5
- package/habilidades/node-experto/SKILL.md +0 -5
- package/habilidades/orquestacion-async/SKILL.md +0 -5
- package/habilidades/patrones-python/SKILL.md +227 -232
- package/habilidades/planear-fase/SKILL.md +336 -341
- package/habilidades/postgresql-experto/SKILL.md +0 -5
- package/habilidades/prevencion-sobreingenieria/SKILL.md +0 -5
- package/habilidades/protocolo-revision-swl/SKILL.md +0 -5
- package/habilidades/react-experto/SKILL.md +0 -5
- package/habilidades/release-semver/SKILL.md +0 -5
- package/habilidades/swl-claudemd/SKILL.md +0 -5
- package/habilidades/tdd-workflow/SKILL.md +710 -715
- package/habilidades/testing-python/SKILL.md +335 -340
- package/habilidades/verificar-trabajo/SKILL.md +0 -5
- package/hooks/lib/etapa-perfil-usuario.js +1 -1
- package/hooks/lib/evolution-tracker.js +191 -35
- package/hooks/resumen-sesion.js +4 -4
- package/llms.txt +1 -1
- package/manifiestos/canonical-hashes.json +1310 -0
- package/manifiestos/modulos.json +3 -0
- package/manifiestos/skills-lock.json +70 -70
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/scripts/doctor.js +13 -0
- package/scripts/generar-canonical-hashes.js +147 -0
- package/scripts/instalador.js +140 -54
- package/scripts/lib/audit-evolved.js +76 -0
- package/scripts/lib/canonical-hash.js +94 -0
- package/scripts/lib/evolved-fuente.js +138 -0
- package/scripts/lib/manifiestos.js +1 -1
- package/scripts/publicar.js +42 -5
- package/scripts/remediar-evolved-instaladas.js +242 -0
- package/scripts/validar.js +14 -0
- package/scripts/vendor/claude-usage/__pycache__/scanner.cpython-314.pyc +0 -0
- package/scripts/verificar-evolucion.js +36 -0
- package/scripts/verificar-release.js +33 -0
- package/agentes/.evolved.json +0 -9
- package/comandos/swl/.evolved.json +0 -23
- package/habilidades/auth-patrones/.evolved.json +0 -9
- package/habilidades/extractor-de-aprendizajes/.evolved.json +0 -9
- package/habilidades/instalar-sistema/.evolved.json +0 -9
- package/habilidades/manejo-errores/.evolved.json +0 -9
- package/habilidades/node-experto/.evolved.json +0 -9
- package/habilidades/release-semver/.evolved.json +0 -9
|
@@ -2,11 +2,6 @@
|
|
|
2
2
|
name: verificar-trabajo
|
|
3
3
|
description: Verificación goal-backward del trabajo ejecutado en 4 niveles progresivos — EXISTE, SUSTANTIVO, CONECTADO, DATOS_FLUYEN. Clasifica claims en 4 tipos (TASK, FIX, TEST_OR_BUILD, FEATURE_GO) con evidencia proporcional. Detecta stubs, componentes huérfanos, integraciones rotas y flujos incompletos. Produce veredictos estructurados JSON con clasificación de riesgo (Low/Medium/High) y evidencia verificable. Soporta loop de reparación cuando el veredicto es Fail.
|
|
4
4
|
version: "1.2.1"
|
|
5
|
-
evolved: true
|
|
6
|
-
evolved-from: "1.2.0"
|
|
7
|
-
evolved-at: "2026-05-15"
|
|
8
|
-
evolved-by: "aprender"
|
|
9
|
-
evolved-note: "Gotchas v1.5.1 acumulados en este ciclo: (1) health-check post-hoc que recalcula contexto del install desde cwd actual → falso positivo (caso doctor 2026-05-15); (2) smoke E2E obligatorio para features cross-cutting (Sub-fase 11.5: transformarAgente nunca invocado pese a suite 100% verde); (3) auto-reparación debe verificar que el chequeo subsecuente ya no detecta el problema, si no → loop infinito (Sub-fase 12: doctor)."
|
|
10
5
|
herramientasPermitidas: [Read, Write, Edit, Bash, Glob, Grep]
|
|
11
6
|
exclusiones:
|
|
12
7
|
- "No cargar durante la implementación activa de una tarea; la verificación es posterior a la implementación, no concurrente."
|
|
@@ -251,7 +251,7 @@ function ejecutar(inputRaw) {
|
|
|
251
251
|
// cada sesión.
|
|
252
252
|
const señalesKw = detectarKeywords(textoUsuario)
|
|
253
253
|
.map(s => {
|
|
254
|
-
const r = scanInjection(s.snippet, '
|
|
254
|
+
const r = scanInjection(s.snippet, 'user-profile/dirty.json');
|
|
255
255
|
return { ...s, _scan: r };
|
|
256
256
|
})
|
|
257
257
|
.filter(s => {
|
|
@@ -28,6 +28,75 @@ try {
|
|
|
28
28
|
atomicWriteJSON = (p, o) => fs.writeFileSync(p, JSON.stringify(o, null, 2), 'utf8');
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
// Comparación SemVer real (Fase 16 — el bug original comparaba versiones como
|
|
32
|
+
// string). Fallback defensivo a comparación numérica por segmento.
|
|
33
|
+
let compararSemver;
|
|
34
|
+
try {
|
|
35
|
+
({ compararSemver } = require('../../scripts/lib/npm-version'));
|
|
36
|
+
} catch {
|
|
37
|
+
compararSemver = (a, b) => {
|
|
38
|
+
const pa = /^(\d+)\.(\d+)\.(\d+)/.exec(String(a));
|
|
39
|
+
const pb = /^(\d+)\.(\d+)\.(\d+)/.exec(String(b));
|
|
40
|
+
if (!pa || !pb) return null;
|
|
41
|
+
for (let i = 1; i <= 3; i++) {
|
|
42
|
+
const x = Number(pa[i]), y = Number(pb[i]);
|
|
43
|
+
if (x !== y) return x > y ? 1 : -1;
|
|
44
|
+
}
|
|
45
|
+
return 0;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Discriminador A/B por hash del cuerpo canónico (Fase 16).
|
|
50
|
+
let canonicalHash, hashCoincide;
|
|
51
|
+
try {
|
|
52
|
+
({ canonicalHash, hashCoincide } = require('../../scripts/lib/canonical-hash'));
|
|
53
|
+
} catch (e) {
|
|
54
|
+
// H3 (nemesis O5): no-fallback-silencioso. Si canonical-hash no carga, el
|
|
55
|
+
// discriminador A/B queda inhabilitado (todo evolved → conflict/merge, jamás
|
|
56
|
+
// overwrite — protector pero bloquea actualizaciones de shipped). Avisar.
|
|
57
|
+
try { process.stderr.write(`[evolution-tracker] canonical-hash no disponible (${e.code || e.message}); discriminador A/B deshabilitado → todo evolved se trata como evolución del usuario.\n`); } catch { /* noop */ }
|
|
58
|
+
canonicalHash = null;
|
|
59
|
+
hashCoincide = () => false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Ruta al manifiesto de hashes canónicos (baseline del discriminador A/B). */
|
|
63
|
+
const CANONICAL_HASHES_PATH = path.resolve(__dirname, '../../manifiestos/canonical-hashes.json');
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Carga el manifiesto de hashes canónicos. Tolera ausencia (→ {}), de modo que
|
|
67
|
+
* la ausencia de baseline degrada a estrategia protectora (conflict), nunca a
|
|
68
|
+
* overwrite. Permite override de ruta para tests.
|
|
69
|
+
* @param {string} [rutaManifiesto]
|
|
70
|
+
* @returns {object}
|
|
71
|
+
*/
|
|
72
|
+
function _cargarCanonicalHashes(rutaManifiesto) {
|
|
73
|
+
const ruta = rutaManifiesto || CANONICAL_HASHES_PATH;
|
|
74
|
+
try {
|
|
75
|
+
return JSON.parse(fs.readFileSync(ruta, 'utf8'));
|
|
76
|
+
} catch {
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Deriva la ruta relativa del componente (clave del manifiesto) a partir de la
|
|
83
|
+
* ruta del archivo ORIGEN (del paquete). El manifiesto usa claves tipo
|
|
84
|
+
* `agentes/X-swl.md`, `comandos/swl/foo.md`, `habilidades/Y/SKILL.md`,
|
|
85
|
+
* `reglas/z.md`. Devuelve null si no se reconoce un dominio versionable.
|
|
86
|
+
* @param {string} origenAbs
|
|
87
|
+
* @returns {string|null}
|
|
88
|
+
*/
|
|
89
|
+
function _rutaRelComponente(origenAbs) {
|
|
90
|
+
const partes = String(origenAbs).split(/[\\/]+/);
|
|
91
|
+
const dominios = new Set(['agentes', 'habilidades', 'comandos', 'reglas']);
|
|
92
|
+
for (let i = partes.length - 1; i >= 0; i--) {
|
|
93
|
+
if (dominios.has(partes[i])) {
|
|
94
|
+
return partes.slice(i).join('/');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
31
100
|
// ---------------------------------------------------------------------------
|
|
32
101
|
// Constantes
|
|
33
102
|
// ---------------------------------------------------------------------------
|
|
@@ -41,6 +110,7 @@ const EVOLUTION_FIELDS = {
|
|
|
41
110
|
evolvedRounds: 'evolved-rounds', // number: rondas de autoresearch (si aplica)
|
|
42
111
|
evolvedScore: 'evolved-score', // string: "baseline% → final%"
|
|
43
112
|
evolvedNote: 'evolved-note', // string: descripción breve del cambio
|
|
113
|
+
evolvedOrigin: 'evolved-origin', // string: "user" | "shipped" (defensa en profundidad)
|
|
44
114
|
};
|
|
45
115
|
|
|
46
116
|
/** Nombre del archivo sidecar para componentes sin frontmatter (reglas). */
|
|
@@ -123,7 +193,11 @@ function readEvolutionMeta(filePath) {
|
|
|
123
193
|
|
|
124
194
|
return { evolved: true, metadata };
|
|
125
195
|
} catch (_) {
|
|
126
|
-
|
|
196
|
+
// Fail-cerrado (Fase 16, REQ-16-10): un error de lectura/parse NO significa
|
|
197
|
+
// "no evolucionado". Señalamos `_readError` para que decideUpdateStrategy
|
|
198
|
+
// proteja el archivo (conflict) en lugar de sobreescribirlo. Mantenemos
|
|
199
|
+
// `evolved: false` para no contaminar el conteo de scanEvolved.
|
|
200
|
+
return { evolved: false, metadata: {}, _readError: true };
|
|
127
201
|
}
|
|
128
202
|
}
|
|
129
203
|
|
|
@@ -145,7 +219,10 @@ function _readSidecar(filePath) {
|
|
|
145
219
|
if (entry && entry.evolved) {
|
|
146
220
|
return { evolved: true, metadata: entry };
|
|
147
221
|
}
|
|
148
|
-
} catch (_) {
|
|
222
|
+
} catch (_) {
|
|
223
|
+
// Fail-cerrado (REQ-16-10): sidecar corrupto/ilegible → proteger, no overwrite.
|
|
224
|
+
return { evolved: false, metadata: {}, _readError: true };
|
|
225
|
+
}
|
|
149
226
|
|
|
150
227
|
return { evolved: false, metadata: {} };
|
|
151
228
|
}
|
|
@@ -235,10 +312,18 @@ function markAsEvolved(filePath, meta) {
|
|
|
235
312
|
// Detectar EOL del archivo para preservarlo en la escritura.
|
|
236
313
|
const eol = prefix.includes('\r\n') ? '\r\n' : '\n';
|
|
237
314
|
|
|
238
|
-
//
|
|
315
|
+
// Preservar el origen existente si el caller no lo sobreescribe (REQ-16-06):
|
|
316
|
+
// un `evolved-origin: shipped` no debe degradarse a `user` por defecto.
|
|
317
|
+
const existingOrigin = (frontmatter.match(/^evolved-origin:\s*["']?(\w+)["']?/mi) || [])[1];
|
|
318
|
+
const origin = meta.origin || existingOrigin || 'user';
|
|
319
|
+
|
|
320
|
+
// Remover campos evolved previos. Flag `i`: simétrico con la extracción
|
|
321
|
+
// case-insensitive de existingOrigin (arriba) y con readEvolutionMeta — sin
|
|
322
|
+
// él, `Evolved-Origin:` se detectaría pero no se removería, dejando campos
|
|
323
|
+
// duplicados en el frontmatter (Bug 1).
|
|
239
324
|
frontmatter = frontmatter
|
|
240
325
|
.split(/\r?\n/)
|
|
241
|
-
.filter(line => !line.match(/^evolved[-\w]*:/))
|
|
326
|
+
.filter(line => !line.match(/^evolved[-\w]*:/i))
|
|
242
327
|
.join(eol);
|
|
243
328
|
|
|
244
329
|
// Agregar campos nuevos
|
|
@@ -248,6 +333,7 @@ function markAsEvolved(filePath, meta) {
|
|
|
248
333
|
`evolved-from: "${meta.from}"`,
|
|
249
334
|
`evolved-at: "${date}"`,
|
|
250
335
|
`evolved-by: "${meta.by}"`,
|
|
336
|
+
`evolved-origin: "${origin}"`,
|
|
251
337
|
];
|
|
252
338
|
if (meta.rounds) newFields.push(`evolved-rounds: ${meta.rounds}`);
|
|
253
339
|
if (meta.score) newFields.push(`evolved-score: "${meta.score}"`);
|
|
@@ -279,11 +365,13 @@ function _writeSidecar(filePath, meta) {
|
|
|
279
365
|
}
|
|
280
366
|
} catch (_) {}
|
|
281
367
|
|
|
368
|
+
const existingOrigin = data[basename] && data[basename].evolvedOrigin;
|
|
282
369
|
data[basename] = {
|
|
283
370
|
evolved: true,
|
|
284
371
|
evolvedFrom: meta.from,
|
|
285
372
|
evolvedAt: new Date().toISOString().split('T')[0],
|
|
286
373
|
evolvedBy: meta.by,
|
|
374
|
+
evolvedOrigin: meta.origin || existingOrigin || 'user',
|
|
287
375
|
...(meta.rounds && { evolvedRounds: meta.rounds }),
|
|
288
376
|
...(meta.score && { evolvedScore: meta.score }),
|
|
289
377
|
...(meta.note && { evolvedNote: meta.note }),
|
|
@@ -305,9 +393,18 @@ function _writeSidecar(filePath, meta) {
|
|
|
305
393
|
* @param {string} versionNueva - Versión del paquete nuevo (ej: "5.2.0").
|
|
306
394
|
* @returns {{ strategy: 'preserve'|'overwrite'|'conflict', reason: string, metadata?: object }}
|
|
307
395
|
*/
|
|
308
|
-
function decideUpdateStrategy(destino, origen, versionNueva) {
|
|
396
|
+
function decideUpdateStrategy(destino, origen, versionNueva, opts = {}) {
|
|
309
397
|
const evo = readEvolutionMeta(destino);
|
|
310
398
|
|
|
399
|
+
// Fail-cerrado (REQ-16-10): metadata ilegible/corrupta → proteger, NUNCA overwrite.
|
|
400
|
+
if (evo._readError) {
|
|
401
|
+
return {
|
|
402
|
+
strategy: 'conflict',
|
|
403
|
+
reason: 'No se pudo leer/parsear la metadata de evolución del destino — se protege el archivo (no overwrite)',
|
|
404
|
+
metadata: {},
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
|
|
311
408
|
if (!evo.evolved) {
|
|
312
409
|
return { strategy: 'overwrite', reason: 'No evolucionado — actualización normal' };
|
|
313
410
|
}
|
|
@@ -316,7 +413,7 @@ function decideUpdateStrategy(destino, origen, versionNueva) {
|
|
|
316
413
|
const evolvedBy = evo.metadata.evolvedBy || evo.metadata.evolved_by || 'auto-evolución';
|
|
317
414
|
|
|
318
415
|
// Caso 1: evolved: true y no hay archivo origen disponible → preservar siempre
|
|
319
|
-
// (no podemos comparar, pero el
|
|
416
|
+
// (no podemos comparar, pero el archivo está marcado como evolucionado).
|
|
320
417
|
if (!origen || !fs.existsSync(origen)) {
|
|
321
418
|
return {
|
|
322
419
|
strategy: 'preserve',
|
|
@@ -325,39 +422,75 @@ function decideUpdateStrategy(destino, origen, versionNueva) {
|
|
|
325
422
|
};
|
|
326
423
|
}
|
|
327
424
|
|
|
328
|
-
|
|
329
|
-
// → preservar (la versión nueva no cambió el contenido base de este archivo)
|
|
425
|
+
let origenContent, destinoContent;
|
|
330
426
|
try {
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
} catch (_) {}
|
|
427
|
+
origenContent = fs.readFileSync(origen, 'utf8');
|
|
428
|
+
destinoContent = fs.readFileSync(destino, 'utf8');
|
|
429
|
+
} catch (_) {
|
|
430
|
+
// No se pudo leer alguno de los dos → proteger (fail-cerrado).
|
|
431
|
+
return {
|
|
432
|
+
strategy: 'conflict',
|
|
433
|
+
reason: 'Error de lectura al comparar origen/destino — se protege el archivo (no overwrite)',
|
|
434
|
+
metadata: evo.metadata,
|
|
435
|
+
};
|
|
436
|
+
}
|
|
344
437
|
|
|
345
|
-
// Caso
|
|
346
|
-
//
|
|
347
|
-
|
|
348
|
-
|
|
438
|
+
// Caso 2: cuerpo canónico idéntico (ignorando campos evolved, CRLF-safe)
|
|
439
|
+
// → preservar: la versión nueva no cambió el contenido base de este archivo.
|
|
440
|
+
const mismoBody = canonicalHash
|
|
441
|
+
? canonicalHash(origenContent) === canonicalHash(destinoContent)
|
|
442
|
+
: _stripEvolutionFields(origenContent).trim() === _stripEvolutionFields(destinoContent).trim();
|
|
443
|
+
if (mismoBody) {
|
|
349
444
|
return {
|
|
350
445
|
strategy: 'preserve',
|
|
351
|
-
reason:
|
|
446
|
+
reason: 'Contenido base idéntico — solo difieren los campos evolved',
|
|
447
|
+
metadata: evo.metadata,
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// El cuerpo difiere entre origen (canónico nuevo) y destino (instalado evolved).
|
|
452
|
+
// Discriminador A/B (REQ-16-05, REQ-16-06):
|
|
453
|
+
// B — shipped-evolved intacto: el cuerpo del destino coincide con el baseline
|
|
454
|
+
// de la versión `evolvedFrom` → el usuario NO lo tocó → actualización
|
|
455
|
+
// segura (overwrite con backup, lo aplica el caller).
|
|
456
|
+
// A — evolución del usuario: el cuerpo difiere del baseline (o no hay
|
|
457
|
+
// baseline para esa versión) → MERGE, jamás overwrite (invariante).
|
|
458
|
+
|
|
459
|
+
// Defensa en profundidad (REQ-16-06): una marca explícita `evolved-origin: user`
|
|
460
|
+
// gana sobre el hash. Si el componente declara que fue evolucionado por el
|
|
461
|
+
// usuario, es A definitivamente — ni siquiera una coincidencia de hash con el
|
|
462
|
+
// baseline puede degradarlo a overwrite. (Las evoluciones de usuario las marca
|
|
463
|
+
// markAsEvolved con origin 'user' por defecto.) Inversamente, `shipped` no
|
|
464
|
+
// fuerza B: aún exige que el hash baseline coincida (el hash manda para B).
|
|
465
|
+
const origin = (evo.metadata.evolvedOrigin || '').toLowerCase();
|
|
466
|
+
if (origin === 'user') {
|
|
467
|
+
return {
|
|
468
|
+
strategy: 'conflict',
|
|
469
|
+
reason: 'evolved-origin: user (marca explícita del usuario) — merge, no overwrite',
|
|
352
470
|
metadata: evo.metadata,
|
|
353
471
|
};
|
|
354
472
|
}
|
|
355
473
|
|
|
356
|
-
|
|
357
|
-
|
|
474
|
+
const rutaRel = _rutaRelComponente(origen);
|
|
475
|
+
const manifiesto = _cargarCanonicalHashes(opts.manifestPath);
|
|
476
|
+
if (rutaRel && hashCoincide(manifiesto, evolvedFrom, rutaRel, destinoContent)) {
|
|
477
|
+
return {
|
|
478
|
+
strategy: 'overwrite',
|
|
479
|
+
reason: `shipped-evolved (hash baseline de v${evolvedFrom} coincide) — actualización segura a v${versionNueva}`,
|
|
480
|
+
origin: 'shipped',
|
|
481
|
+
metadata: evo.metadata,
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Caso 3 (REGLA NUEVA): evolución del usuario o sin baseline verificable.
|
|
486
|
+
// Antes esto hacía `preserve` mudo por comparación string; ahora enruta al
|
|
487
|
+
// merge definido (el caller invoca mergeEvolved → preserve + .evolved-diff.txt).
|
|
488
|
+
const semverInfo = compararSemver(evolvedFrom, versionNueva);
|
|
358
489
|
return {
|
|
359
490
|
strategy: 'conflict',
|
|
360
|
-
reason:
|
|
491
|
+
reason: rutaRel
|
|
492
|
+
? `Evolución del usuario (cuerpo difiere del baseline de v${evolvedFrom}) — merge, no overwrite`
|
|
493
|
+
: `Evolucionado desde v${evolvedFrom} (semver ${semverInfo}) sin baseline verificable — merge, no overwrite`,
|
|
361
494
|
metadata: evo.metadata,
|
|
362
495
|
};
|
|
363
496
|
}
|
|
@@ -371,7 +504,7 @@ function decideUpdateStrategy(destino, origen, versionNueva) {
|
|
|
371
504
|
function _stripEvolutionFields(content) {
|
|
372
505
|
return content
|
|
373
506
|
.split(/\r?\n/)
|
|
374
|
-
.filter(line => !line.match(/^evolved[-\w]*:/))
|
|
507
|
+
.filter(line => !line.match(/^evolved[-\w]*:/i))
|
|
375
508
|
.join('\n');
|
|
376
509
|
}
|
|
377
510
|
|
|
@@ -441,7 +574,7 @@ const DIFF_NOISY_THRESHOLD = 50;
|
|
|
441
574
|
* error?: string
|
|
442
575
|
* }}
|
|
443
576
|
*/
|
|
444
|
-
function mergeEvolved(destino, origen, versionNueva) {
|
|
577
|
+
function mergeEvolved(destino, origen, versionNueva, opts = {}) {
|
|
445
578
|
try {
|
|
446
579
|
if (!fs.existsSync(destino) || !fs.existsSync(origen)) {
|
|
447
580
|
return { merged: false, error: 'Archivo destino u origen no existe' };
|
|
@@ -478,11 +611,31 @@ function mergeEvolved(destino, origen, versionNueva) {
|
|
|
478
611
|
}
|
|
479
612
|
}
|
|
480
613
|
|
|
481
|
-
|
|
482
|
-
//
|
|
483
|
-
//
|
|
484
|
-
// componente
|
|
614
|
+
// Diff centralizado (Fase 16, Punto 2): si opts.diffDir está presente, el
|
|
615
|
+
// diff se escribe en un directorio único de reconciliación (p. ej.
|
|
616
|
+
// `.planning/evolution/reconcile/`) con nombre plano, en vez de adyacente al
|
|
617
|
+
// componente. Esto evita ruido en `commands/`/`skills/` (donde scanners y el
|
|
618
|
+
// harness podrían detectarlo) y da una superficie única de revisión para
|
|
619
|
+
// decidir incorporación upstream. Si no, fallback adyacente (legacy).
|
|
620
|
+
const adyacenteTxt = destino.replace(/\.md$/, '.evolved-diff.txt');
|
|
485
621
|
const diffPathLegacy = destino.replace(/\.md$/, '.evolved-diff.md');
|
|
622
|
+
let diffPath = adyacenteTxt;
|
|
623
|
+
if (opts.diffDir) {
|
|
624
|
+
try { fs.mkdirSync(opts.diffDir, { recursive: true }); } catch { /* best-effort */ }
|
|
625
|
+
// Nombre plano con el directorio padre como prefijo (layout-agnóstico:
|
|
626
|
+
// sirve para runtime `agents/skills/commands/rules` y fuente
|
|
627
|
+
// `agentes/habilidades/comandos/reglas`). Evita colisión entre dominios
|
|
628
|
+
// (p. ej. agents/foo.md vs commands/foo.md → 'agents__foo' vs 'commands__foo').
|
|
629
|
+
const segs = destino.replace(/\\/g, '/').split('/').filter(Boolean);
|
|
630
|
+
const esSkill = segs[segs.length - 1] === 'SKILL.md';
|
|
631
|
+
const baseNombre = esSkill
|
|
632
|
+
? `${segs[segs.length - 3] || 'x'}__${segs[segs.length - 2]}__SKILL`
|
|
633
|
+
: `${segs[segs.length - 2] || 'x'}__${path.basename(segs[segs.length - 1], '.md')}`;
|
|
634
|
+
const nombrePlano = baseNombre.replace(/[^A-Za-z0-9_.-]/g, '_');
|
|
635
|
+
diffPath = path.join(opts.diffDir, nombrePlano + '.evolved-diff.txt');
|
|
636
|
+
// Limpiar cualquier diff adyacente previo (de versiones que no centralizaban).
|
|
637
|
+
if (fs.existsSync(adyacenteTxt)) { try { fs.unlinkSync(adyacenteTxt); } catch { /* best-effort */ } }
|
|
638
|
+
}
|
|
486
639
|
const limpiarLegacy = () => {
|
|
487
640
|
if (fs.existsSync(diffPathLegacy)) {
|
|
488
641
|
try { fs.unlinkSync(diffPathLegacy); return true; } catch { /* best-effort */ }
|
|
@@ -515,6 +668,9 @@ function mergeEvolved(destino, origen, versionNueva) {
|
|
|
515
668
|
rounds: evo.metadata.evolvedRounds ? parseInt(evo.metadata.evolvedRounds, 10) : undefined,
|
|
516
669
|
score: evo.metadata.evolvedScore,
|
|
517
670
|
note: `Re-aplicado desde v${evo.metadata.evolvedFrom || '?'} tras actualización a v${versionNueva}`,
|
|
671
|
+
// mergeEvolved solo se invoca para evolución del usuario (población A):
|
|
672
|
+
// decideUpdateStrategy enruta shipped (B) a 'overwrite', nunca aquí.
|
|
673
|
+
origin: evo.metadata.evolvedOrigin || 'user',
|
|
518
674
|
force: true,
|
|
519
675
|
});
|
|
520
676
|
return { merged: marked.marked, cleanedDiff };
|
package/hooks/resumen-sesion.js
CHANGED
|
@@ -85,10 +85,10 @@ function hooksDisparados(sessionId) {
|
|
|
85
85
|
try {
|
|
86
86
|
const planningDir = path.join(process.cwd(), '.planning');
|
|
87
87
|
const jsonlCandidatos = [
|
|
88
|
-
['
|
|
89
|
-
['auto-
|
|
90
|
-
['
|
|
91
|
-
['
|
|
88
|
+
['evolution/nudges.jsonl', 'nudge-tracker'],
|
|
89
|
+
['auto-evolution/agentes.jsonl', 'auto-evolucion'],
|
|
90
|
+
['user-profile/dirty.json', 'actualizar-perfil-usuario'],
|
|
91
|
+
['evolution/metricas.json', 'metricas-evolucion'],
|
|
92
92
|
];
|
|
93
93
|
for (const [rel, nombre] of jsonlCandidatos) {
|
|
94
94
|
const p = path.join(planningDir, rel);
|
package/llms.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# swl-ses (@saulwade/swl-ses)
|
|
2
2
|
|
|
3
|
-
> Sistema de ingeniería de software auto-evolutivo multi-runtime polyglot (SDLC completo), distribuido como paquete npm y plugin de Claude Code. 60 agentes, 182 habilidades, 44 comandos, 37 reglas base y 48 hooks. Soporta 11 lenguajes y 7 runtimes (Claude Code, OpenClaude, OpenCode, Gemini, Cursor, Codex, Copilot). Versión 2.2.
|
|
3
|
+
> Sistema de ingeniería de software auto-evolutivo multi-runtime polyglot (SDLC completo), distribuido como paquete npm y plugin de Claude Code. 60 agentes, 182 habilidades, 44 comandos, 37 reglas base y 48 hooks. Soporta 11 lenguajes y 7 runtimes (Claude Code, OpenClaude, OpenCode, Gemini, Cursor, Codex, Copilot). Versión 2.2.3.
|
|
4
4
|
|
|
5
5
|
Archivo generado por `node scripts/generar-inventario.js` — no editar a mano. Las cifras se sincronizan con INVENTARIO.md en cada regeneración.
|
|
6
6
|
|