@saulwade/swl-ses 2.5.3 → 2.6.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/CLAUDE.md +9 -9
- package/README.md +37 -37
- package/agentes/_intent-spec.md +73 -73
- package/agentes/_propose-step.md +90 -90
- package/agentes/accesibilidad-wcag-swl.md +690 -690
- package/agentes/arquitecto-swl.md +267 -267
- package/agentes/auto-evolucion-swl.md +932 -908
- package/agentes/backend-csharp-swl.md +420 -420
- package/agentes/backend-go-swl.md +390 -390
- package/agentes/backend-java-swl.md +281 -281
- package/agentes/backend-rust-swl.md +364 -364
- package/agentes/backend-workers-swl.md +482 -482
- package/agentes/cloud-infra-swl.md +509 -509
- package/agentes/consolidador-swl.md +541 -541
- package/agentes/depurador-swl.md +352 -352
- package/agentes/devops-ci-swl.md +400 -400
- package/agentes/disenador-ui-swl.md +569 -569
- package/agentes/documentador-swl.md +345 -345
- package/agentes/frontend-angular-swl.md +621 -621
- package/agentes/frontend-css-swl.md +716 -716
- package/agentes/frontend-react-swl.md +692 -692
- package/agentes/frontend-swl.md +496 -496
- package/agentes/frontend-tailwind-swl.md +826 -826
- package/agentes/investigador-swl.md +432 -432
- package/agentes/investigador-ux-swl.md +505 -505
- package/agentes/migrador-swl.md +442 -442
- package/agentes/mobile-android-swl.md +511 -511
- package/agentes/mobile-cross-swl.md +541 -541
- package/agentes/mobile-ios-swl.md +502 -502
- package/agentes/mobile-testing-swl.md +302 -302
- package/agentes/nemesis-auditor-swl.md +285 -285
- package/agentes/observabilidad-swl.md +438 -438
- package/agentes/pagos-swl.md +310 -310
- package/agentes/perfilador-usuario-swl.md +321 -321
- package/agentes/planificador-swl.md +399 -399
- package/agentes/producto-prd-swl.md +589 -589
- package/agentes/red-team-swl.md +218 -218
- package/agentes/release-manager-swl.md +590 -590
- package/agentes/rendimiento-swl.md +713 -713
- package/agentes/revisor-angular-swl.md +278 -278
- package/agentes/revisor-csharp-swl.md +264 -264
- package/agentes/revisor-go-swl.md +259 -259
- package/agentes/revisor-java-swl.md +257 -257
- package/agentes/revisor-kotlin-swl.md +273 -273
- package/agentes/revisor-nextjs-swl.md +281 -281
- package/agentes/revisor-php-swl.md +271 -271
- package/agentes/revisor-react-swl.md +278 -278
- package/agentes/revisor-rust-swl.md +346 -346
- package/agentes/revisor-seguridad-swl.md +399 -399
- package/agentes/revisor-swift-swl.md +268 -268
- package/agentes/revisor-typescript-swl.md +346 -346
- package/agentes/tdd-qa-swl.md +393 -393
- package/bin/swl-ses.js +32 -7
- package/comandos/swl/actualizar.md +3 -3
- package/comandos/swl/aprender.md +13 -0
- package/comandos/swl/deuda-codigo.md +97 -97
- package/comandos/swl/evaluar-skill.md +18 -3
- package/comandos/swl/evolucion-continua.md +73 -0
- package/comandos/swl/evolucionar.md +13 -0
- package/comandos/swl/instalar.md +4 -4
- package/comandos/swl/notificaciones.md +1 -1
- package/comandos/swl/status.md +2 -2
- package/gateway/cron/jobs.example.json +12 -0
- package/habilidades/auto-evolucion-protocolo/SKILL.md +19 -1
- package/habilidades/autoresearch/SKILL.md +3 -2
- package/habilidades/backend-async-postgres-testing/SKILL.md +2 -1
- package/habilidades/benchmark-memoria/SKILL.md +7 -7
- package/habilidades/changelog-generator/SKILL.md +1 -1
- package/habilidades/changelog-generator/scripts/parse-commits.js +2 -1
- package/habilidades/checkpoints-verificacion/SKILL.md +6 -0
- package/habilidades/compactacion-contexto/SKILL.md +2 -1
- package/habilidades/contenedores-docker/SKILL.md +4 -2
- package/habilidades/context-builder/SKILL.md +4 -0
- package/habilidades/doubt-driven-review/SKILL.md +17 -1
- package/habilidades/drift-detection/SKILL.md +6 -1
- package/habilidades/ejecutar-fase/SKILL.md +6 -6
- package/habilidades/ejecutar-task-iterativo/SKILL.md +278 -278
- package/habilidades/eval-framework/SKILL.md +8 -3
- package/habilidades/extractor-de-aprendizajes/SKILL.md +8 -2
- package/habilidades/git-worktrees-paralelo/SKILL.md +19 -1
- package/habilidades/harness-claude-code/SKILL.md +7 -3
- package/habilidades/infra-github-actions/SKILL.md +4 -3
- package/habilidades/instalar-sistema/SKILL.md +5 -1
- package/habilidades/memoria-busqueda/SKILL.md +31 -39
- package/habilidades/planear-fase/SKILL.md +9 -1
- package/habilidades/prevencion-sobreingenieria/recursos/soluciones-nativas.md +166 -166
- package/habilidades/prevencion-sobreingenieria/recursos/variables-residuales-post-refactor.md +85 -85
- package/habilidades/proceso-ddia-fundamentos/SKILL.md +3 -2
- package/habilidades/proceso-ingenieria-requerimientos/SKILL.md +147 -147
- package/habilidades/release-semver/SKILL.md +2 -2
- package/habilidades/swl-claudemd/SKILL.md +6 -7
- package/habilidades/swl-dashboard/SKILL.md +11 -43
- package/habilidades/tdd-workflow/SKILL.md +12 -7
- package/habilidades/validacion-ci-sistema/SKILL.md +1 -1
- package/hooks/agente-lifecycle.js +2 -1
- package/hooks/aiisms-detector.js +13 -4
- package/hooks/audit-trail.js +2 -1
- package/hooks/auto-consolidacion.js +2 -1
- package/hooks/captura-acciones-post.js +2 -1
- package/hooks/captura-acciones-session.js +2 -1
- package/hooks/captura-feedback-usuario.js +3 -2
- package/hooks/claudemd-bloat-detector.js +12 -3
- package/hooks/claudemd-duplicacion-detector.js +13 -3
- package/hooks/contexto-iteracion.js +2 -1
- package/hooks/contexto-subagente.js +68 -68
- package/hooks/degradacion-instintos.js +2 -1
- package/hooks/extraccion-aprendizajes.js +109 -15
- package/hooks/grafo-contexto.js +2 -1
- package/hooks/guardrail-modelo.js +2 -1
- package/hooks/inbox-aviso.js +2 -1
- package/hooks/inyeccion-contexto.js +2 -1
- package/hooks/lib/agent-matcher.js +2 -1
- package/hooks/lib/agent-routing.js +2 -1
- package/hooks/lib/autonomia.js +5 -3
- package/hooks/lib/captura-acciones.js +2 -1
- package/hooks/lib/consolidation-lock.js +21 -10
- package/hooks/lib/etapa-auto-evolucion.js +10 -4
- package/hooks/lib/etapa-metricas.js +2 -1
- package/hooks/lib/etapa-perfil-usuario.js +20 -4
- package/hooks/lib/evolution-tracker.js +2 -1
- package/hooks/lib/gateway-notify.js +17 -3
- package/hooks/lib/loop-telemetry.js +5 -4
- package/hooks/lib/mcp-health.js +2 -1
- package/hooks/lib/memory-search.js +4 -0
- package/hooks/lib/merkle-audit.js +58 -6
- package/hooks/lib/notificacion-formato.js +58 -0
- package/hooks/lib/nudge-tracker.js +2 -1
- package/hooks/lib/otlp-exporter.js +2 -1
- package/hooks/lib/propose-step.js +3 -2
- package/hooks/lib/raiz-proyecto.js +127 -0
- package/hooks/lib/run-log.js +2 -1
- package/hooks/lib/singleton-guard.js +225 -27
- package/hooks/lib/telegram-cliente.js +28 -11
- package/hooks/notificacion-telegram.js +13 -3
- package/hooks/preservar-estado-pre-compact.js +2 -1
- package/hooks/proteccion-rutas.js +59 -3
- package/hooks/registro-turnos.js +2 -1
- package/hooks/resumen-sesion.js +2 -1
- package/hooks/risk-scoring.js +2 -1
- package/hooks/rotar-audit-auto.js +46 -20
- package/hooks/session-briefing.js +127 -1
- package/hooks/spec-gate.js +2 -1
- package/hooks/sugerir-contribuir.js +6 -3
- package/hooks/sugerir-regenerar-inventario.js +3 -2
- package/hooks/tdd-gate.js +2 -1
- package/hooks/telemetria-agentes.js +2 -1
- package/hooks/telemetria-skill-routing.js +2 -1
- package/hooks/tracking-costos.js +4 -3
- package/hooks/validar-formato-post-subagente.js +2 -1
- package/hooks/validar-intent-spec.js +2 -1
- package/hooks/validar-memoria-hook.js +13 -3
- package/hooks/validar-planning-paths.js +2 -1
- package/instintos/perfil-usuario.yaml +506 -3
- package/instintos/proyecto.yaml +78 -0
- package/llms.txt +29 -29
- package/manifiestos/canonical-hashes.json +5588 -4925
- package/manifiestos/hooks-config.json +469 -469
- package/manifiestos/invariantes-criticos.json +30 -30
- package/manifiestos/modulos.json +1429 -1423
- package/manifiestos/planning-paths.json +1 -0
- package/manifiestos/skills-lock.json +1275 -1275
- package/package.json +94 -95
- package/plugin.json +369 -369
- package/scripts/actualizar.js +3 -0
- package/scripts/auditar-clases-conocidas.js +134 -106
- package/scripts/benchmark-memoria.js +1 -0
- package/scripts/bootstrap-instintos.js +85 -14
- package/scripts/canario-hooks.js +166 -166
- package/scripts/cli/autonomia.js +23 -0
- package/scripts/cli/benchmark-memoria.js +37 -0
- package/scripts/cli/ciclo-autonomo.js +73 -0
- package/scripts/cli/ciclo-fase-b.js +102 -0
- package/scripts/cli/guardrail-metrics.js +39 -0
- package/scripts/cli/loop-telemetry.js +4 -2
- package/scripts/cli/memoria-search.js +69 -0
- package/scripts/cli/nudge-accionar.js +39 -0
- package/scripts/cli/run-eval.js +38 -0
- package/scripts/cli/run-skill-evals.js +13 -2
- package/scripts/derivar-feature-list.js +15 -14
- package/scripts/desinstalar.js +11 -0
- package/scripts/doctor.js +50 -13
- package/scripts/evidencia-valor.js +101 -101
- package/scripts/field-report.js +16 -16
- package/scripts/instalador.js +98 -7
- package/scripts/lib/activar-hooks-proyecto.js +116 -104
- package/scripts/lib/auditar-invocaciones-comandos.js +96 -6
- package/scripts/lib/ciclo-autonomo/candidatos.js +174 -0
- package/scripts/lib/ciclo-autonomo/config.js +165 -0
- package/scripts/lib/ciclo-autonomo/drenador-feedback.js +174 -0
- package/scripts/lib/ciclo-autonomo/fallback.js +77 -0
- package/scripts/lib/ciclo-autonomo/guard-convivencia.js +139 -0
- package/scripts/lib/ciclo-autonomo/higiene-nudges.js +112 -0
- package/scripts/lib/ciclo-autonomo/index.js +301 -0
- package/scripts/lib/ciclo-autonomo/lock.js +124 -0
- package/scripts/lib/ciclo-autonomo/presupuesto.js +122 -0
- package/scripts/lib/ciclo-autonomo/puente-degradacion.js +240 -0
- package/scripts/lib/ciclo-autonomo/runner-fase-b.js +248 -0
- package/scripts/lib/ciclo-autonomo/writer-instintos.js +190 -0
- package/scripts/lib/ciclo-autonomo/yaml-instintos.js +535 -0
- package/scripts/lib/estado.js +9 -0
- package/scripts/lib/evidencia-valor.js +228 -228
- package/scripts/lib/expandir-targets.js +71 -71
- package/scripts/lib/gitignore-manifest.js +8 -1
- package/scripts/lib/hooks-settings.js +45 -0
- package/scripts/lib/limpiar-basura-global.js +161 -0
- package/scripts/lib/toml-merge.js +204 -204
- package/scripts/mcp-server/auth.js +105 -105
- package/scripts/mcp-server/cache.js +106 -106
- package/scripts/rotar-audit-logs.js +48 -2
- package/scripts/run-eval.js +1 -0
- package/scripts/run-skill-evals.js +287 -8
- package/scripts/smoke-test.js +16 -8
- package/scripts/tui/pantallas/install-wizard.js +69 -13
- package/scripts/validar.js +40 -1
|
@@ -15,8 +15,10 @@
|
|
|
15
15
|
* node scripts/run-skill-evals.js <nombre-skill> — imprime plan
|
|
16
16
|
* node scripts/run-skill-evals.js <nombre-skill> --json — plan JSON
|
|
17
17
|
* node scripts/run-skill-evals.js <nombre-skill> --record-baseline --score=N
|
|
18
|
-
* node scripts/run-skill-evals.js <nombre-skill> --
|
|
19
|
-
*
|
|
18
|
+
* node scripts/run-skill-evals.js <nombre-skill> --emitir-evaluacion \
|
|
19
|
+
* --score=N --producido-por=<actor> [--badge=B] — artefacto de procedencia
|
|
20
|
+
* node scripts/run-skill-evals.js <nombre-skill> --record-after \
|
|
21
|
+
* [--hypothesis="razon-causal"] [--check-invariants] [--evaluacion=<path>]
|
|
20
22
|
* node scripts/run-skill-evals.js <nombre-skill> --record-revert --score=N
|
|
21
23
|
* node scripts/run-skill-evals.js --list — lista skills con evals
|
|
22
24
|
*
|
|
@@ -25,16 +27,31 @@
|
|
|
25
27
|
* --check-invariants valida safety invariants declarados en el frontmatter
|
|
26
28
|
* antes de aceptar el commit (operador Commit §3.2.2).
|
|
27
29
|
*
|
|
30
|
+
* Anti-gaming (DT-AGP-SCORE-AUTODECLARADO, cerrada 2026-07-10 — regla
|
|
31
|
+
* `seguridad-agentes.md § Anti-gaming`): el score que APRUEBA una evolución
|
|
32
|
+
* (--record-after) NO se acepta de un flag arbitrario. Debe venir de un
|
|
33
|
+
* artefacto de evaluación con procedencia (.planning/evolution/evaluaciones/
|
|
34
|
+
* <target>.json) emitido por /swl:evaluar-skill vía --emitir-evaluacion:
|
|
35
|
+
* fechado, con `producido_por` ≠ target, y fresco (≤7 días). Si además se
|
|
36
|
+
* pasa --score, debe COINCIDIR con el del artefacto. Todo evento registra el
|
|
37
|
+
* actor que produjo el score — el evaluado nunca aprueba su propia evidencia.
|
|
38
|
+
*
|
|
28
39
|
* Zero-deps.
|
|
29
40
|
*/
|
|
30
41
|
|
|
31
|
-
const fs
|
|
32
|
-
const path
|
|
42
|
+
const fs = require('fs');
|
|
43
|
+
const path = require('path');
|
|
44
|
+
const crypto = require('crypto');
|
|
33
45
|
const { validarInvariantesDeAgente } = require('./lib/agp-frontmatter');
|
|
34
46
|
|
|
35
47
|
const CWD = process.cwd();
|
|
36
48
|
const EVOL_DIR = path.join(CWD, '.planning', 'evolution');
|
|
37
49
|
const EVOL_LOG = path.join(EVOL_DIR, 'evoluciones.jsonl');
|
|
50
|
+
const EVALUACIONES_DIR = path.join(EVOL_DIR, 'evaluaciones');
|
|
51
|
+
|
|
52
|
+
/** Ventana de frescura del artefacto de evaluación (ms): 7 días. Una
|
|
53
|
+
* evaluación más vieja no puede aprobar una mutación nueva — se re-corre. */
|
|
54
|
+
const FRESCURA_EVALUACION_MS = 7 * 24 * 3600 * 1000;
|
|
38
55
|
|
|
39
56
|
function ensureDir(d) { try { fs.mkdirSync(d, { recursive: true }); } catch { /* ignore */ } }
|
|
40
57
|
|
|
@@ -49,6 +66,36 @@ function resolverEvals(nombre) {
|
|
|
49
66
|
return null;
|
|
50
67
|
}
|
|
51
68
|
|
|
69
|
+
/** Resuelve el archivo de contenido del target (SKILL.md o el .md del agente). */
|
|
70
|
+
function resolverContenidoTarget(nombre) {
|
|
71
|
+
const candidatos = [
|
|
72
|
+
path.join(CWD, 'habilidades', nombre, 'SKILL.md'),
|
|
73
|
+
path.join(CWD, 'agentes', `${nombre}.md`),
|
|
74
|
+
];
|
|
75
|
+
for (const c of candidatos) {
|
|
76
|
+
if (fs.existsSync(c)) return c;
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* SHA256 del contenido evaluado (EOL normalizado — CRLF/LF no cambia el hash,
|
|
83
|
+
* clase C2). Liga el artefacto de evaluación a la versión EXACTA evaluada: si
|
|
84
|
+
* el skill/agente muta en la ventana de frescura entre --emitir-evaluacion y
|
|
85
|
+
* --record-after, el hash deja de coincidir y la aprobación se rechaza
|
|
86
|
+
* (cierra la ventana TOCTOU del anti-gaming, nemesis iter-1 S3-2).
|
|
87
|
+
*/
|
|
88
|
+
function hashContenidoTarget(nombre) {
|
|
89
|
+
const ruta = resolverContenidoTarget(nombre);
|
|
90
|
+
if (!ruta) return null;
|
|
91
|
+
try {
|
|
92
|
+
const txt = fs.readFileSync(ruta, 'utf8').replace(/\r\n/g, '\n');
|
|
93
|
+
return crypto.createHash('sha256').update(txt).digest('hex');
|
|
94
|
+
} catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
52
99
|
function listarSkillsConEvals() {
|
|
53
100
|
const out = [];
|
|
54
101
|
try {
|
|
@@ -107,6 +154,171 @@ function registrarEvento(tipo, detalles) {
|
|
|
107
154
|
}
|
|
108
155
|
}
|
|
109
156
|
|
|
157
|
+
/**
|
|
158
|
+
/** Escritura atómica de JSON con require tolerante (repo madre vs destino). */
|
|
159
|
+
function escribirJSONAtomico(ruta, obj) {
|
|
160
|
+
let atomicWriteJSON = null;
|
|
161
|
+
try {
|
|
162
|
+
({ atomicWriteJSON } = require(path.join(__dirname, '..', 'hooks', 'lib', 'atomic-write.js')));
|
|
163
|
+
} catch {
|
|
164
|
+
try { ({ atomicWriteJSON } = require(path.join(__dirname, 'atomic-write.js'))); } catch { /* fallback abajo */ }
|
|
165
|
+
}
|
|
166
|
+
if (atomicWriteJSON) atomicWriteJSON(ruta, obj);
|
|
167
|
+
else fs.writeFileSync(ruta, JSON.stringify(obj, null, 2) + '\n', 'utf8');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Marca un artefacto de evaluación como consumido tras un --record-after
|
|
172
|
+
* exitoso: una evaluación aprueba UNA aplicación. Impide el replay del mismo
|
|
173
|
+
* artefacto para inflar el contador de evoluciones (nemesis iter-1 S3-1).
|
|
174
|
+
*/
|
|
175
|
+
function marcarEvaluacionConsumida(ruta) {
|
|
176
|
+
try {
|
|
177
|
+
const art = JSON.parse(fs.readFileSync(ruta, 'utf8'));
|
|
178
|
+
art.consumido_en = new Date().toISOString();
|
|
179
|
+
escribirJSONAtomico(ruta, art);
|
|
180
|
+
} catch (e) {
|
|
181
|
+
// Fail-loud (regla anti-fallback-silencioso): si el marcado falla (disco
|
|
182
|
+
// lleno, permisos), el evento `aplicada` YA quedó registrado y el artefacto
|
|
183
|
+
// sigue sin `consumido_en` → replayable. Avisar en vez de tragarlo, para
|
|
184
|
+
// que el usuario borre el artefacto a mano (re-auditoría nemesis iter-2).
|
|
185
|
+
process.stderr.write(
|
|
186
|
+
`[run-skill-evals] AVISO: no se pudo marcar el artefacto como consumido (${e.message}).\n` +
|
|
187
|
+
` El artefacto ${ruta} sigue siendo replayable — bórralo manualmente para evitar un doble --record-after.\n`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Escribe el artefacto de evaluación con procedencia (modo --emitir-evaluacion).
|
|
194
|
+
* Lo invoca /swl:evaluar-skill al cerrar su reporte; es la ÚNICA fuente
|
|
195
|
+
* válida del score que --record-after acepta.
|
|
196
|
+
*/
|
|
197
|
+
function emitirEvaluacion({ target, score, badge, producidoPor }) {
|
|
198
|
+
ensureDir(EVALUACIONES_DIR);
|
|
199
|
+
const ruta = path.join(EVALUACIONES_DIR, `${target}.json`);
|
|
200
|
+
|
|
201
|
+
// Emisión concurrente (nemesis iter-1 S3-7): si ya existe un artefacto para
|
|
202
|
+
// este target emitido hace segundos (otra evaluación en curso, regla
|
|
203
|
+
// sesiones-paralelas), avisar antes de sobrescribirlo — el last-write-wins
|
|
204
|
+
// silencioso podía tirar una evaluación fresca justo antes de leerse.
|
|
205
|
+
try {
|
|
206
|
+
if (fs.existsSync(ruta)) {
|
|
207
|
+
const previo = JSON.parse(fs.readFileSync(ruta, 'utf8'));
|
|
208
|
+
const edadMs = Date.now() - Date.parse(previo.fecha || '');
|
|
209
|
+
if (Number.isFinite(edadMs) && edadMs >= 0 && edadMs < 10_000) {
|
|
210
|
+
process.stderr.write(
|
|
211
|
+
`[run-skill-evals] AVISO: sobrescribiendo un artefacto de evaluación de "${target}" ` +
|
|
212
|
+
`emitido hace ${(edadMs / 1000).toFixed(1)}s (posible evaluación concurrente). ` +
|
|
213
|
+
`Verifica que no haya dos /swl:evaluar-skill corriendo sobre el mismo target.\n`
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
} catch { /* artefacto ilegible: se sobrescribe sin aviso */ }
|
|
218
|
+
|
|
219
|
+
const artefacto = {
|
|
220
|
+
target,
|
|
221
|
+
score,
|
|
222
|
+
badge: badge || null,
|
|
223
|
+
producido_por: producidoPor,
|
|
224
|
+
fecha: new Date().toISOString(),
|
|
225
|
+
hash_contenido: hashContenidoTarget(target),
|
|
226
|
+
};
|
|
227
|
+
escribirJSONAtomico(ruta, artefacto);
|
|
228
|
+
return { ruta, artefacto };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Carga y valida el artefacto de evaluación para --record-after.
|
|
233
|
+
* Devuelve { ok, artefacto?, ruta?, error?, codigo? } — codigo 8 = artefacto
|
|
234
|
+
* ausente/inválido/vencido/auto-calificado; el caller decide el exit.
|
|
235
|
+
*/
|
|
236
|
+
function cargarEvaluacion(target, rutaOverride) {
|
|
237
|
+
const ruta = rutaOverride
|
|
238
|
+
? path.resolve(CWD, rutaOverride)
|
|
239
|
+
: path.join(EVALUACIONES_DIR, `${target}.json`);
|
|
240
|
+
|
|
241
|
+
if (!fs.existsSync(ruta)) {
|
|
242
|
+
return {
|
|
243
|
+
ok: false,
|
|
244
|
+
codigo: 8,
|
|
245
|
+
error: `FAIL: --record-after exige un artefacto de evaluación con procedencia y no existe:\n` +
|
|
246
|
+
` ${path.relative(CWD, ruta)}\n` +
|
|
247
|
+
`Genéralo corriendo /swl:evaluar-skill ${target} (emite el artefacto vía --emitir-evaluacion).\n` +
|
|
248
|
+
`El score que aprueba una evolución no puede declararlo el propio evaluado (DT-AGP-SCORE-AUTODECLARADO).`,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
let artefacto;
|
|
253
|
+
try {
|
|
254
|
+
artefacto = JSON.parse(fs.readFileSync(ruta, 'utf8'));
|
|
255
|
+
} catch (e) {
|
|
256
|
+
return { ok: false, codigo: 8, error: `FAIL: artefacto de evaluación ilegible (${e.message}): ${ruta}` };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (artefacto.target !== target) {
|
|
260
|
+
return { ok: false, codigo: 8, error: `FAIL: el artefacto de evaluación es de "${artefacto.target}", no de "${target}".` };
|
|
261
|
+
}
|
|
262
|
+
const scoreArtefacto = Number(artefacto.score);
|
|
263
|
+
if (!Number.isFinite(scoreArtefacto) || scoreArtefacto < 0 || scoreArtefacto > 100) {
|
|
264
|
+
return { ok: false, codigo: 8, error: `FAIL: score inválido en el artefacto de evaluación: ${artefacto.score}` };
|
|
265
|
+
}
|
|
266
|
+
const productor = String(artefacto.producido_por || '').trim();
|
|
267
|
+
if (!productor) {
|
|
268
|
+
return { ok: false, codigo: 8, error: 'FAIL: el artefacto de evaluación no declara `producido_por` — sin procedencia no hay aprobación.' };
|
|
269
|
+
}
|
|
270
|
+
if (productor === target) {
|
|
271
|
+
return {
|
|
272
|
+
ok: false,
|
|
273
|
+
codigo: 8,
|
|
274
|
+
error: `FAIL: auto-calificación rechazada — el artefacto declara producido_por="${productor}" igual al target evaluado.\n` +
|
|
275
|
+
`La evidencia que aprueba una evolución debe venir de un actor independiente (regla seguridad-agentes § Anti-gaming).`,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
const fecha = Date.parse(artefacto.fecha || '');
|
|
279
|
+
if (!Number.isFinite(fecha)) {
|
|
280
|
+
return { ok: false, codigo: 8, error: 'FAIL: el artefacto de evaluación no tiene `fecha` válida.' };
|
|
281
|
+
}
|
|
282
|
+
if (Date.now() - fecha > FRESCURA_EVALUACION_MS) {
|
|
283
|
+
return {
|
|
284
|
+
ok: false,
|
|
285
|
+
codigo: 8,
|
|
286
|
+
error: `FAIL: artefacto de evaluación vencido (viejo, >7 días: ${artefacto.fecha}).\n` +
|
|
287
|
+
`Re-corre /swl:evaluar-skill ${target} — una evaluación vieja no aprueba una mutación nueva.`,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Anti-replay: una evaluación aprueba UNA aplicación. Si ya se consumió
|
|
292
|
+
// en un --record-after previo, rechazar (nemesis iter-1 S3-1) — evita que
|
|
293
|
+
// el mismo artefacto infle el contador de evoluciones.
|
|
294
|
+
if (artefacto.consumido_en) {
|
|
295
|
+
return {
|
|
296
|
+
ok: false,
|
|
297
|
+
codigo: 8,
|
|
298
|
+
error: `FAIL: el artefacto de evaluación ya se consumió (${artefacto.consumido_en}).\n` +
|
|
299
|
+
`Una evaluación aprueba una sola aplicación — re-corre /swl:evaluar-skill ${target}.`,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Binding de contenido: el artefacto aprueba la versión EXACTA que se evaluó.
|
|
304
|
+
// Si el target mutó entre --emitir-evaluacion y --record-after, el hash no
|
|
305
|
+
// coincide y la aprobación se rechaza (cierra la ventana TOCTOU, nemesis S3-2).
|
|
306
|
+
// Se exige cuando el artefacto lo declara Y el contenido del target resuelve.
|
|
307
|
+
if (artefacto.hash_contenido) {
|
|
308
|
+
const hashActual = hashContenidoTarget(target);
|
|
309
|
+
if (hashActual && hashActual !== artefacto.hash_contenido) {
|
|
310
|
+
return {
|
|
311
|
+
ok: false,
|
|
312
|
+
codigo: 8,
|
|
313
|
+
error: `FAIL: el contenido de "${target}" cambió tras la evaluación (hash no coincide).\n` +
|
|
314
|
+
`El artefacto aprueba una versión distinta a la actual — re-corre /swl:evaluar-skill ${target}.`,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return { ok: true, artefacto, ruta };
|
|
320
|
+
}
|
|
321
|
+
|
|
110
322
|
// Nota: el parser YAML y el validador de safety invariants (AGP Commit operator)
|
|
111
323
|
// viven en scripts/lib/agp-frontmatter.js para reutilizarse desde tests y hooks.
|
|
112
324
|
|
|
@@ -122,11 +334,18 @@ const flags = {
|
|
|
122
334
|
recordAfter: argv.includes('--record-after'),
|
|
123
335
|
recordRevert: argv.includes('--record-revert'),
|
|
124
336
|
checkInvariants: argv.includes('--check-invariants'),
|
|
337
|
+
emitirEvaluacion: argv.includes('--emitir-evaluacion'),
|
|
125
338
|
};
|
|
126
339
|
const scoreArg = argv.find(a => a.startsWith('--score='));
|
|
127
340
|
const score = scoreArg ? parseFloat(scoreArg.split('=')[1]) : null;
|
|
128
341
|
const hypothesisArg = argv.find(a => a.startsWith('--hypothesis='));
|
|
129
342
|
const hypothesis = hypothesisArg ? hypothesisArg.slice('--hypothesis='.length) : null;
|
|
343
|
+
const producidoPorArg = argv.find(a => a.startsWith('--producido-por='));
|
|
344
|
+
const producidoPor = producidoPorArg ? producidoPorArg.slice('--producido-por='.length).trim() : null;
|
|
345
|
+
const badgeArg = argv.find(a => a.startsWith('--badge='));
|
|
346
|
+
const badge = badgeArg ? badgeArg.slice('--badge='.length) : null;
|
|
347
|
+
const evaluacionArg = argv.find(a => a.startsWith('--evaluacion='));
|
|
348
|
+
const evaluacionPath = evaluacionArg ? evaluacionArg.slice('--evaluacion='.length) : null;
|
|
130
349
|
const nombre = argv.find(a => !a.startsWith('--'));
|
|
131
350
|
|
|
132
351
|
if (flags.list) {
|
|
@@ -172,8 +391,35 @@ if (errors.length) {
|
|
|
172
391
|
process.exit(4);
|
|
173
392
|
}
|
|
174
393
|
|
|
394
|
+
// Modo --emitir-evaluacion: /swl:evaluar-skill persiste aquí el artefacto de
|
|
395
|
+
// procedencia que --record-after exigirá (DT-AGP-SCORE-AUTODECLARADO).
|
|
396
|
+
if (flags.emitirEvaluacion) {
|
|
397
|
+
if (score === null || Number.isNaN(score) || score < 0 || score > 100) {
|
|
398
|
+
console.error('FAIL: --emitir-evaluacion requiere --score=N (0-100)');
|
|
399
|
+
process.exit(5);
|
|
400
|
+
}
|
|
401
|
+
if (!producidoPor) {
|
|
402
|
+
console.error('FAIL: --emitir-evaluacion requiere --producido-por=<actor> (comando/agente que corrió la evaluación).');
|
|
403
|
+
console.error(' Sin procedencia declarada el artefacto no puede aprobar evoluciones.');
|
|
404
|
+
process.exit(5);
|
|
405
|
+
}
|
|
406
|
+
const { ruta } = emitirEvaluacion({ target: nombre, score, badge, producidoPor });
|
|
407
|
+
registrarEvento('evaluacion', {
|
|
408
|
+
target: nombre,
|
|
409
|
+
score,
|
|
410
|
+
badge: badge || null,
|
|
411
|
+
producido_por: producidoPor,
|
|
412
|
+
evaluacion_path: path.relative(CWD, ruta),
|
|
413
|
+
});
|
|
414
|
+
console.log(`evaluación emitida: target=${nombre} score=${score} producido_por=${producidoPor}`);
|
|
415
|
+
console.log(`artefacto: ${path.relative(CWD, ruta)}`);
|
|
416
|
+
process.exit(0);
|
|
417
|
+
}
|
|
418
|
+
|
|
175
419
|
if (flags.recordBaseline || flags.recordAfter || flags.recordRevert) {
|
|
176
|
-
|
|
420
|
+
// El score de --record-after viene del artefacto de evaluación (abajo);
|
|
421
|
+
// baseline y revert siguen requiriendo --score explícito.
|
|
422
|
+
if (!flags.recordAfter && (score === null || Number.isNaN(score))) {
|
|
177
423
|
console.error('FAIL: --record-* requiere --score=N (0-100)');
|
|
178
424
|
process.exit(5);
|
|
179
425
|
}
|
|
@@ -181,6 +427,25 @@ if (flags.recordBaseline || flags.recordAfter || flags.recordRevert) {
|
|
|
181
427
|
: flags.recordAfter ? 'aplicada'
|
|
182
428
|
: 'revertida';
|
|
183
429
|
|
|
430
|
+
// Anti-gaming (--record-after): el score que aprueba la evolución se lee
|
|
431
|
+
// del artefacto con procedencia; un --score explícito debe COINCIDIR.
|
|
432
|
+
let evaluacion = null;
|
|
433
|
+
if (flags.recordAfter) {
|
|
434
|
+
const carga = cargarEvaluacion(nombre, evaluacionPath);
|
|
435
|
+
if (!carga.ok) {
|
|
436
|
+
console.error(carga.error);
|
|
437
|
+
process.exit(carga.codigo);
|
|
438
|
+
}
|
|
439
|
+
evaluacion = carga;
|
|
440
|
+
const scoreArtefacto = Number(carga.artefacto.score);
|
|
441
|
+
if (score !== null && !Number.isNaN(score) && Math.abs(score - scoreArtefacto) > 0.01) {
|
|
442
|
+
console.error(`FAIL: --score=${score} discrepa del artefacto de evaluación (${scoreArtefacto}).`);
|
|
443
|
+
console.error(` El número que se registra es el del artefacto — no se acepta un score paralelo.`);
|
|
444
|
+
process.exit(9);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
const scoreEfectivo = flags.recordAfter ? Number(evaluacion.artefacto.score) : score;
|
|
448
|
+
|
|
184
449
|
let invariantes_ok = null;
|
|
185
450
|
if (flags.checkInvariants && (flags.recordAfter || flags.recordRevert)) {
|
|
186
451
|
const resultado = validarInvariantesDeAgente(nombre);
|
|
@@ -192,7 +457,7 @@ if (flags.recordBaseline || flags.recordAfter || flags.recordRevert) {
|
|
|
192
457
|
}
|
|
193
458
|
registrarEvento(tipo, {
|
|
194
459
|
target: nombre,
|
|
195
|
-
score,
|
|
460
|
+
score: scoreEfectivo,
|
|
196
461
|
evals_total: data.evals.length,
|
|
197
462
|
evals_path: path.relative(CWD, evalsPath),
|
|
198
463
|
hypothesis,
|
|
@@ -206,18 +471,32 @@ if (flags.recordBaseline || flags.recordAfter || flags.recordRevert) {
|
|
|
206
471
|
|
|
207
472
|
const payload = {
|
|
208
473
|
target: nombre,
|
|
209
|
-
score,
|
|
474
|
+
score: scoreEfectivo,
|
|
210
475
|
evals_total: data.evals.length,
|
|
211
476
|
evals_path: path.relative(CWD, evalsPath),
|
|
212
477
|
};
|
|
213
478
|
if (hypothesis) payload.hypothesis = hypothesis;
|
|
214
479
|
if (invariantes_ok !== null) payload.invariantes_ok = invariantes_ok;
|
|
480
|
+
if (evaluacion) {
|
|
481
|
+
// Procedencia del score (DT-AGP-SCORE-AUTODECLARADO): quién lo produjo
|
|
482
|
+
// y de qué artefacto sale — el detector de validar.js exige estos campos
|
|
483
|
+
// en toda entrada `aplicada`.
|
|
484
|
+
payload.producido_por = evaluacion.artefacto.producido_por;
|
|
485
|
+
payload.evaluacion_path = path.relative(CWD, evaluacion.ruta);
|
|
486
|
+
payload.evaluacion_fecha = evaluacion.artefacto.fecha;
|
|
487
|
+
}
|
|
215
488
|
|
|
216
489
|
const ok = registrarEvento(tipo, payload);
|
|
217
490
|
if (ok) {
|
|
491
|
+
// Consumir el artefacto tras un --record-after exitoso: una evaluación
|
|
492
|
+
// aprueba UNA aplicación (anti-replay, nemesis S3-1).
|
|
493
|
+
if (flags.recordAfter && evaluacion) {
|
|
494
|
+
marcarEvaluacionConsumida(evaluacion.ruta);
|
|
495
|
+
}
|
|
218
496
|
const sufijo = hypothesis ? ` hypothesis="${hypothesis.slice(0, 40)}${hypothesis.length > 40 ? '...' : ''}"` : '';
|
|
219
497
|
const invSufijo = invariantes_ok === true ? ' invariantes_ok=true' : '';
|
|
220
|
-
|
|
498
|
+
const procSufijo = evaluacion ? ` producido_por=${evaluacion.artefacto.producido_por}` : '';
|
|
499
|
+
console.log(`registrado: ${tipo} score=${scoreEfectivo} target=${nombre}${sufijo}${invSufijo}${procSufijo}`);
|
|
221
500
|
process.exit(0);
|
|
222
501
|
} else {
|
|
223
502
|
process.exit(6);
|
package/scripts/smoke-test.js
CHANGED
|
@@ -53,9 +53,10 @@ function archivosEsperadosEnPerfil(perfil) {
|
|
|
53
53
|
for (const modKey of modulosPerfil) {
|
|
54
54
|
const mod = modulos.modulos[modKey];
|
|
55
55
|
if (!mod) continue;
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
|
|
56
|
+
// Histórico: los módulos tipo 'scripts' se OMITÍAN del smoke — ese punto
|
|
57
|
+
// ciego escondió que el instalador depositaba sus archivos (incluidos
|
|
58
|
+
// hooks y comandos) en rutas.base (DT-MODULOS-TIPO-SCRIPTS, 2026-07-10).
|
|
59
|
+
// Hoy no queda ningún módulo tipo 'scripts'; todos se verifican.
|
|
59
60
|
for (const a of mod.archivos || []) archivos.add(a);
|
|
60
61
|
}
|
|
61
62
|
return archivos;
|
|
@@ -98,12 +99,19 @@ function traducirRuta(rel) {
|
|
|
98
99
|
.replace(/^reglas\//, "rules/");
|
|
99
100
|
}
|
|
100
101
|
if (rel.startsWith("hooks/lib/")) {
|
|
101
|
-
// El instalador
|
|
102
|
-
|
|
102
|
+
// El instalador preserva el subárbol lib/ (contrato require('./lib/X')
|
|
103
|
+
// de los hooks — el aplanado dejaba esos require muertos en installs
|
|
104
|
+
// frescos; restaurado 2026-07-10).
|
|
105
|
+
return rel;
|
|
103
106
|
}
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
+
// scripts/lib/* se une al MISMO lib/ de hooks (los fallbacks tolerantes
|
|
108
|
+
// de check-update/session-briefing esperan './lib/X'). Preserva subdirs
|
|
109
|
+
// (lib/ciclo-autonomo/...).
|
|
110
|
+
if (rel.startsWith("scripts/lib/")) {
|
|
111
|
+
return "hooks/lib/" + rel.slice("scripts/lib/".length);
|
|
112
|
+
}
|
|
113
|
+
// Resto de scripts/ declarados en módulos de hooks: aplanados por basename
|
|
114
|
+
// en la raíz de hooks (rotar-audit-logs.js, *.py, etc.).
|
|
107
115
|
if (rel.startsWith("scripts/")) {
|
|
108
116
|
return "hooks/" + path.posix.basename(rel);
|
|
109
117
|
}
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
* 1. Runtime destino (Claude/Codex/OpenCode/etc.)
|
|
11
11
|
* 2. Perfil (core / backend-python / fullstack-* / completo / ...)
|
|
12
12
|
* 3. Scope (proyecto / global)
|
|
13
|
-
* 4. Flags adicionales (with-mcp, dry-run)
|
|
14
|
-
* 5.
|
|
15
|
-
* 6.
|
|
16
|
-
* 7.
|
|
13
|
+
* 4. Flags adicionales (with-mcp, dry-run, solo-hooks)
|
|
14
|
+
* 5. Módulos opt-in (fuera de todos los perfiles — DT-MODULOS-OPT-IN)
|
|
15
|
+
* 6. Stacks de lenguajes (solo si perfil = completo)
|
|
16
|
+
* 7. Preview del plan + confirmación final
|
|
17
17
|
*
|
|
18
18
|
* Devuelve el objeto de opciones para `scripts/instalador.js` o null si el
|
|
19
19
|
* usuario canceló en cualquier paso.
|
|
@@ -112,12 +112,43 @@ const FLAGS = [
|
|
|
112
112
|
descripcion: 'Útil para inspeccionar qué se haría sin tocar el filesystem.',
|
|
113
113
|
preseleccionado: false,
|
|
114
114
|
},
|
|
115
|
+
{
|
|
116
|
+
valor: 'solo-hooks',
|
|
117
|
+
etiqueta: 'Solo hooks (telemetría de proyecto, sin copiar componentes)',
|
|
118
|
+
descripcion: 'Registra los hooks del scope GLOBAL en el settings.json de ESTE proyecto — evita duplicar agentes/comandos en la UI. Ignora perfil, scope y módulos. Solo target Claude Code.',
|
|
119
|
+
preseleccionado: false,
|
|
120
|
+
},
|
|
115
121
|
];
|
|
116
122
|
|
|
117
123
|
// ---------------------------------------------------------------------------
|
|
118
124
|
// Helpers
|
|
119
125
|
// ---------------------------------------------------------------------------
|
|
120
126
|
|
|
127
|
+
const MODULOS_PATH = path.join(RAIZ, 'manifiestos', 'modulos.json');
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Módulos `opt_in: true` de modulos.json — fuera de todos los perfiles,
|
|
131
|
+
* instalables solo por selección explícita (DT-MODULOS-OPT-IN). El wizard
|
|
132
|
+
* los ofrece como paso propio; el CLI equivalente es `--with a,b`.
|
|
133
|
+
*
|
|
134
|
+
* @returns {Array<{valor: string, etiqueta: string, descripcion: string}>}
|
|
135
|
+
*/
|
|
136
|
+
function cargarModulosOptIn() {
|
|
137
|
+
try {
|
|
138
|
+
const data = JSON.parse(fs.readFileSync(MODULOS_PATH, 'utf8'));
|
|
139
|
+
const modulos = data.modulos || {};
|
|
140
|
+
return Object.entries(modulos)
|
|
141
|
+
.filter(([, m]) => m && m.opt_in === true)
|
|
142
|
+
.map(([nombre, m]) => ({
|
|
143
|
+
valor: nombre,
|
|
144
|
+
etiqueta: nombre,
|
|
145
|
+
descripcion: String(m.descripcion || '').slice(0, 140),
|
|
146
|
+
}));
|
|
147
|
+
} catch (_) {
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
121
152
|
function cargarPerfiles() {
|
|
122
153
|
try {
|
|
123
154
|
const data = JSON.parse(fs.readFileSync(PERFILES_PATH, 'utf8'));
|
|
@@ -181,6 +212,13 @@ function pantallaPreview(opciones, perfiles) {
|
|
|
181
212
|
filas.push(['Stacks extra', opciones.stacks.join(', ')]);
|
|
182
213
|
}
|
|
183
214
|
|
|
215
|
+
if (opciones['solo-hooks']) {
|
|
216
|
+
filas.push(['Solo hooks', 'sí — registra hooks del scope global en el proyecto, sin copiar componentes']);
|
|
217
|
+
}
|
|
218
|
+
if (opciones.with) {
|
|
219
|
+
filas.push(['Módulos opt-in', opciones.with]);
|
|
220
|
+
}
|
|
221
|
+
|
|
184
222
|
const perfilInfo = perfiles[opciones.profile];
|
|
185
223
|
if (perfilInfo?.modulos) {
|
|
186
224
|
filas.push(['Módulos', String(perfilInfo.modulos.length)]);
|
|
@@ -247,7 +285,7 @@ async function ejecutarWizardInstall() {
|
|
|
247
285
|
// (fix 2026-07-09: era selector único; el flujo multi-runtime obligaba a
|
|
248
286
|
// repetir el wizard completo por cada runtime).
|
|
249
287
|
let targets = null;
|
|
250
|
-
let tituloPaso1 = 'Paso 1/
|
|
288
|
+
let tituloPaso1 = 'Paso 1/7 — ¿En qué runtime(s) instalas SWL? (espacio para marcar)';
|
|
251
289
|
for (;;) {
|
|
252
290
|
targets = await selectorMulti({ titulo: tituloPaso1, items: RUNTIMES });
|
|
253
291
|
if (targets === null) return { exito: false, cancelado: true };
|
|
@@ -259,13 +297,13 @@ async function ejecutarWizardInstall() {
|
|
|
259
297
|
targets = [RUNTIMES[0].valor];
|
|
260
298
|
break;
|
|
261
299
|
}
|
|
262
|
-
tituloPaso1 = 'Paso 1/
|
|
300
|
+
tituloPaso1 = 'Paso 1/7 — Marca AL MENOS un runtime con espacio (Esc para cancelar)';
|
|
263
301
|
}
|
|
264
302
|
const target = targets[0]; // compat: preview/ejecutores leen .target
|
|
265
303
|
|
|
266
304
|
// 2. Perfil
|
|
267
305
|
const profile = await selectorUnico({
|
|
268
|
-
titulo: 'Paso 2/
|
|
306
|
+
titulo: 'Paso 2/7 — ¿Qué perfil instalas?',
|
|
269
307
|
items: construirItemsPerfil(perfiles),
|
|
270
308
|
indiceDefault: 0,
|
|
271
309
|
});
|
|
@@ -273,7 +311,7 @@ async function ejecutarWizardInstall() {
|
|
|
273
311
|
|
|
274
312
|
// 3. Scope
|
|
275
313
|
const scope = await selectorUnico({
|
|
276
|
-
titulo: 'Paso 3/
|
|
314
|
+
titulo: 'Paso 3/7 — ¿Dónde se instala?',
|
|
277
315
|
items: SCOPES,
|
|
278
316
|
indiceDefault: 0,
|
|
279
317
|
});
|
|
@@ -281,16 +319,31 @@ async function ejecutarWizardInstall() {
|
|
|
281
319
|
|
|
282
320
|
// 4. Flags
|
|
283
321
|
const flagsSeleccionados = await selectorMulti({
|
|
284
|
-
titulo: 'Paso 4/
|
|
322
|
+
titulo: 'Paso 4/7 — Flags adicionales (espacio para toggle):',
|
|
285
323
|
items: FLAGS,
|
|
286
324
|
});
|
|
287
325
|
if (flagsSeleccionados === null) return { exito: false, cancelado: true };
|
|
288
326
|
|
|
289
|
-
// 5.
|
|
327
|
+
// 5. Módulos opt-in (DT-MODULOS-OPT-IN) — fuera de todos los perfiles;
|
|
328
|
+
// solo se instalan por selección explícita. Se omite en modo solo-hooks
|
|
329
|
+
// (ese camino no copia componentes).
|
|
330
|
+
const soloHooks = flagsSeleccionados.includes('solo-hooks');
|
|
331
|
+
let modulosOptIn = [];
|
|
332
|
+
const itemsOptIn = soloHooks ? [] : cargarModulosOptIn();
|
|
333
|
+
if (itemsOptIn.length > 0) {
|
|
334
|
+
const elegidos = await selectorMulti({
|
|
335
|
+
titulo: 'Paso 5/7 — Módulos opt-in (fuera del perfil; espacio para marcar, Enter para omitir):',
|
|
336
|
+
items: itemsOptIn,
|
|
337
|
+
});
|
|
338
|
+
if (elegidos === null) return { exito: false, cancelado: true };
|
|
339
|
+
modulosOptIn = elegidos;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// 6. Stacks (solo si perfil = completo)
|
|
290
343
|
let stacks = [];
|
|
291
|
-
if (profile === 'completo') {
|
|
344
|
+
if (profile === 'completo' && !soloHooks) {
|
|
292
345
|
const elegidos = await selectorMulti({
|
|
293
|
-
titulo: 'Paso
|
|
346
|
+
titulo: 'Paso 6/7 — Stacks de lenguajes adicionales (perfil completo):',
|
|
294
347
|
items: STACKS_ADICIONALES,
|
|
295
348
|
});
|
|
296
349
|
if (elegidos === null) return { exito: false, cancelado: true };
|
|
@@ -304,6 +357,8 @@ async function ejecutarWizardInstall() {
|
|
|
304
357
|
global: scope === 'global',
|
|
305
358
|
with_mcp: flagsSeleccionados.includes('with-mcp'),
|
|
306
359
|
dry_run: flagsSeleccionados.includes('dry-run'),
|
|
360
|
+
'solo-hooks': soloHooks,
|
|
361
|
+
with: modulosOptIn.join(',') || undefined,
|
|
307
362
|
all_langs: stacks.length > 0,
|
|
308
363
|
stacks,
|
|
309
364
|
};
|
|
@@ -314,7 +369,7 @@ async function ejecutarWizardInstall() {
|
|
|
314
369
|
|
|
315
370
|
// 7. Confirmación final (selector único con 2 opciones para claridad visual)
|
|
316
371
|
const confirmacion = await selectorUnico({
|
|
317
|
-
titulo: 'Paso
|
|
372
|
+
titulo: 'Paso 7/7 — Confirmar e iniciar instalación',
|
|
318
373
|
items: [
|
|
319
374
|
{
|
|
320
375
|
valor: 'si',
|
|
@@ -344,4 +399,5 @@ module.exports = {
|
|
|
344
399
|
FLAGS,
|
|
345
400
|
cargarPerfiles,
|
|
346
401
|
construirItemsPerfil,
|
|
402
|
+
cargarModulosOptIn,
|
|
347
403
|
};
|
package/scripts/validar.js
CHANGED
|
@@ -92,7 +92,7 @@ verificar(comandos.length >= 9, `Comandos: ${comandos.length} (mínimo 9)`);
|
|
|
92
92
|
// proyecto (node scripts/ | node hooks/ | require('./scripts|hooks/...')).
|
|
93
93
|
// Esas rutas no existen downstream; deben usar el subcomando del CLI.
|
|
94
94
|
// Ver docs/invocacion-cli-cross-scope.md.
|
|
95
|
-
const { auditarInvocacionesComandos } = require('./lib/auditar-invocaciones-comandos');
|
|
95
|
+
const { auditarInvocacionesComandos, auditarInvocacionesSkills } = require('./lib/auditar-invocaciones-comandos');
|
|
96
96
|
const auditInvoc = auditarInvocacionesComandos(path.join(RAIZ, 'comandos', 'swl'));
|
|
97
97
|
verificar(
|
|
98
98
|
auditInvoc.ok,
|
|
@@ -101,6 +101,17 @@ 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
|
+
// 7b-bis. Mismo gate sobre skills user-facing (DT-PATHS-DESTINO, cierre
|
|
105
|
+
// 2026-07-10): solo fences ejecutables (bash/sh/sin lenguaje); los bloques js
|
|
106
|
+
// de API llevan clarificador de layout. Skills host-only en SKILLS_SOLO_MADRE.
|
|
107
|
+
const auditInvocSkills = auditarInvocacionesSkills(path.join(RAIZ, 'habilidades'));
|
|
108
|
+
verificar(
|
|
109
|
+
auditInvocSkills.ok,
|
|
110
|
+
auditInvocSkills.ok
|
|
111
|
+
? `Cross-scope skills: ${auditInvocSkills.escaneados} skills sin rutas relativas al proyecto`
|
|
112
|
+
: `Cross-scope skills: ${auditInvocSkills.violaciones.length} invocación(es) relativa(s) — ${auditInvocSkills.violaciones.map(v => `${v.comando}:${v.linea}`).join(', ')}`
|
|
113
|
+
);
|
|
114
|
+
|
|
104
115
|
// 7c. Gate inverso de evolución (Fase 16, REQ-16-03): el FUENTE no debe portar
|
|
105
116
|
// marcadores evolved (frontmatter evolved:true ni sidecar .evolved.json). Un
|
|
106
117
|
// marcador en el repo madre es shipped-evolved espurio que congela el componente
|
|
@@ -166,6 +177,34 @@ try {
|
|
|
166
177
|
}
|
|
167
178
|
} catch { /* ignore */ }
|
|
168
179
|
|
|
180
|
+
// 15. Procedencia de evoluciones aplicadas (DT-AGP-SCORE-AUTODECLARADO):
|
|
181
|
+
// toda entrada tipo:aplicada en evoluciones.jsonl debe llevar la cadena de
|
|
182
|
+
// procedencia del score (producido_por ≠ target + evaluacion_path). Cutoff
|
|
183
|
+
// 2026-07-10: fecha en que --record-after empezó a exigir el artefacto —
|
|
184
|
+
// entradas previas son legacy sin contrato.
|
|
185
|
+
try {
|
|
186
|
+
const evolLog = path.join(RAIZ, '.planning', 'evolution', 'evoluciones.jsonl');
|
|
187
|
+
if (fs.existsSync(evolLog)) {
|
|
188
|
+
const CUTOFF_PROCEDENCIA = Date.parse('2026-07-10T00:00:00Z');
|
|
189
|
+
const sinProcedencia = fs.readFileSync(evolLog, 'utf-8')
|
|
190
|
+
.split(/\r?\n/)
|
|
191
|
+
.filter(Boolean)
|
|
192
|
+
.map(l => { try { return JSON.parse(l); } catch { return null; } })
|
|
193
|
+
.filter(e => e && e.tipo === 'aplicada' && Date.parse(e.ts || '') >= CUTOFF_PROCEDENCIA)
|
|
194
|
+
.filter(e =>
|
|
195
|
+
!e.producido_por ||
|
|
196
|
+
e.producido_por === e.target ||
|
|
197
|
+
!e.evaluacion_path
|
|
198
|
+
);
|
|
199
|
+
verificar(
|
|
200
|
+
sinProcedencia.length === 0,
|
|
201
|
+
sinProcedencia.length === 0
|
|
202
|
+
? 'Evoluciones aplicadas: cadena de procedencia completa (anti-gaming)'
|
|
203
|
+
: `Evoluciones aplicadas SIN procedencia válida: ${sinProcedencia.map(e => `${e.target}@${e.ts}`).join(', ')} — el score debe venir del artefacto de /swl:evaluar-skill`
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
} catch { /* log ilegible: no bloquear la validación estructural */ }
|
|
207
|
+
|
|
169
208
|
console.log('\n' + '='.repeat(40));
|
|
170
209
|
if (errores === 0) {
|
|
171
210
|
console.log('Todas las validaciones pasaron.');
|