@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
@@ -14,18 +14,21 @@
14
14
  */
15
15
 
16
16
  /**
17
- * Resume el último mensaje del asistente para una notificación: una alerta debe
18
- * ser un extracto BREVE y legible, no el volcado crudo del mensaje completo (que
19
- * mete tablas markdown, encabezados y cientos de líneas). Quita el ruido
20
- * estructural de markdown (fences de código, filas de tabla, reglas horizontales)
21
- * y el markdown inline (negritas, itálicas, código, enlaces, viñetas), y trunca a
22
- * `maxChars`.
17
+ * Prepara el último mensaje del asistente para una notificación de Telegram:
18
+ * limpia el ruido de markdown que Telegram no renderiza (fences de código, filas
19
+ * de tabla, reglas horizontales) y el markdown inline (negritas, itálicas,
20
+ * código, enlaces, viñetas), preservando el TEXTO COMPLETO. Solo trunca mensajes
21
+ * ENORMES que no caben en Telegram (~3500 chars el mensaje completo con el
22
+ * wrapper), y en ese caso corta en un límite natural (fin de línea u oración),
23
+ * nunca a media palabra. El default alto evita el corte agresivo previo (400
24
+ * chars) que dejaba mensajes útiles truncados (feedback 2026-07-15).
23
25
  *
24
26
  * @param {string} texto - Mensaje completo del asistente (markdown).
25
- * @param {number} [maxChars=400] - Largo máximo del extracto.
26
- * @returns {string} Extracto limpio y corto en texto plano.
27
+ * @param {number} [maxChars=3300] - Largo máximo del cuerpo (deja margen para el
28
+ * título/labels dentro del límite de 3500 del cliente Telegram).
29
+ * @returns {string} Texto limpio (completo salvo mensajes enormes).
27
30
  */
