@saulwade/swl-ses 2.5.3 → 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 (214) hide show
  1. package/CLAUDE.md +9 -9
  2. package/README.md +37 -37
  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 -908
  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/bin/swl-ses.js +32 -7
  54. package/comandos/swl/actualizar.md +3 -3
  55. package/comandos/swl/aprender.md +13 -0
  56. package/comandos/swl/deuda-codigo.md +97 -97
  57. package/comandos/swl/evaluar-skill.md +18 -3
  58. package/comandos/swl/evolucion-continua.md +73 -0
  59. package/comandos/swl/evolucionar.md +13 -0
  60. package/comandos/swl/instalar.md +4 -4
  61. package/comandos/swl/notificaciones.md +1 -1
  62. package/comandos/swl/status.md +2 -2
  63. package/gateway/cron/jobs.example.json +12 -0
  64. package/habilidades/auto-evolucion-protocolo/SKILL.md +19 -1
  65. package/habilidades/autoresearch/SKILL.md +3 -2
  66. package/habilidades/backend-async-postgres-testing/SKILL.md +2 -1
  67. package/habilidades/benchmark-memoria/SKILL.md +7 -7
  68. package/habilidades/changelog-generator/SKILL.md +1 -1
  69. package/habilidades/changelog-generator/scripts/parse-commits.js +2 -1
  70. package/habilidades/checkpoints-verificacion/SKILL.md +6 -0
  71. package/habilidades/compactacion-contexto/SKILL.md +2 -1
  72. package/habilidades/contenedores-docker/SKILL.md +4 -2
  73. package/habilidades/context-builder/SKILL.md +4 -0
  74. package/habilidades/doubt-driven-review/SKILL.md +17 -1
  75. package/habilidades/drift-detection/SKILL.md +6 -1
  76. package/habilidades/ejecutar-fase/SKILL.md +6 -6
  77. package/habilidades/ejecutar-task-iterativo/SKILL.md +278 -278
  78. package/habilidades/eval-framework/SKILL.md +8 -3
  79. package/habilidades/extractor-de-aprendizajes/SKILL.md +8 -2
  80. package/habilidades/git-worktrees-paralelo/SKILL.md +19 -1
  81. package/habilidades/harness-claude-code/SKILL.md +7 -3
  82. package/habilidades/infra-github-actions/SKILL.md +4 -3
  83. package/habilidades/instalar-sistema/SKILL.md +5 -1
  84. package/habilidades/memoria-busqueda/SKILL.md +31 -39
  85. package/habilidades/planear-fase/SKILL.md +9 -1
  86. package/habilidades/prevencion-sobreingenieria/recursos/soluciones-nativas.md +166 -166
  87. package/habilidades/prevencion-sobreingenieria/recursos/variables-residuales-post-refactor.md +85 -85
  88. package/habilidades/proceso-ddia-fundamentos/SKILL.md +3 -2
  89. package/habilidades/proceso-ingenieria-requerimientos/SKILL.md +147 -147
  90. package/habilidades/release-semver/SKILL.md +2 -2
  91. package/habilidades/swl-claudemd/SKILL.md +6 -7
  92. package/habilidades/swl-dashboard/SKILL.md +11 -43
  93. package/habilidades/tdd-workflow/SKILL.md +12 -7
  94. package/habilidades/validacion-ci-sistema/SKILL.md +1 -1
  95. package/hooks/agente-lifecycle.js +2 -1
  96. package/hooks/aiisms-detector.js +13 -4
  97. package/hooks/audit-trail.js +2 -1
  98. package/hooks/auto-consolidacion.js +2 -1
  99. package/hooks/captura-acciones-post.js +2 -1
  100. package/hooks/captura-acciones-session.js +2 -1
  101. package/hooks/captura-feedback-usuario.js +3 -2
  102. package/hooks/claudemd-bloat-detector.js +12 -3
  103. package/hooks/claudemd-duplicacion-detector.js +13 -3
  104. package/hooks/contexto-iteracion.js +2 -1
  105. package/hooks/contexto-subagente.js +68 -68
  106. package/hooks/degradacion-instintos.js +2 -1
  107. package/hooks/extraccion-aprendizajes.js +109 -15
  108. package/hooks/grafo-contexto.js +2 -1
  109. package/hooks/guardrail-modelo.js +2 -1
  110. package/hooks/inbox-aviso.js +2 -1
  111. package/hooks/inyeccion-contexto.js +2 -1
  112. package/hooks/lib/agent-matcher.js +2 -1
  113. package/hooks/lib/agent-routing.js +2 -1
  114. package/hooks/lib/autonomia.js +5 -3
  115. package/hooks/lib/captura-acciones.js +2 -1
  116. package/hooks/lib/consolidation-lock.js +21 -10
  117. package/hooks/lib/etapa-auto-evolucion.js +10 -4
  118. package/hooks/lib/etapa-metricas.js +2 -1
  119. package/hooks/lib/etapa-perfil-usuario.js +20 -4
  120. package/hooks/lib/evolution-tracker.js +2 -1
  121. package/hooks/lib/gateway-notify.js +17 -3
  122. package/hooks/lib/loop-telemetry.js +5 -4
  123. package/hooks/lib/mcp-health.js +2 -1
  124. package/hooks/lib/memory-search.js +4 -0
  125. package/hooks/lib/merkle-audit.js +58 -6
  126. package/hooks/lib/notificacion-formato.js +58 -0
  127. package/hooks/lib/nudge-tracker.js +2 -1
  128. package/hooks/lib/otlp-exporter.js +2 -1
  129. package/hooks/lib/propose-step.js +3 -2
  130. package/hooks/lib/raiz-proyecto.js +127 -0
  131. package/hooks/lib/run-log.js +2 -1
  132. package/hooks/lib/singleton-guard.js +225 -27
  133. package/hooks/lib/telegram-cliente.js +28 -11
  134. package/hooks/notificacion-telegram.js +13 -3
  135. package/hooks/preservar-estado-pre-compact.js +2 -1
  136. package/hooks/proteccion-rutas.js +59 -3
  137. package/hooks/registro-turnos.js +2 -1
  138. package/hooks/resumen-sesion.js +2 -1
  139. package/hooks/risk-scoring.js +2 -1
  140. package/hooks/rotar-audit-auto.js +46 -20
  141. package/hooks/session-briefing.js +127 -1
  142. package/hooks/spec-gate.js +2 -1
  143. package/hooks/sugerir-contribuir.js +6 -3
  144. package/hooks/sugerir-regenerar-inventario.js +3 -2
  145. package/hooks/tdd-gate.js +2 -1
  146. package/hooks/telemetria-agentes.js +2 -1
  147. package/hooks/telemetria-skill-routing.js +2 -1
  148. package/hooks/tracking-costos.js +4 -3
  149. package/hooks/validar-formato-post-subagente.js +2 -1
  150. package/hooks/validar-intent-spec.js +2 -1
  151. package/hooks/validar-memoria-hook.js +13 -3
  152. package/hooks/validar-planning-paths.js +2 -1
  153. package/instintos/perfil-usuario.yaml +506 -3
  154. package/instintos/proyecto.yaml +78 -0
  155. package/llms.txt +29 -29
  156. package/manifiestos/canonical-hashes.json +5588 -4925
  157. package/manifiestos/hooks-config.json +469 -469
  158. package/manifiestos/invariantes-criticos.json +30 -30
  159. package/manifiestos/modulos.json +1429 -1423
  160. package/manifiestos/planning-paths.json +1 -0
  161. package/manifiestos/skills-lock.json +1275 -1275
  162. package/package.json +94 -95
  163. package/plugin.json +369 -369
  164. package/scripts/actualizar.js +3 -0
  165. package/scripts/auditar-clases-conocidas.js +134 -106
  166. package/scripts/benchmark-memoria.js +1 -0
  167. package/scripts/bootstrap-instintos.js +85 -14
  168. package/scripts/canario-hooks.js +166 -166
  169. package/scripts/cli/autonomia.js +23 -0
  170. package/scripts/cli/benchmark-memoria.js +37 -0
  171. package/scripts/cli/ciclo-autonomo.js +73 -0
  172. package/scripts/cli/ciclo-fase-b.js +102 -0
  173. package/scripts/cli/guardrail-metrics.js +39 -0
  174. package/scripts/cli/loop-telemetry.js +4 -2
  175. package/scripts/cli/memoria-search.js +69 -0
  176. package/scripts/cli/nudge-accionar.js +39 -0
  177. package/scripts/cli/run-eval.js +38 -0
  178. package/scripts/cli/run-skill-evals.js +13 -2
  179. package/scripts/derivar-feature-list.js +15 -14
  180. package/scripts/desinstalar.js +11 -0
  181. package/scripts/doctor.js +50 -13
  182. package/scripts/evidencia-valor.js +101 -101
  183. package/scripts/field-report.js +16 -16
  184. package/scripts/instalador.js +98 -7
  185. package/scripts/lib/activar-hooks-proyecto.js +116 -104
  186. package/scripts/lib/auditar-invocaciones-comandos.js +96 -6
  187. package/scripts/lib/ciclo-autonomo/candidatos.js +174 -0
  188. package/scripts/lib/ciclo-autonomo/config.js +165 -0
  189. package/scripts/lib/ciclo-autonomo/drenador-feedback.js +174 -0
  190. package/scripts/lib/ciclo-autonomo/fallback.js +77 -0
  191. package/scripts/lib/ciclo-autonomo/guard-convivencia.js +139 -0
  192. package/scripts/lib/ciclo-autonomo/higiene-nudges.js +112 -0
  193. package/scripts/lib/ciclo-autonomo/index.js +301 -0
  194. package/scripts/lib/ciclo-autonomo/lock.js +124 -0
  195. package/scripts/lib/ciclo-autonomo/presupuesto.js +122 -0
  196. package/scripts/lib/ciclo-autonomo/puente-degradacion.js +240 -0
  197. package/scripts/lib/ciclo-autonomo/runner-fase-b.js +248 -0
  198. package/scripts/lib/ciclo-autonomo/writer-instintos.js +190 -0
  199. package/scripts/lib/ciclo-autonomo/yaml-instintos.js +535 -0
  200. package/scripts/lib/estado.js +9 -0
  201. package/scripts/lib/evidencia-valor.js +228 -228
  202. package/scripts/lib/expandir-targets.js +71 -71
  203. package/scripts/lib/gitignore-manifest.js +8 -1
  204. package/scripts/lib/hooks-settings.js +45 -0
  205. package/scripts/lib/limpiar-basura-global.js +161 -0
  206. package/scripts/lib/toml-merge.js +204 -204
  207. package/scripts/mcp-server/auth.js +105 -105
  208. package/scripts/mcp-server/cache.js +106 -106
  209. package/scripts/rotar-audit-logs.js +48 -2
  210. package/scripts/run-eval.js +1 -0
  211. package/scripts/run-skill-evals.js +287 -8
  212. package/scripts/smoke-test.js +16 -8
  213. package/scripts/tui/pantallas/install-wizard.js +69 -13
  214. package/scripts/validar.js +40 -1
