@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,500 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mobile-ios-swl
|
|
3
|
+
description: >
|
|
4
|
+
Especialista en desarrollo iOS nativo con Swift y SwiftUI/UIKit. Invocar cuando
|
|
5
|
+
se necesita implementar vistas con SwiftUI moderno (iOS 16+), gestión de estado
|
|
6
|
+
con @Observable o TCA (The Composable Architecture), persistencia local con
|
|
7
|
+
Core Data o SwiftData, networking con URLSession o Alamofire, navegación con
|
|
8
|
+
NavigationStack, o arquitectura MVVM-C. También invocar para decisiones de
|
|
9
|
+
SwiftUI vs UIKit, profiling con Instruments, o escribir tests con XCTest y
|
|
10
|
+
snapshot testing. NO invocar para Android, backend, o multiplataforma —
|
|
11
|
+
esos corresponden a mobile-android-swl, implementador-swl y
|
|
12
|
+
mobile-cross-swl. Siempre carga manejo-errores antes de implementar
|
|
13
|
+
cualquier capa de red o persistencia.
|
|
14
|
+
tools: Read, Write, Edit, Bash, Grep, Glob, Skill
|
|
15
|
+
model: claude-sonnet-4-6
|
|
16
|
+
modeloAlterno: claude-haiku-4-5-20251001
|
|
17
|
+
ventanaContexto: 200k
|
|
18
|
+
permissionMode: acceptEdits
|
|
19
|
+
color: blue
|
|
20
|
+
version: 1.0.0
|
|
21
|
+
nivelRiesgo: MEDIO
|
|
22
|
+
skillsInvocables: manejo-errores, auth-patrones, accesibilidad-a11y, swift-experto, swift-patrones, swift-testing
|
|
23
|
+
skillsRestringidos: fastapi-experto, angular-moderno, react-native-best-practices
|
|
24
|
+
permisosRed: false
|
|
25
|
+
permisosEscritura: true
|
|
26
|
+
permisosComandos: true
|
|
27
|
+
toolBudget:
|
|
28
|
+
simple: 15
|
|
29
|
+
standard: 30
|
|
30
|
+
complex: 60
|
|
31
|
+
evolvable: true
|
|
32
|
+
evolvable_scope: [description, examples, instructions]
|
|
33
|
+
invariantes:
|
|
34
|
+
- campo: nivelRiesgo
|
|
35
|
+
operador: eq
|
|
36
|
+
valor: MEDIO
|
|
37
|
+
razon: Este agente no debe escalar riesgo sin ADR explicito.
|
|
38
|
+
exclusiones:
|
|
39
|
+
- "No invocar para desarrollo Android — ese trabajo corresponde a mobile-android-swl."
|
|
40
|
+
- "No invocar para desarrollo multiplataforma (React Native, Flutter) — usar mobile-cross-swl."
|
|
41
|
+
- "No invocar para backend, APIs o bases de datos remotas — usar implementador-swl o backend-*-swl."
|
|
42
|
+
---
|
|
43
|
+
# Mobile iOS
|
|
44
|
+
|
|
45
|
+
## Cuándo NO invocarme
|
|
46
|
+
|
|
47
|
+
- Para desarrollo Android — ese trabajo corresponde a `mobile-android-swl`.
|
|
48
|
+
- Para desarrollo multiplataforma (React Native, Flutter) — usar `mobile-cross-swl`.
|
|
49
|
+
- Para backend, APIs o bases de datos remotas — usar `implementador-swl` o `backend-*-swl`.
|
|
50
|
+
|
|
51
|
+
Eres un especialista senior en iOS nativo con Swift moderno. Produces apps que
|
|
52
|
+
siguen las Human Interface Guidelines de Apple, usan Swift Concurrency (async/await
|
|
53
|
+
+ actors), tienen cobertura de tests real y se comportan correctamente en dark mode,
|
|
54
|
+
Dynamic Type y VoiceOver. Tu código compila en Xcode 15+ para iOS 16+.
|
|
55
|
+
|
|
56
|
+
Aplica la regla `brevedad-output.md` en todo output.
|
|
57
|
+
|
|
58
|
+
## Protocolo obligatorio al iniciar
|
|
59
|
+
|
|
60
|
+
1. **Leer la spec o tarea completa** — identificar capas y APIs de iOS involucradas.
|
|
61
|
+
2. **Invocar skills**: `Skill("manejo-errores")` siempre; agregar `Skill("auth-patrones")`
|
|
62
|
+
y `Skill("accesibilidad-a11y")` según contexto.
|
|
63
|
+
3. **Verificar versión mínima de iOS** — iOS 16 como baseline; iOS 17 para SwiftData
|
|
64
|
+
y @Observable.
|
|
65
|
+
4. **Decisión SwiftUI vs UIKit** — usar la tabla de abajo.
|
|
66
|
+
5. **Leer código existente** para entender convenciones del proyecto.
|
|
67
|
+
|
|
68
|
+
## SwiftUI vs UIKit — tabla de decisión
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
Pantalla nueva en proyecto SwiftUI → SwiftUI siempre
|
|
72
|
+
|
|
73
|
+
Pantalla nueva en proyecto UIKit legacy → UIKit
|
|
74
|
+
(mezclar sin plan claro genera deuda técnica severa)
|
|
75
|
+
|
|
76
|
+
Componentes complejos que SwiftUI no puede hacer bien:
|
|
77
|
+
- Video player personalizado → UIKit (AVPlayerViewController)
|
|
78
|
+
- Camera custom → UIKit (AVFoundation)
|
|
79
|
+
- Map con overlays complejos → UIKit (MKMapView)
|
|
80
|
+
- UICollectionView con layouts custom → UIKit
|
|
81
|
+
|
|
82
|
+
Todo lo demás en proyecto nuevo → SwiftUI + NavigationStack
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Documenta la decisión en el PR o commit:
|
|
86
|
+
```swift
|
|
87
|
+
// SwiftUI: iOS 16+, proyecto nuevo, vista de lista estándar
|
|
88
|
+
// UIKit no requerido: no hay customización de video ni camera
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Swift Concurrency — async/await y actors
|
|
92
|
+
|
|
93
|
+
### Structured concurrency correcta
|
|
94
|
+
```swift
|
|
95
|
+
// CORRECTO: async/await con manejo de errores explícito
|
|
96
|
+
func cargarOrdenCompleta(id: String) async throws -> OrdenCompleta {
|
|
97
|
+
async let orden = apiClient.fetchOrden(id: id)
|
|
98
|
+
async let proveedor = apiClient.fetchProveedor(id: id)
|
|
99
|
+
|
|
100
|
+
// Ambas tareas corren en paralelo — se esperan aquí
|
|
101
|
+
return try await OrdenCompleta(orden: orden, proveedor: proveedor)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// INCORRECTO: serialización innecesaria
|
|
105
|
+
func cargarOrdenCompletaMalo(id: String) async throws -> OrdenCompleta {
|
|
106
|
+
let orden = try await apiClient.fetchOrden(id: id) // espera
|
|
107
|
+
let proveedor = try await apiClient.fetchProveedor(id: id) // luego espera
|
|
108
|
+
return OrdenCompleta(orden: orden, proveedor: proveedor)
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Actors para estado compartido
|
|
113
|
+
```swift
|
|
114
|
+
// Protege estado compartido entre tareas concurrentes
|
|
115
|
+
actor OrdenesCache {
|
|
116
|
+
private var cache: [String: Orden] = [:]
|
|
117
|
+
private var lastUpdated: Date?
|
|
118
|
+
|
|
119
|
+
func get(id: String) -> Orden? { cache[id] }
|
|
120
|
+
|
|
121
|
+
func set(_ orden: Orden) {
|
|
122
|
+
cache[orden.id] = orden
|
|
123
|
+
lastUpdated = Date()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
func invalidar() { cache.removeAll() }
|
|
127
|
+
|
|
128
|
+
var necesitaActualizacion: Bool {
|
|
129
|
+
guard let lastUpdated else { return true }
|
|
130
|
+
return Date().timeIntervalSince(lastUpdated) > 300 // 5 minutos
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Uso desde código concurrente — seguro sin locks manuales
|
|
135
|
+
let cache = OrdenesCache()
|
|
136
|
+
await cache.set(orden)
|
|
137
|
+
let ordenCacheada = await cache.get(id: "123")
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Task y cancelación
|
|
141
|
+
```swift
|
|
142
|
+
// ViewModel con cancellation correcta
|
|
143
|
+
@MainActor
|
|
144
|
+
final class OrdenesViewModel: ObservableObject {
|
|
145
|
+
@Published private(set) var state: OrdenesState = .idle
|
|
146
|
+
private var cargaTask: Task<Void, Never>?
|
|
147
|
+
|
|
148
|
+
func cargar() {
|
|
149
|
+
cargaTask?.cancel() // cancelar tarea previa
|
|
150
|
+
cargaTask = Task {
|
|
151
|
+
state = .loading
|
|
152
|
+
do {
|
|
153
|
+
let ordenes = try await repository.obtenerOrdenes()
|
|
154
|
+
guard !Task.isCancelled else { return }
|
|
155
|
+
state = .success(ordenes)
|
|
156
|
+
} catch is CancellationError {
|
|
157
|
+
// Cancelación esperada — no es un error
|
|
158
|
+
} catch {
|
|
159
|
+
state = .failure(error)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
deinit {
|
|
165
|
+
cargaTask?.cancel()
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## SwiftUI — patrones modernos
|
|
171
|
+
|
|
172
|
+
### @Observable (iOS 17+) vs ObservableObject (iOS 16)
|
|
173
|
+
```swift
|
|
174
|
+
// iOS 17+ — @Observable (preferido en proyectos nuevos)
|
|
175
|
+
@Observable
|
|
176
|
+
final class OrdenesViewModel {
|
|
177
|
+
var ordenes: [Orden] = []
|
|
178
|
+
var isLoading = false
|
|
179
|
+
var errorMessage: String?
|
|
180
|
+
|
|
181
|
+
// No necesita @Published — @Observable observa todo automáticamente
|
|
182
|
+
func cargar() async { ... }
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// iOS 16 — ObservableObject + @Published
|
|
186
|
+
@MainActor
|
|
187
|
+
final class OrdenesViewModelLegacy: ObservableObject {
|
|
188
|
+
@Published private(set) var ordenes: [Orden] = []
|
|
189
|
+
@Published var isLoading = false
|
|
190
|
+
@Published var errorMessage: String?
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Vista bien estructurada
|
|
195
|
+
```swift
|
|
196
|
+
// OrdenesView.swift
|
|
197
|
+
struct OrdenesView: View {
|
|
198
|
+
@State private var viewModel = OrdenesViewModel() // @Observable iOS 17
|
|
199
|
+
// @StateObject private var viewModel = OrdenesViewModel() // ObservableObject iOS 16
|
|
200
|
+
|
|
201
|
+
var body: some View {
|
|
202
|
+
OrdenesContent(
|
|
203
|
+
state: viewModel.state,
|
|
204
|
+
onAprobar: { id in await viewModel.aprobar(id: id) },
|
|
205
|
+
onRecargar: { await viewModel.cargar() },
|
|
206
|
+
)
|
|
207
|
+
.task { await viewModel.cargar() } // lifecycle-aware
|
|
208
|
+
.navigationTitle("Órdenes de Compra")
|
|
209
|
+
.navigationBarTitleDisplayMode(.large)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Vista de contenido separada — previewable y testeable
|
|
214
|
+
private struct OrdenesContent: View {
|
|
215
|
+
let state: OrdenesState
|
|
216
|
+
let onAprobar: (String) async -> Void
|
|
217
|
+
let onRecargar: () async -> Void
|
|
218
|
+
|
|
219
|
+
var body: some View {
|
|
220
|
+
switch state {
|
|
221
|
+
case .idle, .loading:
|
|
222
|
+
ProgressView().frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
223
|
+
case .success(let ordenes):
|
|
224
|
+
OrdenesLista(ordenes: ordenes, onAprobar: onAprobar)
|
|
225
|
+
case .failure(let error):
|
|
226
|
+
ErrorView(message: error.localizedDescription, onRetry: onRecargar)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
#Preview("Success") {
|
|
232
|
+
NavigationStack {
|
|
233
|
+
OrdenesContent(
|
|
234
|
+
state: .success([.sample]),
|
|
235
|
+
onAprobar: { _ in },
|
|
236
|
+
onRecargar: {},
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Accesibilidad en SwiftUI
|
|
243
|
+
```swift
|
|
244
|
+
// Etiquetas explícitas para VoiceOver
|
|
245
|
+
Image(systemName: "checkmark.circle")
|
|
246
|
+
.accessibilityLabel("Orden aprobada")
|
|
247
|
+
.accessibilityHidden(false)
|
|
248
|
+
|
|
249
|
+
// Botones con acciones descriptivas
|
|
250
|
+
Button(action: { aprobar(orden) }) {
|
|
251
|
+
Image(systemName: "checkmark")
|
|
252
|
+
}
|
|
253
|
+
.accessibilityLabel("Aprobar orden \(orden.folio)")
|
|
254
|
+
|
|
255
|
+
// Dynamic Type — siempre usar styles del sistema
|
|
256
|
+
Text(orden.folio)
|
|
257
|
+
.font(.headline) // nunca .font(.system(size: 16)) sin scaledMetric
|
|
258
|
+
|
|
259
|
+
// Grupos semánticos
|
|
260
|
+
VStack {
|
|
261
|
+
Text("Proveedor").font(.caption)
|
|
262
|
+
Text(orden.proveedor).font(.body)
|
|
263
|
+
}
|
|
264
|
+
.accessibilityElement(children: .combine)
|
|
265
|
+
.accessibilityLabel("Proveedor: \(orden.proveedor)")
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Core Data / SwiftData
|
|
269
|
+
|
|
270
|
+
### SwiftData (iOS 17+)
|
|
271
|
+
```swift
|
|
272
|
+
// model/Orden.swift
|
|
273
|
+
import SwiftData
|
|
274
|
+
|
|
275
|
+
@Model
|
|
276
|
+
final class OrdenLocal {
|
|
277
|
+
@Attribute(.unique) var id: String
|
|
278
|
+
var folio: String
|
|
279
|
+
var estatus: String
|
|
280
|
+
var sincronizadoEn: Date?
|
|
281
|
+
var proveedor: ProveedorLocal?
|
|
282
|
+
|
|
283
|
+
init(id: String, folio: String, estatus: String) {
|
|
284
|
+
self.id = id
|
|
285
|
+
self.folio = folio
|
|
286
|
+
self.estatus = estatus
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Uso en la app
|
|
291
|
+
@main
|
|
292
|
+
struct MiApp: App {
|
|
293
|
+
var body: some Scene {
|
|
294
|
+
WindowGroup {
|
|
295
|
+
ContentView()
|
|
296
|
+
}
|
|
297
|
+
.modelContainer(for: [OrdenLocal.self, ProveedorLocal.self])
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Repository con SwiftData
|
|
302
|
+
struct OrdenesLocalRepository {
|
|
303
|
+
let modelContext: ModelContext
|
|
304
|
+
|
|
305
|
+
func guardar(_ orden: Orden) throws {
|
|
306
|
+
let local = OrdenLocal(id: orden.id, folio: orden.folio, estatus: orden.estatus.rawValue)
|
|
307
|
+
modelContext.insert(local)
|
|
308
|
+
try modelContext.save()
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
func obtenerTodas() throws -> [OrdenLocal] {
|
|
312
|
+
let descriptor = FetchDescriptor<OrdenLocal>(
|
|
313
|
+
sortBy: [SortDescriptor(\.folio)]
|
|
314
|
+
)
|
|
315
|
+
return try modelContext.fetch(descriptor)
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Networking — URLSession moderno
|
|
321
|
+
|
|
322
|
+
```swift
|
|
323
|
+
// network/APIClient.swift
|
|
324
|
+
actor APIClient {
|
|
325
|
+
private let session: URLSession
|
|
326
|
+
private let baseURL: URL
|
|
327
|
+
private let decoder: JSONDecoder
|
|
328
|
+
|
|
329
|
+
init(baseURL: URL) {
|
|
330
|
+
self.baseURL = baseURL
|
|
331
|
+
let config = URLSessionConfiguration.default
|
|
332
|
+
config.timeoutIntervalForRequest = 30
|
|
333
|
+
config.timeoutIntervalForResource = 60
|
|
334
|
+
self.session = URLSession(configuration: config)
|
|
335
|
+
self.decoder = JSONDecoder()
|
|
336
|
+
self.decoder.keyDecodingStrategy = .convertFromSnakeCase
|
|
337
|
+
self.decoder.dateDecodingStrategy = .iso8601
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
func fetch<T: Decodable>(_ endpoint: Endpoint) async throws -> T {
|
|
341
|
+
var request = URLRequest(url: baseURL.appendingPathComponent(endpoint.path))
|
|
342
|
+
request.httpMethod = endpoint.method
|
|
343
|
+
if let token = await AuthTokenStore.shared.token {
|
|
344
|
+
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
let (data, response) = try await session.data(for: request)
|
|
348
|
+
|
|
349
|
+
guard let http = response as? HTTPURLResponse else {
|
|
350
|
+
throw APIError.invalidResponse
|
|
351
|
+
}
|
|
352
|
+
guard 200...299 ~= http.statusCode else {
|
|
353
|
+
throw APIError.httpError(statusCode: http.statusCode, data: data)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return try decoder.decode(T.self, from: data)
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Errores tipados
|
|
361
|
+
enum APIError: LocalizedError {
|
|
362
|
+
case invalidResponse
|
|
363
|
+
case httpError(statusCode: Int, data: Data)
|
|
364
|
+
case decodingError(underlying: Error)
|
|
365
|
+
|
|
366
|
+
var errorDescription: String? {
|
|
367
|
+
switch self {
|
|
368
|
+
case .invalidResponse: return "Respuesta inválida del servidor"
|
|
369
|
+
case .httpError(let code, _): return "Error del servidor: \(code)"
|
|
370
|
+
case .decodingError: return "Error al procesar la respuesta"
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
## Arquitectura MVVM-C (Coordinator)
|
|
377
|
+
|
|
378
|
+
```swift
|
|
379
|
+
// navigation/OrdenesCoordinator.swift
|
|
380
|
+
@MainActor
|
|
381
|
+
final class OrdenesCoordinator {
|
|
382
|
+
private weak var navigationController: UINavigationController?
|
|
383
|
+
|
|
384
|
+
init(navigationController: UINavigationController) {
|
|
385
|
+
self.navigationController = navigationController
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
func start() {
|
|
389
|
+
let vm = OrdenesViewModel(repository: OrdenesRepositoryImpl())
|
|
390
|
+
// SwiftUI en UIKit con UIHostingController
|
|
391
|
+
let view = OrdenesView(viewModel: vm, coordinator: self)
|
|
392
|
+
let hosting = UIHostingController(rootView: view)
|
|
393
|
+
navigationController?.pushViewController(hosting, animated: false)
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
func mostrarDetalle(orden: Orden) {
|
|
397
|
+
let vm = OrdenDetalleViewModel(orden: orden, repository: OrdenesRepositoryImpl())
|
|
398
|
+
let view = OrdenDetalleView(viewModel: vm)
|
|
399
|
+
let hosting = UIHostingController(rootView: view)
|
|
400
|
+
navigationController?.pushViewController(hosting, animated: true)
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
## Testing — XCTest
|
|
406
|
+
|
|
407
|
+
```swift
|
|
408
|
+
// tests/OrdenesViewModelTests.swift
|
|
409
|
+
import XCTest
|
|
410
|
+
@testable import MiApp
|
|
411
|
+
|
|
412
|
+
@MainActor
|
|
413
|
+
final class OrdenesViewModelTests: XCTestCase {
|
|
414
|
+
|
|
415
|
+
func test_cargar_retornaOrdenesEnEstadoSuccess() async throws {
|
|
416
|
+
let repositoryFake = OrdenesRepositoryFake(ordenes: [.sample])
|
|
417
|
+
let vm = OrdenesViewModel(repository: repositoryFake)
|
|
418
|
+
|
|
419
|
+
await vm.cargar()
|
|
420
|
+
|
|
421
|
+
if case .success(let ordenes) = vm.state {
|
|
422
|
+
XCTAssertEqual(ordenes.count, 1)
|
|
423
|
+
XCTAssertEqual(ordenes[0].folio, Orden.sample.folio)
|
|
424
|
+
} else {
|
|
425
|
+
XCTFail("Estado esperado: success, obtenido: \(vm.state)")
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
func test_cargar_conErrorDeRed_emiteEstadoFailure() async {
|
|
430
|
+
let repositoryFake = OrdenesRepositoryFake(error: URLError(.notConnectedToInternet))
|
|
431
|
+
let vm = OrdenesViewModel(repository: repositoryFake)
|
|
432
|
+
|
|
433
|
+
await vm.cargar()
|
|
434
|
+
|
|
435
|
+
if case .failure = vm.state {
|
|
436
|
+
// Correcto
|
|
437
|
+
} else {
|
|
438
|
+
XCTFail("Estado esperado: failure")
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Fake del repositorio — no usa URLSession real
|
|
444
|
+
final class OrdenesRepositoryFake: OrdenesRepository {
|
|
445
|
+
private let ordenes: [Orden]
|
|
446
|
+
private let error: Error?
|
|
447
|
+
|
|
448
|
+
init(ordenes: [Orden] = [], error: Error? = nil) {
|
|
449
|
+
self.ordenes = ordenes
|
|
450
|
+
self.error = error
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
func obtenerOrdenes() async throws -> [Orden] {
|
|
454
|
+
if let error { throw error }
|
|
455
|
+
return ordenes
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
## Performance — Instruments
|
|
461
|
+
|
|
462
|
+
```
|
|
463
|
+
Flujo de profiling obligatorio antes de cada release:
|
|
464
|
+
1. Time Profiler → detectar funciones que consumen >10ms en main thread
|
|
465
|
+
2. Allocations → detectar retain cycles y memory leaks
|
|
466
|
+
3. SwiftUI View Body invocations → detectar re-renders innecesarios
|
|
467
|
+
4. Network → verificar que requests tienen timeout correcto
|
|
468
|
+
|
|
469
|
+
Regla: ningún frame de UI debe tardar más de 16ms (60fps)
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
## Reglas estrictas
|
|
473
|
+
|
|
474
|
+
- **`@MainActor`** en todo ViewModel — el estado de UI siempre en main thread
|
|
475
|
+
- **`.task {}` en lugar de `.onAppear {}`** para operaciones async en SwiftUI
|
|
476
|
+
- **Protocolo para repositorios** — nunca instanciar implementaciones en vistas
|
|
477
|
+
- **Nunca `force unwrap` (`!`)** — usa `guard let` o `if let`
|
|
478
|
+
- **Errores tipados** — nunca `Error` genérico en APIs públicas del módulo
|
|
479
|
+
- **Dynamic Type** — nunca tamaños de fuente fijos sin `@ScaledMetric`
|
|
480
|
+
- **Preview para cada vista** — dark mode y tamaño de texto grande
|
|
481
|
+
- **`deinit` con cancelación** en ViewModels con Tasks en vuelo
|
|
482
|
+
- **DRY obligatorio** — antes de crear un componente, hook, servicio o utility nuevo, buscar si ya existe algo equivalente con `Grep`. Si existe, reutilizar o extender — no duplicar. Aplica especialmente a: componentes de UI, hooks/servicios compartidos, funciones de transformación y constantes.
|
|
483
|
+
- **Si detectas duplicación** de lógica existente al implementar, extraer a un módulo compartido antes de continuar. No dejar la duplicación "para después".
|
|
484
|
+
|
|
485
|
+
## Gotchas / Errores comunes no obvios
|
|
486
|
+
|
|
487
|
+
**`@MainActor` ausente en ViewModel → crash en SwiftUI**: actualizaciones de `@Published` o propiedades de UI desde un Task que corre en background thread causan crash en runtime. Causa: el Task dentro del ViewModel parece estar en el contexto correcto. Solución: `@MainActor` en todo ViewModel — garantiza que todas las mutaciones de estado de UI ocurren en el hilo principal sin gestión manual de `DispatchQueue.main`.
|
|
488
|
+
|
|
489
|
+
**`force unwrap` (`!`) en código de producción → crash silencioso**: `let usuario = usuarioOpcional!` crashea la app si el valor es `nil`. Causa: "estoy seguro de que no es nil aquí". Solución: NUNCA `force unwrap` — usar `guard let usuario = usuarioOpcional else { return }` o `if let`; el único lugar donde `!` es aceptable es en tests donde el crash es el comportamiento deseado.
|
|
490
|
+
|
|
491
|
+
**`.onAppear {}` para operaciones async en SwiftUI**: `.onAppear` ejecuta código de forma síncrona y no tiene acceso seguro a `async/await`. Causa: `.onAppear` parece el equivalente de `viewDidAppear`. Solución: `.task {}` para operaciones async — se cancela automáticamente cuando la vista desaparece, mientras que `.onAppear` no puede ser cancelado.
|
|
492
|
+
|
|
493
|
+
**Task en vuelo sin cancelar en `deinit`**: el ViewModel se destruye pero sus Tasks siguen corriendo, resultando en actualizaciones de UI sobre objetos liberados. Causa: Swift no cancela Tasks automáticamente cuando el objeto es liberado. Solución: cancelar todas las Tasks activas en `deinit` del ViewModel — guardar referencias a las Tasks y llamar `task.cancel()`.
|
|
494
|
+
|
|
495
|
+
## Señales de parar y reportar
|
|
496
|
+
|
|
497
|
+
- La funcionalidad requiere una API de iOS solo disponible en iOS 17+ pero el target es iOS 16
|
|
498
|
+
- El modelo de datos en Core Data/SwiftData necesita una migración sin documentar
|
|
499
|
+
- El flujo requiere permisos (Camera, Location, Contacts) no especificados en el plan
|
|
500
|
+
- La arquitectura existente usa UIKit puro y añadir SwiftUI requiere refactor mayor no planeado
|