28
- function resumirParaNotificacion(texto, maxChars = 400) {
31
+ function resumirParaNotificacion(texto, maxChars = 3300) {
29
32
  if (!texto) return '';
30
33
  const limpias = [];
31
34
  let enFence = false;
@@ -52,7 +55,38 @@ function resumirParaNotificacion(texto, maxChars = 400) {
52
55
  }
53
56
  const resumen = limpias.join('\n').replace(/\n{3,}/g, '\n\n').trim();
54
57
  if (resumen.length <= maxChars) return resumen;
55
- return resumen.slice(0, maxChars - 1).trimEnd() + '…';
58
+ // Mensaje enorme: cortar en el último límite natural antes de maxChars.
59
+ const corte = resumen.slice(0, maxChars - 1);
60
+ const limite = Math.max(
61
+ corte.lastIndexOf('\n'),
62
+ corte.lastIndexOf('. '),
63
+ corte.lastIndexOf('! '),
64
+ corte.lastIndexOf('? ')
65
+ );
66
+ const base = limite > (maxChars - 1) * 0.6 ? corte.slice(0, limite + 1) : corte;
67
+ return base.trimEnd() + '…';
56
68
  }
57
69
 
58
- module.exports = { resumirParaNotificacion };
70
+ /**
71
+ * Clasifica el `message` de un evento Notification de Claude Code y devuelve
72
+ * subtítulo + cuerpo EN ESPAÑOL (regla es-MX de brevedad-output). El mensaje
73
+ * nativo de Claude Code viene en inglés ("Claude needs your permission",
74
+ * "Claude is waiting for your input"); volcarlo crudo mezclaba idiomas en la
75
+ * notificación (bug 2026-07-15). Se detecta la categoría por keyword (en/es) y
76
+ * se emite texto es-MX propio, nunca el `message` inglés.
77
+ *
78
+ * @param {string} mensaje - evt.message del evento Notification (puede venir en inglés).
79
+ * @returns {{subtitulo: string, cuerpo: string}} Ambos en español.
80
+ */
81
+ function clasificarNotificacion(mensaje) {
82
+ const m = String(mensaje || '').toLowerCase();
83
+ if (m.includes('permission') || m.includes('permiso')) {
84
+ return { subtitulo: 'Permisos pendientes', cuerpo: 'Claude necesita tu permiso para continuar.' };
85
+ }
86
+ if (m.includes('idle') || m.includes('waiting') || m.includes('esperando')) {
87
+ return { subtitulo: 'Esperando respuesta', cuerpo: 'Claude está esperando tu respuesta.' };
88
+ }
89
+ return { subtitulo: 'Atención requerida', cuerpo: 'Claude requiere tu atención.' };
90
+ }
91
+
92
+ module.exports = { resumirParaNotificacion, clasificarNotificacion };
@@ -1,191 +1,191 @@
1
- 'use strict';
2
-
3
- /**
4
- * Provenance Tracker — Rastreo de proveniencia de skills y componentes.
5
- *
6
- * Registra de dónde viene cada skill/agente instalado: si es del core SWL,
7
- * de un repo externo, de auto-evolución, o de la comunidad. Genera y lee
8
- * archivos meta.json por componente.
9
- *
10
- * Patrón adoptado de skillshare (internal/install/meta.go).
11
- *
12
- * Zero dependencias externas.
13
- *
14
- * Uso:
15
- * const { registrarProveniencia, leerProveniencia, listarProveniencias } = require('./lib/provenance-tracker');
16
- *
17
- * registrarProveniencia('/ruta/skill', {
18
- * origin: 'core',
19
- * repoUrl: 'https://github.com/saulwadeleon/swl-ses',
20
- * version: '5.6.3',
21
- * });
22
- *
23
- * const meta = leerProveniencia('/ruta/skill');
24
- * // { origin: 'core', repoUrl: '...', version: '5.6.3', installedAt: '...' }
25
- *
26
- * @module hooks/lib/provenance-tracker
27
- */
28
-
29
- const fs = require('fs');
30
- const path = require('path');
31
-
32
- // Escritura atómica obligatoria para meta.json (regla CLAUDE.md).
33
- let atomicWriteJSON;
34
- try {
35
- ({ atomicWriteJSON } = require('./atomic-write'));
36
- } catch {
37
- atomicWriteJSON = (p, o) => fs.writeFileSync(p, JSON.stringify(o, null, 2), 'utf8');
38
- }
39
-
40
- // ---------------------------------------------------------------------------
41
- // Constantes
42
- // ---------------------------------------------------------------------------
43
-
44
- const META_FILENAME = 'meta.json';
45
-
46
- /**
47
- * Orígenes válidos de un componente.
48
- * @readonly
49
- */
50
- const ORIGENES = Object.freeze({
51
- CORE: 'core', // Del paquete swl-ses
52
- AUTO_EVOLUTION: 'auto-evolution', // Generado por /swl:evolucionar
53
- IMPORTED: 'imported', // Importado de repo externo
54
- COMMUNITY: 'community', // De skills.sh o similares
55
- USERLAND: 'userland', // Del usuario (_userland/)
56
- ANTHROPIC: 'anthropic', // De anthropics/skills
57
- });
58
-
59
- // ---------------------------------------------------------------------------
60
- // API pública
61
- // ---------------------------------------------------------------------------
62
-
63
- /**
64
- * Registra la proveniencia de un componente instalado.
65
- *
66
- * Crea o actualiza meta.json en el directorio del componente.
67
- *
68
- * @param {string} componentDir - Ruta al directorio del componente
69
- * @param {object} datos
70
- * @param {string} datos.origin - Origen (ver ORIGENES)
71
- * @param {string} [datos.repoUrl] - URL del repo de origen
72
- * @param {string} [datos.commit] - Commit hash del origen
73
- * @param {string} [datos.version] - Versión del sistema al instalar
74
- * @param {string} [datos.author] - Autor o equipo
75
- * @param {string} [datos.note] - Nota libre
76
- */
77
- function registrarProveniencia(componentDir, datos) {
78
- const metaPath = path.join(componentDir, META_FILENAME);
79
-
80
- const meta = {
81
- origin: datos.origin || ORIGENES.CORE,
82
- repoUrl: datos.repoUrl || null,
83
- commit: datos.commit || null,
84
- version: datos.version || null,
85
- author: datos.author || null,
86
- note: datos.note || null,
87
- installedAt: new Date().toISOString(),
88
- installedBy: `swl-ses v${datos.version || 'unknown'}`,
89
- };
90
-
91
- // Si ya existe, preservar datos anteriores y agregar historial
92
- if (fs.existsSync(metaPath)) {
93
- try {
94
- const existing = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
95
- // Mover datos anteriores a historial
96
- if (!existing.history) existing.history = [];
97
- const { history, ...prevData } = existing;
98
- existing.history.push({ ...prevData, replacedAt: meta.installedAt });
99
-
100
- // Actualizar con nuevos datos
101
- Object.assign(existing, meta);
102
- existing.history = existing.history.slice(-5); // Máximo 5 entradas de historial
103
-
104
- atomicWriteJSON(metaPath, existing);
105
- } catch {
106
- // Error leyendo existente — sobrescribir
107
- atomicWriteJSON(metaPath, meta);
108
- }
109
- } else {
110
- atomicWriteJSON(metaPath, meta);
111
- }
112
- }
113
-
114
- /**
115
- * Lee la proveniencia de un componente.
116
- *
117
- * @param {string} componentDir - Ruta al directorio del componente
118
- * @returns {object|null} Datos de proveniencia o null si no existe
119
- */
120
- function leerProveniencia(componentDir) {
121
- const metaPath = path.join(componentDir, META_FILENAME);
122
- if (!fs.existsSync(metaPath)) return null;
123
-
124
- try {
125
- return JSON.parse(fs.readFileSync(metaPath, 'utf8'));
126
- } catch {
127
- return null;
128
- }
129
- }
130
-
131
- /**
132
- * Lista la proveniencia de todos los componentes en un directorio.
133
- *
134
- * @param {string} parentDir - Directorio padre (ej: habilidades/)
135
- * @returns {Array<{ name: string, origin: string, version: string, installedAt: string }>}
136
- */
137
- function listarProveniencias(parentDir) {
138
- if (!fs.existsSync(parentDir)) return [];
139
-
140
- const resultado = [];
141
- const entries = fs.readdirSync(parentDir, { withFileTypes: true });
142
-
143
- for (const entry of entries) {
144
- if (!entry.isDirectory()) continue;
145
- const meta = leerProveniencia(path.join(parentDir, entry.name));
146
- resultado.push({
147
- name: entry.name,
148
- origin: meta ? meta.origin : 'unknown',
149
- version: meta ? meta.version : null,
150
- installedAt: meta ? meta.installedAt : null,
151
- repoUrl: meta ? meta.repoUrl : null,
152
- });
153
- }
154
-
155
- return resultado;
156
- }
157
-
158
- /**
159
- * Genera un resumen de proveniencia por origen.
160
- *
161
- * @param {string} parentDir - Directorio padre
162
- * @returns {{ total: number, byOrigin: Object<string, number>, unknown: number }}
163
- */
164
- function resumenProveniencia(parentDir) {
165
- const lista = listarProveniencias(parentDir);
166
- const byOrigin = {};
167
- let unknown = 0;
168
-
169
- for (const item of lista) {
170
- if (item.origin === 'unknown') {
171
- unknown++;
172
- } else {
173
- byOrigin[item.origin] = (byOrigin[item.origin] || 0) + 1;
174
- }
175
- }
176
-
177
- return { total: lista.length, byOrigin, unknown };
178
- }
179
-
180
- // ---------------------------------------------------------------------------
181
- // Exports
182
- // ---------------------------------------------------------------------------
183
-
184
- module.exports = {
185
- registrarProveniencia,
186
- leerProveniencia,
187
- listarProveniencias,
188
- resumenProveniencia,
189
- ORIGENES,
190
- META_FILENAME,
191
- };
1
+ 'use strict';
2
+
3
+ /**
4
+ * Provenance Tracker — Rastreo de proveniencia de skills y componentes.
5
+ *
6
+ * Registra de dónde viene cada skill/agente instalado: si es del core SWL,
7
+ * de un repo externo, de auto-evolución, o de la comunidad. Genera y lee
8
+ * archivos meta.json por componente.
9
+ *
10
+ * Patrón adoptado de skillshare (internal/install/meta.go).
11
+ *
12
+ * Zero dependencias externas.
13
+ *
14
+ * Uso:
15
+ * const { registrarProveniencia, leerProveniencia, listarProveniencias } = require('./lib/provenance-tracker');
16
+ *
17
+ * registrarProveniencia('/ruta/skill', {
18
+ * origin: 'core',
19
+ * repoUrl: 'https://github.com/saulwadeleon/swl-ses',
20
+ * version: '5.6.3',
21
+ * });
22
+ *
23
+ * const meta = leerProveniencia('/ruta/skill');
24
+ * // { origin: 'core', repoUrl: '...', version: '5.6.3', installedAt: '...' }
25
+ *
26
+ * @module hooks/lib/provenance-tracker
27
+ */
28
+
29
+ const fs = require('fs');
30
+ const path = require('path');
31
+
32
+ // Escritura atómica obligatoria para meta.json (regla CLAUDE.md).
33
+ let atomicWriteJSON;
34
+ try {
35
+ ({ atomicWriteJSON } = require('./atomic-write'));
36
+ } catch {
37
+ atomicWriteJSON = (p, o) => fs.writeFileSync(p, JSON.stringify(o, null, 2), 'utf8');
38
+ }
39
+
40
+ // ---------------------------------------------------------------------------
41
+ // Constantes
42
+ // ---------------------------------------------------------------------------
43
+
44
+ const META_FILENAME = 'meta.json';
45
+
46
+ /**
47
+ * Orígenes válidos de un componente.
48
+ * @readonly
49
+ */
50
+ const ORIGENES = Object.freeze({
51
+ CORE: 'core', // Del paquete swl-ses
52
+ AUTO_EVOLUTION: 'auto-evolution', // Generado por /swl:evolucionar
53
+ IMPORTED: 'imported', // Importado de repo externo
54
+ COMMUNITY: 'community', // De skills.sh o similares
55
+ USERLAND: 'userland', // Del usuario (_userland/)
56
+ ANTHROPIC: 'anthropic', // De anthropics/skills
57
+ });
58
+
59
+ // ---------------------------------------------------------------------------
60
+ // API pública
61
+ // ---------------------------------------------------------------------------
62
+
63
+ /**
64
+ * Registra la proveniencia de un componente instalado.
65
+ *
66
+ * Crea o actualiza meta.json en el directorio del componente.
67
+ *
68
+ * @param {string} componentDir - Ruta al directorio del componente
69
+ * @param {object} datos
70
+ * @param {string} datos.origin - Origen (ver ORIGENES)
71
+ * @param {string} [datos.repoUrl] - URL del repo de origen
72
+ * @param {string} [datos.commit] - Commit hash del origen
73
+ * @param {string} [datos.version] - Versión del sistema al instalar
74
+ * @param {string} [datos.author] - Autor o equipo
75
+ * @param {string} [datos.note] - Nota libre
76
+ */
77
+ function registrarProveniencia(componentDir, datos) {
78
+ const metaPath = path.join(componentDir, META_FILENAME);
79
+
80
+ const meta = {
81
+ origin: datos.origin || ORIGENES.CORE,
82
+ repoUrl: datos.repoUrl || null,
83
+ commit: datos.commit || null,
84
+ version: datos.version || null,
85
+ author: datos.author || null,
86
+ note: datos.note || null,
87
+ installedAt: new Date().toISOString(),
88
+ installedBy: `swl-ses v${datos.version || 'unknown'}`,
89
+ };
90
+
91
+ // Si ya existe, preservar datos anteriores y agregar historial
92
+ if (fs.existsSync(metaPath)) {
93
+ try {
94
+ const existing = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
95
+ // Mover datos anteriores a historial
96
+ if (!existing.history) existing.history = [];
97
+ const { history, ...prevData } = existing;
98
+ existing.history.push({ ...prevData, replacedAt: meta.installedAt });
99
+
100
+ // Actualizar con nuevos datos
101
+ Object.assign(existing, meta);
102
+ existing.history = existing.history.slice(-5); // Máximo 5 entradas de historial
103
+
104
+ atomicWriteJSON(metaPath, existing);
105
+ } catch {
106
+ // Error leyendo existente — sobrescribir
107
+ atomicWriteJSON(metaPath, meta);
108
+ }
109
+ } else {
110
+ atomicWriteJSON(metaPath, meta);
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Lee la proveniencia de un componente.
116
+ *
117
+ * @param {string} componentDir - Ruta al directorio del componente
118
+ * @returns {object|null} Datos de proveniencia o null si no existe
119
+ */
120
+ function leerProveniencia(componentDir) {
121
+ const metaPath = path.join(componentDir, META_FILENAME);
122
+ if (!fs.existsSync(metaPath)) return null;
123
+
124
+ try {
125
+ return JSON.parse(fs.readFileSync(metaPath, 'utf8'));
126
+ } catch {
127
+ return null;
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Lista la proveniencia de todos los componentes en un directorio.
133
+ *
134
+ * @param {string} parentDir - Directorio padre (ej: habilidades/)
135
+ * @returns {Array<{ name: string, origin: string, version: string, installedAt: string }>}
136
+ */
137
+ function listarProveniencias(parentDir) {
138
+ if (!fs.existsSync(parentDir)) return [];
139
+
140
+ const resultado = [];
141
+ const entries = fs.readdirSync(parentDir, { withFileTypes: true });
142
+
143
+ for (const entry of entries) {
144
+ if (!entry.isDirectory()) continue;
145
+ const meta = leerProveniencia(path.join(parentDir, entry.name));
146
+ resultado.push({
147
+ name: entry.name,
148
+ origin: meta ? meta.origin : 'unknown',
149
+ version: meta ? meta.version : null,
150
+ installedAt: meta ? meta.installedAt : null,
151
+ repoUrl: meta ? meta.repoUrl : null,
152
+ });
153
+ }
154
+
155
+ return resultado;
156
+ }
157
+
158
+ /**
159
+ * Genera un resumen de proveniencia por origen.
160
+ *
161
+ * @param {string} parentDir - Directorio padre
162
+ * @returns {{ total: number, byOrigin: Object<string, number>, unknown: number }}
163
+ */
164
+ function resumenProveniencia(parentDir) {
165
+ const lista = listarProveniencias(parentDir);
166
+ const byOrigin = {};
167
+ let unknown = 0;
168
+
169
+ for (const item of lista) {
170
+ if (item.origin === 'unknown') {
171
+ unknown++;
172
+ } else {
173
+ byOrigin[item.origin] = (byOrigin[item.origin] || 0) + 1;
174
+ }
175
+ }
176
+
177
+ return { total: lista.length, byOrigin, unknown };
178
+ }
179
+
180
+ // ---------------------------------------------------------------------------
181
+ // Exports
182
+ // ---------------------------------------------------------------------------
183
+
184
+ module.exports = {
185
+ registrarProveniencia,
186
+ leerProveniencia,
187
+ listarProveniencias,
188
+ resumenProveniencia,
189
+ ORIGENES,
190
+ META_FILENAME,
191
+ };
@@ -87,13 +87,14 @@ function canonicalizar(p) {
87
87
  }
88
88
 
89
89
  /**
90
- * Resuelve la raíz del proyecto a la que deben anclarse las escrituras
91
- * de `.planning/`.
90
+ * Ancla base: asciende desde `desde` buscando la raíz del proyecto (marcadores
91
+ * o `.git`), con frontera del HOME. Ver reglas de resolución en el docstring
92
+ * del módulo. Sin post-proceso de redirección (eso lo hace raizProyecto).
92
93
  *
93
94
  * @param {string} [desde] - Directorio de partida (default: process.cwd()).
94
- * @returns {string} Ruta absoluta de la raíz del proyecto.
95
+ * @returns {string} Ruta absoluta del ancla base.
95
96
  */
96
- function raizProyecto(desde) {
97
+ function _anclaBase(desde) {
97
98
  const inicio = path.resolve(desde || process.cwd());
98
99
  let actual = inicio;
99
100
  let homedirCanon = null;
@@ -124,4 +125,34 @@ function raizProyecto(desde) {
124
125
  return inicio;
125
126
  }
126
127
 
128
+ /**
129
+ * Resuelve la raíz del proyecto a la que deben anclarse las escrituras
130
+ * de `.planning/`.
131
+ *
132
+ * Redirección de telemetría (fix 2026-07-15): si el ancla resuelta es el HOME
133
+ * del usuario y el HOME NO es un proyecto SWL real (solo un `.git` de dotfiles
134
+ * o un fallback sin proyecto), NINGÚN hook debe sembrar `.planning/` en la raíz
135
+ * del HOME — se contiene en `~/.claude/` (mismo scope que los backups del
136
+ * install global, A3). Esto arregla los 33 hooks que anclan vía esta función
137
+ * en un solo punto, sin tocarlos. Un proyecto SWL real EN el HOME (con
138
+ * marcadores de proyecto) se respeta.
139
+ *
140
+ * @param {string} [desde] - Directorio de partida (default: process.cwd()).
141
+ * @returns {string} Ruta absoluta de la raíz del proyecto (o ~/.claude si el
142
+ * HOME no es un proyecto).
143
+ */
144
+ function raizProyecto(desde) {
145
+ const raiz = _anclaBase(desde);
146
+ let homedirCanon = null;
147
+ try {
148
+ homedirCanon = canonicalizar(path.resolve(os.homedir()));
149
+ } catch (_) {
150
+ return raiz; // sin home resolvible — no redirigir
151
+ }
152
+ if (canonicalizar(raiz) === homedirCanon && !tienePlanningReal(raiz)) {
153
+ try { return path.join(os.homedir(), '.claude'); } catch (_) { return raiz; }
154
+ }
155
+ return raiz;
156
+ }
157
+
127
158
  module.exports = { raizProyecto, tienePlanningReal, canonicalizar };