@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,745 @@
|
|
|
1
|
+
# Rich Notifications
|
|
2
|
+
|
|
3
|
+
Rich notifications enhance the standard notification banner with images, video, audio, custom UI, and interactive elements. They use two extension types: Notification Service Extension (modifies content before display) and Notification Content Extension (provides custom UI in the expanded notification).
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- [Notification Service Extension](#notification-service-extension)
|
|
8
|
+
- [Notification Attachments](#notification-attachments)
|
|
9
|
+
- [Notification Content Extension](#notification-content-extension)
|
|
10
|
+
- [Communication Notifications](#communication-notifications)
|
|
11
|
+
- [Extension Gotchas](#extension-gotchas)
|
|
12
|
+
- [Complete Service Extension Example](#complete-service-extension-example)
|
|
13
|
+
|
|
14
|
+
## Notification Service Extension
|
|
15
|
+
|
|
16
|
+
A Notification Service Extension runs when a notification with `mutable-content: 1` arrives. It has approximately 30 seconds to modify the notification content before the system displays it. If the extension does not call the content handler in time, the system displays the original notification.
|
|
17
|
+
|
|
18
|
+
### Creating the Extension
|
|
19
|
+
|
|
20
|
+
In Xcode: File > New > Target > Notification Service Extension. This creates a new target with a `NotificationService` class.
|
|
21
|
+
|
|
22
|
+
```swift
|
|
23
|
+
import UserNotifications
|
|
24
|
+
|
|
25
|
+
class NotificationService: UNNotificationServiceExtension {
|
|
26
|
+
private var contentHandler: ((UNNotificationContent) -> Void)?
|
|
27
|
+
private var bestAttemptContent: UNMutableNotificationContent?
|
|
28
|
+
|
|
29
|
+
override func didReceive(
|
|
30
|
+
_ request: UNNotificationRequest,
|
|
31
|
+
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
|
|
32
|
+
) {
|
|
33
|
+
self.contentHandler = contentHandler
|
|
34
|
+
bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent
|
|
35
|
+
|
|
36
|
+
guard let content = bestAttemptContent else {
|
|
37
|
+
contentHandler(request.content)
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Modify the notification content here
|
|
42
|
+
Task {
|
|
43
|
+
await processNotification(content: content)
|
|
44
|
+
contentHandler(content)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// Called if the extension is about to be terminated (ran out of time).
|
|
49
|
+
/// Deliver the best attempt -- even a partially modified notification
|
|
50
|
+
/// is better than nothing.
|
|
51
|
+
override func serviceExtensionTimeWillExpire() {
|
|
52
|
+
if let handler = contentHandler, let content = bestAttemptContent {
|
|
53
|
+
handler(content)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private func processNotification(content: UNMutableNotificationContent) async {
|
|
58
|
+
// Download image if URL is provided
|
|
59
|
+
if let imageUrlString = content.userInfo["imageUrl"] as? String {
|
|
60
|
+
await attachImage(from: imageUrlString, to: content)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Decrypt body if encrypted
|
|
64
|
+
if let encrypted = content.userInfo["encryptedBody"] as? String {
|
|
65
|
+
content.body = decrypt(encrypted)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Downloading Images and Media
|
|
72
|
+
|
|
73
|
+
Download media from a URL and attach it to the notification. The attachment must be written to disk in the extension's temporary directory.
|
|
74
|
+
|
|
75
|
+
```swift
|
|
76
|
+
extension NotificationService {
|
|
77
|
+
private func attachImage(
|
|
78
|
+
from urlString: String,
|
|
79
|
+
to content: UNMutableNotificationContent
|
|
80
|
+
) async {
|
|
81
|
+
guard let url = URL(string: urlString) else { return }
|
|
82
|
+
|
|
83
|
+
do {
|
|
84
|
+
let (data, response) = try await URLSession.shared.data(from: url)
|
|
85
|
+
|
|
86
|
+
// Determine file extension from MIME type
|
|
87
|
+
let ext = fileExtension(for: response.mimeType)
|
|
88
|
+
let tempDir = FileManager.default.temporaryDirectory
|
|
89
|
+
let fileURL = tempDir.appendingPathComponent(UUID().uuidString + ext)
|
|
90
|
+
|
|
91
|
+
try data.write(to: fileURL)
|
|
92
|
+
|
|
93
|
+
let attachment = try UNNotificationAttachment(
|
|
94
|
+
identifier: "image",
|
|
95
|
+
url: fileURL,
|
|
96
|
+
options: nil
|
|
97
|
+
)
|
|
98
|
+
content.attachments = [attachment]
|
|
99
|
+
} catch {
|
|
100
|
+
print("Failed to download notification image: \(error)")
|
|
101
|
+
// Notification displays without the image -- graceful degradation
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private func fileExtension(for mimeType: String?) -> String {
|
|
106
|
+
switch mimeType {
|
|
107
|
+
case "image/jpeg": return ".jpg"
|
|
108
|
+
case "image/png": return ".png"
|
|
109
|
+
case "image/gif": return ".gif"
|
|
110
|
+
case "video/mp4", "video/mpeg4": return ".mp4"
|
|
111
|
+
case "audio/mpeg", "audio/mp3": return ".mp3"
|
|
112
|
+
case "audio/wav": return ".wav"
|
|
113
|
+
default: return ".jpg"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Decrypting Encrypted Payloads
|
|
120
|
+
|
|
121
|
+
Use the service extension to decrypt sensitive notification content. The APNs payload carries an encrypted blob; the extension decrypts it before display.
|
|
122
|
+
|
|
123
|
+
```swift
|
|
124
|
+
extension NotificationService {
|
|
125
|
+
private func decrypt(_ encryptedBase64: String) -> String {
|
|
126
|
+
guard let data = Data(base64Encoded: encryptedBase64) else {
|
|
127
|
+
return "New notification" // fallback
|
|
128
|
+
}
|
|
129
|
+
// Use your encryption library (CryptoKit, etc.) to decrypt
|
|
130
|
+
// The decryption key should be stored in the shared Keychain
|
|
131
|
+
// accessible to both the main app and the extension via App Groups.
|
|
132
|
+
do {
|
|
133
|
+
let decrypted = try EncryptionService.shared.decrypt(data)
|
|
134
|
+
return String(data: decrypted, encoding: .utf8) ?? "New notification"
|
|
135
|
+
} catch {
|
|
136
|
+
return "New notification"
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### App Groups for Data Sharing
|
|
143
|
+
|
|
144
|
+
The service extension runs in a separate process from the main app. Use App Groups to share data (Keychain items, UserDefaults, files) between them.
|
|
145
|
+
|
|
146
|
+
1. Enable "App Groups" capability on both the main app target and the extension target.
|
|
147
|
+
2. Use the same group identifier (e.g., `group.com.example.myapp`).
|
|
148
|
+
|
|
149
|
+
```swift
|
|
150
|
+
// Shared UserDefaults
|
|
151
|
+
let sharedDefaults = UserDefaults(suiteName: "group.com.example.myapp")
|
|
152
|
+
|
|
153
|
+
// Shared file container
|
|
154
|
+
let sharedContainer = FileManager.default.containerURL(
|
|
155
|
+
forSecurityApplicationGroupIdentifier: "group.com.example.myapp"
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
// Shared Keychain: set kSecAttrAccessGroup to your App Group identifier
|
|
159
|
+
// when storing/retrieving Keychain items.
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Extension memory limit:** Notification Service Extensions have a strict memory limit (approximately 24 MB). Do not load large frameworks or perform memory-intensive operations. If the extension exceeds memory limits, the system terminates it and shows the original notification.
|
|
163
|
+
|
|
164
|
+
## Notification Attachments
|
|
165
|
+
|
|
166
|
+
`UNNotificationAttachment` supports images, GIFs, video, and audio. The system displays a thumbnail in the collapsed notification and the full media in the expanded view.
|
|
167
|
+
|
|
168
|
+
### Supported Formats and Limits
|
|
169
|
+
|
|
170
|
+
| Type | Formats | Max Size |
|
|
171
|
+
|------|---------|----------|
|
|
172
|
+
| Image | JPEG, PNG, GIF | 10 MB |
|
|
173
|
+
| Audio | AIFF, WAV, MP3, M4A | 5 MB |
|
|
174
|
+
| Video | MPEG, MPEG-2, MPEG-4, AVI | 50 MB |
|
|
175
|
+
|
|
176
|
+
### Creating Attachments
|
|
177
|
+
|
|
178
|
+
```swift
|
|
179
|
+
// From a local file URL
|
|
180
|
+
let attachment = try UNNotificationAttachment(
|
|
181
|
+
identifier: "photo",
|
|
182
|
+
url: localFileURL,
|
|
183
|
+
options: nil
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
// With options for thumbnailing
|
|
187
|
+
let attachment = try UNNotificationAttachment(
|
|
188
|
+
identifier: "photo",
|
|
189
|
+
url: localFileURL,
|
|
190
|
+
options: [
|
|
191
|
+
UNNotificationAttachmentOptionsThumbnailClippingRectKey:
|
|
192
|
+
CGRect(x: 0, y: 0, width: 1, height: 0.5).dictionaryRepresentation,
|
|
193
|
+
UNNotificationAttachmentOptionsThumbnailTimeKey: 0 // for video: thumbnail at 0 seconds
|
|
194
|
+
]
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
// Attach to content
|
|
198
|
+
content.attachments = [attachment]
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Important:** The file URL must point to a file that the notification system can access. For service extensions, write to the extension's temporary directory. For local notifications, write to a location accessible by the notification system (not the app's sandbox). The system moves the file after attachment creation, so the original URL becomes invalid.
|
|
202
|
+
|
|
203
|
+
### Multiple Attachments
|
|
204
|
+
|
|
205
|
+
You can attach multiple items, but only the first attachment is shown as the thumbnail in the collapsed notification. The expanded view can show all attachments.
|
|
206
|
+
|
|
207
|
+
```swift
|
|
208
|
+
content.attachments = [imageAttachment, audioAttachment]
|
|
209
|
+
// imageAttachment appears as the thumbnail
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### GIF Animations
|
|
213
|
+
|
|
214
|
+
GIFs animate automatically in the expanded notification view. No special handling needed -- attach the GIF file as any other image.
|
|
215
|
+
|
|
216
|
+
```swift
|
|
217
|
+
let gifURL = tempDir.appendingPathComponent("animation.gif")
|
|
218
|
+
try gifData.write(to: gifURL)
|
|
219
|
+
let attachment = try UNNotificationAttachment(
|
|
220
|
+
identifier: "animation",
|
|
221
|
+
url: gifURL,
|
|
222
|
+
options: nil
|
|
223
|
+
)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Notification Content Extension
|
|
227
|
+
|
|
228
|
+
A Notification Content Extension provides a custom view controller that displays when the user long-presses (or expands) a notification. Use it for richer UI than attachments alone can provide.
|
|
229
|
+
|
|
230
|
+
### Creating the Extension
|
|
231
|
+
|
|
232
|
+
In Xcode: File > New > Target > Notification Content Extension. This creates a new target with a storyboard and a `NotificationViewController`.
|
|
233
|
+
|
|
234
|
+
### Configuration (Info.plist)
|
|
235
|
+
|
|
236
|
+
The extension's `Info.plist` must declare which notification categories it handles:
|
|
237
|
+
|
|
238
|
+
```xml
|
|
239
|
+
<key>NSExtension</key>
|
|
240
|
+
<dict>
|
|
241
|
+
<key>NSExtensionAttributes</key>
|
|
242
|
+
<dict>
|
|
243
|
+
<key>UNNotificationExtensionCategory</key>
|
|
244
|
+
<!-- Single category -->
|
|
245
|
+
<string>MESSAGE_CATEGORY</string>
|
|
246
|
+
<!-- Or multiple categories -->
|
|
247
|
+
<array>
|
|
248
|
+
<string>MESSAGE_CATEGORY</string>
|
|
249
|
+
<string>PHOTO_CATEGORY</string>
|
|
250
|
+
</array>
|
|
251
|
+
<!-- Optional: size ratio (height / width). Default 1.0 -->
|
|
252
|
+
<key>UNNotificationExtensionInitialContentSizeRatio</key>
|
|
253
|
+
<real>0.5</real>
|
|
254
|
+
<!-- Optional: hide the default notification body below the custom UI -->
|
|
255
|
+
<key>UNNotificationExtensionDefaultContentHidden</key>
|
|
256
|
+
<true/>
|
|
257
|
+
<!-- Optional: allow user interaction in the custom UI -->
|
|
258
|
+
<key>UNNotificationExtensionUserInteractionEnabled</key>
|
|
259
|
+
<true/>
|
|
260
|
+
</dict>
|
|
261
|
+
<key>NSExtensionMainStoryboard</key>
|
|
262
|
+
<string>MainInterface</string>
|
|
263
|
+
<key>NSExtensionPointIdentifier</key>
|
|
264
|
+
<string>com.apple.usernotifications.content-extension</string>
|
|
265
|
+
</dict>
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### View Controller Implementation
|
|
269
|
+
|
|
270
|
+
```swift
|
|
271
|
+
import UIKit
|
|
272
|
+
import UserNotifications
|
|
273
|
+
import UserNotificationsUI
|
|
274
|
+
|
|
275
|
+
class NotificationViewController: UIViewController, UNNotificationContentExtension {
|
|
276
|
+
@IBOutlet weak var titleLabel: UILabel!
|
|
277
|
+
@IBOutlet weak var bodyLabel: UILabel!
|
|
278
|
+
@IBOutlet weak var imageView: UIImageView!
|
|
279
|
+
|
|
280
|
+
func didReceive(_ notification: UNNotification) {
|
|
281
|
+
let content = notification.request.content
|
|
282
|
+
titleLabel.text = content.title
|
|
283
|
+
bodyLabel.text = content.body
|
|
284
|
+
|
|
285
|
+
// Display the first attachment
|
|
286
|
+
if let attachment = content.attachments.first,
|
|
287
|
+
attachment.url.startAccessingSecurityScopedResource() {
|
|
288
|
+
defer { attachment.url.stopAccessingSecurityScopedResource() }
|
|
289
|
+
if let data = try? Data(contentsOf: attachment.url) {
|
|
290
|
+
imageView.image = UIImage(data: data)
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/// Called when the user taps a notification action while the content
|
|
296
|
+
/// extension is visible.
|
|
297
|
+
func didReceive(
|
|
298
|
+
_ response: UNNotificationResponse,
|
|
299
|
+
completionHandler completion: @escaping (UNNotificationContentExtensionResponseOption) -> Void
|
|
300
|
+
) {
|
|
301
|
+
switch response.actionIdentifier {
|
|
302
|
+
case "LIKE_ACTION":
|
|
303
|
+
// Update UI to show "liked" state
|
|
304
|
+
animateLikeConfirmation()
|
|
305
|
+
// Dismiss after a short delay
|
|
306
|
+
Task {
|
|
307
|
+
try? await Task.sleep(for: .seconds(0.5))
|
|
308
|
+
completion(.dismiss)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
case "REPLY_ACTION":
|
|
312
|
+
if let textResponse = response as? UNTextInputNotificationResponse {
|
|
313
|
+
// Handle reply inline without opening the app
|
|
314
|
+
showReplySentConfirmation(text: textResponse.userText)
|
|
315
|
+
Task {
|
|
316
|
+
try? await Task.sleep(for: .seconds(1.0))
|
|
317
|
+
completion(.dismiss)
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
default:
|
|
322
|
+
// Forward to the app's notification delegate
|
|
323
|
+
completion(.dismissAndForwardAction)
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Response Options
|
|
330
|
+
|
|
331
|
+
| Option | Behavior |
|
|
332
|
+
|--------|----------|
|
|
333
|
+
| `.doNotDismiss` | Keep the content extension visible. Use for multi-step interactions. |
|
|
334
|
+
| `.dismiss` | Dismiss the notification. The action is handled entirely in the extension. |
|
|
335
|
+
| `.dismissAndForwardAction` | Dismiss and forward the action to `UNUserNotificationCenterDelegate.didReceive`. Use when the app needs to handle the action. |
|
|
336
|
+
|
|
337
|
+
### Media Playback in Notifications
|
|
338
|
+
|
|
339
|
+
The content extension can play audio or video. Implement `mediaPlayPauseButtonType` and `mediaPlayPauseButtonFrame` for a system-provided play/pause button.
|
|
340
|
+
|
|
341
|
+
```swift
|
|
342
|
+
class MediaNotificationViewController: UIViewController, UNNotificationContentExtension {
|
|
343
|
+
var player: AVPlayer?
|
|
344
|
+
|
|
345
|
+
override var mediaPlayPauseButtonType: UNNotificationContentExtensionMediaPlayPauseButtonType {
|
|
346
|
+
return .overlay // .none, .default, .overlay
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
override var mediaPlayPauseButtonFrame: CGRect {
|
|
350
|
+
return CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
func mediaPlay() {
|
|
354
|
+
player?.play()
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
func mediaPause() {
|
|
358
|
+
player?.pause()
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
func didReceive(_ notification: UNNotification) {
|
|
362
|
+
guard let attachment = notification.request.content.attachments.first,
|
|
363
|
+
attachment.url.startAccessingSecurityScopedResource() else { return }
|
|
364
|
+
|
|
365
|
+
let playerItem = AVPlayerItem(url: attachment.url)
|
|
366
|
+
player = AVPlayer(playerItem: playerItem)
|
|
367
|
+
|
|
368
|
+
let playerLayer = AVPlayerLayer(player: player)
|
|
369
|
+
playerLayer.frame = view.bounds
|
|
370
|
+
view.layer.addSublayer(playerLayer)
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Interactive Custom UI
|
|
376
|
+
|
|
377
|
+
With `UNNotificationExtensionUserInteractionEnabled` set to `true`, the content extension supports gesture recognizers, buttons, and other interactive elements.
|
|
378
|
+
|
|
379
|
+
```swift
|
|
380
|
+
class InteractiveNotificationViewController: UIViewController, UNNotificationContentExtension {
|
|
381
|
+
private var ratingStars: [UIButton] = []
|
|
382
|
+
private var selectedRating = 0
|
|
383
|
+
|
|
384
|
+
override func viewDidLoad() {
|
|
385
|
+
super.viewDidLoad()
|
|
386
|
+
setupRatingUI()
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
func didReceive(_ notification: UNNotification) {
|
|
390
|
+
// Configure with notification content
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
private func setupRatingUI() {
|
|
394
|
+
let stack = UIStackView()
|
|
395
|
+
stack.axis = .horizontal
|
|
396
|
+
stack.spacing = 8
|
|
397
|
+
stack.translatesAutoresizingMaskIntoConstraints = false
|
|
398
|
+
view.addSubview(stack)
|
|
399
|
+
|
|
400
|
+
for i in 1...5 {
|
|
401
|
+
let button = UIButton(type: .system)
|
|
402
|
+
button.setImage(UIImage(systemName: "star"), for: .normal)
|
|
403
|
+
button.tag = i
|
|
404
|
+
button.addTarget(self, action: #selector(starTapped(_:)), for: .touchUpInside)
|
|
405
|
+
stack.addArrangedSubview(button)
|
|
406
|
+
ratingStars.append(button)
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
NSLayoutConstraint.activate([
|
|
410
|
+
stack.centerXAnchor.constraint(equalTo: view.centerXAnchor),
|
|
411
|
+
stack.centerYAnchor.constraint(equalTo: view.centerYAnchor),
|
|
412
|
+
])
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
@objc private func starTapped(_ sender: UIButton) {
|
|
416
|
+
selectedRating = sender.tag
|
|
417
|
+
for (index, button) in ratingStars.enumerated() {
|
|
418
|
+
let imageName = index < selectedRating ? "star.fill" : "star"
|
|
419
|
+
button.setImage(UIImage(systemName: imageName), for: .normal)
|
|
420
|
+
}
|
|
421
|
+
// Send rating to server
|
|
422
|
+
Task {
|
|
423
|
+
await submitRating(selectedRating)
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
## Communication Notifications
|
|
430
|
+
|
|
431
|
+
Communication notifications display the sender's avatar and name prominently. They use SiriKit intents (`INSendMessageIntent`) to provide sender information.
|
|
432
|
+
|
|
433
|
+
### Setup
|
|
434
|
+
|
|
435
|
+
1. Add the `Intents` framework to the Notification Service Extension target.
|
|
436
|
+
2. Configure `INSendMessageIntent` in the service extension before delivery.
|
|
437
|
+
|
|
438
|
+
```swift
|
|
439
|
+
import Intents
|
|
440
|
+
import UserNotifications
|
|
441
|
+
|
|
442
|
+
extension NotificationService {
|
|
443
|
+
func configureCommunicationNotification(
|
|
444
|
+
content: UNMutableNotificationContent,
|
|
445
|
+
senderName: String,
|
|
446
|
+
senderImageURL: String?,
|
|
447
|
+
conversationId: String
|
|
448
|
+
) async {
|
|
449
|
+
// Create the sender identity
|
|
450
|
+
let handle = INPersonHandle(value: conversationId, type: .unknown)
|
|
451
|
+
var avatar: INImage? = nil
|
|
452
|
+
|
|
453
|
+
// Download sender avatar
|
|
454
|
+
if let urlString = senderImageURL,
|
|
455
|
+
let url = URL(string: urlString) {
|
|
456
|
+
if let (data, _) = try? await URLSession.shared.data(from: url) {
|
|
457
|
+
avatar = INImage(imageData: data)
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
let sender = INPerson(
|
|
462
|
+
personHandle: handle,
|
|
463
|
+
nameComponents: try? PersonNameComponents(senderName),
|
|
464
|
+
displayName: senderName,
|
|
465
|
+
image: avatar,
|
|
466
|
+
contactIdentifier: nil,
|
|
467
|
+
customIdentifier: conversationId
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
// Create the messaging intent
|
|
471
|
+
let intent = INSendMessageIntent(
|
|
472
|
+
recipients: nil,
|
|
473
|
+
outgoingMessageType: .outgoingMessageText,
|
|
474
|
+
content: content.body,
|
|
475
|
+
speakableGroupName: nil,
|
|
476
|
+
conversationIdentifier: conversationId,
|
|
477
|
+
serviceName: nil,
|
|
478
|
+
sender: sender,
|
|
479
|
+
attachments: nil
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
// Donate the interaction so Siri learns about this contact
|
|
483
|
+
let interaction = INInteraction(intent: intent, response: nil)
|
|
484
|
+
interaction.direction = .incoming
|
|
485
|
+
try? await interaction.donate()
|
|
486
|
+
|
|
487
|
+
// Update the notification content with the intent
|
|
488
|
+
do {
|
|
489
|
+
let updatedContent = try content.updating(from: intent)
|
|
490
|
+
// Copy updated properties back to mutable content
|
|
491
|
+
// The updating(from:) method returns a new UNNotificationContent,
|
|
492
|
+
// so we need to use the content handler with the updated version.
|
|
493
|
+
// In practice, call the content handler with updatedContent directly.
|
|
494
|
+
} catch {
|
|
495
|
+
print("Failed to update content with intent: \(error)")
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
### Handling in the Service Extension
|
|
502
|
+
|
|
503
|
+
The complete flow integrates communication notifications into the standard service extension:
|
|
504
|
+
|
|
505
|
+
```swift
|
|
506
|
+
override func didReceive(
|
|
507
|
+
_ request: UNNotificationRequest,
|
|
508
|
+
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
|
|
509
|
+
) {
|
|
510
|
+
guard let content = request.content.mutableCopy() as? UNMutableNotificationContent else {
|
|
511
|
+
contentHandler(request.content)
|
|
512
|
+
return
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
Task {
|
|
516
|
+
// Download and attach image
|
|
517
|
+
if let imageUrl = content.userInfo["imageUrl"] as? String {
|
|
518
|
+
await attachImage(from: imageUrl, to: content)
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Configure as communication notification if sender info is present
|
|
522
|
+
if let senderName = content.userInfo["senderName"] as? String,
|
|
523
|
+
let conversationId = content.userInfo["conversationId"] as? String {
|
|
524
|
+
let senderImage = content.userInfo["senderImageUrl"] as? String
|
|
525
|
+
|
|
526
|
+
let handle = INPersonHandle(value: conversationId, type: .unknown)
|
|
527
|
+
var avatar: INImage? = nil
|
|
528
|
+
if let urlString = senderImage,
|
|
529
|
+
let url = URL(string: urlString),
|
|
530
|
+
let (data, _) = try? await URLSession.shared.data(from: url) {
|
|
531
|
+
avatar = INImage(imageData: data)
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
let sender = INPerson(
|
|
535
|
+
personHandle: handle,
|
|
536
|
+
nameComponents: try? PersonNameComponents(senderName),
|
|
537
|
+
displayName: senderName,
|
|
538
|
+
image: avatar,
|
|
539
|
+
contactIdentifier: nil,
|
|
540
|
+
customIdentifier: conversationId
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
let intent = INSendMessageIntent(
|
|
544
|
+
recipients: nil,
|
|
545
|
+
outgoingMessageType: .outgoingMessageText,
|
|
546
|
+
content: content.body,
|
|
547
|
+
speakableGroupName: nil,
|
|
548
|
+
conversationIdentifier: conversationId,
|
|
549
|
+
serviceName: nil,
|
|
550
|
+
sender: sender,
|
|
551
|
+
attachments: nil
|
|
552
|
+
)
|
|
553
|
+
|
|
554
|
+
let interaction = INInteraction(intent: intent, response: nil)
|
|
555
|
+
interaction.direction = .incoming
|
|
556
|
+
try? await interaction.donate()
|
|
557
|
+
|
|
558
|
+
if let updatedContent = try? content.updating(from: intent) {
|
|
559
|
+
contentHandler(updatedContent)
|
|
560
|
+
return
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
contentHandler(content)
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
## Extension Gotchas
|
|
570
|
+
|
|
571
|
+
**Service extension not running:**
|
|
572
|
+
- Verify `mutable-content: 1` is set in the APNs payload.
|
|
573
|
+
- The notification must have an alert (title or body). Silent pushes do not trigger the service extension.
|
|
574
|
+
- Check that the extension's bundle identifier is `<main-app-bundle-id>.NotificationServiceExtension`.
|
|
575
|
+
- Confirm the extension is included in the same App Group as the main app if sharing data.
|
|
576
|
+
|
|
577
|
+
**Content extension not showing:**
|
|
578
|
+
- Verify the `UNNotificationExtensionCategory` in Info.plist matches the `categoryIdentifier` in the notification.
|
|
579
|
+
- Check that the storyboard or programmatic UI is properly configured.
|
|
580
|
+
- Ensure `UNNotificationExtensionInitialContentSizeRatio` is set to a reasonable value.
|
|
581
|
+
|
|
582
|
+
**Memory pressure:**
|
|
583
|
+
- Service extensions have approximately 24 MB of memory.
|
|
584
|
+
- Content extensions have more memory but should still be conservative.
|
|
585
|
+
- Avoid loading large frameworks (no SwiftUI, no heavy networking libraries).
|
|
586
|
+
- Use `URLSession` directly for network requests in extensions.
|
|
587
|
+
|
|
588
|
+
**Network access in extensions:**
|
|
589
|
+
- Extensions can make network requests. Use `URLSession` with the `.default` configuration.
|
|
590
|
+
- Keep requests fast -- the service extension has approximately 30 seconds total.
|
|
591
|
+
- Handle network failures gracefully; fall back to showing what the original payload contains.
|
|
592
|
+
|
|
593
|
+
**Debugging extensions:**
|
|
594
|
+
- In Xcode, select the extension scheme and attach to the extension process.
|
|
595
|
+
- Use `Debug > Attach to Process by PID or Name` with the extension's process name.
|
|
596
|
+
- Use `os_log` or `print` statements and view them in Console.app filtered by the extension's bundle identifier.
|
|
597
|
+
- Set breakpoints in the extension target and trigger a notification to hit them.
|
|
598
|
+
|
|
599
|
+
## Complete Service Extension Example
|
|
600
|
+
|
|
601
|
+
A production-ready service extension that handles image download, body decryption, and communication notifications:
|
|
602
|
+
|
|
603
|
+
```swift
|
|
604
|
+
import UserNotifications
|
|
605
|
+
import Intents
|
|
606
|
+
|
|
607
|
+
class NotificationService: UNNotificationServiceExtension {
|
|
608
|
+
private var contentHandler: ((UNNotificationContent) -> Void)?
|
|
609
|
+
private var bestAttemptContent: UNMutableNotificationContent?
|
|
610
|
+
|
|
611
|
+
override func didReceive(
|
|
612
|
+
_ request: UNNotificationRequest,
|
|
613
|
+
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
|
|
614
|
+
) {
|
|
615
|
+
self.contentHandler = contentHandler
|
|
616
|
+
bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent
|
|
617
|
+
|
|
618
|
+
guard let content = bestAttemptContent else {
|
|
619
|
+
contentHandler(request.content)
|
|
620
|
+
return
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
Task {
|
|
624
|
+
// Step 1: Decrypt body if needed
|
|
625
|
+
if let encrypted = content.userInfo["encryptedBody"] as? String,
|
|
626
|
+
let decrypted = decryptBody(encrypted) {
|
|
627
|
+
content.body = decrypted
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// Step 2: Download and attach image
|
|
631
|
+
if let imageUrl = content.userInfo["imageUrl"] as? String {
|
|
632
|
+
await attachMedia(from: imageUrl, to: content)
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// Step 3: Configure communication notification
|
|
636
|
+
if let senderName = content.userInfo["senderName"] as? String,
|
|
637
|
+
let convId = content.userInfo["conversationId"] as? String {
|
|
638
|
+
if let updated = await configureAsCommunication(
|
|
639
|
+
content: content,
|
|
640
|
+
senderName: senderName,
|
|
641
|
+
senderImageURL: content.userInfo["senderImage"] as? String,
|
|
642
|
+
conversationId: convId
|
|
643
|
+
) {
|
|
644
|
+
contentHandler(updated)
|
|
645
|
+
return
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
contentHandler(content)
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
override func serviceExtensionTimeWillExpire() {
|
|
654
|
+
if let handler = contentHandler, let content = bestAttemptContent {
|
|
655
|
+
handler(content)
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// MARK: - Private
|
|
660
|
+
|
|
661
|
+
private func decryptBody(_ base64: String) -> String? {
|
|
662
|
+
guard let data = Data(base64Encoded: base64) else { return nil }
|
|
663
|
+
// Use shared Keychain via App Group to retrieve encryption key
|
|
664
|
+
let defaults = UserDefaults(suiteName: "group.com.example.myapp")
|
|
665
|
+
guard let keyData = defaults?.data(forKey: "encryptionKey") else { return nil }
|
|
666
|
+
// Decrypt using CryptoKit or similar
|
|
667
|
+
return try? Decryptor.decrypt(data, key: keyData)
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
private func attachMedia(
|
|
671
|
+
from urlString: String,
|
|
672
|
+
to content: UNMutableNotificationContent
|
|
673
|
+
) async {
|
|
674
|
+
guard let url = URL(string: urlString) else { return }
|
|
675
|
+
do {
|
|
676
|
+
let (data, response) = try await URLSession.shared.data(from: url)
|
|
677
|
+
let ext = fileExtension(for: response.mimeType)
|
|
678
|
+
let fileURL = FileManager.default.temporaryDirectory
|
|
679
|
+
.appendingPathComponent(UUID().uuidString + ext)
|
|
680
|
+
try data.write(to: fileURL)
|
|
681
|
+
let attachment = try UNNotificationAttachment(
|
|
682
|
+
identifier: UUID().uuidString,
|
|
683
|
+
url: fileURL,
|
|
684
|
+
options: nil
|
|
685
|
+
)
|
|
686
|
+
content.attachments = [attachment]
|
|
687
|
+
} catch {
|
|
688
|
+
// Image download failed -- notification displays without media
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
private func configureAsCommunication(
|
|
693
|
+
content: UNMutableNotificationContent,
|
|
694
|
+
senderName: String,
|
|
695
|
+
senderImageURL: String?,
|
|
696
|
+
conversationId: String
|
|
697
|
+
) async -> UNNotificationContent? {
|
|
698
|
+
let handle = INPersonHandle(value: conversationId, type: .unknown)
|
|
699
|
+
var avatar: INImage? = nil
|
|
700
|
+
|
|
701
|
+
if let urlStr = senderImageURL,
|
|
702
|
+
let url = URL(string: urlStr),
|
|
703
|
+
let (data, _) = try? await URLSession.shared.data(from: url) {
|
|
704
|
+
avatar = INImage(imageData: data)
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
let sender = INPerson(
|
|
708
|
+
personHandle: handle,
|
|
709
|
+
nameComponents: try? PersonNameComponents(senderName),
|
|
710
|
+
displayName: senderName,
|
|
711
|
+
image: avatar,
|
|
712
|
+
contactIdentifier: nil,
|
|
713
|
+
customIdentifier: conversationId
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
let intent = INSendMessageIntent(
|
|
717
|
+
recipients: nil,
|
|
718
|
+
outgoingMessageType: .outgoingMessageText,
|
|
719
|
+
content: content.body,
|
|
720
|
+
speakableGroupName: nil,
|
|
721
|
+
conversationIdentifier: conversationId,
|
|
722
|
+
serviceName: nil,
|
|
723
|
+
sender: sender,
|
|
724
|
+
attachments: nil
|
|
725
|
+
)
|
|
726
|
+
|
|
727
|
+
let interaction = INInteraction(intent: intent, response: nil)
|
|
728
|
+
interaction.direction = .incoming
|
|
729
|
+
try? await interaction.donate()
|
|
730
|
+
|
|
731
|
+
return try? content.updating(from: intent)
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
private func fileExtension(for mimeType: String?) -> String {
|
|
735
|
+
switch mimeType {
|
|
736
|
+
case "image/jpeg": return ".jpg"
|
|
737
|
+
case "image/png": return ".png"
|
|
738
|
+
case "image/gif": return ".gif"
|
|
739
|
+
case "video/mp4": return ".mp4"
|
|
740
|
+
case "audio/mpeg": return ".mp3"
|
|
741
|
+
default: return ".jpg"
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
```
|