@saulwade/swl-ses 2.4.2 → 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.
Files changed (198) hide show
  1. package/CLAUDE.md +194 -241
  2. package/README.md +600 -597
  3. package/agentes/_intent-spec.md +73 -73
  4. package/agentes/_propose-step.md +90 -90
  5. package/agentes/abogado-diablo-swl.md +145 -0
  6. package/agentes/accesibilidad-wcag-swl.md +690 -690
  7. package/agentes/arquitecto-swl.md +267 -267
  8. package/agentes/auto-evolucion-swl.md +908 -908
  9. package/agentes/backend-api-swl.md +1 -1
  10. package/agentes/backend-csharp-swl.md +420 -420
  11. package/agentes/backend-go-swl.md +390 -390
  12. package/agentes/backend-java-swl.md +281 -281
  13. package/agentes/backend-node-swl.md +1 -1
  14. package/agentes/backend-python-swl.md +1 -1
  15. package/agentes/backend-rust-swl.md +364 -364
  16. package/agentes/backend-workers-swl.md +482 -482
  17. package/agentes/cloud-infra-swl.md +509 -509
  18. package/agentes/consolidador-swl.md +541 -541
  19. package/agentes/datos-swl.md +1 -1
  20. package/agentes/depurador-swl.md +352 -352
  21. package/agentes/devops-ci-swl.md +400 -400
  22. package/agentes/disenador-ui-swl.md +569 -569
  23. package/agentes/documentador-swl.md +345 -345
  24. package/agentes/frontend-angular-swl.md +621 -621
  25. package/agentes/frontend-css-swl.md +716 -716
  26. package/agentes/frontend-react-swl.md +692 -692
  27. package/agentes/frontend-swl.md +496 -496
  28. package/agentes/frontend-tailwind-swl.md +826 -826
  29. package/agentes/gh-fix-ci-swl.md +6 -1
  30. package/agentes/implementador-swl.md +1 -1
  31. package/agentes/investigador-swl.md +432 -432
  32. package/agentes/investigador-ux-swl.md +505 -505
  33. package/agentes/llm-apps-swl.md +1 -1
  34. package/agentes/migrador-swl.md +442 -442
  35. package/agentes/mobile-android-swl.md +511 -511
  36. package/agentes/mobile-cross-swl.md +541 -541
  37. package/agentes/mobile-ios-swl.md +502 -502
  38. package/agentes/mobile-testing-swl.md +302 -302
  39. package/agentes/nemesis-auditor-swl.md +285 -285
  40. package/agentes/notificador-swl.md +1 -1
  41. package/agentes/observabilidad-swl.md +438 -438
  42. package/agentes/pagos-swl.md +310 -310
  43. package/agentes/perfilador-usuario-swl.md +321 -321
  44. package/agentes/planificador-swl.md +399 -399
  45. package/agentes/producto-prd-swl.md +589 -589
  46. package/agentes/red-team-swl.md +218 -218
  47. package/agentes/release-manager-swl.md +590 -590
  48. package/agentes/rendimiento-swl.md +713 -713
  49. package/agentes/resolutor-build-swl.md +10 -1
  50. package/agentes/revisor-angular-swl.md +278 -278
  51. package/agentes/revisor-codigo-swl.md +1 -1
  52. package/agentes/revisor-csharp-swl.md +264 -264
  53. package/agentes/revisor-go-swl.md +259 -259
  54. package/agentes/revisor-java-swl.md +257 -257
  55. package/agentes/revisor-kotlin-swl.md +273 -273
  56. package/agentes/revisor-nextjs-swl.md +281 -281
  57. package/agentes/revisor-php-swl.md +271 -271
  58. package/agentes/revisor-react-swl.md +278 -278
  59. package/agentes/revisor-rust-swl.md +346 -346
  60. package/agentes/revisor-seguridad-swl.md +399 -399
  61. package/agentes/revisor-swift-swl.md +268 -268
  62. package/agentes/revisor-typescript-swl.md +346 -346
  63. package/agentes/sre-swl.md +1 -1
  64. package/agentes/tdd-qa-swl.md +393 -393
  65. package/bin/lib/bot-comandos.js +1 -1
  66. package/bin/swl-ses.js +6 -0
  67. package/comandos/swl/adoptar-proyecto.md +14 -2
  68. package/comandos/swl/configurar-ci.md +8 -1
  69. package/comandos/swl/deuda-codigo.md +97 -97
  70. package/comandos/swl/discutir-fase.md +22 -118
  71. package/comandos/swl/fix.md +118 -0
  72. package/comandos/swl/nuevo-proyecto.md +54 -3
  73. package/comandos/swl/predecir.md +32 -2
  74. package/comandos/swl/seguridad.md +189 -0
  75. package/comandos/swl/status.md +5 -3
  76. package/habilidades/aprendizaje-continuo/SKILL.md +3 -1
  77. package/habilidades/discutir-fase/SKILL.md +84 -81
  78. package/habilidades/discutir-fase/recursos/plantilla-contexto.md +136 -0
  79. package/habilidades/doc-sync/SKILL.md +3 -1
  80. package/habilidades/doubt-driven-review/SKILL.md +15 -1
  81. package/habilidades/ejecutar-task-iterativo/SKILL.md +278 -278
  82. package/habilidades/estructura-proyecto-claude/SKILL.md +11 -2
  83. package/habilidades/harness-claude-code/SKILL.md +3 -1
  84. package/habilidades/instalar-sistema/SKILL.md +3 -1
  85. package/habilidades/meta-reglas-extendido/SKILL.md +92 -0
  86. package/habilidades/meta-reglas-extendido/recursos/analisis-previo-tareas-grandes.md +186 -0
  87. package/habilidades/meta-reglas-extendido/recursos/analizar-directorios-antes-de-escribir.md +235 -0
  88. package/habilidades/meta-reglas-extendido/recursos/api-diseno.md +413 -0
  89. package/habilidades/meta-reglas-extendido/recursos/arquitectura.md +491 -0
  90. package/habilidades/meta-reglas-extendido/recursos/arreglar-al-detectar.md +264 -0
  91. package/habilidades/meta-reglas-extendido/recursos/debatir-antes-de-aceptar.md +152 -0
  92. package/habilidades/meta-reglas-extendido/recursos/git-workflow.md +259 -0
  93. package/habilidades/meta-reglas-extendido/recursos/gobernanza.md +291 -0
  94. package/habilidades/meta-reglas-extendido/recursos/memoria-consolidada.md +263 -0
  95. package/habilidades/meta-reglas-extendido/recursos/seguridad-agentes.md +443 -0
  96. package/habilidades/meta-reglas-extendido/recursos/sesiones-paralelas.md +190 -0
  97. package/habilidades/meta-reglas-extendido/recursos/sin-duplicacion-reglas-globales.md +179 -0
  98. package/habilidades/meta-reglas-extendido/recursos/skills-estandar.md +394 -0
  99. package/habilidades/meta-reglas-extendido/recursos/usar-code-review-graph.md +156 -0
  100. package/habilidades/meta-reglas-extendido/recursos/usar-context7.md +236 -0
  101. package/habilidades/meta-reglas-extendido/recursos/usar-sistema-swl.md +253 -0
  102. package/habilidades/meta-reglas-extendido/recursos/verificar-citas-normativas.md +527 -0
  103. package/habilidades/meta-skills-estandar/SKILL.md +3 -1
  104. package/habilidades/nuevo-proyecto/SKILL.md +20 -3
  105. package/habilidades/php-experto/SKILL.md +10 -3
  106. package/habilidades/{filament-admin/SKILL.md → php-experto/recursos/filament-admin.md} +23 -39
  107. package/habilidades/prevencion-sobreingenieria/recursos/soluciones-nativas.md +166 -166
  108. package/habilidades/prevencion-sobreingenieria/recursos/variables-residuales-post-refactor.md +85 -85
  109. package/habilidades/proceso-debate-adversarial/recursos/personas.md +5 -4
  110. package/habilidades/proceso-ingenieria-requerimientos/SKILL.md +147 -0
  111. package/hooks/check-update.js +19 -10
  112. package/hooks/contexto-subagente.js +68 -68
  113. package/hooks/degradacion-instintos.js +1 -1
  114. package/hooks/extraccion-aprendizajes.js +2 -2
  115. package/hooks/lib/briefing.js +3 -3
  116. package/hooks/lib/nudge-tracker.js +1 -1
  117. package/hooks/lib/otlp-exporter.js +1 -1
  118. package/hooks/lib/webhook-dedup.js +1 -1
  119. package/hooks/session-briefing.js +1 -1
  120. package/llms.txt +6 -6
  121. package/manifiestos/canonical-hashes.json +989 -0
  122. package/manifiestos/hooks-config.json +469 -469
  123. package/manifiestos/invariantes-criticos.json +30 -30
  124. package/manifiestos/modulos.json +168 -135
  125. package/manifiestos/perfiles.json +0 -2
  126. package/manifiestos/skills-lock.json +52 -59
  127. package/package.json +7 -5
  128. package/plantillas/github-workflows/README.md +15 -1
  129. package/plantillas/github-workflows/swl-devsecops.yml +70 -0
  130. package/plugin.json +5 -5
  131. package/reglas/analisis-previo-tareas-grandes.md +30 -156
  132. package/reglas/analizar-directorios-antes-de-escribir.md +30 -211
  133. package/reglas/api-diseno.md +28 -398
  134. package/reglas/arquitectura.md +35 -456
  135. package/reglas/arreglar-al-detectar.md +30 -230
  136. package/reglas/debatir-antes-de-aceptar.md +30 -143
  137. package/reglas/docs.md +7 -0
  138. package/reglas/estilo-codigo.md +9 -0
  139. package/reglas/fragmentos-compartidos.md +6 -0
  140. package/reglas/git-workflow.md +44 -240
  141. package/reglas/gobernanza.md +23 -262
  142. package/reglas/memoria-consolidada.md +34 -228
  143. package/reglas/performance.md +8 -0
  144. package/reglas/pruebas.md +12 -0
  145. package/reglas/seguridad-agentes.md +37 -418
  146. package/reglas/seguridad.md +12 -0
  147. package/reglas/sesiones-paralelas.md +29 -162
  148. package/reglas/sin-duplicacion-reglas-globales.md +25 -166
  149. package/reglas/skills-estandar.md +23 -373
  150. package/reglas/usar-code-review-graph.md +31 -140
  151. package/reglas/usar-context7.md +30 -208
  152. package/reglas/usar-sistema-swl.md +47 -242
  153. package/reglas/verificar-citas-normativas.md +47 -537
  154. package/scripts/actualizar.js +253 -253
  155. package/scripts/audit-tools/auditar-relleno-inventario.js +145 -0
  156. package/scripts/auditar-clases-conocidas.js +106 -0
  157. package/scripts/bootstrap-instintos.js +2 -2
  158. package/scripts/canario-hooks.js +166 -0
  159. package/scripts/cli/configurar-ci.js +2 -1
  160. package/scripts/evidencia-valor.js +93 -0
  161. package/scripts/field-report.js +1 -1
  162. package/scripts/generar-comandos.js +143 -0
  163. package/scripts/generar-inventario.js +236 -23
  164. package/scripts/generar-matriz-lenguajes.js +1 -1
  165. package/scripts/instalador.js +15 -1
  166. package/scripts/lib/configurar-ci.js +10 -3
  167. package/scripts/lib/detectar-runtime.js +12 -3
  168. package/scripts/lib/diary-entry.js +3 -1
  169. package/scripts/lib/drift-detector.js +1 -1
  170. package/scripts/lib/evidencia-valor.js +189 -0
  171. package/scripts/lib/expandir-targets.js +71 -71
  172. package/scripts/lib/frontmatter-md.js +63 -0
  173. package/scripts/lib/parsear-opciones.js +2 -0
  174. package/scripts/lib/prune-componentes.js +180 -0
  175. package/scripts/lib/reglas-globales-conocidas.json +16 -2
  176. package/scripts/lib/scoring-instintos.js +2 -2
  177. package/scripts/lib/toml-merge.js +204 -204
  178. package/scripts/lib/transformadores/claude.js +1 -1
  179. package/scripts/lib/transformadores/codex.js +1 -1
  180. package/scripts/lib/transformadores/copilot.js +1 -1
  181. package/scripts/lib/transformadores/cursor.js +1 -1
  182. package/scripts/lib/transformadores/gemini.js +22 -2
  183. package/scripts/lib/transformadores/opencode.js +1 -1
  184. package/scripts/mcp-server/auth.js +105 -105
  185. package/scripts/mcp-server/cache.js +106 -106
  186. package/scripts/prune.js +102 -0
  187. package/scripts/publicar.js +18 -2
  188. package/scripts/tui/pantallas/inspect.js +175 -175
  189. package/scripts/tui/pantallas/uninstall-wizard.js +210 -210
  190. package/scripts/tui/pantallas/update-wizard.js +234 -234
  191. package/scripts/tui/pantallas/welcome.js +189 -189
  192. package/habilidades/paid-media-tracking/SKILL.md +0 -269
  193. package/habilidades/paid-media-tracking/recursos/auditoria-tracking.md +0 -220
  194. package/habilidades/paid-media-tracking/recursos/google-ads-api.md +0 -215
  195. package/habilidades/tracking-measurement/SKILL.md +0 -239
  196. package/habilidades/tracking-measurement/recursos/consent-mode.md +0 -231
  197. package/habilidades/tracking-measurement/recursos/gtm-datalayer.md +0 -216
  198. package/habilidades/tracking-measurement/recursos/meta-capi.md +0 -262
