@saulwade/swl-ses 1.5.1 → 1.6.0
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 +225 -209
- package/README.md +578 -561
- package/agentes/arquitecto-swl.md +33 -1
- package/agentes/nemesis-auditor-swl.md +59 -19
- package/bin/swl-mcp-server.js +214 -214
- package/bin/swl-ses.js +49 -7
- package/comandos/swl/.evolved.json +22 -22
- package/comandos/swl/contribuir.md +233 -233
- package/comandos/swl/nemesis.md +230 -56
- package/gateway/lib/event-channel.js +191 -191
- package/habilidades/backend-production-resilience/SKILL.md +288 -288
- package/habilidades/benchmark-memoria/SKILL.md +186 -186
- package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
- package/habilidades/doubt-driven-review/SKILL.md +171 -171
- package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
- package/habilidades/ejecutar-task-iterativo/SKILL.md +278 -278
- package/habilidades/eval-framework/SKILL.md +212 -212
- package/habilidades/feynman-auditor-swl/SKILL.md +123 -123
- package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -108
- package/habilidades/harness-claude-code/SKILL.md +299 -299
- package/habilidades/infra-github-actions/SKILL.md +166 -166
- package/habilidades/legacy-code-rescue/SKILL.md +267 -267
- package/habilidades/manejo-errores/.evolved.json +8 -8
- package/habilidades/meta-skills-estandar/SKILL.md +207 -4
- package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
- package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -163
- package/habilidades/nemesis-evaluacion-json/SKILL.md +266 -0
- package/habilidades/nemesis-redistribuir/SKILL.md +341 -0
- package/habilidades/node-experto/SKILL.md +94 -4
- package/habilidades/patrones-python/SKILL.md +229 -229
- package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
- package/habilidades/planear-fase/SKILL.md +319 -319
- package/habilidades/protocolo-revision-swl/SKILL.md +350 -276
- package/habilidades/release-semver/.evolved.json +8 -8
- package/habilidades/state-inconsistency-auditor-swl/SKILL.md +166 -166
- package/habilidades/state-inconsistency-auditor-swl/recursos/coupled-state-patterns.md +147 -147
- package/habilidades/tdd-workflow/SKILL.md +121 -4
- package/habilidades/testing-python/SKILL.md +340 -340
- package/habilidades/web-fetcher-routing/SKILL.md +75 -75
- package/hooks/check-update.js +31 -3
- package/hooks/claudemd-bloat-detector.js +161 -161
- package/hooks/extraccion-aprendizajes.js +11 -0
- package/hooks/lib/agent-routing.js +107 -107
- package/hooks/lib/auto-consolidator.js +335 -335
- package/hooks/lib/error-classifier.js +308 -308
- package/hooks/lib/merkle-audit.js +96 -96
- package/hooks/lib/provenance-tracker.js +191 -191
- package/hooks/lib/rate-limit-tracker.js +253 -253
- package/hooks/lib/resource-quota.js +122 -122
- package/hooks/lib/retry-jitter.js +165 -165
- package/hooks/lib/security-net.js +201 -201
- package/hooks/lib/skill-auditor.js +588 -588
- package/hooks/lib/sync-status.js +228 -228
- package/hooks/lib/taint-tracker.js +107 -107
- package/hooks/lib/text-similarity.js +241 -241
- package/hooks/lib/toon-compressor.js +245 -245
- package/hooks/registro-turnos.js +209 -209
- package/hooks/sugerir-regenerar-inventario.js +170 -170
- package/hooks/validar-formato-post-subagente.js +140 -140
- package/hooks/validar-memoria-hook.js +218 -218
- package/instintos/prompt-appendices.yaml +57 -57
- package/manifiestos/agent-output-schemas.json +57 -57
- package/manifiestos/modulos.json +1324 -1321
- package/manifiestos/skills-lock.json +1142 -1114
- package/package.json +5 -4
- package/plantillas/auditor-veto-template.md +105 -105
- package/plantillas/github-workflows/README.md +47 -47
- package/plantillas/github-workflows/release-please.yml +44 -44
- package/plantillas/github-workflows/swl-ci.yml +107 -107
- package/plantillas/github-workflows/swl-security.yml +51 -51
- package/plugin.json +355 -351
- package/reglas/analisis-previo-tareas-grandes.md +172 -172
- package/reglas/arreglar-al-detectar.md +147 -147
- package/reglas/fragmentos-compartidos.md +152 -152
- package/reglas/harness-claude-code.md +213 -213
- package/reglas/registro-componentes-nuevos.md +192 -0
- package/reglas/usar-context7.md +226 -226
- package/schemas/diary-entry.schema.json +80 -80
- package/scripts/actualizar.js +110 -1
- package/scripts/audit-tools/audit-history.js +330 -330
- package/scripts/audit-tools/bundle-tracker.js +290 -290
- package/scripts/audit-tools/canary-monitor.js +352 -352
- package/scripts/audit-tools/code-profiler.js +605 -605
- package/scripts/audit-tools/dep-doctor.js +320 -320
- package/scripts/audit-tools/env-validator.js +206 -206
- package/scripts/audit-tools/lib/fs-walk.js +48 -48
- package/scripts/audit-tools/lib/output.js +23 -23
- package/scripts/audit-tools/migration-checker.js +392 -392
- package/scripts/audit-tools/pentest-scanner.js +1436 -1436
- package/scripts/benchmark-memoria.js +167 -167
- package/scripts/configurar-branch-protection.js +418 -418
- package/scripts/derivar-feature-list.js +489 -489
- package/scripts/desinstalar.js +105 -24
- package/scripts/detectar-aprendizajes-duplicados.js +151 -151
- package/scripts/doctor.js +27 -0
- package/scripts/field-report.js +199 -199
- package/scripts/generar-checklists-consolidados.js +273 -273
- package/scripts/generar-inventario.js +420 -420
- package/scripts/generar-matriz-lenguajes.js +271 -271
- package/scripts/instalador.js +55 -4
- package/scripts/lib/artefactos-python.js +43 -43
- package/scripts/lib/benchmark-metrics.js +160 -160
- package/scripts/lib/budget-enforcer.js +252 -252
- package/scripts/lib/configurar-ci.js +380 -380
- package/scripts/lib/contadores-inventario.js +217 -217
- package/scripts/lib/detectar-stack-detallado.js +307 -307
- package/scripts/lib/diary-entry.js +234 -234
- package/scripts/lib/eval-metrics-store.js +218 -218
- package/scripts/lib/eval-quality.js +171 -171
- package/scripts/lib/eval-schemas.js +144 -144
- package/scripts/lib/eval-self-correct.js +106 -106
- package/scripts/lib/eval-validator.js +185 -185
- package/scripts/lib/expandir-targets.js +71 -71
- package/scripts/lib/jaccard-similarity.js +98 -98
- package/scripts/lib/longmemeval-runner.js +125 -125
- package/scripts/lib/mcp_config.py +127 -0
- package/scripts/lib/npm-version.js +261 -261
- package/scripts/lib/paquetes-conocidos.js +50 -50
- package/scripts/lib/parsear-opciones.js +3 -0
- package/scripts/lib/prompt-builder.js +264 -264
- package/scripts/lib/rrf-fusion.js +175 -175
- package/scripts/lib/scoring-instintos.js +277 -277
- package/scripts/lib/semantic-search.js +252 -252
- package/scripts/lib/toml-merge.js +204 -204
- package/scripts/lib/transformadores/codex.js +375 -375
- package/scripts/lib/transformadores/cursor.js +359 -359
- package/scripts/lib/ui.js +148 -22
- package/scripts/limpiar-artefactos-python.js +131 -131
- package/scripts/mcp-orchestrator.py +8 -18
- package/scripts/mcp-pool-manager.py +12 -23
- package/scripts/mcp-server/README.md +170 -170
- package/scripts/mcp-server/auth.js +105 -105
- package/scripts/mcp-server/cache.js +106 -106
- package/scripts/mcp-server/telemetry.js +78 -78
- package/scripts/migrar-csv-a-array.js +168 -168
- package/scripts/migrar-fase-dominio.js +201 -201
- package/scripts/publicar.js +511 -511
- package/scripts/run-eval.js +141 -141
- package/scripts/tui/componentes/selector-multi.js +189 -0
- package/scripts/tui/componentes/selector-unico.js +158 -0
- package/scripts/tui/ejecutores.js +375 -0
- package/scripts/tui/index.js +162 -0
- package/scripts/tui/lib/colores.js +129 -0
- package/scripts/tui/lib/render.js +264 -0
- package/scripts/tui/lib/teclas.js +113 -0
- package/scripts/tui/pantallas/inspect.js +173 -0
- package/scripts/tui/pantallas/install-wizard.js +334 -0
- package/scripts/tui/pantallas/menu-principal.js +52 -0
- package/scripts/tui/pantallas/progreso.js +274 -0
- package/scripts/tui/pantallas/resumen.js +132 -0
- package/scripts/tui/pantallas/uninstall-wizard.js +208 -0
- package/scripts/tui/pantallas/update-wizard.js +232 -0
- package/scripts/tui/pantallas/welcome.js +187 -0
- package/scripts/validar-userland-vacio.js +110 -110
- package/scripts/verificar-docs-vs-codigo.js +425 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pantalla Welcome de la TUI de swl-ses.
|
|
5
|
+
*
|
|
6
|
+
* Diseño:
|
|
7
|
+
* - Logo ASCII centrado en la parte superior
|
|
8
|
+
* - Tabla de runtimes detectados (nombre, scope, versión instalada, estado)
|
|
9
|
+
* - Pie con atajos: Enter continuar / Esc salir
|
|
10
|
+
*
|
|
11
|
+
* No-TTY: imprime versión texto plano y retorna { continuar: true } inmediatamente.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const render = require('../lib/render');
|
|
17
|
+
const teclas = require('../lib/teclas');
|
|
18
|
+
const { colores, semantico, iconos } = require('../lib/colores');
|
|
19
|
+
|
|
20
|
+
// Logo en caracteres ASCII art compactos (5 líneas)
|
|
21
|
+
const LOGO = [
|
|
22
|
+
' ███████ ██ ██ ██ ███████ ███████ ███████ ',
|
|
23
|
+
' ██ ██ ██ ██ ██ ██ ██ ',
|
|
24
|
+
' ███████ ██ █ ██ ██ ███████ █████ ███████ ',
|
|
25
|
+
' ██ ██ ███ ██ ██ ██ ██ ██ ',
|
|
26
|
+
' ███████ ███ ███ ███████ ███████ ███████ ███████ ',
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const SUBTITULO = 'Sistema de ingeniería de software auto-evolutivo multi-runtime';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Detecta runtimes instalados leyendo desde scripts/lib/detectar-runtime.
|
|
33
|
+
* No falla si la lib no está disponible — devuelve array vacío.
|
|
34
|
+
*/
|
|
35
|
+
function detectarInstalaciones() {
|
|
36
|
+
let detectarRuntimes;
|
|
37
|
+
let cargarEstado;
|
|
38
|
+
try {
|
|
39
|
+
({ detectarRuntimes } = require('../../lib/detectar-runtime'));
|
|
40
|
+
({ cargarEstado } = require('../../lib/estado'));
|
|
41
|
+
} catch (_) {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const runtimes = detectarRuntimes();
|
|
46
|
+
const filas = [];
|
|
47
|
+
for (const runtime of runtimes) {
|
|
48
|
+
const dirs = [
|
|
49
|
+
{ ruta: runtime.global, scope: 'global' },
|
|
50
|
+
{ ruta: path.resolve(runtime.local), scope: 'proyecto' },
|
|
51
|
+
];
|
|
52
|
+
for (const { ruta, scope } of dirs) {
|
|
53
|
+
try {
|
|
54
|
+
const estado = cargarEstado(ruta);
|
|
55
|
+
if (!estado) continue;
|
|
56
|
+
filas.push({
|
|
57
|
+
runtime: runtime.nombre,
|
|
58
|
+
scope,
|
|
59
|
+
version: estado.versionSistema || 'desconocida',
|
|
60
|
+
perfil: estado.perfil || '-',
|
|
61
|
+
componentes: estado.componentesInstalados?.length || 0,
|
|
62
|
+
});
|
|
63
|
+
} catch (_) {
|
|
64
|
+
// ignorar errores de lectura
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return filas;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Pinta el contenido completo de la pantalla. Idempotente — se puede invocar
|
|
73
|
+
* múltiples veces (resize, re-render).
|
|
74
|
+
*/
|
|
75
|
+
function _renderizar(filas, version) {
|
|
76
|
+
render.limpiarPantalla();
|
|
77
|
+
const { cols, rows } = render.obtenerDimensiones();
|
|
78
|
+
|
|
79
|
+
// Logo centrado
|
|
80
|
+
const filaLogo = 2;
|
|
81
|
+
for (let i = 0; i < LOGO.length; i++) {
|
|
82
|
+
const linea = LOGO[i];
|
|
83
|
+
const colLogo = Math.max(1, Math.floor((cols - linea.length) / 2));
|
|
84
|
+
render.escribirEn(filaLogo + i, colLogo, semantico.titulo(linea));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Subtítulo + versión
|
|
88
|
+
const filaSub = filaLogo + LOGO.length + 1;
|
|
89
|
+
const subFinal = `${SUBTITULO} ${colores.dim('v' + version)}`;
|
|
90
|
+
const colSub = Math.max(1, Math.floor((cols - render.anchoVisual(subFinal)) / 2));
|
|
91
|
+
render.escribirEn(filaSub, colSub, subFinal);
|
|
92
|
+
|
|
93
|
+
// Tabla de runtimes detectados
|
|
94
|
+
const filaTabla = filaSub + 3;
|
|
95
|
+
if (filas.length === 0) {
|
|
96
|
+
const mensaje = colores.dim('No se detectaron instalaciones SWL en este equipo.');
|
|
97
|
+
const colMsg = Math.max(1, Math.floor((cols - render.anchoVisual(mensaje)) / 2));
|
|
98
|
+
render.escribirEn(filaTabla, colMsg, mensaje);
|
|
99
|
+
} else {
|
|
100
|
+
render.escribirEn(filaTabla - 1, 4, semantico.enfasis('Instalaciones detectadas:'));
|
|
101
|
+
const cabecera =
|
|
102
|
+
render.rellenarDer(colores.dim('Runtime'), 14) +
|
|
103
|
+
render.rellenarDer(colores.dim('Scope'), 10) +
|
|
104
|
+
render.rellenarDer(colores.dim('Versión'), 12) +
|
|
105
|
+
render.rellenarDer(colores.dim('Perfil'), 18) +
|
|
106
|
+
colores.dim('Componentes');
|
|
107
|
+
render.escribirEn(filaTabla, 4, cabecera);
|
|
108
|
+
|
|
109
|
+
filas.slice(0, rows - filaTabla - 4).forEach((f, i) => {
|
|
110
|
+
const estado = f.version === version
|
|
111
|
+
? semantico.exito(iconos.check)
|
|
112
|
+
: semantico.warn(iconos.warn);
|
|
113
|
+
const linea =
|
|
114
|
+
render.rellenarDer(`${estado} ${f.runtime}`, 14) +
|
|
115
|
+
render.rellenarDer(f.scope, 10) +
|
|
116
|
+
render.rellenarDer('v' + f.version, 12) +
|
|
117
|
+
render.rellenarDer(f.perfil, 18) +
|
|
118
|
+
colores.cyan(String(f.componentes));
|
|
119
|
+
render.escribirEn(filaTabla + 1 + i, 4, linea);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Mensaje central de continuación
|
|
124
|
+
const filaCont = rows - 4;
|
|
125
|
+
const mensaje = `Presiona ${semantico.cursor('Enter')} para continuar al menú principal o ${semantico.cursor('Esc')} para salir.`;
|
|
126
|
+
const colCont = Math.max(1, Math.floor((cols - render.anchoVisual(mensaje)) / 2));
|
|
127
|
+
render.escribirEn(filaCont, colCont, mensaje);
|
|
128
|
+
|
|
129
|
+
// Pie con atajos
|
|
130
|
+
render.dibujarPiePagina([
|
|
131
|
+
['Enter', 'continuar'],
|
|
132
|
+
['Esc', 'salir'],
|
|
133
|
+
]);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Muestra la pantalla Welcome y espera input del usuario.
|
|
138
|
+
*
|
|
139
|
+
* @returns {Promise<{ continuar: boolean, instalaciones: Array }>}
|
|
140
|
+
*/
|
|
141
|
+
function mostrarWelcome() {
|
|
142
|
+
const version = require('../../../package.json').version;
|
|
143
|
+
const filas = detectarInstalaciones();
|
|
144
|
+
|
|
145
|
+
return new Promise((resolve) => {
|
|
146
|
+
// Modo no-TTY: salida texto plano sin esperar input
|
|
147
|
+
if (!render.ES_TTY || !process.stdin.isTTY) {
|
|
148
|
+
console.log('');
|
|
149
|
+
console.log(` swl-ses v${version}`);
|
|
150
|
+
console.log(` ${SUBTITULO}`);
|
|
151
|
+
console.log('');
|
|
152
|
+
if (filas.length > 0) {
|
|
153
|
+
console.log(' Instalaciones detectadas:');
|
|
154
|
+
for (const f of filas) {
|
|
155
|
+
console.log(` ${f.runtime} (${f.scope}): v${f.version}, perfil ${f.perfil}`);
|
|
156
|
+
}
|
|
157
|
+
} else {
|
|
158
|
+
console.log(' No se detectaron instalaciones SWL.');
|
|
159
|
+
}
|
|
160
|
+
console.log('');
|
|
161
|
+
resolve({ continuar: true, instalaciones: filas });
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
render.iniciarModoTui();
|
|
166
|
+
_renderizar(filas, version);
|
|
167
|
+
|
|
168
|
+
// Re-renderizar en resize
|
|
169
|
+
const onResize = () => _renderizar(filas, version);
|
|
170
|
+
process.stdout.on('resize', onResize);
|
|
171
|
+
|
|
172
|
+
const teclado = teclas.crearTeclado();
|
|
173
|
+
|
|
174
|
+
function finalizar(continuar) {
|
|
175
|
+
teclado.desactivar();
|
|
176
|
+
process.stdout.removeListener('resize', onResize);
|
|
177
|
+
render.salirModoTui();
|
|
178
|
+
resolve({ continuar, instalaciones: filas });
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
teclado.on('return', () => finalizar(true));
|
|
182
|
+
teclado.on('escape', () => finalizar(false));
|
|
183
|
+
teclado.activar();
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
module.exports = { mostrarWelcome, detectarInstalaciones, LOGO, SUBTITULO };
|
|
@@ -1,110 +1,110 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* validar-userland-vacio.js — guard pre-publish.
|
|
6
|
-
*
|
|
7
|
-
* Verifica que `_userland/` solo contenga archivos `.gitkeep` antes de
|
|
8
|
-
* empaquetar el tarball. Si algún contributor accidentalmente commiteó
|
|
9
|
-
* agentes, skills o credenciales experimentales en `_userland/`, este
|
|
10
|
-
* gate aborta el publish con exit 1 y lista los archivos extraños.
|
|
11
|
-
*
|
|
12
|
-
* Por qué existe (defensa en profundidad):
|
|
13
|
-
* `_userland/` es el directorio donde el USUARIO instala su contenido
|
|
14
|
-
* custom. En el repo del sistema debe permanecer vacío (solo
|
|
15
|
-
* `.gitkeep` para preservar la estructura). El campo `files` de
|
|
16
|
-
* package.json incluye `_userland/`, así que cualquier archivo real
|
|
17
|
-
* que se cuele entrará al tarball publicado a npm — exposición
|
|
18
|
-
* potencial de IP o secretos. Aunque `.gitignore` y `.npmignore` ya
|
|
19
|
-
* cubren los vectores típicos (.env, credenciales), este guard
|
|
20
|
-
* garantiza que la regla "_userland/ vacío en distribución" se cumpla
|
|
21
|
-
* determinísticamente.
|
|
22
|
-
*
|
|
23
|
-
* Variables de entorno:
|
|
24
|
-
* SWL_USERLAND_GUARD_ALLOW=archivo1,archivo2 — permite archivos extra
|
|
25
|
-
* (uso excepcional documentado en commit que active la variable).
|
|
26
|
-
*
|
|
27
|
-
* Exit codes:
|
|
28
|
-
* 0 — OK, _userland/ solo contiene .gitkeep
|
|
29
|
-
* 1 — hay archivos extraños; lista impresa a stderr
|
|
30
|
-
* 2 — error de invariante (cwd incorrecto, _userland/ no existe)
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
const fs = require('fs');
|
|
34
|
-
const path = require('path');
|
|
35
|
-
|
|
36
|
-
const USERLAND_DIR = '_userland';
|
|
37
|
-
const PERMITIDOS_BASE = new Set(['.gitkeep', '.gitignore']);
|
|
38
|
-
|
|
39
|
-
function log(msg) { process.stdout.write(`[userland-guard] ${msg}\n`); }
|
|
40
|
-
function err(msg) { process.stderr.write(`[userland-guard] ERROR: ${msg}\n`); }
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Normaliza una ruta relativa al separador POSIX (`/`) para que la
|
|
44
|
-
* comparación contra la allowlist funcione idéntica en Windows y POSIX.
|
|
45
|
-
*/
|
|
46
|
-
function normalizar(rel) {
|
|
47
|
-
return rel.replace(/\\/g, '/');
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function obtenerExtras() {
|
|
51
|
-
const extras = process.env.SWL_USERLAND_GUARD_ALLOW;
|
|
52
|
-
if (!extras) return new Set();
|
|
53
|
-
return new Set(extras.split(',').map(s => normalizar(s.trim())).filter(Boolean));
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function listarRecursivo(dir, baseDir = dir, acumulador = []) {
|
|
57
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
58
|
-
for (const entry of entries) {
|
|
59
|
-
const full = path.join(dir, entry.name);
|
|
60
|
-
const rel = normalizar(path.relative(baseDir, full));
|
|
61
|
-
if (entry.isDirectory()) {
|
|
62
|
-
listarRecursivo(full, baseDir, acumulador);
|
|
63
|
-
} else if (entry.isFile()) {
|
|
64
|
-
acumulador.push(rel);
|
|
65
|
-
} else if (entry.isSymbolicLink()) {
|
|
66
|
-
// Symlinks dentro del paquete distribuido son anti-patrón.
|
|
67
|
-
acumulador.push(`${rel} (symlink)`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return acumulador;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function main() {
|
|
74
|
-
const cwd = process.cwd();
|
|
75
|
-
const userland = path.join(cwd, USERLAND_DIR);
|
|
76
|
-
|
|
77
|
-
if (!fs.existsSync(userland)) {
|
|
78
|
-
err(`${USERLAND_DIR}/ no existe en ${cwd}. ¿cwd incorrecto?`);
|
|
79
|
-
process.exit(2);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const extrasPermitidos = obtenerExtras();
|
|
83
|
-
const archivos = listarRecursivo(userland);
|
|
84
|
-
|
|
85
|
-
const noPermitidos = archivos.filter(rel => {
|
|
86
|
-
const nombre = path.basename(rel);
|
|
87
|
-
if (PERMITIDOS_BASE.has(nombre)) return false;
|
|
88
|
-
if (extrasPermitidos.has(rel)) return false;
|
|
89
|
-
return true;
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
if (noPermitidos.length > 0) {
|
|
93
|
-
err(`${USERLAND_DIR}/ contiene ${noPermitidos.length} archivo(s) que no deberían distribuirse en el tarball npm:`);
|
|
94
|
-
for (const rel of noPermitidos) {
|
|
95
|
-
err(` - ${USERLAND_DIR}/${rel}`);
|
|
96
|
-
}
|
|
97
|
-
err('');
|
|
98
|
-
err('Acciones posibles:');
|
|
99
|
-
err(` 1. Mover el contenido fuera de ${USERLAND_DIR}/ si es del sistema base`);
|
|
100
|
-
err(` 2. Borrarlo si era código experimental local`);
|
|
101
|
-
err(` 3. Permitir explícitamente con SWL_USERLAND_GUARD_ALLOW="ruta1,ruta2"`);
|
|
102
|
-
err(' (documentar la excepción en el mensaje de commit)');
|
|
103
|
-
process.exit(1);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
log(`OK: ${USERLAND_DIR}/ solo contiene ${archivos.length} archivo(s) permitido(s).`);
|
|
107
|
-
process.exit(0);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
main();
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* validar-userland-vacio.js — guard pre-publish.
|
|
6
|
+
*
|
|
7
|
+
* Verifica que `_userland/` solo contenga archivos `.gitkeep` antes de
|
|
8
|
+
* empaquetar el tarball. Si algún contributor accidentalmente commiteó
|
|
9
|
+
* agentes, skills o credenciales experimentales en `_userland/`, este
|
|
10
|
+
* gate aborta el publish con exit 1 y lista los archivos extraños.
|
|
11
|
+
*
|
|
12
|
+
* Por qué existe (defensa en profundidad):
|
|
13
|
+
* `_userland/` es el directorio donde el USUARIO instala su contenido
|
|
14
|
+
* custom. En el repo del sistema debe permanecer vacío (solo
|
|
15
|
+
* `.gitkeep` para preservar la estructura). El campo `files` de
|
|
16
|
+
* package.json incluye `_userland/`, así que cualquier archivo real
|
|
17
|
+
* que se cuele entrará al tarball publicado a npm — exposición
|
|
18
|
+
* potencial de IP o secretos. Aunque `.gitignore` y `.npmignore` ya
|
|
19
|
+
* cubren los vectores típicos (.env, credenciales), este guard
|
|
20
|
+
* garantiza que la regla "_userland/ vacío en distribución" se cumpla
|
|
21
|
+
* determinísticamente.
|
|
22
|
+
*
|
|
23
|
+
* Variables de entorno:
|
|
24
|
+
* SWL_USERLAND_GUARD_ALLOW=archivo1,archivo2 — permite archivos extra
|
|
25
|
+
* (uso excepcional documentado en commit que active la variable).
|
|
26
|
+
*
|
|
27
|
+
* Exit codes:
|
|
28
|
+
* 0 — OK, _userland/ solo contiene .gitkeep
|
|
29
|
+
* 1 — hay archivos extraños; lista impresa a stderr
|
|
30
|
+
* 2 — error de invariante (cwd incorrecto, _userland/ no existe)
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
const fs = require('fs');
|
|
34
|
+
const path = require('path');
|
|
35
|
+
|
|
36
|
+
const USERLAND_DIR = '_userland';
|
|
37
|
+
const PERMITIDOS_BASE = new Set(['.gitkeep', '.gitignore']);
|
|
38
|
+
|
|
39
|
+
function log(msg) { process.stdout.write(`[userland-guard] ${msg}\n`); }
|
|
40
|
+
function err(msg) { process.stderr.write(`[userland-guard] ERROR: ${msg}\n`); }
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Normaliza una ruta relativa al separador POSIX (`/`) para que la
|
|
44
|
+
* comparación contra la allowlist funcione idéntica en Windows y POSIX.
|
|
45
|
+
*/
|
|
46
|
+
function normalizar(rel) {
|
|
47
|
+
return rel.replace(/\\/g, '/');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function obtenerExtras() {
|
|
51
|
+
const extras = process.env.SWL_USERLAND_GUARD_ALLOW;
|
|
52
|
+
if (!extras) return new Set();
|
|
53
|
+
return new Set(extras.split(',').map(s => normalizar(s.trim())).filter(Boolean));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function listarRecursivo(dir, baseDir = dir, acumulador = []) {
|
|
57
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
58
|
+
for (const entry of entries) {
|
|
59
|
+
const full = path.join(dir, entry.name);
|
|
60
|
+
const rel = normalizar(path.relative(baseDir, full));
|
|
61
|
+
if (entry.isDirectory()) {
|
|
62
|
+
listarRecursivo(full, baseDir, acumulador);
|
|
63
|
+
} else if (entry.isFile()) {
|
|
64
|
+
acumulador.push(rel);
|
|
65
|
+
} else if (entry.isSymbolicLink()) {
|
|
66
|
+
// Symlinks dentro del paquete distribuido son anti-patrón.
|
|
67
|
+
acumulador.push(`${rel} (symlink)`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return acumulador;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function main() {
|
|
74
|
+
const cwd = process.cwd();
|
|
75
|
+
const userland = path.join(cwd, USERLAND_DIR);
|
|
76
|
+
|
|
77
|
+
if (!fs.existsSync(userland)) {
|
|
78
|
+
err(`${USERLAND_DIR}/ no existe en ${cwd}. ¿cwd incorrecto?`);
|
|
79
|
+
process.exit(2);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const extrasPermitidos = obtenerExtras();
|
|
83
|
+
const archivos = listarRecursivo(userland);
|
|
84
|
+
|
|
85
|
+
const noPermitidos = archivos.filter(rel => {
|
|
86
|
+
const nombre = path.basename(rel);
|
|
87
|
+
if (PERMITIDOS_BASE.has(nombre)) return false;
|
|
88
|
+
if (extrasPermitidos.has(rel)) return false;
|
|
89
|
+
return true;
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
if (noPermitidos.length > 0) {
|
|
93
|
+
err(`${USERLAND_DIR}/ contiene ${noPermitidos.length} archivo(s) que no deberían distribuirse en el tarball npm:`);
|
|
94
|
+
for (const rel of noPermitidos) {
|
|
95
|
+
err(` - ${USERLAND_DIR}/${rel}`);
|
|
96
|
+
}
|
|
97
|
+
err('');
|
|
98
|
+
err('Acciones posibles:');
|
|
99
|
+
err(` 1. Mover el contenido fuera de ${USERLAND_DIR}/ si es del sistema base`);
|
|
100
|
+
err(` 2. Borrarlo si era código experimental local`);
|
|
101
|
+
err(` 3. Permitir explícitamente con SWL_USERLAND_GUARD_ALLOW="ruta1,ruta2"`);
|
|
102
|
+
err(' (documentar la excepción en el mensaje de commit)');
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
log(`OK: ${USERLAND_DIR}/ solo contiene ${archivos.length} archivo(s) permitido(s).`);
|
|
107
|
+
process.exit(0);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
main();
|