@saulwade/swl-ses 2.6.1 → 2.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/CLAUDE.md +14 -2
  2. package/README.md +65 -18
  3. package/agentes/_intent-spec.md +73 -73
  4. package/agentes/_propose-step.md +90 -90
  5. package/bin/swl-ses.js +10 -0
  6. package/comandos/swl/brainstorm.md +1 -0
  7. package/comandos/swl/briefing.md +119 -119
  8. package/comandos/swl/contribuir.md +233 -233
  9. package/comandos/swl/deuda-codigo.md +97 -97
  10. package/comandos/swl/mcp-status.md +1 -0
  11. package/gateway/lib/event-channel.js +191 -191
  12. package/habilidades/agent-deep-links/SKILL.md +148 -148
  13. package/habilidades/backend-async-postgres-testing/SKILL.md +216 -216
  14. package/habilidades/backend-error-design/SKILL.md +221 -221
  15. package/habilidades/backend-production-resilience/SKILL.md +288 -288
  16. package/habilidades/calidad-anti-patrones-universales/SKILL.md +105 -1
  17. package/habilidades/calidad-contract-testing/SKILL.md +165 -165
  18. package/habilidades/calidad-mutation-testing/SKILL.md +25 -1
  19. package/habilidades/checklist-seguridad/recursos/stride-cobertura.md +60 -60
  20. package/habilidades/ci-cd-pipelines/SKILL.md +5 -1
  21. package/habilidades/css-moderno/SKILL.md +7 -1
  22. package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
  23. package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
  24. package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +57 -57
  25. package/habilidades/extractor-de-aprendizajes/SKILL.md +5 -1
  26. package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -108
  27. package/habilidades/harness-claude-code/SKILL.md +3 -2
  28. package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
  29. package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
  30. package/habilidades/perfil-usuario/SKILL.md +200 -200
  31. package/habilidades/prevencion-sobreingenieria/recursos/EXAMPLES.md +580 -580
  32. package/habilidades/prevencion-sobreingenieria/recursos/soluciones-nativas.md +166 -166
  33. package/habilidades/prevencion-sobreingenieria/recursos/variables-residuales-post-refactor.md +85 -85
  34. package/habilidades/proceso-ddia-streaming/SKILL.md +231 -231
  35. package/habilidades/proceso-discovery-machote/SKILL.md +157 -157
  36. package/habilidades/proceso-dynamic-workflows/SKILL.md +60 -0
  37. package/habilidades/proceso-dynamic-workflows/recursos/template-adversarial-verify.js +65 -65
  38. package/habilidades/proceso-dynamic-workflows/recursos/template-triage.js +65 -65
  39. package/habilidades/proceso-intent-engineering/SKILL.md +269 -269
  40. package/habilidades/proceso-modular-split/SKILL.md +256 -256
  41. package/habilidades/state-inconsistency-auditor-swl/recursos/coupled-state-patterns.md +147 -147
  42. package/habilidades/swl-claudemd/recursos/contrato-aprender.md +83 -83
  43. package/habilidades/swl-claudemd/recursos/duplicacion-reglas-globales.md +85 -85
  44. package/habilidades/swl-claudemd/recursos/plantillas-init.md +94 -94
  45. package/habilidades/tdd-workflow/recursos/gherkin-bdd.md +111 -111
  46. package/hooks/calidad-pre-commit.js +159 -10
  47. package/hooks/ciclo-evolucion-subagente.js +26 -26
  48. package/hooks/ciclo-evolucion.js +26 -26
  49. package/hooks/contexto-subagente.js +68 -68
  50. package/hooks/lib/auto-consolidator.js +335 -335
  51. package/hooks/lib/ciclo-evolucion.js +47 -47
  52. package/hooks/lib/deep-links.js +185 -185
  53. package/hooks/lib/error-classifier.js +308 -308
  54. package/hooks/lib/notificacion-formato.js +45 -11
  55. package/hooks/lib/provenance-tracker.js +191 -191
  56. package/hooks/lib/raiz-proyecto.js +35 -4
  57. package/hooks/lib/resource-quota.js +122 -122
  58. package/hooks/lib/retry-jitter.js +165 -165
  59. package/hooks/lib/security-net.js +201 -201
  60. package/hooks/lib/skill-auditor.js +588 -588
  61. package/hooks/lib/sync-status.js +228 -228
  62. package/hooks/lib/taint-tracker.js +107 -107
  63. package/hooks/lib/text-similarity.js +241 -241
  64. package/hooks/lib/toon-compressor.js +245 -245
  65. package/hooks/notificacion-telegram.js +5 -11
  66. package/hooks/session-briefing.js +12 -4
  67. package/instintos/autonomia.yaml +27 -27
  68. package/instintos/prompt-appendices.yaml +57 -57
  69. package/llms.txt +1 -1
  70. package/manifiestos/agent-output-schemas.json +57 -57
  71. package/manifiestos/canonical-hashes.json +662 -0
  72. package/manifiestos/harness-ir.json +47536 -0
  73. package/manifiestos/hooks-config.json +469 -469
  74. package/manifiestos/invariantes-criticos.json +30 -30
  75. package/manifiestos/policy-bundle.json +2065 -0
  76. package/manifiestos/policy-corpus-w2.json +3926 -0
  77. package/manifiestos/runtime-adapters-core3.json +208 -0
  78. package/manifiestos/runtime-conformance.json +139 -0
  79. package/manifiestos/skills-lock.json +43 -43
  80. package/package.json +2 -2
  81. package/plantillas/auditor-veto-template.md +105 -105
  82. package/plantillas/github-workflows/release-please.yml +44 -44
  83. package/plantillas/github-workflows/swl-ci.yml +107 -107
  84. package/plantillas/github-workflows/swl-security.yml +51 -51
  85. package/plugin.json +2 -2
  86. package/reglas/accesibilidad.md +10 -10
  87. package/reglas/auditorias-documentales-estructurales.md +7 -7
  88. package/reglas/cloud-infra.md +8 -8
  89. package/reglas/consultar-vault-primero.md +195 -195
  90. package/reglas/git-workflow.md +1 -0
  91. package/reglas/hooks.md +6 -6
  92. package/reglas/intent-engineering.md +218 -218
  93. package/reglas/markitdown.md +8 -8
  94. package/reglas/monitor-ci.md +12 -0
  95. package/reglas/patrones.md +6 -6
  96. package/reglas/testing.md +7 -7
  97. package/reglas/tests-cleanup.md +224 -224
  98. package/schemas/agent-message.schema.json +73 -73
  99. package/schemas/agent-output-implementacion.schema.json +114 -114
  100. package/schemas/agent-output-planificacion.schema.json +150 -150
  101. package/schemas/agent-output-review.schema.json +98 -98
  102. package/schemas/diary-entry.schema.json +112 -112
  103. package/schemas/gate-state.schema.json +76 -0
  104. package/schemas/harness-ir.schema.json +369 -0
  105. package/schemas/hook-profiles.schema.json +54 -54
  106. package/schemas/hooks-config.schema.json +89 -89
  107. package/schemas/legacy-gates.schema.json +45 -0
  108. package/schemas/modulos.schema.json +38 -38
  109. package/schemas/perfiles.schema.json +36 -36
  110. package/schemas/plugin.schema.json +77 -77
  111. package/schemas/policy-bundle.schema.json +140 -0
  112. package/schemas/policy-enforcement.schema.json +117 -0
  113. package/schemas/policy-operation.schema.json +261 -0
  114. package/schemas/runtime-adapter.schema.json +176 -0
  115. package/schemas/runtime-build-attestation.schema.json +100 -0
  116. package/schemas/runtime-conformance.schema.json +239 -0
  117. package/schemas/runtime-diagnostic.schema.json +395 -0
  118. package/schemas/skill-evals.schema.json +119 -119
  119. package/schemas/skill-frontmatter.schema.json +245 -245
  120. package/schemas/w4-certification-request.schema.json +72 -0
  121. package/schemas/w4-certification-verdict.schema.json +224 -0
  122. package/schemas/w4-corpus.schema.json +172 -0
  123. package/schemas/w4-mutation-report.schema.json +116 -0
  124. package/schemas/w4-replay-result.schema.json +164 -0
  125. package/schemas/w4-scoring-report.schema.json +89 -0
  126. package/scripts/audit-tools/audit-history.js +330 -330
  127. package/scripts/audit-tools/bundle-tracker.js +290 -290
  128. package/scripts/audit-tools/canary-monitor.js +352 -352
  129. package/scripts/audit-tools/code-profiler.js +605 -605
  130. package/scripts/audit-tools/dep-doctor.js +320 -320
  131. package/scripts/audit-tools/env-validator.js +206 -206
  132. package/scripts/audit-tools/lib/fs-walk.js +48 -48
  133. package/scripts/audit-tools/lib/output.js +23 -23
  134. package/scripts/audit-tools/migration-checker.js +392 -392
  135. package/scripts/audit-tools/pentest-scanner.js +1436 -1436
  136. package/scripts/bootstrap-instintos.js +3 -0
  137. package/scripts/cli/aprobar-plan.js +73 -73
  138. package/scripts/cli/briefing.js +23 -23
  139. package/scripts/cli/ciclo-evolucion.js +26 -26
  140. package/scripts/cli/derivar-feature-list.js +25 -25
  141. package/scripts/cli/detectar-host.js +27 -27
  142. package/scripts/cli/diary-entry.js +69 -69
  143. package/scripts/cli/execution-state.js +18 -18
  144. package/scripts/cli/gateway-notify.js +41 -41
  145. package/scripts/cli/liberar-fase.js +42 -42
  146. package/scripts/cli/mark-evolved.js +56 -56
  147. package/scripts/cli/metricas-dora.js +26 -26
  148. package/scripts/cli/near-duplicate.js +55 -55
  149. package/scripts/cli/notificaciones.js +123 -123
  150. package/scripts/cli/propose-step.js +29 -29
  151. package/scripts/cli/schedule-parse.js +19 -19
  152. package/scripts/cli/sugerir-modelo.js +20 -20
  153. package/scripts/cli/verificar-plan.js +36 -36
  154. package/scripts/cli/verificar-trazabilidad.js +35 -35
  155. package/scripts/comandos/install-asistido.js +8 -7
  156. package/scripts/configurar-branch-protection.js +418 -418
  157. package/scripts/detectar-aprendizajes-duplicados.js +151 -151
  158. package/scripts/doctor.js +61 -36
  159. package/scripts/generar-checklists-consolidados.js +273 -273
  160. package/scripts/generar-claims-runtime.js +1342 -0
  161. package/scripts/generar-harness-ir.js +257 -0
  162. package/scripts/generar-inventario.js +52 -54
  163. package/scripts/generar-policy-bundle.js +202 -0
  164. package/scripts/instalador.js +26 -7
  165. package/scripts/lib/approval-receipts.js +190 -0
  166. package/scripts/lib/artefactos-python.js +43 -43
  167. package/scripts/lib/benchmark-metrics.js +160 -160
  168. package/scripts/lib/budget-enforcer.js +252 -252
  169. package/scripts/lib/certificacion-loop-state.js +421 -0
  170. package/scripts/lib/ci-reader.js +193 -193
  171. package/scripts/lib/ciclo-autonomo/yaml-instintos.js +56 -0
  172. package/scripts/lib/clasificar-directorio.js +92 -0
  173. package/scripts/lib/contadores-inventario.js +217 -217
  174. package/scripts/lib/detectar-host-swl.js +175 -175
  175. package/scripts/lib/detectar-runtime.js +29 -20
  176. package/scripts/lib/detectar-stack-detallado.js +307 -307
  177. package/scripts/lib/detector-autoduplicacion-intra-archivo.js +234 -234
  178. package/scripts/lib/detector-reglas-duplicadas.js +220 -220
  179. package/scripts/lib/eval-metrics-store.js +218 -218
  180. package/scripts/lib/eval-quality.js +171 -171
  181. package/scripts/lib/eval-schemas.js +144 -144
  182. package/scripts/lib/eval-self-correct.js +106 -106
  183. package/scripts/lib/eval-validator.js +185 -185
  184. package/scripts/lib/evidence-verifier.js +192 -0
  185. package/scripts/lib/evidencia-release.js +322 -322
  186. package/scripts/lib/frontmatter-canonico.js +509 -0
  187. package/scripts/lib/gate-engine.js +871 -0
  188. package/scripts/lib/gate-hooks-requires.js +249 -249
  189. package/scripts/lib/gate-licencias.js +212 -212
  190. package/scripts/lib/git-config-preflight.js +48 -0
  191. package/scripts/lib/git-metricas.js +257 -257
  192. package/scripts/lib/harness-ir.js +778 -0
  193. package/scripts/lib/harness-source-snapshot.js +309 -0
  194. package/scripts/lib/integrity-ledger.js +1147 -0
  195. package/scripts/lib/jaccard-similarity.js +98 -98
  196. package/scripts/lib/legacy-gate-migration.js +324 -0
  197. package/scripts/lib/limpiar-basura-global.js +45 -2
  198. package/scripts/lib/longmemeval-runner.js +125 -125
  199. package/scripts/lib/metricas-dora.js +204 -204
  200. package/scripts/lib/notificaciones-telegram.js +1 -0
  201. package/scripts/lib/npm-version.js +1 -0
  202. package/scripts/lib/paquetes-conocidos.js +50 -50
  203. package/scripts/lib/plan-lock.js +61 -13
  204. package/scripts/lib/policy-broker.js +338 -0
  205. package/scripts/lib/policy-bundle.js +342 -0
  206. package/scripts/lib/policy-context-provider.js +310 -0
  207. package/scripts/lib/policy-contract.js +479 -0
  208. package/scripts/lib/policy-verifier-utils.js +65 -0
  209. package/scripts/lib/pr-analyzer.js +399 -399
  210. package/scripts/lib/principal-verifier.js +178 -0
  211. package/scripts/lib/prompt-builder.js +264 -264
  212. package/scripts/lib/resolver-plan-fase.js +37 -37
  213. package/scripts/lib/rrf-fusion.js +175 -175
  214. package/scripts/lib/runtime-adapter-contract.js +267 -0
  215. package/scripts/lib/runtime-artifact-verifier.js +426 -0
  216. package/scripts/lib/runtime-build-attestation.js +127 -0
  217. package/scripts/lib/runtime-bundle-installer.js +586 -0
  218. package/scripts/lib/runtime-compiler.js +327 -0
  219. package/scripts/lib/runtime-conformance.js +202 -0
  220. package/scripts/lib/runtime-doctor-core3.js +567 -0
  221. package/scripts/lib/runtime-doctor-input.js +59 -0
  222. package/scripts/lib/runtime-operation-adapter.js +267 -0
  223. package/scripts/lib/schema-version.js +164 -164
  224. package/scripts/lib/semantic-search.js +252 -252
  225. package/scripts/lib/signed-envelope.js +545 -0
  226. package/scripts/lib/single-use-store.js +359 -0
  227. package/scripts/lib/skills-externas.js +31 -0
  228. package/scripts/lib/transformadores/codex.js +15 -8
  229. package/scripts/lib/transformadores/gemini.js +79 -5
  230. package/scripts/lib/w4-attestation-adapter.js +158 -0
  231. package/scripts/lib/w4-canario.js +337 -0
  232. package/scripts/lib/w4-claims.js +182 -0
  233. package/scripts/lib/w4-corpus-generador.js +542 -0
  234. package/scripts/lib/w4-gate-c5.js +115 -0
  235. package/scripts/lib/w4-harness-bajo-prueba.js +155 -0
  236. package/scripts/lib/w4-matriz-combos.js +55 -0
  237. package/scripts/lib/w4-motor-mutacion.js +1348 -0
  238. package/scripts/lib/w4-motor-replay.js +735 -0
  239. package/scripts/lib/w4-pin-origen.js +54 -0
  240. package/scripts/lib/w4-publicar-request.js +132 -0
  241. package/scripts/lib/w4-revocacion.js +62 -0
  242. package/scripts/lib/w4-runtimes-core3.js +38 -0
  243. package/scripts/lib/w4-scorer-certificacion.js +692 -0
  244. package/scripts/lib/w4-superficie-candidato.js +49 -0
  245. package/scripts/lib/w4-veredicto.js +452 -0
  246. package/scripts/lib/w4-verificar-veredicto.js +302 -0
  247. package/scripts/limpiar-artefactos-python.js +131 -131
  248. package/scripts/migrar-csv-a-array.js +168 -168
  249. package/scripts/migrar-fase-dominio.js +200 -200
  250. package/scripts/migrar-gates-legacy.js +108 -0
  251. package/scripts/publicar-certification-request.js +115 -0
  252. package/scripts/runtime-doctor.js +107 -0
  253. package/scripts/tui/componentes/selector-multi.js +189 -189
  254. package/scripts/tui/componentes/selector-unico.js +158 -158
  255. package/scripts/tui/ejecutores.js +375 -375
  256. package/scripts/tui/lib/colores.js +129 -129
  257. package/scripts/tui/lib/render.js +264 -264
  258. package/scripts/tui/lib/teclas.js +113 -113
  259. package/scripts/tui/pantallas/install-wizard.js +12 -7
  260. package/scripts/tui/pantallas/menu-principal.js +52 -52
  261. package/scripts/tui/pantallas/progreso.js +274 -274
  262. package/scripts/tui/pantallas/resumen.js +132 -132
  263. package/scripts/validar-userland-vacio.js +110 -110
  264. package/scripts/verificar-aislamiento-swl-eval.js +87 -0
  265. package/scripts/verificar-empaquetado-downstream.js +375 -0
  266. package/scripts/verificar-loop-constructor.js +215 -0
  267. package/scripts/verificar-trazabilidad.js +13 -6
  268. package/scripts/verificar-veredicto-real.js +84 -0
