@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,438 @@
|
|
|
1
|
+
# Patrones Async de Producción — FastAPI + SQLAlchemy
|
|
2
|
+
|
|
3
|
+
## El anti-patrón MissingGreenlet — explicación y solución
|
|
4
|
+
|
|
5
|
+
`MissingGreenlet` es el error más frecuente en SQLAlchemy async. Ocurre cuando
|
|
6
|
+
SQLAlchemy intenta resolver una relación lazy **fuera del contexto async**,
|
|
7
|
+
generalmente al serializar el objeto con Pydantic después de que la sesión ya se cerró.
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
# ERROR — lazy loading implícito post-sesión
|
|
11
|
+
async def obtener_factura(factura_id: str, db: AsyncSession = Depends(get_db)):
|
|
12
|
+
factura = await db.get(Factura, factura_id)
|
|
13
|
+
return factura # Pydantic intenta acceder a factura.cliente — sesión ya cerrada
|
|
14
|
+
|
|
15
|
+
# ERROR — acceso a relación sin cargar explícitamente
|
|
16
|
+
factura = await db.get(Factura, factura_id)
|
|
17
|
+
nombre = factura.cliente.nombre # MissingGreenlet aquí
|
|
18
|
+
|
|
19
|
+
# SOLUCIÓN — selectinload explícito SIEMPRE
|
|
20
|
+
from sqlalchemy.orm import selectinload
|
|
21
|
+
|
|
22
|
+
result = await db.execute(
|
|
23
|
+
select(Factura)
|
|
24
|
+
.where(Factura.id == factura_id)
|
|
25
|
+
.options(selectinload(Factura.cliente)) # Carga en la misma query async
|
|
26
|
+
)
|
|
27
|
+
factura = result.scalar_one_or_none()
|
|
28
|
+
nombre = factura.cliente.nombre # Seguro — ya está cargado
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Prevención a nivel de modelo
|
|
32
|
+
|
|
33
|
+
Configurar `lazy="raise"` en relaciones que NO deben cargarse automáticamente
|
|
34
|
+
hace que el error aparezca en desarrollo, no en producción.
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
class Factura(Base):
|
|
38
|
+
__tablename__ = "facturas"
|
|
39
|
+
|
|
40
|
+
# lazy="selectin" — se carga siempre, accesible sin selectinload explícito
|
|
41
|
+
cliente: Mapped["Cliente"] = relationship(lazy="selectin")
|
|
42
|
+
|
|
43
|
+
# lazy="raise" — falla inmediatamente si se accede sin selectinload
|
|
44
|
+
# Útil para detectar accesos no intencionales en desarrollo
|
|
45
|
+
items: Mapped[list["ItemFactura"]] = relationship(lazy="raise")
|
|
46
|
+
auditoria: Mapped[list["Auditoria"]] = relationship(lazy="raise")
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## selectinload vs joinedload vs subqueryload en async
|
|
52
|
+
|
|
53
|
+
| Estrategia | Cuándo usar | Genera |
|
|
54
|
+
|------------|-------------|--------|
|
|
55
|
+
| `selectinload` | Relaciones 1:N o N:1. Recomendado en async | 2 queries separadas |
|
|
56
|
+
| `joinedload` | Solo relaciones N:1 con pocos registros | JOIN en la query principal |
|
|
57
|
+
| `subqueryload` | Evitar en async — puede causar problemas | Subquery correlacionada |
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from sqlalchemy.orm import selectinload, joinedload, contains_eager
|
|
61
|
+
|
|
62
|
+
# selectinload — el patrón correcto para async
|
|
63
|
+
result = await db.execute(
|
|
64
|
+
select(Factura)
|
|
65
|
+
.options(
|
|
66
|
+
selectinload(Factura.items).selectinload(ItemFactura.producto), # Anidado
|
|
67
|
+
selectinload(Factura.cliente),
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# joinedload — solo para relaciones N:1 donde el JOIN es conveniente
|
|
72
|
+
result = await db.execute(
|
|
73
|
+
select(Factura)
|
|
74
|
+
.join(Factura.cliente)
|
|
75
|
+
.options(contains_eager(Factura.cliente)) # Usa el JOIN existente
|
|
76
|
+
.where(Cliente.pais == "MX")
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# NUNCA usar lazy="joined" en modelos async — causa N+1 silencioso
|
|
80
|
+
# class Factura(Base):
|
|
81
|
+
# cliente = relationship(lazy="joined") # MAL en async
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Background Tasks: BackgroundTasks vs Celery vs ARQ
|
|
87
|
+
|
|
88
|
+
### BackgroundTasks — para tareas ligeras en el mismo proceso
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from fastapi import BackgroundTasks
|
|
92
|
+
import structlog
|
|
93
|
+
|
|
94
|
+
logger = structlog.get_logger()
|
|
95
|
+
|
|
96
|
+
async def _enviar_confirmacion(email: str, folio: str) -> None:
|
|
97
|
+
"""Tarea interna. No re-raise — no debe crashear la app."""
|
|
98
|
+
try:
|
|
99
|
+
await servicio_email.enviar(
|
|
100
|
+
destinatario=email,
|
|
101
|
+
asunto=f"Factura {folio} emitida",
|
|
102
|
+
plantilla="confirmacion_factura",
|
|
103
|
+
)
|
|
104
|
+
logger.info("Confirmación enviada", email=email, folio=folio)
|
|
105
|
+
except Exception as exc:
|
|
106
|
+
logger.error("Error enviando confirmación", email=email, exc_info=exc)
|
|
107
|
+
# NO re-raise — BackgroundTasks no tiene manejo de errores externo
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@router.post("/facturas/{factura_id}/emitir", response_model=FacturaRead)
|
|
111
|
+
async def emitir_factura(
|
|
112
|
+
factura_id: str,
|
|
113
|
+
background_tasks: BackgroundTasks,
|
|
114
|
+
db: DbDep,
|
|
115
|
+
usuario: UsuarioActualDep,
|
|
116
|
+
) -> FacturaRead:
|
|
117
|
+
factura = await servicio.emitir(db, factura_id)
|
|
118
|
+
# La tarea se ejecuta DESPUÉS de retornar la respuesta
|
|
119
|
+
background_tasks.add_task(_enviar_confirmacion, usuario.email, factura.folio)
|
|
120
|
+
return factura
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Limitaciones de BackgroundTasks**: sin reintentos, sin persistencia, sin monitoreo.
|
|
124
|
+
Usar solo para tareas que pueden fallar sin consecuencias graves.
|
|
125
|
+
|
|
126
|
+
### Celery — para tareas persistentes con reintentos
|
|
127
|
+
|
|
128
|
+
Ver la sección de Celery en la documentación principal del sistema SWL. Usar cuando:
|
|
129
|
+
se necesitan reintentos automáticos, colas separadas por prioridad, o la tarea puede
|
|
130
|
+
tardar más de 30 segundos.
|
|
131
|
+
|
|
132
|
+
### ARQ — alternativa async pura a Celery
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
# workers/arq_worker.py
|
|
136
|
+
from arq import create_pool
|
|
137
|
+
from arq.connections import RedisSettings
|
|
138
|
+
|
|
139
|
+
async def enviar_reporte(ctx: dict, usuario_id: str, periodo: str) -> dict:
|
|
140
|
+
"""Tarea ARQ — async nativa, sin Celery."""
|
|
141
|
+
db: AsyncSession = ctx["db"]
|
|
142
|
+
reporte = await generar_reporte(db, usuario_id, periodo)
|
|
143
|
+
await servicio_email.enviar_reporte(reporte)
|
|
144
|
+
return {"status": "enviado", "filas": reporte.total_filas}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class WorkerSettings:
|
|
148
|
+
functions = [enviar_reporte]
|
|
149
|
+
redis_settings = RedisSettings(host=settings.REDIS_HOST)
|
|
150
|
+
max_jobs = 10
|
|
151
|
+
job_timeout = 300 # 5 minutos máximo por tarea
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# Encolar desde un endpoint
|
|
155
|
+
async def encolar_reporte(usuario_id: str) -> str:
|
|
156
|
+
redis = await create_pool(RedisSettings(host=settings.REDIS_HOST))
|
|
157
|
+
job = await redis.enqueue_job("enviar_reporte", usuario_id, "2024-Q1")
|
|
158
|
+
return job.job_id
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## WebSockets con FastAPI — chat completo
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
# app/routers/chat.py
|
|
167
|
+
import asyncio
|
|
168
|
+
import json
|
|
169
|
+
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
|
|
170
|
+
from typing import Any
|
|
171
|
+
|
|
172
|
+
router = APIRouter()
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class SalaChat:
|
|
176
|
+
"""Gestiona conexiones WebSocket activas por sala."""
|
|
177
|
+
|
|
178
|
+
def __init__(self) -> None:
|
|
179
|
+
# sala_id -> {client_id -> WebSocket}
|
|
180
|
+
self._salas: dict[str, dict[str, WebSocket]] = {}
|
|
181
|
+
|
|
182
|
+
async def unirse(self, sala_id: str, client_id: str, ws: WebSocket) -> None:
|
|
183
|
+
await ws.accept()
|
|
184
|
+
if sala_id not in self._salas:
|
|
185
|
+
self._salas[sala_id] = {}
|
|
186
|
+
self._salas[sala_id][client_id] = ws
|
|
187
|
+
|
|
188
|
+
def salir(self, sala_id: str, client_id: str) -> None:
|
|
189
|
+
sala = self._salas.get(sala_id, {})
|
|
190
|
+
sala.pop(client_id, None)
|
|
191
|
+
if not sala:
|
|
192
|
+
self._salas.pop(sala_id, None)
|
|
193
|
+
|
|
194
|
+
async def broadcast_sala(self, sala_id: str, mensaje: dict[str, Any]) -> None:
|
|
195
|
+
sala = self._salas.get(sala_id, {})
|
|
196
|
+
desconectados: list[str] = []
|
|
197
|
+
texto = json.dumps(mensaje, ensure_ascii=False)
|
|
198
|
+
for cid, ws in sala.items():
|
|
199
|
+
try:
|
|
200
|
+
await ws.send_text(texto)
|
|
201
|
+
except Exception:
|
|
202
|
+
desconectados.append(cid)
|
|
203
|
+
for cid in desconectados:
|
|
204
|
+
self.salir(sala_id, cid)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
sala_manager = SalaChat()
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
async def _heartbeat(ws: WebSocket, intervalo: int = 30) -> None:
|
|
211
|
+
"""Ping periódico para detectar conexiones muertas."""
|
|
212
|
+
while True:
|
|
213
|
+
await asyncio.sleep(intervalo)
|
|
214
|
+
try:
|
|
215
|
+
await ws.send_text('{"tipo":"ping"}')
|
|
216
|
+
except Exception:
|
|
217
|
+
break # Conexión muerta — terminar coroutine
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
@router.websocket("/chat/{sala_id}/{usuario_id}")
|
|
221
|
+
async def endpoint_chat(
|
|
222
|
+
websocket: WebSocket,
|
|
223
|
+
sala_id: str,
|
|
224
|
+
usuario_id: str,
|
|
225
|
+
) -> None:
|
|
226
|
+
await sala_manager.unirse(sala_id, usuario_id, websocket)
|
|
227
|
+
heartbeat = asyncio.create_task(_heartbeat(websocket))
|
|
228
|
+
try:
|
|
229
|
+
while True:
|
|
230
|
+
texto = await websocket.receive_text()
|
|
231
|
+
mensaje = json.loads(texto)
|
|
232
|
+
await sala_manager.broadcast_sala(sala_id, {
|
|
233
|
+
"tipo": "mensaje",
|
|
234
|
+
"autor": usuario_id,
|
|
235
|
+
"contenido": mensaje.get("contenido", ""),
|
|
236
|
+
})
|
|
237
|
+
except WebSocketDisconnect:
|
|
238
|
+
pass
|
|
239
|
+
finally:
|
|
240
|
+
heartbeat.cancel()
|
|
241
|
+
sala_manager.salir(sala_id, usuario_id)
|
|
242
|
+
await sala_manager.broadcast_sala(sala_id, {
|
|
243
|
+
"tipo": "sistema",
|
|
244
|
+
"contenido": f"{usuario_id} salió de la sala",
|
|
245
|
+
})
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Server-Sent Events (SSE) con StreamingResponse
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
# app/routers/notificaciones.py
|
|
254
|
+
import asyncio
|
|
255
|
+
import json
|
|
256
|
+
from fastapi import APIRouter
|
|
257
|
+
from fastapi.responses import StreamingResponse
|
|
258
|
+
from typing import AsyncGenerator
|
|
259
|
+
|
|
260
|
+
router = APIRouter()
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
async def _stream_notificaciones(
|
|
264
|
+
usuario_id: str,
|
|
265
|
+
db: AsyncSession,
|
|
266
|
+
) -> AsyncGenerator[str, None]:
|
|
267
|
+
"""Genera eventos SSE. Envía keep-alive cada 15s para evitar timeout."""
|
|
268
|
+
try:
|
|
269
|
+
ultima_id = await obtener_ultima_notificacion_id(db, usuario_id)
|
|
270
|
+
while True:
|
|
271
|
+
nuevas = await obtener_notificaciones_nuevas(db, usuario_id, desde=ultima_id)
|
|
272
|
+
for notif in nuevas:
|
|
273
|
+
payload = json.dumps(notif.model_dump(), ensure_ascii=False)
|
|
274
|
+
yield f"id: {notif.id}\ndata: {payload}\n\n"
|
|
275
|
+
ultima_id = notif.id
|
|
276
|
+
if not nuevas:
|
|
277
|
+
# Keep-alive — evita que el proxy cierre la conexión inactiva
|
|
278
|
+
yield ": keep-alive\n\n"
|
|
279
|
+
await asyncio.sleep(2)
|
|
280
|
+
except asyncio.CancelledError:
|
|
281
|
+
pass # Cliente desconectado — salida limpia, no loggear como error
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
@router.get("/notificaciones/stream")
|
|
285
|
+
async def stream_notificaciones(
|
|
286
|
+
db: DbDep,
|
|
287
|
+
usuario: UsuarioActualDep,
|
|
288
|
+
) -> StreamingResponse:
|
|
289
|
+
return StreamingResponse(
|
|
290
|
+
_stream_notificaciones(usuario.id, db),
|
|
291
|
+
media_type="text/event-stream",
|
|
292
|
+
headers={
|
|
293
|
+
"Cache-Control": "no-cache",
|
|
294
|
+
"X-Accel-Buffering": "no", # Desactiva buffer en Nginx
|
|
295
|
+
"Connection": "keep-alive",
|
|
296
|
+
},
|
|
297
|
+
)
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Timeout y cancellation con asyncio.wait_for
|
|
303
|
+
|
|
304
|
+
```python
|
|
305
|
+
import asyncio
|
|
306
|
+
from fastapi import HTTPException
|
|
307
|
+
|
|
308
|
+
async def llamar_servicio_externo(url: str, payload: dict) -> dict:
|
|
309
|
+
"""Llama a un servicio externo con timeout estricto."""
|
|
310
|
+
try:
|
|
311
|
+
return await asyncio.wait_for(
|
|
312
|
+
_hacer_peticion(url, payload),
|
|
313
|
+
timeout=5.0, # 5 segundos máximo
|
|
314
|
+
)
|
|
315
|
+
except asyncio.TimeoutError:
|
|
316
|
+
raise HTTPException(
|
|
317
|
+
status_code=504,
|
|
318
|
+
detail="El servicio externo tardó demasiado en responder",
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
# Timeout en endpoints — cancelar la tarea si el cliente se desconecta
|
|
323
|
+
@router.post("/procesar-documento")
|
|
324
|
+
async def procesar_documento(archivo: UploadFile, db: DbDep) -> dict:
|
|
325
|
+
try:
|
|
326
|
+
resultado = await asyncio.wait_for(
|
|
327
|
+
servicio_ocr.procesar(await archivo.read()),
|
|
328
|
+
timeout=30.0,
|
|
329
|
+
)
|
|
330
|
+
return resultado
|
|
331
|
+
except asyncio.TimeoutError:
|
|
332
|
+
raise HTTPException(status_code=408, detail="Procesamiento excedió el tiempo límite")
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## Connection pooling con create_async_engine
|
|
338
|
+
|
|
339
|
+
```python
|
|
340
|
+
# app/db/session.py
|
|
341
|
+
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
|
|
342
|
+
|
|
343
|
+
engine = create_async_engine(
|
|
344
|
+
settings.DATABASE_URL, # postgresql+asyncpg://user:pass@host/db
|
|
345
|
+
pool_size=10, # Conexiones permanentes en el pool
|
|
346
|
+
max_overflow=20, # Conexiones adicionales bajo carga alta
|
|
347
|
+
pool_pre_ping=True, # Verifica conexiones muertas antes de usar
|
|
348
|
+
pool_recycle=3600, # Recicla conexiones cada hora (evita timeouts)
|
|
349
|
+
pool_timeout=30, # Tiempo máximo esperando una conexión libre
|
|
350
|
+
echo=settings.DEBUG, # Loggea SQL en desarrollo
|
|
351
|
+
echo_pool=settings.DEBUG, # Loggea eventos del pool en desarrollo
|
|
352
|
+
connect_args={
|
|
353
|
+
"server_settings": {
|
|
354
|
+
"application_name": settings.APP_NOMBRE, # Visible en pg_stat_activity
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
AsyncSessionLocal = async_sessionmaker(
|
|
360
|
+
engine,
|
|
361
|
+
expire_on_commit=False, # OBLIGATORIO — evita lazy loads post-commit en async
|
|
362
|
+
autoflush=False, # Control explícito de flush en services
|
|
363
|
+
class_=AsyncSession,
|
|
364
|
+
)
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## Evitar bloqueos en async: asyncio.to_thread
|
|
370
|
+
|
|
371
|
+
Código síncrono bloqueante dentro de una función `async` bloquea el event loop
|
|
372
|
+
completo. Usar `asyncio.to_thread` para ejecutarlo en un thread pool.
|
|
373
|
+
|
|
374
|
+
```python
|
|
375
|
+
import asyncio
|
|
376
|
+
from pathlib import Path
|
|
377
|
+
|
|
378
|
+
# MAL — bloquea el event loop mientras lee el archivo
|
|
379
|
+
async def leer_archivo_mal(ruta: Path) -> bytes:
|
|
380
|
+
return ruta.read_bytes() # I/O síncrono — bloquea
|
|
381
|
+
|
|
382
|
+
# BIEN — delega a thread pool, el event loop queda libre
|
|
383
|
+
async def leer_archivo(ruta: Path) -> bytes:
|
|
384
|
+
return await asyncio.to_thread(ruta.read_bytes)
|
|
385
|
+
|
|
386
|
+
# BIEN — operaciones CPU-intensivas fuera del event loop
|
|
387
|
+
async def comprimir_pdf(datos: bytes) -> bytes:
|
|
388
|
+
return await asyncio.to_thread(_comprimir_sincrono, datos)
|
|
389
|
+
|
|
390
|
+
def _comprimir_sincrono(datos: bytes) -> bytes:
|
|
391
|
+
"""Función síncrona que puede bloquearse sin problema en su propio thread."""
|
|
392
|
+
import gzip
|
|
393
|
+
return gzip.compress(datos, compresslevel=9)
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## AsyncGenerator para streaming de respuestas grandes
|
|
399
|
+
|
|
400
|
+
```python
|
|
401
|
+
# app/routers/exportar.py
|
|
402
|
+
import csv
|
|
403
|
+
import io
|
|
404
|
+
from fastapi.responses import StreamingResponse
|
|
405
|
+
|
|
406
|
+
async def _generar_csv_facturas(
|
|
407
|
+
db: AsyncSession,
|
|
408
|
+
empresa_id: str,
|
|
409
|
+
) -> AsyncGenerator[bytes, None]:
|
|
410
|
+
"""Genera CSV por lotes de 1000 registros para no saturar memoria."""
|
|
411
|
+
encabezado = io.StringIO()
|
|
412
|
+
writer = csv.writer(encabezado)
|
|
413
|
+
writer.writerow(["folio", "fecha", "cliente", "total", "estatus"])
|
|
414
|
+
yield encabezado.getvalue().encode("utf-8-sig") # BOM para Excel en Windows
|
|
415
|
+
|
|
416
|
+
offset = 0
|
|
417
|
+
lote = 1000
|
|
418
|
+
while True:
|
|
419
|
+
facturas = await obtener_facturas_lote(db, empresa_id, offset=offset, limit=lote)
|
|
420
|
+
if not facturas:
|
|
421
|
+
break
|
|
422
|
+
buffer = io.StringIO()
|
|
423
|
+
writer = csv.writer(buffer)
|
|
424
|
+
for f in facturas:
|
|
425
|
+
writer.writerow([f.folio, f.fecha, f.cliente.nombre, f.total, f.estatus])
|
|
426
|
+
yield buffer.getvalue().encode("utf-8-sig")
|
|
427
|
+
offset += lote
|
|
428
|
+
await asyncio.sleep(0) # yield al event loop entre lotes
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
@router.get("/facturas/exportar.csv")
|
|
432
|
+
async def exportar_csv(db: DbDep, usuario: UsuarioActualDep) -> StreamingResponse:
|
|
433
|
+
return StreamingResponse(
|
|
434
|
+
_generar_csv_facturas(db, usuario.empresa_id),
|
|
435
|
+
media_type="text/csv",
|
|
436
|
+
headers={"Content-Disposition": "attachment; filename=facturas.csv"},
|
|
437
|
+
)
|
|
438
|
+
```
|