@saulwade/swl-ses 1.0.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 +238 -0
- package/README.md +560 -0
- package/_userland/agentes/.gitkeep +0 -0
- package/_userland/habilidades/.gitkeep +0 -0
- package/agentes/.evolved.json +9 -0
- package/agentes/accesibilidad-wcag-swl.md +692 -0
- package/agentes/arquitecto-swl.md +238 -0
- package/agentes/auto-evolucion-swl.md +854 -0
- package/agentes/backend-api-swl.md +470 -0
- package/agentes/backend-csharp-swl.md +418 -0
- package/agentes/backend-go-swl.md +388 -0
- package/agentes/backend-java-swl.md +279 -0
- package/agentes/backend-node-swl.md +477 -0
- package/agentes/backend-python-swl.md +608 -0
- package/agentes/backend-rust-swl.md +362 -0
- package/agentes/backend-workers-swl.md +480 -0
- package/agentes/cloud-infra-swl.md +485 -0
- package/agentes/consolidador-swl.md +539 -0
- package/agentes/datos-swl.md +584 -0
- package/agentes/depurador-swl.md +349 -0
- package/agentes/devops-ci-swl.md +374 -0
- package/agentes/disenador-ui-swl.md +558 -0
- package/agentes/documentador-swl.md +343 -0
- package/agentes/evals/arquitecto-swl.evals.json +56 -0
- package/agentes/evals/auto-evolucion-swl.evals.json +68 -0
- package/agentes/evals/implementador-swl.evals.json +56 -0
- package/agentes/evals/orquestador-swl.evals.json +60 -0
- package/agentes/evals/perfilador-usuario-swl.evals.json +60 -0
- package/agentes/evals/red-team-swl.evals.json +59 -0
- package/agentes/evals/revisor-codigo-swl.evals.json +59 -0
- package/agentes/frontend-angular-swl.md +627 -0
- package/agentes/frontend-css-swl.md +720 -0
- package/agentes/frontend-react-swl.md +696 -0
- package/agentes/frontend-swl.md +500 -0
- package/agentes/frontend-tailwind-swl.md +830 -0
- package/agentes/implementador-swl.md +328 -0
- package/agentes/investigador-swl.md +430 -0
- package/agentes/investigador-ux-swl.md +500 -0
- package/agentes/llm-apps-swl.md +276 -0
- package/agentes/migrador-swl.md +417 -0
- package/agentes/mobile-android-swl.md +509 -0
- package/agentes/mobile-cross-swl.md +539 -0
- package/agentes/mobile-ios-swl.md +500 -0
- package/agentes/mobile-testing-swl.md +300 -0
- package/agentes/notificador-swl.md +916 -0
- package/agentes/observabilidad-swl.md +436 -0
- package/agentes/orquestador-swl.md +884 -0
- package/agentes/pagos-swl.md +283 -0
- package/agentes/perfilador-usuario-swl.md +306 -0
- package/agentes/planificador-swl.md +402 -0
- package/agentes/producto-prd-swl.md +587 -0
- package/agentes/red-team-swl.md +216 -0
- package/agentes/release-manager-swl.md +568 -0
- package/agentes/rendimiento-swl.md +714 -0
- package/agentes/resolutor-build-swl.md +243 -0
- package/agentes/revisor-angular-swl.md +276 -0
- package/agentes/revisor-codigo-swl.md +348 -0
- package/agentes/revisor-csharp-swl.md +262 -0
- package/agentes/revisor-go-swl.md +257 -0
- package/agentes/revisor-java-swl.md +255 -0
- package/agentes/revisor-kotlin-swl.md +271 -0
- package/agentes/revisor-nextjs-swl.md +279 -0
- package/agentes/revisor-php-swl.md +269 -0
- package/agentes/revisor-react-swl.md +276 -0
- package/agentes/revisor-rust-swl.md +344 -0
- package/agentes/revisor-seguridad-swl.md +390 -0
- package/agentes/revisor-swift-swl.md +266 -0
- package/agentes/revisor-typescript-swl.md +344 -0
- package/agentes/sre-swl.md +265 -0
- package/agentes/tdd-qa-swl.md +354 -0
- package/agentes/ux-disenador-swl.md +501 -0
- package/bin/lib/bot-comandos.js +1030 -0
- package/bin/lib/bot-discovery.js +182 -0
- package/bin/lib/bot-git.js +142 -0
- package/bin/swl-ses.js +325 -0
- package/bin/swl-telegram-bot.js +442 -0
- package/bin/swl-telegram-bot.plist +21 -0
- package/bin/swl-telegram-bot.service +14 -0
- package/comandos/swl/.evolved.json +23 -0
- package/comandos/swl/actualizar.md +174 -0
- package/comandos/swl/adoptar-proyecto.md +207 -0
- package/comandos/swl/aprender.md +701 -0
- package/comandos/swl/auditar-deps.md +134 -0
- package/comandos/swl/autoresearch.md +170 -0
- package/comandos/swl/ayuda.md +224 -0
- package/comandos/swl/brainstorm.md +50 -0
- package/comandos/swl/checkpoint.md +330 -0
- package/comandos/swl/compactar.md +283 -0
- package/comandos/swl/configurar-ci.md +227 -0
- package/comandos/swl/contexto.md +112 -0
- package/comandos/swl/contribuir.md +233 -0
- package/comandos/swl/crear-skill.md +292 -0
- package/comandos/swl/cron.md +196 -0
- package/comandos/swl/dashboard.md +146 -0
- package/comandos/swl/discutir-fase.md +230 -0
- package/comandos/swl/ejecutar-fase.md +135 -0
- package/comandos/swl/evaluar-skill.md +487 -0
- package/comandos/swl/evolucion-estado.md +142 -0
- package/comandos/swl/evolucionar.md +259 -0
- package/comandos/swl/exportar-vault.md +189 -0
- package/comandos/swl/gateway.md +158 -0
- package/comandos/swl/inbox.md +116 -0
- package/comandos/swl/instalar.md +220 -0
- package/comandos/swl/instintos.md +86 -0
- package/comandos/swl/mapear-codebase.md +312 -0
- package/comandos/swl/mcp-status.md +175 -0
- package/comandos/swl/metricas.md +270 -0
- package/comandos/swl/modelo.md +102 -0
- package/comandos/swl/notificaciones.md +396 -0
- package/comandos/swl/nuevo-proyecto.md +154 -0
- package/comandos/swl/planear-fase.md +221 -0
- package/comandos/swl/plugins.md +256 -0
- package/comandos/swl/reflect-skills.md +125 -0
- package/comandos/swl/release.md +217 -0
- package/comandos/swl/revisar-impacto.md +206 -0
- package/comandos/swl/revisar.md +330 -0
- package/comandos/swl/salud.md +363 -0
- package/comandos/swl/sesiones.md +200 -0
- package/comandos/swl/skill-search.md +113 -0
- package/comandos/swl/verificar.md +585 -0
- package/comandos/swl/wiki.md +620 -0
- package/contextos/dev.md +32 -0
- package/contextos/research.md +30 -0
- package/contextos/review.md +31 -0
- package/habilidades/accesibilidad-a11y/SKILL.md +201 -0
- package/habilidades/accesibilidad-a11y/evals/evals.json +56 -0
- package/habilidades/accesibilidad-a11y/recursos/ejemplos-y-checklist-completo.md +441 -0
- package/habilidades/agent-browser/SKILL.md +218 -0
- package/habilidades/agentes-como-servicio/SKILL.md +218 -0
- package/habilidades/ai-runtime-security/SKILL.md +273 -0
- package/habilidades/angular-avanzado/SKILL.md +164 -0
- package/habilidades/angular-avanzado/recursos/ejemplos-avanzados.md +219 -0
- package/habilidades/angular-moderno/SKILL.md +186 -0
- package/habilidades/angular-moderno/evals/evals.json +45 -0
- package/habilidades/angular-moderno/recursos/ejemplos-avanzados.md +106 -0
- package/habilidades/api-rest-diseno/SKILL.md +191 -0
- package/habilidades/api-rest-diseno/recursos/openapi-template.yaml +506 -0
- package/habilidades/api-rest-diseno/recursos/referencia-api.md +140 -0
- package/habilidades/aprendizaje-continuo/SKILL.md +151 -0
- package/habilidades/aprendizaje-continuo/evals/evals.json +53 -0
- package/habilidades/aprendizaje-continuo/recursos/referencia-instintos.md +290 -0
- package/habilidades/async-python/SKILL.md +149 -0
- package/habilidades/async-python/evals/evals.json +47 -0
- package/habilidades/async-python/recursos/patrones-y-ejemplos-completos.md +292 -0
- package/habilidades/auth-patrones/.evolved.json +9 -0
- package/habilidades/auth-patrones/SKILL.md +413 -0
- package/habilidades/auth-patrones/recursos/implementaciones-completas.md +229 -0
- package/habilidades/auto-evolucion-protocolo/SKILL.md +276 -0
- package/habilidades/auto-evolucion-protocolo/evals/evals.json +55 -0
- package/habilidades/auto-evolucion-protocolo/recursos/referencia-completa.md +145 -0
- package/habilidades/autoresearch/SKILL.md +268 -0
- package/habilidades/autoresearch/evals/evals.json +41 -0
- package/habilidades/autoresearch/recursos/checklist-template.md +191 -0
- package/habilidades/autoresearch/scripts/calcular-score.js +88 -0
- package/habilidades/azure-cloud/SKILL.md +308 -0
- package/habilidades/azure-cloud/recursos/aks.md +327 -0
- package/habilidades/backend-mcp-servidor/SKILL.md +270 -0
- package/habilidades/backend-production-resilience/SKILL.md +288 -0
- package/habilidades/brainstorming/SKILL.md +295 -0
- package/habilidades/brainstorming/recursos/componentes-html.md +247 -0
- package/habilidades/build-errors-cpp/SKILL.md +270 -0
- package/habilidades/build-errors-csharp/SKILL.md +265 -0
- package/habilidades/build-errors-go/SKILL.md +306 -0
- package/habilidades/build-errors-java/SKILL.md +278 -0
- package/habilidades/build-errors-kotlin/SKILL.md +303 -0
- package/habilidades/build-errors-nextjs/SKILL.md +312 -0
- package/habilidades/build-errors-php/SKILL.md +270 -0
- package/habilidades/build-errors-python/SKILL.md +292 -0
- package/habilidades/build-errors-rust/SKILL.md +284 -0
- package/habilidades/build-errors-swift/SKILL.md +272 -0
- package/habilidades/build-errors-typescript/SKILL.md +369 -0
- package/habilidades/checklist-calidad/SKILL.md +271 -0
- package/habilidades/checklist-calidad/recursos/quality-report-template.md +148 -0
- package/habilidades/checklist-seguridad/SKILL.md +285 -0
- package/habilidades/checkpoints-verificacion/SKILL.md +298 -0
- package/habilidades/checkpoints-verificacion/recursos/checkpoint-templates.md +360 -0
- package/habilidades/ci-cd-pipelines/SKILL.md +157 -0
- package/habilidades/ci-cd-pipelines/recursos/github-actions-template.yaml +403 -0
- package/habilidades/ci-cd-pipelines/recursos/pipelines-completos.md +487 -0
- package/habilidades/cloud-aws/SKILL.md +142 -0
- package/habilidades/cloud-aws/recursos/servicios-aws-referencia.md +321 -0
- package/habilidades/compactacion-contexto/SKILL.md +247 -0
- package/habilidades/contenedores-docker/SKILL.md +137 -0
- package/habilidades/contenedores-docker/recursos/dockerfile-template.dockerfile +160 -0
- package/habilidades/contenedores-docker/recursos/ejemplos-y-configuraciones.md +327 -0
- package/habilidades/context-builder/SKILL.md +170 -0
- package/habilidades/control-profundidad/SKILL.md +128 -0
- package/habilidades/csharp-experto/SKILL.md +322 -0
- package/habilidades/csharp-patrones/SKILL.md +316 -0
- package/habilidades/csharp-testing/SKILL.md +286 -0
- package/habilidades/css-moderno/SKILL.md +166 -0
- package/habilidades/css-moderno/evals/evals.json +43 -0
- package/habilidades/css-moderno/recursos/ejemplos-y-patrones-completos.md +337 -0
- package/habilidades/datos-etl/SKILL.md +129 -0
- package/habilidades/datos-etl/recursos/implementaciones-completas.md +322 -0
- package/habilidades/dbml-experto/SKILL.md +339 -0
- package/habilidades/dbml-experto/evals/evals.json +56 -0
- package/habilidades/dependencias-auditoria/SKILL.md +320 -0
- package/habilidades/deprecacion-migracion/SKILL.md +169 -0
- package/habilidades/deprecacion-migracion/recursos/implementaciones-completas.md +220 -0
- package/habilidades/design-tokens/SKILL.md +158 -0
- package/habilidades/design-tokens/recursos/tokens-y-configuracion.md +363 -0
- package/habilidades/devsecops-pipeline-security/SKILL.md +309 -0
- package/habilidades/diagrama-arquitectura/SKILL.md +165 -0
- package/habilidades/diagrama-arquitectura/assets/template.html +276 -0
- package/habilidades/discutir-fase/SKILL.md +188 -0
- package/habilidades/diseno-herramientas-agente/SKILL.md +199 -0
- package/habilidades/diseno-responsivo/SKILL.md +186 -0
- package/habilidades/diseno-responsivo/recursos/ejemplos-layouts.md +156 -0
- package/habilidades/django-experto/SKILL.md +205 -0
- package/habilidades/django-experto/recursos/async-django.md +390 -0
- package/habilidades/django-experto/recursos/drf-patrones.md +438 -0
- package/habilidades/django-experto/recursos/orm-avanzado.md +382 -0
- package/habilidades/django-experto/recursos/referencia-completa.md +188 -0
- package/habilidades/django-experto/recursos/testing-django.md +415 -0
- package/habilidades/doc-sync/SKILL.md +280 -0
- package/habilidades/drift-detection/SKILL.md +179 -0
- package/habilidades/ejecutar-fase/SKILL.md +468 -0
- package/habilidades/estilo-sin-ai-isms/SKILL.md +775 -0
- package/habilidades/estilo-sin-ai-isms/evals/evals.json +63 -0
- package/habilidades/estilo-sin-ai-isms/scripts/detectar_aiisms.py +500 -0
- package/habilidades/estructura-proyecto-claude/SKILL.md +215 -0
- package/habilidades/estructura-proyecto-claude/recursos/claude-md-template.md +261 -0
- package/habilidades/estructura-proyecto-claude/recursos/configuracion-y-extensiones.md +176 -0
- package/habilidades/estructura-proyecto-claude/recursos/frontmatter-y-hooks-referencia.md +289 -0
- package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +77 -0
- package/habilidades/estructura-proyecto-claude/recursos/variantes-por-stack.md +177 -0
- package/habilidades/evaluacion-agentes/SKILL.md +314 -0
- package/habilidades/event-driven/SKILL.md +153 -0
- package/habilidades/event-driven/recursos/implementaciones-completas.md +423 -0
- package/habilidades/extraccion-documentos/SKILL.md +221 -0
- package/habilidades/extractor-de-aprendizajes/.evolved.json +9 -0
- package/habilidades/extractor-de-aprendizajes/SKILL.md +311 -0
- package/habilidades/extractor-de-aprendizajes/evals/evals.json +55 -0
- package/habilidades/fastapi-experto/SKILL.md +221 -0
- package/habilidades/fastapi-experto/recursos/async-patterns.md +438 -0
- package/habilidades/fastapi-experto/recursos/dependency-injection.md +330 -0
- package/habilidades/fastapi-experto/recursos/referencia-completa.md +79 -0
- package/habilidades/fastapi-experto/recursos/testing-httpx.md +420 -0
- package/habilidades/filament-admin/SKILL.md +290 -0
- package/habilidades/frontend-avanzado/SKILL.md +257 -0
- package/habilidades/frontend-avanzado/recursos/apis-nativas-ejemplos.md +341 -0
- package/habilidades/gcp-cloud/SKILL.md +260 -0
- package/habilidades/gcp-cloud/recursos/gke.md +234 -0
- package/habilidades/gcp-cloud/recursos/terraform-gcp.md +307 -0
- package/habilidades/generacion-mermaid/SKILL.md +229 -0
- package/habilidades/git-worktrees-paralelo/SKILL.md +270 -0
- package/habilidades/go-experto/SKILL.md +305 -0
- package/habilidades/go-patrones/SKILL.md +299 -0
- package/habilidades/go-testing/SKILL.md +291 -0
- package/habilidades/graphql-experto/SKILL.md +323 -0
- package/habilidades/guardrail-semantico/SKILL.md +282 -0
- package/habilidades/harness-claude-code/SKILL.md +299 -0
- package/habilidades/iam-secretos/SKILL.md +265 -0
- package/habilidades/iam-secretos/recursos/implementaciones-completas.md +356 -0
- package/habilidades/infra-github-actions/SKILL.md +166 -0
- package/habilidades/instalar-sistema/.evolved.json +9 -0
- package/habilidades/instalar-sistema/SKILL.md +221 -0
- package/habilidades/java-experto/SKILL.md +290 -0
- package/habilidades/java-patrones/SKILL.md +275 -0
- package/habilidades/java-testing/SKILL.md +288 -0
- package/habilidades/kotlin-compose/SKILL.md +278 -0
- package/habilidades/kotlin-compose/recursos/animaciones-performance.md +93 -0
- package/habilidades/kotlin-experto/SKILL.md +318 -0
- package/habilidades/kotlin-testing/SKILL.md +267 -0
- package/habilidades/kotlin-testing/recursos/testing-avanzado.md +74 -0
- package/habilidades/kubernetes-orquestacion/SKILL.md +152 -0
- package/habilidades/kubernetes-orquestacion/recursos/manifiestos-completos.md +452 -0
- package/habilidades/langchain-langraph/SKILL.md +386 -0
- package/habilidades/langchain-langraph/recursos/evaluacion-rag.md +321 -0
- package/habilidades/langchain-langraph/recursos/rag-maturity-model.md +225 -0
- package/habilidades/langchain-langraph/recursos/vectorstores.md +306 -0
- package/habilidades/legacy-code-rescue/SKILL.md +267 -0
- package/habilidades/likec4-experto/SKILL.md +412 -0
- package/habilidades/likec4-experto/evals/evals.json +69 -0
- package/habilidades/manejo-errores/.evolved.json +9 -0
- package/habilidades/manejo-errores/SKILL.md +407 -0
- package/habilidades/manejo-errores/recursos/implementaciones-completas.md +248 -0
- package/habilidades/mapear-codebase/SKILL.md +275 -0
- package/habilidades/memoria-busqueda/SKILL.md +194 -0
- package/habilidades/memoria-busqueda/evals/evals.json +44 -0
- package/habilidades/meta-skills-estandar/SKILL.md +298 -0
- package/habilidades/meta-skills-estandar/recursos/anti-patrones-y-leyes.md +205 -0
- package/habilidades/meta-skills-estandar/recursos/frameworks-seguridad.md +107 -0
- package/habilidades/meta-skills-estandar/recursos/idiomas-framework.md +60 -0
- package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -0
- package/habilidades/microservicios/SKILL.md +155 -0
- package/habilidades/microservicios/recursos/patrones-y-ejemplos-completos.md +325 -0
- package/habilidades/mobile-flutter/SKILL.md +199 -0
- package/habilidades/mobile-flutter/recursos/ejemplos-completos.md +319 -0
- package/habilidades/mobile-react-native/SKILL.md +176 -0
- package/habilidades/mobile-react-native/recursos/ejemplos-completos.md +216 -0
- package/habilidades/mongodb-experto/SKILL.md +302 -0
- package/habilidades/monitoring-alertas/SKILL.md +201 -0
- package/habilidades/monitoring-alertas/recursos/instrumentacion-y-alertas.md +301 -0
- package/habilidades/nestjs-experto/SKILL.md +307 -0
- package/habilidades/nestjs-experto/recursos/guards-interceptors.md +339 -0
- package/habilidades/nestjs-experto/recursos/modulos-di.md +287 -0
- package/habilidades/nestjs-experto/recursos/testing-nestjs.md +354 -0
- package/habilidades/nextjs-experto/SKILL.md +335 -0
- package/habilidades/nextjs-patrones/SKILL.md +303 -0
- package/habilidades/nextjs-testing/SKILL.md +331 -0
- package/habilidades/node-experto/.evolved.json +9 -0
- package/habilidades/node-experto/SKILL.md +266 -0
- package/habilidades/node-experto/recursos/patrones-completos.md +283 -0
- package/habilidades/notificaciones-multicanal/SKILL.md +159 -0
- package/habilidades/notificaciones-multicanal/recursos/config-template.json +115 -0
- package/habilidades/notificaciones-multicanal/recursos/configuracion-y-templates.md +303 -0
- package/habilidades/nuevo-proyecto/SKILL.md +204 -0
- package/habilidades/orquestacion-async/SKILL.md +303 -0
- package/habilidades/paid-media-tracking/SKILL.md +269 -0
- package/habilidades/paid-media-tracking/recursos/auditoria-tracking.md +220 -0
- package/habilidades/paid-media-tracking/recursos/google-ads-api.md +215 -0
- package/habilidades/patrones-python/SKILL.md +228 -0
- package/habilidades/patrones-python/evals/evals.json +56 -0
- package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -0
- package/habilidades/patrones-python/recursos/referencia-completa.md +202 -0
- package/habilidades/perfil-usuario/SKILL.md +200 -0
- package/habilidades/perfil-usuario/evals/evals.json +55 -0
- package/habilidades/performance-baseline/SKILL.md +297 -0
- package/habilidades/php-experto/SKILL.md +291 -0
- package/habilidades/php-patrones/SKILL.md +306 -0
- package/habilidades/php-testing/SKILL.md +280 -0
- package/habilidades/planear-fase/SKILL.md +269 -0
- package/habilidades/postgresql-experto/SKILL.md +151 -0
- package/habilidades/postgresql-experto/evals/evals.json +53 -0
- package/habilidades/postgresql-experto/recursos/referencia-completa.md +215 -0
- package/habilidades/prevencion-racionalizacion/SKILL.md +175 -0
- package/habilidades/prevencion-sobreingenieria/SKILL.md +323 -0
- package/habilidades/privacy-memoria/SKILL.md +141 -0
- package/habilidades/privacy-memoria/evals/evals.json +43 -0
- package/habilidades/prompt-engineering/SKILL.md +518 -0
- package/habilidades/prompt-engineering/recursos/patrones-avanzados.md +467 -0
- package/habilidades/rag-arquitectura/SKILL.md +338 -0
- package/habilidades/rails-experto/SKILL.md +237 -0
- package/habilidades/rails-experto/recursos/active-record.md +260 -0
- package/habilidades/rails-experto/recursos/hotwire-turbo.md +293 -0
- package/habilidades/rails-experto/recursos/testing-rspec.md +362 -0
- package/habilidades/react-experto/SKILL.md +209 -0
- package/habilidades/react-experto/evals/evals.json +55 -0
- package/habilidades/react-experto/recursos/patrones-y-ejemplos-completos.md +240 -0
- package/habilidades/react-optimizacion/SKILL.md +174 -0
- package/habilidades/react-optimizacion/recursos/patrones-avanzados.md +138 -0
- package/habilidades/redis-experto/SKILL.md +305 -0
- package/habilidades/release-semver/.evolved.json +9 -0
- package/habilidades/release-semver/SKILL.md +248 -0
- package/habilidades/release-semver/scripts/generar-changelog.sh +238 -0
- package/habilidades/rust-experto/SKILL.md +400 -0
- package/habilidades/rust-patrones/SKILL.md +296 -0
- package/habilidades/rust-testing/SKILL.md +311 -0
- package/habilidades/seguridad-skills-ia/SKILL.md +262 -0
- package/habilidades/sql-optimizacion/SKILL.md +200 -0
- package/habilidades/sql-optimizacion/evals/evals.json +54 -0
- package/habilidades/sql-optimizacion/recursos/patrones-sql-avanzados.md +131 -0
- package/habilidades/sre-patrones/SKILL.md +333 -0
- package/habilidades/sre-patrones/recursos/chaos-engineering.md +241 -0
- package/habilidades/sre-patrones/recursos/oncall-design.md +236 -0
- package/habilidades/stripe-pagos/SKILL.md +550 -0
- package/habilidades/stripe-pagos/recursos/errores-reintentos.md +390 -0
- package/habilidades/stripe-pagos/recursos/stripe-connect.md +290 -0
- package/habilidades/structured-outputs/SKILL.md +343 -0
- package/habilidades/swift-experto/SKILL.md +320 -0
- package/habilidades/swift-experto/recursos/keychain-y-wrappers.md +110 -0
- package/habilidades/swift-patrones/SKILL.md +313 -0
- package/habilidades/swift-patrones/recursos/tca-ejemplo-completo.md +113 -0
- package/habilidades/swift-testing/SKILL.md +254 -0
- package/habilidades/swift-testing/recursos/xcuitest-planes.md +143 -0
- package/habilidades/swl-dashboard/SKILL.md +370 -0
- package/habilidades/swl-markitdown/SKILL.md +285 -0
- package/habilidades/swl-markitdown/evals/evals.json +52 -0
- package/habilidades/swl-revisar-impacto/SKILL.md +233 -0
- package/habilidades/tailwind-experto/SKILL.md +240 -0
- package/habilidades/tailwind-experto/recursos/referencia-completa.md +184 -0
- package/habilidades/tdd-workflow/SKILL.md +293 -0
- package/habilidades/terraform-experto/SKILL.md +321 -0
- package/habilidades/testing-python/SKILL.md +340 -0
- package/habilidades/testing-python/recursos/ejemplos-completos.md +167 -0
- package/habilidades/threat-model-lite/SKILL.md +246 -0
- package/habilidades/tracing-processor/SKILL.md +212 -0
- package/habilidades/tracking-measurement/SKILL.md +239 -0
- package/habilidades/tracking-measurement/recursos/consent-mode.md +231 -0
- package/habilidades/tracking-measurement/recursos/gtm-datalayer.md +216 -0
- package/habilidades/tracking-measurement/recursos/meta-capi.md +262 -0
- package/habilidades/typescript-avanzado/SKILL.md +144 -0
- package/habilidades/typescript-avanzado/evals/evals.json +55 -0
- package/habilidades/typescript-avanzado/recursos/patrones-y-ejemplos-completos.md +298 -0
- package/habilidades/typescript-diagnosticos/SKILL.md +513 -0
- package/habilidades/ux-diseno/SKILL.md +116 -0
- package/habilidades/ux-diseno/evals/evals.json +43 -0
- package/habilidades/ux-diseno/recursos/patrones-ux-referencia.md +214 -0
- package/habilidades/validacion-ci-sistema/SKILL.md +136 -0
- package/habilidades/validacion-ci-sistema/recursos/validadores-completos.md +369 -0
- package/habilidades/validacion-ci-sistema/scripts/validar-sistema.sh +286 -0
- package/habilidades/verificacion-evidencia/SKILL.md +160 -0
- package/habilidades/verificar-trabajo/SKILL.md +303 -0
- package/habilidades/verificar-trabajo/recursos/plantilla-verificacion.md +60 -0
- package/habilidades/wiki-conocimiento/SKILL.md +276 -0
- package/habilidades/wireframes-flujos/SKILL.md +212 -0
- package/habilidades/wireframes-flujos/recursos/referencia-completa.md +192 -0
- package/habilidades/workflow-claude-code/SKILL.md +260 -0
- package/habilidades/workflow-claude-code/recursos/referencia-completa.md +109 -0
- package/hooks/_run-hook.sh +57 -0
- package/hooks/actualizar-perfil-usuario.js +364 -0
- package/hooks/agente-lifecycle.js +71 -0
- package/hooks/aiisms-detector.js +173 -0
- package/hooks/audit-trail.js +204 -0
- package/hooks/auto-background.js +97 -0
- package/hooks/auto-consolidacion.js +178 -0
- package/hooks/auto-evolucion.js +666 -0
- package/hooks/auto-restaurar-settings.js +360 -0
- package/hooks/calidad-pre-commit.js +929 -0
- package/hooks/calidad-typescript.js +511 -0
- package/hooks/captura-feedback-usuario.js +148 -0
- package/hooks/check-update.js +211 -0
- package/hooks/clasificador-mensajes.js +271 -0
- package/hooks/degradacion-instintos.js +272 -0
- package/hooks/escaneo-secretos.js +389 -0
- package/hooks/extraccion-aprendizajes.js +763 -0
- package/hooks/grafo-contexto.js +129 -0
- package/hooks/graph-update.js +67 -0
- package/hooks/guardrail-modelo.js +247 -0
- package/hooks/inbox-aviso.js +75 -0
- package/hooks/inyeccion-contexto.js +246 -0
- package/hooks/lib/abort-registry.js +214 -0
- package/hooks/lib/agent-backend.js +210 -0
- package/hooks/lib/agent-comms.js +263 -0
- package/hooks/lib/agent-issue-codes.js +284 -0
- package/hooks/lib/agent-matcher.js +189 -0
- package/hooks/lib/async-hook-registry.js +252 -0
- package/hooks/lib/atomic-write.js +130 -0
- package/hooks/lib/auto-consolidator.js +335 -0
- package/hooks/lib/canary-skills.js +187 -0
- package/hooks/lib/consolidation-lock.js +291 -0
- package/hooks/lib/context-builder.js +430 -0
- package/hooks/lib/context-compressor.js +657 -0
- package/hooks/lib/convergence-detector.js +105 -0
- package/hooks/lib/delegation-tracker.js +198 -0
- package/hooks/lib/detectar-package-manager.js +423 -0
- package/hooks/lib/edit-accumulator.js +171 -0
- package/hooks/lib/error-classifier.js +308 -0
- package/hooks/lib/event-bus.js +112 -0
- package/hooks/lib/evolution-tracker.js +442 -0
- package/hooks/lib/execution-state.js +316 -0
- package/hooks/lib/fingerprint-id.js +135 -0
- package/hooks/lib/gateway-notify.js +116 -0
- package/hooks/lib/graph-security.js +75 -0
- package/hooks/lib/guardrail-metrics.js +202 -0
- package/hooks/lib/hook-circuit-breaker.js +206 -0
- package/hooks/lib/loop-detector.js +267 -0
- package/hooks/lib/mcp-health.js +184 -0
- package/hooks/lib/mcp-pool.js +436 -0
- package/hooks/lib/memory-search.js +506 -0
- package/hooks/lib/merkle-audit.js +96 -0
- package/hooks/lib/model-router.js +222 -0
- package/hooks/lib/normalize-error.js +324 -0
- package/hooks/lib/normalize-input.js +65 -0
- package/hooks/lib/nudge-tracker.js +306 -0
- package/hooks/lib/otlp-exporter.js +365 -0
- package/hooks/lib/performance-marks.js +239 -0
- package/hooks/lib/privacy-filter.js +128 -0
- package/hooks/lib/prompt-injection-scanner.js +209 -0
- package/hooks/lib/provenance-tracker.js +183 -0
- package/hooks/lib/rate-limit-tracker.js +253 -0
- package/hooks/lib/reflect-classifier.js +164 -0
- package/hooks/lib/resource-quota.js +122 -0
- package/hooks/lib/retry-jitter.js +165 -0
- package/hooks/lib/risk-engine.js +368 -0
- package/hooks/lib/run-log.js +408 -0
- package/hooks/lib/session-fts.js +379 -0
- package/hooks/lib/session-store.js +293 -0
- package/hooks/lib/singleton-guard.js +159 -0
- package/hooks/lib/skill-auditor.js +588 -0
- package/hooks/lib/sync-status.js +228 -0
- package/hooks/lib/taint-tracker.js +107 -0
- package/hooks/lib/task-service.js +295 -0
- package/hooks/lib/tech-skills-map.js +146 -0
- package/hooks/lib/telegram-cliente.js +159 -0
- package/hooks/lib/telegram-config.js +170 -0
- package/hooks/lib/token-budget.js +156 -0
- package/hooks/lib/token-estimator.js +420 -0
- package/hooks/lib/toon-compressor.js +245 -0
- package/hooks/lib/usage-model.js +183 -0
- package/hooks/lib/variable-resolver.js +230 -0
- package/hooks/linea-estado.js +324 -0
- package/hooks/metricas-evolucion.js +209 -0
- package/hooks/monitor-contexto.js +325 -0
- package/hooks/notificacion-sesion-stop.js +198 -0
- package/hooks/notificacion-telegram-notification.js +4 -0
- package/hooks/notificacion-telegram-subagent.js +4 -0
- package/hooks/notificacion-telegram.js +267 -0
- package/hooks/preservar-estado-pre-compact.js +150 -0
- package/hooks/proteccion-rutas.js +366 -0
- package/hooks/registro-turnos.js +209 -0
- package/hooks/resumen-sesion.js +249 -0
- package/hooks/risk-scoring.js +323 -0
- package/hooks/rotar-audit-auto.js +122 -0
- package/hooks/sugerir-regenerar-inventario.js +170 -0
- package/hooks/telemetria-agentes.js +167 -0
- package/hooks/tracking-costos.js +688 -0
- package/instintos/global.yaml +8 -0
- package/instintos/perfil-usuario.yaml +53 -0
- package/instintos/prompt-appendices.yaml +57 -0
- package/instintos/proyecto.yaml +372 -0
- package/manifiestos/gateway-config.json +77 -0
- package/manifiestos/handoff-context.json +223 -0
- package/manifiestos/hook-profiles.json +44 -0
- package/manifiestos/hooks-config.json +360 -0
- package/manifiestos/modulos.json +1173 -0
- package/manifiestos/perfiles.json +404 -0
- package/package.json +86 -0
- package/plantillas/ESTADO.md +109 -0
- package/plantillas/HOJA-RUTA.md +143 -0
- package/plantillas/PROYECTO.md +122 -0
- package/plantillas/REQUISITOS.md +132 -0
- package/plantillas/auditor-veto-template.md +105 -0
- package/plantillas/github-workflows/README.md +47 -0
- package/plantillas/github-workflows/release-please.yml +44 -0
- package/plantillas/github-workflows/swl-ci.yml +107 -0
- package/plantillas/github-workflows/swl-security.yml +51 -0
- package/plantillas/mcp-mineru.json +13 -0
- package/plantillas/research/ARQUITECTURA.md +220 -0
- package/plantillas/research/FUNCIONALIDADES.md +175 -0
- package/plantillas/research/RESUMEN.md +165 -0
- package/plantillas/research/STACK.md +233 -0
- package/plantillas/research/TRAMPAS.md +299 -0
- package/plantillas/skill-evals-template.json +44 -0
- package/plugin.json +343 -0
- package/reglas/accesibilidad.md +269 -0
- package/reglas/api-diseno.md +400 -0
- package/reglas/arquitectura.md +352 -0
- package/reglas/brevedad-output.md +124 -0
- package/reglas/cloud-infra.md +247 -0
- package/reglas/docs.md +245 -0
- package/reglas/estilo-codigo.md +201 -0
- package/reglas/git-workflow.md +245 -0
- package/reglas/gobernanza.md +271 -0
- package/reglas/harness-claude-code.md +213 -0
- package/reglas/hooks.md +186 -0
- package/reglas/lenguajes/csharp/estilo-codigo.md +231 -0
- package/reglas/lenguajes/csharp/hooks.md +281 -0
- package/reglas/lenguajes/csharp/patrones.md +226 -0
- package/reglas/lenguajes/csharp/seguridad.md +258 -0
- package/reglas/lenguajes/csharp/testing.md +176 -0
- package/reglas/lenguajes/go/estilo-codigo.md +195 -0
- package/reglas/lenguajes/go/hooks.md +249 -0
- package/reglas/lenguajes/go/patrones.md +249 -0
- package/reglas/lenguajes/go/seguridad.md +225 -0
- package/reglas/lenguajes/go/testing.md +272 -0
- package/reglas/lenguajes/java/estilo-codigo.md +217 -0
- package/reglas/lenguajes/java/hooks.md +251 -0
- package/reglas/lenguajes/java/patrones.md +226 -0
- package/reglas/lenguajes/java/seguridad.md +233 -0
- package/reglas/lenguajes/java/testing.md +238 -0
- package/reglas/lenguajes/kotlin/estilo-codigo.md +208 -0
- package/reglas/lenguajes/kotlin/hooks.md +245 -0
- package/reglas/lenguajes/kotlin/patrones.md +201 -0
- package/reglas/lenguajes/kotlin/seguridad.md +202 -0
- package/reglas/lenguajes/kotlin/testing.md +236 -0
- package/reglas/lenguajes/nextjs/estilo-codigo.md +175 -0
- package/reglas/lenguajes/nextjs/hooks.md +186 -0
- package/reglas/lenguajes/nextjs/patrones.md +225 -0
- package/reglas/lenguajes/nextjs/seguridad.md +216 -0
- package/reglas/lenguajes/nextjs/testing.md +193 -0
- package/reglas/lenguajes/php/estilo-codigo.md +228 -0
- package/reglas/lenguajes/php/hooks.md +165 -0
- package/reglas/lenguajes/php/patrones.md +233 -0
- package/reglas/lenguajes/php/seguridad.md +186 -0
- package/reglas/lenguajes/php/testing.md +205 -0
- package/reglas/lenguajes/rust/estilo-codigo.md +207 -0
- package/reglas/lenguajes/rust/hooks.md +240 -0
- package/reglas/lenguajes/rust/patrones.md +250 -0
- package/reglas/lenguajes/rust/seguridad.md +221 -0
- package/reglas/lenguajes/rust/testing.md +194 -0
- package/reglas/lenguajes/swift/estilo-codigo.md +238 -0
- package/reglas/lenguajes/swift/hooks.md +257 -0
- package/reglas/lenguajes/swift/patrones.md +235 -0
- package/reglas/lenguajes/swift/seguridad.md +248 -0
- package/reglas/lenguajes/swift/testing.md +242 -0
- package/reglas/markitdown.md +60 -0
- package/reglas/memoria-consolidada.md +209 -0
- package/reglas/patrones.md +225 -0
- package/reglas/performance.md +195 -0
- package/reglas/pruebas.md +159 -0
- package/reglas/seguridad-agentes.md +351 -0
- package/reglas/seguridad.md +151 -0
- package/reglas/skills-estandar.md +373 -0
- package/reglas/testing.md +193 -0
- package/schemas/agent-contract.json +176 -0
- package/schemas/agent-frontmatter.schema.json +149 -0
- package/schemas/agent-message.schema.json +53 -0
- package/schemas/agent-output-implementacion.schema.json +85 -0
- package/schemas/agent-output-planificacion.schema.json +113 -0
- package/schemas/agent-output-review.schema.json +78 -0
- package/schemas/diary-entry.schema.json +80 -0
- package/schemas/hook-profiles.schema.json +39 -0
- package/schemas/hooks-config.schema.json +74 -0
- package/schemas/instinct.schema.json +115 -0
- package/schemas/modulos.schema.json +29 -0
- package/schemas/perfiles.schema.json +28 -0
- package/schemas/plugin.schema.json +64 -0
- package/schemas/skill-evals.schema.json +95 -0
- package/schemas/skill-frontmatter.schema.json +170 -0
- package/scripts/actualizar.js +145 -0
- package/scripts/audit-skills.sh +78 -0
- package/scripts/auditar-agentes-gaps.js +149 -0
- package/scripts/auditar-cobertura-frameworks.js +241 -0
- package/scripts/auditar-skills-gaps.js +206 -0
- package/scripts/bootstrap-instintos.js +259 -0
- package/scripts/check-update.js +109 -0
- package/scripts/comandos/agents.js +105 -0
- package/scripts/comandos/info.js +108 -0
- package/scripts/comandos/install-asistido.js +186 -0
- package/scripts/comandos/skills.js +211 -0
- package/scripts/configurar-branch-protection.js +418 -0
- package/scripts/daemon-swl.py +388 -0
- package/scripts/desinstalar.js +130 -0
- package/scripts/doctor.js +559 -0
- package/scripts/field-report.js +199 -0
- package/scripts/generar-inventario.js +317 -0
- package/scripts/inbox-tmux-inject.js +161 -0
- package/scripts/inferir-herramientas-permitidas.js +586 -0
- package/scripts/inicializar.js +133 -0
- package/scripts/instalador.js +1031 -0
- package/scripts/instalar-git-hook.js +122 -0
- package/scripts/lib/agp-frontmatter.js +222 -0
- package/scripts/lib/append-con-marcadores.js +199 -0
- package/scripts/lib/artefactos-python.js +43 -0
- package/scripts/lib/audit-query.js +221 -0
- package/scripts/lib/autostart-linux.js +347 -0
- package/scripts/lib/autostart-macos.js +360 -0
- package/scripts/lib/autostart-windows.js +307 -0
- package/scripts/lib/budget-enforcer.js +252 -0
- package/scripts/lib/claude-sessions.js +285 -0
- package/scripts/lib/configurar-ci.js +380 -0
- package/scripts/lib/console-span-exporter.js +92 -0
- package/scripts/lib/contadores-inventario.js +217 -0
- package/scripts/lib/dashboard-widgets.js +290 -0
- package/scripts/lib/detectar-runtime.js +279 -0
- package/scripts/lib/detectar-stack.js +187 -0
- package/scripts/lib/diary-entry.js +234 -0
- package/scripts/lib/drift-detector.js +545 -0
- package/scripts/lib/estado.js +124 -0
- package/scripts/lib/gestor-componentes.js +243 -0
- package/scripts/lib/gitignore-manifest.js +305 -0
- package/scripts/lib/graph-analyze.py +556 -0
- package/scripts/lib/graph-builder.py +485 -0
- package/scripts/lib/graph-cluster.py +259 -0
- package/scripts/lib/health-row.js +168 -0
- package/scripts/lib/hooks-settings.js +789 -0
- package/scripts/lib/manifiestos.js +138 -0
- package/scripts/lib/mc-client.js +137 -0
- package/scripts/lib/notificaciones-telegram.js +1107 -0
- package/scripts/lib/npm-version.js +261 -0
- package/scripts/lib/paquetes-conocidos.js +50 -0
- package/scripts/lib/preservar-usuario.js +586 -0
- package/scripts/lib/prompt-builder.js +264 -0
- package/scripts/lib/resolver-externo.js +332 -0
- package/scripts/lib/schedule-parser.js +305 -0
- package/scripts/lib/scoring-instintos.js +240 -0
- package/scripts/lib/seguridad.js +160 -0
- package/scripts/lib/selector-interactivo.js +152 -0
- package/scripts/lib/semantic-search.js +242 -0
- package/scripts/lib/skill-discovery.js +234 -0
- package/scripts/lib/skill-metrics.js +246 -0
- package/scripts/lib/skill-normalizer.js +112 -0
- package/scripts/lib/skills-hub.js +340 -0
- package/scripts/lib/span-schema.js +134 -0
- package/scripts/lib/tool-cost-analyzer.js +255 -0
- package/scripts/lib/tracing-processor-interface.js +286 -0
- package/scripts/lib/transformadores/base.js +80 -0
- package/scripts/lib/transformadores/claude.js +124 -0
- package/scripts/lib/transformadores/codex.js +115 -0
- package/scripts/lib/transformadores/copilot.js +106 -0
- package/scripts/lib/transformadores/gemini.js +74 -0
- package/scripts/lib/transformadores/index.js +35 -0
- package/scripts/lib/transformadores/opencode.js +75 -0
- package/scripts/lib/ui.js +259 -0
- package/scripts/limpiar-artefactos-python.js +131 -0
- package/scripts/mcp-orchestrator.py +386 -0
- package/scripts/mcp-pool-manager.py +352 -0
- package/scripts/mcp-telemetry.py +378 -0
- package/scripts/poblar-evolvable.js +226 -0
- package/scripts/publicar.js +287 -0
- package/scripts/reflect-skills.js +403 -0
- package/scripts/rotar-audit-logs.js +185 -0
- package/scripts/run-skill-evals.js +242 -0
- package/scripts/smoke-test.js +374 -0
- package/scripts/token-analysis.py +471 -0
- package/scripts/validar-manifest.js +195 -0
- package/scripts/validar-memoria.js +321 -0
- package/scripts/validar-tests-aislamiento.js +184 -0
- package/scripts/validar-tokens-test.js +208 -0
- package/scripts/validar.js +147 -0
- package/scripts/validate-markdown.py +339 -0
- package/scripts/validate-skills.py +385 -0
- package/scripts/vendor/claude-usage/README.md +116 -0
- package/scripts/vendor/claude-usage/cli.py +334 -0
- package/scripts/vendor/claude-usage/dashboard.py +795 -0
- package/scripts/vendor/claude-usage/scanner.py +467 -0
- package/scripts/vendor/markitdown/cli.py +194 -0
- package/scripts/verificar-evolucion.js +289 -0
- package/scripts/verificar-release.js +494 -0
|
@@ -0,0 +1,789 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Gestión de hooks en settings.json del runtime destino.
|
|
5
|
+
*
|
|
6
|
+
* Funciones para registrar, desregistrar y verificar hooks SWL
|
|
7
|
+
* en la configuración del runtime (Claude Code, Gemini CLI, etc.).
|
|
8
|
+
*
|
|
9
|
+
* Los hooks SWL se identifican por la presencia de "hooks/" + nombre
|
|
10
|
+
* del archivo en el campo `command`.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const RAIZ_PKG = path.resolve(__dirname, '..', '..');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Carga la configuración de hooks desde manifiestos/hooks-config.json
|
|
20
|
+
* @returns {object} Mapa de nombre_archivo → { event, matcher, description, blocking }
|
|
21
|
+
*/
|
|
22
|
+
function cargarHooksConfig() {
|
|
23
|
+
const configPath = path.join(RAIZ_PKG, 'manifiestos', 'hooks-config.json');
|
|
24
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
25
|
+
return config.hooks;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Lee settings.json del runtime. Si no existe, retorna objeto base.
|
|
30
|
+
* @param {string} settingsPath - Ruta completa al archivo settings.json
|
|
31
|
+
* @returns {object}
|
|
32
|
+
*/
|
|
33
|
+
function leerSettings(settingsPath) {
|
|
34
|
+
if (!fs.existsSync(settingsPath)) {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
|
|
39
|
+
} catch {
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Escribe settings.json preservando formato legible.
|
|
46
|
+
* @param {string} settingsPath
|
|
47
|
+
* @param {object} settings
|
|
48
|
+
*/
|
|
49
|
+
function escribirSettings(settingsPath, settings) {
|
|
50
|
+
const dir = path.dirname(settingsPath);
|
|
51
|
+
if (!fs.existsSync(dir)) {
|
|
52
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
53
|
+
}
|
|
54
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Determina si un hook entry en settings.json pertenece a SWL.
|
|
59
|
+
* @param {object} entry - { matcher, hooks: [{ type, command }] }
|
|
60
|
+
* @returns {boolean}
|
|
61
|
+
*/
|
|
62
|
+
function esHookSWL(entry) {
|
|
63
|
+
const hooksArr = entry.hooks || [];
|
|
64
|
+
return hooksArr.some(h => {
|
|
65
|
+
const cmd = h.command || '';
|
|
66
|
+
// Los hooks SWL usan el patrón: node hooks/nombre-hook.js
|
|
67
|
+
return /node\s+.*hooks\/[a-z-]+\.js/.test(cmd);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Calcula la ruta del settings.json para un runtime.
|
|
73
|
+
* @param {object} runtime - Configuración del runtime
|
|
74
|
+
* @param {string} base - Directorio base (.claude, ~/.claude, etc.)
|
|
75
|
+
* @returns {string} Ruta completa al settings.json
|
|
76
|
+
*/
|
|
77
|
+
function rutaSettings(runtime, base) {
|
|
78
|
+
const configFile = runtime.hookConfig || 'settings.json';
|
|
79
|
+
return path.join(base, configFile);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* En Windows (o cuando el entorno lo solicita vía SWL_HOOKS_PATH_PREFIX),
|
|
84
|
+
* agrega ubicaciones típicas de Node al PATH del comando para resolver
|
|
85
|
+
* `node: command not found` en git-bash/MinGW y similares.
|
|
86
|
+
*
|
|
87
|
+
* El prefix es inocuo en Linux/macOS: los paths no existen y se ignoran.
|
|
88
|
+
* El usuario puede desactivarlo con SWL_HOOKS_PATH_PREFIX=0.
|
|
89
|
+
*/
|
|
90
|
+
function prefijoPathNode() {
|
|
91
|
+
if (process.env.SWL_HOOKS_PATH_PREFIX === '0') return '';
|
|
92
|
+
// En Windows siempre; en otros sistemas solo si se pide explícitamente.
|
|
93
|
+
const aplicar = process.platform === 'win32' || process.env.SWL_HOOKS_PATH_PREFIX === '1';
|
|
94
|
+
if (!aplicar) return '';
|
|
95
|
+
// Usamos rutas de estilo MSYS/Git-Bash: funcionan en git-bash y son
|
|
96
|
+
// ignoradas silenciosamente por `sh` en sistemas donde no existen.
|
|
97
|
+
return 'PATH="/c/Program Files/nodejs:/c/Program Files (x86)/nodejs:$PATH" ';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Decide si debe usarse el wrapper bash `hooks/_run-hook.sh` en lugar del
|
|
102
|
+
* inline `node -e`. El wrapper es más robusto contra formatters que
|
|
103
|
+
* reescriben settings.json, porque el command es corto y no contiene el
|
|
104
|
+
* prefix PATH que algunos formatters remueven.
|
|
105
|
+
*
|
|
106
|
+
* Activar con SWL_HOOKS_USAR_WRAPPER=1 (opt-in por ahora para no romper
|
|
107
|
+
* instalaciones existentes; se activará por default en v6.0.0).
|
|
108
|
+
*/
|
|
109
|
+
function debeUsarWrapper(hooksDir) {
|
|
110
|
+
if (process.env.SWL_HOOKS_USAR_WRAPPER === '0') return false;
|
|
111
|
+
if (process.env.SWL_HOOKS_USAR_WRAPPER !== '1') return false;
|
|
112
|
+
try {
|
|
113
|
+
const fs = require('fs');
|
|
114
|
+
const wrapperPath = path.join(hooksDir, '_run-hook.sh');
|
|
115
|
+
return fs.existsSync(wrapperPath);
|
|
116
|
+
} catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Genera el comando para ejecutar un hook desde el directorio de trabajo.
|
|
123
|
+
* @param {string} hookFilename - Nombre del archivo de hook (ej: "escaneo-secretos.js")
|
|
124
|
+
* @param {string} hooksDir - Directorio donde se instalaron los hooks
|
|
125
|
+
* @param {boolean} esGlobal - Si es instalación global
|
|
126
|
+
* @returns {string} Comando ejecutable
|
|
127
|
+
*/
|
|
128
|
+
function generarComando(hookFilename, hooksDir, esGlobal) {
|
|
129
|
+
// Modo wrapper (opt-in): más resiliente contra formatters
|
|
130
|
+
if (debeUsarWrapper(hooksDir)) {
|
|
131
|
+
if (esGlobal) {
|
|
132
|
+
const wrapper = path.join(hooksDir, '_run-hook.sh').replace(/\\/g, '/');
|
|
133
|
+
const hookAbs = path.join(hooksDir, hookFilename).replace(/\\/g, '/');
|
|
134
|
+
return `bash "${wrapper}" "${hookAbs}"`;
|
|
135
|
+
}
|
|
136
|
+
return `bash ./hooks/_run-hook.sh ${hookFilename}`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Modo inline (default, compatible con instalaciones previas)
|
|
140
|
+
const prefix = prefijoPathNode();
|
|
141
|
+
if (esGlobal) {
|
|
142
|
+
const rutaAbsoluta = path.join(hooksDir, hookFilename).replace(/\\/g, '/');
|
|
143
|
+
return `${prefix}node -e "try{require('${rutaAbsoluta}')}catch(e){if(e.code!=='MODULE_NOT_FOUND')throw e}"`;
|
|
144
|
+
}
|
|
145
|
+
return `${prefix}node -e "try{require('./hooks/${hookFilename}')}catch(e){if(e.code!=='MODULE_NOT_FOUND')throw e}"`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Registra los hooks SWL en settings.json del runtime.
|
|
150
|
+
* Preserva hooks existentes no-SWL. Reemplaza hooks SWL previos.
|
|
151
|
+
*
|
|
152
|
+
* @param {object} opciones
|
|
153
|
+
* @param {string} opciones.settingsPath - Ruta al settings.json
|
|
154
|
+
* @param {string} opciones.hooksDir - Directorio donde están los hooks
|
|
155
|
+
* @param {boolean} opciones.esGlobal - Si es instalación global
|
|
156
|
+
* @param {string[]} opciones.hookFiles - Lista de archivos de hooks instalados
|
|
157
|
+
* @returns {{ registrados: number, preservados: number }}
|
|
158
|
+
*/
|
|
159
|
+
function registrarHooks(opciones) {
|
|
160
|
+
const { settingsPath, hooksDir, esGlobal, hookFiles } = opciones;
|
|
161
|
+
const hooksConfig = cargarHooksConfig();
|
|
162
|
+
const settings = leerSettings(settingsPath);
|
|
163
|
+
|
|
164
|
+
// Inicializar sección hooks si no existe
|
|
165
|
+
if (!settings.hooks) {
|
|
166
|
+
settings.hooks = {};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
let registrados = 0;
|
|
170
|
+
let preservados = 0;
|
|
171
|
+
|
|
172
|
+
// Agrupar hooks SWL por evento
|
|
173
|
+
const hooksPorEvento = {};
|
|
174
|
+
for (const filename of hookFiles) {
|
|
175
|
+
const config = hooksConfig[filename];
|
|
176
|
+
if (!config) continue;
|
|
177
|
+
|
|
178
|
+
const evento = config.event;
|
|
179
|
+
if (!hooksPorEvento[evento]) {
|
|
180
|
+
hooksPorEvento[evento] = [];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
hooksPorEvento[evento].push({
|
|
184
|
+
matcher: config.matcher || '',
|
|
185
|
+
hooks: [{
|
|
186
|
+
type: 'command',
|
|
187
|
+
command: generarComando(filename, hooksDir, esGlobal),
|
|
188
|
+
}],
|
|
189
|
+
});
|
|
190
|
+
registrados++;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Para cada tipo de evento, preservar hooks no-SWL y agregar los SWL
|
|
194
|
+
const todosEventos = new Set(['PreToolUse', 'PostToolUse']);
|
|
195
|
+
Object.keys(hooksPorEvento).forEach(e => todosEventos.add(e));
|
|
196
|
+
for (const evento of todosEventos) {
|
|
197
|
+
const existentes = settings.hooks[evento] || [];
|
|
198
|
+
const noSWL = existentes.filter(entry => !esHookSWL(entry));
|
|
199
|
+
preservados += noSWL.length;
|
|
200
|
+
|
|
201
|
+
const swlNuevos = hooksPorEvento[evento] || [];
|
|
202
|
+
|
|
203
|
+
// Limpiar entradas con matcher vacío (usar "" para match-all)
|
|
204
|
+
const swlLimpios = swlNuevos.map(h => {
|
|
205
|
+
const entry = { matcher: h.matcher || '' };
|
|
206
|
+
entry.hooks = h.hooks;
|
|
207
|
+
return entry;
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const combinados = [...noSWL, ...swlLimpios];
|
|
211
|
+
|
|
212
|
+
if (combinados.length > 0) {
|
|
213
|
+
settings.hooks[evento] = combinados;
|
|
214
|
+
} else {
|
|
215
|
+
delete settings.hooks[evento];
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Limpiar sección hooks si quedó vacía
|
|
220
|
+
if (Object.keys(settings.hooks).length === 0) {
|
|
221
|
+
delete settings.hooks;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
escribirSettings(settingsPath, settings);
|
|
225
|
+
|
|
226
|
+
return { registrados, preservados };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Desregistra todos los hooks SWL de settings.json.
|
|
231
|
+
* Preserva hooks no-SWL intactos.
|
|
232
|
+
*
|
|
233
|
+
* @param {string} settingsPath - Ruta al settings.json
|
|
234
|
+
* @returns {{ eliminados: number, preservados: number }}
|
|
235
|
+
*/
|
|
236
|
+
function desregistrarHooks(settingsPath) {
|
|
237
|
+
const settings = leerSettings(settingsPath);
|
|
238
|
+
|
|
239
|
+
if (!settings.hooks) {
|
|
240
|
+
return { eliminados: 0, preservados: 0 };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
let eliminados = 0;
|
|
244
|
+
let preservados = 0;
|
|
245
|
+
|
|
246
|
+
// Iterar TODOS los eventos presentes en settings.json (no solo PreToolUse/PostToolUse)
|
|
247
|
+
for (const evento of Object.keys(settings.hooks)) {
|
|
248
|
+
const existentes = settings.hooks[evento] || [];
|
|
249
|
+
if (!Array.isArray(existentes)) continue;
|
|
250
|
+
const noSWL = [];
|
|
251
|
+
|
|
252
|
+
for (const entry of existentes) {
|
|
253
|
+
if (esHookSWL(entry)) {
|
|
254
|
+
eliminados++;
|
|
255
|
+
} else {
|
|
256
|
+
noSWL.push(entry);
|
|
257
|
+
preservados++;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (noSWL.length > 0) {
|
|
262
|
+
settings.hooks[evento] = noSWL;
|
|
263
|
+
} else {
|
|
264
|
+
delete settings.hooks[evento];
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Limpiar sección hooks si quedó vacía
|
|
269
|
+
if (Object.keys(settings.hooks).length === 0) {
|
|
270
|
+
delete settings.hooks;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
escribirSettings(settingsPath, settings);
|
|
274
|
+
|
|
275
|
+
return { eliminados, preservados };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Verifica que los hooks SWL estén correctamente registrados en settings.json.
|
|
280
|
+
*
|
|
281
|
+
* @param {string} settingsPath - Ruta al settings.json
|
|
282
|
+
* @param {string[]} hookFiles - Lista de archivos de hooks que deberían estar registrados
|
|
283
|
+
* @returns {{ ok: boolean, registrados: string[], faltantes: string[], incorrectos: string[] }}
|
|
284
|
+
*/
|
|
285
|
+
function verificarHooks(settingsPath, hookFiles) {
|
|
286
|
+
const hooksConfig = cargarHooksConfig();
|
|
287
|
+
const settings = leerSettings(settingsPath);
|
|
288
|
+
|
|
289
|
+
const registrados = [];
|
|
290
|
+
const faltantes = [];
|
|
291
|
+
const incorrectos = [];
|
|
292
|
+
|
|
293
|
+
for (const filename of hookFiles) {
|
|
294
|
+
const config = hooksConfig[filename];
|
|
295
|
+
if (!config) {
|
|
296
|
+
faltantes.push(filename);
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const evento = config.event;
|
|
301
|
+
const entradas = (settings.hooks && settings.hooks[evento]) || [];
|
|
302
|
+
|
|
303
|
+
// Buscar si existe un hook SWL que referencie este archivo
|
|
304
|
+
const encontrado = entradas.find(entry => {
|
|
305
|
+
const hooksArr = entry.hooks || [];
|
|
306
|
+
return hooksArr.some(h => (h.command || '').includes(filename));
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
if (!encontrado) {
|
|
310
|
+
faltantes.push(filename);
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Verificar matcher correcto
|
|
315
|
+
const matcherEsperado = config.matcher || '';
|
|
316
|
+
const matcherActual = encontrado.matcher || '';
|
|
317
|
+
|
|
318
|
+
if (matcherEsperado !== matcherActual) {
|
|
319
|
+
incorrectos.push(`${filename}: matcher esperado="${matcherEsperado || '(ninguno)'}" actual="${matcherActual || '(ninguno)'}"`);
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Verificar que todos los hooks tienen type: "command"
|
|
324
|
+
const hooksSinType = (encontrado.hooks || []).filter(h => h.type !== 'command');
|
|
325
|
+
if (hooksSinType.length > 0) {
|
|
326
|
+
incorrectos.push(`${filename}: falta "type": "command" en hook entry (causa Settings Error en Claude Code)`);
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
registrados.push(filename);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return {
|
|
334
|
+
ok: faltantes.length === 0 && incorrectos.length === 0,
|
|
335
|
+
registrados,
|
|
336
|
+
faltantes,
|
|
337
|
+
incorrectos,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Repara hooks SWL en settings.json que no tienen el campo "type": "command".
|
|
343
|
+
* Claude Code requiere este campo; sin él, genera "Settings Error: type: Invalid input".
|
|
344
|
+
*
|
|
345
|
+
* @param {string} settingsPath - Ruta al settings.json
|
|
346
|
+
* @returns {{ reparados: number }}
|
|
347
|
+
*/
|
|
348
|
+
function repararHooks(settingsPath) {
|
|
349
|
+
const settings = leerSettings(settingsPath);
|
|
350
|
+
|
|
351
|
+
if (!settings.hooks) {
|
|
352
|
+
return { reparados: 0 };
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
let reparados = 0;
|
|
356
|
+
|
|
357
|
+
// Iterar TODOS los eventos presentes (no solo PreToolUse/PostToolUse)
|
|
358
|
+
for (const evento of Object.keys(settings.hooks)) {
|
|
359
|
+
const entradas = settings.hooks[evento] || [];
|
|
360
|
+
if (!Array.isArray(entradas)) continue;
|
|
361
|
+
|
|
362
|
+
for (const entry of entradas) {
|
|
363
|
+
if (!esHookSWL(entry)) continue;
|
|
364
|
+
|
|
365
|
+
const hooksArr = entry.hooks || [];
|
|
366
|
+
for (const h of hooksArr) {
|
|
367
|
+
if (h.command && h.type !== 'command') {
|
|
368
|
+
h.type = 'command';
|
|
369
|
+
reparados++;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (reparados > 0) {
|
|
376
|
+
escribirSettings(settingsPath, settings);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return { reparados };
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Agrega hooks SWL al settings.json GLOBAL (~/.claude/settings.json) sin
|
|
384
|
+
* remover hooks no-SWL ni duplicar entries SWL que ya existan.
|
|
385
|
+
*
|
|
386
|
+
* EXCEPCIÓN DOCUMENTADA (ADR-0009): Esta función es la única excepción a la
|
|
387
|
+
* regla "SWL solo registra hooks a nivel proyecto". Solo debe invocarse bajo
|
|
388
|
+
* opt-in explícito del usuario (instalador interactivo o /swl:notificaciones init).
|
|
389
|
+
*
|
|
390
|
+
* @param {string} settingsPath - Ruta al settings.json global.
|
|
391
|
+
* @param {Array<{ evento: string, matcher: string, archivo: string, hooksDir?: string|null }>} hookEntries
|
|
392
|
+
* Lista de hooks a agregar. Cada entry describe el evento, matcher, archivo y
|
|
393
|
+
* directorio de hooks instalados para construir el comando.
|
|
394
|
+
* @returns {{ agregados: number, preservados: number, error?: string }}
|
|
395
|
+
*/
|
|
396
|
+
function mergeHooksGlobales(settingsPath, hookEntries) {
|
|
397
|
+
// Validar que el archivo es JSON parseable antes de modificar
|
|
398
|
+
let settings;
|
|
399
|
+
try {
|
|
400
|
+
settings = leerSettings(settingsPath);
|
|
401
|
+
} catch (err) {
|
|
402
|
+
return { agregados: 0, preservados: 0, error: `No se puede leer settings.json: ${err.message}` };
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Verificar que el JSON existente (si existe el archivo) es válido
|
|
406
|
+
if (fs.existsSync(settingsPath)) {
|
|
407
|
+
try {
|
|
408
|
+
JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
409
|
+
} catch (err) {
|
|
410
|
+
return {
|
|
411
|
+
agregados: 0,
|
|
412
|
+
preservados: 0,
|
|
413
|
+
error: `settings.json tiene JSON inválido — abortando merge para proteger el archivo. Error: ${err.message}`,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Crear backup antes de cualquier modificación
|
|
419
|
+
const ts = Date.now();
|
|
420
|
+
const backup = `${settingsPath}.swl-backup-${ts}`;
|
|
421
|
+
if (fs.existsSync(settingsPath)) {
|
|
422
|
+
try {
|
|
423
|
+
fs.copyFileSync(settingsPath, backup);
|
|
424
|
+
} catch (err) {
|
|
425
|
+
return { agregados: 0, preservados: 0, error: `No se pudo crear backup: ${err.message}` };
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (!settings.hooks) settings.hooks = {};
|
|
430
|
+
|
|
431
|
+
let agregados = 0;
|
|
432
|
+
let preservados = 0;
|
|
433
|
+
let migrados = 0;
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* REM-C helper: detecta si dos comandos corresponden al mismo hook
|
|
437
|
+
* (mismo nombre de archivo .js) independientemente de si la ruta es
|
|
438
|
+
* relativa o absoluta.
|
|
439
|
+
*
|
|
440
|
+
* Ejemplos que deben coincidir:
|
|
441
|
+
* "node hooks/notificacion-telegram.js"
|
|
442
|
+
* 'node "C:/Users/.../.claude/hooks/notificacion-telegram.js"'
|
|
443
|
+
* 'node "/home/user/.claude/hooks/notificacion-telegram.js"'
|
|
444
|
+
*
|
|
445
|
+
* @param {string} commandA
|
|
446
|
+
* @param {string} archivoRef - Nombre de archivo simple, ej: "notificacion-telegram.js"
|
|
447
|
+
* @returns {boolean}
|
|
448
|
+
*/
|
|
449
|
+
function _mismoArchivo(commandA, archivoRef) {
|
|
450
|
+
// Extraer solo el nombre de archivo del comando (parte después del último /)
|
|
451
|
+
const match = commandA.match(/([a-z0-9-]+\.js)["']?\s*$/i);
|
|
452
|
+
if (!match) return false;
|
|
453
|
+
return match[1] === path.basename(archivoRef);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
for (const entry of hookEntries) {
|
|
457
|
+
const { evento, matcher, archivo, hooksDir } = entry;
|
|
458
|
+
|
|
459
|
+
if (!settings.hooks[evento]) {
|
|
460
|
+
settings.hooks[evento] = [];
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const existentes = settings.hooks[evento];
|
|
464
|
+
|
|
465
|
+
// Contar hooks no-SWL que se preservan
|
|
466
|
+
const noSWL = existentes.filter(e => !esHookSWL(e));
|
|
467
|
+
preservados += noSWL.length;
|
|
468
|
+
|
|
469
|
+
// REM-B: siempre usar ruta absoluta — si no hay hooksDir es error de configuración
|
|
470
|
+
// (quien llama debe siempre proporcionar hooksDir con HOOKS_GLOBAL_DIR)
|
|
471
|
+
const comandoNuevo = hooksDir
|
|
472
|
+
? `node "${path.join(hooksDir, archivo).replace(/\\/g, '/')}"`
|
|
473
|
+
: `node hooks/${archivo}`; // fallback de emergencia — nunca debería usarse
|
|
474
|
+
|
|
475
|
+
// REM-C: buscar entry existente con mismo archivo (puede tener ruta relativa/incorrecta)
|
|
476
|
+
const idxExistente = existentes.findIndex(e =>
|
|
477
|
+
(e.hooks || []).some(h => h.command && _mismoArchivo(h.command, archivo))
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
if (idxExistente === -1) {
|
|
481
|
+
// No existe — agregar
|
|
482
|
+
settings.hooks[evento].push({
|
|
483
|
+
matcher: matcher || '',
|
|
484
|
+
hooks: [{ type: 'command', command: comandoNuevo }],
|
|
485
|
+
});
|
|
486
|
+
agregados++;
|
|
487
|
+
} else {
|
|
488
|
+
// Existe — verificar si necesita migración de ruta relativa → absoluta
|
|
489
|
+
const entryExistente = existentes[idxExistente];
|
|
490
|
+
const commandActual = (entryExistente.hooks || [])[0]?.command || '';
|
|
491
|
+
const esRelativo = /^node\s+hooks\//.test(commandActual.trim());
|
|
492
|
+
const esIncorrecto = commandActual !== comandoNuevo;
|
|
493
|
+
|
|
494
|
+
if (esRelativo || esIncorrecto) {
|
|
495
|
+
// REM-C: migrar en lugar de duplicar
|
|
496
|
+
existentes[idxExistente] = {
|
|
497
|
+
matcher: matcher || entryExistente.matcher || '',
|
|
498
|
+
hooks: [{ type: 'command', command: comandoNuevo }],
|
|
499
|
+
};
|
|
500
|
+
migrados++;
|
|
501
|
+
}
|
|
502
|
+
// Si ya tiene el comando correcto: no-op (idempotente)
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Serializar y validar antes de escribir
|
|
507
|
+
let contenidoNuevo;
|
|
508
|
+
try {
|
|
509
|
+
contenidoNuevo = JSON.stringify(settings, null, 2) + '\n';
|
|
510
|
+
JSON.parse(contenidoNuevo); // validación pre-escritura
|
|
511
|
+
} catch (err) {
|
|
512
|
+
return {
|
|
513
|
+
agregados: 0,
|
|
514
|
+
preservados: 0,
|
|
515
|
+
error: `JSON inválido al serializar settings — operación abortada: ${err.message}`,
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Escritura atómica
|
|
520
|
+
try {
|
|
521
|
+
const atomicWrite = require('../../hooks/lib/atomic-write');
|
|
522
|
+
atomicWrite.atomicWriteSync(settingsPath, contenidoNuevo);
|
|
523
|
+
} catch (err) {
|
|
524
|
+
// Intentar restaurar backup
|
|
525
|
+
try {
|
|
526
|
+
if (fs.existsSync(backup)) {
|
|
527
|
+
fs.copyFileSync(backup, settingsPath);
|
|
528
|
+
}
|
|
529
|
+
} catch (_) { /* no se pudo restaurar — dejar backup en su lugar */ }
|
|
530
|
+
return { agregados: 0, preservados: 0, error: `Error escribiendo settings.json: ${err.message}` };
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// Validación post-escritura
|
|
534
|
+
try {
|
|
535
|
+
const escritoFinal = fs.readFileSync(settingsPath, 'utf8');
|
|
536
|
+
JSON.parse(escritoFinal);
|
|
537
|
+
} catch (err) {
|
|
538
|
+
// Rollback desde backup
|
|
539
|
+
try {
|
|
540
|
+
if (fs.existsSync(backup)) {
|
|
541
|
+
fs.copyFileSync(backup, settingsPath);
|
|
542
|
+
console.error(` [notificaciones] Rollback desde backup ${backup} (JSON inválido post-escritura).`);
|
|
543
|
+
}
|
|
544
|
+
} catch (_) { /* no se pudo restaurar */ }
|
|
545
|
+
return {
|
|
546
|
+
agregados: 0,
|
|
547
|
+
preservados: 0,
|
|
548
|
+
error: `Validación post-escritura falló — settings restaurado desde backup: ${err.message}`,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
return { agregados, preservados, migrados };
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Quita todas las entries SWL del settings.json GLOBAL preservando hooks ajenos.
|
|
557
|
+
*
|
|
558
|
+
* EXCEPCIÓN DOCUMENTADA (ADR-0009): Inversa de mergeHooksGlobales.
|
|
559
|
+
* Solo debe invocarse desde /swl:notificaciones disable.
|
|
560
|
+
*
|
|
561
|
+
* @param {string} settingsPath
|
|
562
|
+
* @returns {{ eliminados: number, preservados: number, error?: string }}
|
|
563
|
+
*/
|
|
564
|
+
function desregistrarHooksGlobales(settingsPath) {
|
|
565
|
+
if (!fs.existsSync(settingsPath)) {
|
|
566
|
+
return { eliminados: 0, preservados: 0 };
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
let settings;
|
|
570
|
+
try {
|
|
571
|
+
settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
572
|
+
} catch (err) {
|
|
573
|
+
return { eliminados: 0, preservados: 0, error: `JSON inválido en settings.json: ${err.message}` };
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (!settings.hooks) {
|
|
577
|
+
return { eliminados: 0, preservados: 0 };
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Backup antes de modificar
|
|
581
|
+
const backup = `${settingsPath}.swl-backup-${Date.now()}`;
|
|
582
|
+
try {
|
|
583
|
+
fs.copyFileSync(settingsPath, backup);
|
|
584
|
+
} catch (err) {
|
|
585
|
+
return { eliminados: 0, preservados: 0, error: `No se pudo crear backup: ${err.message}` };
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
let eliminados = 0;
|
|
589
|
+
let preservados = 0;
|
|
590
|
+
|
|
591
|
+
for (const evento of Object.keys(settings.hooks)) {
|
|
592
|
+
const existentes = settings.hooks[evento] || [];
|
|
593
|
+
if (!Array.isArray(existentes)) continue;
|
|
594
|
+
|
|
595
|
+
const noSWL = [];
|
|
596
|
+
for (const entry of existentes) {
|
|
597
|
+
if (esHookSWL(entry)) {
|
|
598
|
+
eliminados++;
|
|
599
|
+
} else {
|
|
600
|
+
noSWL.push(entry);
|
|
601
|
+
preservados++;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
if (noSWL.length > 0) {
|
|
606
|
+
settings.hooks[evento] = noSWL;
|
|
607
|
+
} else {
|
|
608
|
+
delete settings.hooks[evento];
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
if (Object.keys(settings.hooks).length === 0) {
|
|
613
|
+
delete settings.hooks;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// Escritura atómica + validación post-escritura
|
|
617
|
+
try {
|
|
618
|
+
const contenido = JSON.stringify(settings, null, 2) + '\n';
|
|
619
|
+
JSON.parse(contenido); // validación pre-escritura
|
|
620
|
+
const atomicWrite = require('../../hooks/lib/atomic-write');
|
|
621
|
+
atomicWrite.atomicWriteSync(settingsPath, contenido);
|
|
622
|
+
} catch (err) {
|
|
623
|
+
try {
|
|
624
|
+
if (fs.existsSync(backup)) fs.copyFileSync(backup, settingsPath);
|
|
625
|
+
} catch (_) { /* no se pudo restaurar */ }
|
|
626
|
+
return { eliminados: 0, preservados: 0, error: `Error al escribir settings.json: ${err.message}` };
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
return { eliminados, preservados };
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Optimizaciones de settings.json recomendadas por SWL.
|
|
634
|
+
* Basadas en auditoría de 858 sesiones / 18,903 turnos.
|
|
635
|
+
*
|
|
636
|
+
* - ENABLE_TOOL_SEARCH: carga schemas de herramientas bajo demanda en vez de
|
|
637
|
+
* todos al inicio. Reduce ~20,000 tokens/turno de definiciones no usadas.
|
|
638
|
+
* - showThinkingSummaries: hace visible cuando Claude reduce profundidad de
|
|
639
|
+
* razonamiento, permitiendo detectar regresiones de calidad.
|
|
640
|
+
*
|
|
641
|
+
* Estas configuraciones se aplican solo a nivel de PROYECTO (.claude/settings.json),
|
|
642
|
+
* no a nivel global (~/.claude/settings.json), para no afectar otros proyectos.
|
|
643
|
+
*/
|
|
644
|
+
const OPTIMIZACIONES_SWL = {
|
|
645
|
+
env: {
|
|
646
|
+
ENABLE_TOOL_SEARCH: 'true',
|
|
647
|
+
},
|
|
648
|
+
preferences: {
|
|
649
|
+
showThinkingSummaries: true,
|
|
650
|
+
},
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Registra las optimizaciones SWL en settings.json del proyecto.
|
|
655
|
+
* No sobrescribe valores existentes del usuario — solo agrega los faltantes.
|
|
656
|
+
*
|
|
657
|
+
* @param {string} settingsPath - Ruta al settings.json
|
|
658
|
+
* @returns {{ aplicadas: number, yaExistentes: number }}
|
|
659
|
+
*/
|
|
660
|
+
function registrarOptimizaciones(settingsPath) {
|
|
661
|
+
const settings = leerSettings(settingsPath);
|
|
662
|
+
let aplicadas = 0;
|
|
663
|
+
let yaExistentes = 0;
|
|
664
|
+
|
|
665
|
+
for (const [seccion, valores] of Object.entries(OPTIMIZACIONES_SWL)) {
|
|
666
|
+
if (!settings[seccion]) {
|
|
667
|
+
settings[seccion] = {};
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
for (const [clave, valor] of Object.entries(valores)) {
|
|
671
|
+
if (settings[seccion][clave] === undefined) {
|
|
672
|
+
settings[seccion][clave] = valor;
|
|
673
|
+
aplicadas++;
|
|
674
|
+
} else {
|
|
675
|
+
yaExistentes++;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
if (aplicadas > 0) {
|
|
681
|
+
escribirSettings(settingsPath, settings);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
return { aplicadas, yaExistentes };
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Valida la estructura del settings.json: secciones esperadas, tipos correctos,
|
|
689
|
+
* hooks con formato válido. No valida contenido semántico, solo estructura.
|
|
690
|
+
*
|
|
691
|
+
* @param {string} settingsPath
|
|
692
|
+
* @returns {{ ok: boolean, problemas: string[] }}
|
|
693
|
+
*/
|
|
694
|
+
function validarEstructuraSettings(settingsPath) {
|
|
695
|
+
const settings = leerSettings(settingsPath);
|
|
696
|
+
const problemas = [];
|
|
697
|
+
|
|
698
|
+
// Validar sección hooks
|
|
699
|
+
if (settings.hooks) {
|
|
700
|
+
const eventosValidos = new Set([
|
|
701
|
+
'PreToolUse', 'PostToolUse', 'UserPromptSubmit', 'Stop',
|
|
702
|
+
'SubagentStart', 'SubagentStop', 'PreCompact', 'PostCompact',
|
|
703
|
+
'Notification', 'SessionStart', 'TaskCompleted',
|
|
704
|
+
]);
|
|
705
|
+
|
|
706
|
+
for (const [evento, entradas] of Object.entries(settings.hooks)) {
|
|
707
|
+
if (!eventosValidos.has(evento)) {
|
|
708
|
+
problemas.push(`hooks.${evento}: evento no reconocido`);
|
|
709
|
+
}
|
|
710
|
+
if (!Array.isArray(entradas)) {
|
|
711
|
+
problemas.push(`hooks.${evento}: debe ser un array de matchers`);
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
for (let i = 0; i < entradas.length; i++) {
|
|
715
|
+
const entrada = entradas[i];
|
|
716
|
+
if (typeof entrada.matcher !== 'string' && entrada.matcher !== undefined) {
|
|
717
|
+
problemas.push(`hooks.${evento}[${i}].matcher: debe ser string`);
|
|
718
|
+
}
|
|
719
|
+
if (!Array.isArray(entrada.hooks)) {
|
|
720
|
+
problemas.push(`hooks.${evento}[${i}].hooks: debe ser array de hooks`);
|
|
721
|
+
continue;
|
|
722
|
+
}
|
|
723
|
+
for (let j = 0; j < entrada.hooks.length; j++) {
|
|
724
|
+
const hook = entrada.hooks[j];
|
|
725
|
+
const tiposValidos = ['command', 'prompt', 'agent', 'http'];
|
|
726
|
+
if (!tiposValidos.includes(hook.type)) {
|
|
727
|
+
problemas.push(`hooks.${evento}[${i}].hooks[${j}].type: "${hook.type}" no es válido (command|prompt|agent|http)`);
|
|
728
|
+
}
|
|
729
|
+
if (hook.type === 'command' && !hook.command) {
|
|
730
|
+
problemas.push(`hooks.${evento}[${i}].hooks[${j}]: hook tipo command sin campo command`);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
// Validar sección env
|
|
738
|
+
if (settings.env && typeof settings.env !== 'object') {
|
|
739
|
+
problemas.push('env: debe ser un objeto { clave: valor }');
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// Validar sección preferences
|
|
743
|
+
if (settings.preferences && typeof settings.preferences !== 'object') {
|
|
744
|
+
problemas.push('preferences: debe ser un objeto');
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
return { ok: problemas.length === 0, problemas };
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* Verifica que las optimizaciones SWL estén presentes en settings.json.
|
|
752
|
+
*
|
|
753
|
+
* @param {string} settingsPath
|
|
754
|
+
* @returns {{ ok: boolean, faltantes: string[] }}
|
|
755
|
+
*/
|
|
756
|
+
function verificarOptimizaciones(settingsPath) {
|
|
757
|
+
const settings = leerSettings(settingsPath);
|
|
758
|
+
const faltantes = [];
|
|
759
|
+
|
|
760
|
+
for (const [seccion, valores] of Object.entries(OPTIMIZACIONES_SWL)) {
|
|
761
|
+
for (const [clave, valor] of Object.entries(valores)) {
|
|
762
|
+
if (!settings[seccion] || settings[seccion][clave] === undefined) {
|
|
763
|
+
faltantes.push(`${seccion}.${clave}`);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
return { ok: faltantes.length === 0, faltantes };
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
module.exports = {
|
|
772
|
+
cargarHooksConfig,
|
|
773
|
+
leerSettings,
|
|
774
|
+
escribirSettings,
|
|
775
|
+
rutaSettings,
|
|
776
|
+
registrarHooks,
|
|
777
|
+
desregistrarHooks,
|
|
778
|
+
verificarHooks,
|
|
779
|
+
repararHooks,
|
|
780
|
+
esHookSWL,
|
|
781
|
+
registrarOptimizaciones,
|
|
782
|
+
verificarOptimizaciones,
|
|
783
|
+
validarEstructuraSettings,
|
|
784
|
+
generarComando,
|
|
785
|
+
OPTIMIZACIONES_SWL,
|
|
786
|
+
// ADR-0009: excepción opt-in para hooks globales de notificaciones Telegram
|
|
787
|
+
mergeHooksGlobales,
|
|
788
|
+
desregistrarHooksGlobales,
|
|
789
|
+
};
|