@@ -1,151 +1,151 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- /**
5
- * detectar-aprendizajes-duplicados.js
6
- *
7
- * Detecta pares de entradas en `.planning/APRENDIZAJES.md` con alta similitud
8
- * de tokens (Jaccard > umbral). Útil para identificar candidatos a fusionar
9
- * cuando el hook de auto-extracción genera entradas redundantes.
10
- *
11
- * Patrón adoptado de `temp/agentmemory-main/src/functions/auto-forget.ts`
12
- * (contradiction detection con Jaccard >= 0.9). Aquí se usa con threshold
13
- * configurable más bajo (0.6 default) porque queremos sugerir, no auto-borrar.
14
- *
15
- * NO modifica APRENDIZAJES.md. Solo reporta. La acción de fusión queda en
16
- * manos del usuario o de un comando separado (`/swl:aprender consolidar`).
17
- *
18
- * Uso:
19
- * node scripts/detectar-aprendizajes-duplicados.js [threshold]
20
- *
21
- * Argumentos:
22
- * threshold - Similitud mínima para reportar (default: 0.6, rango [0, 1]).
23
- *
24
- * Exit codes:
25
- * 0 - Ejecución OK (haya o no duplicados)
26
- * 1 - Error de I/O o parseo
27
- *
28
- * Output: tabla legible en stdout. Si se detectan ≥ 1 duplicados, también
29
- * imprime sugerencia para revisar/consolidar.
30
- */
31
-
32
- const fs = require('fs');
33
- const path = require('path');
34
-
35
- const { tokenize, jaccard } = require('./lib/jaccard-similarity');
36
-
37
- const RUTA_APRENDIZAJES = path.join(process.cwd(), '.planning', 'APRENDIZAJES.md');
38
- const DEFAULT_THRESHOLD = 0.6;
39
- const MAX_PARES_REPORTADOS = 30;
40
-
41
- function parsearEntradas(contenido) {
42
- const lineas = contenido.split('\n');
43
- const entradas = [];
44
- let actual = null;
45
-
46
- for (let i = 0; i < lineas.length; i++) {
47
- const linea = lineas[i];
48
- if (linea.startsWith('## ')) {
49
- if (actual) entradas.push(actual);
50
- actual = {
51
- lineaInicio: i + 1,
52
- titulo: linea.slice(3).trim(),
53
- contenido: '',
54
- };
55
- } else if (actual) {
56
- actual.contenido += linea + '\n';
57
- }
58
- }
59
- if (actual) entradas.push(actual);
60
-
61
- // Filtrar entradas vacías o triviales (< 50 chars de contenido real)
62
- return entradas.filter(e => e.contenido.replace(/\s/g, '').length >= 50);
63
- }
64
-
65
- function detectarDuplicados(entradas, threshold) {
66
- const tokensCache = entradas.map(e => tokenize(e.titulo + ' ' + e.contenido));
67
- const pares = [];
68
-
69
- for (let i = 0; i < entradas.length; i++) {
70
- for (let j = i + 1; j < entradas.length; j++) {
71
- const sim = jaccard(tokensCache[i], tokensCache[j]);
72
- if (sim >= threshold) {
73
- pares.push({
74
- entradaA: entradas[i],
75
- entradaB: entradas[j],
76
- similitud: sim,
77
- });
78
- }
79
- }
80
- }
81
-
82
- pares.sort((a, b) => b.similitud - a.similitud);
83
- return pares;
84
- }
85
-
86
- function reportarTexto(pares) {
87
- if (pares.length === 0) {
88
- console.log('Sin duplicados detectados sobre el umbral.');
89
- return;
90
- }
91
-
92
- console.log(`Pares con similitud Jaccard ≥ umbral: ${pares.length}`);
93
- console.log('');
94
-
95
- const limite = Math.min(pares.length, MAX_PARES_REPORTADOS);
96
- for (let i = 0; i < limite; i++) {
97
- const p = pares[i];
98
- console.log(` [${(p.similitud * 100).toFixed(1)}%] ` +
99
- `L${p.entradaA.lineaInicio} ↔ L${p.entradaB.lineaInicio}`);
100
- console.log(' A: ' + p.entradaA.titulo.slice(0, 80));
101
- console.log(' B: ' + p.entradaB.titulo.slice(0, 80));
102
- console.log('');
103
- }
104
-
105
- if (pares.length > limite) {
106
- console.log(` ... ${pares.length - limite} pares adicionales no mostrados`);
107
- }
108
-
109
- console.log('Sugerencia: revisa los pares con mayor similitud y considera ' +
110
- 'fusionarlos en una sola entrada con `/swl:aprender consolidar` o manualmente.');
111
- }
112
-
113
- function main() {
114
- const threshold = parseFloat(process.argv[2]) || DEFAULT_THRESHOLD;
115
-
116
- if (!Number.isFinite(threshold) || threshold < 0 || threshold > 1) {
117
- console.error(`Threshold inválido: ${process.argv[2]}. Usar valor en [0, 1].`);
118
- process.exit(1);
119
- }
120
-
121
- if (!fs.existsSync(RUTA_APRENDIZAJES)) {
122
- console.error(`No existe ${RUTA_APRENDIZAJES}.`);
123
- process.exit(1);
124
- }
125
-
126
- let contenido;
127
- try {
128
- contenido = fs.readFileSync(RUTA_APRENDIZAJES, 'utf8');
129
- } catch (err) {
130
- console.error(`Error leyendo ${RUTA_APRENDIZAJES}: ${err.message}`);
131
- process.exit(1);
132
- }
133
-
134
- const entradas = parsearEntradas(contenido);
135
- console.log(`Entradas encontradas: ${entradas.length}`);
136
- console.log(`Threshold de similitud: ${threshold}`);
137
- console.log('');
138
-
139
- const pares = detectarDuplicados(entradas, threshold);
140
- reportarTexto(pares);
141
- }
142
-
143
- if (require.main === module) {
144
- main();
145
- }
146
-
147
- module.exports = {
148
- parsearEntradas,
149
- detectarDuplicados,
150
- reportarTexto,
151
- };
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * detectar-aprendizajes-duplicados.js
6
+ *
7
+ * Detecta pares de entradas en `.planning/APRENDIZAJES.md` con alta similitud
8
+ * de tokens (Jaccard > umbral). Útil para identificar candidatos a fusionar
9
+ * cuando el hook de auto-extracción genera entradas redundantes.
10
+ *
11
+ * Patrón adoptado de `temp/agentmemory-main/src/functions/auto-forget.ts`
12
+ * (contradiction detection con Jaccard >= 0.9). Aquí se usa con threshold
13
+ * configurable más bajo (0.6 default) porque queremos sugerir, no auto-borrar.
14
+ *
15
+ * NO modifica APRENDIZAJES.md. Solo reporta. La acción de fusión queda en
16
+ * manos del usuario o de un comando separado (`/swl:aprender consolidar`).
17
+ *
18
+ * Uso:
19
+ * node scripts/detectar-aprendizajes-duplicados.js [threshold]
20
+ *
21
+ * Argumentos:
22
+ * threshold - Similitud mínima para reportar (default: 0.6, rango [0, 1]).
23
+ *
24
+ * Exit codes:
25
+ * 0 - Ejecución OK (haya o no duplicados)
26
+ * 1 - Error de I/O o parseo
27
+ *
28
+ * Output: tabla legible en stdout. Si se detectan ≥ 1 duplicados, también
29
+ * imprime sugerencia para revisar/consolidar.
30
+ */
31
+
32
+ const fs = require('fs');
33
+ const path = require('path');
34
+
35
+ const { tokenize, jaccard } = require('./lib/jaccard-similarity');
36
+
37
+ const RUTA_APRENDIZAJES = path.join(process.cwd(), '.planning', 'APRENDIZAJES.md');
38
+ const DEFAULT_THRESHOLD = 0.6;
39
+ const MAX_PARES_REPORTADOS = 30;
40
+
41
+ function parsearEntradas(contenido) {
42
+ const lineas = contenido.split('\n');
43
+ const entradas = [];
44
+ let actual = null;
45
+
46
+ for (let i = 0; i < lineas.length; i++) {
47
+ const linea = lineas[i];
48
+ if (linea.startsWith('## ')) {
49
+ if (actual) entradas.push(actual);
50
+ actual = {
51
+ lineaInicio: i + 1,
52
+ titulo: linea.slice(3).trim(),
53
+ contenido: '',
54
+ };
55
+ } else if (actual) {
56
+ actual.contenido += linea + '\n';
57
+ }
58
+ }
59
+ if (actual) entradas.push(actual);
60
+
61
+ // Filtrar entradas vacías o triviales (< 50 chars de contenido real)
62
+ return entradas.filter(e => e.contenido.replace(/\s/g, '').length >= 50);
63
+ }
64
+
65
+ function detectarDuplicados(entradas, threshold) {
66
+ const tokensCache = entradas.map(e => tokenize(e.titulo + ' ' + e.contenido));
67
+ const pares = [];
68
+
69
+ for (let i = 0; i < entradas.length; i++) {
70
+ for (let j = i + 1; j < entradas.length; j++) {
71
+ const sim = jaccard(tokensCache[i], tokensCache[j]);
72
+ if (sim >= threshold) {
73
+ pares.push({
74
+ entradaA: entradas[i],
75
+ entradaB: entradas[j],
76
+ similitud: sim,
77
+ });
78
+ }
79
+ }
80
+ }
81
+
82
+ pares.sort((a, b) => b.similitud - a.similitud);
83
+ return pares;
84
+ }
85
+
86
+ function reportarTexto(pares) {
87
+ if (pares.length === 0) {
88
+ console.log('Sin duplicados detectados sobre el umbral.');
89
+ return;
90
+ }
91
+
92
+ console.log(`Pares con similitud Jaccard ≥ umbral: ${pares.length}`);
93
+ console.log('');
94
+
95
+ const limite = Math.min(pares.length, MAX_PARES_REPORTADOS);
96
+ for (let i = 0; i < limite; i++) {
97
+ const p = pares[i];
98
+ console.log(` [${(p.similitud * 100).toFixed(1)}%] ` +
99
+ `L${p.entradaA.lineaInicio} ↔ L${p.entradaB.lineaInicio}`);
100
+ console.log(' A: ' + p.entradaA.titulo.slice(0, 80));
101
+ console.log(' B: ' + p.entradaB.titulo.slice(0, 80));
102
+ console.log('');
103
+ }
104
+
105
+ if (pares.length > limite) {
106
+ console.log(` ... ${pares.length - limite} pares adicionales no mostrados`);
107
+ }
108
+
109
+ console.log('Sugerencia: revisa los pares con mayor similitud y considera ' +
110
+ 'fusionarlos en una sola entrada con `/swl:aprender consolidar` o manualmente.');
111
+ }
112
+
113
+ function main() {
114
+ const threshold = parseFloat(process.argv[2]) || DEFAULT_THRESHOLD;
115
+
116
+ if (!Number.isFinite(threshold) || threshold < 0 || threshold > 1) {
117
+ console.error(`Threshold inválido: ${process.argv[2]}. Usar valor en [0, 1].`);
118
+ process.exit(1);
119
+ }
120
+
121
+ if (!fs.existsSync(RUTA_APRENDIZAJES)) {
122
+ console.error(`No existe ${RUTA_APRENDIZAJES}.`);
123
+ process.exit(1);
124
+ }
125
+
126
+ let contenido;
127
+ try {
128
+ contenido = fs.readFileSync(RUTA_APRENDIZAJES, 'utf8');
129
+ } catch (err) {
130
+ console.error(`Error leyendo ${RUTA_APRENDIZAJES}: ${err.message}`);
131
+ process.exit(1);
132
+ }
133
+
134
+ const entradas = parsearEntradas(contenido);
135
+ console.log(`Entradas encontradas: ${entradas.length}`);
136
+ console.log(`Threshold de similitud: ${threshold}`);
137
+ console.log('');
138
+
139
+ const pares = detectarDuplicados(entradas, threshold);
140
+ reportarTexto(pares);
141
+ }
142
+
143
+ if (require.main === module) {
144
+ main();
145
+ }
146
+
147
+ module.exports = {
148
+ parsearEntradas,
149
+ detectarDuplicados,
150
+ reportarTexto,
151
+ };
package/scripts/doctor.js CHANGED
@@ -168,41 +168,41 @@ async function doctor(opciones = {}) {
168
168
  advertencias++;
169
169
  }
