@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
package/scripts/instalador.js
CHANGED
|
@@ -21,7 +21,7 @@ const { preguntarSiNo, ES_TTY } = require('./lib/ui');
|
|
|
21
21
|
const { resolverPerfil, generarPlan } = require('./lib/manifiestos');
|
|
22
22
|
const { crearEstado, registrarArchivo, registrarComponente, guardarEstado, cargarEstado } = require('./lib/estado');
|
|
23
23
|
const { validarRuta, esArchivoSensible, filtrarSecretos } = require('./lib/seguridad');
|
|
24
|
-
const { registrarHooks, registrarOptimizaciones, rutaSettings, cargarHooksConfig } = require('./lib/hooks-settings');
|
|
24
|
+
const { registrarHooks, registrarOptimizaciones, rutaSettings, cargarHooksConfig, hooksSWLRegistrados } = require('./lib/hooks-settings');
|
|
25
25
|
const { obtenerTransformador } = require('./lib/transformadores');
|
|
26
26
|
const {
|
|
27
27
|
esProtegido,
|
|
@@ -41,6 +41,7 @@ const { canonicalHash } = require('./lib/canonical-hash');
|
|
|
41
41
|
const { auditarEscritura } = require('./lib/audit-evolved');
|
|
42
42
|
const { detectarStack, filtrarReglasPorStack } = require('./lib/detectar-stack');
|
|
43
43
|
const { actualizarGitignore, entradasParaRuntime, limpiarTracked, leerManifest, escribirManifest } = require('./lib/gitignore-manifest');
|
|
44
|
+
const { limpiarBasuraGlobal } = require('./lib/limpiar-basura-global');
|
|
44
45
|
|
|
45
46
|
const RAIZ_PKG = path.resolve(__dirname, '..');
|
|
46
47
|
const VERSION = require('../package.json').version;
|
|
@@ -164,6 +165,28 @@ async function install(opciones) {
|
|
|
164
165
|
|| perfilDeState
|
|
165
166
|
|| 'completo';
|
|
166
167
|
|
|
168
|
+
// DT-MODULOS-OPT-IN: preservar los módulos extra (--with) registrados por
|
|
169
|
+
// instalaciones previas en el install-state. Corre SIEMPRE (independiente
|
|
170
|
+
// de si el usuario pasó --profile): sin esto, cada update/reinstalación
|
|
171
|
+
// perdía en silencio los módulos opt-in (p. ej. reglas-personales).
|
|
172
|
+
let extrasDeState = [];
|
|
173
|
+
try {
|
|
174
|
+
const runtimeParaState = obtenerRuntime(target);
|
|
175
|
+
const dirDestino = esGlobal ? runtimeParaState.global : path.resolve(runtimeParaState.local);
|
|
176
|
+
const stateExistente = fs.existsSync(dirDestino) ? cargarEstado(dirDestino) : null;
|
|
177
|
+
if (stateExistente && Array.isArray(stateExistente.modulosExtra) && stateExistente.modulosExtra.length > 0) {
|
|
178
|
+
extrasDeState = stateExistente.modulosExtra;
|
|
179
|
+
console.log(`[install-state] Preservando módulos extra previos: ${extrasDeState.join(', ')}`);
|
|
180
|
+
console.log(`[install-state] Para quitarlos pasa --without <modulo> explícito.`);
|
|
181
|
+
}
|
|
182
|
+
} catch { /* state ilegible: seguir solo con --with del CLI */ }
|
|
183
|
+
|
|
184
|
+
// Unión de los extras del CLI (--with) y los preservados del state.
|
|
185
|
+
const withCombinado = [
|
|
186
|
+
...String(opciones.with || '').split(',').map(s => s.trim()).filter(Boolean),
|
|
187
|
+
...extrasDeState,
|
|
188
|
+
].filter((m, i, arr) => arr.indexOf(m) === i);
|
|
189
|
+
|
|
167
190
|
console.log(`swl-ses install v${VERSION}`);
|
|
168
191
|
console.log(`${'='.repeat(40)}`);
|
|
169
192
|
|
|
@@ -230,7 +253,7 @@ async function install(opciones) {
|
|
|
230
253
|
try {
|
|
231
254
|
resolucion = resolverPerfil(perfil, {
|
|
232
255
|
target,
|
|
233
|
-
with:
|
|
256
|
+
with: withCombinado.join(',') || undefined,
|
|
234
257
|
without: opciones.without,
|
|
235
258
|
});
|
|
236
259
|
} catch (err) {
|
|
@@ -330,6 +353,18 @@ async function install(opciones) {
|
|
|
330
353
|
}
|
|
331
354
|
}
|
|
332
355
|
|
|
356
|
+
// Scope global: purgar directorios basura sembrados en el HOME por versiones
|
|
357
|
+
// previas: ~/.planning/ scaffold (doctor confundía el HOME con un proyecto por
|
|
358
|
+
// ~/_userland) y ~/_userland/ vacío. Conservador: solo el .planning con la
|
|
359
|
+
// huella del scaffold y el _userland sin plugins reales, nunca contenido del
|
|
360
|
+
// usuario. Ver scripts/lib/limpiar-basura-global.js.
|
|
361
|
+
if (esGlobal) {
|
|
362
|
+
const basura = limpiarBasuraGlobal();
|
|
363
|
+
if (basura.eliminados.length > 0) {
|
|
364
|
+
console.log(`\n[global] Basura purgada del HOME: ${basura.eliminados.join(', ')}.`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
333
368
|
// 4. Detectar _userland/
|
|
334
369
|
const userlandAgentes = path.join(process.cwd(), '_userland', 'agentes');
|
|
335
370
|
const userlandHabilidades = path.join(process.cwd(), '_userland', 'habilidades');
|
|
@@ -575,6 +610,9 @@ async function install(opciones) {
|
|
|
575
610
|
// desde el cwd actual del doctor). Fix v1.4.3.
|
|
576
611
|
allLangs: allLangs === true,
|
|
577
612
|
stackInstalado: stackDetectado instanceof Set ? [...stackDetectado] : null,
|
|
613
|
+
// Solo los extras que el perfil realmente resolvió (existen y no fueron
|
|
614
|
+
// excluidos con --without) — DT-MODULOS-OPT-IN.
|
|
615
|
+
modulosExtra: withCombinado.filter(m => resolucion.modulos.includes(m)),
|
|
578
616
|
});
|
|
579
617
|
|
|
580
618
|
let instalados = 0;
|
|
@@ -727,6 +765,19 @@ async function install(opciones) {
|
|
|
727
765
|
registrarComponente(estado, modulo);
|
|
728
766
|
}
|
|
729
767
|
|
|
768
|
+
// Persistencia INTERMEDIA (DT-INSTALL-STATE-TRANSACCIONAL, nemesis sub1-S3):
|
|
769
|
+
// todos los archivos ya están en disco. Sin este save, una interrupción
|
|
770
|
+
// durante el registro de hooks (settings.json) o la generación de
|
|
771
|
+
// CLAUDE.md/AGENTS.md dejaría archivos copiados que el install-state NO
|
|
772
|
+
// registra → huérfanos que ni doctor ni uninstall ven. guardarEstado es
|
|
773
|
+
// atómico e idempotente; el save final (abajo) fija hooksRegistrados.
|
|
774
|
+
// Best-effort: un fallo aquí no aborta la instalación.
|
|
775
|
+
try {
|
|
776
|
+
guardarEstado(estado, rutas.base);
|
|
777
|
+
} catch (err) {
|
|
778
|
+
console.error(` ! Aviso: no se pudo persistir el estado intermedio: ${err.message}`);
|
|
779
|
+
}
|
|
780
|
+
|
|
730
781
|
// 7. Registrar hooks en settings.json del runtime
|
|
731
782
|
// Los hooks solo se registran a nivel PROYECTO (no global) para evitar
|
|
732
783
|
// ejecucion duplicada cuando Claude Code combina hooks de ambos scopes.
|
|
@@ -738,8 +789,15 @@ async function install(opciones) {
|
|
|
738
789
|
// No filtrar por tipo de módulo porque hooks como grafo-contexto.js
|
|
739
790
|
// pueden pertenecer a módulos con tipo distinto a "hooks".
|
|
740
791
|
const hooksConfig = cargarHooksConfig();
|
|
741
|
-
|
|
742
|
-
|
|
792
|
+
// Registrar SOLO los hooks realmente copiados (estado.archivosInstalados),
|
|
793
|
+
// no los del PLAN (resolucion.archivos) — un hook cuyo .js falló al copiar
|
|
794
|
+
// (throw, colisión, origen inexistente) no debe quedar en settings.json
|
|
795
|
+
// apuntando a un archivo ausente: el wrapper traga el MODULE_NOT_FOUND y
|
|
796
|
+
// el hook muere en silencio (DT-REGISTRAR-HOOKS-PLAN, nemesis sub1-S2).
|
|
797
|
+
// Los hooks nunca entran a la rama `evolucionado` (no son componentes
|
|
798
|
+
// evolucionables) → un hook exitoso siempre queda en archivosInstalados.
|
|
799
|
+
const hookFilesInstalados = (estado.archivosInstalados || [])
|
|
800
|
+
.filter(a => (a.origen || '').endsWith('.js') && hooksConfig[path.basename(a.origen)])
|
|
743
801
|
.map(a => path.basename(a.origen));
|
|
744
802
|
|
|
745
803
|
if (hookFilesInstalados.length > 0) {
|
|
@@ -748,12 +806,22 @@ async function install(opciones) {
|
|
|
748
806
|
console.log(' Los hooks se activan al instalar a nivel proyecto con: npx @saulwade/swl-ses@latest install --target claude');
|
|
749
807
|
} else {
|
|
750
808
|
const settingsPath = rutaSettings(runtime, rutas.base);
|
|
809
|
+
// UNIÓN con los hooks SWL YA registrados cuyo .js sigue en disco:
|
|
810
|
+
// registrarHooks reconstruye la sección SWL de settings.json, así que
|
|
811
|
+
// pasar solo lo recién copiado DESREGISTRARÍA los hooks pre-existentes
|
|
812
|
+
// que colisionaron sin --force pero siguen funcionando (regresión
|
|
813
|
+
// detectada por la re-auditoría nemesis iter-2). El plan-base ya no se
|
|
814
|
+
// usa: solo entra lo copiado ∪ lo pre-registrado-con-archivo-en-disco.
|
|
815
|
+
const preRegistrados = hooksSWLRegistrados(settingsPath).filter(
|
|
816
|
+
nombre => cargarHooksConfig()[nombre] && fs.existsSync(path.join(rutas.hooks, nombre))
|
|
817
|
+
);
|
|
818
|
+
const hookFilesFinal = [...new Set([...hookFilesInstalados, ...preRegistrados])];
|
|
751
819
|
try {
|
|
752
820
|
const resultado = registrarHooks({
|
|
753
821
|
settingsPath,
|
|
754
822
|
hooksDir: rutas.hooks,
|
|
755
823
|
esGlobal: false,
|
|
756
|
-
hookFiles:
|
|
824
|
+
hookFiles: hookFilesFinal,
|
|
757
825
|
});
|
|
758
826
|
hooksRegistrados = resultado.registrados;
|
|
759
827
|
hooksPreservados = resultado.preservados;
|
|
@@ -1172,12 +1240,35 @@ function instalarArchivo(archivo, rutas, runtime, opciones = {}) {
|
|
|
1172
1240
|
} else {
|
|
1173
1241
|
destino = path.join(dirDestino, nombreArchivo);
|
|
1174
1242
|
}
|
|
1243
|
+
} else if (
|
|
1244
|
+
archivo.tipo === 'hooks' &&
|
|
1245
|
+
archivo.rutaRelativa &&
|
|
1246
|
+
(archivo.rutaRelativa.startsWith('hooks/lib/') || archivo.rutaRelativa.startsWith('scripts/lib/'))
|
|
1247
|
+
) {
|
|
1248
|
+
// Contrato de layout de hooks: los hooks requieren sus libs como
|
|
1249
|
+
// './lib/X' (y check-update/session-briefing usan ese fallback
|
|
1250
|
+
// explícito). El aplanado total dejaba TODOS esos require() muertos en
|
|
1251
|
+
// instalaciones frescas — MODULE_NOT_FOUND tragado por el wrapper de
|
|
1252
|
+
// settings; los installs de campo sobrevivían por el lib/ residual de
|
|
1253
|
+
// versiones previas. Preservar el subárbol lib/ (incluye subdirs como
|
|
1254
|
+
// lib/ciclo-autonomo/) restaura el contrato. scripts/ NO-lib sigue
|
|
1255
|
+
// aplanado en la raíz de hooks (status quo: rotar-audit-logs, etc.).
|
|
1256
|
+
const resto = archivo.rutaRelativa.replace(/^(?:hooks|scripts)\/lib\//, '');
|
|
1257
|
+
const partes = resto.split('/');
|
|
1258
|
+
const subdirLib = path.join(dirDestino, 'lib', ...partes.slice(0, -1));
|
|
1259
|
+
if (!fs.existsSync(subdirLib)) {
|
|
1260
|
+
fs.mkdirSync(subdirLib, { recursive: true });
|
|
1261
|
+
}
|
|
1262
|
+
destino = path.join(subdirLib, partes[partes.length - 1]);
|
|
1175
1263
|
} else {
|
|
1176
1264
|
destino = path.join(dirDestino, nombreArchivo);
|
|
1177
1265
|
}
|
|
1178
1266
|
|
|
1179
|
-
// Seguridad: validar
|
|
1180
|
-
|
|
1267
|
+
// Seguridad: validar el DESTINO calculado completo, no solo el basename —
|
|
1268
|
+
// cubre también las ramas con subdirectorios (reglas/lenguajes, hooks/lib)
|
|
1269
|
+
// contra un rutaRelativa con traversal (endurecimiento, revisor-seguridad
|
|
1270
|
+
// 2026-07-10; el manifiesto es artefacto confiable, defensa en profundidad).
|
|
1271
|
+
validarRuta(dirDestino, path.relative(dirDestino, destino));
|
|
1181
1272
|
|
|
1182
1273
|
// Verificar archivo sensible
|
|
1183
1274
|
if (esArchivoSensible(nombreArchivo)) {
|
|
@@ -1,104 +1,116 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* activar-hooks-proyecto.js — Camino "solo hooks" (DT-HOOKS-SOLO-PROYECTO).
|
|
5
|
-
*
|
|
6
|
-
* Registra en el `.claude/settings.json` del PROYECTO los hooks del scope
|
|
7
|
-
* GLOBAL ya instalado, sin copiar ningún componente. Cierra el dilema del
|
|
8
|
-
* install local: los hooks solo funcionan registrados a nivel proyecto
|
|
9
|
-
* (ADR-0009), pero el install local completo duplica agentes/comandos en la
|
|
10
|
-
* UI de Claude Code (lee ambos scopes). Con este camino:
|
|
11
|
-
*
|
|
12
|
-
* - Los comandos del settings apuntan con ruta ABSOLUTA al hooks/ global
|
|
13
|
-
* (esGlobal: true en registrarHooks) → cero archivos copiados.
|
|
14
|
-
* - `swl-ses update` del global refresca los hooks de TODOS los proyectos
|
|
15
|
-
* activados así, gratis (apuntan a los mismos archivos).
|
|
16
|
-
* - Hooks externos (no-SWL) del settings del proyecto se preservan
|
|
17
|
-
* (garantía de registrarHooks).
|
|
18
|
-
*
|
|
19
|
-
* Uso: `swl-ses install --target claude --solo-hooks` (en la raíz del
|
|
20
|
-
* proyecto). Requiere instalación global previa del runtime.
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
const fs = require('fs');
|
|
24
|
-
const path = require('path');
|
|
25
|
-
|
|
26
|
-
const RUNTIMES_SOPORTADOS = new Set(['claude', 'openclaude']);
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* @param {object} opciones
|
|
30
|
-
* @param {string} [opciones.target='claude'] Runtime (claude|openclaude — formato de settings de Claude Code)
|
|
31
|
-
* @param {string} [opciones.dirProyecto=cwd] Raíz del proyecto destino
|
|
32
|
-
* @param {string} [opciones.hooksDirGlobal] Override del hooks/ global (tests)
|
|
33
|
-
* @param {object} [opciones.deps] Inyección para tests: { registrarHooks, registrarOptimizaciones, cargarHooksConfig, scopesReales, obtenerRuntime }
|
|
34
|
-
* @returns {{ok: boolean, error?: string, settingsPath?: string, hooksDirGlobal?: string, registrados?: number, preservados?: number}}
|
|
35
|
-
*/
|
|
36
|
-
function activarHooksProyecto(opciones = {}) {
|
|
37
|
-
const target = opciones.target || 'claude';
|
|
38
|
-
if (!RUNTIMES_SOPORTADOS.has(target)) {
|
|
39
|
-
return { ok: false, error: `--solo-hooks soporta por ahora targets con settings formato Claude Code (claude, openclaude); recibido: "${target}".` };
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const deps = opciones.deps || {};
|
|
43
|
-
const hs = require('./hooks-settings');
|
|
44
|
-
const registrarHooks = deps.registrarHooks || hs.registrarHooks;
|
|
45
|
-
const registrarOptimizaciones = deps.registrarOptimizaciones || hs.registrarOptimizaciones;
|
|
46
|
-
const cargarHooksConfig = deps.cargarHooksConfig || hs.cargarHooksConfig;
|
|
47
|
-
|
|
48
|
-
// 1. Localizar el hooks/ del scope GLOBAL del runtime.
|
|
49
|
-
let hooksDirGlobal = opciones.hooksDirGlobal;
|
|
50
|
-
if (!hooksDirGlobal) {
|
|
51
|
-
try {
|
|
52
|
-
const dr = require('./detectar-runtime');
|
|
53
|
-
const obtenerRuntime = deps.obtenerRuntime || dr.obtenerRuntime;
|
|
54
|
-
const scopesReales = deps.scopesReales || dr.scopesReales;
|
|
55
|
-
const runtime = obtenerRuntime(target);
|
|
56
|
-
const global = scopesReales(runtime).find(s => s.esGlobal);
|
|
57
|
-
if (!global) return { ok: false, error: `No se pudo resolver el scope global del runtime "${target}".` };
|
|
58
|
-
hooksDirGlobal = path.join(global.dir, 'hooks');
|
|
59
|
-
} catch (err) {
|
|
60
|
-
return { ok: false, error: `No se pudo detectar el runtime "${target}": ${err.message}` };
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
if (!fs.existsSync(hooksDirGlobal)) {
|
|
64
|
-
return { ok: false, error: `No hay instalación global con hooks en ${hooksDirGlobal}. Instala primero el global: npx -y @saulwade/swl-ses@latest install --target ${target} --global --perfil completo` };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// 2. Hooks disponibles = archivos del global ∩ hooks-config del paquete.
|
|
68
|
-
const hooksConfig = cargarHooksConfig();
|
|
69
|
-
const hookFiles = fs.readdirSync(hooksDirGlobal)
|
|
70
|
-
.filter(f => f.endsWith('.js') && hooksConfig[f]);
|
|
71
|
-
if (hookFiles.length === 0) {
|
|
72
|
-
return { ok: false, error: `El hooks/ global (${hooksDirGlobal}) no contiene hooks reconocidos por hooks-config.json — instalación global corrupta o muy antigua; corre update primero.` };
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// 3. Registrar en el settings.json del PROYECTO, apuntando al global.
|
|
76
|
-
const dirProyecto = opciones.dirProyecto || process.cwd();
|
|
77
|
-
const dirClaude = path.join(dirProyecto, '.claude');
|
|
78
|
-
fs.mkdirSync(dirClaude, { recursive: true });
|
|
79
|
-
const settingsPath = path.join(dirClaude, 'settings.json');
|
|
80
|
-
|
|
81
|
-
const r = registrarHooks({
|
|
82
|
-
settingsPath,
|
|
83
|
-
hooksDir: hooksDirGlobal,
|
|
84
|
-
esGlobal: true, // semántica: comandos con ruta ABSOLUTA al hooksDir (que es el global)
|
|
85
|
-
hookFiles,
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
let optimizaciones = 0;
|
|
89
|
-
try {
|
|
90
|
-
const opt = registrarOptimizaciones(settingsPath);
|
|
91
|
-
optimizaciones = (opt && opt.registradas) || 0;
|
|
92
|
-
} catch { /* opcional — no bloquea la activación */ }
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* activar-hooks-proyecto.js — Camino "solo hooks" (DT-HOOKS-SOLO-PROYECTO).
|
|
5
|
+
*
|
|
6
|
+
* Registra en el `.claude/settings.json` del PROYECTO los hooks del scope
|
|
7
|
+
* GLOBAL ya instalado, sin copiar ningún componente. Cierra el dilema del
|
|
8
|
+
* install local: los hooks solo funcionan registrados a nivel proyecto
|
|
9
|
+
* (ADR-0009), pero el install local completo duplica agentes/comandos en la
|
|
10
|
+
* UI de Claude Code (lee ambos scopes). Con este camino:
|
|
11
|
+
*
|
|
12
|
+
* - Los comandos del settings apuntan con ruta ABSOLUTA al hooks/ global
|
|
13
|
+
* (esGlobal: true en registrarHooks) → cero archivos copiados.
|
|
14
|
+
* - `swl-ses update` del global refresca los hooks de TODOS los proyectos
|
|
15
|
+
* activados así, gratis (apuntan a los mismos archivos).
|
|
16
|
+
* - Hooks externos (no-SWL) del settings del proyecto se preservan
|
|
17
|
+
* (garantía de registrarHooks).
|
|
18
|
+
*
|
|
19
|
+
* Uso: `swl-ses install --target claude --solo-hooks` (en la raíz del
|
|
20
|
+
* proyecto). Requiere instalación global previa del runtime.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
|
|
26
|
+
const RUNTIMES_SOPORTADOS = new Set(['claude', 'openclaude']);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {object} opciones
|
|
30
|
+
* @param {string} [opciones.target='claude'] Runtime (claude|openclaude — formato de settings de Claude Code)
|
|
31
|
+
* @param {string} [opciones.dirProyecto=cwd] Raíz del proyecto destino
|
|
32
|
+
* @param {string} [opciones.hooksDirGlobal] Override del hooks/ global (tests)
|
|
33
|
+
* @param {object} [opciones.deps] Inyección para tests: { registrarHooks, registrarOptimizaciones, cargarHooksConfig, scopesReales, obtenerRuntime }
|
|
34
|
+
* @returns {{ok: boolean, error?: string, settingsPath?: string, hooksDirGlobal?: string, registrados?: number, preservados?: number}}
|
|
35
|
+
*/
|
|
36
|
+
function activarHooksProyecto(opciones = {}) {
|
|
37
|
+
const target = opciones.target || 'claude';
|
|
38
|
+
if (!RUNTIMES_SOPORTADOS.has(target)) {
|
|
39
|
+
return { ok: false, error: `--solo-hooks soporta por ahora targets con settings formato Claude Code (claude, openclaude); recibido: "${target}".` };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const deps = opciones.deps || {};
|
|
43
|
+
const hs = require('./hooks-settings');
|
|
44
|
+
const registrarHooks = deps.registrarHooks || hs.registrarHooks;
|
|
45
|
+
const registrarOptimizaciones = deps.registrarOptimizaciones || hs.registrarOptimizaciones;
|
|
46
|
+
const cargarHooksConfig = deps.cargarHooksConfig || hs.cargarHooksConfig;
|
|
47
|
+
|
|
48
|
+
// 1. Localizar el hooks/ del scope GLOBAL del runtime.
|
|
49
|
+
let hooksDirGlobal = opciones.hooksDirGlobal;
|
|
50
|
+
if (!hooksDirGlobal) {
|
|
51
|
+
try {
|
|
52
|
+
const dr = require('./detectar-runtime');
|
|
53
|
+
const obtenerRuntime = deps.obtenerRuntime || dr.obtenerRuntime;
|
|
54
|
+
const scopesReales = deps.scopesReales || dr.scopesReales;
|
|
55
|
+
const runtime = obtenerRuntime(target);
|
|
56
|
+
const global = scopesReales(runtime).find(s => s.esGlobal);
|
|
57
|
+
if (!global) return { ok: false, error: `No se pudo resolver el scope global del runtime "${target}".` };
|
|
58
|
+
hooksDirGlobal = path.join(global.dir, 'hooks');
|
|
59
|
+
} catch (err) {
|
|
60
|
+
return { ok: false, error: `No se pudo detectar el runtime "${target}": ${err.message}` };
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (!fs.existsSync(hooksDirGlobal)) {
|
|
64
|
+
return { ok: false, error: `No hay instalación global con hooks en ${hooksDirGlobal}. Instala primero el global: npx -y @saulwade/swl-ses@latest install --target ${target} --global --perfil completo` };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 2. Hooks disponibles = archivos del global ∩ hooks-config del paquete.
|
|
68
|
+
const hooksConfig = cargarHooksConfig();
|
|
69
|
+
const hookFiles = fs.readdirSync(hooksDirGlobal)
|
|
70
|
+
.filter(f => f.endsWith('.js') && hooksConfig[f]);
|
|
71
|
+
if (hookFiles.length === 0) {
|
|
72
|
+
return { ok: false, error: `El hooks/ global (${hooksDirGlobal}) no contiene hooks reconocidos por hooks-config.json — instalación global corrupta o muy antigua; corre update primero.` };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 3. Registrar en el settings.json del PROYECTO, apuntando al global.
|
|
76
|
+
const dirProyecto = opciones.dirProyecto || process.cwd();
|
|
77
|
+
const dirClaude = path.join(dirProyecto, '.claude');
|
|
78
|
+
fs.mkdirSync(dirClaude, { recursive: true });
|
|
79
|
+
const settingsPath = path.join(dirClaude, 'settings.json');
|
|
80
|
+
|
|
81
|
+
const r = registrarHooks({
|
|
82
|
+
settingsPath,
|
|
83
|
+
hooksDir: hooksDirGlobal,
|
|
84
|
+
esGlobal: true, // semántica: comandos con ruta ABSOLUTA al hooksDir (que es el global)
|
|
85
|
+
hookFiles,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
let optimizaciones = 0;
|
|
89
|
+
try {
|
|
90
|
+
const opt = registrarOptimizaciones(settingsPath);
|
|
91
|
+
optimizaciones = (opt && opt.registradas) || 0;
|
|
92
|
+
} catch { /* opcional — no bloquea la activación */ }
|
|
93
|
+
|
|
94
|
+
// 4. Sembrar el .gitignore del proyecto: encender la telemetría hace que
|
|
95
|
+
// los hooks ESCRIBAN (.planning/METRICAS.md, audit*.jsonl, evolution/...)
|
|
96
|
+
// — sin la sección swl-ses esos archivos runtime aparecen como cambios
|
|
97
|
+
// pendientes en el editor del usuario (caso real sistema-verificacion-oic,
|
|
98
|
+
// 2026-07-10: 19 archivos sin trackear tras activar hooks).
|
|
99
|
+
let gitignorePath = null;
|
|
100
|
+
try {
|
|
101
|
+
const { actualizarGitignore } = deps.gitignoreManifest || require('./gitignore-manifest');
|
|
102
|
+
gitignorePath = actualizarGitignore(dirProyecto);
|
|
103
|
+
} catch { /* no bloquea la activación — se reporta null */ }
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
ok: true,
|
|
107
|
+
settingsPath,
|
|
108
|
+
hooksDirGlobal,
|
|
109
|
+
registrados: r.registrados,
|
|
110
|
+
preservados: r.preservados,
|
|
111
|
+
optimizaciones,
|
|
112
|
+
gitignorePath,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
module.exports = { activarHooksProyecto, RUNTIMES_SOPORTADOS };
|
|
@@ -87,17 +87,107 @@ function auditarInvocacionesComandos(comandosDir) {
|
|
|
87
87
|
return { ok: violaciones.length === 0, violaciones, escaneados };
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
// Skills que corren SOLO en el repo madre (host-only declarado en su propio
|
|
91
|
+
// frontmatter/cuerpo): sus `node scripts/...` son legítimos porque el CWD ES
|
|
92
|
+
// el repo (DT-PATHS-DESTINO, cierre 2026-07-10).
|
|
93
|
+
const SKILLS_SOLO_MADRE = new Set([
|
|
94
|
+
'doc-sync',
|
|
95
|
+
'release-semver',
|
|
96
|
+
'validacion-ci-sistema',
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
// Lenguajes de fence que se auditan en skills: solo instrucciones ejecutables
|
|
100
|
+
// de shell. Los bloques js/javascript documentan APIs de módulos (referencia,
|
|
101
|
+
// no comandos a ejecutar downstream) y llevan su clarificador de layout.
|
|
102
|
+
const LENGUAJES_EJECUTABLES = new Set(['', 'bash', 'sh', 'shell']);
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Igual que auditarInvocacionesComandos pero sobre habilidades/<skill>/SKILL.md.
|
|
106
|
+
* Solo audita fences ejecutables (bash/sh/shell/sin lenguaje).
|
|
107
|
+
* @param {string} [habilidadesDir]
|
|
108
|
+
* @returns {{ok:boolean, violaciones:Array<{comando:string,linea:number,texto:string,patron:string}>, escaneados:number}}
|
|
109
|
+
*/
|
|
110
|
+
function auditarInvocacionesSkills(habilidadesDir) {
|
|
111
|
+
const dir = habilidadesDir || path.join(__dirname, '..', '..', 'habilidades');
|
|
112
|
+
const violaciones = [];
|
|
113
|
+
let escaneados = 0;
|
|
114
|
+
|
|
115
|
+
let skills;
|
|
116
|
+
try {
|
|
117
|
+
skills = fs.readdirSync(dir, { withFileTypes: true })
|
|
118
|
+
.filter((d) => d.isDirectory())
|
|
119
|
+
.map((d) => d.name);
|
|
120
|
+
} catch {
|
|
121
|
+
return { ok: true, violaciones, escaneados: 0 };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
for (const skill of skills) {
|
|
125
|
+
if (SKILLS_SOLO_MADRE.has(skill)) continue;
|
|
126
|
+
const ruta = path.join(dir, skill, 'SKILL.md');
|
|
127
|
+
let contenido;
|
|
128
|
+
try {
|
|
129
|
+
contenido = fs.readFileSync(ruta, 'utf8');
|
|
130
|
+
} catch {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
escaneados++;
|
|
134
|
+
|
|
135
|
+
const lineas = contenido.split('\n');
|
|
136
|
+
let dentroCodigo = false;
|
|
137
|
+
let lenguaje = '';
|
|
138
|
+
let marcadores = 0;
|
|
139
|
+
|
|
140
|
+
for (let i = 0; i < lineas.length; i++) {
|
|
141
|
+
const linea = lineas[i];
|
|
142
|
+
const fence = linea.match(/^\s*```\s*(\S*)/);
|
|
143
|
+
if (fence) {
|
|
144
|
+
marcadores++;
|
|
145
|
+
dentroCodigo = !dentroCodigo;
|
|
146
|
+
lenguaje = dentroCodigo ? fence[1].toLowerCase() : '';
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
if (!dentroCodigo || !LENGUAJES_EJECUTABLES.has(lenguaje)) continue;
|
|
150
|
+
for (const { re, desc } of PATRONES) {
|
|
151
|
+
if (re.test(linea)) {
|
|
152
|
+
violaciones.push({
|
|
153
|
+
comando: `habilidades/${skill}/SKILL.md`,
|
|
154
|
+
linea: i + 1,
|
|
155
|
+
texto: linea.trim(),
|
|
156
|
+
patron: desc,
|
|
157
|
+
});
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Fences desbalanceados (conteo impar de ```): el toggle booleano quedó
|
|
163
|
+
// invertido para el resto del archivo y pudo silenciar violaciones reales.
|
|
164
|
+
// Se reporta como violación para que el scan NO termine en verde falso.
|
|
165
|
+
if (marcadores % 2 !== 0) {
|
|
166
|
+
violaciones.push({
|
|
167
|
+
comando: `habilidades/${skill}/SKILL.md`,
|
|
168
|
+
linea: 0,
|
|
169
|
+
texto: `${marcadores} marcadores \`\`\` (impar) — fence sin cerrar o anidado`,
|
|
170
|
+
patron: 'fences desbalanceados (el scan de invocaciones pudo quedar ciego)',
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return { ok: violaciones.length === 0, violaciones, escaneados };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
module.exports = { auditarInvocacionesComandos, auditarInvocacionesSkills, SELF_DEV, SKILLS_SOLO_MADRE };
|
|
91
179
|
|
|
92
180
|
if (require.main === module) {
|
|
93
181
|
const r = auditarInvocacionesComandos();
|
|
94
|
-
|
|
95
|
-
|
|
182
|
+
const rs = auditarInvocacionesSkills();
|
|
183
|
+
if (r.ok && rs.ok) {
|
|
184
|
+
console.log(`[OK] invocaciones-comandos — ${r.escaneados} comandos + ${rs.escaneados} skills user-facing sin rutas relativas al proyecto`);
|
|
96
185
|
process.exit(0);
|
|
97
186
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
187
|
+
const todas = [...r.violaciones, ...rs.violaciones];
|
|
188
|
+
console.error(`[FALLA] invocaciones-comandos — ${todas.length} invocación(es) relativa(s) al proyecto en componentes user-facing:`);
|
|
189
|
+
for (const v of todas) {
|
|
190
|
+
console.error(` ${v.comando}:${v.linea} [${v.patron}] ${v.texto}`);
|
|
101
191
|
}
|
|
102
192
|
console.error('\nUsa el patrón cross-scope (docs/invocacion-cli-cross-scope.md): swl-ses <sub> / npx ... <sub> / node scripts/cli/<sub>.js');
|
|
103
193
|
process.exit(1);
|