@@ -0,0 +1,69 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Wrapper CLI `swl-ses memoria-search <op>` — búsqueda en la memoria del
5
+ * proyecto (.planning/sessions/, APRENDIZAJES.md, diary). Reemplaza los
6
+ * `require('./hooks/lib/memory-search')` relativos al proyecto que
7
+ * memoria-busqueda y benchmark-memoria instruían — rotos downstream (ver
8
+ * docs/invocacion-cli-cross-scope.md).
9
+ *
10
+ * Ops:
11
+ * search --query="..." [--limit=N] [--tipo=aprendizaje|sesion|instinto]
12
+ * [--desde=YYYY-MM-DD] [--hasta=YYYY-MM-DD] (default)
13
+ * timeline --ids=id1,id2,...
14
+ * fetch --id=<id>
15
+ *
16
+ * Salida: JSON a stdout. La base es la RAÍZ del proyecto (raizProyecto),
17
+ * no el cwd crudo — misma semántica que los hooks (DT-HOOKS-RAIZ-GIT).
18
+ */
19
+
20
+ const ms = require('../../hooks/lib/memory-search');
21
+ const { raizProyecto } = require('../../hooks/lib/raiz-proyecto');
22
+
23
+ function memoriaSearch(opciones = {}) {
24
+ const op = (opciones._args && opciones._args[0]) || 'search';
25
+ const base = raizProyecto(process.cwd());
26
+
27
+ if (op === 'search') {
28
+ const query = opciones.query != null && opciones.query !== true ? String(opciones.query) : '';
29
+ if (!query) {
30
+ console.error('Error: falta --query="<términos>"');
31
+ process.exitCode = 1;
32
+ return;
33
+ }
34
+ const filtros = {};
35
+ const limit = parseInt(opciones.limit, 10);
36
+ if (!isNaN(limit) && limit > 0) filtros.limit = limit;
37
+ if (opciones.tipo != null && opciones.tipo !== true) filtros.tipo = String(opciones.tipo);
38
+ if (opciones.desde != null && opciones.desde !== true) filtros.fecha_desde = String(opciones.desde);
39
+ if (opciones.hasta != null && opciones.hasta !== true) filtros.fecha_hasta = String(opciones.hasta);
40
+ console.log(JSON.stringify(ms.search(base, query, filtros), null, 2));
41
+ return;
42
+ }
43
+
44
+ if (op === 'timeline') {
45
+ const ids = String(opciones.ids || '').split(',').map(s => s.trim()).filter(Boolean);
46
+ if (ids.length === 0) {
47
+ console.error('Error: falta --ids=id1,id2,...');
48
+ process.exitCode = 1;
49
+ return;
50
+ }
51
+ console.log(JSON.stringify(ms.timeline(base, ids), null, 2));
52
+ return;
53
+ }
54
+
55
+ if (op === 'fetch') {
56
+ if (opciones.id == null || opciones.id === true) {
57
+ console.error('Error: falta --id=<id>');
58
+ process.exitCode = 1;
59
+ return;
60
+ }
61
+ console.log(JSON.stringify(ms.fetch(base, String(opciones.id)), null, 2));
62
+ return;
63
+ }
64
+
65
+ console.error(`Error: op desconocida "${op}" (usa search|timeline|fetch)`);
66
+ process.exitCode = 1;
67
+ }
68
+
69
+ module.exports = memoriaSearch;
@@ -0,0 +1,39 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Subcomando `swl-ses nudge-accionar <id> --por <consumidor>` — cierra el
5
+ * ciclo de un nudge (T-09, Fase 21 / ADR-0041).
6
+ *
7
+ * Lo invocan los comandos /swl:aprender y /swl:evolucionar (y el agente
8
+ * auto-evolucion-swl) tras atender la sugerencia de un nudge, para que
9
+ * `evolution/nudges.jsonl` registre quién lo accionó. Sin este cierre los
10
+ * nudges se acumulan sin consumidor (174 emitidos / 0 accionados al crear
11
+ * la Fase 21).
12
+ *
13
+ * Uso: swl-ses nudge-accionar <id> [--por <consumidor>]
14
+ */
15
+
16
+ const { marcarAccionado } = require('../lib/ciclo-autonomo/higiene-nudges');
17
+
18
+ function nudgeAccionar(opciones = {}) {
19
+ const id = (opciones._args || [])[0];
20
+ const baseDir = opciones.dir || process.cwd();
21
+
22
+ if (!id) {
23
+ console.error('[nudge-accionar] falta el id. Uso: swl-ses nudge-accionar <id> [--por <consumidor>]');
24
+ process.exitCode = 1;
25
+ return null;
26
+ }
27
+
28
+ const por = opciones.por || opciones.by || 'desconocido';
29
+ const resultado = marcarAccionado(baseDir, { id, por });
30
+
31
+ if (resultado.marcados === 0) {
32
+ console.error(`[nudge-accionar] nudge "${id}" no encontrado o ya accionado`);
33
+ } else {
34
+ console.log(`[nudge-accionar] nudge "${id}" marcado como accionado por "${por}"`);
35
+ }
36
+ return resultado;
37
+ }
38
+
39
+ module.exports = nudgeAccionar;
@@ -0,0 +1,38 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Wrapper CLI `swl-ses run-eval` — ejecuta un eval del framework de validación
5
+ * de outputs estructurados (scripts/run-eval.js). Reemplaza el
6
+ * `node scripts/run-eval.js` relativo al proyecto que eval-framework
7
+ * instruía — roto downstream (ver docs/invocacion-cli-cross-scope.md).
8
+ *
9
+ * Uso:
10
+ * swl-ses run-eval <ruta-eval.json>
11
+ * swl-ses run-eval --rebuild-aggregate
12
+ *
13
+ * ADVERTENCIA (nemesis iter-1 S3-6): el main() envuelto llama process.exit()
14
+ * en todas sus ramas — este wrapper TERMINA el proceso host. Correcto vía
15
+ * `bin/swl-ses.js` (proceso fresco por invocación); NO invocar in-process
16
+ * (batch runners, tests que compongan varios wrappers) o matará el proceso.
17
+ */
18
+
19
+ const { main } = require('../run-eval.js');
20
+
21
+ function runEval(opciones = {}) {
22
+ const args = [];
23
+ if (opciones['rebuild-aggregate'] || opciones.rebuild_aggregate) {
24
+ args.push('--rebuild-aggregate');
25
+ } else if (opciones._args && opciones._args[0]) {
26
+ args.push(String(opciones._args[0]));
27
+ }
28
+
29
+ const argvOriginal = process.argv;
30
+ process.argv = ['node', 'run-eval.js', ...args];
31
+ try {
32
+ main();
33
+ } finally {
34
+ process.argv = argvOriginal;
35
+ }
36
+ }
37
+
38
+ module.exports = runEval;
@@ -11,10 +11,17 @@
11
11
  * --list → listar evals del skill
