@saulwade/swl-ses 2.4.3 → 2.5.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 +194 -241
- package/README.md +600 -597
- package/agentes/_intent-spec.md +73 -73
- package/agentes/_propose-step.md +90 -90
- package/agentes/abogado-diablo-swl.md +145 -0
- package/agentes/accesibilidad-wcag-swl.md +690 -690
- package/agentes/arquitecto-swl.md +267 -267
- package/agentes/auto-evolucion-swl.md +908 -908
- package/agentes/backend-api-swl.md +1 -1
- 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-node-swl.md +1 -1
- package/agentes/backend-python-swl.md +1 -1
- 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/datos-swl.md +1 -1
- 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/gh-fix-ci-swl.md +6 -1
- package/agentes/implementador-swl.md +1 -1
- package/agentes/investigador-swl.md +432 -432
- package/agentes/investigador-ux-swl.md +505 -505
- package/agentes/llm-apps-swl.md +1 -1
- 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/notificador-swl.md +1 -1
- 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/resolutor-build-swl.md +10 -1
- package/agentes/revisor-angular-swl.md +278 -278
- package/agentes/revisor-codigo-swl.md +1 -1
- 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/sre-swl.md +1 -1
- package/agentes/tdd-qa-swl.md +393 -393
- package/bin/lib/bot-comandos.js +1 -1
- package/bin/swl-ses.js +6 -0
- package/comandos/swl/adoptar-proyecto.md +14 -2
- package/comandos/swl/configurar-ci.md +8 -1
- package/comandos/swl/deuda-codigo.md +97 -97
- package/comandos/swl/discutir-fase.md +22 -118
- package/comandos/swl/fix.md +118 -0
- package/comandos/swl/nuevo-proyecto.md +54 -3
- package/comandos/swl/predecir.md +32 -2
- package/comandos/swl/seguridad.md +189 -0
- package/comandos/swl/status.md +5 -3
- package/habilidades/aprendizaje-continuo/SKILL.md +3 -1
- package/habilidades/discutir-fase/SKILL.md +84 -81
- package/habilidades/discutir-fase/recursos/plantilla-contexto.md +136 -0
- package/habilidades/doc-sync/SKILL.md +3 -1
- package/habilidades/doubt-driven-review/SKILL.md +15 -1
- package/habilidades/ejecutar-task-iterativo/SKILL.md +278 -278
- package/habilidades/estructura-proyecto-claude/SKILL.md +11 -2
- package/habilidades/harness-claude-code/SKILL.md +3 -1
- package/habilidades/instalar-sistema/SKILL.md +3 -1
- package/habilidades/meta-reglas-extendido/SKILL.md +92 -0
- package/habilidades/meta-reglas-extendido/recursos/analisis-previo-tareas-grandes.md +186 -0
- package/habilidades/meta-reglas-extendido/recursos/analizar-directorios-antes-de-escribir.md +235 -0
- package/habilidades/meta-reglas-extendido/recursos/api-diseno.md +413 -0
- package/habilidades/meta-reglas-extendido/recursos/arquitectura.md +491 -0
- package/habilidades/meta-reglas-extendido/recursos/arreglar-al-detectar.md +264 -0
- package/habilidades/meta-reglas-extendido/recursos/debatir-antes-de-aceptar.md +152 -0
- package/habilidades/meta-reglas-extendido/recursos/git-workflow.md +259 -0
- package/habilidades/meta-reglas-extendido/recursos/gobernanza.md +291 -0
- package/habilidades/meta-reglas-extendido/recursos/memoria-consolidada.md +263 -0
- package/habilidades/meta-reglas-extendido/recursos/seguridad-agentes.md +443 -0
- package/habilidades/meta-reglas-extendido/recursos/sesiones-paralelas.md +190 -0
- package/habilidades/meta-reglas-extendido/recursos/sin-duplicacion-reglas-globales.md +179 -0
- package/habilidades/meta-reglas-extendido/recursos/skills-estandar.md +394 -0
- package/habilidades/meta-reglas-extendido/recursos/usar-code-review-graph.md +156 -0
- package/habilidades/meta-reglas-extendido/recursos/usar-context7.md +236 -0
- package/habilidades/meta-reglas-extendido/recursos/usar-sistema-swl.md +253 -0
- package/habilidades/meta-reglas-extendido/recursos/verificar-citas-normativas.md +527 -0
- package/habilidades/meta-skills-estandar/SKILL.md +3 -1
- package/habilidades/nuevo-proyecto/SKILL.md +20 -3
- package/habilidades/php-experto/SKILL.md +10 -3
- package/habilidades/{filament-admin/SKILL.md → php-experto/recursos/filament-admin.md} +23 -39
- 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-debate-adversarial/recursos/personas.md +5 -4
- package/habilidades/proceso-ingenieria-requerimientos/SKILL.md +147 -0
- package/hooks/check-update.js +19 -10
- package/hooks/contexto-subagente.js +68 -68
- package/hooks/degradacion-instintos.js +1 -1
- package/hooks/extraccion-aprendizajes.js +2 -2
- package/hooks/lib/briefing.js +3 -3
- package/hooks/lib/nudge-tracker.js +1 -1
- package/hooks/lib/otlp-exporter.js +1 -1
- package/hooks/lib/webhook-dedup.js +1 -1
- package/hooks/session-briefing.js +1 -1
- package/llms.txt +6 -6
- package/manifiestos/canonical-hashes.json +713 -52
- package/manifiestos/hooks-config.json +469 -469
- package/manifiestos/invariantes-criticos.json +30 -30
- package/manifiestos/modulos.json +168 -135
- package/manifiestos/perfiles.json +0 -2
- package/manifiestos/skills-lock.json +49 -56
- package/package.json +7 -5
- package/plantillas/github-workflows/README.md +15 -1
- package/plantillas/github-workflows/swl-devsecops.yml +70 -0
- package/plugin.json +5 -5
- package/reglas/analisis-previo-tareas-grandes.md +30 -156
- package/reglas/analizar-directorios-antes-de-escribir.md +30 -211
- package/reglas/api-diseno.md +28 -398
- package/reglas/arquitectura.md +35 -456
- package/reglas/arreglar-al-detectar.md +30 -230
- package/reglas/debatir-antes-de-aceptar.md +30 -143
- package/reglas/docs.md +7 -0
- package/reglas/estilo-codigo.md +9 -0
- package/reglas/fragmentos-compartidos.md +6 -0
- package/reglas/git-workflow.md +44 -240
- package/reglas/gobernanza.md +23 -262
- package/reglas/memoria-consolidada.md +34 -228
- package/reglas/performance.md +8 -0
- package/reglas/pruebas.md +12 -0
- package/reglas/seguridad-agentes.md +37 -418
- package/reglas/seguridad.md +12 -0
- package/reglas/sesiones-paralelas.md +29 -162
- package/reglas/sin-duplicacion-reglas-globales.md +25 -166
- package/reglas/skills-estandar.md +23 -373
- package/reglas/usar-code-review-graph.md +31 -140
- package/reglas/usar-context7.md +30 -208
- package/reglas/usar-sistema-swl.md +47 -242
- package/reglas/verificar-citas-normativas.md +47 -537
- package/scripts/actualizar.js +253 -253
- package/scripts/audit-tools/auditar-relleno-inventario.js +145 -0
- package/scripts/auditar-clases-conocidas.js +106 -0
- package/scripts/bootstrap-instintos.js +2 -2
- package/scripts/canario-hooks.js +166 -0
- package/scripts/cli/configurar-ci.js +2 -1
- package/scripts/evidencia-valor.js +93 -0
- package/scripts/field-report.js +1 -1
- package/scripts/generar-comandos.js +143 -0
- package/scripts/generar-inventario.js +236 -23
- package/scripts/generar-matriz-lenguajes.js +1 -1
- package/scripts/instalador.js +15 -1
- package/scripts/lib/configurar-ci.js +10 -3
- package/scripts/lib/diary-entry.js +3 -1
- package/scripts/lib/drift-detector.js +1 -1
- package/scripts/lib/evidencia-valor.js +189 -0
- package/scripts/lib/expandir-targets.js +71 -71
- package/scripts/lib/frontmatter-md.js +63 -0
- package/scripts/lib/parsear-opciones.js +2 -0
- package/scripts/lib/prune-componentes.js +180 -0
- package/scripts/lib/reglas-globales-conocidas.json +16 -2
- package/scripts/lib/scoring-instintos.js +2 -2
- package/scripts/lib/toml-merge.js +204 -204
- package/scripts/lib/transformadores/claude.js +1 -1
- package/scripts/lib/transformadores/codex.js +1 -1
- package/scripts/lib/transformadores/copilot.js +1 -1
- package/scripts/lib/transformadores/cursor.js +1 -1
- package/scripts/lib/transformadores/gemini.js +22 -2
- package/scripts/lib/transformadores/opencode.js +1 -1
- package/scripts/mcp-server/auth.js +105 -105
- package/scripts/mcp-server/cache.js +106 -106
- package/scripts/prune.js +102 -0
- package/scripts/publicar.js +18 -2
- package/scripts/tui/pantallas/inspect.js +175 -175
- package/scripts/tui/pantallas/uninstall-wizard.js +210 -210
- package/scripts/tui/pantallas/update-wizard.js +234 -234
- package/scripts/tui/pantallas/welcome.js +189 -189
- package/habilidades/paid-media-tracking/SKILL.md +0 -269
- package/habilidades/paid-media-tracking/recursos/auditoria-tracking.md +0 -220
- package/habilidades/paid-media-tracking/recursos/google-ads-api.md +0 -215
- package/habilidades/tracking-measurement/SKILL.md +0 -239
- package/habilidades/tracking-measurement/recursos/consent-mode.md +0 -231
- package/habilidades/tracking-measurement/recursos/gtm-datalayer.md +0 -216
- package/habilidades/tracking-measurement/recursos/meta-capi.md +0 -262
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Expansión de --target (CSV) y --all-runtimes para multi-target install.
|
|
5
|
-
*
|
|
6
|
-
* ADR-0019 Sub-fase 2.5.
|
|
7
|
-
*
|
|
8
|
-
* Extraído de bin/swl-ses.js para permitir tests unitarios. NUNCA debe tener
|
|
9
|
-
* side effects fuera de logging — solo manipula la lista de strings.
|
|
10
|
-
*
|
|
11
|
-
* @module scripts/lib/expandir-targets
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
const { listarRuntimesInstalables, RUNTIMES } = require('./detectar-runtime');
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Expande las opciones del CLI a un array de target IDs.
|
|
18
|
-
*
|
|
19
|
-
* Reglas:
|
|
20
|
-
* - `all_runtimes` tiene prioridad sobre `target`. Si ambos vienen, se loggea
|
|
21
|
-
* aviso (vía `logger.warn`) y se usa `all_runtimes`.
|
|
22
|
-
* - `target='a,b,c'` → ['a','b','c'].
|
|
23
|
-
* - `target='claude'` → ['claude'].
|
|
24
|
-
* - Sin nada → ['claude'] (default histórico, backward-compat).
|
|
25
|
-
* - Duplicados se deduplican preservando el primer orden.
|
|
26
|
-
* - Targets desconocidos se omiten con aviso (logger.warn).
|
|
27
|
-
*
|
|
28
|
-
* @param {object} opciones - Objeto con `target` (string|undefined) y `all_runtimes` (bool).
|
|
29
|
-
* @param {object} [logger] - Sustituible para tests. Default console.
|
|
30
|
-
* @returns {{ targets: string[], omitidos: string[], errores: string[] }}
|
|
31
|
-
*/
|
|
32
|
-
function expandirTargets(opciones, logger) {
|
|
33
|
-
const log = logger || console;
|
|
34
|
-
const omitidos = [];
|
|
35
|
-
const errores = [];
|
|
36
|
-
|
|
37
|
-
let candidatos;
|
|
38
|
-
if (opciones.all_runtimes) {
|
|
39
|
-
if (opciones.target && typeof opciones.target === 'string') {
|
|
40
|
-
log.warn && log.warn('[expandir-targets] --all-runtimes tiene prioridad sobre --target; se ignora --target.');
|
|
41
|
-
}
|
|
42
|
-
candidatos = listarRuntimesInstalables();
|
|
43
|
-
} else if (typeof opciones.target === 'string' && opciones.target.includes(',')) {
|
|
44
|
-
candidatos = opciones.target.split(',').map(s => s.trim()).filter(Boolean);
|
|
45
|
-
} else if (typeof opciones.target === 'string' && opciones.target.length > 0) {
|
|
46
|
-
candidatos = [opciones.target.trim()];
|
|
47
|
-
} else {
|
|
48
|
-
candidatos = ['claude']; // backward-compat
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const visto = new Set();
|
|
52
|
-
const targets = [];
|
|
53
|
-
for (const t of candidatos) {
|
|
54
|
-
if (visto.has(t)) continue;
|
|
55
|
-
visto.add(t);
|
|
56
|
-
if (!RUNTIMES[t]) {
|
|
57
|
-
log.warn && log.warn(`[expandir-targets] Target desconocido omitido: "${t}". Disponibles: ${Object.keys(RUNTIMES).join(', ')}`);
|
|
58
|
-
omitidos.push(t);
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
targets.push(t);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (targets.length === 0) {
|
|
65
|
-
errores.push('Ningún target válido tras expansión.');
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return { targets, omitidos, errores };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
module.exports = { expandirTargets };
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Expansión de --target (CSV) y --all-runtimes para multi-target install.
|
|
5
|
+
*
|
|
6
|
+
* ADR-0019 Sub-fase 2.5.
|
|
7
|
+
*
|
|
8
|
+
* Extraído de bin/swl-ses.js para permitir tests unitarios. NUNCA debe tener
|
|
9
|
+
* side effects fuera de logging — solo manipula la lista de strings.
|
|
10
|
+
*
|
|
11
|
+
* @module scripts/lib/expandir-targets
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const { listarRuntimesInstalables, RUNTIMES } = require('./detectar-runtime');
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Expande las opciones del CLI a un array de target IDs.
|
|
18
|
+
*
|
|
19
|
+
* Reglas:
|
|
20
|
+
* - `all_runtimes` tiene prioridad sobre `target`. Si ambos vienen, se loggea
|
|
21
|
+
* aviso (vía `logger.warn`) y se usa `all_runtimes`.
|
|
22
|
+
* - `target='a,b,c'` → ['a','b','c'].
|
|
23
|
+
* - `target='claude'` → ['claude'].
|
|
24
|
+
* - Sin nada → ['claude'] (default histórico, backward-compat).
|
|
25
|
+
* - Duplicados se deduplican preservando el primer orden.
|
|
26
|
+
* - Targets desconocidos se omiten con aviso (logger.warn).
|
|
27
|
+
*
|
|
28
|
+
* @param {object} opciones - Objeto con `target` (string|undefined) y `all_runtimes` (bool).
|
|
29
|
+
* @param {object} [logger] - Sustituible para tests. Default console.
|
|
30
|
+
* @returns {{ targets: string[], omitidos: string[], errores: string[] }}
|
|
31
|
+
*/
|
|
32
|
+
function expandirTargets(opciones, logger) {
|
|
33
|
+
const log = logger || console;
|
|
34
|
+
const omitidos = [];
|
|
35
|
+
const errores = [];
|
|
36
|
+
|
|
37
|
+
let candidatos;
|
|
38
|
+
if (opciones.all_runtimes) {
|
|
39
|
+
if (opciones.target && typeof opciones.target === 'string') {
|
|
40
|
+
log.warn && log.warn('[expandir-targets] --all-runtimes tiene prioridad sobre --target; se ignora --target.');
|
|
41
|
+
}
|
|
42
|
+
candidatos = listarRuntimesInstalables();
|
|
43
|
+
} else if (typeof opciones.target === 'string' && opciones.target.includes(',')) {
|
|
44
|
+
candidatos = opciones.target.split(',').map(s => s.trim()).filter(Boolean);
|
|
45
|
+
} else if (typeof opciones.target === 'string' && opciones.target.length > 0) {
|
|
46
|
+
candidatos = [opciones.target.trim()];
|
|
47
|
+
} else {
|
|
48
|
+
candidatos = ['claude']; // backward-compat
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const visto = new Set();
|
|
52
|
+
const targets = [];
|
|
53
|
+
for (const t of candidatos) {
|
|
54
|
+
if (visto.has(t)) continue;
|
|
55
|
+
visto.add(t);
|
|
56
|
+
if (!RUNTIMES[t]) {
|
|
57
|
+
log.warn && log.warn(`[expandir-targets] Target desconocido omitido: "${t}". Disponibles: ${Object.keys(RUNTIMES).join(', ')}`);
|
|
58
|
+
omitidos.push(t);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
targets.push(t);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (targets.length === 0) {
|
|
65
|
+
errores.push('Ningún target válido tras expansión.');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { targets, omitidos, errores };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = { expandirTargets };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* frontmatter-md.js — Parser mínimo zero-deps de frontmatter YAML en archivos .md.
|
|
5
|
+
*
|
|
6
|
+
* Soporta el subconjunto que usan los componentes SWL:
|
|
7
|
+
* - pares `clave: valor` de una línea (comillas opcionales)
|
|
8
|
+
* - escalares plegados (`clave: >`, `|`, `>-`, `|-`) juntando las líneas indentadas
|
|
9
|
+
* - listas inline (`tools: [Read, Write]`) — se devuelven como string; usar
|
|
10
|
+
* `parsearListaInline()` para convertirlas a array
|
|
11
|
+
*
|
|
12
|
+
* NO es un parser YAML completo (sin listas multilínea ni objetos anidados) —
|
|
13
|
+
* suficiente y determinista para frontmatter de agentes/skills/comandos.
|
|
14
|
+
* Consumidores: scripts/generar-inventario.js, scripts/generar-comandos.js.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
|
|
19
|
+
function leerFrontmatter(ruta) {
|
|
20
|
+
try {
|
|
21
|
+
const contenido = fs.readFileSync(ruta, 'utf8');
|
|
22
|
+
const match = contenido.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
23
|
+
if (!match) return {};
|
|
24
|
+
const fm = {};
|
|
25
|
+
const lineas = match[1].split(/\r?\n/);
|
|
26
|
+
for (let i = 0; i < lineas.length; i++) {
|
|
27
|
+
const linea = lineas[i];
|
|
28
|
+
if (/^\s/.test(linea)) continue; // continuación de multilínea
|
|
29
|
+
const idx = linea.indexOf(':');
|
|
30
|
+
if (idx <= 0) continue;
|
|
31
|
+
const key = linea.slice(0, idx).trim();
|
|
32
|
+
let val = linea.slice(idx + 1).trim().replace(/^["']|["']$/g, '');
|
|
33
|
+
if (val === '>' || val === '|' || val === '>-' || val === '|-' || val === '') {
|
|
34
|
+
const partes = [];
|
|
35
|
+
for (let j = i + 1; j < lineas.length && /^\s+\S/.test(lineas[j]); j++) {
|
|
36
|
+
partes.push(lineas[j].trim());
|
|
37
|
+
}
|
|
38
|
+
if (partes.length > 0) val = partes.join(' ');
|
|
39
|
+
}
|
|
40
|
+
fm[key] = val;
|
|
41
|
+
}
|
|
42
|
+
return fm;
|
|
43
|
+
} catch { return {}; }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** `[a, b, c]` → ['a','b','c']; string vacío → []. */
|
|
47
|
+
function parsearListaInline(valor) {
|
|
48
|
+
return String(valor || '')
|
|
49
|
+
.replace(/^\[|\]$/g, '')
|
|
50
|
+
.split(',')
|
|
51
|
+
.map(s => s.trim())
|
|
52
|
+
.filter(Boolean);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Recorta a n caracteres en límite de palabra, colapsando whitespace y pipes. */
|
|
56
|
+
function resumir(texto, n = 110) {
|
|
57
|
+
const limpio = String(texto || '').replace(/\s+/g, ' ').replace(/\|/g, '/').trim();
|
|
58
|
+
if (limpio.length <= n) return limpio;
|
|
59
|
+
const corte = limpio.lastIndexOf(' ', n);
|
|
60
|
+
return limpio.slice(0, corte > 40 ? corte : n) + '…';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = { leerFrontmatter, parsearListaInline, resumir };
|
|
@@ -48,6 +48,8 @@ const BOOLEANAS = [
|
|
|
48
48
|
// TUI opt-out (Fase 4 instalador visual). Default: si stdin es TTY y no
|
|
49
49
|
// hay flags, lanza el TUI. Con --no-tui cae al install-asistido clásico.
|
|
50
50
|
'no-tui', 'tui',
|
|
51
|
+
// prune (DT-INSTALADOR-PRUNE): --confirmar ejecuta el plan; --json salida machine-readable
|
|
52
|
+
'confirmar', 'json',
|
|
51
53
|
];
|
|
52
54
|
|
|
53
55
|
/**
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* prune-componentes — Purga de componentes retirados en instalaciones SWL.
|
|
5
|
+
*
|
|
6
|
+
* Cierra DT-INSTALADOR-PRUNE: cuando el paquete retira un componente
|
|
7
|
+
* (skill/agente/comando/regla/hook), las instalaciones existentes lo
|
|
8
|
+
* conservaban para siempre. Este módulo compara el `.swl-install-state.json`
|
|
9
|
+
* del destino contra el paquete actual y elimina SOLO lo que cumple las dos
|
|
10
|
+
* condiciones de seguridad:
|
|
11
|
+
*
|
|
12
|
+
* 1. El `origen` registrado ya NO existe en el paquete (componente retirado).
|
|
13
|
+
* 2. El contenido instalado coincide con un hash canónico conocido del
|
|
14
|
+
* componente (manifiestos/canonical-hashes.json, cualquier versión) —
|
|
15
|
+
* es contenido de fábrica sin tocar.
|
|
16
|
+
*
|
|
17
|
+
* Si el contenido difiere de todo hash canónico conocido (evolución del
|
|
18
|
+
* usuario) o el componente no tiene hash registrado (p. ej. recursos/ de un
|
|
19
|
+
* skill), NUNCA se elimina: se reporta como `preservados` para revisión
|
|
20
|
+
* manual. Invariante merge-no-overwrite (ADR-0040) extendida al borrado.
|
|
21
|
+
*
|
|
22
|
+
* API pura respecto de rutas: recibe `rutaBase` (instalación destino) y
|
|
23
|
+
* `raizPaquete` (paquete fuente actual). El caller (scripts/prune.js) resuelve
|
|
24
|
+
* ambos; los tests inyectan sandboxes.
|
|
25
|
+
*
|
|
26
|
+
* @module scripts/lib/prune-componentes
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const fs = require('fs');
|
|
30
|
+
const path = require('path');
|
|
31
|
+
|
|
32
|
+
const { cargarEstado, guardarEstado } = require('./estado.js');
|
|
33
|
+
const { canonicalHash } = require('./canonical-hash.js');
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Construye el índice rutaRelativa → Set(hashes canónicos de todas las
|
|
37
|
+
* versiones) desde manifiestos/canonical-hashes.json del paquete.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} raizPaquete
|
|
40
|
+
* @returns {Map<string, Set<string>>}
|
|
41
|
+
*/
|
|
42
|
+
function cargarHashesConocidos(raizPaquete) {
|
|
43
|
+
const indice = new Map();
|
|
44
|
+
try {
|
|
45
|
+
const ruta = path.join(raizPaquete, 'manifiestos', 'canonical-hashes.json');
|
|
46
|
+
const porVersion = JSON.parse(fs.readFileSync(ruta, 'utf-8'));
|
|
47
|
+
for (const version of Object.keys(porVersion)) {
|
|
48
|
+
const componentes = porVersion[version];
|
|
49
|
+
if (!componentes || typeof componentes !== 'object') continue;
|
|
50
|
+
for (const [rutaRel, hash] of Object.entries(componentes)) {
|
|
51
|
+
const clave = rutaRel.replace(/\\/g, '/');
|
|
52
|
+
if (!indice.has(clave)) indice.set(clave, new Set());
|
|
53
|
+
indice.get(clave).add(String(hash));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
} catch { /* sin manifiesto de hashes → índice vacío → todo se preserva */ }
|
|
57
|
+
return indice;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Clasifica los archivos instalados contra el paquete actual.
|
|
62
|
+
*
|
|
63
|
+
* @param {object} opciones
|
|
64
|
+
* @param {string} opciones.rutaBase Directorio de la instalación (contiene .swl-install-state.json)
|
|
65
|
+
* @param {string} opciones.raizPaquete Raíz del paquete swl-ses actual
|
|
66
|
+
* @returns {{
|
|
67
|
+
* error?: string,
|
|
68
|
+
* eliminables: Array<{origen: string, destino: string, tipo: string}>,
|
|
69
|
+
* preservados: Array<{origen: string, destino: string, tipo: string, razon: string}>,
|
|
70
|
+
* yaAusentes: Array<{origen: string, destino: string}>,
|
|
71
|
+
* vigentes: number,
|
|
72
|
+
* }}
|
|
73
|
+
*/
|
|
74
|
+
function planPrune({ rutaBase, raizPaquete }) {
|
|
75
|
+
const estado = cargarEstado(rutaBase);
|
|
76
|
+
if (!estado) {
|
|
77
|
+
return { error: `No hay .swl-install-state.json en ${rutaBase} — nada que podar.`, eliminables: [], preservados: [], yaAusentes: [], vigentes: 0 };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const hashesConocidos = cargarHashesConocidos(raizPaquete);
|
|
81
|
+
const eliminables = [];
|
|
82
|
+
const preservados = [];
|
|
83
|
+
const yaAusentes = [];
|
|
84
|
+
let vigentes = 0;
|
|
85
|
+
|
|
86
|
+
for (const archivo of estado.archivosInstalados || []) {
|
|
87
|
+
const origenRel = String(archivo.origen || '').replace(/\\/g, '/');
|
|
88
|
+
if (!origenRel) continue;
|
|
89
|
+
|
|
90
|
+
if (fs.existsSync(path.join(raizPaquete, origenRel))) {
|
|
91
|
+
vigentes++;
|
|
92
|
+
continue; // el paquete todavía distribuye este componente
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Componente retirado del paquete.
|
|
96
|
+
if (!fs.existsSync(archivo.destino)) {
|
|
97
|
+
yaAusentes.push({ origen: origenRel, destino: archivo.destino });
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const hashes = hashesConocidos.get(origenRel);
|
|
102
|
+
if (!hashes || hashes.size === 0) {
|
|
103
|
+
preservados.push({
|
|
104
|
+
origen: origenRel,
|
|
105
|
+
destino: archivo.destino,
|
|
106
|
+
tipo: archivo.tipo || '?',
|
|
107
|
+
razon: 'sin hash canónico registrado — revisar y borrar manualmente si aplica',
|
|
108
|
+
});
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let hashLocal;
|
|
113
|
+
try {
|
|
114
|
+
hashLocal = canonicalHash(fs.readFileSync(archivo.destino, 'utf-8'));
|
|
115
|
+
} catch {
|
|
116
|
+
preservados.push({ origen: origenRel, destino: archivo.destino, tipo: archivo.tipo || '?', razon: 'destino ilegible' });
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (hashes.has(hashLocal)) {
|
|
121
|
+
eliminables.push({ origen: origenRel, destino: archivo.destino, tipo: archivo.tipo || '?' });
|
|
122
|
+
} else {
|
|
123
|
+
preservados.push({
|
|
124
|
+
origen: origenRel,
|
|
125
|
+
destino: archivo.destino,
|
|
126
|
+
tipo: archivo.tipo || '?',
|
|
127
|
+
razon: 'contenido evolucionado por el usuario (hash ≠ canónico) — merge-no-overwrite',
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return { eliminables, preservados, yaAusentes, vigentes };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Ejecuta el plan: borra los eliminables, limpia del estado las entradas
|
|
137
|
+
* eliminadas y las ya ausentes, y persiste el estado actualizado.
|
|
138
|
+
*
|
|
139
|
+
* @param {object} plan Resultado de planPrune
|
|
140
|
+
* @param {object} opciones
|
|
141
|
+
* @param {string} opciones.rutaBase
|
|
142
|
+
* @returns {{eliminados: string[], erroresBorrado: Array<{destino: string, error: string}>, entradasLimpiadas: number}}
|
|
143
|
+
*/
|
|
144
|
+
function ejecutarPrune(plan, { rutaBase }) {
|
|
145
|
+
const eliminados = [];
|
|
146
|
+
const erroresBorrado = [];
|
|
147
|
+
|
|
148
|
+
for (const item of plan.eliminables) {
|
|
149
|
+
try {
|
|
150
|
+
fs.unlinkSync(item.destino);
|
|
151
|
+
eliminados.push(item.destino);
|
|
152
|
+
// Si el directorio del skill quedó vacío (p. ej. habilidades/x/), retirarlo.
|
|
153
|
+
const dir = path.dirname(item.destino);
|
|
154
|
+
try {
|
|
155
|
+
if (fs.readdirSync(dir).length === 0) fs.rmdirSync(dir);
|
|
156
|
+
} catch { /* dir compartido o ya ausente — irrelevante */ }
|
|
157
|
+
} catch (err) {
|
|
158
|
+
erroresBorrado.push({ destino: item.destino, error: err.message });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Limpiar el estado: fuera las entradas eliminadas y las ya ausentes.
|
|
163
|
+
const estado = cargarEstado(rutaBase);
|
|
164
|
+
let entradasLimpiadas = 0;
|
|
165
|
+
if (estado) {
|
|
166
|
+
const quitar = new Set([
|
|
167
|
+
...eliminados,
|
|
168
|
+
...plan.yaAusentes.map(a => a.destino),
|
|
169
|
+
]);
|
|
170
|
+
const antes = estado.archivosInstalados.length;
|
|
171
|
+
estado.archivosInstalados = estado.archivosInstalados.filter(a => !quitar.has(a.destino));
|
|
172
|
+
entradasLimpiadas = antes - estado.archivosInstalados.length;
|
|
173
|
+
estado.actualizadoEn = new Date().toISOString();
|
|
174
|
+
guardarEstado(estado, rutaBase);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return { eliminados, erroresBorrado, entradasLimpiadas };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
module.exports = { planPrune, ejecutarPrune, cargarHashesConocidos };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema-version": "1.0.0",
|
|
3
3
|
"description": "Catálogo de reglas globales ~/.claude/rules/ que NO deben duplicarse inline en CLAUDE.md de proyectos. Consumido por scripts/lib/detector-reglas-duplicadas.js y por scripts/auditar-claudemd.js dimensión 'duplicacion-reglas-globales'. Cada entrada lista: nombre canónico, archivo origen, sección canónica para referenciar, patrones de detección (regex), y reemplazo sugerido.",
|
|
4
|
-
"version": "1.
|
|
5
|
-
"actualizado": "2026-
|
|
4
|
+
"version": "1.2.0",
|
|
5
|
+
"actualizado": "2026-07-08",
|
|
6
6
|
"reglas": [
|
|
7
7
|
{
|
|
8
8
|
"id": "idioma-espanol-mexico",
|
|
@@ -168,6 +168,20 @@
|
|
|
168
168
|
"min_matches": 1,
|
|
169
169
|
"scope": "project-level",
|
|
170
170
|
"remediacion_sugerida": "Eliminar el bloque local. La regla global aplica a cualquier proyecto con vault Obsidian disponible."
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"id": "usar-code-review-graph",
|
|
174
|
+
"regla_global": "usar-code-review-graph.md",
|
|
175
|
+
"seccion_canonica": "Usar code-review-graph antes de Read/Grep/Glob para explorar",
|
|
176
|
+
"referencia_canonica": "@~/.claude/rules/usar-code-review-graph.md",
|
|
177
|
+
"descripcion": "Comportamiento completo del MCP code-review-graph (tabla de herramientas, workflow, excepciones). El CLAUDE.md del proyecto solo declara la disponibilidad del grafo — re-derivar el comportamiento inline es duplicación. Caso real: CLAUDE.md de swl-ses acumuló 37 líneas re-derivándolo en inglés (removido 2026-07-08).",
|
|
178
|
+
"patrones": [
|
|
179
|
+
"ALWAYS use thes+code-review-graph",
|
|
180
|
+
"code-review-graph MCP tools BEFORE",
|
|
181
|
+
"graph tools FIRST",
|
|
182
|
+
"detect_changes.*get_review_context|get_impact_radius.*blast radius"
|
|
183
|
+
],
|
|
184
|
+
"remediacion_sugerida": "Una línea en Stack: 'MCP del proyecto: code-review-graph disponible vía .mcp.json. Comportamiento: @~/.claude/rules/usar-code-review-graph.md'"
|
|
171
185
|
}
|
|
172
186
|
],
|
|
173
187
|
"configuracion": {
|
|
@@ -203,8 +203,8 @@ function applyFeedback(instinto, type, currentDate) {
|
|
|
203
203
|
throw new Error(`Tipo de feedback inválido: ${type}`);
|
|
204
204
|
}
|
|
205
205
|
const now = (currentDate instanceof Date)
|
|
206
|
-
? currentDate.
|
|
207
|
-
: (currentDate || new Date().
|
|
206
|
+
? currentDate.toLocaleDateString('sv')
|
|
207
|
+
: (currentDate || new Date().toLocaleDateString('sv'));
|
|
208
208
|
|
|
209
209
|
const next = { ...instinto };
|
|
210
210
|
if (type === 'helpful') next.helpful_count = (next.helpful_count || 0) + 1;
|