cfsa-antigravity 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/cli.mjs +324 -0
- package/package.json +34 -0
- package/template/.agent/instructions/commands.md +48 -0
- package/template/.agent/instructions/patterns.md +61 -0
- package/template/.agent/instructions/structure.md +29 -0
- package/template/.agent/instructions/tech-stack.md +43 -0
- package/template/.agent/instructions/workflow.md +41 -0
- package/template/.agent/kit-sync.md +15 -0
- package/template/.agent/rules/boundary-not-placeholder.md +146 -0
- package/template/.agent/rules/completion-checklist.md +48 -0
- package/template/.agent/rules/decision-classification.md +103 -0
- package/template/.agent/rules/extensibility.md +47 -0
- package/template/.agent/rules/question-vs-command.md +81 -0
- package/template/.agent/rules/security-first.md +43 -0
- package/template/.agent/rules/specificity-standards.md +54 -0
- package/template/.agent/rules/tdd-contract-first.md +57 -0
- package/template/.agent/rules/vertical-slices.md +42 -0
- package/template/.agent/skill-library/MANIFEST.md +480 -0
- package/template/.agent/skill-library/README.md +38 -0
- package/template/.agent/skill-library/meta/brand-guidelines/SKILL.md +73 -0
- package/template/.agent/skill-library/meta/claude-code/README.md +9 -0
- package/template/.agent/skill-library/meta/claude-code/agent-development/SKILL.md +415 -0
- package/template/.agent/skill-library/meta/claude-code/hook-development/SKILL.md +712 -0
- package/template/.agent/skill-library/meta/claude-code/plugin-structure/SKILL.md +476 -0
- package/template/.agent/skill-library/meta/git-advanced/SKILL.md +972 -0
- package/template/.agent/skill-library/meta/mcp-builder/SKILL.md +236 -0
- package/template/.agent/skill-library/meta/product-marketing-context/SKILL.md +241 -0
- package/template/.agent/skill-library/meta/regex-patterns/SKILL.md +751 -0
- package/template/.agent/skill-library/meta/tmux-processes/SKILL.md +210 -0
- package/template/.agent/skill-library/meta/using-tmux-for-interactive-commands/SKILL.md +178 -0
- package/template/.agent/skill-library/stack/3d/threejs-pro/SKILL.md +300 -0
- package/template/.agent/skill-library/stack/ai/ai-sdk/SKILL.md +77 -0
- package/template/.agent/skill-library/stack/ai/langchain/SKILL.md +530 -0
- package/template/.agent/skill-library/stack/ai/ollama/SKILL.md +321 -0
- package/template/.agent/skill-library/stack/ai/openai-sdk/SKILL.md +549 -0
- package/template/.agent/skill-library/stack/analytics/google-analytics/SKILL.md +153 -0
- package/template/.agent/skill-library/stack/api/graphql/SKILL.md +1061 -0
- package/template/.agent/skill-library/stack/api/trpc/SKILL.md +576 -0
- package/template/.agent/skill-library/stack/auth/authjs/SKILL.md +569 -0
- package/template/.agent/skill-library/stack/auth/clerk/SKILL.md +590 -0
- package/template/.agent/skill-library/stack/auth/firebase-auth/SKILL.md +734 -0
- package/template/.agent/skill-library/stack/cms/payload-cms/SKILL.md +573 -0
- package/template/.agent/skill-library/stack/cms/shopify/SKILL.md +1193 -0
- package/template/.agent/skill-library/stack/cms/wordpress/SKILL.md +1104 -0
- package/template/.agent/skill-library/stack/css/sass-scss/SKILL.md +1121 -0
- package/template/.agent/skill-library/stack/css/tailwind-css-patterns/SKILL.md +863 -0
- package/template/.agent/skill-library/stack/css/tailwind-design-system/SKILL.md +490 -0
- package/template/.agent/skill-library/stack/css/vanilla-css/SKILL.md +1078 -0
- package/template/.agent/skill-library/stack/databases/clickhouse/SKILL.md +311 -0
- package/template/.agent/skill-library/stack/databases/influxdb/SKILL.md +280 -0
- package/template/.agent/skill-library/stack/databases/lancedb/SKILL.md +415 -0
- package/template/.agent/skill-library/stack/databases/mongodb/SKILL.md +1169 -0
- package/template/.agent/skill-library/stack/databases/neo4j/SKILL.md +839 -0
- package/template/.agent/skill-library/stack/databases/pgvector/SKILL.md +241 -0
- package/template/.agent/skill-library/stack/databases/pinecone/SKILL.md +212 -0
- package/template/.agent/skill-library/stack/databases/postgresql/SKILL.md +658 -0
- package/template/.agent/skill-library/stack/databases/qdrant/SKILL.md +312 -0
- package/template/.agent/skill-library/stack/databases/redis/SKILL.md +1079 -0
- package/template/.agent/skill-library/stack/databases/spacetimedb/SKILL.md +532 -0
- package/template/.agent/skill-library/stack/databases/sqlite/SKILL.md +1132 -0
- package/template/.agent/skill-library/stack/databases/supabase/SKILL.md +640 -0
- package/template/.agent/skill-library/stack/databases/surrealdb-expert/SKILL.md +945 -0
- package/template/.agent/skill-library/stack/databases/timescaledb/SKILL.md +745 -0
- package/template/.agent/skill-library/stack/databases/weaviate/SKILL.md +218 -0
- package/template/.agent/skill-library/stack/devops/github-actions/SKILL.md +554 -0
- package/template/.agent/skill-library/stack/devops/kubernetes/SKILL.md +950 -0
- package/template/.agent/skill-library/stack/devops/nginx/SKILL.md +841 -0
- package/template/.agent/skill-library/stack/devops/terraform/SKILL.md +860 -0
- package/template/.agent/skill-library/stack/email/resend/SKILL.md +391 -0
- package/template/.agent/skill-library/stack/engines/godot/SKILL.md +488 -0
- package/template/.agent/skill-library/stack/extensions/chrome-extension/SKILL.md +375 -0
- package/template/.agent/skill-library/stack/extensions/vscode-extension/SKILL.md +453 -0
- package/template/.agent/skill-library/stack/frameworks/astro-framework/SKILL.md +162 -0
- package/template/.agent/skill-library/stack/frameworks/electron/SKILL.md +1286 -0
- package/template/.agent/skill-library/stack/frameworks/fastapi/SKILL.md +650 -0
- package/template/.agent/skill-library/stack/frameworks/hono/SKILL.md +90 -0
- package/template/.agent/skill-library/stack/frameworks/nestjs/SKILL.md +878 -0
- package/template/.agent/skill-library/stack/frameworks/nextjs/SKILL.md +635 -0
- package/template/.agent/skill-library/stack/frameworks/nuxt/SKILL.md +564 -0
- package/template/.agent/skill-library/stack/frameworks/sveltekit/SKILL.md +614 -0
- package/template/.agent/skill-library/stack/frameworks/tauri/SKILL.md +920 -0
- package/template/.agent/skill-library/stack/gamedev/godot/SKILL.md +1032 -0
- package/template/.agent/skill-library/stack/gamedev/unity/SKILL.md +1175 -0
- package/template/.agent/skill-library/stack/hosting/aws/SKILL.md +467 -0
- package/template/.agent/skill-library/stack/hosting/cloudflare/SKILL.md +201 -0
- package/template/.agent/skill-library/stack/hosting/docker-expert/SKILL.md +409 -0
- package/template/.agent/skill-library/stack/hosting/vercel/SKILL.md +484 -0
- package/template/.agent/skill-library/stack/languages/bash-scripting/SKILL.md +773 -0
- package/template/.agent/skill-library/stack/languages/c-cpp/SKILL.md +712 -0
- package/template/.agent/skill-library/stack/languages/gdscript/SKILL.md +789 -0
- package/template/.agent/skill-library/stack/languages/go/SKILL.md +664 -0
- package/template/.agent/skill-library/stack/languages/java/SKILL.md +778 -0
- package/template/.agent/skill-library/stack/languages/kotlin/SKILL.md +665 -0
- package/template/.agent/skill-library/stack/languages/python/SKILL.md +678 -0
- package/template/.agent/skill-library/stack/languages/rust/SKILL.md +673 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/SKILL.md +141 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/advanced-generics.md +90 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/branded-types.md +57 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/builder-pattern.md +71 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/common-pitfalls.md +135 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/conditional-types.md +27 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/decorators.md +98 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/discriminated-unions.md +62 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/mapped-types.md +53 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/performance-best-practices.md +104 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/template-literal-types.md +49 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/testing-types.md +112 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/type-guards.md +70 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/type-inference.md +101 -0
- package/template/.agent/skill-library/stack/languages/typescript-advanced-patterns/references/utility-types.md +98 -0
- package/template/.agent/skill-library/stack/languages/vanilla-javascript/SKILL.md +803 -0
- package/template/.agent/skill-library/stack/messaging/kafka/SKILL.md +235 -0
- package/template/.agent/skill-library/stack/mobile/expo-react-native/SKILL.md +665 -0
- package/template/.agent/skill-library/stack/mobile/flutter/SKILL.md +316 -0
- package/template/.agent/skill-library/stack/mobile/react-native/SKILL.md +337 -0
- package/template/.agent/skill-library/stack/monitoring/posthog/SKILL.md +396 -0
- package/template/.agent/skill-library/stack/monitoring/sentry/SKILL.md +509 -0
- package/template/.agent/skill-library/stack/observability/datadog/SKILL.md +179 -0
- package/template/.agent/skill-library/stack/observability/distributed-tracing/SKILL.md +140 -0
- package/template/.agent/skill-library/stack/observability/logging-best-practices/SKILL.md +168 -0
- package/template/.agent/skill-library/stack/observability/opentelemetry/SKILL.md +164 -0
- package/template/.agent/skill-library/stack/observability/prometheus-grafana/SKILL.md +246 -0
- package/template/.agent/skill-library/stack/observability/python-observability/SKILL.md +158 -0
- package/template/.agent/skill-library/stack/orm/drizzle-orm/SKILL.md +613 -0
- package/template/.agent/skill-library/stack/orm/prisma/SKILL.md +744 -0
- package/template/.agent/skill-library/stack/payments/lemonsqueezy/SKILL.md +393 -0
- package/template/.agent/skill-library/stack/payments/stripe-integration/SKILL.md +457 -0
- package/template/.agent/skill-library/stack/queue/bullmq/SKILL.md +385 -0
- package/template/.agent/skill-library/stack/queue/inngest/SKILL.md +438 -0
- package/template/.agent/skill-library/stack/realtime/socketio/SKILL.md +595 -0
- package/template/.agent/skill-library/stack/search/elasticsearch/SKILL.md +248 -0
- package/template/.agent/skill-library/stack/search/meilisearch/SKILL.md +385 -0
- package/template/.agent/skill-library/stack/security/crypto-patterns/SKILL.md +437 -0
- package/template/.agent/skill-library/stack/security/csp-cors-headers/SKILL.md +588 -0
- package/template/.agent/skill-library/stack/security/dependency-auditing/SKILL.md +560 -0
- package/template/.agent/skill-library/stack/security/input-sanitization/SKILL.md +430 -0
- package/template/.agent/skill-library/stack/security/owasp-web-security/SKILL.md +421 -0
- package/template/.agent/skill-library/stack/state/tanstack-query/SKILL.md +637 -0
- package/template/.agent/skill-library/stack/state/zustand/SKILL.md +483 -0
- package/template/.agent/skill-library/stack/storage/aws-s3/SKILL.md +415 -0
- package/template/.agent/skill-library/stack/testing/playwright/SKILL.md +641 -0
- package/template/.agent/skill-library/stack/testing/storybook/SKILL.md +923 -0
- package/template/.agent/skill-library/stack/testing/testing-library/SKILL.md +872 -0
- package/template/.agent/skill-library/stack/testing/vitest/SKILL.md +714 -0
- package/template/.agent/skill-library/stack/ui/react-best-practices/SKILL.md +877 -0
- package/template/.agent/skill-library/stack/ui/react-composition-patterns/SKILL.md +1107 -0
- package/template/.agent/skill-library/stack/ui/react-flow/SKILL.md +425 -0
- package/template/.agent/skill-library/stack/ui/shadcn-ui/SKILL.md +703 -0
- package/template/.agent/skill-library/surface/api/api-caching/SKILL.md +458 -0
- package/template/.agent/skill-library/surface/api/api-documentation-openapi/SKILL.md +697 -0
- package/template/.agent/skill-library/surface/api/api-error-handling/SKILL.md +478 -0
- package/template/.agent/skill-library/surface/api/api-security-checklist/SKILL.md +147 -0
- package/template/.agent/skill-library/surface/api/api-versioning/SKILL.md +420 -0
- package/template/.agent/skill-library/surface/api/email-best-practices/SKILL.md +59 -0
- package/template/.agent/skill-library/surface/api/rate-limiting-abuse-protection/SKILL.md +147 -0
- package/template/.agent/skill-library/surface/api/rest-api-design/SKILL.md +478 -0
- package/template/.agent/skill-library/surface/api/webhook-design/SKILL.md +752 -0
- package/template/.agent/skill-library/surface/cli/cli-configuration-management/SKILL.md +445 -0
- package/template/.agent/skill-library/surface/cli/cli-error-diagnostics/SKILL.md +515 -0
- package/template/.agent/skill-library/surface/cli/cli-shell-integration/SKILL.md +479 -0
- package/template/.agent/skill-library/surface/cli/cli-ux-design/SKILL.md +477 -0
- package/template/.agent/skill-library/surface/desktop/desktop-app-distribution/SKILL.md +416 -0
- package/template/.agent/skill-library/surface/desktop/desktop-security-sandboxing/SKILL.md +407 -0
- package/template/.agent/skill-library/surface/desktop/desktop-ux-conventions/SKILL.md +361 -0
- package/template/.agent/skill-library/surface/desktop/native-os-integration/SKILL.md +563 -0
- package/template/.agent/skill-library/surface/extension/browser-extension-patterns/SKILL.md +482 -0
- package/template/.agent/skill-library/surface/extension/plugin-architecture-design/SKILL.md +632 -0
- package/template/.agent/skill-library/surface/extension/vscode-extension-development/SKILL.md +728 -0
- package/template/.agent/skill-library/surface/mobile/app-store-submission/SKILL.md +304 -0
- package/template/.agent/skill-library/surface/mobile/mobile-offline-sync/SKILL.md +443 -0
- package/template/.agent/skill-library/surface/mobile/mobile-responsive-patterns/SKILL.md +432 -0
- package/template/.agent/skill-library/surface/mobile/push-notifications/SKILL.md +495 -0
- package/template/.agent/skill-library/surface/web/accessibility-compliance/SKILL.md +827 -0
- package/template/.agent/skill-library/surface/web/ai-seo/SKILL.md +398 -0
- package/template/.agent/skill-library/surface/web/ai-seo/references/content-patterns.md +285 -0
- package/template/.agent/skill-library/surface/web/ai-seo/references/platform-ranking-factors.md +152 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/SKILL.md +309 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/references/event-library.md +260 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/references/ga4-implementation.md +300 -0
- package/template/.agent/skill-library/surface/web/analytics-tracking/references/gtm-implementation.md +390 -0
- package/template/.agent/skill-library/surface/web/authentication-ui-flows/SKILL.md +530 -0
- package/template/.agent/skill-library/surface/web/dark-mode-theming/SKILL.md +516 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/SKILL.md +105 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/charts.csv +26 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/colors.csv +97 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/landing.csv +31 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/styles.csv +59 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/typography.csv +58 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/data/ux-guidelines.csv +100 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/scripts/core.py +258 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/scripts/design_system.py +1067 -0
- package/template/.agent/skill-library/surface/web/design-reference-data/scripts/search.py +106 -0
- package/template/.agent/skill-library/surface/web/form-handling-validation/SKILL.md +675 -0
- package/template/.agent/skill-library/surface/web/frontend-design/SKILL.md +1393 -0
- package/template/.agent/skill-library/surface/web/frontend-design/templates/cppn-hero.tsx +299 -0
- package/template/.agent/skill-library/surface/web/frontend-design/templates/wave-hero.tsx +875 -0
- package/template/.agent/skill-library/surface/web/frontend-verification/SKILL.md +111 -0
- package/template/.agent/skill-library/surface/web/frontend-verification/scripts/ux_audit.py +739 -0
- package/template/.agent/skill-library/surface/web/i18n-localization/SKILL.md +154 -0
- package/template/.agent/skill-library/surface/web/offline-first-pwa/SKILL.md +657 -0
- package/template/.agent/skill-library/surface/web/page-cro/SKILL.md +182 -0
- package/template/.agent/skill-library/surface/web/page-cro/references/experiments.md +248 -0
- package/template/.agent/skill-library/surface/web/programmatic-seo/SKILL.md +238 -0
- package/template/.agent/skill-library/surface/web/programmatic-seo/references/playbooks.md +308 -0
- package/template/.agent/skill-library/surface/web/schema-markup/SKILL.md +179 -0
- package/template/.agent/skill-library/surface/web/schema-markup/references/schema-examples.md +398 -0
- package/template/.agent/skill-library/surface/web/seo-audit/SKILL.md +394 -0
- package/template/.agent/skill-library/surface/web/seo-audit/references/ai-writing-detection.md +200 -0
- package/template/.agent/skill-library/surface/web/web-performance-optimization/SKILL.md +646 -0
- package/template/.agent/skill-library/surface/web/web-scraping/SKILL.md +58 -0
- package/template/.agent/skills/accessibility/SKILL.md +522 -0
- package/template/.agent/skills/accessibility/references/WCAG.md +162 -0
- package/template/.agent/skills/adversarial-review/SKILL.md +90 -0
- package/template/.agent/skills/antigravity-workflows/SKILL.md +81 -0
- package/template/.agent/skills/antigravity-workflows/resources/implementation-playbook.md +36 -0
- package/template/.agent/skills/api-design-principles/SKILL.md +37 -0
- package/template/.agent/skills/api-design-principles/assets/api-design-checklist.md +155 -0
- package/template/.agent/skills/api-design-principles/assets/rest-api-template.py +182 -0
- package/template/.agent/skills/api-design-principles/references/graphql-schema-design.md +583 -0
- package/template/.agent/skills/api-design-principles/references/rest-best-practices.md +408 -0
- package/template/.agent/skills/api-design-principles/resources/implementation-playbook.md +513 -0
- package/template/.agent/skills/api-versioning/SKILL.md +420 -0
- package/template/.agent/skills/architecture-mapping/SKILL.md +219 -0
- package/template/.agent/skills/bootstrap-agents/SKILL.md +259 -0
- package/template/.agent/skills/brainstorming/SKILL.md +236 -0
- package/template/.agent/skills/brand-guidelines/SKILL.md +44 -0
- package/template/.agent/skills/clean-code/SKILL.md +94 -0
- package/template/.agent/skills/code-review-pro/SKILL.md +152 -0
- package/template/.agent/skills/concise-planning/SKILL.md +68 -0
- package/template/.agent/skills/cross-layer-consistency/SKILL.md +117 -0
- package/template/.agent/skills/database-schema-design/SKILL.md +429 -0
- package/template/.agent/skills/deployment-procedures/SKILL.md +241 -0
- package/template/.agent/skills/design-anti-cliche/SKILL.md +159 -0
- package/template/.agent/skills/design-direction/SKILL.md +45 -0
- package/template/.agent/skills/error-handling-patterns/SKILL.md +721 -0
- package/template/.agent/skills/find-skills/SKILL.md +145 -0
- package/template/.agent/skills/git-advanced/SKILL.md +972 -0
- package/template/.agent/skills/git-workflow/SKILL.md +420 -0
- package/template/.agent/skills/idea-extraction/SKILL.md +271 -0
- package/template/.agent/skills/logging-best-practices/SKILL.md +851 -0
- package/template/.agent/skills/migration-management/SKILL.md +384 -0
- package/template/.agent/skills/minimalist-surgical-development/SKILL.md +69 -0
- package/template/.agent/skills/parallel-agents/SKILL.md +165 -0
- package/template/.agent/skills/parallel-debugging/SKILL.md +135 -0
- package/template/.agent/skills/parallel-feature-development/SKILL.md +166 -0
- package/template/.agent/skills/performance-budgeting/SKILL.md +144 -0
- package/template/.agent/skills/pipeline-rubrics/SKILL.md +51 -0
- package/template/.agent/skills/pipeline-rubrics/references/architecture-rubric.md +19 -0
- package/template/.agent/skills/pipeline-rubrics/references/be-rubric.md +21 -0
- package/template/.agent/skills/pipeline-rubrics/references/fe-rubric.md +20 -0
- package/template/.agent/skills/pipeline-rubrics/references/ia-rubric.md +19 -0
- package/template/.agent/skills/pipeline-rubrics/references/scoring.md +28 -0
- package/template/.agent/skills/pipeline-rubrics/references/vision-rubric.md +11 -0
- package/template/.agent/skills/prd-templates/SKILL.md +88 -0
- package/template/.agent/skills/prd-templates/references/architecture-design-template.md +88 -0
- package/template/.agent/skills/prd-templates/references/be-spec-template.md +101 -0
- package/template/.agent/skills/prd-templates/references/data-placement-template.md +74 -0
- package/template/.agent/skills/prd-templates/references/decomposition-templates.md +211 -0
- package/template/.agent/skills/prd-templates/references/design-system-decisions.md +198 -0
- package/template/.agent/skills/prd-templates/references/engineering-standards-template.md +124 -0
- package/template/.agent/skills/prd-templates/references/fe-classification-procedures.md +47 -0
- package/template/.agent/skills/prd-templates/references/fe-spec-template.md +84 -0
- package/template/.agent/skills/prd-templates/references/infrastructure-report-template.md +71 -0
- package/template/.agent/skills/prd-templates/references/operational-templates.md +116 -0
- package/template/.agent/skills/prd-templates/references/placeholder-guard-template.md +21 -0
- package/template/.agent/skills/prd-templates/references/surface-model.md +61 -0
- package/template/.agent/skills/prd-templates/references/vision-template.md +66 -0
- package/template/.agent/skills/prompt-engineer/README.md +659 -0
- package/template/.agent/skills/prompt-engineer/SKILL.md +249 -0
- package/template/.agent/skills/regex-patterns/SKILL.md +751 -0
- package/template/.agent/skills/resolve-ambiguity/SKILL.md +278 -0
- package/template/.agent/skills/rest-api-design/SKILL.md +478 -0
- package/template/.agent/skills/security-scanning-security-hardening/SKILL.md +231 -0
- package/template/.agent/skills/session-continuity/SKILL.md +730 -0
- package/template/.agent/skills/session-continuity/protocols/01-session-resumption.md +38 -0
- package/template/.agent/skills/session-continuity/protocols/02-progress-generation.md +85 -0
- package/template/.agent/skills/session-continuity/protocols/03-progress-update.md +70 -0
- package/template/.agent/skills/session-continuity/protocols/04-pattern-extraction.md +60 -0
- package/template/.agent/skills/session-continuity/protocols/05-session-close.md +37 -0
- package/template/.agent/skills/session-continuity/protocols/06-decision-analysis.md +84 -0
- package/template/.agent/skills/session-continuity/protocols/07-spec-pipeline-generation.md +48 -0
- package/template/.agent/skills/session-continuity/protocols/08-spec-pipeline-update.md +43 -0
- package/template/.agent/skills/session-continuity/protocols/09-parallel-claim.md +122 -0
- package/template/.agent/skills/session-continuity/protocols/10-placeholder-verification-gate.md +104 -0
- package/template/.agent/skills/session-continuity/protocols/ambiguity-gates.md +48 -0
- package/template/.agent/skills/skill-creator/LICENSE.txt +202 -0
- package/template/.agent/skills/skill-creator/README.md +270 -0
- package/template/.agent/skills/skill-creator/SKILL.md +590 -0
- package/template/.agent/skills/skill-creator/references/output-patterns.md +82 -0
- package/template/.agent/skills/skill-creator/references/workflows.md +28 -0
- package/template/.agent/skills/skill-creator/scripts/init_skill.py +303 -0
- package/template/.agent/skills/skill-creator/scripts/package_skill.py +110 -0
- package/template/.agent/skills/skill-creator/scripts/quick_validate.py +95 -0
- package/template/.agent/skills/spec-writing/SKILL.md +110 -0
- package/template/.agent/skills/systematic-debugging/CREATION-LOG.md +119 -0
- package/template/.agent/skills/systematic-debugging/SKILL.md +297 -0
- package/template/.agent/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/template/.agent/skills/systematic-debugging/condition-based-waiting.md +115 -0
- package/template/.agent/skills/systematic-debugging/defense-in-depth.md +122 -0
- package/template/.agent/skills/systematic-debugging/find-polluter.sh +63 -0
- package/template/.agent/skills/systematic-debugging/root-cause-tracing.md +169 -0
- package/template/.agent/skills/systematic-debugging/test-academic.md +14 -0
- package/template/.agent/skills/systematic-debugging/test-pressure-1.md +58 -0
- package/template/.agent/skills/systematic-debugging/test-pressure-2.md +68 -0
- package/template/.agent/skills/systematic-debugging/test-pressure-3.md +69 -0
- package/template/.agent/skills/tdd-workflow/SKILL.md +409 -0
- package/template/.agent/skills/tech-stack-catalog/SKILL.md +49 -0
- package/template/.agent/skills/tech-stack-catalog/references/constraint-questions.md +21 -0
- package/template/.agent/skills/tech-stack-catalog/references/dev-tooling-decisions.md +37 -0
- package/template/.agent/skills/tech-stack-catalog/references/surface-decision-tables.md +69 -0
- package/template/.agent/skills/technical-writer/SKILL.md +242 -0
- package/template/.agent/skills/testing-strategist/SKILL.md +932 -0
- package/template/.agent/skills/verification-before-completion/SKILL.md +145 -0
- package/template/.agent/skills/workflow-automation/SKILL.md +73 -0
- package/template/.agent/workflows/audit-ambiguity-execute.md +165 -0
- package/template/.agent/workflows/audit-ambiguity-rubrics.md +83 -0
- package/template/.agent/workflows/audit-ambiguity.md +64 -0
- package/template/.agent/workflows/bootstrap-agents-fill.md +201 -0
- package/template/.agent/workflows/bootstrap-agents-provision.md +197 -0
- package/template/.agent/workflows/bootstrap-agents.md +66 -0
- package/template/.agent/workflows/create-prd-architecture.md +119 -0
- package/template/.agent/workflows/create-prd-compile.md +138 -0
- package/template/.agent/workflows/create-prd-design-system.md +135 -0
- package/template/.agent/workflows/create-prd-security.md +113 -0
- package/template/.agent/workflows/create-prd-stack.md +91 -0
- package/template/.agent/workflows/create-prd.md +168 -0
- package/template/.agent/workflows/decompose-architecture-structure.md +82 -0
- package/template/.agent/workflows/decompose-architecture-validate.md +119 -0
- package/template/.agent/workflows/decompose-architecture.md +111 -0
- package/template/.agent/workflows/evolve-contract.md +98 -0
- package/template/.agent/workflows/evolve-feature-cascade.md +140 -0
- package/template/.agent/workflows/evolve-feature-classify.md +116 -0
- package/template/.agent/workflows/evolve-feature.md +56 -0
- package/template/.agent/workflows/ideate-discover.md +144 -0
- package/template/.agent/workflows/ideate-extract.md +129 -0
- package/template/.agent/workflows/ideate-validate.md +117 -0
- package/template/.agent/workflows/ideate.md +113 -0
- package/template/.agent/workflows/implement-slice-setup.md +113 -0
- package/template/.agent/workflows/implement-slice-tdd.md +198 -0
- package/template/.agent/workflows/implement-slice.md +50 -0
- package/template/.agent/workflows/plan-phase.md +202 -0
- package/template/.agent/workflows/propagate-decision-apply.md +135 -0
- package/template/.agent/workflows/propagate-decision-scan.md +147 -0
- package/template/.agent/workflows/propagate-decision.md +56 -0
- package/template/.agent/workflows/remediate-pipeline-assess.md +138 -0
- package/template/.agent/workflows/remediate-pipeline-execute.md +135 -0
- package/template/.agent/workflows/remediate-pipeline.md +55 -0
- package/template/.agent/workflows/resolve-ambiguity.md +82 -0
- package/template/.agent/workflows/sync-kit.md +209 -0
- package/template/.agent/workflows/update-architecture-map.md +74 -0
- package/template/.agent/workflows/validate-phase.md +219 -0
- package/template/.agent/workflows/verify-infrastructure.md +207 -0
- package/template/.agent/workflows/write-architecture-spec-deepen.md +139 -0
- package/template/.agent/workflows/write-architecture-spec-design.md +202 -0
- package/template/.agent/workflows/write-architecture-spec.md +63 -0
- package/template/.agent/workflows/write-be-spec-classify.md +165 -0
- package/template/.agent/workflows/write-be-spec-write.md +98 -0
- package/template/.agent/workflows/write-be-spec.md +76 -0
- package/template/.agent/workflows/write-fe-spec-classify.md +170 -0
- package/template/.agent/workflows/write-fe-spec-write.md +94 -0
- package/template/.agent/workflows/write-fe-spec.md +71 -0
- package/template/AGENTS.md +176 -0
- package/template/GEMINI.md +177 -0
- package/template/docs/README.md +187 -0
- package/template/docs/audits/.gitkeep +0 -0
- package/template/docs/audits/README.md +10 -0
- package/template/docs/plans/.gitkeep +0 -0
- package/template/docs/plans/README.md +21 -0
- package/template/docs/plans/be/.gitkeep +0 -0
- package/template/docs/plans/be/README.md +11 -0
- package/template/docs/plans/fe/.gitkeep +0 -0
- package/template/docs/plans/fe/README.md +11 -0
- package/template/docs/plans/ia/.gitkeep +0 -0
- package/template/docs/plans/ia/README.md +17 -0
- package/template/docs/plans/ia/deep-dives/.gitkeep +0 -0
- package/template/docs/plans/ia/deep-dives/README.md +5 -0
- package/template/docs/plans/phases/.gitkeep +0 -0
- package/template/docs/plans/phases/README.md +11 -0
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: push-notifications
|
|
3
|
+
description: Push notification implementation covering permission flows, deep linking, notification channels, APNs/FCM integration, and in-app notification management. Use when adding push notifications to mobile or web applications.
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Push Notification Handling
|
|
8
|
+
|
|
9
|
+
Implement push notifications that respect user attention, drive meaningful engagement, and work reliably across platforms.
|
|
10
|
+
|
|
11
|
+
## Permission Request Timing
|
|
12
|
+
|
|
13
|
+
Never request notification permission on first launch. Users deny blanket permission requests at a rate above 50%. Instead, use a pre-permission prompt that explains the value.
|
|
14
|
+
|
|
15
|
+
| Timing | Approach | Conversion Rate |
|
|
16
|
+
|--------|----------|----------------|
|
|
17
|
+
| First launch | System prompt immediately | ~40% allow |
|
|
18
|
+
| After value moment | Pre-prompt explaining benefit, then system prompt | ~70% allow |
|
|
19
|
+
| Contextual | "Notify you when your order ships?" at checkout | ~80% allow |
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
// Pre-permission prompt flow
|
|
23
|
+
function NotificationOptIn({ onAccept, onDecline }: NotificationOptInProps) {
|
|
24
|
+
return (
|
|
25
|
+
<View style={styles.container}>
|
|
26
|
+
<BellIcon size={48} color="#6366f1" />
|
|
27
|
+
<Text style={styles.title}>Stay in the loop</Text>
|
|
28
|
+
<Text style={styles.body}>
|
|
29
|
+
Get notified when your builds finish, teammates comment, or
|
|
30
|
+
deployments go live. You can customize this anytime in Settings.
|
|
31
|
+
</Text>
|
|
32
|
+
<Button title="Enable Notifications" onPress={onAccept} />
|
|
33
|
+
<Pressable onPress={onDecline}>
|
|
34
|
+
<Text style={styles.skip}>Not now</Text>
|
|
35
|
+
</Pressable>
|
|
36
|
+
</View>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Only request system permission after user taps "Enable"
|
|
41
|
+
async function requestPermissionAfterOptIn(): Promise<boolean> {
|
|
42
|
+
const { status } = await Notifications.requestPermissionsAsync();
|
|
43
|
+
return status === 'granted';
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Anti-pattern**: Requesting permission in `useEffect` on app launch with no context.
|
|
48
|
+
**Anti-pattern**: Not providing a way to enable notifications later if the user declined initially.
|
|
49
|
+
|
|
50
|
+
## Notification Channels (Android) and Categories (iOS)
|
|
51
|
+
|
|
52
|
+
Android 8+ requires notification channels. iOS supports categories for actionable notifications.
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
// Android notification channels
|
|
56
|
+
import notifee, { AndroidImportance } from '@notifee/react-native';
|
|
57
|
+
|
|
58
|
+
async function createNotificationChannels() {
|
|
59
|
+
await notifee.createChannelGroup({
|
|
60
|
+
id: 'activity',
|
|
61
|
+
name: 'Activity',
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
await notifee.createChannel({
|
|
65
|
+
id: 'messages',
|
|
66
|
+
name: 'Messages',
|
|
67
|
+
description: 'Direct messages and mentions',
|
|
68
|
+
importance: AndroidImportance.HIGH,
|
|
69
|
+
groupId: 'activity',
|
|
70
|
+
sound: 'message_sound',
|
|
71
|
+
vibration: true,
|
|
72
|
+
lights: true,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
await notifee.createChannel({
|
|
76
|
+
id: 'updates',
|
|
77
|
+
name: 'Updates',
|
|
78
|
+
description: 'Build status and deployment updates',
|
|
79
|
+
importance: AndroidImportance.DEFAULT,
|
|
80
|
+
groupId: 'activity',
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
await notifee.createChannel({
|
|
84
|
+
id: 'marketing',
|
|
85
|
+
name: 'Promotions',
|
|
86
|
+
description: 'Feature announcements and tips',
|
|
87
|
+
importance: AndroidImportance.LOW,
|
|
88
|
+
groupId: 'activity',
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
// iOS notification categories with actions
|
|
95
|
+
import notifee, { IOSAuthorizationStatus } from '@notifee/react-native';
|
|
96
|
+
|
|
97
|
+
async function setupIOSCategories() {
|
|
98
|
+
await notifee.setNotificationCategories([
|
|
99
|
+
{
|
|
100
|
+
id: 'message',
|
|
101
|
+
actions: [
|
|
102
|
+
{
|
|
103
|
+
id: 'reply',
|
|
104
|
+
title: 'Reply',
|
|
105
|
+
input: true, // Inline text reply
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: 'mark-read',
|
|
109
|
+
title: 'Mark as Read',
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: 'build-status',
|
|
115
|
+
actions: [
|
|
116
|
+
{
|
|
117
|
+
id: 'view-logs',
|
|
118
|
+
title: 'View Logs',
|
|
119
|
+
foreground: true, // Opens the app
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
id: 'retry',
|
|
123
|
+
title: 'Retry Build',
|
|
124
|
+
destructive: false,
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
]);
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Rule**: Create channels at app startup, not when sending the first notification.
|
|
133
|
+
**Rule**: Users can disable individual channels. Check channel status before assuming delivery.
|
|
134
|
+
|
|
135
|
+
## Deep Linking from Notifications
|
|
136
|
+
|
|
137
|
+
Every notification that navigates the user must include structured deep link data.
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
// Notification payload with deep link
|
|
141
|
+
interface NotificationData {
|
|
142
|
+
type: 'message' | 'build' | 'comment' | 'invite';
|
|
143
|
+
deepLink: string; // e.g., "myapp://builds/abc123/logs"
|
|
144
|
+
entityId: string;
|
|
145
|
+
entityType: string;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Handle notification tap -- works for both foreground and background/killed states
|
|
149
|
+
import messaging from '@react-native-firebase/messaging';
|
|
150
|
+
import { Linking } from 'react-native';
|
|
151
|
+
import { navigationRef } from './navigation';
|
|
152
|
+
|
|
153
|
+
// App opened from killed state via notification
|
|
154
|
+
messaging().getInitialNotification().then((remoteMessage) => {
|
|
155
|
+
if (remoteMessage?.data?.deepLink) {
|
|
156
|
+
// Store for processing after navigation is ready
|
|
157
|
+
pendingDeepLink = remoteMessage.data.deepLink as string;
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// App in background, notification tapped
|
|
162
|
+
messaging().onNotificationOpenedApp((remoteMessage) => {
|
|
163
|
+
if (remoteMessage.data?.deepLink) {
|
|
164
|
+
handleDeepLink(remoteMessage.data.deepLink as string);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
function handleDeepLink(url: string) {
|
|
169
|
+
const parsed = parseDeepLink(url);
|
|
170
|
+
|
|
171
|
+
switch (parsed.screen) {
|
|
172
|
+
case 'builds':
|
|
173
|
+
navigationRef.navigate('BuildDetail', { buildId: parsed.params.id });
|
|
174
|
+
break;
|
|
175
|
+
case 'messages':
|
|
176
|
+
navigationRef.navigate('Conversation', { threadId: parsed.params.id });
|
|
177
|
+
break;
|
|
178
|
+
default:
|
|
179
|
+
navigationRef.navigate('Home');
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Anti-pattern**: Notifications that open the app but land on the home screen instead of the relevant content.
|
|
185
|
+
**Anti-pattern**: Deep links that crash if the user is not authenticated. Always check auth state first and redirect to login if needed.
|
|
186
|
+
|
|
187
|
+
## Badge Management
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
// Badge count represents unread items across the app
|
|
191
|
+
import notifee from '@notifee/react-native';
|
|
192
|
+
|
|
193
|
+
async function updateBadgeCount(unreadCount: number) {
|
|
194
|
+
// iOS: sets the app icon badge
|
|
195
|
+
// Android: sets the badge on supported launchers
|
|
196
|
+
await notifee.setBadgeCount(unreadCount);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Clear badge when user opens the relevant screen
|
|
200
|
+
async function clearBadge() {
|
|
201
|
+
await notifee.setBadgeCount(0);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Server-side: include badge count in push payload
|
|
205
|
+
const pushPayload = {
|
|
206
|
+
notification: {
|
|
207
|
+
title: 'New message from Alex',
|
|
208
|
+
body: 'Hey, can you review the PR?',
|
|
209
|
+
},
|
|
210
|
+
apns: {
|
|
211
|
+
payload: {
|
|
212
|
+
aps: {
|
|
213
|
+
badge: totalUnreadCount, // Server tracks this
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**Rule**: Badge count must be managed server-side for accuracy. The server knows the true unread count.
|
|
221
|
+
**Rule**: Clear the badge when the user views the relevant content, not when the app opens.
|
|
222
|
+
|
|
223
|
+
## Silent / Background Notifications
|
|
224
|
+
|
|
225
|
+
Silent notifications wake the app in the background to perform work without showing anything to the user.
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
// FCM data-only message (silent)
|
|
229
|
+
const silentPush = {
|
|
230
|
+
data: {
|
|
231
|
+
type: 'sync',
|
|
232
|
+
syncTable: 'tasks',
|
|
233
|
+
lastModified: '2026-02-16T12:00:00Z',
|
|
234
|
+
},
|
|
235
|
+
// No 'notification' key = silent on Android
|
|
236
|
+
apns: {
|
|
237
|
+
payload: {
|
|
238
|
+
aps: {
|
|
239
|
+
'content-available': 1, // Required for iOS background delivery
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
topic: 'com.example.myapp',
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// Handle in background handler
|
|
247
|
+
messaging().setBackgroundMessageHandler(async (remoteMessage) => {
|
|
248
|
+
if (remoteMessage.data?.type === 'sync') {
|
|
249
|
+
await syncTable(remoteMessage.data.syncTable);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Rule**: iOS limits background execution to ~30 seconds. Keep background work minimal.
|
|
255
|
+
**Rule**: iOS will throttle silent notifications if the app does not demonstrate user engagement.
|
|
256
|
+
|
|
257
|
+
## Notification Grouping
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
// Group notifications by thread/conversation
|
|
261
|
+
async function displayGroupedNotification(message: ChatMessage) {
|
|
262
|
+
await notifee.displayNotification({
|
|
263
|
+
title: message.senderName,
|
|
264
|
+
body: message.text,
|
|
265
|
+
android: {
|
|
266
|
+
channelId: 'messages',
|
|
267
|
+
groupId: `thread-${message.threadId}`,
|
|
268
|
+
groupSummary: false,
|
|
269
|
+
},
|
|
270
|
+
ios: {
|
|
271
|
+
threadId: `thread-${message.threadId}`,
|
|
272
|
+
summaryArgument: message.senderName,
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Android: create a summary notification for the group
|
|
277
|
+
await notifee.displayNotification({
|
|
278
|
+
title: `${unreadCount} new messages`,
|
|
279
|
+
body: `From ${senderNames.join(', ')}`,
|
|
280
|
+
android: {
|
|
281
|
+
channelId: 'messages',
|
|
282
|
+
groupId: `thread-${message.threadId}`,
|
|
283
|
+
groupSummary: true, // This is the group header
|
|
284
|
+
groupAlertBehavior: AndroidGroupAlertBehavior.CHILDREN,
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Rich Media Notifications
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
// Notification with image
|
|
294
|
+
await notifee.displayNotification({
|
|
295
|
+
title: 'Build #1234 deployed',
|
|
296
|
+
body: 'Production deployment completed successfully',
|
|
297
|
+
android: {
|
|
298
|
+
channelId: 'updates',
|
|
299
|
+
largeIcon: 'https://example.com/avatar.png',
|
|
300
|
+
style: {
|
|
301
|
+
type: AndroidStyle.BIGPICTURE,
|
|
302
|
+
picture: 'https://example.com/deployment-preview.png',
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
ios: {
|
|
306
|
+
attachments: [
|
|
307
|
+
{
|
|
308
|
+
url: 'https://example.com/deployment-preview.png',
|
|
309
|
+
thumbnailHidden: false,
|
|
310
|
+
},
|
|
311
|
+
],
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## APNs vs FCM Differences
|
|
317
|
+
|
|
318
|
+
| Feature | APNs (iOS) | FCM (Android + iOS) |
|
|
319
|
+
|---------|-----------|---------------------|
|
|
320
|
+
| Protocol | HTTP/2 to Apple servers | HTTP v1 to Google servers |
|
|
321
|
+
| Auth | JWT or certificate-based | Service account (OAuth 2.0) |
|
|
322
|
+
| Payload limit | 4 KB | 4 KB |
|
|
323
|
+
| Silent push | `content-available: 1` in `aps` | Data-only message (no `notification` key) |
|
|
324
|
+
| Priority | 1-10 (`apns-priority` header) | `HIGH` or `NORMAL` |
|
|
325
|
+
| Token format | Hex string (device token) | Registration token (FCM token) |
|
|
326
|
+
| Feedback | HTTP response per push | FCM Diagnostics, BigQuery export |
|
|
327
|
+
| Token refresh | App delegate callback | `onTokenRefresh` listener |
|
|
328
|
+
| Grouping | `threadIdentifier` | `notification.tag` or `groupId` |
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
// Server-side: sending via FCM Admin SDK (handles both platforms)
|
|
332
|
+
import { getMessaging } from 'firebase-admin/messaging';
|
|
333
|
+
|
|
334
|
+
async function sendPush(token: string, payload: PushPayload) {
|
|
335
|
+
const message = {
|
|
336
|
+
token,
|
|
337
|
+
notification: {
|
|
338
|
+
title: payload.title,
|
|
339
|
+
body: payload.body,
|
|
340
|
+
},
|
|
341
|
+
data: payload.data,
|
|
342
|
+
android: {
|
|
343
|
+
priority: 'high' as const,
|
|
344
|
+
notification: {
|
|
345
|
+
channelId: payload.channelId,
|
|
346
|
+
clickAction: 'OPEN_DEEP_LINK',
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
apns: {
|
|
350
|
+
payload: {
|
|
351
|
+
aps: {
|
|
352
|
+
badge: payload.badgeCount,
|
|
353
|
+
sound: 'default',
|
|
354
|
+
category: payload.category,
|
|
355
|
+
threadId: payload.threadId,
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
try {
|
|
362
|
+
await getMessaging().send(message);
|
|
363
|
+
} catch (error: unknown) {
|
|
364
|
+
if (isTokenExpired(error)) {
|
|
365
|
+
await removeDeviceToken(token);
|
|
366
|
+
}
|
|
367
|
+
throw error;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## In-App Notification Center
|
|
373
|
+
|
|
374
|
+
Not all notifications are push notifications. Provide an in-app notification center for non-urgent updates.
|
|
375
|
+
|
|
376
|
+
```tsx
|
|
377
|
+
interface InAppNotification {
|
|
378
|
+
id: string;
|
|
379
|
+
type: 'info' | 'success' | 'warning' | 'action_required';
|
|
380
|
+
title: string;
|
|
381
|
+
body: string;
|
|
382
|
+
deepLink?: string;
|
|
383
|
+
readAt?: string;
|
|
384
|
+
createdAt: string;
|
|
385
|
+
expiresAt?: string;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function NotificationCenter() {
|
|
389
|
+
const { notifications, unreadCount, markAsRead, markAllAsRead } =
|
|
390
|
+
useNotifications();
|
|
391
|
+
|
|
392
|
+
return (
|
|
393
|
+
<View style={styles.container}>
|
|
394
|
+
<View style={styles.header}>
|
|
395
|
+
<Text style={styles.title}>Notifications</Text>
|
|
396
|
+
{unreadCount > 0 && (
|
|
397
|
+
<Pressable onPress={markAllAsRead}>
|
|
398
|
+
<Text style={styles.markAll}>Mark all as read</Text>
|
|
399
|
+
</Pressable>
|
|
400
|
+
)}
|
|
401
|
+
</View>
|
|
402
|
+
<FlatList
|
|
403
|
+
data={notifications}
|
|
404
|
+
renderItem={({ item }) => (
|
|
405
|
+
<NotificationRow
|
|
406
|
+
notification={item}
|
|
407
|
+
onPress={() => {
|
|
408
|
+
markAsRead(item.id);
|
|
409
|
+
if (item.deepLink) handleDeepLink(item.deepLink);
|
|
410
|
+
}}
|
|
411
|
+
/>
|
|
412
|
+
)}
|
|
413
|
+
ListEmptyComponent={<EmptyState message="No notifications yet" />}
|
|
414
|
+
/>
|
|
415
|
+
</View>
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## Notification Preferences UI
|
|
421
|
+
|
|
422
|
+
```tsx
|
|
423
|
+
interface NotificationPreferences {
|
|
424
|
+
pushEnabled: boolean;
|
|
425
|
+
channels: {
|
|
426
|
+
messages: { push: boolean; inApp: boolean; email: boolean };
|
|
427
|
+
builds: { push: boolean; inApp: boolean; email: boolean };
|
|
428
|
+
mentions: { push: boolean; inApp: boolean; email: boolean };
|
|
429
|
+
marketing: { push: boolean; inApp: boolean; email: boolean };
|
|
430
|
+
};
|
|
431
|
+
quietHours: {
|
|
432
|
+
enabled: boolean;
|
|
433
|
+
start: string; // "22:00"
|
|
434
|
+
end: string; // "08:00"
|
|
435
|
+
timezone: string;
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function NotificationSettings() {
|
|
440
|
+
const { preferences, updatePreference } = useNotificationPreferences();
|
|
441
|
+
|
|
442
|
+
return (
|
|
443
|
+
<ScrollView>
|
|
444
|
+
<Section title="Delivery Methods">
|
|
445
|
+
{Object.entries(preferences.channels).map(([channel, settings]) => (
|
|
446
|
+
<ChannelRow
|
|
447
|
+
key={channel}
|
|
448
|
+
label={CHANNEL_LABELS[channel]}
|
|
449
|
+
push={settings.push}
|
|
450
|
+
inApp={settings.inApp}
|
|
451
|
+
email={settings.email}
|
|
452
|
+
onToggle={(method, value) =>
|
|
453
|
+
updatePreference(`channels.${channel}.${method}`, value)
|
|
454
|
+
}
|
|
455
|
+
/>
|
|
456
|
+
))}
|
|
457
|
+
</Section>
|
|
458
|
+
<Section title="Quiet Hours">
|
|
459
|
+
<Toggle
|
|
460
|
+
label="Do Not Disturb"
|
|
461
|
+
value={preferences.quietHours.enabled}
|
|
462
|
+
onToggle={(v) => updatePreference('quietHours.enabled', v)}
|
|
463
|
+
/>
|
|
464
|
+
{preferences.quietHours.enabled && (
|
|
465
|
+
<TimeRangePicker
|
|
466
|
+
start={preferences.quietHours.start}
|
|
467
|
+
end={preferences.quietHours.end}
|
|
468
|
+
onChange={(start, end) => {
|
|
469
|
+
updatePreference('quietHours.start', start);
|
|
470
|
+
updatePreference('quietHours.end', end);
|
|
471
|
+
}}
|
|
472
|
+
/>
|
|
473
|
+
)}
|
|
474
|
+
</Section>
|
|
475
|
+
</ScrollView>
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
## Output Checklist
|
|
481
|
+
|
|
482
|
+
- [ ] Permission requested after a value moment, not on first launch
|
|
483
|
+
- [ ] Pre-permission prompt explains specific value to the user
|
|
484
|
+
- [ ] Notification channels created at app startup (Android)
|
|
485
|
+
- [ ] Notification categories with actions configured (iOS)
|
|
486
|
+
- [ ] Every notification includes deep link data
|
|
487
|
+
- [ ] Deep links handle unauthenticated state gracefully
|
|
488
|
+
- [ ] Badge count managed server-side
|
|
489
|
+
- [ ] Silent notifications used for background sync
|
|
490
|
+
- [ ] Notifications grouped by thread or category
|
|
491
|
+
- [ ] In-app notification center for non-push updates
|
|
492
|
+
- [ ] Notification preferences UI with per-channel controls
|
|
493
|
+
- [ ] Quiet hours / Do Not Disturb support
|
|
494
|
+
- [ ] Expired device tokens removed from server
|
|
495
|
+
- [ ] Notification tap handling works from killed, background, and foreground states
|