@saulwade/swl-ses 2.0.0 → 2.2.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.
Files changed (97) hide show
  1. package/CLAUDE.md +196 -196
  2. package/README.md +579 -579
  3. package/agentes/_propose-step.md +90 -0
  4. package/agentes/implementador-swl.md +2 -0
  5. package/agentes/orquestador-swl.md +2 -0
  6. package/agentes/perfilador-usuario-swl.md +14 -1
  7. package/bin/swl-ses.js +64 -1
  8. package/comandos/swl/adoptar-proyecto.md +258 -255
  9. package/comandos/swl/aprender.md +828 -840
  10. package/comandos/swl/aprobar-plan.md +26 -37
  11. package/comandos/swl/autoresearch.md +12 -14
  12. package/comandos/swl/briefing.md +119 -0
  13. package/comandos/swl/checkpoint.md +10 -15
  14. package/comandos/swl/claudemd.md +239 -234
  15. package/comandos/swl/compactar.md +29 -2
  16. package/comandos/swl/configurar-ci.md +20 -19
  17. package/comandos/swl/cron.md +10 -12
  18. package/comandos/swl/discutir-fase.md +8 -5
  19. package/comandos/swl/ejecutar-fase.md +15 -2
  20. package/comandos/swl/evolucionar.md +6 -11
  21. package/comandos/swl/inbox.md +10 -10
  22. package/comandos/swl/modelo.md +7 -9
  23. package/comandos/swl/notificaciones.md +19 -116
  24. package/comandos/swl/nuevo-proyecto.md +205 -205
  25. package/comandos/swl/planear-fase.md +5 -3
  26. package/comandos/swl/release.md +46 -0
  27. package/comandos/swl/status.md +333 -279
  28. package/comandos/swl/verificar.md +817 -812
  29. package/habilidades/changelog-generator/scripts/parse-commits.js +6 -4
  30. package/habilidades/ejecutar-fase/SKILL.md +541 -518
  31. package/habilidades/planear-fase/SKILL.md +3 -2
  32. package/habilidades/swl-claudemd/SKILL.md +10 -6
  33. package/habilidades/tdd-workflow/SKILL.md +715 -713
  34. package/habilidades/validacion-ci-sistema/SKILL.md +17 -1
  35. package/hooks/calidad-pre-commit.js +5 -1
  36. package/hooks/check-update.js +39 -1
  37. package/hooks/lib/autonomia.js +208 -0
  38. package/hooks/lib/briefing.js +474 -0
  39. package/hooks/lib/propose-step.js +358 -0
  40. package/hooks/session-briefing.js +98 -0
  41. package/hooks/telemetria-skill-routing.js +100 -0
  42. package/instintos/autonomia.yaml +27 -0
  43. package/llms.txt +4 -4
  44. package/manifiestos/hooks-config.json +18 -0
  45. package/manifiestos/modulos.json +25 -3
  46. package/manifiestos/skills-lock.json +17 -17
  47. package/package.json +93 -93
  48. package/plugin.json +371 -371
  49. package/reglas/analizar-directorios-antes-de-escribir.md +228 -0
  50. package/reglas/consultar-vault-primero.md +195 -0
  51. package/reglas/debatir-antes-de-aceptar.md +158 -0
  52. package/reglas/git-coauthor.md +100 -0
  53. package/reglas/monitor-ci.md +309 -0
  54. package/reglas/registro-componentes-nuevos.md +38 -10
  55. package/reglas/sesiones-paralelas.md +180 -0
  56. package/reglas/usar-code-review-graph.md +155 -0
  57. package/reglas/verificar-citas-normativas.md +548 -0
  58. package/scripts/auditar-claudemd.js +38 -0
  59. package/scripts/cli/aprobar-plan.js +73 -0
  60. package/scripts/cli/briefing.js +23 -0
  61. package/scripts/cli/ciclo-evolucion.js +26 -0
  62. package/scripts/cli/configurar-ci.js +40 -0
  63. package/scripts/cli/derivar-feature-list.js +25 -0
  64. package/scripts/cli/detectar-host.js +27 -0
  65. package/scripts/cli/diary-entry.js +69 -0
  66. package/scripts/cli/execution-state.js +18 -0
  67. package/scripts/cli/gateway-notify.js +41 -0
  68. package/scripts/cli/liberar-fase.js +42 -0
  69. package/scripts/cli/loop-telemetry.js +125 -0
  70. package/scripts/cli/mark-evolved.js +56 -0
  71. package/scripts/cli/metricas-dora.js +26 -0
  72. package/scripts/cli/near-duplicate.js +55 -0
  73. package/scripts/cli/notificaciones.js +123 -0
  74. package/scripts/cli/propose-step.js +29 -0
  75. package/scripts/cli/schedule-parse.js +19 -0
  76. package/scripts/cli/sugerir-modelo.js +20 -0
  77. package/scripts/cli/verificar-plan.js +36 -0
  78. package/scripts/cli/verificar-trazabilidad.js +35 -0
  79. package/scripts/derivar-feature-list.js +1 -0
  80. package/scripts/instalador.js +52 -6
  81. package/scripts/lib/auditar-invocaciones-comandos.js +104 -0
  82. package/scripts/lib/ci-reader.js +193 -0
  83. package/scripts/lib/detectar-host-swl.js +175 -0
  84. package/scripts/lib/evidencia-release.js +322 -0
  85. package/scripts/lib/gate-hooks-requires.js +249 -0
  86. package/scripts/lib/gate-licencias.js +212 -0
  87. package/scripts/lib/git-metricas.js +257 -0
  88. package/scripts/lib/metricas-dora.js +204 -0
  89. package/scripts/lib/resolver-plan-fase.js +37 -0
  90. package/scripts/tui/ejecutores.js +1 -1
  91. package/scripts/validar-manifest.js +92 -1
  92. package/scripts/validar.js +13 -0
  93. package/scripts/verificar-evolucion.js +54 -4
  94. package/scripts/verificar-release.js +102 -0
  95. package/scripts/verificar-trazabilidad.js +12 -6
  96. package/reglas/arquitectura.evolved.json +0 -7
  97. package/reglas/seguridad.evolved.json +0 -7
