@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.
- package/CLAUDE.md +14 -2
- package/README.md +65 -18
- package/agentes/_intent-spec.md +73 -73
- package/agentes/_propose-step.md +90 -90
- package/bin/swl-ses.js +10 -0
- package/comandos/swl/brainstorm.md +1 -0
- package/comandos/swl/briefing.md +119 -119
- package/comandos/swl/contribuir.md +233 -233
- package/comandos/swl/deuda-codigo.md +97 -97
- package/comandos/swl/mcp-status.md +1 -0
- package/gateway/lib/event-channel.js +191 -191
- package/habilidades/agent-deep-links/SKILL.md +148 -148
- package/habilidades/backend-async-postgres-testing/SKILL.md +216 -216
- package/habilidades/backend-error-design/SKILL.md +221 -221
- package/habilidades/backend-production-resilience/SKILL.md +288 -288
- package/habilidades/calidad-anti-patrones-universales/SKILL.md +105 -1
- package/habilidades/calidad-contract-testing/SKILL.md +165 -165
- package/habilidades/calidad-mutation-testing/SKILL.md +25 -1
- package/habilidades/checklist-seguridad/recursos/stride-cobertura.md +60 -60
- package/habilidades/ci-cd-pipelines/SKILL.md +5 -1
- package/habilidades/css-moderno/SKILL.md +7 -1
- package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
- package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
- package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +57 -57
- package/habilidades/extractor-de-aprendizajes/SKILL.md +5 -1
- package/habilidades/feynman-auditor-swl/recursos/preguntas-language-agnostic.md +108 -108
- package/habilidades/harness-claude-code/SKILL.md +3 -2
- package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
- package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
- package/habilidades/perfil-usuario/SKILL.md +200 -200
- package/habilidades/prevencion-sobreingenieria/recursos/EXAMPLES.md +580 -580
- package/habilidades/prevencion-sobreingenieria/recursos/soluciones-nativas.md +166 -166
- package/habilidades/prevencion-sobreingenieria/recursos/variables-residuales-post-refactor.md +85 -85
- package/habilidades/proceso-ddia-streaming/SKILL.md +231 -231
- package/habilidades/proceso-discovery-machote/SKILL.md +157 -157
- package/habilidades/proceso-dynamic-workflows/SKILL.md +60 -0
- package/habilidades/proceso-dynamic-workflows/recursos/template-adversarial-verify.js +65 -65
- package/habilidades/proceso-dynamic-workflows/recursos/template-triage.js +65 -65
- package/habilidades/proceso-intent-engineering/SKILL.md +269 -269
- package/habilidades/proceso-modular-split/SKILL.md +256 -256
- package/habilidades/state-inconsistency-auditor-swl/recursos/coupled-state-patterns.md +147 -147
- package/habilidades/swl-claudemd/recursos/contrato-aprender.md +83 -83
- package/habilidades/swl-claudemd/recursos/duplicacion-reglas-globales.md +85 -85
- package/habilidades/swl-claudemd/recursos/plantillas-init.md +94 -94
- package/habilidades/tdd-workflow/recursos/gherkin-bdd.md +111 -111
- package/hooks/calidad-pre-commit.js +159 -10
- package/hooks/ciclo-evolucion-subagente.js +26 -26
- package/hooks/ciclo-evolucion.js +26 -26
- package/hooks/contexto-subagente.js +68 -68
- package/hooks/lib/auto-consolidator.js +335 -335
- package/hooks/lib/ciclo-evolucion.js +47 -47
- package/hooks/lib/deep-links.js +185 -185
- package/hooks/lib/error-classifier.js +308 -308
- package/hooks/lib/notificacion-formato.js +45 -11
- package/hooks/lib/provenance-tracker.js +191 -191
- package/hooks/lib/raiz-proyecto.js +35 -4
- package/hooks/lib/resource-quota.js +122 -122
- package/hooks/lib/retry-jitter.js +165 -165
- package/hooks/lib/security-net.js +201 -201
- package/hooks/lib/skill-auditor.js +588 -588
- package/hooks/lib/sync-status.js +228 -228
- package/hooks/lib/taint-tracker.js +107 -107
- package/hooks/lib/text-similarity.js +241 -241
- package/hooks/lib/toon-compressor.js +245 -245
- package/hooks/notificacion-telegram.js +5 -11
- package/hooks/session-briefing.js +12 -4
- package/instintos/autonomia.yaml +27 -27
- package/instintos/prompt-appendices.yaml +57 -57
- package/llms.txt +1 -1
- package/manifiestos/agent-output-schemas.json +57 -57
- package/manifiestos/canonical-hashes.json +662 -0
- package/manifiestos/harness-ir.json +47536 -0
- package/manifiestos/hooks-config.json +469 -469
- package/manifiestos/invariantes-criticos.json +30 -30
- package/manifiestos/policy-bundle.json +2065 -0
- package/manifiestos/policy-corpus-w2.json +3926 -0
- package/manifiestos/runtime-adapters-core3.json +208 -0
- package/manifiestos/runtime-conformance.json +139 -0
- package/manifiestos/skills-lock.json +43 -43
- package/package.json +2 -2
- package/plantillas/auditor-veto-template.md +105 -105
- package/plantillas/github-workflows/release-please.yml +44 -44
- package/plantillas/github-workflows/swl-ci.yml +107 -107
- package/plantillas/github-workflows/swl-security.yml +51 -51
- package/plugin.json +2 -2
- package/reglas/accesibilidad.md +10 -10
- package/reglas/auditorias-documentales-estructurales.md +7 -7
- package/reglas/cloud-infra.md +8 -8
- package/reglas/consultar-vault-primero.md +195 -195
- package/reglas/git-workflow.md +1 -0
- package/reglas/hooks.md +6 -6
- package/reglas/intent-engineering.md +218 -218
- package/reglas/markitdown.md +8 -8
- package/reglas/monitor-ci.md +12 -0
- package/reglas/patrones.md +6 -6
- package/reglas/testing.md +7 -7
- package/reglas/tests-cleanup.md +224 -224
- package/schemas/agent-message.schema.json +73 -73
- package/schemas/agent-output-implementacion.schema.json +114 -114
- package/schemas/agent-output-planificacion.schema.json +150 -150
- package/schemas/agent-output-review.schema.json +98 -98
- package/schemas/diary-entry.schema.json +112 -112
- package/schemas/gate-state.schema.json +76 -0
- package/schemas/harness-ir.schema.json +369 -0
- package/schemas/hook-profiles.schema.json +54 -54
- package/schemas/hooks-config.schema.json +89 -89
- package/schemas/legacy-gates.schema.json +45 -0
- package/schemas/modulos.schema.json +38 -38
- package/schemas/perfiles.schema.json +36 -36
- package/schemas/plugin.schema.json +77 -77
- package/schemas/policy-bundle.schema.json +140 -0
- package/schemas/policy-enforcement.schema.json +117 -0
- package/schemas/policy-operation.schema.json +261 -0
- package/schemas/runtime-adapter.schema.json +176 -0
- package/schemas/runtime-build-attestation.schema.json +100 -0
- package/schemas/runtime-conformance.schema.json +239 -0
- package/schemas/runtime-diagnostic.schema.json +395 -0
- package/schemas/skill-evals.schema.json +119 -119
- package/schemas/skill-frontmatter.schema.json +245 -245
- package/schemas/w4-certification-request.schema.json +72 -0
- package/schemas/w4-certification-verdict.schema.json +224 -0
- package/schemas/w4-corpus.schema.json +172 -0
- package/schemas/w4-mutation-report.schema.json +116 -0
- package/schemas/w4-replay-result.schema.json +164 -0
- package/schemas/w4-scoring-report.schema.json +89 -0
- package/scripts/audit-tools/audit-history.js +330 -330
- package/scripts/audit-tools/bundle-tracker.js +290 -290
- package/scripts/audit-tools/canary-monitor.js +352 -352
- package/scripts/audit-tools/code-profiler.js +605 -605
- package/scripts/audit-tools/dep-doctor.js +320 -320
- package/scripts/audit-tools/env-validator.js +206 -206
- package/scripts/audit-tools/lib/fs-walk.js +48 -48
- package/scripts/audit-tools/lib/output.js +23 -23
- package/scripts/audit-tools/migration-checker.js +392 -392
- package/scripts/audit-tools/pentest-scanner.js +1436 -1436
- package/scripts/bootstrap-instintos.js +3 -0
- package/scripts/cli/aprobar-plan.js +73 -73
- package/scripts/cli/briefing.js +23 -23
- package/scripts/cli/ciclo-evolucion.js +26 -26
- package/scripts/cli/derivar-feature-list.js +25 -25
- package/scripts/cli/detectar-host.js +27 -27
- package/scripts/cli/diary-entry.js +69 -69
- package/scripts/cli/execution-state.js +18 -18
- package/scripts/cli/gateway-notify.js +41 -41
- package/scripts/cli/liberar-fase.js +42 -42
- package/scripts/cli/mark-evolved.js +56 -56
- package/scripts/cli/metricas-dora.js +26 -26
- package/scripts/cli/near-duplicate.js +55 -55
- package/scripts/cli/notificaciones.js +123 -123
- package/scripts/cli/propose-step.js +29 -29
- package/scripts/cli/schedule-parse.js +19 -19
- package/scripts/cli/sugerir-modelo.js +20 -20
- package/scripts/cli/verificar-plan.js +36 -36
- package/scripts/cli/verificar-trazabilidad.js +35 -35
- package/scripts/comandos/install-asistido.js +8 -7
- package/scripts/configurar-branch-protection.js +418 -418
- package/scripts/detectar-aprendizajes-duplicados.js +151 -151
- package/scripts/doctor.js +61 -36
- package/scripts/generar-checklists-consolidados.js +273 -273
- package/scripts/generar-claims-runtime.js +1342 -0
- package/scripts/generar-harness-ir.js +257 -0
- package/scripts/generar-inventario.js +52 -54
- package/scripts/generar-policy-bundle.js +202 -0
- package/scripts/instalador.js +26 -7
- package/scripts/lib/approval-receipts.js +190 -0
- package/scripts/lib/artefactos-python.js +43 -43
- package/scripts/lib/benchmark-metrics.js +160 -160
- package/scripts/lib/budget-enforcer.js +252 -252
- package/scripts/lib/certificacion-loop-state.js +421 -0
- package/scripts/lib/ci-reader.js +193 -193
- package/scripts/lib/ciclo-autonomo/yaml-instintos.js +56 -0
- package/scripts/lib/clasificar-directorio.js +92 -0
- package/scripts/lib/contadores-inventario.js +217 -217
- package/scripts/lib/detectar-host-swl.js +175 -175
- package/scripts/lib/detectar-runtime.js +29 -20
- package/scripts/lib/detectar-stack-detallado.js +307 -307
- package/scripts/lib/detector-autoduplicacion-intra-archivo.js +234 -234
- package/scripts/lib/detector-reglas-duplicadas.js +220 -220
- package/scripts/lib/eval-metrics-store.js +218 -218
- package/scripts/lib/eval-quality.js +171 -171
- package/scripts/lib/eval-schemas.js +144 -144
- package/scripts/lib/eval-self-correct.js +106 -106
- package/scripts/lib/eval-validator.js +185 -185
- package/scripts/lib/evidence-verifier.js +192 -0
- package/scripts/lib/evidencia-release.js +322 -322
- package/scripts/lib/frontmatter-canonico.js +509 -0
- package/scripts/lib/gate-engine.js +871 -0
- package/scripts/lib/gate-hooks-requires.js +249 -249
- package/scripts/lib/gate-licencias.js +212 -212
- package/scripts/lib/git-config-preflight.js +48 -0
- package/scripts/lib/git-metricas.js +257 -257
- package/scripts/lib/harness-ir.js +778 -0
- package/scripts/lib/harness-source-snapshot.js +309 -0
- package/scripts/lib/integrity-ledger.js +1147 -0
- package/scripts/lib/jaccard-similarity.js +98 -98
- package/scripts/lib/legacy-gate-migration.js +324 -0
- package/scripts/lib/limpiar-basura-global.js +45 -2
- package/scripts/lib/longmemeval-runner.js +125 -125
- package/scripts/lib/metricas-dora.js +204 -204
- package/scripts/lib/notificaciones-telegram.js +1 -0
- package/scripts/lib/npm-version.js +1 -0
- package/scripts/lib/paquetes-conocidos.js +50 -50
- package/scripts/lib/plan-lock.js +61 -13
- package/scripts/lib/policy-broker.js +338 -0
- package/scripts/lib/policy-bundle.js +342 -0
- package/scripts/lib/policy-context-provider.js +310 -0
- package/scripts/lib/policy-contract.js +479 -0
- package/scripts/lib/policy-verifier-utils.js +65 -0
- package/scripts/lib/pr-analyzer.js +399 -399
- package/scripts/lib/principal-verifier.js +178 -0
- package/scripts/lib/prompt-builder.js +264 -264
- package/scripts/lib/resolver-plan-fase.js +37 -37
- package/scripts/lib/rrf-fusion.js +175 -175
- package/scripts/lib/runtime-adapter-contract.js +267 -0
- package/scripts/lib/runtime-artifact-verifier.js +426 -0
- package/scripts/lib/runtime-build-attestation.js +127 -0
- package/scripts/lib/runtime-bundle-installer.js +586 -0
- package/scripts/lib/runtime-compiler.js +327 -0
- package/scripts/lib/runtime-conformance.js +202 -0
- package/scripts/lib/runtime-doctor-core3.js +567 -0
- package/scripts/lib/runtime-doctor-input.js +59 -0
- package/scripts/lib/runtime-operation-adapter.js +267 -0
- package/scripts/lib/schema-version.js +164 -164
- package/scripts/lib/semantic-search.js +252 -252
- package/scripts/lib/signed-envelope.js +545 -0
- package/scripts/lib/single-use-store.js +359 -0
- package/scripts/lib/skills-externas.js +31 -0
- package/scripts/lib/transformadores/codex.js +15 -8
- package/scripts/lib/transformadores/gemini.js +79 -5
- package/scripts/lib/w4-attestation-adapter.js +158 -0
- package/scripts/lib/w4-canario.js +337 -0
- package/scripts/lib/w4-claims.js +182 -0
- package/scripts/lib/w4-corpus-generador.js +542 -0
- package/scripts/lib/w4-gate-c5.js +115 -0
- package/scripts/lib/w4-harness-bajo-prueba.js +155 -0
- package/scripts/lib/w4-matriz-combos.js +55 -0
- package/scripts/lib/w4-motor-mutacion.js +1348 -0
- package/scripts/lib/w4-motor-replay.js +735 -0
- package/scripts/lib/w4-pin-origen.js +54 -0
- package/scripts/lib/w4-publicar-request.js +132 -0
- package/scripts/lib/w4-revocacion.js +62 -0
- package/scripts/lib/w4-runtimes-core3.js +38 -0
- package/scripts/lib/w4-scorer-certificacion.js +692 -0
- package/scripts/lib/w4-superficie-candidato.js +49 -0
- package/scripts/lib/w4-veredicto.js +452 -0
- package/scripts/lib/w4-verificar-veredicto.js +302 -0
- package/scripts/limpiar-artefactos-python.js +131 -131
- package/scripts/migrar-csv-a-array.js +168 -168
- package/scripts/migrar-fase-dominio.js +200 -200
- package/scripts/migrar-gates-legacy.js +108 -0
- package/scripts/publicar-certification-request.js +115 -0
- package/scripts/runtime-doctor.js +107 -0
- package/scripts/tui/componentes/selector-multi.js +189 -189
- package/scripts/tui/componentes/selector-unico.js +158 -158
- package/scripts/tui/ejecutores.js +375 -375
- package/scripts/tui/lib/colores.js +129 -129
- package/scripts/tui/lib/render.js +264 -264
- package/scripts/tui/lib/teclas.js +113 -113
- package/scripts/tui/pantallas/install-wizard.js +12 -7
- package/scripts/tui/pantallas/menu-principal.js +52 -52
- package/scripts/tui/pantallas/progreso.js +274 -274
- package/scripts/tui/pantallas/resumen.js +132 -132
- package/scripts/validar-userland-vacio.js +110 -110
- package/scripts/verificar-aislamiento-swl-eval.js +87 -0
- package/scripts/verificar-empaquetado-downstream.js +375 -0
- package/scripts/verificar-loop-constructor.js +215 -0
- package/scripts/verificar-trazabilidad.js +13 -6
- package/scripts/verificar-veredicto-real.js +84 -0
|
@@ -14,18 +14,21 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
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=
|
|
26
|
-
*
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
91
|
-
* de
|
|
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
|
|
95
|
+
* @returns {string} Ruta absoluta del ancla base.
|
|
95
96
|
*/
|
|
96
|
-
function
|
|
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 };
|