@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,343 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: structured-outputs
|
|
3
|
+
description: >
|
|
4
|
+
Patrones para obtener respuestas estructuradas y validadas de LLMs usando
|
|
5
|
+
Pydantic v2. Incluye reask iterativo, validación semántica con LLM,
|
|
6
|
+
caching multinivel y hooks de ciclo de interacción. Cargar cuando se
|
|
7
|
+
construya cualquier sistema que requiera outputs JSON/tipados de un LLM,
|
|
8
|
+
o cuando un agente necesite respuestas validadas programáticamente.
|
|
9
|
+
version: "1.0.0"
|
|
10
|
+
herramientasPermitidas: [Read]
|
|
11
|
+
evolvable: true # default para skill estandar
|
|
12
|
+
exclusiones:
|
|
13
|
+
- "No cargar para validación de inputs de usuario en endpoints REST (formularios, API bodies) — para validación de inputs de API cargar `fastapi-experto` con Pydantic schemas de request."
|
|
14
|
+
- "No cargar para construir pipelines RAG o agentes con LangChain — para RAG cargar `rag-arquitectura`, para agentes cargar `langchain-langraph`."
|
|
15
|
+
- "No cargar para streaming de respuestas LLM en tiempo real donde no se necesita validación del output completo — el reask requiere la respuesta completa para validar."
|
|
16
|
+
- "No cargar para evaluar la calidad semántica de un corpus de documentos existentes sin pipeline LLM activo — para evaluación de calidad de documentos usar el skill de dominio correspondiente."
|
|
17
|
+
---
|
|
18
|
+
# Structured Outputs — Respuestas validadas de LLMs
|
|
19
|
+
|
|
20
|
+
## Cuándo NO cargar
|
|
21
|
+
|
|
22
|
+
- La tarea es validar inputs de usuario en endpoints REST: usar Pydantic schemas en FastAPI directamente.
|
|
23
|
+
- La tarea es construir un pipeline RAG: cargar `rag-arquitectura`.
|
|
24
|
+
- La tarea es construir agentes con LangGraph: cargar `langchain-langraph`.
|
|
25
|
+
- La tarea es streaming de LLM sin necesidad de validar el output completo: el reask requiere respuesta completa.
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## Problema que resuelve
|
|
29
|
+
|
|
30
|
+
Los LLMs generan texto libre. Cuando tu código necesita un objeto tipado
|
|
31
|
+
(JSON, modelo Pydantic, enum), parsear la respuesta directamente es frágil:
|
|
32
|
+
falla silenciosamente, requiere regex, y no hay garantía de estructura.
|
|
33
|
+
|
|
34
|
+
Structured outputs resuelve esto con un ciclo:
|
|
35
|
+
**generar → validar → reask si falla → retornar objeto tipado**.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Patrón core: response_model + validación Pydantic
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from pydantic import BaseModel, Field
|
|
43
|
+
from typing import Literal
|
|
44
|
+
|
|
45
|
+
class ExtractionResult(BaseModel):
|
|
46
|
+
entidades: list[str] = Field(..., min_length=1)
|
|
47
|
+
sentimiento: Literal["positivo", "negativo", "neutro"]
|
|
48
|
+
confianza: float = Field(..., ge=0.0, le=1.0)
|
|
49
|
+
resumen: str = Field(..., max_length=500)
|
|
50
|
+
|
|
51
|
+
# El modelo debe devolver JSON que valide contra este schema.
|
|
52
|
+
# Si falla validación → reask automático (ver sección siguiente).
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
La clave es definir el modelo Pydantic con **constraints reales**:
|
|
56
|
+
`min_length`, `ge/le`, `Literal`, `max_length`. Esto convierte errores
|
|
57
|
+
de formato en errores de validación detectables y corregibles.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Patrón reask — retry inteligente con feedback
|
|
62
|
+
|
|
63
|
+
El retry convencional repite la misma llamada. El **reask** envía el error
|
|
64
|
+
de validación de vuelta al LLM para que se auto-corrija:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
Intento 1: LLM genera → Pydantic valida → ValidationError("confianza must be <= 1.0")
|
|
68
|
+
Intento 2: LLM recibe error + respuesta previa → genera corregido → Pydantic valida → OK
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Implementación del ciclo reask
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from pydantic import ValidationError
|
|
75
|
+
import json
|
|
76
|
+
|
|
77
|
+
def obtener_structured(
|
|
78
|
+
llm_client,
|
|
79
|
+
mensajes: list[dict],
|
|
80
|
+
response_model: type[BaseModel],
|
|
81
|
+
max_retries: int = 3,
|
|
82
|
+
) -> BaseModel:
|
|
83
|
+
"""Ciclo reask: genera, valida, corrige hasta max_retries."""
|
|
84
|
+
intentos_fallidos = []
|
|
85
|
+
|
|
86
|
+
for intento in range(1, max_retries + 1):
|
|
87
|
+
# Agregar errores previos como contexto
|
|
88
|
+
mensajes_con_feedback = mensajes.copy()
|
|
89
|
+
if intentos_fallidos:
|
|
90
|
+
feedback = "\n".join(
|
|
91
|
+
f"Intento {i}: {err}" for i, err in enumerate(intentos_fallidos, 1)
|
|
92
|
+
)
|
|
93
|
+
mensajes_con_feedback.append({
|
|
94
|
+
"role": "user",
|
|
95
|
+
"content": (
|
|
96
|
+
f"Tu respuesta anterior no pasó validación:\n{feedback}\n\n"
|
|
97
|
+
f"Corrige y responde de nuevo en JSON válido según el schema."
|
|
98
|
+
),
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
respuesta = llm_client.messages.create(
|
|
102
|
+
model="claude-sonnet-4-6-20250514",
|
|
103
|
+
max_tokens=2048,
|
|
104
|
+
system=f"Responde SOLO en JSON válido según este schema:\n"
|
|
105
|
+
f"{json.dumps(response_model.model_json_schema(), indent=2)}",
|
|
106
|
+
messages=mensajes_con_feedback,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
texto = respuesta.content[0].text
|
|
111
|
+
resultado = response_model.model_validate_json(texto)
|
|
112
|
+
return resultado
|
|
113
|
+
except (ValidationError, json.JSONDecodeError) as e:
|
|
114
|
+
intentos_fallidos.append(str(e)[:200])
|
|
115
|
+
|
|
116
|
+
raise RuntimeError(
|
|
117
|
+
f"Reask agotado después de {max_retries} intentos. "
|
|
118
|
+
f"Errores: {intentos_fallidos}"
|
|
119
|
+
)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Diferencia con retry genérico
|
|
123
|
+
|
|
124
|
+
| Aspecto | Retry genérico | Reask |
|
|
125
|
+
|---------|---------------|-------|
|
|
126
|
+
| Feedback al LLM | Ninguno — repite idéntico | Envía error de validación |
|
|
127
|
+
| Probabilidad de éxito | Baja (misma entrada = misma salida) | Alta (LLM corrige con contexto) |
|
|
128
|
+
| Tokens consumidos | Duplicados sin valor | Incrementales con información nueva |
|
|
129
|
+
| Trazabilidad | Solo cuenta de intentos | Errores específicos por intento |
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Validación semántica con LLM (llm_validator)
|
|
134
|
+
|
|
135
|
+
Pydantic valida **estructura**. Un LLM puede validar **semántica**:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from pydantic import field_validator
|
|
139
|
+
|
|
140
|
+
class ResumenDocumento(BaseModel):
|
|
141
|
+
titulo: str
|
|
142
|
+
resumen: str
|
|
143
|
+
temas: list[str]
|
|
144
|
+
|
|
145
|
+
@field_validator("resumen")
|
|
146
|
+
@classmethod
|
|
147
|
+
def validar_resumen_es_coherente(cls, v: str) -> str:
|
|
148
|
+
"""Validación estática: longitud mínima y sin placeholders."""
|
|
149
|
+
if len(v) < 50:
|
|
150
|
+
raise ValueError("Resumen demasiado corto (mínimo 50 caracteres)")
|
|
151
|
+
placeholders = ["PENDIENTE", "PLACEHOLDER", "...", "[insertar"]
|
|
152
|
+
for p in placeholders:
|
|
153
|
+
if p.lower() in v.lower():
|
|
154
|
+
raise ValueError(f"Resumen contiene placeholder: '{p}'")
|
|
155
|
+
return v
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def validar_semanticamente(
|
|
159
|
+
llm_juez,
|
|
160
|
+
respuesta: BaseModel,
|
|
161
|
+
regla: str,
|
|
162
|
+
) -> tuple[bool, str]:
|
|
163
|
+
"""Usa un LLM como juez para validar restricciones semánticas."""
|
|
164
|
+
prompt = f"""Evalúa si esta respuesta cumple la regla.
|
|
165
|
+
|
|
166
|
+
Regla: {regla}
|
|
167
|
+
Respuesta: {respuesta.model_dump_json(indent=2)}
|
|
168
|
+
|
|
169
|
+
Responde SOLO con JSON: {{"cumple": true/false, "razon": "..."}}"""
|
|
170
|
+
|
|
171
|
+
resultado = llm_juez.messages.create(
|
|
172
|
+
model="claude-haiku-4-5-20251001", # Haiku es suficiente para validación
|
|
173
|
+
max_tokens=256,
|
|
174
|
+
messages=[{"role": "user", "content": prompt}],
|
|
175
|
+
)
|
|
176
|
+
import json
|
|
177
|
+
evaluacion = json.loads(resultado.content[0].text)
|
|
178
|
+
return evaluacion["cumple"], evaluacion["razon"]
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Cuándo usar**: Cuando las restricciones no son expresables como tipos
|
|
182
|
+
(ej: "el resumen no contradice el documento original", "las entidades
|
|
183
|
+
mencionadas existen en el contexto dado").
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Caching de respuestas LLM — 3 niveles
|
|
188
|
+
|
|
189
|
+
Cachear respuestas validadas evita llamadas redundantes al LLM.
|
|
190
|
+
|
|
191
|
+
### Estrategia de clave de caché
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
import hashlib
|
|
195
|
+
import json
|
|
196
|
+
|
|
197
|
+
def generar_cache_key(
|
|
198
|
+
modelo: str,
|
|
199
|
+
mensajes: list[dict],
|
|
200
|
+
response_model: type[BaseModel],
|
|
201
|
+
) -> str:
|
|
202
|
+
"""Clave basada en modelo + mensajes + schema del response_model."""
|
|
203
|
+
payload = json.dumps({
|
|
204
|
+
"model": modelo,
|
|
205
|
+
"messages": mensajes,
|
|
206
|
+
"schema_hash": hashlib.sha256(
|
|
207
|
+
json.dumps(response_model.model_json_schema()).encode()
|
|
208
|
+
).hexdigest()[:16],
|
|
209
|
+
}, sort_keys=True)
|
|
210
|
+
return hashlib.sha256(payload.encode()).hexdigest()
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Niveles de caché
|
|
214
|
+
|
|
215
|
+
| Nivel | Tecnología | TTL | Cuándo usar |
|
|
216
|
+
|-------|-----------|-----|-------------|
|
|
217
|
+
| L1 | `functools.lru_cache` | Duración del proceso | Misma sesión, mismas queries |
|
|
218
|
+
| L2 | Disco (`shelve`/`diskcache`) | Horas/días | Queries repetitivos entre sesiones |
|
|
219
|
+
| L3 | Redis (`SETEX`) | Configurable | Múltiples instancias/usuarios |
|
|
220
|
+
|
|
221
|
+
### Invalidación
|
|
222
|
+
|
|
223
|
+
La clave incluye el hash del schema Pydantic. Si cambias el modelo
|
|
224
|
+
(agregas un campo, cambias un tipo), la clave cambia automáticamente
|
|
225
|
+
y el caché se invalida sin intervención manual.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Hooks de ciclo de interacción LLM
|
|
230
|
+
|
|
231
|
+
Puntos de intercepción en el ciclo genera→valida→reask:
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
from typing import Callable, Any
|
|
235
|
+
from dataclasses import dataclass, field
|
|
236
|
+
from enum import Enum
|
|
237
|
+
|
|
238
|
+
class HookLLM(Enum):
|
|
239
|
+
ANTES_LLAMADA = "antes_llamada" # Antes de enviar al LLM
|
|
240
|
+
DESPUES_RESPUESTA = "despues_respuesta" # Respuesta recibida (antes de validar)
|
|
241
|
+
ERROR_VALIDACION = "error_validacion" # Validación Pydantic falló
|
|
242
|
+
ULTIMO_INTENTO = "ultimo_intento" # Último reask antes de fallar
|
|
243
|
+
ERROR_API = "error_api" # Error de red/API
|
|
244
|
+
|
|
245
|
+
@dataclass
|
|
246
|
+
class HooksLLM:
|
|
247
|
+
_handlers: dict[HookLLM, list[Callable]] = field(default_factory=dict)
|
|
248
|
+
|
|
249
|
+
def on(self, evento: HookLLM, handler: Callable) -> None:
|
|
250
|
+
self._handlers.setdefault(evento, []).append(handler)
|
|
251
|
+
|
|
252
|
+
def emit(self, evento: HookLLM, **kwargs: Any) -> None:
|
|
253
|
+
for handler in self._handlers.get(evento, []):
|
|
254
|
+
handler(**kwargs)
|
|
255
|
+
|
|
256
|
+
# Uso: tracking de tokens acumulativo por reintentos
|
|
257
|
+
hooks = HooksLLM()
|
|
258
|
+
tokens_acumulados = {"input": 0, "output": 0}
|
|
259
|
+
|
|
260
|
+
def rastrear_tokens(**kwargs):
|
|
261
|
+
usage = kwargs.get("usage", {})
|
|
262
|
+
tokens_acumulados["input"] += usage.get("input_tokens", 0)
|
|
263
|
+
tokens_acumulados["output"] += usage.get("output_tokens", 0)
|
|
264
|
+
|
|
265
|
+
hooks.on(HookLLM.DESPUES_RESPUESTA, rastrear_tokens)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Tipos de response_model por caso de uso
|
|
271
|
+
|
|
272
|
+
| Caso | Patrón | Ejemplo |
|
|
273
|
+
|------|--------|---------|
|
|
274
|
+
| Extracción simple | `BaseModel` con campos tipados | `class Usuario(BaseModel): nombre: str; edad: int` |
|
|
275
|
+
| Clasificación | `Literal` o `Enum` | `categoria: Literal["bug", "feature", "pregunta"]` |
|
|
276
|
+
| Respuesta opcional | `BaseModel` con campo `encontrado: bool` | Cuando el LLM puede no tener la respuesta |
|
|
277
|
+
| Lista de items | `list[MiModelo]` como campo | `items: list[Producto]` |
|
|
278
|
+
| Uniones discriminadas | `Annotated[Union[...], Field(discriminator="tipo")]` | Diferentes schemas según el tipo |
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Interfaz unificada multi-proveedor (referencia)
|
|
283
|
+
|
|
284
|
+
El patrón `from_provider` abstrae el proveedor de LLM:
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
# Patrón conceptual — no es una dependencia de swl-ses
|
|
288
|
+
# Referencia: instructor.from_provider()
|
|
289
|
+
|
|
290
|
+
# OpenAI
|
|
291
|
+
client = crear_cliente_structured("openai", model="gpt-4o")
|
|
292
|
+
|
|
293
|
+
# Anthropic
|
|
294
|
+
client = crear_cliente_structured("anthropic", model="claude-sonnet-4-6-20250514")
|
|
295
|
+
|
|
296
|
+
# La interfaz es idéntica: client.create(response_model=MiModelo, messages=[...])
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**Nota**: swl-ses es mono-proveedor (Anthropic). Este patrón se documenta
|
|
300
|
+
como referencia para proyectos que necesiten soportar múltiples LLMs.
|
|
301
|
+
El `hooks/lib/model-router.js` existente ya hace routing por complejidad
|
|
302
|
+
dentro de Anthropic — podría evolucionar a routing por proveedor si se requiere.
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## MUST DO / MUST NOT DO
|
|
307
|
+
|
|
308
|
+
### Obligatorio
|
|
309
|
+
- Definir constraints reales en Pydantic (`Field(ge=0)`, `Literal[...]`, `max_length`)
|
|
310
|
+
- Usar reask (no retry ciego) cuando la respuesta del LLM falla validación
|
|
311
|
+
- Incluir el JSON Schema del modelo en el system prompt
|
|
312
|
+
- Trackear tokens acumulados incluyendo reintentos, no solo la llamada exitosa
|
|
313
|
+
- Cachear respuestas validadas con clave que incluya hash del schema
|
|
314
|
+
|
|
315
|
+
### Prohibido
|
|
316
|
+
- NUNCA parsear JSON con regex — usar `model_validate_json()`
|
|
317
|
+
- NUNCA reintentar sin enviar el error de vuelta al LLM (retry ciego)
|
|
318
|
+
- NUNCA confiar en la respuesta sin validación Pydantic
|
|
319
|
+
- NUNCA usar `temperature > 0.3` para structured outputs (reduce determinismo)
|
|
320
|
+
- NUNCA cachear respuestas que fallaron validación
|
|
321
|
+
- NUNCA usar `Any` como tipo en response_model — pierde toda garantía
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Tabla de referencias
|
|
326
|
+
|
|
327
|
+
| Tema | Archivo |
|
|
328
|
+
|------|---------|
|
|
329
|
+
| Patrones avanzados: prompt chaining, LLM-as-judge, caching | [prompt-engineering/recursos/patrones-avanzados.md](../prompt-engineering/recursos/patrones-avanzados.md) |
|
|
330
|
+
| RAG con structured outputs | [langchain-langraph/SKILL.md](../langchain-langraph/SKILL.md) |
|
|
331
|
+
| Validación de schemas JSON | [schemas/](../../schemas/) |
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Gotchas / Errores comunes no obvios
|
|
336
|
+
|
|
337
|
+
**`model_validate_json()` lanza `ValidationError` con el mensaje "1 validation error for X: JSON input should be string, bytes or bytearray" cuando el output del LLM envuelve el JSON en bloques de código Markdown**: Claude y GPT frecuentemente responden con ` ```json\n{...}\n``` ` aunque el system prompt diga "responde SOLO en JSON". Causa: los modelos fueron entrenados en conversaciones donde el JSON se muestra en bloques de código por legibilidad; el system prompt de "solo JSON" reduce pero no elimina este comportamiento, especialmente en Haiku y GPT-3.5. Fix: antes de llamar a `model_validate_json()`, limpiar el texto con `texto = re.sub(r'^```(?:json)?\s*|\s*```$', '', texto.strip())`. Alternativamente, usar `tool_use` de Anthropic o `response_format` de OpenAI para forzar JSON estructurado a nivel de API.
|
|
338
|
+
|
|
339
|
+
**El ciclo reask con `max_retries=3` puede consumir 6-9x más tokens que una llamada exitosa sin que el código lo detecte porque los tokens de los intentos fallidos se acumulan silenciosamente**: un sistema que procesa 1,000 documentos por hora y tiene 15% de tasa de fallo en el primer intento gasta 1.15x los tokens esperados. Si el 5% necesita el tercer reintento, el gasto real es 1.35x. Sin tracking explícito, el costo de producción supera el estimado del prototipo en 20-40%. Causa: el código de ejemplo del skill acumula `intentos_fallidos` pero no registra el total de tokens usados incluyendo los intentos fallidos. Fix: registrar `usage.input_tokens + usage.output_tokens` por cada intento (exitoso o fallido) y emitir el total acumulado como métrica. Establecer un alerta si el ratio `tokens_reales / tokens_exitosos` supera 1.5x.
|
|
340
|
+
|
|
341
|
+
**La validación semántica con LLM juez (`validar_semanticamente`) genera un loop infinito cuando el campo `cumple: false` + `reask` del ciclo exterior incluye el error del juez en el mensaje de corrección pero el LLM principal no puede satisfacer la regla semántica**: si la regla es "el resumen no debe contradecir el documento original" y el documento original no está en el contexto de corrección, el LLM nunca puede saber qué contradice. Agota los 3 reintentos, eleva `RuntimeError`, y el pipeline se detiene. Causa: la validación semántica con LLM juez verifica constraints que el LLM corrector no puede satisfacer sin el contexto completo de la regla. Fix: al incluir el error del juez en el mensaje de reask, incluir también el contexto relevante: "Tu resumen contradice el siguiente fragmento del documento original: [cita]". Si el contexto es demasiado grande para el mensaje de reask, reducir el scope de la validación semántica.
|
|
342
|
+
|
|
343
|
+
**`functools.lru_cache` como L1 de caché de embeddings en un servidor FastAPI con múltiples workers no comparte el caché entre procesos, causando que cada worker tenga su propio caché independiente y se llame a la API de embeddings N veces para la misma query en N workers**: con 4 workers uvicorn, la misma query frecuente ("¿cuáles son los horarios?") llama a la API de embeddings 4 veces en lugar de 1. El L1 aparece con 100% de hit rate en cada worker individualmente, pero el hit rate real del sistema es mucho menor. Causa: `lru_cache` es por-proceso; en producción con multiprocessing, el caché no es compartido. Fix: para el L1 inter-proceso, usar una variable de módulo compartida con `multiprocessing.Manager().dict()` o, más simple, subir directamente al L2 (diskcache/Redis) que sí es compartido entre workers.
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: swift-experto
|
|
3
|
+
description: >
|
|
4
|
+
Swift moderno con SwiftUI, @Observable (iOS 17+), NavigationStack,
|
|
5
|
+
async/await con TaskGroup, actors para concurrencia segura, SwiftData,
|
|
6
|
+
Keychain y property wrappers. Cargar cuando se implementen vistas SwiftUI,
|
|
7
|
+
concurrencia Swift, modelos de datos SwiftData, o servicios iOS/macOS.
|
|
8
|
+
version: "1.0.0"
|
|
9
|
+
herramientasPermitidas: [Read, Grep]
|
|
10
|
+
exclusiones:
|
|
11
|
+
- "No cargar para diseñar patrones arquitectónicos Swift (TCA, MVVM, DI con protocolos) — para diseño cargar `swift-patrones`."
|
|
12
|
+
- "No cargar para escribir tests Swift (swift-testing, XCTest, mocks con protocolos) — para testing cargar `swift-testing`."
|
|
13
|
+
- "No cargar para errores de compilación Swift o Xcode — para build errors cargar `build-errors-swift`."
|
|
14
|
+
- "No cargar para Jetpack Compose o Android — Swift/SwiftUI es específico de Apple platforms; para Android cargar `kotlin-compose`."
|
|
15
|
+
evolvable: true # default para skill estandar
|
|
16
|
+
---
|
|
17
|
+
# Swift Experto — SwiftUI, Concurrencia y SwiftData
|
|
18
|
+
|
|
19
|
+
## Cuándo NO cargar
|
|
20
|
+
|
|
21
|
+
- La pregunta es sobre diseñar patrones arquitectónicos Swift (TCA, MVVM, dependency injection con protocolos) — cargar `swift-patrones`.
|
|
22
|
+
- La tarea es escribir tests Swift con swift-testing o XCTest — cargar `swift-testing`.
|
|
23
|
+
- Los errores son de compilación Swift o Xcode (provisioning, signing) — cargar `build-errors-swift`.
|
|
24
|
+
- El proyecto es Android con Kotlin/Compose — SwiftUI es específico de Apple platforms; cargar `kotlin-compose`.
|
|
25
|
+
|
|
26
|
+
## SwiftUI — View Lifecycle y Estructura
|
|
27
|
+
|
|
28
|
+
```swift
|
|
29
|
+
// Estructura recomendada de una Feature
|
|
30
|
+
// Features/Facturas/
|
|
31
|
+
// FacturasView.swift
|
|
32
|
+
// FacturasViewModel.swift
|
|
33
|
+
// FacturasRepository.swift
|
|
34
|
+
// Models/Factura.swift
|
|
35
|
+
|
|
36
|
+
struct FacturasView: View {
|
|
37
|
+
@State private var viewModel = FacturasViewModel() // @Observable — iOS 17+
|
|
38
|
+
|
|
39
|
+
var body: some View {
|
|
40
|
+
NavigationStack {
|
|
41
|
+
content
|
|
42
|
+
.navigationTitle("Facturas")
|
|
43
|
+
.toolbar { toolbarItems }
|
|
44
|
+
}
|
|
45
|
+
.task { await viewModel.cargar() } // task: se cancela con la vista
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@ViewBuilder
|
|
49
|
+
private var content: some View {
|
|
50
|
+
switch viewModel.estado {
|
|
51
|
+
case .cargando: ProgressView()
|
|
52
|
+
case .vacio: ContentUnavailableView("Sin facturas", systemImage: "doc.text")
|
|
53
|
+
case .error(let msg): ErrorView(mensaje: msg)
|
|
54
|
+
case .exito(let facturas): listaFacturas(facturas)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private func listaFacturas(_ facturas: [Factura]) -> some View {
|
|
59
|
+
List(facturas) { factura in
|
|
60
|
+
NavigationLink(value: factura) {
|
|
61
|
+
FacturaRow(factura: factura)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
.navigationDestination(for: Factura.self) { factura in
|
|
65
|
+
DetalleFacturaView(factura: factura)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## @Observable — iOS 17+ (preferir sobre ObservableObject)
|
|
74
|
+
|
|
75
|
+
```swift
|
|
76
|
+
// @Observable: el sistema de dependencias es automático y granular
|
|
77
|
+
// Solo recompone las vistas que leen la propiedad que cambió
|
|
78
|
+
@Observable
|
|
79
|
+
final class FacturasViewModel {
|
|
80
|
+
var estado: EstadoFacturas = .cargando
|
|
81
|
+
var query: String = ""
|
|
82
|
+
|
|
83
|
+
// @Observable NO necesita @Published
|
|
84
|
+
// Las propiedades privadas no rastreadas se marcan con @ObservationIgnored
|
|
85
|
+
@ObservationIgnored private var tarea: Task<Void, Never>?
|
|
86
|
+
|
|
87
|
+
var facturasFiltradas: [Factura] {
|
|
88
|
+
guard case .exito(let facturas) = estado else { return [] }
|
|
89
|
+
return query.isEmpty ? facturas : facturas.filter {
|
|
90
|
+
$0.folio.localizedCaseInsensitiveContains(query)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
func cargar() async {
|
|
95
|
+
estado = .cargando
|
|
96
|
+
do {
|
|
97
|
+
let facturas = try await repositorio.obtener()
|
|
98
|
+
estado = .exito(facturas)
|
|
99
|
+
} catch {
|
|
100
|
+
estado = .error(error.localizedDescription)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
enum EstadoFacturas {
|
|
106
|
+
case cargando
|
|
107
|
+
case vacio
|
|
108
|
+
case exito([Factura])
|
|
109
|
+
case error(String)
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Async/Await con TaskGroup
|
|
116
|
+
|
|
117
|
+
```swift
|
|
118
|
+
// Carga paralela de múltiples recursos
|
|
119
|
+
func cargarDashboard() async throws -> Dashboard {
|
|
120
|
+
async let facturas = api.obtenerFacturas()
|
|
121
|
+
async let clientes = api.obtenerClientes()
|
|
122
|
+
async let estadisticas = api.obtenerEstadisticas()
|
|
123
|
+
|
|
124
|
+
// Espera todos en paralelo
|
|
125
|
+
return try await Dashboard(
|
|
126
|
+
facturas: facturas,
|
|
127
|
+
clientes: clientes,
|
|
128
|
+
estadisticas: estadisticas
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// TaskGroup para cantidad dinámica de tareas
|
|
133
|
+
func procesarFacturas(_ ids: [String]) async throws -> [FacturaDto] {
|
|
134
|
+
try await withThrowingTaskGroup(of: FacturaDto.self) { group in
|
|
135
|
+
for id in ids {
|
|
136
|
+
group.addTask { try await api.obtenerFactura(id: id) }
|
|
137
|
+
}
|
|
138
|
+
var resultados: [FacturaDto] = []
|
|
139
|
+
for try await dto in group {
|
|
140
|
+
resultados.append(dto)
|
|
141
|
+
}
|
|
142
|
+
return resultados
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Actors — Concurrencia Segura
|
|
150
|
+
|
|
151
|
+
```swift
|
|
152
|
+
// Actor: acceso exclusivo a estado compartido desde múltiples tareas
|
|
153
|
+
actor CachFacturas {
|
|
154
|
+
private var cache: [String: Factura] = [:]
|
|
155
|
+
private var timestamps: [String: Date] = [:]
|
|
156
|
+
private let ttl: TimeInterval = 300 // 5 minutos
|
|
157
|
+
|
|
158
|
+
func obtener(id: String) -> Factura? {
|
|
159
|
+
guard let ts = timestamps[id], Date().timeIntervalSince(ts) < ttl else {
|
|
160
|
+
return nil
|
|
161
|
+
}
|
|
162
|
+
return cache[id]
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
func guardar(_ factura: Factura) {
|
|
166
|
+
cache[factura.id] = factura
|
|
167
|
+
timestamps[factura.id] = Date()
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
func invalidar(id: String) {
|
|
171
|
+
cache.removeValue(forKey: id)
|
|
172
|
+
timestamps.removeValue(forKey: id)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// @MainActor — garantiza ejecución en hilo principal
|
|
177
|
+
@MainActor
|
|
178
|
+
final class FacturasViewModel: ObservableObject {
|
|
179
|
+
func actualizarUI() {
|
|
180
|
+
// Siempre en main thread, sin DispatchQueue.main.async
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## URLSession Async
|
|
188
|
+
|
|
189
|
+
```swift
|
|
190
|
+
struct FacturasApiClient {
|
|
191
|
+
private let session: URLSession
|
|
192
|
+
private let decoder: JSONDecoder = {
|
|
193
|
+
let d = JSONDecoder()
|
|
194
|
+
d.keyDecodingStrategy = .convertFromSnakeCase
|
|
195
|
+
d.dateDecodingStrategy = .iso8601
|
|
196
|
+
return d
|
|
197
|
+
}()
|
|
198
|
+
|
|
199
|
+
func obtenerFacturas(empresaId: String) async throws -> [FacturaDto] {
|
|
200
|
+
let url = URL(string: "\(baseURL)/v1/facturas?empresa_id=\(empresaId)")!
|
|
201
|
+
var request = URLRequest(url: url)
|
|
202
|
+
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
|
|
203
|
+
request.timeoutInterval = 30
|
|
204
|
+
|
|
205
|
+
let (data, response) = try await session.data(for: request)
|
|
206
|
+
|
|
207
|
+
guard let http = response as? HTTPURLResponse else {
|
|
208
|
+
throw ApiError.respuestaInvalida
|
|
209
|
+
}
|
|
210
|
+
guard (200..<300).contains(http.statusCode) else {
|
|
211
|
+
throw ApiError.statusCode(http.statusCode)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return try decoder.decode([FacturaDto].self, from: data)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## SwiftData
|
|
222
|
+
|
|
223
|
+
```swift
|
|
224
|
+
// Modelo
|
|
225
|
+
@Model
|
|
226
|
+
final class Factura {
|
|
227
|
+
@Attribute(.unique) var id: String
|
|
228
|
+
var folio: String
|
|
229
|
+
var total: Double
|
|
230
|
+
var fechaEmision: Date
|
|
231
|
+
var estatus: String
|
|
232
|
+
|
|
233
|
+
@Relationship(deleteRule: .cascade) var items: [ItemFactura] = []
|
|
234
|
+
|
|
235
|
+
init(id: String, folio: String, total: Double) {
|
|
236
|
+
self.id = id
|
|
237
|
+
self.folio = folio
|
|
238
|
+
self.total = total
|
|
239
|
+
self.fechaEmision = Date()
|
|
240
|
+
self.estatus = "borrador"
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Configuración en App
|
|
245
|
+
@main
|
|
246
|
+
struct MiApp: App {
|
|
247
|
+
var body: some Scene {
|
|
248
|
+
WindowGroup {
|
|
249
|
+
ContentView()
|
|
250
|
+
}
|
|
251
|
+
.modelContainer(for: [Factura.self, ItemFactura.self])
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Uso en Vista
|
|
256
|
+
struct FacturasView: View {
|
|
257
|
+
@Query(sort: \Factura.fechaEmision, order: .reverse) var facturas: [Factura]
|
|
258
|
+
@Environment(\.modelContext) private var ctx
|
|
259
|
+
|
|
260
|
+
func eliminar(_ factura: Factura) {
|
|
261
|
+
ctx.delete(factura)
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
Para la implementación completa del Keychain wrapper (guardar, leer, eliminar)
|
|
269
|
+
y property wrappers personalizados (@Clamped, @Trimmed), ver
|
|
270
|
+
[recursos/keychain-y-wrappers.md](recursos/keychain-y-wrappers.md).
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Anti-patrones
|
|
275
|
+
|
|
276
|
+
### Force unwrap — NUNCA en producción
|
|
277
|
+
|
|
278
|
+
```swift
|
|
279
|
+
// MAL — crash en runtime
|
|
280
|
+
let factura = facturas.first!
|
|
281
|
+
let url = URL(string: urlString)!
|
|
282
|
+
|
|
283
|
+
// BIEN — manejo explícito
|
|
284
|
+
guard let factura = facturas.first else { return }
|
|
285
|
+
guard let url = URL(string: urlString) else {
|
|
286
|
+
logger.error("URL inválida: \(urlString)")
|
|
287
|
+
return
|
|
288
|
+
}
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Massive View y ObservableObject con muchas @Published
|
|
292
|
+
|
|
293
|
+
```swift
|
|
294
|
+
// MAL — body con 200+ líneas mezclando lógica y UI
|
|
295
|
+
// BIEN — extraer sub-vistas (@ViewBuilder) y mover lógica al ViewModel
|
|
296
|
+
|
|
297
|
+
// En iOS 17+, migrar de ObservableObject/@Published a @Observable.
|
|
298
|
+
// @Observable solo recompone las vistas que leen la propiedad que cambió.
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## Gotchas / Errores comunes no obvios
|
|
304
|
+
|
|
305
|
+
**`@State` inicializado con un valor que depende de una propiedad del padre no se actualiza cuando el padre cambia**: `@State var total = calcular(items)` se inicializa una vez cuando la vista se crea — si `items` cambia en el padre, `total` no se recalcula. Causa: `@State` es estado local de la vista, no derivado. Fix: usar `@Binding` si el padre necesita controlar el valor, o calcular `total` como una propiedad computada en la vista (sin `@State`) si no necesita ser modificada localmente.
|
|
306
|
+
|
|
307
|
+
**`Task { await ... }` lanzado en `.onAppear` no se cancela cuando la vista desaparece**: si la vista desaparece antes de que el Task termine, el Task sigue ejecutando aunque la vista ya no exista, potencialmente modificando estado stale. Fix: usar `.task { await ... }` (el modificador de SwiftUI, no `Task { }`) — `.task` cancela automáticamente la tarea cuando la vista desaparece.
|
|
308
|
+
|
|
309
|
+
**`actor` con métodos que retornan tipos no-`Sendable` causa errores de concurrencia en Swift 6**: si un `actor` tiene un método que retorna `UIImage` y `UIImage` no es `Sendable`, el compilador con Swift 6 concurrency checks habilitado lanza error. Causa: los valores retornados de actores cruzan isolation boundaries. Fix: marcar los tipos de retorno como `Sendable`, o usar `@unchecked Sendable` con justificación, o rediseñar para que el actor retorne solo tipos value (structs, enums).
|
|
310
|
+
|
|
311
|
+
**`SwiftData` con `@Query` en una vista no se actualiza si el `ModelContext` usado para la mutación es diferente al del `@Environment`**: si se crea un `ModelContext` secundario para inserciones en background y se guarda, la vista con `@Query` del contexto principal puede no reflejar los cambios hasta el próximo ciclo de runloop. Fix: usar el `modelContext` del `@Environment(\.modelContext)` para mutaciones en el hilo principal, o llamar `try context.save()` explícitamente después de las mutaciones en background.
|
|
312
|
+
|
|
313
|
+
## Checklist Swift
|
|
314
|
+
|
|
315
|
+
- [ ] Usar @Observable en lugar de ObservableObject (iOS 17+)
|
|
316
|
+
- [ ] Sin force unwrap (!) en código de producción
|
|
317
|
+
- [ ] Tokens y datos sensibles en Keychain, nunca en UserDefaults
|
|
318
|
+
- [ ] .task { } en lugar de .onAppear + Task { } para cargas async
|
|
319
|
+
- [ ] Sendable conformance para tipos que cruzan límites de actor
|
|
320
|
+
- [ ] CancellationError no atrapado silenciosamente en catches genéricos
|