170
170
 
171
- // C-2: doctor DIAGNOSTICA; no siembra dirs de proyecto (.planning/_userland)
172
- // en un cwd que no es un proyecto swl. Correr `doctor` desde un directorio
173
- // cualquiera no debe generar clutter. Marcadores de proyecto: .planning/ o
174
- // _userland/ ya presentes, o un install-state de scope proyecto.
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 && (
183
- fs.existsSync(path.join(cwd, '.planning')) ||
184
- fs.existsSync(path.join(cwd, '_userland')) ||
185
- fs.existsSync(path.join(cwd, '.swl-install-state.json'))
186
- );
187
-
188
- // 3. Verificar .planning/
171
+ // C-2: doctor DIAGNOSTICA según la CLASE del directorio (clasificar-directorio):
172
+ // - 'home' : ubicación de una instalación global, NUNCA un proyecto. Un
173
+ // ~/.planning (backups del install, scaffold de doctor,
174
+ // telemetría mal dirigida) o ~/_userland vacío es basura
175
+ // OFRECER eliminar, jamás scaffoldear marcadores.
176
+ // - 'proyecto' : proyecto de desarrollo real (.git/stack/.planning)
177
+ // verificar/reparar/inicializar la estructura .planning.
178
+ // - 'nuevo'/'otro' : no scaffoldear a ciegas "No aplica".
179
+ // Origen del rediseño (bug 2026-07-15): `install --global` desde el HOME
180
+ // sembraba ~/.planning/backups y doctor ofrecía scaffoldear marcadores ahí.
181
+ const { clasificarDirectorio } = require('./lib/clasificar-directorio');
182
+ const { esUserlandVacio, esPlanningRemovibleEnHome, limpiarBasuraGlobal } = require('./lib/limpiar-basura-global');
183
+ const claseDir = clasificarDirectorio(cwd);
184
+ const esProyectoSwl = claseDir === 'proyecto'; // consumido también por la sección 4 (_userland)
185
+
186
+ // 3. Verificar .planning/ según la clase del directorio
189
187
  const planningDir = path.join(cwd, '.planning');
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)) {
188
+ if (claseDir === 'home') {
189
+ const basura = [];
190
+ if (esPlanningRemovibleEnHome(cwd)) basura.push('~/.planning');
191
+ if (esUserlandVacio(cwd)) basura.push('~/_userland (vacío)');
192
+ if (basura.length > 0) {
193
+ console.log(formatearAdvertencia('instalación global', `Directorios basura en el HOME: ${basura.join(', ')}el HOME no es un proyecto`));
194
+ advertencias++;
195
+ reparables.push({
196
+ descripcion: `Eliminar del HOME: ${basura.join(', ')}`,
197
+ reparar: () => {
198
+ const r = limpiarBasuraGlobal({ home: cwd });
199
+ return r.eliminados.length ? `Eliminado: ${r.eliminados.join(', ')}` : 'Nada que eliminar';
200
+ },
201
+ });
202
+ } else {
203
+ console.log(formatearPaso('.planning/', 'No aplica — el HOME es la ubicación de instalación global, no un proyecto'));
204
+ }
205
+ } else if (claseDir === 'proyecto' && fs.existsSync(planningDir)) {
206
206
  const archivosEsperados = ['PROYECTO.md', 'REQUISITOS.md', 'HOJA-RUTA.md', 'ESTADO.md'];
207
207
  const faltantes = archivosEsperados.filter(
208
208
  f => !fs.existsSync(path.join(planningDir, f))
@@ -261,9 +261,10 @@ async function doctor(opciones = {}) {
261
261
  });
262
262
  }
263
263
 
264
- // 4. Verificar _userland/
264
+ // 4. Verificar _userland/ (solo relevante en un proyecto de desarrollo; en el
265
+ // HOME un _userland vacío ya se ofreció eliminar en la sección 3).
265
266
  const userlandDir = path.join(cwd, '_userland');
266
- if (fs.existsSync(userlandDir)) {
267
+ if (claseDir === 'proyecto' && fs.existsSync(userlandDir)) {
267
268
  console.log(formatearPaso('_userland/', 'Presente'));
268
269
  ok++;
269
270
 
@@ -694,6 +695,30 @@ async function doctor(opciones = {}) {
694
695
  }
695
696
  }
696
697
 
698
+ // X. Diagnóstico Core-3 (Claude Code, Codex CLI, Gemini CLI) — F31-T07.
699
+ // Puramente informativo: read-only, best-effort, nunca repara ni evalúa
700
+ // canarios conductuales. Disponibilidad != certificación (ADR-0047); por
701
+ // eso no suma a ok/advertencias/errores — un runtime experimental ausente
702
+ // no es un problema de instalación de SWL.
703
+ try {
704
+ const { diagnosticarCore3 } = require('./lib/runtime-doctor-core3');
705
+ const { pathEntriesFromEnv, emptyRuntimeMap } = require('./lib/runtime-doctor-input');
706
+ const diagnosticoCore3 = await diagnosticarCore3({
707
+ now: new Date().toISOString(),
708
+ pathEntries: pathEntriesFromEnv(),
709
+ bundles: emptyRuntimeMap(),
710
+ buildEvents: emptyRuntimeMap(),
711
+ });
712
+ console.log(formatearPaso('Runtimes Core-3', 'experimental/unverified — `swl-ses runtime-doctor --json` para detalle'));
713
+ for (const runtime of diagnosticoCore3.runtimes) {
714
+ const detalle = runtime.availability.status === 'available'
715
+ ? `available v${runtime.availability.version}`
716
+ : runtime.availability.status;
717
+ console.log(` ${color.dim(`${runtime.runtimeId}: ${detalle}`)}`);
718
+ }
719
+ console.log('');
720
+ } catch { /* nunca bloquear el doctor por este check */ }
721
+
697
722
  // Reporte final
698
723
  console.log('');
699
724
  console.log(` ${lineaSeparadora()}`);