@@ -33,6 +33,26 @@ const fs = require('fs');
33
33
  const path = require('path');
34
34
  const { execSync } = require('child_process');
35
35
 
36
+ /**
37
+ * Detecta si `dir` es la raíz del paquete swl-ses (repo madre).
38
+ *
39
+ * Espeja la excepción de `markAsEvolved` (hooks/lib/evolution-tracker.js):
40
+ * en el repo madre los cambios del mantenedor se rastrean por git + bump de
41
+ * `version`, NO por metadatos `evolved-*` — el tracker se NIEGA a marcarlos.
42
+ * Antes de este fix, los CHECKs 2/3 exigían aquí lo que el tracker rechaza
43
+ * escribir → el gate del Paso 6 de /swl:aprender era insatisfacible en el
44
+ * repo madre (detectado 2026-06-12, aprendizaje M4). El set de nombres debe
45
+ * mantenerse alineado con `isPackageRoot` del tracker.
46
+ */
47
+ function esRaizDelPaquete(dir) {
48
+ try {
49
+ const pkg = JSON.parse(fs.readFileSync(path.join(dir || process.cwd(), 'package.json'), 'utf8'));
50
+ return pkg.name === '@saulwade/swl-ses' || pkg.name === 'swl-ses';
51
+ } catch {
52
+ return false;
53
+ }
54
+ }
55
+
36
56
  /** Extrae el bloque de frontmatter YAML del inicio del archivo. */
37
57
  function extraerFrontmatter(contenido) {
38
58
  const m = /^---\r?\n([\s\S]*?)\r?\n---/.exec(contenido);
@@ -132,8 +152,16 @@ function archivosModificados(since) {
132
152
  }
133
153
  }
134
154
 
