@saulwade/swl-ses 2.6.0 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/CLAUDE.md +197 -197
  2. package/README.md +600 -600
  3. package/agentes/_intent-spec.md +73 -73
  4. package/agentes/_propose-step.md +90 -90
  5. package/agentes/accesibilidad-wcag-swl.md +690 -690
  6. package/agentes/arquitecto-swl.md +267 -267
  7. package/agentes/auto-evolucion-swl.md +932 -932
  8. package/agentes/backend-csharp-swl.md +420 -420
  9. package/agentes/backend-go-swl.md +390 -390
  10. package/agentes/backend-java-swl.md +281 -281
  11. package/agentes/backend-rust-swl.md +364 -364
  12. package/agentes/backend-workers-swl.md +482 -482
  13. package/agentes/cloud-infra-swl.md +509 -509
  14. package/agentes/consolidador-swl.md +541 -541
  15. package/agentes/depurador-swl.md +352 -352
  16. package/agentes/devops-ci-swl.md +400 -400
  17. package/agentes/disenador-ui-swl.md +569 -569
  18. package/agentes/documentador-swl.md +345 -345
  19. package/agentes/frontend-angular-swl.md +621 -621
  20. package/agentes/frontend-css-swl.md +716 -716
  21. package/agentes/frontend-react-swl.md +692 -692
  22. package/agentes/frontend-swl.md +496 -496
  23. package/agentes/frontend-tailwind-swl.md +826 -826
  24. package/agentes/investigador-swl.md +432 -432
  25. package/agentes/investigador-ux-swl.md +505 -505
  26. package/agentes/migrador-swl.md +442 -442
  27. package/agentes/mobile-android-swl.md +511 -511
  28. package/agentes/mobile-cross-swl.md +541 -541
  29. package/agentes/mobile-ios-swl.md +502 -502
  30. package/agentes/mobile-testing-swl.md +302 -302
  31. package/agentes/nemesis-auditor-swl.md +285 -285
  32. package/agentes/observabilidad-swl.md +438 -438
  33. package/agentes/pagos-swl.md +310 -310
  34. package/agentes/perfilador-usuario-swl.md +321 -321
  35. package/agentes/planificador-swl.md +399 -399
  36. package/agentes/producto-prd-swl.md +589 -589
  37. package/agentes/red-team-swl.md +218 -218
  38. package/agentes/release-manager-swl.md +590 -590
  39. package/agentes/rendimiento-swl.md +713 -713
  40. package/agentes/revisor-angular-swl.md +278 -278
  41. package/agentes/revisor-csharp-swl.md +264 -264
  42. package/agentes/revisor-go-swl.md +259 -259
  43. package/agentes/revisor-java-swl.md +257 -257
  44. package/agentes/revisor-kotlin-swl.md +273 -273
  45. package/agentes/revisor-nextjs-swl.md +281 -281
  46. package/agentes/revisor-php-swl.md +271 -271
  47. package/agentes/revisor-react-swl.md +278 -278
  48. package/agentes/revisor-rust-swl.md +346 -346
  49. package/agentes/revisor-seguridad-swl.md +399 -399
  50. package/agentes/revisor-swift-swl.md +268 -268
  51. package/agentes/revisor-typescript-swl.md +346 -346
  52. package/agentes/tdd-qa-swl.md +393 -393
  53. package/comandos/swl/actualizar.md +174 -174
  54. package/comandos/swl/adoptar-proyecto.md +265 -265
  55. package/comandos/swl/aprender.md +836 -836
  56. package/comandos/swl/aprobar-plan.md +146 -146
  57. package/comandos/swl/auditar-deps.md +134 -134
  58. package/comandos/swl/autoresearch.md +264 -264
  59. package/comandos/swl/ayuda.md +224 -224
  60. package/comandos/swl/brainstorm.md +51 -51
  61. package/comandos/swl/briefing.md +119 -119
  62. package/comandos/swl/checkpoint.md +325 -325
  63. package/comandos/swl/claudemd.md +234 -234
  64. package/comandos/swl/compactar.md +310 -310
  65. package/comandos/swl/configurar-ci.md +235 -235
  66. package/comandos/swl/contexto.md +110 -110
  67. package/comandos/swl/contribuir.md +233 -233
  68. package/comandos/swl/crear-skill.md +292 -292
  69. package/comandos/swl/cron.md +194 -194
  70. package/comandos/swl/discutir-fase.md +169 -169
  71. package/comandos/swl/ejecutar-fase.md +233 -233
  72. package/comandos/swl/evaluar-skill.md +520 -520
  73. package/comandos/swl/evolucion-continua.md +73 -73
  74. package/comandos/swl/evolucionar.md +267 -267
  75. package/comandos/swl/exportar-vault.md +583 -583
  76. package/comandos/swl/fix.md +118 -118
  77. package/comandos/swl/gateway.md +158 -158
  78. package/comandos/swl/inbox.md +116 -116
  79. package/comandos/swl/instalar.md +220 -220
  80. package/comandos/swl/instintos.md +86 -86
  81. package/comandos/swl/mapear-codebase.md +312 -312
  82. package/comandos/swl/mcp-status.md +175 -175
  83. package/comandos/swl/modelo.md +100 -100
  84. package/comandos/swl/nemesis.md +433 -433
  85. package/comandos/swl/notificaciones.md +299 -299
  86. package/comandos/swl/nuevo-proyecto.md +251 -251
  87. package/comandos/swl/planear-fase.md +263 -263
  88. package/comandos/swl/plugins.md +256 -256
  89. package/comandos/swl/predecir.md +169 -169
  90. package/comandos/swl/reflect-skills.md +125 -125
  91. package/comandos/swl/release.md +450 -450
  92. package/comandos/swl/revisar-impacto.md +201 -201
  93. package/comandos/swl/revisar.md +330 -330
  94. package/comandos/swl/seguridad.md +189 -189
  95. package/comandos/swl/sesiones.md +200 -200
  96. package/comandos/swl/skill-search.md +113 -113
  97. package/comandos/swl/status.md +345 -345
  98. package/comandos/swl/verificar.md +817 -817
  99. package/comandos/swl/wiki.md +620 -620
  100. package/gateway/cron/jobs.example.json +12 -12
  101. package/habilidades/auto-evolucion-protocolo/SKILL.md +294 -294
  102. package/habilidades/backend-async-postgres-testing/SKILL.md +2 -1
  103. package/habilidades/changelog-generator/SKILL.md +174 -174
  104. package/habilidades/compactacion-contexto/SKILL.md +2 -1
  105. package/habilidades/contenedores-docker/SKILL.md +4 -2
  106. package/habilidades/doubt-driven-review/SKILL.md +207 -207
  107. package/habilidades/drift-detection/SKILL.md +1 -1
  108. package/habilidades/ejecutar-task-iterativo/SKILL.md +278 -278
  109. package/habilidades/extractor-de-aprendizajes/SKILL.md +8 -2
  110. package/habilidades/git-worktrees-paralelo/SKILL.md +19 -1
  111. package/habilidades/harness-claude-code/SKILL.md +314 -314
  112. package/habilidades/instalar-sistema/SKILL.md +227 -227
  113. package/habilidades/planear-fase/SKILL.md +358 -358
  114. package/habilidades/prevencion-sobreingenieria/recursos/soluciones-nativas.md +166 -166
  115. package/habilidades/prevencion-sobreingenieria/recursos/variables-residuales-post-refactor.md +85 -85
  116. package/habilidades/proceso-ingenieria-requerimientos/SKILL.md +147 -147
  117. package/habilidades/release-semver/SKILL.md +2 -2
  118. package/habilidades/tdd-workflow/SKILL.md +749 -749
  119. package/hooks/agente-lifecycle.js +1 -1
  120. package/hooks/audit-trail.js +1 -1
  121. package/hooks/auto-consolidacion.js +1 -1
  122. package/hooks/captura-acciones-post.js +1 -1
  123. package/hooks/captura-acciones-session.js +1 -1
  124. package/hooks/captura-feedback-usuario.js +1 -1
  125. package/hooks/contexto-iteracion.js +1 -1
  126. package/hooks/contexto-subagente.js +68 -68
  127. package/hooks/degradacion-instintos.js +1 -1
  128. package/hooks/grafo-contexto.js +1 -1
  129. package/hooks/guardrail-modelo.js +1 -1
  130. package/hooks/inbox-aviso.js +1 -1
  131. package/hooks/inyeccion-contexto.js +1 -1
  132. package/hooks/lib/agent-matcher.js +1 -1
  133. package/hooks/lib/agent-routing.js +1 -1
  134. package/hooks/lib/captura-acciones.js +1 -1
  135. package/hooks/lib/etapa-metricas.js +1 -1
  136. package/hooks/lib/evolution-tracker.js +1 -1
  137. package/hooks/lib/gateway-notify.js +193 -193
  138. package/hooks/lib/mcp-health.js +1 -1
  139. package/hooks/lib/notificacion-formato.js +58 -0
  140. package/hooks/lib/nudge-tracker.js +1 -1
  141. package/hooks/lib/otlp-exporter.js +1 -1
  142. package/hooks/lib/propose-step.js +1 -1
  143. package/hooks/lib/raiz-proyecto.js +127 -102
  144. package/hooks/lib/run-log.js +1 -1
  145. package/hooks/lib/singleton-guard.js +20 -13
  146. package/hooks/lib/telegram-cliente.js +11 -3
  147. package/hooks/notificacion-telegram.js +13 -3
  148. package/hooks/preservar-estado-pre-compact.js +1 -1
  149. package/hooks/registro-turnos.js +1 -1
  150. package/hooks/resumen-sesion.js +1 -1
  151. package/hooks/risk-scoring.js +1 -1
  152. package/hooks/session-briefing.js +1 -1
  153. package/hooks/spec-gate.js +1 -1
  154. package/hooks/sugerir-regenerar-inventario.js +1 -1
  155. package/hooks/tdd-gate.js +1 -1
  156. package/hooks/telemetria-agentes.js +1 -1
  157. package/hooks/telemetria-skill-routing.js +1 -1
  158. package/hooks/tracking-costos.js +1 -1
  159. package/hooks/validar-formato-post-subagente.js +1 -1
  160. package/hooks/validar-intent-spec.js +1 -1
  161. package/hooks/validar-planning-paths.js +1 -1
  162. package/llms.txt +29 -29
  163. package/manifiestos/canonical-hashes.json +5588 -5257
  164. package/manifiestos/hooks-config.json +469 -469
  165. package/manifiestos/invariantes-criticos.json +30 -30
  166. package/manifiestos/modulos.json +1429 -1428
  167. package/manifiestos/skills-lock.json +1275 -1275
  168. package/package.json +94 -94
  169. package/plugin.json +369 -369
  170. package/scripts/auditar-clases-conocidas.js +134 -134
  171. package/scripts/bootstrap-instintos.js +85 -14
  172. package/scripts/canario-hooks.js +166 -166
  173. package/scripts/cli/autonomia.js +23 -23
  174. package/scripts/cli/benchmark-memoria.js +37 -37
  175. package/scripts/cli/ciclo-autonomo.js +73 -73
  176. package/scripts/cli/ciclo-fase-b.js +102 -102
  177. package/scripts/cli/guardrail-metrics.js +39 -39
  178. package/scripts/cli/memoria-search.js +69 -69
  179. package/scripts/cli/nudge-accionar.js +39 -39
  180. package/scripts/cli/run-eval.js +38 -38
  181. package/scripts/doctor.js +26 -3
  182. package/scripts/evidencia-valor.js +101 -101
  183. package/scripts/field-report.js +16 -16
  184. package/scripts/instalador.js +13 -0
  185. package/scripts/lib/activar-hooks-proyecto.js +116 -116
  186. package/scripts/lib/ciclo-autonomo/candidatos.js +174 -174
  187. package/scripts/lib/ciclo-autonomo/config.js +165 -165
  188. package/scripts/lib/ciclo-autonomo/drenador-feedback.js +174 -174
  189. package/scripts/lib/ciclo-autonomo/fallback.js +77 -77
  190. package/scripts/lib/ciclo-autonomo/guard-convivencia.js +139 -139
  191. package/scripts/lib/ciclo-autonomo/higiene-nudges.js +112 -112
  192. package/scripts/lib/ciclo-autonomo/index.js +301 -301
  193. package/scripts/lib/ciclo-autonomo/lock.js +124 -124
  194. package/scripts/lib/ciclo-autonomo/presupuesto.js +122 -122
  195. package/scripts/lib/ciclo-autonomo/puente-degradacion.js +240 -240
  196. package/scripts/lib/ciclo-autonomo/runner-fase-b.js +248 -248
  197. package/scripts/lib/ciclo-autonomo/writer-instintos.js +190 -190
  198. package/scripts/lib/ciclo-autonomo/yaml-instintos.js +535 -535
  199. package/scripts/lib/evidencia-valor.js +228 -228
  200. package/scripts/lib/expandir-targets.js +71 -71
  201. package/scripts/lib/limpiar-basura-global.js +161 -0
  202. package/scripts/lib/toml-merge.js +204 -204
  203. package/scripts/mcp-server/auth.js +105 -105
  204. package/scripts/mcp-server/cache.js +106 -106
  205. package/scripts/tui/pantallas/install-wizard.js +403 -403
  206. package/instintos/.backups/perfil-usuario.yaml.2026-07-10-165128.bak +0 -53
  207. package/instintos/.backups/proyecto.yaml.2026-07-10-165128.bak +0 -372