@@ -44,18 +44,47 @@ function leerFrontmatter(ruta) {
44
44
  const match = contenido.match(/^---\r?\n([\s\S]*?)\r?\n---/);
45
45
  if (!match) return {};
46
46
  const fm = {};
47
- for (const linea of match[1].split(/\r?\n/)) {
47
+ const lineas = match[1].split(/\r?\n/);
48
+ for (let i = 0; i < lineas.length; i++) {
49
+ const linea = lineas[i];
50
+ if (/^\s/.test(linea)) continue; // continuación de multilínea, la consume el bloque de abajo
48
51
  const idx = linea.indexOf(':');
49
- if (idx > 0) {
50
- const key = linea.slice(0, idx).trim();
51
- const val = linea.slice(idx + 1).trim().replace(/^["']|["']$/g, '');
52
- fm[key] = val;
52
+ if (idx <= 0) continue;
53
+ const key = linea.slice(0, idx).trim();
54
+ let val = linea.slice(idx + 1).trim().replace(/^["']|["']$/g, '');
55
+ // Escalares plegados YAML (`description: >` / `|`): juntar las líneas indentadas siguientes.
56
+ if (val === '>' || val === '|' || val === '>-' || val === '|-' || val === '') {
57
+ const partes = [];
58
+ for (let j = i + 1; j < lineas.length && /^\s+\S/.test(lineas[j]); j++) {
59
+ partes.push(lineas[j].trim());
60
+ }
61
+ if (partes.length > 0) val = partes.join(' ');
53
62
  }
63
+ fm[key] = val;
54
64
  }
55
65
  return fm;
56
66
  } catch { return {}; }
57
67
  }
58
68
 
69
+ /** Recorta una descripción a n caracteres en límite de palabra, colapsando whitespace. */
70
+ function resumir(texto, n = 110) {
71
+ const limpio = String(texto || '').replace(/\s+/g, ' ').replace(/\|/g, '/').trim();
72
+ if (limpio.length <= n) return limpio;
73
+ const corte = limpio.lastIndexOf(' ', n);
74
+ return limpio.slice(0, corte > 40 ? corte : n) + '…';
75
+ }
76
+
77
+ function contarArchivosRec(dir, ext) {
78
+ let total = 0;
79
+ try {
80
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
81
+ if (e.isDirectory()) total += contarArchivosRec(path.join(dir, e.name), ext);
82
+ else if (e.name.endsWith(ext)) total++;
83
+ }
84
+ } catch { /* dir inexistente → 0 */ }
85
+ return total;
86
+ }
87
+
59
88
  function contarArchivos(dir, ext) {
60
89
  try {
61
90
  return fs.readdirSync(dir).filter(f => f.endsWith(ext)).length;
@@ -94,6 +123,12 @@ function recolectarAgentes() {
94
123
  version: fm.version || '?',
95
124
  model: fm.model || '?',
96
125
  nivelRiesgo: fm.nivelRiesgo || '?',
126
+ description: fm.description || '',
127
+ skillsInvocables: (fm.skillsInvocables || '')
128
+ .replace(/^\[|\]$/g, '')
129
+ .split(',')
130
+ .map(s => s.trim())
131
+ .filter(Boolean),
97
132
  };
98
133
  });
99
134
  }
@@ -105,7 +140,24 @@ function recolectarSkills() {
105
140
  );
106
141
  return dirs.map(d => {
107
142
  const fm = leerFrontmatter(path.join(dir, d, 'SKILL.md'));
108
- return { dir: d, name: fm.name || d, description: (fm.description || '').substring(0, 80) };
143
+ return {
144
+ dir: d,
145
+ name: fm.name || d,
146
+ version: fm.version || '—',
147
+ description: fm.description || '',
148
+ };
149
+ });
150
+ }
151
+
152
+ function recolectarComandos() {
153
+ const dir = path.join(RAIZ, 'comandos', 'swl');
154
+ return fs.readdirSync(dir).filter(f => f.endsWith('.md')).map(f => {
155
+ const fm = leerFrontmatter(path.join(dir, f));
156
+ return {
157
+ archivo: f,
158
+ name: fm.name || `swl:${f.replace('.md', '')}`,
159
+ description: fm.description || '',
160
+ };
109
161
  });
110
162
  }
111
163
 
@@ -139,19 +191,38 @@ function generarInventario() {
139
191
  const skills = recolectarSkills();
140
192
  const hooks = recolectarHooks();
141
193
  const libs = recolectarLibs();
142
- const comandos = contarArchivos(path.join(RAIZ, 'comandos', 'swl'), '.md');
194
+ const comandos = recolectarComandos();
143
195
  const reglasBase = contarArchivos(path.join(RAIZ, 'reglas'), '.md');
144
196
  const reglasLang = fs.readdirSync(path.join(RAIZ, 'reglas', 'lenguajes'))
145
197
  .filter(d => fs.statSync(path.join(RAIZ, 'reglas', 'lenguajes', d)).isDirectory())
146
198
  .reduce((acc, d) => acc + contarArchivos(path.join(RAIZ, 'reglas', 'lenguajes', d), '.md'), 0);
147
- const schemas = contarArchivos(path.join(RAIZ, 'schemas'), '.json');
148
- const scripts = contarArchivos(path.join(RAIZ, 'scripts', 'lib'), '.js') + contarArchivos(path.join(RAIZ, 'scripts', 'lib'), '.py');
199
+ const schemasList = fs.readdirSync(path.join(RAIZ, 'schemas')).filter(f => f.endsWith('.json')).sort();
200
+ const scriptsLibList = fs.readdirSync(path.join(RAIZ, 'scripts', 'lib'))
201
+ .filter(f => f.endsWith('.js') || f.endsWith('.py')).sort();
202
+ // Tipos antes omitidos del inventario (auditoría 2026-07-08)
203
+ const fragmentos = fs.readdirSync(path.join(RAIZ, 'agentes')).filter(f => f.startsWith('_') && f.endsWith('.md'));
204
+ const gatewayJs = contarArchivosRec(path.join(RAIZ, 'gateway'), '.js');
205
+ const manifiestosJson = contarArchivos(path.join(RAIZ, 'manifiestos'), '.json');
206
+ const contextos = contarArchivos(path.join(RAIZ, 'contextos'), '.md');
207
+ const plantillasTotal = contarArchivosRec(path.join(RAIZ, 'plantillas'), '.md') + contarArchivosRec(path.join(RAIZ, 'plantillas'), '.yml');
208
+ const scriptsTop = contarArchivos(path.join(RAIZ, 'scripts'), '.js');
209
+
210
+ // Mapa skill → agentes que la declaran en skillsInvocables
211
+ const skillAAgentes = new Map();
212
+ for (const a of agentes) {
213
+ for (const s of a.skillsInvocables) {
214
+ if (!skillAAgentes.has(s)) skillAAgentes.set(s, []);
215
+ skillAAgentes.get(s).push(a.name);
216
+ }
217
+ }
149
218
 
150
219
  const lines = [];
151
220
  lines.push(`# INVENTARIO.md — Referencia completa de componentes SWL v${pkg.version}`);
152
221
  lines.push('');
153
222
  lines.push('> Archivo generado automáticamente por `node scripts/generar-inventario.js`.');
154
223
  lines.push('> NO editar manualmente — se regenera con cada release.');
224
+ lines.push('> Índice de descubrimiento: cada catálogo incluye la descripción del');
225
+ lines.push('> componente para decidir qué usar sin abrir su archivo.');
155
226
  lines.push('');
156
227
  lines.push('## Resumen');
157
228
  lines.push('');
@@ -159,13 +230,19 @@ function generarInventario() {
159
230
  lines.push('|-----------|----------|');
160
231
  lines.push(`| Agentes SWL | ${agentes.length} |`);
161
232
  lines.push(`| Habilidades | ${skills.length} |`);
162
- lines.push(`| Comandos | ${comandos} |`);
233
+ lines.push(`| Comandos | ${comandos.length} |`);
163
234
  lines.push(`| Reglas base | ${reglasBase} |`);
164
235
  lines.push(`| Reglas por lenguaje | ${reglasLang} |`);
165
236
  lines.push(`| Hooks | ${hooks.length} |`);
166
237
  lines.push(`| Librerías de hooks | ${libs.length} |`);
167
- lines.push(`| Schemas | ${schemas} |`);
168
- lines.push(`| Scripts lib | ${scripts} |`);
238
+ lines.push(`| Schemas | ${schemasList.length} |`);
239
+ lines.push(`| Scripts lib | ${scriptsLibList.length} |`);
240
+ lines.push(`| Scripts top-level | ${scriptsTop} |`);
241
+ lines.push(`| Fragmentos de agentes | ${fragmentos.length} |`);
242
+ lines.push(`| Gateway (archivos .js) | ${gatewayJs} |`);
243
+ lines.push(`| Manifiestos | ${manifiestosJson} |`);
244
+ lines.push(`| Contextos | ${contextos} |`);
245
+ lines.push(`| Plantillas | ${plantillasTotal} |`);
169
246
  lines.push('');
170
247
 
171
248
  // Equipos
@@ -174,22 +251,47 @@ function generarInventario() {
174
251
  lines.push('| Equipo | Líder | Miembros paralelos | Cuándo |');
175
252
  lines.push('|--------|-------|-------------------|--------|');
176
253
  lines.push('| Discovery | investigador-swl | investigador-ux-swl, producto-prd-swl | Proyecto nuevo |');
177
- lines.push('| Arquitectura | arquitecto-swl | rendimiento-swl, revisor-seguridad-swl | Decisión de diseño |');
254
+ lines.push('| Arquitectura | arquitecto-swl | rendimiento-swl, revisor-seguridad-swl, abogado-diablo-swl | Decisión de diseño |');
178
255
  lines.push('| Frontend | frontend-swl | frontend-react-swl, frontend-css-swl, frontend-tailwind-swl | Feature UI compleja |');
179
256
  lines.push('| Backend | backend-python-swl | backend-api-swl, backend-workers-swl | Feature backend compleja |');
180
257
  lines.push('| Mobile | mobile-cross-swl | mobile-android-swl, mobile-ios-swl, mobile-testing-swl | Feature mobile |');
181
258
  lines.push('| Calidad | tdd-qa-swl | revisor-codigo-swl, revisor-seguridad-swl, accesibilidad-wcag-swl | Pre-release |');
182
- lines.push('| Operaciones | devops-ci-swl | cloud-infra-swl, observabilidad-swl, sre-swl | Deploy, infra, confiabilidad |');
259
+ lines.push('| Auditoría profunda | nemesis-auditor-swl | feynman-auditor-swl, state-inconsistency-auditor-swl | Lógica compleja con estado acoplado |');
260
+ lines.push('| Operaciones | devops-ci-swl | cloud-infra-swl, observabilidad-swl, sre-swl, gh-fix-ci-swl | Deploy, infra, confiabilidad, fallas CI |');
183
261
  lines.push('| Cierre | consolidador-swl | documentador-swl, release-manager-swl, notificador-swl | Fase completada |');
184
262
  lines.push('');
185
263
 
186
264
  // Agentes
187
265
  lines.push(`## Agentes SWL — Registro completo (${agentes.length})`);
188
266
  lines.push('');
189
- lines.push('| Agente | Versión | Modelo | Riesgo |');
190
- lines.push('|--------|---------|--------|--------|');
267
+ lines.push('| Agente | Versión | Modelo | Riesgo | Qué hace / cuándo invocarlo |');
268
+ lines.push('|--------|---------|--------|--------|------------------------------|');
191
269
  for (const a of agentes.sort((x, y) => x.name.localeCompare(y.name))) {
192
- lines.push(`| ${a.name} | ${a.version} | ${a.model} | ${a.nivelRiesgo} |`);
270
+ lines.push(`| ${a.name} | ${a.version} | ${a.model} | ${a.nivelRiesgo} | ${resumir(a.description, 160)} |`);
271
+ }
272
+ lines.push('');
273
+
274
+ // Habilidades — catálogo consultable (antes solo se contaban)
275
+ lines.push(`## Habilidades — Catálogo completo (${skills.length})`);
276
+ lines.push('');
277
+ lines.push('La columna "Agentes" lista quién la declara en `skillsInvocables`;');
278
+ lines.push('"—" significa que se invoca por descripción o desde comandos.');
279
+ lines.push('');
280
+ lines.push('| Skill | Versión | Descripción | Agentes |');
281
+ lines.push('|-------|---------|-------------|---------|');
282
+ for (const s of [...skills].sort((x, y) => x.name.localeCompare(y.name))) {
283
+ const invocadores = (skillAAgentes.get(s.name) || []).join(', ') || '—';
284
+ lines.push(`| ${s.name} | ${s.version} | ${resumir(s.description, 140)} | ${invocadores} |`);
285
+ }
286
+ lines.push('');
287
+
288
+ // Comandos — catálogo consultable (antes solo se contaban)
289
+ lines.push(`## Comandos /swl:* — Catálogo completo (${comandos.length})`);
290
+ lines.push('');
291
+ lines.push('| Comando | Descripción |');
292
+ lines.push('|---------|-------------|');
293
+ for (const c of [...comandos].sort((x, y) => x.name.localeCompare(y.name))) {
294
+ lines.push(`| /${c.name} | ${resumir(c.description, 180)} |`);
193
295
  }
194
296
  lines.push('');
195
297
 
@@ -243,6 +345,26 @@ function generarInventario() {
243
345
  }
244
346
  lines.push('');
245
347
 
348
+ // Schemas — superficie de contrato del sistema
349
+ lines.push(`## Schemas (${schemasList.length})`);
350
+ lines.push('');
351
+ lines.push('| Schema | Archivo |');
352
+ lines.push('|--------|---------|');
353
+ for (const s of schemasList) {
354
+ lines.push(`| ${s.replace('.schema.json', '').replace('.json', '')} | \`schemas/${s}\` |`);
355
+ }
356
+ lines.push('');
357
+
358
+ // Scripts lib — enumerados (antes solo se contaban)
359
+ lines.push(`## Librerías de scripts (scripts/lib/) — ${scriptsLibList.length}`);
360
+ lines.push('');
361
+ lines.push('| Librería | Archivo |');
362
+ lines.push('|----------|---------|');
363
+ for (const s of scriptsLibList) {
364
+ lines.push(`| ${s.replace(/\.(js|py)$/, '')} | \`scripts/lib/${s}\` |`);
365
+ }
366
+ lines.push('');
367
+
246
368
  return lines.join('\n');
247
369
  }
248
370
 
@@ -353,7 +475,9 @@ function generarSalud() {
353
475
  const lines = [];
354
476
  lines.push('# Reporte de Salud del Sistema SWL');
355
477
  lines.push('');
356
- lines.push(`**Generado**: ${new Date().toISOString().slice(0, 10)}`);
478
+ // Fecha LOCAL (no UTC): en horario de México la tarde ya es "mañana" en UTC
479
+ // y el reporte quedaba con fecha futura. 'sv' formatea como YYYY-MM-DD.
480
+ lines.push(`**Generado**: ${new Date().toLocaleDateString('sv')}`);
357
481
  lines.push(`**Versión del sistema**: ${pkg.version}`);
358
482
  lines.push('');
359
483
  lines.push('> Archivo generado automáticamente por `node scripts/generar-inventario.js --salud`.');
@@ -463,11 +587,12 @@ function generarLlmsTxt() {
463
587
  L.push('');
464
588
  L.push('## Componentes');
465
589
  L.push('');
466
- L.push(`- ${agentes} agentes especializados en \`agentes/\` (orquestación, implementación por stack, revisión, calidad, diseño)`);
467
- L.push(`- ${skills} habilidades cargables bajo demanda en \`habilidades/\` (conocimiento operacional con divulgación progresiva)`);
468
- L.push(`- ${comandos} comandos \`/swl:*\` en \`comandos/swl/\` (ciclo GSD, calidad, release, diagnóstico)`);
469
- L.push(`- ${reglasBase} reglas base + ${reglasLang} reglas por lenguaje en \`reglas/\` (políticas obligatorias por matcher)`);
470
- L.push(`- ${hooks} hooks en \`hooks/\` (telemetría, validación, seguridad; zero-deps, escrituras atómicas)`);
590
+ // llmstxt.org: cada entrada de sección debe ser un link markdown navegable.
591
+ L.push(`- [${agentes} agentes especializados](AGENTS.md): orquestación, implementación por stack, revisión, calidad, diseño fuente en \`agentes/\``);
592
+ L.push(`- [${skills} habilidades cargables bajo demanda](INVENTARIO.md): conocimiento operacional con divulgación progresiva — fuente en \`habilidades/\``);
593
+ L.push(`- [${comandos} comandos /swl:*](COMANDOS.md): ciclo GSD, calidad, release, diagnóstico — fuente en \`comandos/swl/\``);
594
+ L.push(`- [${reglasBase} reglas base + ${reglasLang} por lenguaje](INVENTARIO.md): políticas obligatorias por matcher — fuente en \`reglas/\``);
595
+ L.push(`- [${hooks} hooks](INVENTARIO.md): telemetría, validación, seguridad; zero-deps, escrituras atómicas — fuente en \`hooks/\``);
471
596
  L.push('');
472
597
  L.push('## Opcional');
473
598
  L.push('');
@@ -479,6 +604,83 @@ function generarLlmsTxt() {
479
604
  return L.join('\n');
480
605
  }
481
606
 
607
+ // ── MAPEO_SKILLS_AGENTES.md ───────────────────────────────────────────
608
+ // Antes se mantenía a mano y era el peor infractor de drift del sistema
609
+ // (auditoría 2026-07-08: mencionaba skills retiradas, mapeos falsos en 3/5
610
+ // agentes muestreados y totales stale, pese a ser insumo del gate de release).
611
+ // Se deriva por completo de los skillsInvocables del frontmatter de agentes.
612
+ function generarMapeo() {
613
+ const pkg = JSON.parse(fs.readFileSync(path.join(RAIZ, 'package.json'), 'utf8'));
614
+ const agentes = recolectarAgentes();
615
+ const skills = recolectarSkills();
616
+ const nombresSkill = new Set(skills.map(s => s.name));
617
+
618
+ const L = [];
619
+ L.push(`# MAPEO_SKILLS_AGENTES.md — v${pkg.version}`);
620
+ L.push('');
621
+ L.push('> Archivo generado automáticamente por `node scripts/generar-inventario.js`.');
622
+ L.push('> NO editar manualmente — se deriva de los `skillsInvocables` declarados');
623
+ L.push('> en el frontmatter de `agentes/*.md`. El mapa inverso (skill → agentes)');
624
+ L.push('> vive en `INVENTARIO.md § Habilidades — Catálogo completo`.');
625
+ L.push('');
626
+ L.push(`Total de agentes: ${agentes.length}`);
627
+ L.push(`Total de skills: ${skills.length}`);
628
+ L.push('');
629
+ L.push('## Agente → skills que declara invocables');
630
+ L.push('');
631
+ L.push('| Agente | # | skillsInvocables |');
632
+ L.push('|--------|---|------------------|');
633
+ // Skills de la colección oficial Anthropic (opt-in, instaladas en
634
+ // .claude/skills/ vía `swl-ses skills add anthropics/skills`) — referencias
635
+ // legítimas aunque no vivan en habilidades/. Ver MANUAL_USO § Anthropic Skills.
636
+ const SKILLS_ANTHROPIC = new Set([
637
+ 'pdf', 'docx', 'xlsx', 'pptx', 'frontend-design', 'web-artifacts-builder',
638
+ 'canvas-design', 'algorithmic-art', 'theme-factory', 'slack-gif-creator',
639
+ 'claude-api', 'mcp-builder', 'webapp-testing', 'doc-coauthoring',
640
+ 'internal-comms', 'brand-guidelines', 'skill-creator',
641
+ ]);
642
+ const externas = new Set();
643
+ const inexistentes = [];
644
+ for (const a of [...agentes].sort((x, y) => x.name.localeCompare(y.name))) {
645
+ for (const s of a.skillsInvocables) {
646
+ if (s === 'todos') continue; // centinela: el agente puede invocar cualquier skill
647
+ if (nombresSkill.has(s)) continue;
648
+ if (SKILLS_ANTHROPIC.has(s)) externas.add(s);
649
+ else inexistentes.push(`${a.name} → ${s}`);
650
+ }
651
+ const etiqueta = a.skillsInvocables.includes('todos')
652
+ ? 'todos (acceso a cualquier skill)'
653
+ : (a.skillsInvocables.join(', ') || '—');
654
+ L.push(`| ${a.name} | ${a.skillsInvocables.includes('todos') ? skills.length : a.skillsInvocables.length} | ${etiqueta} |`);
655
+ }
656
+ L.push('');
657
+ if (externas.size > 0) {
658
+ L.push(`## Skills externas referenciadas (colección Anthropic, opt-in) — ${externas.size}`);
659
+ L.push('');
660
+ L.push('No viven en `habilidades/`; se instalan con `swl-ses skills add anthropics/skills`.');
661
+ L.push('Si la colección no está instalada, el agente degrada sin fallar.');
662
+ L.push('');
663
+ L.push([...externas].sort().map(s => `\`${s}\``).join(', '));
664
+ L.push('');
665
+ }
666
+ if (inexistentes.length > 0) {
667
+ L.push('## ⚠ Referencias a skills inexistentes (corregir en el frontmatter del agente)');
668
+ L.push('');
669
+ for (const i of inexistentes) L.push(`- ${i}`);
670
+ L.push('');
671
+ }
672
+ const declaradas = new Set(agentes.flatMap(a => a.skillsInvocables));
673
+ const sinAgente = skills.filter(s => !declaradas.has(s.name));
674
+ L.push(`## Skills sin agente declarado (${sinAgente.length})`);
675
+ L.push('');
676
+ L.push('No es error: se invocan por descripción (matching nativo del runtime),');
677
+ L.push('desde comandos `/swl:*`, o son infraestructura del sistema.');
678
+ L.push('');
679
+ L.push(sinAgente.map(s => `\`${s.name}\``).join(', '));
680
+ L.push('');
681
+ return L.join('\n');
682
+ }
683
+
482
684
  // ── Ejecutar ──────────────────────────────────────────────────────────
483
685
 
484
686
  if (AMBOS || SOLO_INV) {
@@ -513,3 +715,14 @@ if (AMBOS || SOLO_LLMS) {
513
715
  console.log('llms.txt generado correctamente.');
514
716
  }
515
717
  }
718
+
719
+ if (AMBOS || args.has('--mapeo')) {
720
+ const mapeo = generarMapeo();
721
+ if (DRY) {
722
+ console.log('\n=== MAPEO_SKILLS_AGENTES.md (dry-run) ===\n');
723
+ console.log(mapeo.substring(0, 1500) + '\n...(truncado)');
724
+ } else {
725
+ atomicWriteSync(path.join(RAIZ, '.planning', 'MAPEO_SKILLS_AGENTES.md'), mapeo, 'utf8');
726
+ console.log('MAPEO_SKILLS_AGENTES.md generado correctamente.');
727
+ }
728
+ }
@@ -207,7 +207,7 @@ function renderResumen() {
207
207
  // ---------------------------------------------------------------------------
208
208
 
209
209
  function main() {
210
- const fecha = new Date().toISOString().slice(0, 10);
210
+ const fecha = new Date().toLocaleDateString('sv'); // local, no UTC (regla CLAUDE.md Code style)
211
211
  const contenido = `# Matriz de cobertura SWL × lenguaje
212
212
 
213
213
  > **Generado automáticamente** por \`scripts/generar-matriz-lenguajes.js\`.
@@ -857,7 +857,20 @@ async function install(opciones) {
857
857
  accion: resMerge.accion,
858
858
  });
859
859
  } else {
860
- // Comportamiento clásico: sobrescribir archivo completo (codex/gemini/copilot/opencode)
860
+ // Comportamiento clásico: sobrescribir archivo completo (codex/gemini/copilot/opencode).
861
+ // GUARD anti-clobber (bug 2026-07-08): si el destino YA existe y NO fue
862
+ // generado por SWL (sin el marcador "Generado por swl-ses"), es contenido
863
+ // del usuario o documentación canónica del proyecto — sobreescribirlo es
864
+ // pérdida de datos (caso real: install --target opencode pisó el AGENTS.md
865
+ // canónico del repo madre, 420 líneas). Preservar y avisar; el usuario
866
+ // puede borrar el archivo manualmente si quiere el generado.
867
+ const yaExiste = fs.existsSync(rutaInstrucciones);
868
+ const esGeneradoSwl = yaExiste
869
+ ? fs.readFileSync(rutaInstrucciones, 'utf-8').includes('Generado por swl-ses')
870
+ : false;
871
+ if (yaExiste && !esGeneradoSwl) {
872
+ console.log(` = ${instrucciones.rutaRelativa} NO modificado — existe contenido propio del proyecto (sin marcador "Generado por swl-ses"). Borra el archivo y reinstala si quieres el generado.`);
873
+ } else {
861
874
  if (!fs.existsSync(dirInstrucciones)) {
862
875
  fs.mkdirSync(dirInstrucciones, { recursive: true });
863
876
  }
@@ -868,6 +881,7 @@ async function install(opciones) {
868
881
  destino: rutaInstrucciones,
869
882
  tipo: 'instrucciones',
870
883
  });
884
+ } // cierre del guard anti-clobber
871
885
  }
872
886
  }
873
887
  } catch (err) {
@@ -44,9 +44,10 @@ const WORKFLOWS_DIR = '.github/workflows';
44
44
 
45
45
  /** Workflows que este módulo gestiona (identificados por nombre de archivo). */
46
46
  const WORKFLOWS_SWL = {
47
- security: 'swl-security.yml',
48
- ci: 'swl-ci.yml',
49
- release: 'release-please.yml',
47
+ security: 'swl-security.yml',
48
+ ci: 'swl-ci.yml',
49
+ release: 'release-please.yml',
50
+ devsecops: 'swl-devsecops.yml',
50
51
  };
51
52
 
52
53
  /** Secrets que requiere cada workflow. */
@@ -54,6 +55,9 @@ const SECRETS_REQUERIDOS = {
54
55
  'swl-security.yml': ['CLAUDE_API_KEY'],
55
56
  'swl-ci.yml': [],
56
57
  'release-please.yml': [],
58
+ // GITLEAKS_LICENSE solo es necesaria en repos de organización (gratuita
59
+ // para individuos) — no se lista como requerida para no bloquear el caso común.
60
+ 'swl-devsecops.yml': [],
57
61
  };
58
62
 
59
63
  // ---------------------------------------------------------------------------
@@ -121,6 +125,7 @@ function listarPlantillas() {
121
125
  * @param {boolean} [opciones.withSecurity=true] Instalar swl-security.yml
122
126
  * @param {boolean} [opciones.withCi=true] Instalar swl-ci.yml
123
127
  * @param {boolean} [opciones.withReleasePlease=false] Instalar release-please.yml
128
+ * @param {boolean} [opciones.withDevsecops=false] Instalar swl-devsecops.yml (gitleaks + audit de deps)
124
129
  * @param {boolean} [opciones.dryRun=false] Solo mostrar cambios, no copiar
125
130
  * @param {boolean} [opciones.force=false] Sobreescribir archivos existentes
126
131
  * @param {string} [opciones._proyectoPathOverride] Path del proyecto (tests)
@@ -130,6 +135,7 @@ function init({
130
135
  withSecurity = true,
131
136
  withCi = true,
132
137
  withReleasePlease = false,
138
+ withDevsecops = false,
133
139
  dryRun = false,
134
140
  force = false,
135
141
  _proyectoPathOverride,
@@ -141,6 +147,7 @@ function init({
141
147
  if (withSecurity) seleccionados.push(WORKFLOWS_SWL.security);
142
148
  if (withCi) seleccionados.push(WORKFLOWS_SWL.ci);
143
149
  if (withReleasePlease) seleccionados.push(WORKFLOWS_SWL.release);
150
+ if (withDevsecops) seleccionados.push(WORKFLOWS_SWL.devsecops);
144
151
 
145
152
  const copiados = [];
146
153
  const existentes = [];
@@ -378,16 +378,25 @@ function detectarInstalacionesDuales(runtimeId, opciones = {}) {
378
378
  *
379
379
  * @returns {string[]} Lista de runtime IDs ordenada de "más usado" a "menos usado".
380
380
  */
381
- function listarRuntimesInstalables() {
382
- return [
381
+ function listarRuntimesInstalables(opciones = {}) {
382
+ const existeDir = opciones.existeDir || fs.existsSync;
383
+ const base = [
383
384
  'claude', // Soporte completo, primer ciudadano
384
385
  'cursor', // Soporte parcial — reglas + MCP
385
386
  'codex', // Soporte parcial — AGENTS.md + MCP (v1.5.0+)
386
387
  'opencode', // Soporte completo
387
388
  'gemini', // Soporte completo
388
389
  'copilot', // Soporte parcial — agentes consolidados
389
- // 'openclaude' intencionalmente excluido — comparte dir con claude
390
390
  ];
391
+ // OpenClaude comparte el dir de PROYECTO (.claude/) con Claude Code, así
392
+ // que NO se bootstrapea por default (evita duplicados para quien no lo
393
+ // usa). Pero su GLOBAL (~/.openclaude) es un dir propio: si YA existe (el
394
+ // usuario lo instaló alguna vez), --all-runtimes DEBE actualizarlo —
395
+ // excluirlo incondicionalmente dejaba un drift de versión permanente que
396
+ // ningún reinstall masivo cerraba (caso real doctor 2026-07-03: todo el
397
+ // parque en 2.4.2 y ~/.openclaude clavado en 2.4.1 pese a --all-runtimes).
398
+ if (existeDir(RUNTIMES.openclaude.global)) base.splice(1, 0, 'openclaude');
399
+ return base;
391
400
  }
392
401
 
393
402
  module.exports = {
@@ -38,7 +38,9 @@ function nowIso() {
38
38
  }
39
39
 
40
40
  function todayDate() {
41
- return nowIso().slice(0, 10);
41
+ // Fecha LOCAL, no UTC: desde México la tarde ya es "mañana" en UTC y el id
42
+ // del diary quedaba fechado en el futuro (regla CLAUDE.md § Code style).
43
+ return new Date().toLocaleDateString('sv');
42
44
  }
43
45
 
44
46
  function shortHash(input) {
@@ -367,7 +367,7 @@ function generarLineaTemporal({
367
367
  for (let i = dias - 1; i >= 0; i--) {
368
368
  const inicioDia = ahora - (i + 1) * 24 * 3600 * 1000;
369
369
  const finDia = ahora - i * 24 * 3600 * 1000;
370
- const fecha = new Date(inicioDia).toISOString().slice(0, 10);
370
+ const fecha = new Date(inicioDia).toLocaleDateString('sv');
371
371
 
372
372
  const del_dia = eventos.filter(e => {
373
373
  const ms = aMs(e.timestamp ?? e.ts ?? e.inicio ?? e.created_at);