135
- /** Verifica un archivo concreto. Devuelve `{ archivo, problemas[], info }`. */
136
- function verificarArchivo(filePath) {
155
+ /**
156
+ * Verifica un archivo concreto. Devuelve `{ archivo, problemas[], info }`.
157
+ * @param {object} [opts] - `opts.raizPaquete` fuerza/inyecta la detección de
158
+ * repo madre (default: `esRaizDelPaquete(process.cwd())`). En repo madre los
159
+ * CHECKs 2/3 (evolved) se omiten — solo aplican version presente + bump.
160
+ */
161
+ function verificarArchivo(filePath, opts = {}) {
162
+ const raizPaquete = opts.raizPaquete !== undefined
163
+ ? opts.raizPaquete
164
+ : esRaizDelPaquete(process.cwd());
137
165
  const resultado = { archivo: filePath, problemas: [], info: {} };
138
166
 
139
167
  if (!fs.existsSync(filePath)) {
@@ -186,7 +214,15 @@ function verificarArchivo(filePath) {
186
214
  // CHECK 2: evolved = true (en frontmatter o en .evolved.json)
187
215
  // v1.6.1 (Cabo A4): exento si deprecated:true — no tiene sentido exigir
188
216
  // metadatos de evolución para componentes que se van a eliminar.
189
- if (ev.evolved !== 'true' && !esDeprecated) {
217
+ // Exento también en el repo madre: markAsEvolved se NIEGA a marcar ahí
218
+ // (los cambios del mantenedor se rastrean por git + bump de version), así
219
+ // que exigirlo haría el gate insatisfacible. Ver esRaizDelPaquete().
220
+ if (raizPaquete) {
221
+ resultado.info.notas = resultado.info.notas || [];
222
+ resultado.info.notas.push(
223
+ 'raíz del paquete: checks evolved omitidos (cambios del mantenedor = git + bump)'
224
+ );
225
+ } else if (ev.evolved !== 'true' && !esDeprecated) {
190
226
  resultado.problemas.push(
191
227
  '`evolved` no encontrado (ni en frontmatter ni en .evolved.json del directorio)'
192
228
  );
@@ -284,6 +320,13 @@ function main() {
284
320
  archivos = args.filter(a => !a.startsWith('--'));
285
321
  }
286
322
 
323
+ if (esRaizDelPaquete(process.cwd())) {
324
+ process.stdout.write(
325
+ '[nota] Raíz del paquete swl-ses: los checks de `evolved` se omiten — ' +
326
+ 'los cambios del mantenedor se rastrean por git + bump de `version`.\n'
327
+ );
328
+ }
329
+
287
330
  let fallos = 0;
288
331
  for (const archivo of archivos) {
289
332
  const r = verificarArchivo(archivo);
@@ -302,4 +345,11 @@ function main() {
302
345
 
303
346
  if (require.main === module) main();
304
347
 
305
- module.exports = { verificarArchivo, extraerFrontmatter, leerCampo, obtenerVersionEnHEAD, main };
348
+ module.exports = {
349
+ verificarArchivo,
350
+ extraerFrontmatter,
351
+ leerCampo,
352
+ obtenerVersionEnHEAD,
353
+ esRaizDelPaquete,
354
+ main,
355
+ };
@@ -32,6 +32,10 @@
32
32
  const fs = require('fs');
33
33
  const path = require('path');
34
34
  const contadoresLib = require('./lib/contadores-inventario');
35
+ // Gate de licencias (Fase 14, ADR-0038) — WARN-ONLY: reporta copyleft pero
36
+ // NUNCA bloquea el release en v1 (D-14-02). Cargado como módulo, no subproceso.
37
+ const { evaluarLicencias } = require('./lib/gate-licencias');
38
+ const { evaluarHooksRequires } = require('./lib/gate-hooks-requires');
35
39
 
36
40
  const CWD = process.cwd();
37
41
 
@@ -272,6 +276,18 @@ function main() {
272
276
  fallasObligatorias++;
273
277
  }
274
278
 
279
+ // Gate de requires hooks→scripts: valida que toda lib de scripts/ requerida
280
+ // por un hook DISTRIBUIDO (incluidas dependencias transitivas) esté registrada
281
+ // en modulos.json y cubierta en todos los perfiles que instalan el hook.
282
+ // Bloqueante: sin esto el hook muere con MODULE_NOT_FOUND en el destino y el
283
+ // wrapper de settings.json lo traga en silencio. Origen: bug check-update —
284
+ // el aviso de nuevas versiones nunca llegó a instalaciones destino (2026-06-12,
285
+ // commit 12b2a31). Misma familia que el gate de bin-imports, eje instalador→destino.
286
+ const gateHooksRequires = ejecutarGateHooksRequires();
287
+ if (gateHooksRequires.disponible && gateHooksRequires.hallazgos.length > 0) {
288
+ fallasObligatorias++;
289
+ }
290
+
275
291
  // Gate de consistencia cross-manifest del campo description:
276
292
  // valida que package.json#description y plugin.json#description tengan
277
293
  // las mismas cifras (60 agentes / N habilidades / M comandos / K reglas / L hooks)
@@ -295,6 +311,12 @@ function main() {
295
311
  }
296
312
  }
297
313
 
314
+ // Gate de licencias (Fase 14, ADR-0038) — WARN-ONLY por diseño (D-14-02):
315
+ // clasifica el árbol prod (copyleft fuerte/débil, desconocida) pero NUNCA
316
+ // suma a fallasObligatorias. Solo informa; la promoción a blocking exigiría
317
+ // calibración + ADR posterior (mismo patrón que G0/G2/G3).
318
+ const gateLicencias = ejecutarGateLicencias();
319
+
298
320
  if (jsonOut) {
299
321
  process.stdout.write(JSON.stringify({
300
322
  version,
@@ -304,8 +326,10 @@ function main() {
304
326
  warnings_opcionales: warningsOpcionales,
305
327
  contadores_gate: gateContadores,
306
328
  bin_imports_gate: gateBinImports,
329
+ hooks_requires_gate: gateHooksRequires,
307
330
  description_gate: gateDescription,
308
331
  aiisms_gate: aiismsGate,
332
+ gate_licencias: gateLicencias,
309
333
  resultados: resultados.map(({ entrada, resultado }) => ({
310
334
  archivo: entrada.archivo,
311
335
  obligatorio: entrada.obligatorio,
@@ -378,6 +402,28 @@ function main() {
378
402
  process.stdout.write('\n');
379
403
  process.stdout.write('Gate de bin-imports: no disponible — ' + gateBinImports.error + '\n');
380
404
  }
405
+ if (gateHooksRequires.disponible) {
406
+ process.stdout.write('\n');
407
+ process.stdout.write('Gate de requires hooks→scripts (vs modulos.json + perfiles.json):\n');
408
+ process.stdout.write(
409
+ ' Hooks distribuidos analizados: ' + gateHooksRequires.hooksDistribuidos +
410
+ ' | dependencias hooks→scripts: ' + gateHooksRequires.dependencias.length + '\n'
411
+ );
412
+ if (gateHooksRequires.hallazgos.length === 0) {
413
+ process.stdout.write(' [OK] Toda lib de scripts/ requerida por hooks distribuidos está registrada y cubierta\n');
414
+ } else {
415
+ for (const h of gateHooksRequires.hallazgos) {
416
+ process.stdout.write(' [FALLA] [' + h.tipo + '] ' + h.detalle + '\n');
417
+ }
418
+ process.stdout.write(
419
+ ' Bloqueo: registra la lib (y sus transitivas) en el módulo del hook en modulos.json. ' +
420
+ 'Sin esto el hook muere en silencio en el destino (wrapper traga MODULE_NOT_FOUND).\n'
421
+ );
422
+ }
423
+ } else if (gateHooksRequires.error) {
424
+ process.stdout.write('\n');
425
+ process.stdout.write('Gate de hooks-requires: no disponible — ' + gateHooksRequires.error + '\n');
426
+ }
381
427
  if (gateDescription.disponible) {
382
428
  process.stdout.write('\n');
383
429
  process.stdout.write('Gate de description (package.json vs plugin.json vs INVENTARIO.md):\n');
@@ -420,6 +466,29 @@ function main() {
420
466
  );
421
467
  }
422
468
  }
469
+ if (gateLicencias.disponible) {
470
+ process.stdout.write('\n');
471
+ process.stdout.write('Gate de licencias (árbol prod — WARN-ONLY, no bloquea):\n');
472
+ const r = gateLicencias.resumen;
473
+ process.stdout.write(
474
+ ' permisiva: ' + r.permisiva + ' | débil: ' + r.debil +
475
+ ' | fuerte: ' + r.fuerte + ' | desconocida: ' + r.desconocida + '\n'
476
+ );
477
+ const alertas = gateLicencias.hallazgos.filter((h) => h.clasificacion !== 'permisiva');
478
+ if (alertas.length > 0) {
479
+ for (const h of alertas) {
480
+ process.stdout.write(
481
+ ' [WARN ' + h.clasificacion + '] ' + h.paquete + '@' + h.version + ' — ' + h.licencia + '\n'
482
+ );
483
+ }
484
+ process.stdout.write(' Aviso: licencias no permisivas detectadas (informativo, no bloquea el release).\n');
485
+ } else {
486
+ process.stdout.write(' [OK] Todas las dependencias de producción son permisivas.\n');
487
+ }
488
+ } else if (gateLicencias.error) {
489
+ process.stdout.write('\n');
490
+ process.stdout.write('Gate de licencias: no disponible — ' + gateLicencias.error + '\n');
491
+ }
423
492
  if (fallasObligatorias > 0) {
424
493
  process.stdout.write('\n');
425
494
  process.stdout.write('Corrige los archivos marcados como FALLA antes de hacer push o publicar.\n');
@@ -583,6 +652,18 @@ function ejecutarGateBinImports() {
583
652
  }
584
653
  }
585
654
 
655
+ /**
656
+ * Gate de requires hooks→scripts (lib: scripts/lib/gate-hooks-requires.js).
657
+ * Bloqueante. Ver comentario en el flujo principal.
658
+ */
659
+ function ejecutarGateHooksRequires() {
660
+ try {
661
+ return evaluarHooksRequires(CWD);
662
+ } catch (err) {
663
+ return { disponible: false, error: err.message };
664
+ }
665
+ }
666
+
586
667
  function ejecutarGateAiisms() {
587
668
  const { spawnSync } = require('child_process');
588
669
  const detector = path.join(CWD, 'habilidades', 'estilo-sin-ai-isms', 'scripts', 'detectar_aiisms.py');
@@ -795,6 +876,26 @@ function ejecutarGateDescription(contadoresReales) {
795
876
  };
796
877
  }
797
878
 
879
+ /**
880
+ * Gate de licencias (Fase 14, ADR-0038) — WARN-ONLY.
881
+ *
882
+ * Clasifica las licencias del árbol de producción (vía scripts/lib/gate-licencias)
883
+ * y devuelve el resumen para impresión. NO bloquea el release: el caller NUNCA
884
+ * suma este resultado a fallasObligatorias (D-14-02, calibración primero).
885
+ *
886
+ * @param {string} [baseDir=CWD] raíz del proyecto a evaluar (parametrizable
887
+ * para tests; en producción usa el CWD del proceso).
888
+ * @returns {{disponible: boolean, hallazgos?: Array, resumen?: object, error?: string}}
889
+ */
890
+ function ejecutarGateLicencias(baseDir = CWD) {
891
+ try {
892
+ const { hallazgos, resumen } = evaluarLicencias(baseDir);
893
+ return { disponible: true, hallazgos, resumen };
894
+ } catch (err) {
895
+ return { disponible: false, error: err.message };
896
+ }
897
+ }
898
+
798
899
  if (require.main === module) main();
799
900
 
800
901
  module.exports = {
@@ -803,4 +904,5 @@ module.exports = {
803
904
  versionObjetivo,
804
905
  extraerCifrasDescription,
805
906
  ejecutarGateDescription,
907
+ ejecutarGateLicencias,
806
908
  };
@@ -51,7 +51,10 @@ function extraerReqs(contextoTexto) {
51
51
  // Definición: línea con `**REQ-NN**:` o `REQ-NN:` (con bullet opcional).
52
52
  // El criterio puede continuar en líneas siguientes indentadas — se capturan
53
53
  // hasta la próxima definición para detectar la anotación de método.
54
- const re = /^\s*(?:[-*]\s*)?\*{0,2}(REQ-\d{1,3})\*{0,2}\s*:([\s\S]*?)(?=^\s*(?:[-*]\s*)?\*{0,2}REQ-\d|^#|\n\n|$(?![\s\S]))/gm;
54
+ // REQ-NN (legacy) o REQ-NN-MM (namespaceado por fase, DT-IDS-NAMESPACE).
55
+ // El grupo `(?:-\d{1,3})?` es greedy: en `REQ-12-01` captura el ID completo,
56
+ // nunca el prefijo `REQ-12` truncado.
57
+ const re = /^\s*(?:[-*]\s*)?\*{0,2}(REQ-\d{1,3}(?:-\d{1,3})?)\*{0,2}\s*:([\s\S]*?)(?=^\s*(?:[-*]\s*)?\*{0,2}REQ-\d|^#|\n\n|$(?![\s\S]))/gm;
55
58
  let m;
56
59
  while ((m = re.exec(contextoTexto)) !== null) {
57
60
  const cuerpo = m[2];
@@ -76,8 +79,11 @@ function extraerMatrizPlan(planTexto) {
76
79
  if (!tareas.has(tareaActual)) tareas.set(tareaActual, []);
77
80
  continue;
78
81
  }
79
- if (tareaActual && /\*\*Verifica REQ\*\*\s*:/i.test(linea)) {
80
- const reqs = linea.match(/REQ-\d{1,3}/g) || [];
82
+ // Solo la línea-campo cuenta (`- **Verifica REQ**: ...` al inicio, bullet
83
+ // opcional). Una descripción que cite `**Verifica REQ**:` en prosa NO debe
84
+ // contaminar la matriz ni ocultar huérfanos (ancla `^` obligatoria).
85
+ if (tareaActual && /^\s*(?:[-*]\s*)?\*\*Verifica REQ\*\*\s*:/i.test(linea)) {
86
+ const reqs = linea.match(/REQ-\d{1,3}(?:-\d{1,3})?/g) || [];
81
87
  tareas.get(tareaActual).push(...reqs);
82
88
  }
83
89
  }
@@ -102,7 +108,7 @@ function extraerCommitsConRefs(cwd) {
102
108
  if (!hash || !cuerpo) continue;
103
109
  const refs = cuerpo.match(/^Refs:\s*(.+)$/m);
104
110
  if (!refs) continue;
105
- for (const req of refs[1].match(/REQ-\d{1,3}/g) || []) {
111
+ for (const req of refs[1].match(/REQ-\d{1,3}(?:-\d{1,3})?/g) || []) {
106
112
  if (!porReq.has(req)) porReq.set(req, []);
107
113
  porReq.get(req).push(hash.trim());
108
114
  }
@@ -138,7 +144,7 @@ function extraerTestsConMarker(cwd) {
138
144
  } catch (_) {
139
145
  continue;
140
146
  }
141
- for (const m of contenido.matchAll(/verifica:\s*(REQ-\d{1,3})/g)) {
147
+ for (const m of contenido.matchAll(/verifica:\s*(REQ-\d{1,3}(?:-\d{1,3})?)/g)) {
142
148
  const req = m[1];
143
149
  if (!porReq.has(req)) porReq.set(req, []);
144
150
  if (!porReq.get(req).includes(archivo)) porReq.get(req).push(archivo);
@@ -289,4 +295,4 @@ if (require.main === module) {
289
295
  main();
290
296
  }
291
297
 
292
- module.exports = { extraerReqs, extraerMatrizPlan, extraerCommitsConRefs, extraerTestsConMarker, validarFase };
298
+ module.exports = { extraerReqs, extraerMatrizPlan, extraerCommitsConRefs, extraerTestsConMarker, validarFase, main };
@@ -1,7 +0,0 @@
1
- {
2
- "evolved": true,
3
- "evolved-from": "1.8.0",
4
- "evolved-at": "2026-06-04",
5
- "evolved-by": "evolucionar",
6
- "evolved-note": "PE-010 sección nueva 'Tablas append-only — excepción documentada a audit columns nullable=False'. Patrón portable a logs estructurados, audit trails, event sourcing, telemetría. Origen: OIC v1.5 Slice 1 2026-06-04 (BitacoraError)."
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "evolved": true,
3
- "evolved-from": "1.8.0",
4
- "evolved-at": "2026-06-04",
5
- "evolved-by": "evolucionar",
6
- "evolved-note": "PE-003 (verificación de flags antes de asumir servicios externos LDAP/Redis/S3) + PE-004 (sanitización PII centralizada en handlers) + PE-007 (generación de passwords legibles sin chars ambiguos). Origen: OIC v1.5 2026-06-04, bug histórico manuel.monteagudo + Slice 2 v1.5 Observabilidad."
7
- }