@@ -1,102 +1,127 @@
1
- 'use strict';
2
-
3
- /**
4
- * raiz-proyecto.js — Resolución de la raíz del proyecto para hooks que
5
- * escriben telemetría/memoria en `.planning/`.
6
- *
7
- * El CWD de arranque de una sesión NO es la frontera del proyecto: en un
8
- * monorepo con sesiones abiertas en subdirectorios (`backend/`, `frontend/`),
9
- * anclar `.planning/` a `process.cwd()` fragmenta la memoria del proyecto en
10
- * árboles huérfanos (DT-HOOKS-RAIZ-GIT; misma clase que el fix de
11
- * proteccion-rutas). La frontera correcta es la raíz del repositorio git,
12
- * con una excepción: un sub-proyecto anidado que ya tiene su propio
13
- * `.planning/` REAL (con marcadores de proyecto) conserva su árbol local.
14
- *
15
- * Reglas de resolución (en orden):
16
- * 1. Ascendiendo desde `desde`, el primer directorio cuyo `.planning/`
17
- * contiene un marcador de proyecto (PROYECTO.md, HOJA-RUTA.md,
18
- * ESTADO.md) gana — respeta sub-proyectos legítimos en monorepos.
19
- * Un `.planning/` sin marcadores (solo telemetría huérfana de hooks)
20
- * NO cuenta: eso es el síntoma del bug, no un proyecto.
21
- * 2. Si ningún nivel tiene `.planning/` real, gana la raíz del repo git
22
- * (`.git` como directorio o como archivo — worktrees).
23
- * 3. Sin `.git` alcanzable: fallback al propio `desde`.
24
- *
25
- * La ascensión se detiene en el HOME del usuario (sin incluirlo) cuando se
26
- * llega ahí subiendo: un dotfiles-repo en `~` no debe capturar la memoria
27
- * de un proyecto sin git que viva debajo.
28
- *
29
- * Zero-deps (regla de hooks/lib/). Consumidor inicial:
30
- * hooks/extraccion-aprendizajes.js; la flota completa migró el 2026-07-10
31
- * (cierre de DT-HOOKS-RAIZ-GIT).
32
- *
33
- * LÍMITE DE USO (hallazgo revisor-seguridad 2026-07-10): esta ancla es SOLO
34
- * para DATOS (.planning/, instintos/, lecturas de config del proyecto).
35
- * NUNCA resolver con ella EJECUTABLES ni módulos a require()/spawn(): la
36
- * raíz resuelta pertenece al árbol del proyecto, y un repositorio no
37
- * confiable puede plantar el script (marcador .planning/PROYECTO.md +
38
- * scripts/x.js hostil = ejecución de código al disparar el hook). Los
39
- * ejecutables de la distribución SWL se resuelven desde __dirname
40
- * (ubicación instalada del hook). Test guardián:
41
- * tests/hooks/ejecutables-desde-instalacion.test.js.
42
- */
43
-
44
- const fs = require('fs');
45
- const path = require('path');
46
- const os = require('os');
47
-
48
- const MARCADORES_PROYECTO = ['PROYECTO.md', 'HOJA-RUTA.md', 'ESTADO.md'];
49
- const MAX_NIVELES = 40;
50
-
51
- /**
52
- * ¿El directorio tiene un `.planning/` de proyecto real (no huérfano)?
53
- * @param {string} dir
54
- * @returns {boolean}
55
- */
56
- function tienePlanningReal(dir) {
57
- try {
58
- return MARCADORES_PROYECTO.some(m =>
59
- fs.existsSync(path.join(dir, '.planning', m))
60
- );
61
- } catch (_) {
62
- return false;
63
- }
64
- }
65
-
66
- /**
67
- * Resuelve la raíz del proyecto a la que deben anclarse las escrituras
68
- * de `.planning/`.
69
- *
70
- * @param {string} [desde] - Directorio de partida (default: process.cwd()).
71
- * @returns {string} Ruta absoluta de la raíz del proyecto.
72
- */
73
- function raizProyecto(desde) {
74
- const inicio = path.resolve(desde || process.cwd());
75
- let actual = inicio;
76
- let homedir = null;
77
- try {
78
- homedir = path.resolve(os.homedir());
79
- } catch (_) { /* sin home resolvible — seguir sin el guard */ }
80
-
81
- for (let i = 0; i < MAX_NIVELES; i++) {
82
- // Frontera del HOME: si llegamos aquí ASCENDIENDO, no seguir — un
83
- // dotfiles-repo en ~ no es la raíz de un proyecto que vive debajo.
84
- if (homedir && actual === homedir && i > 0) break;
85
-
86
- if (tienePlanningReal(actual)) return actual;
87
-
88
- try {
89
- if (fs.existsSync(path.join(actual, '.git'))) return actual;
90
- } catch (_) {
91
- break;
92
- }
93
-
94
- const padre = path.dirname(actual);
95
- if (padre === actual) break; // raíz del filesystem
96
- actual = padre;
97
- }
98
-
99
- return inicio;
100
- }
101
-
102
- module.exports = { raizProyecto, tienePlanningReal };
1
+ 'use strict';
2
+
3
+ /**
4
+ * raiz-proyecto.js — Resolución de la raíz del proyecto para hooks que
5
+ * escriben telemetría/memoria en `.planning/`.
6
+ *
7
+ * El CWD de arranque de una sesión NO es la frontera del proyecto: en un
8
+ * monorepo con sesiones abiertas en subdirectorios (`backend/`, `frontend/`),
9
+ * anclar `.planning/` a `process.cwd()` fragmenta la memoria del proyecto en
10
+ * árboles huérfanos (DT-HOOKS-RAIZ-GIT; misma clase que el fix de
11
+ * proteccion-rutas). La frontera correcta es la raíz del repositorio git,
12
+ * con una excepción: un sub-proyecto anidado que ya tiene su propio
13
+ * `.planning/` REAL (con marcadores de proyecto) conserva su árbol local.
14
+ *
15
+ * Reglas de resolución (en orden):
16
+ * 1. Ascendiendo desde `desde`, el primer directorio cuyo `.planning/`
17
+ * contiene un marcador de proyecto (PROYECTO.md, HOJA-RUTA.md,
18
+ * ESTADO.md) gana — respeta sub-proyectos legítimos en monorepos.
19
+ * Un `.planning/` sin marcadores (solo telemetría huérfana de hooks)
20
+ * NO cuenta: eso es el síntoma del bug, no un proyecto.
21
+ * 2. Si ningún nivel tiene `.planning/` real, gana la raíz del repo git
22
+ * (`.git` como directorio o como archivo — worktrees).
23
+ * 3. Sin `.git` alcanzable: fallback al propio `desde`.
24
+ *
25
+ * La ascensión se detiene en el HOME del usuario (sin incluirlo) cuando se
26
+ * llega ahí subiendo: un dotfiles-repo en `~` o un `~/.planning` sembrado
27
+ * por `swl-ses doctor` en una instalación global — no debe capturar la
28
+ * memoria de un proyecto sin git que viva debajo. La comparación con el HOME
29
+ * se hace en forma CANÓNICA (realpath): en Windows `os.tmpdir()` puede
30
+ * devolver el nombre corto 8.3 (`C:\Users\SAUL~1.WAD`) mientras `os.homedir()`
31
+ * devuelve el largo (`C:\Users\saul.wade`); sin canonicalizar, la frontera no
32
+ * dispararía y capturaría `~/.planning` o `~/.git` (rompía 38 tests locales
33
+ * en Windows, verde en CI Linux porque `/tmp` no cuelga de `$HOME`).
34
+ *
35
+ * Zero-deps (regla de hooks/lib/). Consumidor inicial:
36
+ * hooks/extraccion-aprendizajes.js; la flota completa migró el 2026-07-10
37
+ * (cierre de DT-HOOKS-RAIZ-GIT).
38
+ *
39
+ * LÍMITE DE USO (hallazgo revisor-seguridad 2026-07-10): esta ancla es SOLO
40
+ * para DATOS (.planning/, instintos/, lecturas de config del proyecto).
41
+ * NUNCA resolver con ella EJECUTABLES ni módulos a require()/spawn(): la
42
+ * raíz resuelta pertenece al árbol del proyecto, y un repositorio no
43
+ * confiable puede plantar el script (marcador .planning/PROYECTO.md +
44
+ * scripts/x.js hostil = ejecución de código al disparar el hook). Los
45
+ * ejecutables de la distribución SWL se resuelven desde __dirname
46
+ * (ubicación instalada del hook). Test guardián:
47
+ * tests/hooks/ejecutables-desde-instalacion.test.js.
48
+ */
49
+
50
+ const fs = require('fs');
51
+ const path = require('path');
52
+ const os = require('os');
53
+
54
+ const MARCADORES_PROYECTO = ['PROYECTO.md', 'HOJA-RUTA.md', 'ESTADO.md'];
55
+ const MAX_NIVELES = 40;
56
+
57
+ /**
58
+ * ¿El directorio tiene un `.planning/` de proyecto real (no huérfano)?
59
+ * @param {string} dir
60
+ * @returns {boolean}
61
+ */
62
+ function tienePlanningReal(dir) {
63
+ try {
64
+ return MARCADORES_PROYECTO.some(m =>
65
+ fs.existsSync(path.join(dir, '.planning', m))
66
+ );
67
+ } catch (_) {
68
+ return false;
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Canonicaliza un path resolviendo enlaces simbólicos y el nombre corto 8.3
74
+ * de Windows (`C:\Users\SAUL~1.WAD` `C:\Users\saul.wade`). Solo se usa para
75
+ * la comparación de la frontera del HOME: el valor de RETORNO de raizProyecto
76
+ * conserva la forma del argumento recibido, no la canónica. Si el path no
77
+ * existe (no se puede resolver), cae a `path.resolve`.
78
+ * @param {string} p
79
+ * @returns {string}
80
+ */
81
+ function canonicalizar(p) {
82
+ try {
83
+ return fs.realpathSync.native(p);
84
+ } catch (_) {
85
+ return path.resolve(p);
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Resuelve la raíz del proyecto a la que deben anclarse las escrituras
91
+ * de `.planning/`.
92
+ *
93
+ * @param {string} [desde] - Directorio de partida (default: process.cwd()).
94
+ * @returns {string} Ruta absoluta de la raíz del proyecto.
95
+ */
96
+ function raizProyecto(desde) {
97
+ const inicio = path.resolve(desde || process.cwd());
98
+ let actual = inicio;
99
+ let homedirCanon = null;
100
+ try {
101
+ homedirCanon = canonicalizar(path.resolve(os.homedir()));
102
+ } catch (_) { /* sin home resolvible — seguir sin el guard */ }
103
+
104
+ for (let i = 0; i < MAX_NIVELES; i++) {
105
+ // Frontera del HOME: si llegamos aquí ASCENDIENDO, no seguir — un
106
+ // dotfiles-repo (o un ~/.planning sembrado por doctor) en ~ no es la raíz
107
+ // de un proyecto que vive debajo. Comparación canónica: iguala el nombre
108
+ // corto 8.3 heredado de os.tmpdir() con el HOME largo de os.homedir().
109
+ if (homedirCanon && i > 0 && canonicalizar(actual) === homedirCanon) break;
110
+
111
+ if (tienePlanningReal(actual)) return actual;
112
+
113
+ try {
114
+ if (fs.existsSync(path.join(actual, '.git'))) return actual;
115
+ } catch (_) {
116
+ break;
117
+ }
118
+
119
+ const padre = path.dirname(actual);
120
+ if (padre === actual) break; // raíz del filesystem
121
+ actual = padre;
122
+ }
123
+
124
+ return inicio;
125
+ }
126
+
127
+ module.exports = { raizProyecto, tienePlanningReal, canonicalizar };
@@ -38,7 +38,7 @@
38
38
  */
39
39
 
40
40
  const fs = require('fs');
41
- const { raizProyecto } = require('./raiz-proyecto');
41
+ const { raizProyecto } = require('./raiz-proyecto');
42
42
  const path = require('path');
43
43
  const readline = require('readline');
44
44
  const os = require('os');
@@ -32,6 +32,7 @@ const DIR_LOCKS = path.join(raizProyecto(process.cwd()), '.planning', 'locks');
32
32
  const STALE_MS_DEFAULT = 5 * 60 * 1000; // 5 minutos
33
33
  const locksAdquiridos = new Set();
34
34
  let handlersRegistrados = false;
35
+ let seqTmp = 0; // secuencia para nombres de temp únicos por proceso
35
36
 
36
37
  function rutaLock(nombre) {
37
38
  return path.join(DIR_LOCKS, `${nombre}.lock`);
@@ -60,28 +61,34 @@ function pidVivo(pid) {
60
61
  }
61
62
 
62
63
  /**
63
- * Escribe el lockfile con exclusión atómica (O_EXCL vía flag 'wx'): falla con
64
- * EEXIST si el archivo ya existe, sin ventana check-then-write. Es lo que
65
- * garantiza la exclusión mutua real entre DOS procesos simultáneos
66
- * fs.existsSync + fs.writeFileSync NO la garantiza (ambos ven no-existe y
67
- * ambos escriben; hallazgo re-auditoría nemesis iter-2).
64
+ * Escribe el lockfile con exclusión atómica y SIN ventana de archivo vacío:
65
+ * escribe el contenido completo en un temp único y lo ENLAZA (link(2)) a `ruta`.
66
+ * link es atómico y falla con EEXIST si `ruta` ya existe misma garantía de
67
+ * exclusión mutua que O_EXCL pero, a diferencia de open('wx')+write, `ruta`
68
+ * NUNCA aparece vacío: el inodo enlazado ya trae el JSON completo.
69
+ *
70
+ * Por qué NO open('wx')+write (hallazgo 2026-07-15): esa forma crea el archivo
71
+ * vacío y ESCRIBE después. Un contendiente en el path de takeover que lo leyera
72
+ * en esa ventana obtenía '' → JSON.parse lanza → leerLock=null →
73
+ * contenidoTomable(null)=true → hacía takeover de un lock YA ganado → DOS
74
+ * holders. Reproducido a ~10% con 16 procesos simultáneos sobre el path stale;
75
+ * antes no se veía porque el test no forzaba simultaneidad real.
76
+ * (Precedente: fs.existsSync+writeFileSync tampoco sirve — nemesis iter-2.)
68
77
  *
69
78
  * @param {string} ruta
70
79
  * @param {string} nombre
71
80
  * @returns {boolean} true si este proceso creó el lock.
72
81
  */
73
82
  function crearLockExclusivo(ruta, nombre) {
83
+ const tmp = `${ruta}.${process.pid}.${++seqTmp}.tmp`;
74
84
  try {
75
- const fd = fs.openSync(ruta, 'wx'); // O_CREAT | O_EXCL | O_WRONLY
76
- try {
77
- fs.writeFileSync(fd, JSON.stringify({ pid: process.pid, ts: new Date().toISOString(), nombre }));
78
- } finally {
79
- fs.closeSync(fd);
80
- }
85
+ fs.writeFileSync(tmp, JSON.stringify({ pid: process.pid, ts: new Date().toISOString(), nombre }));
86
+ fs.linkSync(tmp, ruta); // atómico; lanza EEXIST si `ruta` ya existe
81
87
  return true;
82
88
  } catch (err) {
83
- if (err.code === 'EEXIST') return false;
84
- return false;
89
+ return false; // EEXIST (otro ganó) u otro error de I/O → no se adquirió
90
+ } finally {
91
+ try { fs.unlinkSync(tmp); } catch (_) { /* temp ya removido o nunca creado */ }
85
92
  }
86
93
  }
87
94
 
@@ -130,15 +130,23 @@ function _esperar(ms) {
130
130
  *
131
131
  * @param {string} token - Token del bot (`123:ABC...`).
132
132
  * @param {string} chatId - ID numérico del chat destino.
133
- * @param {string} texto - Texto del mensaje. Se escapa y trunca automáticamente.
133
+ * @param {string} texto - Texto del mensaje. Por default se escapa (el llamador
134
+ * pasa texto PLANO). Si el llamador ya construyó HTML válido (tags + contenido
135
+ * escapado), pasar `opts.escaparContenido = false` para NO re-escapar — de lo
136
+ * contrario los `<b>` se convierten en `&lt;b&gt;` y Telegram los muestra
137
+ * literales (doble escape; bug 2026-07-15).
134
138
  * @param {object} [opts] - Campos adicionales para el body de sendMessage.
139
+ * @param {boolean} [opts.escaparContenido=true] - Escapar HTML del texto (para
140
+ * llamadores que pasan texto plano). false si el texto YA es HTML listo.
135
141
  * @returns {Promise<{ok: boolean, statusCode: number, error?: string}>}
136
142
  */
137
143
  async function enviarMensaje(token, chatId, texto, opts = {}) {
138
- const textoPrepado = truncar(escaparHtml(texto), LIMITE_CHARS);
144
+ const { escaparContenido = true, ...payloadOpts } = opts;
145
+ const base = escaparContenido ? escaparHtml(texto) : String(texto);
146
+ const textoPrepado = truncar(base, LIMITE_CHARS);
139
147
 
140
148
  for (let intento = 0; intento < REINTENTOS_MAX; intento++) {
141
- const resultado = await _intentarEnvio(token, chatId, textoPrepado, opts);
149
+ const resultado = await _intentarEnvio(token, chatId, textoPrepado, payloadOpts);
142
150
 
143
151
  if (resultado.ok) {
144
152
  return { ok: true, statusCode: resultado.statusCode };
@@ -23,6 +23,7 @@ const path = require('node:path');
23
23
 
24
24
  const { cargarConfig, estaMuted } = require('./lib/telegram-config');
25
25
  const { enviarMensaje, escaparHtml } = require('./lib/telegram-cliente');
26
+ const { resumirParaNotificacion } = require('./lib/notificacion-formato');
26
27
 
27
28
  // ----------------------------------------------------------------
28
29
  // Utilidades de logging
@@ -121,7 +122,7 @@ function construirCampos(nombreEvento, nombreProyecto, transcriptPath, evt) {
121
122
  if (nombreEvento === 'Stop' || nombreEvento === 'SubagentStop') {
122
123
  tituloBase = nombreEvento === 'SubagentStop' ? 'Subagente terminó' : 'Claude terminó';
123
124
  subtitulo = 'Turno completado';
124
- cuerpo = leerUltimoMensajeAsistente(transcriptPath) || 'Claude terminó el turno.';
125
+ cuerpo = resumirParaNotificacion(leerUltimoMensajeAsistente(transcriptPath)) || 'Claude terminó el turno.';
125
126
 
126
127
  } else if (nombreEvento === 'Notification') {
127
128
  tituloBase = 'Claude espera respuesta';
@@ -262,14 +263,23 @@ async function run() {
262
263
  `<b>Estado:</b> ${escaparHtml(subtitulo)}\n\n` +
263
264
  `${escaparHtml(cuerpo)}`;
264
265
 
265
- // Enviar notificación Telegram
266
+ // Enviar notificación Telegram. escaparContenido:false porque `texto` YA es
267
+ // HTML listo (tags <b> + contenido escapado arriba); re-escaparlo mostraría
268
+ // los tags literales en Telegram (doble escape, bug 2026-07-15).
266
269
  const resultado = await enviarMensaje(
267
270
  config.TELEGRAM_BOT_TOKEN,
268
271
  config.TELEGRAM_CHAT_ID,
269
- texto
272
+ texto,
273
+ { escaparContenido: false }
270
274
  );
271
275
 
272
276
  log('telegram resultado', resultado.ok ? 'ok' : resultado.error);
273
277
  }
274
278
 
279
+ // Sin gate de entrypoint: los hooks se invocan vía el wrapper
280
+ // `node -e "require('./hooks/notificacion-telegram.js')"` de settings.json (y
281
+ // desde los wrappers -notification/-subagent), donde require.main es undefined;
282
+ // un gate de entrypoint dejaría el hook muerto en producción (audit
283
+ // entrypoint-wrapper.test.js). Las funciones puras testeables viven en
284
+ // ./lib/notificacion-formato.js.
275
285
  run().catch(e => log('run error', e && e.message));
@@ -20,7 +20,7 @@
20
20
  */
21
21
 
22
22
  const fs = require('fs');
23
- const { raizProyecto } = require('./lib/raiz-proyecto');
23
+ const { raizProyecto } = require('./lib/raiz-proyecto');
24
24
  const path = require('path');
25
25
 
26
26
  // Atomic write para backups pre-compact (.planning/backups/). Si la
@@ -54,7 +54,7 @@
54
54
  */
55
55
 
56
56
  const fs = require('fs');
57
- const { raizProyecto } = require('./lib/raiz-proyecto');
57
+ const { raizProyecto } = require('./lib/raiz-proyecto');
58
58
  const path = require('path');
59
59
 
60
60
  const LIMITE_TURNOS = parseInt(process.env.SWL_REGISTRO_TURNOS_LIMITE || '1000', 10);
@@ -18,7 +18,7 @@
18
18
  */
19
19
 
20
20
  const fs = require('fs');
21
- const { raizProyecto } = require('./lib/raiz-proyecto');
21
+ const { raizProyecto } = require('./lib/raiz-proyecto');
22
22
  const path = require('path');
23
23
  const os = require('os');
24
24
  const { execFile } = require('child_process');
@@ -26,7 +26,7 @@
26
26
  */
27
27
 
28
28
  const fs = require('fs');
29
- const { raizProyecto } = require('./lib/raiz-proyecto');
29
+ const { raizProyecto } = require('./lib/raiz-proyecto');
30
30
  const path = require('path');
31
31
  const os = require('os');
32
32
 
@@ -19,7 +19,7 @@
19
19
  */
20
20
 
21
21
  const fs = require('fs');
22
- const { raizProyecto } = require('./lib/raiz-proyecto');
22
+ const { raizProyecto } = require('./lib/raiz-proyecto');
23
23
  const path = require('path');
24
24
  const { spawn } = require('child_process');
25
25
 
@@ -32,7 +32,7 @@
32
32
  */
33
33
 
34
34
  const fs = require('fs');
35
- const { raizProyecto } = require('./lib/raiz-proyecto');
35
+ const { raizProyecto } = require('./lib/raiz-proyecto');
36
36
  const path = require('path');
37
37
  const crypto = require('crypto');
38
38
 
@@ -27,7 +27,7 @@
27
27
  */
28
28
 
29
29
  const fs = require('fs');
30
- const { raizProyecto } = require('./lib/raiz-proyecto');
30
+ const { raizProyecto } = require('./lib/raiz-proyecto');
31
31
  const path = require('path');
32
32
 
33
33
  // Carpetas cuyo cambio invalida los contadores de INVENTARIO.md
package/hooks/tdd-gate.js CHANGED
@@ -30,7 +30,7 @@
30
30
  */
31
31
 
32
32
  const fs = require('fs');
33
- const { raizProyecto } = require('./lib/raiz-proyecto');
33
+ const { raizProyecto } = require('./lib/raiz-proyecto');
34
34
  const path = require('path');
35
35
  const { execFileSync } = require('child_process');
36
36
 
@@ -17,7 +17,7 @@
17
17
  */
18
18
 
19
19
  const { registrar } = require('./lib/otlp-exporter');
20
- const { raizProyecto } = require('./lib/raiz-proyecto');
20
+ const { raizProyecto } = require('./lib/raiz-proyecto');
21
21
 
22
22
  // Importar esquema de spans con guard de robustez — el hook no debe fallar
23
23
  // si el módulo aún no existe en instalaciones anteriores del sistema.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
- const { raizProyecto } = require('./lib/raiz-proyecto');
3
+ const { raizProyecto } = require('./lib/raiz-proyecto');
4
4
 
5
5
  /**
6
6
  * Hook: telemetria-skill-routing.js
@@ -21,7 +21,7 @@
21
21
  */
22
22
 
23
23
  const fs = require('fs');
24
- const { raizProyecto } = require('./lib/raiz-proyecto');
24
+ const { raizProyecto } = require('./lib/raiz-proyecto');
25
25
  const path = require('path');
26
26
  const os = require('os');
27
27
 
@@ -23,7 +23,7 @@
23
23
  */
24
24
 
25
25
  const fs = require('fs');
26
- const { raizProyecto } = require('./lib/raiz-proyecto');
26
+ const { raizProyecto } = require('./lib/raiz-proyecto');
27
27
  const path = require('path');
28
28
 
29
29
  const CWD = raizProyecto(process.cwd());
@@ -44,7 +44,7 @@
44
44
  */
45
45
 
46
46
  const fs = require('fs');
47
- const { raizProyecto } = require('./lib/raiz-proyecto');
47
+ const { raizProyecto } = require('./lib/raiz-proyecto');
48
48
  const path = require('path');
49
49
  const crypto = require('crypto');
50
50
 
@@ -19,7 +19,7 @@
19
19
  */
20
20
 
21
21
  const fs = require('fs');
22
- const { raizProyecto } = require('./lib/raiz-proyecto');
22
+ const { raizProyecto } = require('./lib/raiz-proyecto');
23
23
  const path = require('path');
24
24
  const crypto = require('crypto');
25
25
 
package/llms.txt CHANGED
@@ -1,29 +1,29 @@
1
- # swl-ses (@saulwade/swl-ses)
2
-
3
- > Sistema de ingeniería de software auto-evolutivo multi-runtime polyglot (SDLC completo), distribuido como paquete npm y plugin de Claude Code. 61 agentes, 181 habilidades, 48 comandos, 37 reglas base y 49 hooks. Soporta 11 lenguajes y 7 runtimes (Claude Code, OpenClaude, OpenCode, Gemini, Cursor, Codex, Copilot). Versión 2.6.0.
4
-
5
- Archivo generado por `node scripts/generar-inventario.js` — no editar a mano. Las cifras se sincronizan con INVENTARIO.md en cada regeneración.
6
-
7
- ## Documentación
8
-
9
- - [README](README.md): overview público y quickstart
10
- - [Manual de uso](MANUAL_USO.md): manual operacional completo
11
- - [Comandos](COMANDOS.md): referencia detallada de cada comando `/swl:*`
12
- - [Agentes](AGENTS.md): catálogo de agentes con capacidades
13
- - [Inventario](INVENTARIO.md): conteos oficiales de todos los componentes
14
- - [Instalación](INSTALACION.md): instalación, perfiles y configuración
15
- - [Instrucciones del proyecto](CLAUDE.md): convenciones, stack y reglas
16
-
17
- ## Componentes
18
-
19
- - [61 agentes especializados](AGENTS.md): orquestación, implementación por stack, revisión, calidad, diseño — fuente en `agentes/`
20
- - [181 habilidades cargables bajo demanda](INVENTARIO.md): conocimiento operacional con divulgación progresiva — fuente en `habilidades/`
21
- - [48 comandos /swl:*](COMANDOS.md): ciclo GSD, calidad, release, diagnóstico — fuente en `comandos/swl/`
22
- - [37 reglas base + 40 por lenguaje](INVENTARIO.md): políticas obligatorias por matcher — fuente en `reglas/`
23
- - [49 hooks](INVENTARIO.md): telemetría, validación, seguridad; zero-deps, escrituras atómicas — fuente en `hooks/`
24
-
25
- ## Opcional
26
-
27
- - [CHANGELOG](CHANGELOG.md): historial de versiones
28
- - [Índice de ADRs](.planning/adrs/README.md): decisiones de arquitectura
29
- - [Variables de entorno](docs/variables-entorno.md): configuración opt-in
1
+ # swl-ses (@saulwade/swl-ses)
2
+
3
+ > Sistema de ingeniería de software auto-evolutivo multi-runtime polyglot (SDLC completo), distribuido como paquete npm y plugin de Claude Code. 61 agentes, 181 habilidades, 48 comandos, 37 reglas base y 49 hooks. Soporta 11 lenguajes y 7 runtimes (Claude Code, OpenClaude, OpenCode, Gemini, Cursor, Codex, Copilot). Versión 2.6.1.
4
+
5
+ Archivo generado por `node scripts/generar-inventario.js` — no editar a mano. Las cifras se sincronizan con INVENTARIO.md en cada regeneración.
6
+
7
+ ## Documentación
8
+
9
+ - [README](README.md): overview público y quickstart
10
+ - [Manual de uso](MANUAL_USO.md): manual operacional completo
11
+ - [Comandos](COMANDOS.md): referencia detallada de cada comando `/swl:*`
12
+ - [Agentes](AGENTS.md): catálogo de agentes con capacidades
13
+ - [Inventario](INVENTARIO.md): conteos oficiales de todos los componentes
14
+ - [Instalación](INSTALACION.md): instalación, perfiles y configuración
15
+ - [Instrucciones del proyecto](CLAUDE.md): convenciones, stack y reglas
16
+
17
+ ## Componentes
18
+
19
+ - [61 agentes especializados](AGENTS.md): orquestación, implementación por stack, revisión, calidad, diseño — fuente en `agentes/`
20
+ - [181 habilidades cargables bajo demanda](INVENTARIO.md): conocimiento operacional con divulgación progresiva — fuente en `habilidades/`
21
+ - [48 comandos /swl:*](COMANDOS.md): ciclo GSD, calidad, release, diagnóstico — fuente en `comandos/swl/`
22
+ - [37 reglas base + 40 por lenguaje](INVENTARIO.md): políticas obligatorias por matcher — fuente en `reglas/`
23
+ - [49 hooks](INVENTARIO.md): telemetría, validación, seguridad; zero-deps, escrituras atómicas — fuente en `hooks/`
24
+
25
+ ## Opcional
26
+
27
+ - [CHANGELOG](CHANGELOG.md): historial de versiones
28
+ - [Índice de ADRs](.planning/adrs/README.md): decisiones de arquitectura
29
+ - [Variables de entorno](docs/variables-entorno.md): configuración opt-in