@devo-bmad-custom/agent-orchestration 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/bin/bmad.js +36 -0
- package/lib/cli.js +137 -0
- package/lib/filter.js +73 -0
- package/lib/installer.js +787 -0
- package/package.json +30 -0
- package/src/.agents/skills/audit-website/README.md +20 -0
- package/src/.agents/skills/audit-website/SKILL.md +470 -0
- package/src/.agents/skills/audit-website/agents/openai.yaml +6 -0
- package/src/.agents/skills/audit-website/assets/icon-small.svg +41 -0
- package/src/.agents/skills/audit-website/references/OUTPUT-FORMAT.md +250 -0
- package/src/.agents/skills/clean-code-standards/SKILL.md +105 -0
- package/src/.agents/skills/excalidraw-dark-standard/SKILL.md +282 -0
- package/src/.agents/skills/excalidraw-diagram-generator/SKILL.md +613 -0
- package/src/.agents/skills/excalidraw-diagram-generator/references/element-types.md +497 -0
- package/src/.agents/skills/excalidraw-diagram-generator/references/excalidraw-schema.md +350 -0
- package/src/.agents/skills/excalidraw-diagram-generator/scripts/README.md +193 -0
- package/src/.agents/skills/excalidraw-diagram-generator/scripts/add-arrow.py +312 -0
- package/src/.agents/skills/excalidraw-diagram-generator/scripts/add-icon-to-diagram.py +404 -0
- package/src/.agents/skills/excalidraw-diagram-generator/scripts/split-excalidraw-library.py +183 -0
- package/src/.agents/skills/excalidraw-diagram-generator/templates/business-flow-swimlane-template.excalidraw +334 -0
- package/src/.agents/skills/excalidraw-diagram-generator/templates/class-diagram-template.excalidraw +558 -0
- package/src/.agents/skills/excalidraw-diagram-generator/templates/data-flow-diagram-template.excalidraw +279 -0
- package/src/.agents/skills/excalidraw-diagram-generator/templates/er-diagram-template.excalidraw +662 -0
- package/src/.agents/skills/excalidraw-diagram-generator/templates/flowchart-template.excalidraw +179 -0
- package/src/.agents/skills/excalidraw-diagram-generator/templates/mindmap-template.excalidraw +244 -0
- package/src/.agents/skills/excalidraw-diagram-generator/templates/relationship-template.excalidraw +145 -0
- package/src/.agents/skills/excalidraw-diagram-generator/templates/sequence-diagram-template.excalidraw +509 -0
- package/src/.agents/skills/frontend-responsive-design-standards/SKILL.md +434 -0
- package/src/.agents/skills/java-fundamentals/SKILL.md +116 -0
- package/src/.agents/skills/java-performance/SKILL.md +119 -0
- package/src/.agents/skills/next-best-practices/SKILL.md +153 -0
- package/src/.agents/skills/next-best-practices/async-patterns.md +87 -0
- package/src/.agents/skills/next-best-practices/bundling.md +180 -0
- package/src/.agents/skills/next-best-practices/data-patterns.md +297 -0
- package/src/.agents/skills/next-best-practices/debug-tricks.md +105 -0
- package/src/.agents/skills/next-best-practices/directives.md +73 -0
- package/src/.agents/skills/next-best-practices/error-handling.md +227 -0
- package/src/.agents/skills/next-best-practices/file-conventions.md +140 -0
- package/src/.agents/skills/next-best-practices/font.md +245 -0
- package/src/.agents/skills/next-best-practices/functions.md +108 -0
- package/src/.agents/skills/next-best-practices/hydration-error.md +91 -0
- package/src/.agents/skills/next-best-practices/image.md +173 -0
- package/src/.agents/skills/next-best-practices/metadata.md +301 -0
- package/src/.agents/skills/next-best-practices/parallel-routes.md +287 -0
- package/src/.agents/skills/next-best-practices/route-handlers.md +146 -0
- package/src/.agents/skills/next-best-practices/rsc-boundaries.md +159 -0
- package/src/.agents/skills/next-best-practices/runtime-selection.md +39 -0
- package/src/.agents/skills/next-best-practices/scripts.md +141 -0
- package/src/.agents/skills/next-best-practices/self-hosting.md +371 -0
- package/src/.agents/skills/next-best-practices/suspense-boundaries.md +67 -0
- package/src/.agents/skills/nextjs-app-router-patterns/SKILL.md +537 -0
- package/src/.agents/skills/postgresql-optimization/SKILL.md +404 -0
- package/src/.agents/skills/python-backend/SKILL.md +153 -0
- package/src/.agents/skills/python-fundamentals/SKILL.md +234 -0
- package/src/.agents/skills/python-performance/SKILL.md +404 -0
- package/src/.agents/skills/react-expert/SKILL.md +335 -0
- package/src/.agents/skills/redis-best-practices/SKILL.md +438 -0
- package/src/.agents/skills/security-best-practices/SKILL.md +288 -0
- package/src/.agents/skills/security-review/LICENSE +22 -0
- package/src/.agents/skills/security-review/SKILL.md +312 -0
- package/src/.agents/skills/security-review/infrastructure/docker.md +432 -0
- package/src/.agents/skills/security-review/languages/javascript.md +388 -0
- package/src/.agents/skills/security-review/languages/python.md +363 -0
- package/src/.agents/skills/security-review/references/api-security.md +519 -0
- package/src/.agents/skills/security-review/references/authentication.md +353 -0
- package/src/.agents/skills/security-review/references/authorization.md +372 -0
- package/src/.agents/skills/security-review/references/business-logic.md +443 -0
- package/src/.agents/skills/security-review/references/cryptography.md +329 -0
- package/src/.agents/skills/security-review/references/csrf.md +398 -0
- package/src/.agents/skills/security-review/references/data-protection.md +378 -0
- package/src/.agents/skills/security-review/references/deserialization.md +410 -0
- package/src/.agents/skills/security-review/references/error-handling.md +436 -0
- package/src/.agents/skills/security-review/references/file-security.md +457 -0
- package/src/.agents/skills/security-review/references/injection.md +259 -0
- package/src/.agents/skills/security-review/references/logging.md +433 -0
- package/src/.agents/skills/security-review/references/misconfiguration.md +435 -0
- package/src/.agents/skills/security-review/references/modern-threats.md +475 -0
- package/src/.agents/skills/security-review/references/ssrf.md +415 -0
- package/src/.agents/skills/security-review/references/supply-chain.md +405 -0
- package/src/.agents/skills/security-review/references/xss.md +336 -0
- package/src/.agents/skills/subagent-driven-development/SKILL.md +275 -0
- package/src/.agents/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
- package/src/.agents/skills/subagent-driven-development/implementer-prompt.md +113 -0
- package/src/.agents/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/src/.agents/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/src/.agents/skills/systematic-debugging/SKILL.md +296 -0
- package/src/.agents/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/src/.agents/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/src/.agents/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/src/.agents/skills/systematic-debugging/find-polluter.sh +63 -0
- package/src/.agents/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/src/.agents/skills/systematic-debugging/test-academic.md +14 -0
- package/src/.agents/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/src/.agents/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/src/.agents/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/src/.agents/skills/typescript-best-practices/SKILL.md +373 -0
- package/src/.agents/skills/ui-ux-pro-custom/SKILL.md +348 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/charts.csv +26 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/colors.csv +97 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/icons.csv +101 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/SKILL.md +106 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/accessibility.md +475 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/animation.md +466 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/composition-locals.md +231 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/deprecated-patterns.md +323 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/lists-scrolling.md +400 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/modifiers.md +331 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/navigation.md +416 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/performance.md +446 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/side-effects.md +516 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/foundation-source.md +13327 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/material3-source.md +19097 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/navigation-source.md +2947 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/runtime-source.md +11316 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/source-code/ui-source.md +7896 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/state-management.md +377 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/styles-experimental.md +470 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/theming-material3.md +349 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/jetpack-compose-expert-skill/references/view-composition.md +595 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/landing.csv +31 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/mobile-ui-layout.md +654 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/products.csv +97 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/react-performance.csv +45 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/astro.csv +54 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/flutter.csv +53 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/html-tailwind.csv +56 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/jetpack-compose.csv +53 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nextjs.csv +53 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nuxt-ui.csv +51 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/nuxtjs.csv +59 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/react-native.csv +56 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/react.csv +54 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/shadcn.csv +61 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/svelte.csv +54 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/swiftui.csv +51 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/stacks/vue.csv +50 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/styles.csv +68 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/alarmkit/SKILL.md +438 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/alarmkit/references/alarmkit-patterns.md +584 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-clips/SKILL.md +436 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-intents/SKILL.md +489 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-intents/references/appintents-advanced.md +1076 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/SKILL.md +340 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/references/privacy-manifest.md +90 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/app-store-review/references/review-checklists.md +106 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/SKILL.md +500 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/coreml-conversion.md +425 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/coreml-optimization.md +344 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/foundation-models.md +508 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/apple-on-device-ai/references/mlx-swift.md +285 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/authentication/SKILL.md +496 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/authentication/references/keychain-biometric.md +211 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/background-processing/SKILL.md +499 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/background-processing/references/background-task-patterns.md +390 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/callkit-voip/SKILL.md +461 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/callkit-voip/references/callkit-patterns.md +425 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/cloudkit-sync/SKILL.md +492 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/cloudkit-sync/references/cloudkit-patterns.md +461 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/codable-patterns/SKILL.md +467 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/contacts-framework/SKILL.md +425 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/contacts-framework/references/contacts-patterns.md +409 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-bluetooth/SKILL.md +491 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-bluetooth/references/ble-patterns.md +435 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-motion/SKILL.md +388 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-motion/references/motion-patterns.md +405 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-nfc/SKILL.md +495 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/core-nfc/references/nfc-patterns.md +420 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/coreml/SKILL.md +459 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/coreml/references/coreml-swift-integration.md +765 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/SKILL.md +422 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/references/instruments-guide.md +387 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/debugging-instruments/references/lldb-patterns.md +298 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/device-integrity/SKILL.md +477 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/energykit/SKILL.md +460 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/energykit/references/energykit-patterns.md +541 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/eventkit-calendar/SKILL.md +483 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/eventkit-calendar/references/eventkit-patterns.md +326 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/healthkit/SKILL.md +498 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/healthkit/references/healthkit-patterns.md +602 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/homekit-matter/SKILL.md +496 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/homekit-matter/references/matter-commissioning.md +455 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-accessibility/SKILL.md +301 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-accessibility/references/a11y-patterns.md +140 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/SKILL.md +418 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/references/formatstyle-locale.md +627 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-localization/references/string-catalogs.md +462 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/SKILL.md +441 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/background-websocket.md +862 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/lightweight-clients.md +93 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/network-framework.md +563 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-networking/references/urlsession-patterns.md +1116 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/SKILL.md +496 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/app-review-guidelines.md +174 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/cryptokit-advanced.md +297 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/file-storage-patterns.md +354 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/ios-security/references/privacy-manifest.md +117 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/live-activities/SKILL.md +500 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/live-activities/references/live-activity-patterns.md +868 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/SKILL.md +485 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/references/corelocation-patterns.md +730 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/mapkit-location/references/mapkit-patterns.md +748 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/metrickit-diagnostics/SKILL.md +479 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/musickit-audio/SKILL.md +395 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/musickit-audio/references/musickit-patterns.md +363 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/natural-language/SKILL.md +412 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/natural-language/references/translation-patterns.md +311 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/passkit-wallet/SKILL.md +398 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/passkit-wallet/references/wallet-passes.md +254 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/SKILL.md +387 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/references/paperkit-integration.md +376 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/pencilkit-drawing/references/pencilkit-patterns.md +302 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/permissionkit/SKILL.md +446 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/permissionkit/references/permissionkit-patterns.md +435 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/SKILL.md +501 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/av-playback.md +701 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/camera-capture.md +774 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/image-loading-caching.md +869 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/photos-camera-media/references/photospicker-patterns.md +597 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/SKILL.md +501 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/references/notification-patterns.md +677 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/push-notifications/references/rich-notifications.md +745 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/realitykit-ar/SKILL.md +479 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/realitykit-ar/references/realitykit-patterns.md +480 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/shareplay-activities/SKILL.md +483 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/shareplay-activities/references/shareplay-patterns.md +544 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/speech-recognition/SKILL.md +485 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/SKILL.md +478 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/references/app-review-guidelines.md +58 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/storekit/references/storekit-advanced.md +755 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-charts/SKILL.md +487 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-charts/references/charts-patterns.md +895 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/SKILL.md +408 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/approachable-concurrency.md +80 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/swift-6-2-concurrency.md +233 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/swiftui-concurrency.md +187 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-concurrency/references/synchronization-primitives.md +341 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-language/SKILL.md +498 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-language/references/swift-patterns-extended.md +505 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-testing/SKILL.md +467 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swift-testing/references/testing-patterns.md +504 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/SKILL.md +334 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/core-data-coexistence.md +504 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/swiftdata-advanced.md +975 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftdata/references/swiftdata-queries.md +675 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/SKILL.md +481 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/references/animation-advanced.md +804 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-animation/references/core-animation-bridge.md +553 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-gestures/SKILL.md +450 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-gestures/references/gesture-patterns.md +425 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/SKILL.md +336 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/form.md +97 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/grids.md +69 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/list.md +99 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-layout-components/references/scrollview.md +147 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-liquid-glass/SKILL.md +325 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-liquid-glass/references/liquid-glass.md +387 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/SKILL.md +262 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/deeplinks.md +207 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/navigationstack.md +177 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/sheets.md +169 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-navigation/references/tabview.md +178 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/SKILL.md +381 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/architecture-patterns.md +486 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/deprecated-migration.md +1097 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/design-polish.md +780 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-patterns/references/platform-and-sharing.md +696 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/SKILL.md +491 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +46 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +29 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/understanding-hangs-in-your-app.md +33 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-performance/references/understanding-improving-swiftui-performance.md +52 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/SKILL.md +428 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/references/hosting-migration.md +534 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/swiftui-uikit-interop/references/representable-recipes.md +1133 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/tipkit/SKILL.md +494 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/tipkit/references/tipkit-patterns.md +782 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/SKILL.md +475 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/references/vision-requests.md +736 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/vision-framework/references/visionkit-scanner.md +738 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/weatherkit/SKILL.md +410 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/weatherkit/references/weatherkit-patterns.md +567 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/widgetkit/SKILL.md +497 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/swift-ios-skills/widgetkit/references/widgetkit-advanced.md +871 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/typography.csv +58 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/ui-reasoning.csv +101 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/ux-guidelines.csv +100 -0
- package/src/.agents/skills/ui-ux-pro-custom/data/web-interface.csv +31 -0
- package/src/.agents/skills/ui-ux-pro-custom/scripts/core.py +253 -0
- package/src/.agents/skills/ui-ux-pro-custom/scripts/design_system.py +1067 -0
- package/src/.agents/skills/ui-ux-pro-custom/scripts/search.py +114 -0
- package/src/.agents/skills/ux-audit/SKILL.md +151 -0
- package/src/.agents/skills/websocket-engineer/SKILL.md +168 -0
- package/src/.agents/skills/websocket-engineer/references/alternatives.md +391 -0
- package/src/.agents/skills/websocket-engineer/references/patterns.md +400 -0
- package/src/.agents/skills/websocket-engineer/references/protocol.md +195 -0
- package/src/.agents/skills/websocket-engineer/references/scaling.md +333 -0
- package/src/.agents/skills/websocket-engineer/references/security.md +474 -0
- package/src/.agents/skills/writing-skills/SKILL.md +655 -0
- package/src/.agents/skills/writing-skills/anthropic-best-practices.md +1150 -0
- package/src/.agents/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/src/.agents/skills/writing-skills/graphviz-conventions.dot +172 -0
- package/src/.agents/skills/writing-skills/persuasion-principles.md +187 -0
- package/src/.agents/skills/writing-skills/render-graphs.js +168 -0
- package/src/.agents/skills/writing-skills/testing-skills-with-subagents.md +384 -0
- package/src/.claude/commands/bmad-track-compact.md +19 -0
- package/src/.claude/commands/bmad-track-extended.md +19 -0
- package/src/.claude/commands/bmad-track-large.md +19 -0
- package/src/.claude/commands/bmad-track-medium.md +19 -0
- package/src/.claude/commands/bmad-track-nano.md +19 -0
- package/src/.claude/commands/bmad-track-rv.md +18 -0
- package/src/.claude/commands/bmad-track-small.md +19 -0
- package/src/_memory/config.yaml +11 -0
- package/src/_memory/master-orchestrator-sidecar/docs-index.md +3 -0
- package/src/_memory/master-orchestrator-sidecar/instructions.md +2566 -0
- package/src/_memory/master-orchestrator-sidecar/memories.md +8 -0
- package/src/_memory/master-orchestrator-sidecar/session-state.md +15 -0
- package/src/_memory/master-orchestrator-sidecar/triage-history.md +3 -0
- package/src/_memory/master-orchestrator-sidecar/workflows-overview.html +1230 -0
- package/src/_memory/skills/excalidraw/SKILL.md +78 -0
- package/src/_memory/skills/excalidraw/diagram-patterns.md +53 -0
- package/src/_memory/skills/nimbalyst-tracking/SKILL.md +103 -0
- package/src/_memory/skills/writing-skills/SKILL.md +655 -0
- package/src/bmb/agents/agent-builder.md +59 -0
- package/src/bmb/agents/module-builder.md +60 -0
- package/src/bmb/agents/workflow-builder.md +61 -0
- package/src/bmb/config.yaml +12 -0
- package/src/bmb/module-help.csv +13 -0
- package/src/bmb/workflows/agent/data/agent-architecture.md +258 -0
- package/src/bmb/workflows/agent/data/agent-compilation.md +185 -0
- package/src/bmb/workflows/agent/data/agent-menu-patterns.md +189 -0
- package/src/bmb/workflows/agent/data/agent-metadata.md +133 -0
- package/src/bmb/workflows/agent/data/agent-validation.md +111 -0
- package/src/bmb/workflows/agent/data/brainstorm-context.md +96 -0
- package/src/bmb/workflows/agent/data/communication-presets.csv +61 -0
- package/src/bmb/workflows/agent/data/critical-actions.md +75 -0
- package/src/bmb/workflows/agent/data/persona-properties.md +252 -0
- package/src/bmb/workflows/agent/data/principles-crafting.md +142 -0
- package/src/bmb/workflows/agent/data/reference/module-examples/architect.md +68 -0
- package/src/bmb/workflows/agent/data/reference/with-sidecar/journal-keeper/journal-keeper-sidecar/entries/yy-mm-dd-entry-template.md +17 -0
- package/src/bmb/workflows/agent/data/understanding-agent-types.md +126 -0
- package/src/bmb/workflows/agent/steps-c/step-01-brainstorm.md +129 -0
- package/src/bmb/workflows/agent/steps-c/step-02-discovery.md +170 -0
- package/src/bmb/workflows/agent/steps-c/step-03-sidecar-metadata.md +309 -0
- package/src/bmb/workflows/agent/steps-c/step-04-persona.md +213 -0
- package/src/bmb/workflows/agent/steps-c/step-05-commands-menu.md +179 -0
- package/src/bmb/workflows/agent/steps-c/step-06-activation.md +278 -0
- package/src/bmb/workflows/agent/steps-c/step-07-build-agent.md +316 -0
- package/src/bmb/workflows/agent/steps-c/step-08-celebrate.md +247 -0
- package/src/bmb/workflows/agent/steps-e/e-01-load-existing.md +221 -0
- package/src/bmb/workflows/agent/steps-e/e-02-discover-edits.md +195 -0
- package/src/bmb/workflows/agent/steps-e/e-03-placeholder.md +1 -0
- package/src/bmb/workflows/agent/steps-e/e-04-sidecar-metadata.md +126 -0
- package/src/bmb/workflows/agent/steps-e/e-05-persona.md +135 -0
- package/src/bmb/workflows/agent/steps-e/e-06-commands-menu.md +123 -0
- package/src/bmb/workflows/agent/steps-e/e-07-activation.md +124 -0
- package/src/bmb/workflows/agent/steps-e/e-08-edit-agent.md +197 -0
- package/src/bmb/workflows/agent/steps-e/e-09-celebrate.md +155 -0
- package/src/bmb/workflows/agent/steps-v/v-01-load-review.md +137 -0
- package/src/bmb/workflows/agent/steps-v/v-02a-validate-metadata.md +116 -0
- package/src/bmb/workflows/agent/steps-v/v-02b-validate-persona.md +124 -0
- package/src/bmb/workflows/agent/steps-v/v-02c-validate-menu.md +127 -0
- package/src/bmb/workflows/agent/steps-v/v-02d-validate-structure.md +134 -0
- package/src/bmb/workflows/agent/steps-v/v-02e-validate-sidecar.md +134 -0
- package/src/bmb/workflows/agent/steps-v/v-03-summary.md +104 -0
- package/src/bmb/workflows/agent/templates/agent-plan.template.md +5 -0
- package/src/bmb/workflows/agent/templates/agent-template.md +89 -0
- package/src/bmb/workflows/agent/workflow-create-agent.md +72 -0
- package/src/bmb/workflows/agent/workflow-edit-agent.md +75 -0
- package/src/bmb/workflows/agent/workflow-validate-agent.md +73 -0
- package/src/bmb/workflows/module/data/agent-architecture.md +179 -0
- package/src/bmb/workflows/module/data/agent-spec-template.md +79 -0
- package/src/bmb/workflows/module/data/module-standards.md +263 -0
- package/src/bmb/workflows/module/data/module-yaml-conventions.md +392 -0
- package/src/bmb/workflows/module/module-help-generate.md +254 -0
- package/src/bmb/workflows/module/steps-b/step-01-welcome.md +148 -0
- package/src/bmb/workflows/module/steps-b/step-02-spark.md +141 -0
- package/src/bmb/workflows/module/steps-b/step-03-module-type.md +149 -0
- package/src/bmb/workflows/module/steps-b/step-04-vision.md +83 -0
- package/src/bmb/workflows/module/steps-b/step-05-identity.md +97 -0
- package/src/bmb/workflows/module/steps-b/step-06-users.md +86 -0
- package/src/bmb/workflows/module/steps-b/step-07-value.md +76 -0
- package/src/bmb/workflows/module/steps-b/step-08-agents.md +97 -0
- package/src/bmb/workflows/module/steps-b/step-09-workflows.md +83 -0
- package/src/bmb/workflows/module/steps-b/step-10-tools.md +91 -0
- package/src/bmb/workflows/module/steps-b/step-11-scenarios.md +84 -0
- package/src/bmb/workflows/module/steps-b/step-12-creative.md +95 -0
- package/src/bmb/workflows/module/steps-b/step-13-review.md +105 -0
- package/src/bmb/workflows/module/steps-b/step-14-finalize.md +117 -0
- package/src/bmb/workflows/module/steps-c/step-01-load-brief.md +179 -0
- package/src/bmb/workflows/module/steps-c/step-01b-continue.md +82 -0
- package/src/bmb/workflows/module/steps-c/step-02-structure.md +105 -0
- package/src/bmb/workflows/module/steps-c/step-03-config.md +119 -0
- package/src/bmb/workflows/module/steps-c/step-04-agents.md +168 -0
- package/src/bmb/workflows/module/steps-c/step-05-workflows.md +184 -0
- package/src/bmb/workflows/module/steps-c/step-06-docs.md +401 -0
- package/src/bmb/workflows/module/steps-c/step-07-complete.md +152 -0
- package/src/bmb/workflows/module/steps-e/step-01-load-target.md +81 -0
- package/src/bmb/workflows/module/steps-e/step-02-select-edit.md +77 -0
- package/src/bmb/workflows/module/steps-e/step-03-apply-edit.md +77 -0
- package/src/bmb/workflows/module/steps-e/step-04-review.md +80 -0
- package/src/bmb/workflows/module/steps-e/step-05-confirm.md +75 -0
- package/src/bmb/workflows/module/steps-v/step-01-load-target.md +96 -0
- package/src/bmb/workflows/module/steps-v/step-02-file-structure.md +93 -0
- package/src/bmb/workflows/module/steps-v/step-03-module-yaml.md +99 -0
- package/src/bmb/workflows/module/steps-v/step-04-agent-specs.md +152 -0
- package/src/bmb/workflows/module/steps-v/step-05-workflow-specs.md +152 -0
- package/src/bmb/workflows/module/steps-v/step-06-documentation.md +143 -0
- package/src/bmb/workflows/module/steps-v/step-07-installation.md +102 -0
- package/src/bmb/workflows/module/steps-v/step-08-report.md +197 -0
- package/src/bmb/workflows/module/templates/brief-template.md +154 -0
- package/src/bmb/workflows/module/templates/workflow-spec-template.md +96 -0
- package/src/bmb/workflows/module/workflow-create-module-brief.md +71 -0
- package/src/bmb/workflows/module/workflow-create-module.md +86 -0
- package/src/bmb/workflows/module/workflow-edit-module.md +66 -0
- package/src/bmb/workflows/module/workflow-validate-module.md +66 -0
- package/src/bmb/workflows/workflow/data/architecture.md +150 -0
- package/src/bmb/workflows/workflow/data/common-workflow-tools.csv +19 -0
- package/src/bmb/workflows/workflow/data/csv-data-file-standards.md +53 -0
- package/src/bmb/workflows/workflow/data/frontmatter-standards.md +184 -0
- package/src/bmb/workflows/workflow/data/input-discovery-standards.md +191 -0
- package/src/bmb/workflows/workflow/data/intent-vs-prescriptive-spectrum.md +44 -0
- package/src/bmb/workflows/workflow/data/menu-handling-standards.md +133 -0
- package/src/bmb/workflows/workflow/data/output-format-standards.md +135 -0
- package/src/bmb/workflows/workflow/data/step-file-rules.md +235 -0
- package/src/bmb/workflows/workflow/data/step-type-patterns.md +257 -0
- package/src/bmb/workflows/workflow/data/subprocess-optimization-patterns.md +188 -0
- package/src/bmb/workflows/workflow/data/trimodal-workflow-structure.md +164 -0
- package/src/bmb/workflows/workflow/data/workflow-chaining-standards.md +222 -0
- package/src/bmb/workflows/workflow/data/workflow-examples.md +232 -0
- package/src/bmb/workflows/workflow/data/workflow-type-criteria.md +134 -0
- package/src/bmb/workflows/workflow/steps-c/step-00-conversion.md +263 -0
- package/src/bmb/workflows/workflow/steps-c/step-01-discovery.md +194 -0
- package/src/bmb/workflows/workflow/steps-c/step-01b-continuation.md +3 -0
- package/src/bmb/workflows/workflow/steps-c/step-02-classification.md +270 -0
- package/src/bmb/workflows/workflow/steps-c/step-03-requirements.md +283 -0
- package/src/bmb/workflows/workflow/steps-c/step-04-tools.md +282 -0
- package/src/bmb/workflows/workflow/steps-c/step-05-plan-review.md +243 -0
- package/src/bmb/workflows/workflow/steps-c/step-06-design.md +330 -0
- package/src/bmb/workflows/workflow/steps-c/step-07-foundation.md +239 -0
- package/src/bmb/workflows/workflow/steps-c/step-08-build-step-01.md +379 -0
- package/src/bmb/workflows/workflow/steps-c/step-09-build-next-step.md +350 -0
- package/src/bmb/workflows/workflow/steps-c/step-10-confirmation.md +322 -0
- package/src/bmb/workflows/workflow/steps-c/step-11-completion.md +191 -0
- package/src/bmb/workflows/workflow/steps-e/step-e-01-assess-workflow.md +237 -0
- package/src/bmb/workflows/workflow/steps-e/step-e-02-discover-edits.md +251 -0
- package/src/bmb/workflows/workflow/steps-e/step-e-03-fix-validation.md +254 -0
- package/src/bmb/workflows/workflow/steps-e/step-e-04-direct-edit.md +277 -0
- package/src/bmb/workflows/workflow/steps-e/step-e-05-apply-edit.md +154 -0
- package/src/bmb/workflows/workflow/steps-e/step-e-06-validate-after.md +190 -0
- package/src/bmb/workflows/workflow/steps-e/step-e-07-complete.md +206 -0
- package/src/bmb/workflows/workflow/steps-v/step-01-validate-max-mode.md +109 -0
- package/src/bmb/workflows/workflow/steps-v/step-01-validate.md +221 -0
- package/src/bmb/workflows/workflow/steps-v/step-01b-structure.md +152 -0
- package/src/bmb/workflows/workflow/steps-v/step-02-frontmatter-validation.md +199 -0
- package/src/bmb/workflows/workflow/steps-v/step-02b-path-violations.md +265 -0
- package/src/bmb/workflows/workflow/steps-v/step-03-menu-validation.md +164 -0
- package/src/bmb/workflows/workflow/steps-v/step-04-step-type-validation.md +211 -0
- package/src/bmb/workflows/workflow/steps-v/step-05-output-format-validation.md +200 -0
- package/src/bmb/workflows/workflow/steps-v/step-06-validation-design-check.md +195 -0
- package/src/bmb/workflows/workflow/steps-v/step-07-instruction-style-check.md +209 -0
- package/src/bmb/workflows/workflow/steps-v/step-08-collaborative-experience-check.md +199 -0
- package/src/bmb/workflows/workflow/steps-v/step-08b-subprocess-optimization.md +179 -0
- package/src/bmb/workflows/workflow/steps-v/step-09-cohesive-review.md +186 -0
- package/src/bmb/workflows/workflow/steps-v/step-10-report-complete.md +154 -0
- package/src/bmb/workflows/workflow/steps-v/step-11-plan-validation.md +237 -0
- package/src/bmb/workflows/workflow/templates/minimal-output-template.md +11 -0
- package/src/bmb/workflows/workflow/templates/step-01-init-continuable-template.md +241 -0
- package/src/bmb/workflows/workflow/templates/step-1b-template.md +224 -0
- package/src/bmb/workflows/workflow/templates/step-template.md +294 -0
- package/src/bmb/workflows/workflow/templates/workflow-template.md +102 -0
- package/src/bmb/workflows/workflow/workflow-create-workflow.md +79 -0
- package/src/bmb/workflows/workflow/workflow-edit-workflow.md +65 -0
- package/src/bmb/workflows/workflow/workflow-rework-workflow.md +65 -0
- package/src/bmb/workflows/workflow/workflow-validate-max-parallel-workflow.md +66 -0
- package/src/bmb/workflows/workflow/workflow-validate-workflow.md +65 -0
- package/src/bmm/agents/analyst.md +104 -0
- package/src/bmm/agents/architect.md +85 -0
- package/src/bmm/agents/dev.md +100 -0
- package/src/bmm/agents/pm.md +98 -0
- package/src/bmm/agents/qa.md +90 -0
- package/src/bmm/agents/quick-flow-solo-dev.md +92 -0
- package/src/bmm/agents/review-agent.md +129 -0
- package/src/bmm/agents/sm.md +90 -0
- package/src/bmm/agents/tech-writer/tech-writer.md +94 -0
- package/src/bmm/agents/ux-designer.md +124 -0
- package/src/bmm/data/project-context-template.md +26 -0
- package/src/bmm/module-help.csv +31 -0
- package/src/bmm/teams/default-party.csv +20 -0
- package/src/bmm/teams/team-fullstack.yaml +12 -0
- package/src/bmm/workflows/1-analysis/create-product-brief/product-brief.template.md +10 -0
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01-init.md +115 -0
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-01b-continue.md +107 -0
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-02-vision.md +141 -0
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-03-users.md +144 -0
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-04-metrics.md +147 -0
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-05-scope.md +161 -0
- package/src/bmm/workflows/1-analysis/create-product-brief/steps/step-06-complete.md +99 -0
- package/src/bmm/workflows/1-analysis/create-product-brief/workflow.md +57 -0
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-01-init.md +87 -0
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-02-domain-analysis.md +156 -0
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-03-competitive-landscape.md +165 -0
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-04-regulatory-focus.md +140 -0
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-05-technical-trends.md +152 -0
- package/src/bmm/workflows/1-analysis/research/domain-steps/step-06-research-synthesis.md +345 -0
- package/src/bmm/workflows/1-analysis/research/market-steps/step-01-init.md +92 -0
- package/src/bmm/workflows/1-analysis/research/market-steps/step-02-customer-behavior.md +164 -0
- package/src/bmm/workflows/1-analysis/research/market-steps/step-03-customer-pain-points.md +174 -0
- package/src/bmm/workflows/1-analysis/research/market-steps/step-04-customer-decisions.md +184 -0
- package/src/bmm/workflows/1-analysis/research/market-steps/step-05-competitive-analysis.md +105 -0
- package/src/bmm/workflows/1-analysis/research/market-steps/step-06-research-completion.md +360 -0
- package/src/bmm/workflows/1-analysis/research/research.template.md +29 -0
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-01-init.md +87 -0
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-02-technical-overview.md +165 -0
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-03-integration-patterns.md +174 -0
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-04-architectural-patterns.md +141 -0
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-05-implementation-research.md +159 -0
- package/src/bmm/workflows/1-analysis/research/technical-steps/step-06-research-synthesis.md +387 -0
- package/src/bmm/workflows/1-analysis/research/workflow-domain-research.md +54 -0
- package/src/bmm/workflows/1-analysis/research/workflow-market-research.md +54 -0
- package/src/bmm/workflows/1-analysis/research/workflow-technical-research.md +54 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/data/domain-complexity.csv +15 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/data/prd-purpose.md +197 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/data/project-types.csv +11 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-01-init.md +139 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-01b-continue.md +100 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02-discovery.md +160 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02b-vision.md +88 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-02c-executive-summary.md +99 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-03-success.md +169 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-04-journeys.md +156 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-05-domain.md +136 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-06-innovation.md +176 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-07-project-type.md +184 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-08-scoping.md +174 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-09-functional.md +175 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-10-nonfunctional.md +189 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-11-polish.md +162 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-c/step-12-complete.md +79 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01-discovery.md +183 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-01b-legacy-conversion.md +149 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-02-review.md +187 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-03-edit.md +192 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-e/step-e-04-complete.md +108 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-01-discovery.md +166 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02-format-detection.md +131 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-02b-parity-check.md +150 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-03-density-validation.md +118 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-04-brief-coverage-validation.md +155 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-05-measurability-validation.md +170 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-06-traceability-validation.md +158 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-07-implementation-leakage-validation.md +147 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-08-domain-compliance-validation.md +182 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-09-project-type-validation.md +202 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-10-smart-validation.md +148 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-11-holistic-quality-validation.md +201 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-12-completeness-validation.md +179 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/steps-v/step-v-13-report-complete.md +164 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/templates/prd-template.md +10 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md +65 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md +65 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md +63 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01-init.md +63 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-01b-continue.md +63 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-02-discovery.md +106 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-03-core-experience.md +111 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-04-emotional-response.md +115 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-05-inspiration.md +127 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-06-design-system.md +167 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-07-defining-experience.md +143 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-08-visual-foundation.md +118 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-09-design-directions.md +154 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-10-user-journeys.md +136 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-11-component-strategy.md +165 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-12-ux-patterns.md +135 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-13-responsive-accessibility.md +192 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/steps/step-14-complete.md +101 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/ux-design-template.md +13 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +45 -0
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-01-document-discovery.md +185 -0
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-02-prd-analysis.md +129 -0
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-03-epic-coverage-validation.md +130 -0
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-04-ux-alignment.md +93 -0
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-05-epic-quality-review.md +196 -0
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/steps/step-06-final-assessment.md +129 -0
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/templates/readiness-report-template.md +4 -0
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +54 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/architecture-decision-template.md +12 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/data/domain-complexity.csv +13 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/data/project-types.csv +7 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-01-init.md +89 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-01b-continue.md +82 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-02-context.md +106 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-03-starter.md +138 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-04-decisions.md +129 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-05-patterns.md +166 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-06-structure.md +186 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-07-validation.md +163 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/steps/step-08-complete.md +38 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/workflow.md +49 -0
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-01-validate-prerequisites.md +129 -0
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-02-design-epics.md +124 -0
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-03-create-stories.md +122 -0
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/steps/step-04-final-validation.md +84 -0
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/templates/epics-template.md +57 -0
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +58 -0
- package/src/bmm/workflows/4-implementation/code-review/checklist.md +23 -0
- package/src/bmm/workflows/4-implementation/code-review/instructions.xml +227 -0
- package/src/bmm/workflows/4-implementation/code-review/workflow.yaml +43 -0
- package/src/bmm/workflows/4-implementation/correct-course/checklist.md +288 -0
- package/src/bmm/workflows/4-implementation/correct-course/instructions.md +207 -0
- package/src/bmm/workflows/4-implementation/correct-course/workflow.yaml +53 -0
- package/src/bmm/workflows/4-implementation/create-story/checklist.md +159 -0
- package/src/bmm/workflows/4-implementation/create-story/instructions.xml +574 -0
- package/src/bmm/workflows/4-implementation/create-story/template.md +79 -0
- package/src/bmm/workflows/4-implementation/create-story/workflow.yaml +52 -0
- package/src/bmm/workflows/4-implementation/dev-story/checklist.md +80 -0
- package/src/bmm/workflows/4-implementation/dev-story/instructions.xml +493 -0
- package/src/bmm/workflows/4-implementation/dev-story/workflow.yaml +20 -0
- package/src/bmm/workflows/4-implementation/retrospective/instructions.md +1444 -0
- package/src/bmm/workflows/4-implementation/retrospective/workflow.yaml +52 -0
- package/src/bmm/workflows/4-implementation/sprint-planning/checklist.md +33 -0
- package/src/bmm/workflows/4-implementation/sprint-planning/instructions.md +232 -0
- package/src/bmm/workflows/4-implementation/sprint-planning/sprint-status-template.yaml +55 -0
- package/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +52 -0
- package/src/bmm/workflows/4-implementation/sprint-status/instructions.md +230 -0
- package/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml +25 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-01-mode-detection.md +158 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-02-context-gathering.md +122 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-03-execute.md +93 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-04-self-check.md +93 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-05-adversarial-review.md +87 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/steps/step-06-resolve-findings.md +146 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-dev/workflow.md +50 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-01-understand.md +204 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-02-investigate.md +152 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-03-generate.md +123 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/steps/step-04-review.md +201 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/tech-spec-template.md +74 -0
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +79 -0
- package/src/bmm/workflows/document-project/checklist.md +245 -0
- package/src/bmm/workflows/document-project/documentation-requirements.csv +12 -0
- package/src/bmm/workflows/document-project/instructions.md +130 -0
- package/src/bmm/workflows/document-project/templates/deep-dive-template.md +345 -0
- package/src/bmm/workflows/document-project/templates/index-template.md +169 -0
- package/src/bmm/workflows/document-project/templates/project-overview-template.md +103 -0
- package/src/bmm/workflows/document-project/templates/project-scan-report-schema.json +160 -0
- package/src/bmm/workflows/document-project/templates/source-tree-template.md +135 -0
- package/src/bmm/workflows/document-project/workflow.yaml +22 -0
- package/src/bmm/workflows/document-project/workflows/deep-dive-instructions.md +298 -0
- package/src/bmm/workflows/document-project/workflows/deep-dive.yaml +31 -0
- package/src/bmm/workflows/document-project/workflows/full-scan-instructions.md +1106 -0
- package/src/bmm/workflows/document-project/workflows/full-scan.yaml +31 -0
- package/src/bmm/workflows/generate-project-context/project-context-template.md +21 -0
- package/src/bmm/workflows/generate-project-context/steps/step-01-discover.md +184 -0
- package/src/bmm/workflows/generate-project-context/steps/step-02-generate.md +322 -0
- package/src/bmm/workflows/generate-project-context/steps/step-03-complete.md +235 -0
- package/src/bmm/workflows/generate-project-context/workflow.md +49 -0
- package/src/bmm/workflows/qa/automate/workflow.yaml +233 -0
- package/src/bmm/workflows/qa-generate-e2e-tests/checklist.md +33 -0
- package/src/bmm/workflows/qa-generate-e2e-tests/instructions.md +110 -0
- package/src/bmm/workflows/qa-generate-e2e-tests/workflow.yaml +42 -0
- package/src/core/agents/bmad-master.md +56 -0
- package/src/core/agents/master-orchestrator.md +54 -0
- package/src/core/config.yaml +9 -0
- package/src/core/module-help.csv +10 -0
- package/src/core/scripts/generate-loop-report.py +72 -0
- package/src/core/skills/prepare-to-merge/SKILL.md +77 -0
- package/src/core/tasks/editorial-review-prose.xml +102 -0
- package/src/core/tasks/editorial-review-structure.xml +208 -0
- package/src/core/tasks/help.md +86 -0
- package/src/core/tasks/index-docs.xml +65 -0
- package/src/core/tasks/review-adversarial-general.xml +66 -0
- package/src/core/tasks/review-adversarial-loop.xml +46 -0
- package/src/core/tasks/review-edge-case-hunter.xml +63 -0
- package/src/core/tasks/review-party-loop.xml +46 -0
- package/src/core/tasks/shard-doc.xml +108 -0
- package/src/core/tasks/workflow.xml +236 -0
- package/src/core/templates/review-loop-report.html +88 -0
- package/src/core/templates/review-loop-report.md +5 -0
- package/src/core/workflows/advanced-elicitation/methods.csv +51 -0
- package/src/core/workflows/advanced-elicitation/workflow.xml +118 -0
- package/src/core/workflows/brainstorming/brain-methods.csv +62 -0
- package/src/core/workflows/brainstorming/steps/step-01-session-setup.md +212 -0
- package/src/core/workflows/brainstorming/steps/step-01b-continue.md +122 -0
- package/src/core/workflows/brainstorming/steps/step-02a-user-selected.md +225 -0
- package/src/core/workflows/brainstorming/steps/step-02b-ai-recommended.md +237 -0
- package/src/core/workflows/brainstorming/steps/step-02c-random-selection.md +209 -0
- package/src/core/workflows/brainstorming/steps/step-02d-progressive-flow.md +264 -0
- package/src/core/workflows/brainstorming/steps/step-02e-deep-dive.md +68 -0
- package/src/core/workflows/brainstorming/steps/step-03-technique-execution.md +403 -0
- package/src/core/workflows/brainstorming/steps/step-04-idea-organization.md +303 -0
- package/src/core/workflows/brainstorming/template.md +15 -0
- package/src/core/workflows/brainstorming/workflow.md +60 -0
- package/src/core/workflows/extract-trackers/workflow.md +45 -0
- package/src/core/workflows/party-mode/steps/step-01-agent-loading.md +142 -0
- package/src/core/workflows/party-mode/steps/step-02-discussion-orchestration.md +187 -0
- package/src/core/workflows/party-mode/steps/step-03-graceful-exit.md +168 -0
- package/src/core/workflows/party-mode/workflow.md +194 -0
- package/src/docs/dev/tmux/actions_popup.py +291 -0
- package/src/docs/dev/tmux/actions_popup.sh +110 -0
- package/src/docs/dev/tmux/claude_usage.sh +15 -0
- package/src/docs/dev/tmux/colors.conf +34 -0
- package/src/docs/dev/tmux/cpu_usage.sh +7 -0
- package/src/docs/dev/tmux/dispatch.sh +10 -0
- package/src/docs/dev/tmux/float_init.sh +13 -0
- package/src/docs/dev/tmux/float_term.sh +23 -0
- package/src/docs/dev/tmux/open_clip.sh +14 -0
- package/src/docs/dev/tmux/paste_clipboard.sh +13 -0
- package/src/docs/dev/tmux/paste_image_wrapper.sh +83 -0
- package/src/docs/dev/tmux/ram_usage.sh +3 -0
- package/src/docs/dev/tmux/title_sync.sh +54 -0
- package/src/docs/dev/tmux/tmux-setup.md +806 -0
- package/src/docs/dev/tmux/tmux.conf +127 -0
- package/src/docs/dev/tmux/xclip +18 -0
|
@@ -0,0 +1,862 @@
|
|
|
1
|
+
# Background Transfers and WebSocket
|
|
2
|
+
|
|
3
|
+
Patterns for background URLSession downloads/uploads and
|
|
4
|
+
URLSessionWebSocketTask with structured concurrency.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Contents
|
|
9
|
+
|
|
10
|
+
- [Background URLSession Configuration](#background-urlsession-configuration)
|
|
11
|
+
- [Background Download Tasks](#background-download-tasks)
|
|
12
|
+
- [Handling Background Session Events](#handling-background-session-events)
|
|
13
|
+
- [Background Upload Tasks](#background-upload-tasks)
|
|
14
|
+
- [URLSessionWebSocketTask](#urlsessionwebsockettask)
|
|
15
|
+
- [WebSocket Reconnection Strategy](#websocket-reconnection-strategy)
|
|
16
|
+
- [WebSocket with Codable Messages](#websocket-with-codable-messages)
|
|
17
|
+
- [Background Session Gotchas](#background-session-gotchas)
|
|
18
|
+
- [Combining Background Downloads with SwiftUI Progress](#combining-background-downloads-with-swiftui-progress)
|
|
19
|
+
- [WebSocket Authentication](#websocket-authentication)
|
|
20
|
+
- [WebSocket Subprotocol Negotiation](#websocket-subprotocol-negotiation)
|
|
21
|
+
|
|
22
|
+
## Background URLSession Configuration
|
|
23
|
+
|
|
24
|
+
Background sessions allow transfers to continue when the app is
|
|
25
|
+
suspended or terminated. The system manages the transfer in a separate
|
|
26
|
+
process and wakes the app on completion.
|
|
27
|
+
|
|
28
|
+
### Why Background Sessions
|
|
29
|
+
|
|
30
|
+
- Downloads/uploads survive app suspension, termination, and device restarts.
|
|
31
|
+
- The system handles retries for network failures automatically.
|
|
32
|
+
- Required for any transfer the user expects to complete even if they
|
|
33
|
+
switch away from the app (e.g., file sync, media downloads).
|
|
34
|
+
|
|
35
|
+
### Configuration
|
|
36
|
+
|
|
37
|
+
```swift
|
|
38
|
+
@available(iOS 15.0, *)
|
|
39
|
+
final class BackgroundDownloadManager: NSObject, Sendable {
|
|
40
|
+
static let shared = BackgroundDownloadManager()
|
|
41
|
+
|
|
42
|
+
/// Use a unique identifier tied to your app's bundle ID.
|
|
43
|
+
/// The system uses this to reconnect to the session after relaunch.
|
|
44
|
+
private let sessionID = "com.example.app.background-downloads"
|
|
45
|
+
|
|
46
|
+
/// Lazy-initialized background session. Must use a delegate, not async/await,
|
|
47
|
+
/// because the system delivers events through the delegate after app relaunch.
|
|
48
|
+
lazy var session: URLSession = {
|
|
49
|
+
let config = URLSessionConfiguration.background(
|
|
50
|
+
withIdentifier: sessionID
|
|
51
|
+
)
|
|
52
|
+
config.isDiscretionary = false // Start immediately (true = system-scheduled)
|
|
53
|
+
config.sessionSendsLaunchEvents = true // Wake app on completion
|
|
54
|
+
config.allowsExpensiveNetworkAccess = true
|
|
55
|
+
config.allowsConstrainedNetworkAccess = false // Respect Low Data Mode
|
|
56
|
+
config.timeoutIntervalForResource = 24 * 60 * 60 // 24 hours
|
|
57
|
+
|
|
58
|
+
return URLSession(
|
|
59
|
+
configuration: config,
|
|
60
|
+
delegate: self,
|
|
61
|
+
delegateQueue: nil // Use a system-managed serial queue
|
|
62
|
+
)
|
|
63
|
+
}()
|
|
64
|
+
|
|
65
|
+
/// Store completionHandler from AppDelegate for system callback
|
|
66
|
+
nonisolated(unsafe) var backgroundCompletionHandler: (() -> Void)?
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Key Configuration Options
|
|
71
|
+
|
|
72
|
+
| Property | Effect |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `isDiscretionary` | `true` = system schedules for optimal battery/network. Use for non-urgent sync. `false` = start immediately. |
|
|
75
|
+
| `sessionSendsLaunchEvents` | Relaunches the app when transfers complete. Required for completion handling. |
|
|
76
|
+
| `allowsConstrainedNetworkAccess` | `false` = honor Low Data Mode. Good for optional downloads. |
|
|
77
|
+
| `allowsExpensiveNetworkAccess` | `false` = Wi-Fi only. Use for large transfers. |
|
|
78
|
+
| `timeoutIntervalForResource` | Maximum time for the entire transfer. Default is 7 days. |
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Background Download Tasks
|
|
83
|
+
|
|
84
|
+
Background downloads must use `downloadTask(with:)`, not `data(for:)`.
|
|
85
|
+
The async/await overloads are not supported for background sessions --
|
|
86
|
+
you must use the delegate pattern.
|
|
87
|
+
|
|
88
|
+
```swift
|
|
89
|
+
extension BackgroundDownloadManager {
|
|
90
|
+
func startDownload(from url: URL) -> URLSessionDownloadTask {
|
|
91
|
+
let task = session.downloadTask(with: url)
|
|
92
|
+
task.earliestBeginDate = Date() // Start now
|
|
93
|
+
task.countOfBytesClientExpectsToSend = 0
|
|
94
|
+
task.countOfBytesClientExpectsToReceive = 50 * 1024 * 1024 // Estimated size
|
|
95
|
+
task.resume()
|
|
96
|
+
return task
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
func startDownload(from url: URL, resumeData: Data) -> URLSessionDownloadTask {
|
|
100
|
+
let task = session.downloadTask(withResumeData: resumeData)
|
|
101
|
+
task.resume()
|
|
102
|
+
return task
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Download Delegate
|
|
108
|
+
|
|
109
|
+
```swift
|
|
110
|
+
extension BackgroundDownloadManager: URLSessionDownloadDelegate {
|
|
111
|
+
nonisolated func urlSession(
|
|
112
|
+
_ session: URLSession,
|
|
113
|
+
downloadTask: URLSessionDownloadTask,
|
|
114
|
+
didFinishDownloadingTo location: URL
|
|
115
|
+
) {
|
|
116
|
+
// CRITICAL: Move the file before this method returns.
|
|
117
|
+
// The temporary file is deleted immediately after.
|
|
118
|
+
let destinationDir = FileManager.default.urls(
|
|
119
|
+
for: .documentDirectory,
|
|
120
|
+
in: .userDomainMask
|
|
121
|
+
).first!
|
|
122
|
+
|
|
123
|
+
let filename = downloadTask.originalRequest?.url?.lastPathComponent ?? UUID().uuidString
|
|
124
|
+
let destination = destinationDir.appendingPathComponent(filename)
|
|
125
|
+
|
|
126
|
+
do {
|
|
127
|
+
// Remove existing file if present
|
|
128
|
+
if FileManager.default.fileExists(atPath: destination.path) {
|
|
129
|
+
try FileManager.default.removeItem(at: destination)
|
|
130
|
+
}
|
|
131
|
+
try FileManager.default.moveItem(at: location, to: destination)
|
|
132
|
+
// Notify the app (post notification, update state, etc.)
|
|
133
|
+
} catch {
|
|
134
|
+
// Handle file move failure
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
nonisolated func urlSession(
|
|
139
|
+
_ session: URLSession,
|
|
140
|
+
downloadTask: URLSessionDownloadTask,
|
|
141
|
+
didWriteData bytesWritten: Int64,
|
|
142
|
+
totalBytesWritten: Int64,
|
|
143
|
+
totalBytesExpectedToWrite: Int64
|
|
144
|
+
) {
|
|
145
|
+
guard totalBytesExpectedToWrite > 0 else { return }
|
|
146
|
+
let progress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)
|
|
147
|
+
// Update progress UI (dispatch to main if needed)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
nonisolated func urlSession(
|
|
151
|
+
_ session: URLSession,
|
|
152
|
+
task: URLSessionTask,
|
|
153
|
+
didCompleteWithError error: (any Error)?
|
|
154
|
+
) {
|
|
155
|
+
guard let error else { return } // Success handled in didFinishDownloadingTo
|
|
156
|
+
|
|
157
|
+
// Check for resume data on failure
|
|
158
|
+
let nsError = error as NSError
|
|
159
|
+
if let resumeData = nsError.userInfo[NSURLSessionDownloadTaskResumeData] as? Data {
|
|
160
|
+
// Store resumeData for retry
|
|
161
|
+
saveResumeData(resumeData, for: task)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private func saveResumeData(_ data: Data, for task: URLSessionTask) {
|
|
166
|
+
// Persist resume data to disk for later retry
|
|
167
|
+
let key = task.originalRequest?.url?.absoluteString ?? ""
|
|
168
|
+
let path = FileManager.default.temporaryDirectory
|
|
169
|
+
.appendingPathComponent("resume-\(key.hashValue)")
|
|
170
|
+
try? data.write(to: path)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Handling Background Session Events
|
|
178
|
+
|
|
179
|
+
When the system completes a background transfer and the app is not
|
|
180
|
+
running, it relaunches the app and calls the `AppDelegate` method. You
|
|
181
|
+
must call the system's completion handler after processing all events.
|
|
182
|
+
|
|
183
|
+
### UIKit App Delegate
|
|
184
|
+
|
|
185
|
+
```swift
|
|
186
|
+
class AppDelegate: UIResponder, UIApplicationDelegate {
|
|
187
|
+
func application(
|
|
188
|
+
_ application: UIApplication,
|
|
189
|
+
handleEventsForBackgroundURLSession identifier: String,
|
|
190
|
+
completionHandler: @escaping () -> Void
|
|
191
|
+
) {
|
|
192
|
+
// Store the completion handler. The BackgroundDownloadManager will
|
|
193
|
+
// call it after processing all pending events.
|
|
194
|
+
BackgroundDownloadManager.shared.backgroundCompletionHandler = completionHandler
|
|
195
|
+
|
|
196
|
+
// Accessing .session triggers lazy initialization, which reconnects
|
|
197
|
+
// to the background session and starts delivering delegate events.
|
|
198
|
+
_ = BackgroundDownloadManager.shared.session
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Session-Level Delegate
|
|
204
|
+
|
|
205
|
+
```swift
|
|
206
|
+
extension BackgroundDownloadManager: URLSessionDelegate {
|
|
207
|
+
nonisolated func urlSessionDidFinishEvents(
|
|
208
|
+
forBackgroundURLSession session: URLSession
|
|
209
|
+
) {
|
|
210
|
+
// Called after ALL pending delegate events have been delivered.
|
|
211
|
+
// Call the stored completion handler on the main thread.
|
|
212
|
+
Task { @MainActor in
|
|
213
|
+
backgroundCompletionHandler?()
|
|
214
|
+
backgroundCompletionHandler = nil
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### SwiftUI App with AppDelegate Adapter
|
|
221
|
+
|
|
222
|
+
```swift
|
|
223
|
+
@main
|
|
224
|
+
struct MyApp: App {
|
|
225
|
+
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
|
226
|
+
|
|
227
|
+
var body: some Scene {
|
|
228
|
+
WindowGroup {
|
|
229
|
+
ContentView()
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Important:** The completion handler must be called exactly once and
|
|
236
|
+
on the main thread. Failing to call it causes the system to take a
|
|
237
|
+
snapshot of the app in the wrong state and may waste background runtime.
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Background Upload Tasks
|
|
242
|
+
|
|
243
|
+
Background uploads require data from a file, not from memory.
|
|
244
|
+
|
|
245
|
+
```swift
|
|
246
|
+
extension BackgroundDownloadManager {
|
|
247
|
+
func startUpload(
|
|
248
|
+
to url: URL,
|
|
249
|
+
fileURL: URL,
|
|
250
|
+
method: String = "POST",
|
|
251
|
+
headers: [String: String] = [:]
|
|
252
|
+
) -> URLSessionUploadTask {
|
|
253
|
+
var request = URLRequest(url: url)
|
|
254
|
+
request.httpMethod = method
|
|
255
|
+
for (key, value) in headers {
|
|
256
|
+
request.setValue(value, forHTTPHeaderField: key)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
let task = session.uploadTask(with: request, fromFile: fileURL)
|
|
260
|
+
task.resume()
|
|
261
|
+
return task
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Upload Delegate Methods
|
|
267
|
+
|
|
268
|
+
```swift
|
|
269
|
+
extension BackgroundDownloadManager {
|
|
270
|
+
nonisolated func urlSession(
|
|
271
|
+
_ session: URLSession,
|
|
272
|
+
task: URLSessionTask,
|
|
273
|
+
didSendBodyData bytesSent: Int64,
|
|
274
|
+
totalBytesSent: Int64,
|
|
275
|
+
totalBytesExpectedToSend: Int64
|
|
276
|
+
) {
|
|
277
|
+
guard totalBytesExpectedToSend > 0 else { return }
|
|
278
|
+
let progress = Double(totalBytesSent) / Double(totalBytesExpectedToSend)
|
|
279
|
+
// Update progress UI
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Constraints of background uploads:**
|
|
285
|
+
- Data must come from a file (`uploadTask(with:fromFile:)`).
|
|
286
|
+
- `uploadTask(with:from: Data)` is not supported in background sessions.
|
|
287
|
+
- Write multipart form data to a temporary file first, then upload.
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## URLSessionWebSocketTask
|
|
292
|
+
|
|
293
|
+
`URLSessionWebSocketTask` provides native WebSocket support without
|
|
294
|
+
third-party libraries. Available since iOS 13.
|
|
295
|
+
|
|
296
|
+
### Basic Connection
|
|
297
|
+
|
|
298
|
+
```swift
|
|
299
|
+
@available(iOS 15.0, *)
|
|
300
|
+
final class WebSocketConnection: Sendable {
|
|
301
|
+
private let task: URLSessionWebSocketTask
|
|
302
|
+
|
|
303
|
+
init(url: URL, session: URLSession = .shared) {
|
|
304
|
+
self.task = session.webSocketTask(with: url)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
func connect() {
|
|
308
|
+
task.resume()
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
func disconnect(reason: String? = nil) {
|
|
312
|
+
task.cancel(with: .normalClosure, reason: reason?.data(using: .utf8))
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
func send(_ message: URLSessionWebSocketTask.Message) async throws {
|
|
316
|
+
try await task.send(message)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
func send(text: String) async throws {
|
|
320
|
+
try await task.send(.string(text))
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
func send(data: Data) async throws {
|
|
324
|
+
try await task.send(.data(data))
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
func receive() async throws -> URLSessionWebSocketTask.Message {
|
|
328
|
+
try await task.receive()
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### WebSocket with Structured Concurrency
|
|
334
|
+
|
|
335
|
+
The key pattern: run a receive loop as an async task that yields
|
|
336
|
+
messages through an `AsyncStream`. This integrates naturally with
|
|
337
|
+
structured concurrency.
|
|
338
|
+
|
|
339
|
+
```swift
|
|
340
|
+
@available(iOS 15.0, *)
|
|
341
|
+
actor WebSocketManager {
|
|
342
|
+
private var task: URLSessionWebSocketTask?
|
|
343
|
+
private var receiveTask: Task<Void, Never>?
|
|
344
|
+
private let session: URLSession
|
|
345
|
+
private let url: URL
|
|
346
|
+
|
|
347
|
+
enum Event: Sendable {
|
|
348
|
+
case connected
|
|
349
|
+
case text(String)
|
|
350
|
+
case data(Data)
|
|
351
|
+
case disconnected(URLSessionWebSocketTask.CloseCode, Data?)
|
|
352
|
+
case error(Error)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
init(url: URL, session: URLSession = .shared) {
|
|
356
|
+
self.url = url
|
|
357
|
+
self.session = session
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/// Returns a stream of WebSocket events. Call `connect()` to start.
|
|
361
|
+
func events() -> AsyncStream<Event> {
|
|
362
|
+
AsyncStream { continuation in
|
|
363
|
+
let wsTask = session.webSocketTask(with: url)
|
|
364
|
+
self.task = wsTask
|
|
365
|
+
|
|
366
|
+
wsTask.resume()
|
|
367
|
+
continuation.yield(.connected)
|
|
368
|
+
|
|
369
|
+
// Start the receive loop
|
|
370
|
+
self.receiveTask = Task { [weak self] in
|
|
371
|
+
await self?.receiveLoop(continuation: continuation)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
continuation.onTermination = { _ in
|
|
375
|
+
Task { [weak self] in
|
|
376
|
+
await self?.disconnect()
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
private func receiveLoop(continuation: AsyncStream<Event>.Continuation) async {
|
|
383
|
+
guard let task else { return }
|
|
384
|
+
|
|
385
|
+
while !Task.isCancelled {
|
|
386
|
+
do {
|
|
387
|
+
let message = try await task.receive()
|
|
388
|
+
switch message {
|
|
389
|
+
case .string(let text):
|
|
390
|
+
continuation.yield(.text(text))
|
|
391
|
+
case .data(let data):
|
|
392
|
+
continuation.yield(.data(data))
|
|
393
|
+
@unknown default:
|
|
394
|
+
break
|
|
395
|
+
}
|
|
396
|
+
} catch {
|
|
397
|
+
// The receive threw -- connection closed or failed
|
|
398
|
+
let closeCode = task.closeCode
|
|
399
|
+
let closeReason = task.closeReason
|
|
400
|
+
if closeCode == .invalid {
|
|
401
|
+
// Unexpected disconnection
|
|
402
|
+
continuation.yield(.error(error))
|
|
403
|
+
} else {
|
|
404
|
+
continuation.yield(.disconnected(closeCode, closeReason))
|
|
405
|
+
}
|
|
406
|
+
continuation.finish()
|
|
407
|
+
return
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
func send(text: String) async throws {
|
|
413
|
+
try await task?.send(.string(text))
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
func send(data: Data) async throws {
|
|
417
|
+
try await task?.send(.data(data))
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
func disconnect() {
|
|
421
|
+
receiveTask?.cancel()
|
|
422
|
+
receiveTask = nil
|
|
423
|
+
task?.cancel(with: .normalClosure, reason: nil)
|
|
424
|
+
task = nil
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/// Send periodic pings to keep the connection alive
|
|
428
|
+
func startPinging(interval: Duration = .seconds(30)) {
|
|
429
|
+
Task { [weak self] in
|
|
430
|
+
while !Task.isCancelled {
|
|
431
|
+
try? await Task.sleep(for: interval)
|
|
432
|
+
guard let self else { return }
|
|
433
|
+
await self.ping()
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
private func ping() {
|
|
439
|
+
task?.sendPing { error in
|
|
440
|
+
if let error {
|
|
441
|
+
// Connection may be dead
|
|
442
|
+
print("Ping failed: \(error)")
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### Usage in SwiftUI
|
|
450
|
+
|
|
451
|
+
```swift
|
|
452
|
+
@MainActor
|
|
453
|
+
@Observable final class ChatStore {
|
|
454
|
+
var messages: [ChatMessage] = []
|
|
455
|
+
var connectionState: ConnectionState = .disconnected
|
|
456
|
+
|
|
457
|
+
enum ConnectionState { case disconnected, connecting, connected }
|
|
458
|
+
|
|
459
|
+
private let wsManager: WebSocketManager
|
|
460
|
+
private var eventTask: Task<Void, Never>?
|
|
461
|
+
|
|
462
|
+
init(url: URL) {
|
|
463
|
+
self.wsManager = WebSocketManager(url: url)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
func connect() async {
|
|
467
|
+
connectionState = .connecting
|
|
468
|
+
let stream = await wsManager.events()
|
|
469
|
+
|
|
470
|
+
eventTask = Task {
|
|
471
|
+
for await event in stream {
|
|
472
|
+
await handleEvent(event)
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
func sendMessage(_ text: String) async {
|
|
478
|
+
do {
|
|
479
|
+
try await wsManager.send(text: text)
|
|
480
|
+
messages.append(ChatMessage(text: text, isOutgoing: true))
|
|
481
|
+
} catch {
|
|
482
|
+
// Handle send failure
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
func disconnect() async {
|
|
487
|
+
eventTask?.cancel()
|
|
488
|
+
eventTask = nil
|
|
489
|
+
await wsManager.disconnect()
|
|
490
|
+
connectionState = .disconnected
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
private func handleEvent(_ event: WebSocketManager.Event) async {
|
|
494
|
+
switch event {
|
|
495
|
+
case .connected:
|
|
496
|
+
connectionState = .connected
|
|
497
|
+
case .text(let text):
|
|
498
|
+
messages.append(ChatMessage(text: text, isOutgoing: false))
|
|
499
|
+
case .data(let data):
|
|
500
|
+
if let text = String(data: data, encoding: .utf8) {
|
|
501
|
+
messages.append(ChatMessage(text: text, isOutgoing: false))
|
|
502
|
+
}
|
|
503
|
+
case .disconnected:
|
|
504
|
+
connectionState = .disconnected
|
|
505
|
+
case .error:
|
|
506
|
+
connectionState = .disconnected
|
|
507
|
+
// Optionally trigger reconnection
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
```swift
|
|
514
|
+
struct ChatView: View {
|
|
515
|
+
@State var store: ChatStore
|
|
516
|
+
|
|
517
|
+
var body: some View {
|
|
518
|
+
List(store.messages) { message in
|
|
519
|
+
ChatBubble(message: message)
|
|
520
|
+
}
|
|
521
|
+
.task { await store.connect() }
|
|
522
|
+
.onDisappear { Task { await store.disconnect() } }
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## WebSocket Reconnection Strategy
|
|
530
|
+
|
|
531
|
+
Network drops happen. A robust WebSocket client must reconnect
|
|
532
|
+
automatically with exponential backoff.
|
|
533
|
+
|
|
534
|
+
```swift
|
|
535
|
+
@available(iOS 15.0, *)
|
|
536
|
+
actor ReconnectingWebSocket {
|
|
537
|
+
private let url: URL
|
|
538
|
+
private let session: URLSession
|
|
539
|
+
private let maxReconnectAttempts: Int
|
|
540
|
+
private let initialDelay: Duration
|
|
541
|
+
private let maxDelay: Duration
|
|
542
|
+
|
|
543
|
+
private var currentManager: WebSocketManager?
|
|
544
|
+
private var reconnectAttempts = 0
|
|
545
|
+
private var isIntentionalDisconnect = false
|
|
546
|
+
|
|
547
|
+
init(
|
|
548
|
+
url: URL,
|
|
549
|
+
session: URLSession = .shared,
|
|
550
|
+
maxReconnectAttempts: Int = 10,
|
|
551
|
+
initialDelay: Duration = .seconds(1),
|
|
552
|
+
maxDelay: Duration = .seconds(60)
|
|
553
|
+
) {
|
|
554
|
+
self.url = url
|
|
555
|
+
self.session = session
|
|
556
|
+
self.maxReconnectAttempts = maxReconnectAttempts
|
|
557
|
+
self.initialDelay = initialDelay
|
|
558
|
+
self.maxDelay = maxDelay
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/// Returns a stream that automatically reconnects on disconnection.
|
|
562
|
+
func events() -> AsyncStream<WebSocketManager.Event> {
|
|
563
|
+
AsyncStream { continuation in
|
|
564
|
+
Task {
|
|
565
|
+
await connectWithReconnection(continuation: continuation)
|
|
566
|
+
}
|
|
567
|
+
continuation.onTermination = { _ in
|
|
568
|
+
Task { [weak self] in
|
|
569
|
+
await self?.intentionalDisconnect()
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
private func connectWithReconnection(
|
|
576
|
+
continuation: AsyncStream<WebSocketManager.Event>.Continuation
|
|
577
|
+
) async {
|
|
578
|
+
while !isIntentionalDisconnect && reconnectAttempts < maxReconnectAttempts {
|
|
579
|
+
guard !Task.isCancelled else { break }
|
|
580
|
+
|
|
581
|
+
let manager = WebSocketManager(url: url, session: session)
|
|
582
|
+
currentManager = manager
|
|
583
|
+
let stream = await manager.events()
|
|
584
|
+
|
|
585
|
+
for await event in stream {
|
|
586
|
+
switch event {
|
|
587
|
+
case .connected:
|
|
588
|
+
reconnectAttempts = 0 // Reset on successful connection
|
|
589
|
+
continuation.yield(event)
|
|
590
|
+
case .error, .disconnected:
|
|
591
|
+
continuation.yield(event)
|
|
592
|
+
default:
|
|
593
|
+
continuation.yield(event)
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Stream ended -- attempt reconnection unless intentional
|
|
598
|
+
guard !isIntentionalDisconnect, !Task.isCancelled else { break }
|
|
599
|
+
|
|
600
|
+
reconnectAttempts += 1
|
|
601
|
+
let delay = calculateBackoff()
|
|
602
|
+
do {
|
|
603
|
+
try await Task.sleep(for: delay)
|
|
604
|
+
} catch {
|
|
605
|
+
break // Cancelled during sleep
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
continuation.finish()
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
private func calculateBackoff() -> Duration {
|
|
613
|
+
let base = Double(initialDelay.components.seconds) * pow(2.0, Double(reconnectAttempts - 1))
|
|
614
|
+
let capped = min(base, Double(maxDelay.components.seconds))
|
|
615
|
+
let jitter = Double.random(in: 0...(capped * 0.25))
|
|
616
|
+
return .seconds(capped + jitter)
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
func send(text: String) async throws {
|
|
620
|
+
try await currentManager?.send(text: text)
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
func send(data: Data) async throws {
|
|
624
|
+
try await currentManager?.send(data: data)
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
private func intentionalDisconnect() {
|
|
628
|
+
isIntentionalDisconnect = true
|
|
629
|
+
Task {
|
|
630
|
+
await currentManager?.disconnect()
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
---
|
|
637
|
+
|
|
638
|
+
## WebSocket with Codable Messages
|
|
639
|
+
|
|
640
|
+
For typed message protocols (common in chat, gaming, real-time apps),
|
|
641
|
+
decode/encode messages automatically.
|
|
642
|
+
|
|
643
|
+
```swift
|
|
644
|
+
protocol WebSocketMessage: Codable, Sendable {
|
|
645
|
+
static var messageType: String { get }
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
struct TypedWebSocketTransport {
|
|
649
|
+
private let manager: WebSocketManager
|
|
650
|
+
private let encoder = JSONEncoder()
|
|
651
|
+
private let decoder = JSONDecoder()
|
|
652
|
+
|
|
653
|
+
init(manager: WebSocketManager) {
|
|
654
|
+
self.manager = manager
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
func send<T: WebSocketMessage>(_ message: T) async throws {
|
|
658
|
+
let envelope = MessageEnvelope(
|
|
659
|
+
type: T.messageType,
|
|
660
|
+
payload: try encoder.encode(message)
|
|
661
|
+
)
|
|
662
|
+
let data = try encoder.encode(envelope)
|
|
663
|
+
try await manager.send(data: data)
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/// Typed event stream that decodes known message types
|
|
667
|
+
func typedEvents() async -> AsyncStream<DecodedEvent> {
|
|
668
|
+
let rawEvents = await manager.events()
|
|
669
|
+
return AsyncStream { continuation in
|
|
670
|
+
Task {
|
|
671
|
+
for await event in rawEvents {
|
|
672
|
+
switch event {
|
|
673
|
+
case .data(let data):
|
|
674
|
+
if let envelope = try? decoder.decode(MessageEnvelope.self, from: data) {
|
|
675
|
+
continuation.yield(.message(type: envelope.type, payload: envelope.payload))
|
|
676
|
+
}
|
|
677
|
+
case .text(let text):
|
|
678
|
+
if let data = text.data(using: .utf8),
|
|
679
|
+
let envelope = try? decoder.decode(MessageEnvelope.self, from: data) {
|
|
680
|
+
continuation.yield(.message(type: envelope.type, payload: envelope.payload))
|
|
681
|
+
}
|
|
682
|
+
case .connected:
|
|
683
|
+
continuation.yield(.connected)
|
|
684
|
+
case .disconnected(let code, _):
|
|
685
|
+
continuation.yield(.disconnected(code))
|
|
686
|
+
case .error(let error):
|
|
687
|
+
continuation.yield(.error(error))
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
continuation.finish()
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
enum DecodedEvent: Sendable {
|
|
696
|
+
case connected
|
|
697
|
+
case message(type: String, payload: Data)
|
|
698
|
+
case disconnected(URLSessionWebSocketTask.CloseCode)
|
|
699
|
+
case error(Error)
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
private struct MessageEnvelope: Codable, Sendable {
|
|
703
|
+
let type: String
|
|
704
|
+
let payload: Data
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
---
|
|
710
|
+
|
|
711
|
+
## Background Session Gotchas
|
|
712
|
+
|
|
713
|
+
### The session identifier must be unique per app
|
|
714
|
+
If two sessions share the same identifier, events may be delivered to
|
|
715
|
+
the wrong delegate. Use your bundle identifier as a prefix.
|
|
716
|
+
|
|
717
|
+
### Background sessions do not support async/await overloads
|
|
718
|
+
The `data(for:)` and `download(for:)` async methods are not available
|
|
719
|
+
on background sessions. Use `downloadTask(with:)` and the delegate.
|
|
720
|
+
|
|
721
|
+
### Only download and upload tasks are supported
|
|
722
|
+
Data tasks (`dataTask`) are not supported in background sessions. Convert
|
|
723
|
+
data requests to download tasks if needed for background execution.
|
|
724
|
+
|
|
725
|
+
### The app may be terminated and relaunched
|
|
726
|
+
Store any state you need (task identifiers, file destinations) to disk.
|
|
727
|
+
Do not rely on in-memory state surviving a background relaunch.
|
|
728
|
+
|
|
729
|
+
### File must be moved in didFinishDownloadingTo
|
|
730
|
+
The temporary file at `location` is deleted as soon as the delegate
|
|
731
|
+
method returns. Always move or copy the file synchronously within the
|
|
732
|
+
callback.
|
|
733
|
+
|
|
734
|
+
### Call the system completion handler exactly once
|
|
735
|
+
Store the completion handler from
|
|
736
|
+
`application(_:handleEventsForBackgroundURLSession:completionHandler:)`
|
|
737
|
+
and invoke it in `urlSessionDidFinishEvents(forBackgroundURLSession:)`
|
|
738
|
+
on the main thread.
|
|
739
|
+
|
|
740
|
+
### Test on a real device
|
|
741
|
+
Background session behavior differs significantly between the Simulator
|
|
742
|
+
and real devices. Always test background transfers on hardware.
|
|
743
|
+
|
|
744
|
+
---
|
|
745
|
+
|
|
746
|
+
## Combining Background Downloads with SwiftUI Progress
|
|
747
|
+
|
|
748
|
+
Bridge the delegate-based background download to an `@Observable` model
|
|
749
|
+
for live UI updates.
|
|
750
|
+
|
|
751
|
+
```swift
|
|
752
|
+
@MainActor
|
|
753
|
+
@Observable final class DownloadTracker {
|
|
754
|
+
var downloads: [URL: DownloadProgress] = [:]
|
|
755
|
+
|
|
756
|
+
struct DownloadProgress: Sendable {
|
|
757
|
+
var fractionCompleted: Double = 0
|
|
758
|
+
var state: State = .downloading
|
|
759
|
+
|
|
760
|
+
enum State: Sendable { case downloading, completed, failed }
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
func updateProgress(for url: URL, fraction: Double) {
|
|
764
|
+
downloads[url, default: DownloadProgress()].fractionCompleted = fraction
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
func markCompleted(for url: URL) {
|
|
768
|
+
downloads[url]?.state = .completed
|
|
769
|
+
downloads[url]?.fractionCompleted = 1.0
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
func markFailed(for url: URL) {
|
|
773
|
+
downloads[url]?.state = .failed
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
Wire the delegate to the tracker:
|
|
779
|
+
|
|
780
|
+
```swift
|
|
781
|
+
extension BackgroundDownloadManager {
|
|
782
|
+
// Called from delegate methods; dispatches to MainActor
|
|
783
|
+
func reportProgress(for url: URL, fraction: Double) {
|
|
784
|
+
Task { @MainActor in
|
|
785
|
+
downloadTracker.updateProgress(for: url, fraction: fraction)
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
---
|
|
792
|
+
|
|
793
|
+
## WebSocket Authentication
|
|
794
|
+
|
|
795
|
+
WebSocket connections often require authentication via a token in the
|
|
796
|
+
initial handshake (either as a query parameter or a custom header).
|
|
797
|
+
|
|
798
|
+
```swift
|
|
799
|
+
func authenticatedWebSocket(
|
|
800
|
+
baseURL: URL,
|
|
801
|
+
token: String
|
|
802
|
+
) -> URLSessionWebSocketTask {
|
|
803
|
+
// Option 1: Token as query parameter
|
|
804
|
+
guard var components = URLComponents(url: baseURL, resolvingAgainstBaseURL: true) else {
|
|
805
|
+
preconditionFailure("Invalid URL components for: \(baseURL)")
|
|
806
|
+
}
|
|
807
|
+
components.queryItems = [URLQueryItem(name: "token", value: token)]
|
|
808
|
+
guard let authenticatedURL = components.url else {
|
|
809
|
+
preconditionFailure("Failed to construct URL from components")
|
|
810
|
+
}
|
|
811
|
+
let task = URLSession.shared.webSocketTask(with: authenticatedURL)
|
|
812
|
+
|
|
813
|
+
// Option 2: Token as custom header (use URLRequest)
|
|
814
|
+
var request = URLRequest(url: baseURL)
|
|
815
|
+
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
|
|
816
|
+
let taskWithHeader = URLSession.shared.webSocketTask(with: request)
|
|
817
|
+
|
|
818
|
+
return taskWithHeader
|
|
819
|
+
}
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
**Prefer the header approach** when the server supports it. Query
|
|
823
|
+
parameters may appear in server access logs, which is a security
|
|
824
|
+
concern for tokens.
|
|
825
|
+
|
|
826
|
+
---
|
|
827
|
+
|
|
828
|
+
## WebSocket Subprotocol Negotiation
|
|
829
|
+
|
|
830
|
+
```swift
|
|
831
|
+
// Request a specific subprotocol (e.g., graphql-ws)
|
|
832
|
+
let task = URLSession.shared.webSocketTask(
|
|
833
|
+
with: url,
|
|
834
|
+
protocols: ["graphql-transport-ws"]
|
|
835
|
+
)
|
|
836
|
+
task.resume()
|
|
837
|
+
|
|
838
|
+
// After connection, verify the negotiated protocol
|
|
839
|
+
// via the URLSessionWebSocketDelegate
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
```swift
|
|
843
|
+
extension WebSocketConnection: URLSessionWebSocketDelegate {
|
|
844
|
+
nonisolated func urlSession(
|
|
845
|
+
_ session: URLSession,
|
|
846
|
+
webSocketTask: URLSessionWebSocketTask,
|
|
847
|
+
didOpenWithProtocol protocol: String?
|
|
848
|
+
) {
|
|
849
|
+
print("Connected with protocol: \(`protocol` ?? "none")")
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
nonisolated func urlSession(
|
|
853
|
+
_ session: URLSession,
|
|
854
|
+
webSocketTask: URLSessionWebSocketTask,
|
|
855
|
+
didCloseWith closeCode: URLSessionWebSocketTask.CloseCode,
|
|
856
|
+
reason: Data?
|
|
857
|
+
) {
|
|
858
|
+
let reasonString = reason.flatMap { String(data: $0, encoding: .utf8) }
|
|
859
|
+
print("Closed: \(closeCode) - \(reasonString ?? "no reason")")
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
```
|