@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
package/scripts/publicar.js
CHANGED
|
@@ -32,6 +32,32 @@ const {
|
|
|
32
32
|
const ROOT = path.resolve(__dirname, '..');
|
|
33
33
|
const PKG_PATH = path.join(ROOT, 'package.json');
|
|
34
34
|
|
|
35
|
+
// --- Log de la corrida (.planning/logs/publish-<version>-<ts>.log) -----------
|
|
36
|
+
// Captura stdout+stderr del publish para diagnóstico: npm v10 NO escribe el
|
|
37
|
+
// stdout de un script fallido (prepublishOnly/test:release) en su debug log,
|
|
38
|
+
// así que sin esto el test que rompe el publish queda invisible. Tee: eco a
|
|
39
|
+
// consola + archivo persistente.
|
|
40
|
+
let LOG_PATH = null;
|
|
41
|
+
function iniciarLog(version) {
|
|
42
|
+
try {
|
|
43
|
+
const dir = path.join(ROOT, '.planning', 'logs');
|
|
44
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
45
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
46
|
+
LOG_PATH = path.join(dir, `publish-${version}-${ts}.log`);
|
|
47
|
+
// Escritura SÍNCRONA (writeFileSync/appendFileSync): un createWriteStream
|
|
48
|
+
// async + process.exit() inmediato perdía el buffer sin volcarlo a disco
|
|
49
|
+
// (dejaba el log vacío). appendFileSync vacía en cada llamada.
|
|
50
|
+
fs.writeFileSync(LOG_PATH, `# publish ${version} — ${new Date().toISOString()}\n# argv: ${process.argv.slice(2).join(' ')}\n\n`);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
process.stderr.write(`[publicar] no se pudo abrir log: ${String(err.message).slice(0, 120)}\n`);
|
|
53
|
+
LOG_PATH = null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function logWrite(text) {
|
|
57
|
+
if (LOG_PATH && text) { try { fs.appendFileSync(LOG_PATH, text); } catch { /* best-effort */ } }
|
|
58
|
+
}
|
|
59
|
+
function cerrarLog() { /* no-op: appendFileSync ya volcó todo a disco */ }
|
|
60
|
+
|
|
35
61
|
/**
|
|
36
62
|
* Resuelve el binario de npm a invocar. Usa node + npm-cli.js cuando es
|
|
37
63
|
* posible para evitar shell:true en Windows (mismo enfoque que la lib
|
|
@@ -60,15 +86,19 @@ function npmExec(args, opts = {}) {
|
|
|
60
86
|
}
|
|
61
87
|
|
|
62
88
|
/**
|
|
63
|
-
* Variante de npmExec que captura stderr para detección
|
|
64
|
-
* estructurados (ej: EOTP)
|
|
65
|
-
*
|
|
89
|
+
* Variante de npmExec que captura stdout+stderr para (a) detección de errores
|
|
90
|
+
* estructurados (ej: EOTP) y (b) persistencia en el log de la corrida
|
|
91
|
+
* (`iniciarLog`). Ambos streams se pipean, se ecoan a consola al terminar y se
|
|
92
|
+
* vuelcan al log — así el stdout del script que rompe el publish (test:release)
|
|
93
|
+
* queda registrado. Trade-off: la salida del prepublish aparece al finalizar el
|
|
94
|
+
* spawn (no en vivo), por eso se imprime un aviso antes.
|
|
66
95
|
*
|
|
67
96
|
* Retorna { status, stderr } sin lanzar — el caller inspecciona el status.
|
|
68
97
|
*/
|
|
69
98
|
function npmSpawnCaptureStderr(args, opts = {}) {
|
|
70
99
|
const defaults = { cwd: ROOT, timeout: 300_000, env: process.env };
|
|
71
|
-
const merged = { ...defaults, ...opts, stdio: ['inherit', '
|
|
100
|
+
const merged = { ...defaults, ...opts, stdio: ['inherit', 'pipe', 'pipe'] };
|
|
101
|
+
process.stderr.write('[publicar] ejecutando npm (incluye prepublish/test:release; salida completa al finalizar y en el log)...\n');
|
|
72
102
|
let res;
|
|
73
103
|
if (NPM_CLI_JS) {
|
|
74
104
|
res = spawnSync(process.execPath, [NPM_CLI_JS, ...args], merged);
|
|
@@ -76,9 +106,13 @@ function npmSpawnCaptureStderr(args, opts = {}) {
|
|
|
76
106
|
const bin = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
77
107
|
res = spawnSync(bin, args, merged);
|
|
78
108
|
}
|
|
109
|
+
const stdout = res.stdout ? String(res.stdout) : '';
|
|
79
110
|
const stderr = res.stderr ? String(res.stderr) : '';
|
|
80
|
-
//
|
|
111
|
+
// Eco a consola + volcado al log (stdout primero, luego stderr).
|
|
112
|
+
if (stdout) process.stdout.write(stdout);
|
|
81
113
|
if (stderr) process.stderr.write(stderr);
|
|
114
|
+
logWrite(stdout);
|
|
115
|
+
logWrite(stderr);
|
|
82
116
|
return { status: res.status, signal: res.signal, stderr, error: res.error };
|
|
83
117
|
}
|
|
84
118
|
|
|
@@ -482,6 +516,7 @@ function reportarResultado(pkg, opts, npmjsOk, githubOk) {
|
|
|
482
516
|
function main() {
|
|
483
517
|
const opts = parsearArgs(process.argv.slice(2));
|
|
484
518
|
const pkg = leerPkg();
|
|
519
|
+
iniciarLog(pkg.version);
|
|
485
520
|
imprimirEncabezado(pkg.version, opts.dryRun);
|
|
486
521
|
avisarSiHayCambiosSinCommit(opts.dryRun);
|
|
487
522
|
|
|
@@ -489,6 +524,8 @@ function main() {
|
|
|
489
524
|
const githubOk = opts.soloNpmjs ? true : publicarGitHub(pkg, opts.dryRun);
|
|
490
525
|
|
|
491
526
|
reportarResultado(pkg, opts, npmjsOk, githubOk);
|
|
527
|
+
if (LOG_PATH) process.stdout.write(`\nLog de la corrida: ${path.relative(ROOT, LOG_PATH).replace(/\\/g, '/')}\n`);
|
|
528
|
+
cerrarLog();
|
|
492
529
|
process.exit(npmjsOk && githubOk ? 0 : 1);
|
|
493
530
|
}
|
|
494
531
|
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* remediar-evolved-instaladas — Descongela copias instaladas con marcadores
|
|
6
|
+
* evolved espurios (Fase 16, T-25, REQ-16-14).
|
|
7
|
+
*
|
|
8
|
+
* Las instalaciones previas a Fase 16 quedaron con componentes `evolved:true`
|
|
9
|
+
* shippeados de fábrica que el instalador preservaba para siempre (bug). Este
|
|
10
|
+
* sub-comando los reclasifica en la máquina del usuario:
|
|
11
|
+
*
|
|
12
|
+
* - B (shipped intacto): el cuerpo canónico instalado coincide con el baseline
|
|
13
|
+
* de su `evolved-from` (manifiesto actual o `git show v<from>:<ruta>`).
|
|
14
|
+
* → el usuario NO lo tocó → actualizar al canónico actual (con backup + auditoría).
|
|
15
|
+
* - A (evolución del usuario): el cuerpo difiere del baseline, o no hay baseline
|
|
16
|
+
* verificable. → PRESERVAR (jamás overwrite). Invariante.
|
|
17
|
+
*
|
|
18
|
+
* SEGURO por defecto: `--dry-run` (solo reporta). Requiere `--apply` explícito.
|
|
19
|
+
* Ante CUALQUIER duda (sin baseline, sin fuente, error) clasifica A (preserva).
|
|
20
|
+
*
|
|
21
|
+
* Uso:
|
|
22
|
+
* node scripts/remediar-evolved-instaladas.js --target=~/.claude (dry-run)
|
|
23
|
+
* node scripts/remediar-evolved-instaladas.js --target=~/.claude --apply (aplica)
|
|
24
|
+
*
|
|
25
|
+
* Zero-deps salvo git (para baseline histórico). Layout runtime: claude.
|
|
26
|
+
*
|
|
27
|
+
* @module scripts/remediar-evolved-instaladas
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const fs = require('fs');
|
|
31
|
+
const os = require('os');
|
|
32
|
+
const path = require('path');
|
|
33
|
+
const { spawnSync } = require('child_process');
|
|
34
|
+
|
|
35
|
+
const { canonicalHash } = require('./lib/canonical-hash');
|
|
36
|
+
const { auditarEscritura } = require('./lib/audit-evolved');
|
|
37
|
+
const { mergeEvolved } = require('../hooks/lib/evolution-tracker');
|
|
38
|
+
|
|
39
|
+
const RAIZ_PKG = path.resolve(__dirname, '..');
|
|
40
|
+
|
|
41
|
+
// Mapeo dir-runtime (claude) → dir-fuente (clave del manifiesto / git).
|
|
42
|
+
const MAP_DIRS = [
|
|
43
|
+
{ runtime: 'agents', fuente: 'agentes', skill: false },
|
|
44
|
+
{ runtime: 'commands/swl', fuente: 'comandos/swl', skill: false },
|
|
45
|
+
{ runtime: 'rules', fuente: 'reglas', skill: false },
|
|
46
|
+
{ runtime: 'skills', fuente: 'habilidades', skill: true },
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
function expandirTarget(t) {
|
|
50
|
+
if (!t) return path.join(os.homedir(), '.claude');
|
|
51
|
+
if (t === '~' || t.startsWith('~/') || t.startsWith('~\\')) return path.join(os.homedir(), t.slice(1).replace(/^[/\\]/, ''));
|
|
52
|
+
return path.resolve(t);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function leerEvolved(content) {
|
|
56
|
+
const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
|
|
57
|
+
if (!m) return { evolved: false };
|
|
58
|
+
const fm = m[1];
|
|
59
|
+
if (!/^evolved:\s*(true|yes)\b/mi.test(fm)) return { evolved: false };
|
|
60
|
+
const from = (/^evolved-from:\s*["']?([^"'\n\r]+)/mi.exec(fm) || [])[1];
|
|
61
|
+
// CRÍTICO (nemesis): extraer evolved-origin — sin esto, la defensa
|
|
62
|
+
// 'evolved-origin: user fuerza A' era letra muerta aquí (ev.origin undefined).
|
|
63
|
+
const origin = (/^evolved-origin:\s*["']?([^"'\n\r]+)/mi.exec(fm) || [])[1];
|
|
64
|
+
return { evolved: true, from: from ? from.trim() : '', origin: origin ? origin.trim() : '' };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Baseline canónico de un componente en una versión: manifiesto actual o git. */
|
|
68
|
+
function baselineCanonico(manifiesto, from, sourceRel) {
|
|
69
|
+
if (manifiesto && manifiesto[from] && manifiesto[from][sourceRel]) return manifiesto[from][sourceRel];
|
|
70
|
+
if (!from) return null;
|
|
71
|
+
const r = spawnSync('git', ['show', `v${from}:${sourceRel}`], { cwd: RAIZ_PKG, encoding: 'utf8' });
|
|
72
|
+
if (r.status !== 0 || !r.stdout) return null;
|
|
73
|
+
return canonicalHash(r.stdout);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Conjunto de hashes canónicos que el archivo `sourceRel` tuvo a lo largo de
|
|
78
|
+
* TODA la historia del repo madre (siguiendo renames). Si el cuerpo instalado
|
|
79
|
+
* coincide con cualquiera de ellos, el componente es contenido shipped del repo
|
|
80
|
+
* (ya incorporado en algún momento → B), no una evolución inventada por el
|
|
81
|
+
* usuario (Punto 1: "analizar si ya lo incorporas al proyecto madre").
|
|
82
|
+
* @param {string} sourceRel
|
|
83
|
+
* @param {number} [maxCommits=120]
|
|
84
|
+
* @returns {Set<string>}
|
|
85
|
+
*/
|
|
86
|
+
function hashesHistoricos(sourceRel, maxCommits = 120) {
|
|
87
|
+
const set = new Set();
|
|
88
|
+
const GIT_OPTS = { cwd: RAIZ_PKG, encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 };
|
|
89
|
+
const log = spawnSync('git', ['log', '--all', '--follow', '--pretty=%H', '--', sourceRel], GIT_OPTS);
|
|
90
|
+
if (log.status !== 0 || !log.stdout) return set;
|
|
91
|
+
const commits = log.stdout.trim().split('\n').filter(Boolean).slice(0, maxCommits);
|
|
92
|
+
for (const c of commits) {
|
|
93
|
+
// status !== 0 es esperado en commits pre-rename (la ruta nueva no existía):
|
|
94
|
+
// se saltan, no son error. Solo se agregan blobs realmente presentes.
|
|
95
|
+
const show = spawnSync('git', ['show', `${c}:${sourceRel}`], GIT_OPTS);
|
|
96
|
+
if (show.status === 0 && show.stdout) set.add(canonicalHash(show.stdout));
|
|
97
|
+
}
|
|
98
|
+
return set;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function cargarManifiesto() {
|
|
102
|
+
try { return JSON.parse(fs.readFileSync(path.join(RAIZ_PKG, 'manifiestos', 'canonical-hashes.json'), 'utf8')); }
|
|
103
|
+
catch { return {}; }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Lista componentes evolved instalados en el target, con su mapeo a fuente. */
|
|
107
|
+
function escanear(target) {
|
|
108
|
+
const items = [];
|
|
109
|
+
for (const { runtime, fuente, skill } of MAP_DIRS) {
|
|
110
|
+
const dir = path.join(target, runtime);
|
|
111
|
+
if (!fs.existsSync(dir)) continue;
|
|
112
|
+
if (skill) {
|
|
113
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
114
|
+
if (!e.isDirectory()) continue;
|
|
115
|
+
const skillMd = path.join(dir, e.name, 'SKILL.md');
|
|
116
|
+
if (fs.existsSync(skillMd)) items.push({ archivo: skillMd, sourceRel: `${fuente}/${e.name}/SKILL.md`, esDir: true, dir: path.join(dir, e.name) });
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
120
|
+
if (e.isFile() && e.name.endsWith('.md')) items.push({ archivo: path.join(dir, e.name), sourceRel: `${fuente}/${e.name}`, esDir: false });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return items;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function backupSimple(target, archivo, ts) {
|
|
128
|
+
const rel = path.relative(target, archivo);
|
|
129
|
+
const dest = path.join(target, '.swl-evolved-backups', ts, rel);
|
|
130
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
131
|
+
fs.copyFileSync(archivo, dest);
|
|
132
|
+
return dest;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function remediar({ target, apply = false } = {}) {
|
|
136
|
+
target = expandirTarget(target);
|
|
137
|
+
const manifiesto = cargarManifiesto();
|
|
138
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
139
|
+
// El target es un dir de config runtime (~/.claude, ~/.codex), NO un proyecto:
|
|
140
|
+
// los artefactos de gobernanza evolved van bajo `.swl-evolved-backups/` (donde
|
|
141
|
+
// ya viven los backups), sin crear un `.planning/` de proyecto ahí (C-3).
|
|
142
|
+
const reconcileDir = path.join(target, '.swl-evolved-backups', 'reconcile');
|
|
143
|
+
const resumen = { target, A: [], B: [], errores: [], reconcileDir };
|
|
144
|
+
|
|
145
|
+
for (const item of escanear(target)) {
|
|
146
|
+
let content;
|
|
147
|
+
try { content = fs.readFileSync(item.archivo, 'utf8'); } catch (e) { resumen.errores.push({ archivo: item.sourceRel, error: e.message }); continue; }
|
|
148
|
+
const ev = leerEvolved(content);
|
|
149
|
+
if (!ev.evolved) continue;
|
|
150
|
+
|
|
151
|
+
const fuenteAbs = path.join(RAIZ_PKG, item.sourceRel);
|
|
152
|
+
const instaladoHash = canonicalHash(content);
|
|
153
|
+
|
|
154
|
+
// Clasificación B (shipped → ya incorporado en el repo madre):
|
|
155
|
+
// (1) marca explícita evolved-origin: user → A definitivo (defensa en profundidad).
|
|
156
|
+
// (2) coincide con baseline del manifiesto / git show v<from>.
|
|
157
|
+
// (3) coincide con CUALQUIER estado histórico del archivo en el repo (Punto 1).
|
|
158
|
+
let esB = false;
|
|
159
|
+
let evidencia = '';
|
|
160
|
+
if ((ev.origin || '').toLowerCase() === 'user') {
|
|
161
|
+
evidencia = 'evolved-origin: user (marca explícita)';
|
|
162
|
+
} else {
|
|
163
|
+
const baseline = baselineCanonico(manifiesto, ev.from, item.sourceRel);
|
|
164
|
+
if (baseline && instaladoHash === baseline) { esB = true; evidencia = `baseline v${ev.from} coincide`; }
|
|
165
|
+
else if (hashesHistoricos(item.sourceRel).has(instaladoHash)) { esB = true; evidencia = 'coincide con un estado histórico del repo madre'; }
|
|
166
|
+
else evidencia = baseline ? 'cuerpo difiere de todo estado del repo (edición del usuario)' : 'sin baseline ni coincidencia histórica';
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (!esB) {
|
|
170
|
+
// A — evolución del usuario. PRESERVAR + diff centralizado para revisión upstream.
|
|
171
|
+
let diffRel = null;
|
|
172
|
+
if (apply && fs.existsSync(fuenteAbs)) {
|
|
173
|
+
const m = mergeEvolved(item.archivo, fuenteAbs, 'actual', { diffDir: reconcileDir });
|
|
174
|
+
if (m.merged && m.diffPath) diffRel = path.relative(target, m.diffPath).replace(/\\/g, '/');
|
|
175
|
+
}
|
|
176
|
+
resumen.A.push({ archivo: item.sourceRel, from: ev.from, razon: evidencia, diff: diffRel });
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// B — shipped intacto. Actualizar al canónico actual del paquete.
|
|
181
|
+
if (!fs.existsSync(fuenteAbs)) { resumen.errores.push({ archivo: item.sourceRel, error: 'fuente actual no existe' }); continue; }
|
|
182
|
+
if (apply) {
|
|
183
|
+
try {
|
|
184
|
+
const hashFuente = canonicalHash(fs.readFileSync(fuenteAbs, 'utf8')); // antes del copy
|
|
185
|
+
const backupPath = backupSimple(target, item.archivo, ts); // backup PRECEDE al overwrite
|
|
186
|
+
fs.copyFileSync(fuenteAbs, item.archivo); // fuente limpio (sin evolved-*) → descongelado
|
|
187
|
+
auditarEscritura({ archivo: item.sourceRel, clasificacion: 'B', accion: 'overwrite', hashAntes: instaladoHash, hashDespues: hashFuente, backupPath, evidencia: `remediación: ${evidencia}`, cwd: target, subdir: '.swl-evolved-backups' });
|
|
188
|
+
} catch (e) { resumen.errores.push({ archivo: item.sourceRel, error: e.message }); continue; }
|
|
189
|
+
}
|
|
190
|
+
resumen.B.push({ archivo: item.sourceRel, from: ev.from, razon: evidencia });
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (apply) escribirIndiceReconcile(resumen);
|
|
194
|
+
return resumen;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** Escribe un índice consolidado de reconciliación (superficie de revisión upstream). */
|
|
198
|
+
function escribirIndiceReconcile(resumen) {
|
|
199
|
+
if (resumen.A.length === 0) return;
|
|
200
|
+
try {
|
|
201
|
+
fs.mkdirSync(resumen.reconcileDir, { recursive: true });
|
|
202
|
+
const lineas = [
|
|
203
|
+
'# Reconciliación de evoluciones del usuario (revisión upstream)',
|
|
204
|
+
'',
|
|
205
|
+
'Estos componentes difieren de todo estado conocido del repo madre — son',
|
|
206
|
+
'ediciones locales genuinas. Revisa cada diff y decide si incorporarlo al',
|
|
207
|
+
'repo con `/swl:aprender` o `/swl:autoresearch`, o descartarlo.',
|
|
208
|
+
'',
|
|
209
|
+
'| Componente | evolved-from | Diff | Motivo |',
|
|
210
|
+
'|---|---|---|---|',
|
|
211
|
+
...resumen.A.map(a => `| ${a.archivo} | v${a.from || '?'} | ${a.diff || '—'} | ${a.razon} |`),
|
|
212
|
+
'',
|
|
213
|
+
];
|
|
214
|
+
fs.writeFileSync(path.join(resumen.reconcileDir, 'INDEX.md'), lineas.join('\n'), 'utf8');
|
|
215
|
+
} catch { /* best-effort */ }
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function imprimir(r, apply) {
|
|
219
|
+
process.stdout.write(`\n=== Remediación evolved instaladas — target: ${r.target} ===\n`);
|
|
220
|
+
process.stdout.write(`Modo: ${apply ? 'APPLY (escribe)' : 'DRY-RUN (solo reporta)'}\n\n`);
|
|
221
|
+
process.stdout.write(`B (shipped intacto → ${apply ? 'actualizado' : 'se actualizaría'}): ${r.B.length}\n`);
|
|
222
|
+
for (const b of r.B) process.stdout.write(` ⇪ ${b.archivo} (from v${b.from})\n`);
|
|
223
|
+
process.stdout.write(`\nA (evolución del usuario → PRESERVADO): ${r.A.length}\n`);
|
|
224
|
+
for (const a of r.A) process.stdout.write(` ★ ${a.archivo} (from v${a.from || '?'}) — ${a.razon}\n`);
|
|
225
|
+
if (r.errores.length) {
|
|
226
|
+
process.stdout.write(`\nErrores: ${r.errores.length}\n`);
|
|
227
|
+
for (const e of r.errores) process.stdout.write(` ! ${e.archivo}: ${e.error}\n`);
|
|
228
|
+
}
|
|
229
|
+
if (apply && r.A.length) process.stdout.write(`\nDiffs de reconciliación (revisión upstream): ${path.relative(r.target, r.reconcileDir).replace(/\\/g, '/')}/INDEX.md\n`);
|
|
230
|
+
if (!apply && r.B.length) process.stdout.write(`\n→ Para aplicar: agregar --apply (backups, diffs y AUDITORIA centralizados en <target>/.swl-evolved-backups/).\n`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (require.main === module) {
|
|
234
|
+
const args = process.argv.slice(2);
|
|
235
|
+
const targetArg = (args.find(a => a.startsWith('--target=')) || '').slice('--target='.length) || undefined;
|
|
236
|
+
const apply = args.includes('--apply');
|
|
237
|
+
const r = remediar({ target: targetArg, apply });
|
|
238
|
+
imprimir(r, apply);
|
|
239
|
+
process.exit(0);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
module.exports = { remediar, escanear, leerEvolved, baselineCanonico, expandirTarget };
|
package/scripts/validar.js
CHANGED
|
@@ -101,6 +101,20 @@ verificar(
|
|
|
101
101
|
: `Cross-scope: ${auditInvoc.violaciones.length} invocación(es) relativa(s) — ${auditInvoc.violaciones.map(v => `${v.comando}.md:${v.linea}`).join(', ')}`
|
|
102
102
|
);
|
|
103
103
|
|
|
104
|
+
// 7c. Gate inverso de evolución (Fase 16, REQ-16-03): el FUENTE no debe portar
|
|
105
|
+
// marcadores evolved (frontmatter evolved:true ni sidecar .evolved.json). Un
|
|
106
|
+
// marcador en el repo madre es shipped-evolved espurio que congela el componente
|
|
107
|
+
// downstream. Ver scripts/lib/evolved-fuente.js y verificar-evolucion.js --gate-inverso.
|
|
108
|
+
const { listarOfensores } = require('./lib/evolved-fuente');
|
|
109
|
+
const ofensoresEvolved = listarOfensores(RAIZ);
|
|
110
|
+
const totalOfensores = ofensoresEvolved.frontmatter.length + ofensoresEvolved.sidecars.length;
|
|
111
|
+
verificar(
|
|
112
|
+
totalOfensores === 0,
|
|
113
|
+
totalOfensores === 0
|
|
114
|
+
? 'Gate inverso evolved: fuente sin marcadores evolved espurios'
|
|
115
|
+
: `Gate inverso evolved: ${totalOfensores} componente(s) del fuente con evolved (corregir: node scripts/verificar-evolucion.js --gate-inverso --fix)`
|
|
116
|
+
);
|
|
117
|
+
|
|
104
118
|
// 8. Reglas
|
|
105
119
|
const reglas = fs.readdirSync(path.join(RAIZ, 'reglas')).filter(f => f.endsWith('.md'));
|
|
106
120
|
verificar(reglas.length >= 7, `Reglas: ${reglas.length} (mínimo 7)`);
|
|
Binary file
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
const fs = require('fs');
|
|
33
33
|
const path = require('path');
|
|
34
34
|
const { execSync } = require('child_process');
|
|
35
|
+
const { listarOfensores, limpiar } = require('./lib/evolved-fuente');
|
|
35
36
|
|
|
36
37
|
/**
|
|
37
38
|
* Detecta si `dir` es la raíz del paquete swl-ses (repo madre).
|
|
@@ -291,9 +292,43 @@ function imprimirResultado(r) {
|
|
|
291
292
|
process.stdout.write('[' + marca + '] ' + r.archivo + ': ' + mensaje + '\n');
|
|
292
293
|
}
|
|
293
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Gate inverso (Fase 16, REQ-16-03): ningún componente del FUENTE (repo madre)
|
|
297
|
+
* puede portar marcador evolved (frontmatter `evolved:true` o sidecar
|
|
298
|
+
* `.evolved.json`). Un marcador en el fuente es shipped-evolved espurio que
|
|
299
|
+
* congela el componente downstream. Corre SIEMPRE en el repo madre — es el
|
|
300
|
+
* complemento exacto del bypass `if (raizPaquete)` de verificarArchivo (que
|
|
301
|
+
* omite los checks evolved-de-usuario; este gate verifica lo contrario).
|
|
302
|
+
*
|
|
303
|
+
* @param {{ fix?: boolean, raiz?: string }} [opts]
|
|
304
|
+
* @returns {number} 0 si limpio, 1 si hay ofensores (sin --fix)
|
|
305
|
+
*/
|
|
306
|
+
function gateInverso({ fix = false, raiz = process.cwd() } = {}) {
|
|
307
|
+
if (fix) {
|
|
308
|
+
const acciones = limpiar(raiz);
|
|
309
|
+
for (const a of acciones) process.stdout.write(` [fix] ${a.tipo}: ${a.archivo}\n`);
|
|
310
|
+
process.stdout.write(`[gate-inverso] limpieza aplicada: ${acciones.length} acción(es)\n`);
|
|
311
|
+
}
|
|
312
|
+
const { frontmatter, sidecars } = listarOfensores(raiz);
|
|
313
|
+
const total = frontmatter.length + sidecars.length;
|
|
314
|
+
if (total === 0) {
|
|
315
|
+
process.stdout.write('[gate-inverso] OK — ningún componente del fuente porta marcador evolved.\n');
|
|
316
|
+
return 0;
|
|
317
|
+
}
|
|
318
|
+
process.stdout.write(`[gate-inverso] FALLA — ${total} componente(s) del fuente con marcador evolved (deben limpiarse):\n`);
|
|
319
|
+
for (const f of frontmatter) process.stdout.write(` - frontmatter evolved: ${f}\n`);
|
|
320
|
+
for (const s of sidecars) process.stdout.write(` - sidecar .evolved.json: ${s}\n`);
|
|
321
|
+
process.stdout.write(' → corregir: node scripts/verificar-evolucion.js --gate-inverso --fix\n');
|
|
322
|
+
return 1;
|
|
323
|
+
}
|
|
324
|
+
|
|
294
325
|
function main() {
|
|
295
326
|
const args = process.argv.slice(2);
|
|
296
327
|
|
|
328
|
+
if (args.includes('--gate-inverso')) {
|
|
329
|
+
process.exit(gateInverso({ fix: args.includes('--fix') }));
|
|
330
|
+
}
|
|
331
|
+
|
|
297
332
|
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
|
|
298
333
|
process.stdout.write(
|
|
299
334
|
'Uso:\n' +
|
|
@@ -351,5 +386,6 @@ module.exports = {
|
|
|
351
386
|
leerCampo,
|
|
352
387
|
obtenerVersionEnHEAD,
|
|
353
388
|
esRaizDelPaquete,
|
|
389
|
+
gateInverso,
|
|
354
390
|
main,
|
|
355
391
|
};
|
|
@@ -317,6 +317,18 @@ function main() {
|
|
|
317
317
|
// calibración + ADR posterior (mismo patrón que G0/G2/G3).
|
|
318
318
|
const gateLicencias = ejecutarGateLicencias();
|
|
319
319
|
|
|
320
|
+
// Gate de evolución (Fase 16, REQ-16-13): el manifiesto canonical-hashes debe
|
|
321
|
+
// estar al día y el fuente NO debe portar marcadores evolved espurios. Ambos
|
|
322
|
+
// son blocking — un release con manifiesto stale o fuente evolved congelaría
|
|
323
|
+
// componentes downstream.
|
|
324
|
+
const gateEvolved = ejecutarGateEvolved();
|
|
325
|
+
if (!gateEvolved.ok) {
|
|
326
|
+
fallasObligatorias++;
|
|
327
|
+
}
|
|
328
|
+
console.log(gateEvolved.ok
|
|
329
|
+
? ' [OK] Gate evolución: manifiesto al día + fuente sin evolved espurio'
|
|
330
|
+
: ` [FALLA] Gate evolución: ${gateEvolved.problemas.join('; ')}`);
|
|
331
|
+
|
|
320
332
|
if (jsonOut) {
|
|
321
333
|
process.stdout.write(JSON.stringify({
|
|
322
334
|
version,
|
|
@@ -330,6 +342,7 @@ function main() {
|
|
|
330
342
|
description_gate: gateDescription,
|
|
331
343
|
aiisms_gate: aiismsGate,
|
|
332
344
|
gate_licencias: gateLicencias,
|
|
345
|
+
gate_evolved: gateEvolved,
|
|
333
346
|
resultados: resultados.map(({ entrada, resultado }) => ({
|
|
334
347
|
archivo: entrada.archivo,
|
|
335
348
|
obligatorio: entrada.obligatorio,
|
|
@@ -887,6 +900,25 @@ function ejecutarGateDescription(contadoresReales) {
|
|
|
887
900
|
* para tests; en producción usa el CWD del proceso).
|
|
888
901
|
* @returns {{disponible: boolean, hallazgos?: Array, resumen?: object, error?: string}}
|
|
889
902
|
*/
|
|
903
|
+
/**
|
|
904
|
+
* Gate de evolución (Fase 16): manifiesto canonical-hashes al día + fuente sin
|
|
905
|
+
* marcadores evolved espurios. Reusa los scripts existentes vía subproceso.
|
|
906
|
+
* @returns {{ ok: boolean, problemas: string[] }}
|
|
907
|
+
*/
|
|
908
|
+
function ejecutarGateEvolved() {
|
|
909
|
+
const { spawnSync } = require('child_process');
|
|
910
|
+
const problemas = [];
|
|
911
|
+
const correr = (args) => spawnSync(process.execPath, args, { cwd: CWD, encoding: 'utf8' }).status;
|
|
912
|
+
|
|
913
|
+
if (correr([path.join(CWD, 'scripts', 'generar-canonical-hashes.js'), '--check']) !== 0) {
|
|
914
|
+
problemas.push('manifiesto canonical-hashes desactualizado (node scripts/generar-canonical-hashes.js)');
|
|
915
|
+
}
|
|
916
|
+
if (correr([path.join(CWD, 'scripts', 'verificar-evolucion.js'), '--gate-inverso']) !== 0) {
|
|
917
|
+
problemas.push('fuente con marcadores evolved espurios (node scripts/verificar-evolucion.js --gate-inverso --fix)');
|
|
918
|
+
}
|
|
919
|
+
return { ok: problemas.length === 0, problemas };
|
|
920
|
+
}
|
|
921
|
+
|
|
890
922
|
function ejecutarGateLicencias(baseDir = CWD) {
|
|
891
923
|
try {
|
|
892
924
|
const { hallazgos, resumen } = evaluarLicencias(baseDir);
|
|
@@ -905,4 +937,5 @@ module.exports = {
|
|
|
905
937
|
extraerCifrasDescription,
|
|
906
938
|
ejecutarGateDescription,
|
|
907
939
|
ejecutarGateLicencias,
|
|
940
|
+
ejecutarGateEvolved,
|
|
908
941
|
};
|
package/agentes/.evolved.json
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"release.md": {
|
|
3
|
-
"evolved": true,
|
|
4
|
-
"evolvedFrom": "5.4.0",
|
|
5
|
-
"evolvedAt": "2026-04-11",
|
|
6
|
-
"evolvedBy": "aprender",
|
|
7
|
-
"evolvedNote": "mejora de metodología: checklist obligatoria de archivos de versión en paso 6"
|
|
8
|
-
},
|
|
9
|
-
"aprender.md": {
|
|
10
|
-
"evolved": true,
|
|
11
|
-
"evolvedFrom": "5.12.3",
|
|
12
|
-
"evolvedAt": "2026-04-25",
|
|
13
|
-
"evolvedBy": "aprender",
|
|
14
|
-
"evolvedNote": "Paso 2 — filtro crítico obligatorio sobre reportes de sub-agentes Explore para evitar sobre-ingeniería al analizar papers académicos"
|
|
15
|
-
},
|
|
16
|
-
"verificar.md": {
|
|
17
|
-
"evolved": true,
|
|
18
|
-
"evolvedFrom": "5.12.3",
|
|
19
|
-
"evolvedAt": "2026-04-26",
|
|
20
|
-
"evolvedBy": "evolucionar",
|
|
21
|
-
"evolvedNote": "flag --until-converge para iterar verificar→corregir→re-verificar hasta 0 hallazgos CRÍTICO+ALTO+MAYOR (max-iter=5, --no-prompt CI, detección adversarial ≥5 hallazgos nuevos)"
|
|
22
|
-
}
|
|
23
|
-
}
|