12
12
  * --json → output JSON
13
13
  * --record-baseline → registrar score baseline
14
- * --record-after → registrar score post-cambio
15
- * --score=<N> → score numérico (acompaña --record-*)
14
+ * --record-after → registrar score post-cambio (el score se LEE del
15
+ * artefacto de evaluación con procedencia — ver abajo)
16
+ * --score=<N> → score numérico (baseline/revert; en record-after
17
+ * solo como verificación de coincidencia)
16
18
  * --hypothesis=<texto> → hipótesis de la mutación
17
19
  * --invariantes-ok → marcar invariantes como OK
20
+ * --emitir-evaluacion → escribir artefacto de procedencia (lo invoca
21
+ * /swl:evaluar-skill; exige --score y --producido-por)
22
+ * --producido-por=<a> → actor que corrió la evaluación (≠ target)
23
+ * --badge=<b> → badge asignado (opcional, acompaña emitir)
24
+ * --evaluacion=<path> → override del path del artefacto en record-after
18
25
  */
19
26
 
20
27
  const path = require('path');
@@ -32,6 +39,10 @@ function runSkillEvals(opciones) {
32
39
  if (opciones && opciones.score !== undefined) args.push(`--score=${opciones.score}`);
33
40
  if (opciones && opciones.hypothesis) args.push(`--hypothesis=${opciones.hypothesis}`);
34
41
  if (opciones && opciones['invariantes-ok']) args.push('--invariantes-ok');
42
+ if (opciones && opciones['emitir-evaluacion']) args.push('--emitir-evaluacion');
43
+ if (opciones && opciones['producido-por']) args.push(`--producido-por=${opciones['producido-por']}`);
44
+ if (opciones && opciones.badge) args.push(`--badge=${opciones.badge}`);
45
+ if (opciones && opciones.evaluacion) args.push(`--evaluacion=${opciones.evaluacion}`);
35
46
 
36
47
  const argvOriginal = process.argv;
37
48
  process.argv = ['node', 'run-skill-evals.js', ...args];
@@ -102,22 +102,23 @@ function parsearTablaResumen(contenido, fases) {
102
102
 
103
103
  const celdas = trim.split('|').map(c => c.trim()).filter(c => c !== '');
104
104
 
105
- // Detectar header
106
- if (idxFase === -1) {
107
- const lowered = celdas.map(c => c.toLowerCase());
108
- const fa = lowered.findIndex(c => c === 'fase' || c === 'phase' || c === '#');
109
- const no = lowered.findIndex(c => c === 'nombre' || c === 'name');
110
- const ob = lowered.findIndex(c => c === 'objetivo' || c === 'objective');
111
- const es = lowered.findIndex(c => c === 'estado' || c === 'status');
112
- if (fa !== -1 && no !== -1 && es !== -1) {
113
- idxFase = fa;
114
- idxNombre = no;
115
- idxObjetivo = ob;
116
- idxEstado = es;
117
- enTabla = true;
118
- }
105
+ // Detectar header — en CADA línea de tabla, no solo en la primera del
106
+ // archivo: el HOJA-RUTA real acumula varias tablas de fase (una por
107
+ // iniciativa) y fijar los índices de la primera ignoraba las demás en
108
+ // silencio (--check daba verde con fases ausentes del JSON).
109
+ const lowered = celdas.map(c => c.toLowerCase());
110
+ const fa = lowered.findIndex(c => c === 'fase' || c === 'phase' || c === '#');
111
+ const no = lowered.findIndex(c => c === 'nombre' || c === 'name');
112
+ const es = lowered.findIndex(c => c === 'estado' || c === 'status');
113
+ if (fa !== -1 && no !== -1 && es !== -1) {
114
+ idxFase = fa;
115
+ idxNombre = no;
116
+ idxObjetivo = lowered.findIndex(c => c === 'objetivo' || c === 'objective');
117
+ idxEstado = es;
118
+ enTabla = true;
119
119
  continue;
120
120
  }
121
+ if (idxFase === -1) continue;
121
122
 
122
123
  // Línea separadora (|---|---|)
123
124
  if (celdas.every(c => /^-+$/.test(c.replace(/:/g, '')))) continue;
@@ -140,6 +140,17 @@ function uninstall(opciones) {
140
140
  ` - ${archivo.destino}`
141
141
  );
142
142
  eliminados++;
143
+ // Subdirectorios creados por el instalador (hooks/lib/, rules/<lang>/)
144
+ // quedan vacíos tras borrar sus archivos: rmdir no-recursivo — solo
145
+ // cae si el dir quedó realmente vacío; cualquier contenido del usuario
146
+ // lo preserva (ENOTEMPTY ignorado). Acotado a rutas.base: un
147
+ // install-state manipulado no puede dirigir el rmdir fuera del árbol
148
+ // gestionado (endurecimiento, revisor-seguridad 2026-07-10).
149
+ try {
150
+ const padre = path.resolve(path.dirname(archivo.destino));
151
+ const baseGestionada = path.resolve(rutas.base);
152
+ if (padre.startsWith(baseGestionada + path.sep)) fs.rmdirSync(padre);
153
+ } catch (_) { /* no vacío o raíz gestionada */ }
143
154
  } catch (err) {
144
155
  _emitir(
145
156
  { tipo: 'error', archivo: archivo.destino, mensaje: err.message },
package/scripts/doctor.js CHANGED
@@ -31,7 +31,13 @@ async function obtenerVersionRemota() {
31
31
  }
32
32
 
33
33
  async function doctor(opciones = {}) {
34
- const force = opciones.force || !ES_TTY;
34
+ // Reparar exige intención EXPLÍCITA (--force). El default previo
35
+ // (`|| !ES_TTY`) auto-reparaba en headless: los reparadores invocan al
36
+ // INSTALADOR contra el cwd — en un checkout del repo eso materializaba
37
+ // copias dentro del fuente, reescribía settings.json y disparaba
38
+ // `git rm --cached` masivo vía limpiarTracked (incidente 2026-07-10,
39
+ // tests/CI). Sin TTY y sin --force: doctor solo DIAGNOSTICA.
40
+ const force = !!opciones.force;
35
41
  const cwd = process.cwd();
36
42
 
37
43
  let errores = 0;
@@ -166,14 +172,37 @@ async function doctor(opciones = {}) {
166
172
  // en un cwd que no es un proyecto swl. Correr `doctor` desde un directorio
167
173
  // cualquiera no debe generar clutter. Marcadores de proyecto: .planning/ o
168
174
  // _userland/ ya presentes, o un install-state de scope proyecto.
169
- const esProyectoSwl =
175
+ //
176
+ // El HOME es la EXCEPCIÓN: un install global crea ~/_userland ahí, pero el
177
+ // HOME NO es un proyecto — doctor no debe sembrar ~/.planning/ (bug 2026-07-15:
178
+ // sembraba un scaffold espurio y los hooks volcaban telemetría en el HOME vía
179
+ // raiz-proyecto). Ver scripts/lib/limpiar-basura-global.js.
180
+ const { esPlanningEspurio, esUserlandVacio, limpiarBasuraGlobal } = require('./lib/limpiar-basura-global');
181
+ const esHome = path.resolve(cwd) === path.resolve(require('os').homedir());
182
+ const esProyectoSwl = !esHome && (
170
183
  fs.existsSync(path.join(cwd, '.planning')) ||
171
184
  fs.existsSync(path.join(cwd, '_userland')) ||
172
- fs.existsSync(path.join(cwd, '.swl-install-state.json'));
185
+ fs.existsSync(path.join(cwd, '.swl-install-state.json'))
186
+ );
173
187
 
174
188
  // 3. Verificar .planning/
175
189
  const planningDir = path.join(cwd, '.planning');
176
- if (fs.existsSync(planningDir)) {
190
+ const basuraHome = [];
191
+ if (esHome && esPlanningEspurio(cwd)) basuraHome.push('~/.planning (scaffold de doctor)');
192
+ if (esHome && esUserlandVacio(cwd)) basuraHome.push('~/_userland (vacío)');
193
+ if (basuraHome.length > 0) {
194
+ // Basura de instalación global en el HOME: NO "reparar" (el HOME no es un
195
+ // proyecto) — OFRECER eliminarla. Ver scripts/lib/limpiar-basura-global.js.
196
+ console.log(formatearAdvertencia('instalación global', `Directorios basura en el HOME: ${basuraHome.join(', ')} — el HOME no es un proyecto`));
197
+ advertencias++;
198
+ reparables.push({
199
+ descripcion: `Eliminar ${basuraHome.length} directorio(s) basura del HOME: ${basuraHome.join(', ')}`,
200
+ reparar: () => {
201
+ const r = limpiarBasuraGlobal({ home: cwd });
202
+ return r.eliminados.length ? `Eliminado: ${r.eliminados.join(', ')}` : 'Nada que eliminar';
203
+ },
204
+ });
205
+ } else if (fs.existsSync(planningDir)) {
177
206
  const archivosEsperados = ['PROYECTO.md', 'REQUISITOS.md', 'HOJA-RUTA.md', 'ESTADO.md'];
178
207
  const faltantes = archivosEsperados.filter(
179
208
  f => !fs.existsSync(path.join(planningDir, f))
@@ -337,14 +366,18 @@ async function doctor(opciones = {}) {
337
366
  });
338
367
  }
339
368
  if (resultado.incorrectos.length > 0) {
340
- const reparacion = repararHooks(settingsPath);
341
- if (reparacion.reparados > 0) {
342
- console.log(formatearPaso(`Hooks ${runtime.nombre}`, `${reparacion.reparados} hooks reparados automáticamente`));
343
- ok++;
344
- } else {
345
- console.log(formatearAdvertencia(`Hooks ${runtime.nombre}`, `${resultado.incorrectos.length} hooks con problemas`));
346
- advertencias++;
347
- }
369
+ // NO reparar en la fase de diagnóstico: repararHooks muta
370
+ // settings.json. Debe pasar por el gate --force como todo lo
371
+ // demás (incidente 2026-07-10 reparación no consentida).
372
+ console.log(formatearAdvertencia(`Hooks ${runtime.nombre}`, `${resultado.incorrectos.length} hooks con problemas`));
373
+ advertencias++;
374
+ reparables.push({
375
+ descripcion: `Reparar ${resultado.incorrectos.length} hooks con formato incorrecto en settings.json`,
376
+ reparar: () => {
377
+ const reparacion = repararHooks(settingsPath);
378
+ return `${reparacion.reparados} hooks reparados en settings.json`;
379
+ },
380
+ });
348
381
  }
349
382
  }
350
383
  }
@@ -683,10 +716,14 @@ async function doctor(opciones = {}) {
683
716
  console.log('');
684
717
 
685
718
  let proceder = force;
686
- if (!proceder) {
719
+ if (!proceder && ES_TTY) {
687
720
  proceder = await preguntarSiNo('¿Reparar automáticamente?');
688
721
  console.log('');
689
722
  }
723
+ if (!proceder && !ES_TTY) {
724
+ console.log(` ${color.dim('Modo no interactivo: reparación omitida. Ejecuta `swl-ses doctor --force` para reparar.')}`);
725
+ console.log('');
726
+ }
690
727
 
691
728
  if (proceder) {
692
729
  let reparados = 0;
@@ -1,101 +1,101 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- /**
5
- * evidencia-valor.js — CLI `swl-ses valor`: panel de evidencia de valor del
6
- * harness en el proyecto downstream actual.
7
- *
8
- * Uso:
9
- * swl-ses valor # panel legible (ventana 30 días)
10
- * swl-ses valor --dias 90 # ventana distinta
11
- * swl-ses valor --json # salida JSON para tooling
12
- *
13
- * Reglas duras del panel (docs/evidencia-valor.md):
14
- * - Cada métrica declara su PROCEDENCIA (git / GitHub / hook-event) y su
15
- * CARÁCTER (evidencia | proxy | contexto). Nunca scores autodeclarados.
16
- * - Todo se lee LOCAL. El repo madre no lee estos datos.
17
- */
18
-
19
- const { panelEvidencia } = require('./lib/evidencia-valor.js');
20
-
21
- /**
22
- * Contrato del CLI (bin/swl-ses.js): exportar UNA función(opciones) sin
23
- * efectos al require. Violarlo = "modulo is not a function" tras imprimir
24
- * el panel (caso real: SIGM, 2026-07-09).
25
- */
26
- function valor(opciones = {}) {
27
- const json = !!opciones.json;
28
- const ventanaDias = parseInt(opciones.dias, 10) || 30;
29
-
30
- const p = panelEvidencia(process.cwd(), { ventanaDias });
31
-
32
- if (json) {
33
- console.log(JSON.stringify(p, null, 2));
34
- return;
35
- }
36
-
37
- console.log(`Evidencia de valor — ventana ${p.ventanaDias} días (${p.generado})`);
38
- console.log(` ${p.frontera}`);
39
- console.log('');
40
-
41
- const i = p.intercepciones;
42
- console.log('1. Intercepciones de gates [evidencia · hook-event]');
43
- if (!i.disponible) {
44
- console.log(` no disponible — ${i.razon}`);
45
- } else {
46
- console.log(` intervenciones: ${i.intervenciones} de ${i.totalEventos} eventos`);
47
- const tipos = Object.entries(i.porTipo).sort((a, b) => b[1] - a[1]).slice(0, 6);
48
- for (const [t, n] of tipos) console.log(` - ${t}: ${n}`);
49
- }
50
- console.log('');
51
-
52
- const d = p.defectos;
53
- console.log('2. Presión de defectos [proxy · git log]');
54
- if (!d.disponible) {
55
- console.log(` no disponible — ${d.razon}`);
56
- } else {
57
- console.log(` commits: ${d.commits} | fix: ${d.fixes} | feat: ${d.feats} | ratio fix/feat: ${d.ratioFixFeat ?? 'n/a'}`);
58
- if (d.escapes.disponible) {
59
- console.log(` escapes: ${d.escapes.fixesTrasUltimaVerificacion} fix tras la última verificación (${d.escapes.ultimaVerificacion}) — ${d.escapes.nota}`);
60
- } else {
61
- console.log(` escapes: no disponible — ${d.escapes.razon}`);
62
- }
63
- }
64
- console.log('');
65
-
66
- const dc = p.doraCi;
67
- console.log('3. DORA-lite + CI [evidencia de tendencia · git + GitHub]');
68
- if (!dc.disponible) {
69
- console.log(` no disponible — ${dc.razon}`);
70
- } else {
71
- const dora = dc.dora;
72
- const nd = m => `n/d (${(m && m.razon) || 'sin datos'})`;
73
- const df = dora.deploymentFrequency;
74
- console.log(` deployment frequency: ${df && df.disponible !== false && df.valor != null ? `${df.valor} deploys/semana [${df.clasificacion}]` : nd(df)}`);
75
- const lt = dora.leadTime;
76
- console.log(` lead time: ${lt && lt.disponible !== false && lt.p50Horas != null ? `p50 ${(+lt.p50Horas).toFixed(1)}h / p95 ${(+lt.p95Horas).toFixed(1)}h [${lt.clasificacion}]` : nd(lt)}`);
77
- const cfr = dora.changeFailureRate;
78
- console.log(` change failure rate: ${cfr && cfr.disponible !== false && cfr.valor != null ? `${cfr.valor}% [${cfr.clasificacion || ''}]` : nd(cfr)}`);
79
- const mt = dora.mttr;
80
- console.log(` mttr: ${mt && mt.disponible !== false && mt.mttrHoras != null ? `${mt.mttrHoras}h` : nd(mt)}`);
81
- console.log(' (correlación con la adopción del flujo — no causalidad)');
82
- }
83
- console.log('');
84
-
85
- const u = p.uso;
86
- console.log('4. Uso del sistema [contexto · telemetría]');
87
- if (!u.disponible) {
88
- console.log(` no disponible — ${u.razon}`);
89
- } else {
90
- console.log(` invocaciones de skills: ${u.invocaciones} | top: ${u.top5.join(', ')}`);
91
- }
92
- console.log('');
93
- console.log('Compartir agregados con el autor (opt-in): swl-ses field-report — ver docs/evidencia-valor.md');
94
- }
95
-
96
- module.exports = valor;
97
-
98
- if (require.main === module) {
99
- const { parsearOpciones } = require('./lib/parsear-opciones.js');
100
- valor(parsearOpciones(process.argv.slice(2)));
101
- }
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * evidencia-valor.js — CLI `swl-ses valor`: panel de evidencia de valor del
6
+ * harness en el proyecto downstream actual.
7
+ *
8
+ * Uso:
9
+ * swl-ses valor # panel legible (ventana 30 días)
10
+ * swl-ses valor --dias 90 # ventana distinta
11
+ * swl-ses valor --json # salida JSON para tooling
12
+ *
13
+ * Reglas duras del panel (docs/evidencia-valor.md):
14
+ * - Cada métrica declara su PROCEDENCIA (git / GitHub / hook-event) y su
15
+ * CARÁCTER (evidencia | proxy | contexto). Nunca scores autodeclarados.
16
+ * - Todo se lee LOCAL. El repo madre no lee estos datos.
17
+ */
18
+
19
+ const { panelEvidencia } = require('./lib/evidencia-valor.js');
20
+
21
+ /**
22
+ * Contrato del CLI (bin/swl-ses.js): exportar UNA función(opciones) sin
23
+ * efectos al require. Violarlo = "modulo is not a function" tras imprimir
24
+ * el panel (caso real: SIGM, 2026-07-09).
25
+ */
26
+ function valor(opciones = {}) {
27
+ const json = !!opciones.json;
28
+ const ventanaDias = parseInt(opciones.dias, 10) || 30;
29
+
30
+ const p = panelEvidencia(process.cwd(), { ventanaDias });
31
+
32
+ if (json) {
33
+ console.log(JSON.stringify(p, null, 2));
34
+ return;
35
+ }
36
+
37
+ console.log(`Evidencia de valor — ventana ${p.ventanaDias} días (${p.generado})`);
38
+ console.log(` ${p.frontera}`);
39
+ console.log('');
40
+
41
+ const i = p.intercepciones;
42
+ console.log('1. Intercepciones de gates [evidencia · hook-event]');
43
+ if (!i.disponible) {
44
+ console.log(` no disponible — ${i.razon}`);
45
+ } else {
46
+ console.log(` intervenciones: ${i.intervenciones} de ${i.totalEventos} eventos`);
47
+ const tipos = Object.entries(i.porTipo).sort((a, b) => b[1] - a[1]).slice(0, 6);
48
+ for (const [t, n] of tipos) console.log(` - ${t}: ${n}`);
49
+ }
50
+ console.log('');
51
+
52
+ const d = p.defectos;
53
+ console.log('2. Presión de defectos [proxy · git log]');
54
+ if (!d.disponible) {
55
+ console.log(` no disponible — ${d.razon}`);
56
+ } else {
57
+ console.log(` commits: ${d.commits} | fix: ${d.fixes} | feat: ${d.feats} | ratio fix/feat: ${d.ratioFixFeat ?? 'n/a'}`);
58
+ if (d.escapes.disponible) {
59
+ console.log(` escapes: ${d.escapes.fixesTrasUltimaVerificacion} fix tras la última verificación (${d.escapes.ultimaVerificacion}) — ${d.escapes.nota}`);
60
+ } else {
61
+ console.log(` escapes: no disponible — ${d.escapes.razon}`);
62
+ }
63
+ }
64
+ console.log('');
65
+
66
+ const dc = p.doraCi;
67
+ console.log('3. DORA-lite + CI [evidencia de tendencia · git + GitHub]');
68
+ if (!dc.disponible) {
69
+ console.log(` no disponible — ${dc.razon}`);
70
+ } else {
71
+ const dora = dc.dora;
72
+ const nd = m => `n/d (${(m && m.razon) || 'sin datos'})`;
73
+ const df = dora.deploymentFrequency;
74
+ console.log(` deployment frequency: ${df && df.disponible !== false && df.valor != null ? `${df.valor} deploys/semana [${df.clasificacion}]` : nd(df)}`);
75
+ const lt = dora.leadTime;
76
+ console.log(` lead time: ${lt && lt.disponible !== false && lt.p50Horas != null ? `p50 ${(+lt.p50Horas).toFixed(1)}h / p95 ${(+lt.p95Horas).toFixed(1)}h [${lt.clasificacion}]` : nd(lt)}`);
77
+ const cfr = dora.changeFailureRate;
78
+ console.log(` change failure rate: ${cfr && cfr.disponible !== false && cfr.valor != null ? `${cfr.valor}% [${cfr.clasificacion || ''}]` : nd(cfr)}`);
79
+ const mt = dora.mttr;
80
+ console.log(` mttr: ${mt && mt.disponible !== false && mt.mttrHoras != null ? `${mt.mttrHoras}h` : nd(mt)}`);
81
+ console.log(' (correlación con la adopción del flujo — no causalidad)');
82
+ }
83
+ console.log('');
84
+
85
+ const u = p.uso;
86
+ console.log('4. Uso del sistema [contexto · telemetría]');
87
+ if (!u.disponible) {
88
+ console.log(` no disponible — ${u.razon}`);
89
+ } else {
90
+ console.log(` invocaciones de skills: ${u.invocaciones} | top: ${u.top5.join(', ')}`);
91
+ }
92
+ console.log('');
93
+ console.log('Compartir agregados con el autor (opt-in): swl-ses field-report — ver docs/evidencia-valor.md');
94
+ }
95
+
96
+ module.exports = valor;
97
+
98
+ if (require.main === module) {
99
+ const { parsearOpciones } = require('./lib/parsear-opciones.js');
100
+ valor(parsearOpciones(process.argv.slice(2)));
101
+ }
@@ -196,20 +196,20 @@ function generarReporte() {
196
196
  console.log(`\n> Generado por \`node scripts/field-report.js\``);
197
197
  }
198
198
 
199
- function fieldReport(opciones = {}) {
200
- DAYS = parseInt(opciones.days || opciones.dias, 10) || 0;
201
- PROJECT = opciones.project || null;
202
- OUTPUT = opciones.output || 'md';
203
- generarReporte();
204
- }
205
-
206
- module.exports = fieldReport;
207
-
208
- if (require.main === module) {
209
- const args = process.argv.slice(2).reduce((acc, arg) => {
210
- const [k, v] = arg.replace(/^--/, '').split('=');
211
- acc[k] = v || true;
212
- return acc;
213
- }, {});
214
- fieldReport(args);
199
+ function fieldReport(opciones = {}) {
200
+ DAYS = parseInt(opciones.days || opciones.dias, 10) || 0;
201
+ PROJECT = opciones.project || null;
202
+ OUTPUT = opciones.output || 'md';
203
+ generarReporte();
204
+ }
205
+
206
+ module.exports = fieldReport;
207
+
208
+ if (require.main === module) {
209
+ const args = process.argv.slice(2).reduce((acc, arg) => {
210
+ const [k, v] = arg.replace(/^--/, '').split('=');
211
+ acc[k] = v || true;
212
+ return acc;
213
+ }, {});
214
+ fieldReport(args);
215
215
  }