vybekiit 0.7.3 → 0.7.7

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.
Files changed (203) hide show
  1. package/dist/bin.js +16607 -7527
  2. package/dist/global-skills/aws-cdk/SKILL.md +2 -1
  3. package/dist/global-skills/aws-cloudformation/SKILL.md +2 -1
  4. package/dist/global-skills/aws-serverless/SKILL.md +45 -44
  5. package/dist/global-skills/aws-serverless/assets/powertools-handler.py +2 -1
  6. package/dist/global-skills/aws-serverless/references/api-gateway.md +50 -470
  7. package/dist/global-skills/aws-serverless/references/architecture.md +47 -186
  8. package/dist/global-skills/aws-serverless/references/concurrency.md +44 -158
  9. package/dist/global-skills/aws-serverless/references/deployment.md +1 -1
  10. package/dist/global-skills/aws-serverless/references/event-sources.md +72 -391
  11. package/dist/global-skills/aws-serverless/references/lambda.md +69 -428
  12. package/dist/global-skills/aws-serverless/references/orchestration.md +65 -384
  13. package/dist/global-skills/aws-serverless/references/production.md +78 -415
  14. package/dist/global-skills/aws-serverless/references/troubleshooting.md +92 -619
  15. package/dist/global-skills/claimable-postgres/SKILL.md +60 -49
  16. package/dist/global-skills/design-my-data/SKILL.md +2 -0
  17. package/dist/global-skills/eas-app-stores/SKILL.md +8 -0
  18. package/dist/global-skills/eas-app-stores/references/play-store.md +2 -0
  19. package/dist/global-skills/eas-hosting/SKILL.md +8 -0
  20. package/dist/global-skills/eas-observe/SKILL.md +30 -8
  21. package/dist/global-skills/eas-observe/agents/openai.yaml +1 -1
  22. package/dist/global-skills/eas-observe/references/metrics.md +54 -3
  23. package/dist/global-skills/eas-observe/references/queries.md +78 -17
  24. package/dist/global-skills/eas-observe/references/setup.md +164 -5
  25. package/dist/global-skills/eas-observe/references/third-party.md +136 -0
  26. package/dist/global-skills/eas-simulator/SKILL.md +63 -11
  27. package/dist/global-skills/eas-simulator/references/controllers.md +21 -2
  28. package/dist/global-skills/eas-simulator/references/run-your-app.md +16 -11
  29. package/dist/global-skills/eas-simulator/references/troubleshooting.md +8 -5
  30. package/dist/global-skills/eas-update-insights/SKILL.md +8 -0
  31. package/dist/global-skills/eas-workflows/SKILL.md +15 -10
  32. package/dist/global-skills/eas-workflows/scripts/package.json +2 -7
  33. package/dist/global-skills/email-best-practices/.github/workflows/sync-skills.yml +30 -0
  34. package/dist/global-skills/email-best-practices/README.md +63 -0
  35. package/dist/global-skills/email-best-practices/references/accessibility.md +189 -0
  36. package/dist/global-skills/email-best-practices/references/compliance.md +125 -0
  37. package/dist/global-skills/email-best-practices/references/deliverability.md +121 -0
  38. package/dist/global-skills/email-best-practices/references/email-capture.md +129 -0
  39. package/dist/global-skills/email-best-practices/references/email-types.md +173 -0
  40. package/dist/global-skills/email-best-practices/references/list-management.md +157 -0
  41. package/dist/global-skills/email-best-practices/references/marketing-emails.md +115 -0
  42. package/dist/global-skills/email-best-practices/references/sending-reliability.md +155 -0
  43. package/dist/global-skills/email-best-practices/references/transactional-email-catalog.md +418 -0
  44. package/dist/global-skills/email-best-practices/references/transactional-emails.md +92 -0
  45. package/dist/global-skills/email-best-practices/references/webhooks-events.md +167 -0
  46. package/dist/global-skills/email-best-practices/tests/README.md +35 -0
  47. package/dist/global-skills/email-best-practices/tests/scenarios/01-spam-deliverability.md +46 -0
  48. package/dist/global-skills/email-best-practices/tests/scenarios/02-multi-region-compliance.md +48 -0
  49. package/dist/global-skills/email-best-practices/tests/scenarios/03-retry-idempotency.md +36 -0
  50. package/dist/global-skills/email-best-practices/tests/scenarios/04-webhook-bounce-handling.md +52 -0
  51. package/dist/global-skills/email-best-practices/tests/scenarios/05-new-saas-email-plan.md +51 -0
  52. package/dist/global-skills/expo-animation/LICENSE +21 -0
  53. package/dist/global-skills/expo-animation/RECIPES.md +385 -0
  54. package/dist/global-skills/expo-animation/SKILL.md +267 -0
  55. package/dist/global-skills/expo-animation/agents/openai.yaml +4 -0
  56. package/dist/global-skills/expo-app-clip/SKILL.md +8 -0
  57. package/dist/global-skills/expo-brownfield/SKILL.md +8 -0
  58. package/dist/global-skills/expo-brownfield/references/brownfield-isolated.md +53 -4
  59. package/dist/global-skills/expo-data-fetching/SKILL.md +8 -0
  60. package/dist/global-skills/expo-design-system/SKILL.md +356 -0
  61. package/dist/global-skills/expo-design-system/agents/openai.yaml +4 -0
  62. package/dist/global-skills/expo-design-system/references/audit.md +185 -0
  63. package/dist/global-skills/expo-dev-client/SKILL.md +8 -0
  64. package/dist/global-skills/expo-dom/SKILL.md +8 -0
  65. package/dist/global-skills/expo-examples/SKILL.md +8 -0
  66. package/dist/global-skills/expo-migrate-module/SKILL.md +113 -0
  67. package/dist/global-skills/expo-migrate-module/agents/openai.yaml +4 -0
  68. package/dist/global-skills/expo-migrate-module/references/compatibility.md +73 -0
  69. package/dist/global-skills/expo-migrate-module/references/example.md +212 -0
  70. package/dist/global-skills/expo-migrate-module/references/migration-map.md +306 -0
  71. package/dist/global-skills/expo-module/SKILL.md +11 -1
  72. package/dist/global-skills/expo-native-ui/SKILL.md +8 -0
  73. package/dist/global-skills/expo-project-structure/SKILL.md +114 -0
  74. package/dist/global-skills/expo-project-structure/agents/openai.yaml +4 -0
  75. package/dist/global-skills/expo-router/SKILL.md +8 -0
  76. package/dist/global-skills/expo-skill-feedback/SKILL.md +66 -33
  77. package/dist/global-skills/expo-skill-feedback/agents/openai.yaml +2 -2
  78. package/dist/global-skills/expo-tailwind-setup/SKILL.md +8 -0
  79. package/dist/global-skills/expo-ui/SKILL.md +8 -0
  80. package/dist/global-skills/expo-upgrade/SKILL.md +15 -1
  81. package/dist/global-skills/expo-web-to-native/SKILL.md +8 -0
  82. package/dist/global-skills/extension-to-functions-codebase/SKILL.md +129 -0
  83. package/dist/global-skills/extension-to-functions-codebase/references/configuration-migration.md +161 -0
  84. package/dist/global-skills/extension-to-functions-codebase/references/destructuring-shim.md +122 -0
  85. package/dist/global-skills/extension-to-functions-codebase/references/signature-mapping.md +84 -0
  86. package/dist/global-skills/firebase-ai-logic-basics/SKILL.md +61 -19
  87. package/dist/global-skills/firebase-ai-logic-basics/references/flutter_setup.md +6 -3
  88. package/dist/global-skills/firebase-app-hosting-basics/SKILL.md +5 -1
  89. package/dist/global-skills/firebase-auth-basics/SKILL.md +4 -2
  90. package/dist/global-skills/firebase-basics/SKILL.md +5 -5
  91. package/dist/global-skills/firebase-crashlytics/SKILL.md +2 -0
  92. package/dist/global-skills/firebase-data-connect/SKILL.md +2 -0
  93. package/dist/global-skills/firebase-firestore/SKILL.md +3 -6
  94. package/dist/global-skills/firebase-hosting-basics/SKILL.md +4 -1
  95. package/dist/global-skills/firebase-remote-config-basics/SKILL.md +5 -1
  96. package/dist/global-skills/firebase-security-rules-auditor/SKILL.md +4 -1
  97. package/dist/global-skills/harden/SKILL.md +2 -1
  98. package/dist/global-skills/instrument-feature-flags/SKILL.md +1 -0
  99. package/dist/global-skills/instrument-feature-flags/references/COMMANDMENTS.md +5 -0
  100. package/dist/global-skills/instrument-feature-flags/references/adding-feature-flag-code.md +6 -4
  101. package/dist/global-skills/instrument-feature-flags/references/android.md +6 -4
  102. package/dist/global-skills/instrument-feature-flags/references/api.md +6 -4
  103. package/dist/global-skills/instrument-feature-flags/references/best-practices.md +6 -4
  104. package/dist/global-skills/instrument-feature-flags/references/django.md +38 -5
  105. package/dist/global-skills/instrument-feature-flags/references/dotnet.md +10 -4
  106. package/dist/global-skills/instrument-feature-flags/references/elixir.md +6 -4
  107. package/dist/global-skills/instrument-feature-flags/references/flask.md +17 -5
  108. package/dist/global-skills/instrument-feature-flags/references/flutter.md +7 -5
  109. package/dist/global-skills/instrument-feature-flags/references/go.md +6 -4
  110. package/dist/global-skills/instrument-feature-flags/references/ios.md +6 -4
  111. package/dist/global-skills/instrument-feature-flags/references/java.md +6 -4
  112. package/dist/global-skills/instrument-feature-flags/references/laravel.md +6 -4
  113. package/dist/global-skills/instrument-feature-flags/references/next-js.md +74 -8
  114. package/dist/global-skills/instrument-feature-flags/references/nodejs.md +6 -4
  115. package/dist/global-skills/instrument-feature-flags/references/php.md +6 -4
  116. package/dist/global-skills/instrument-feature-flags/references/python.md +6 -4
  117. package/dist/global-skills/instrument-feature-flags/references/react-native.md +6 -4
  118. package/dist/global-skills/instrument-feature-flags/references/react.md +6 -4
  119. package/dist/global-skills/instrument-feature-flags/references/ruby-on-rails.md +6 -4
  120. package/dist/global-skills/instrument-feature-flags/references/ruby.md +6 -4
  121. package/dist/global-skills/instrument-feature-flags/references/rust.md +6 -4
  122. package/dist/global-skills/instrument-feature-flags/references/usage.md +41 -4
  123. package/dist/global-skills/instrument-feature-flags/references/web.md +7 -5
  124. package/dist/global-skills/instrument-product-analytics/SKILL.md +2 -1
  125. package/dist/global-skills/instrument-product-analytics/references/COMMANDMENTS.md +5 -0
  126. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-astro-hybrid.md +12 -14
  127. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-astro-ssr.md +25 -21
  128. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-django.md +75 -55
  129. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-fastapi.md +3 -6
  130. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-flask.md +26 -16
  131. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-nuxt-3-6.md +8 -5
  132. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-nuxt-4.md +4 -4
  133. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-react-react-router-7-framework.md +3 -3
  134. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-ruby-on-rails.md +8 -4
  135. package/dist/global-skills/instrument-product-analytics/references/EXAMPLE-tanstack-start.md +20 -8
  136. package/dist/global-skills/instrument-product-analytics/references/android.md +46 -4
  137. package/dist/global-skills/instrument-product-analytics/references/angular.md +41 -5
  138. package/dist/global-skills/instrument-product-analytics/references/astro.md +41 -5
  139. package/dist/global-skills/instrument-product-analytics/references/configuration.md +7 -4
  140. package/dist/global-skills/instrument-product-analytics/references/django.md +38 -5
  141. package/dist/global-skills/instrument-product-analytics/references/dotnet.md +10 -4
  142. package/dist/global-skills/instrument-product-analytics/references/elixir.md +6 -4
  143. package/dist/global-skills/instrument-product-analytics/references/flask.md +17 -5
  144. package/dist/global-skills/instrument-product-analytics/references/flutter.md +49 -4
  145. package/dist/global-skills/instrument-product-analytics/references/go.md +6 -4
  146. package/dist/global-skills/instrument-product-analytics/references/identify-users.md +38 -7
  147. package/dist/global-skills/instrument-product-analytics/references/ios.md +14 -4
  148. package/dist/global-skills/instrument-product-analytics/references/laravel.md +6 -4
  149. package/dist/global-skills/instrument-product-analytics/references/next-js.md +74 -8
  150. package/dist/global-skills/instrument-product-analytics/references/nuxt-js-3-6.md +39 -7
  151. package/dist/global-skills/instrument-product-analytics/references/nuxt-js.md +40 -4
  152. package/dist/global-skills/instrument-product-analytics/references/php.md +6 -4
  153. package/dist/global-skills/instrument-product-analytics/references/posthog-python.md +21 -17
  154. package/dist/global-skills/instrument-product-analytics/references/python.md +15 -5
  155. package/dist/global-skills/instrument-product-analytics/references/react-native.md +41 -7
  156. package/dist/global-skills/instrument-product-analytics/references/react-router-v6.md +18 -4
  157. package/dist/global-skills/instrument-product-analytics/references/react-router-v7-data-mode.md +18 -4
  158. package/dist/global-skills/instrument-product-analytics/references/react-router-v7-declarative-mode.md +18 -4
  159. package/dist/global-skills/instrument-product-analytics/references/react-router-v7-framework-mode.md +18 -4
  160. package/dist/global-skills/instrument-product-analytics/references/ruby-on-rails.md +6 -4
  161. package/dist/global-skills/instrument-product-analytics/references/ruby.md +6 -4
  162. package/dist/global-skills/instrument-product-analytics/references/svelte.md +42 -6
  163. package/dist/global-skills/instrument-product-analytics/references/tanstack-start.md +31 -7
  164. package/dist/global-skills/instrument-product-analytics/references/usage.md +41 -4
  165. package/dist/global-skills/instrument-product-analytics/references/vue-js.md +42 -6
  166. package/dist/global-skills/manifest.json +6 -1
  167. package/dist/global-skills/neon/SKILL.md +235 -121
  168. package/dist/global-skills/neon-ai-gateway/SKILL.md +95 -16
  169. package/dist/global-skills/neon-functions/SKILL.md +242 -152
  170. package/dist/global-skills/neon-functions/references/ai-sdk.md +9 -3
  171. package/dist/global-skills/neon-functions/references/mastra-studio.md +1 -1
  172. package/dist/global-skills/neon-functions/references/mcp.md +13 -4
  173. package/dist/global-skills/neon-functions/references/sentry.md +165 -52
  174. package/dist/global-skills/neon-functions/references/sse.md +23 -6
  175. package/dist/global-skills/neon-object-storage/SKILL.md +40 -16
  176. package/dist/global-skills/neon-postgres/SKILL.md +63 -262
  177. package/dist/global-skills/neon-postgres-branches/SKILL.md +61 -50
  178. package/dist/global-skills/neon-postgres-egress-optimizer/SKILL.md +22 -3
  179. package/dist/global-skills/resend/SKILL.md +6 -6
  180. package/dist/global-skills/resend/references/automations.md +2 -2
  181. package/dist/global-skills/resend/references/broadcasts.md +11 -3
  182. package/dist/global-skills/resend/references/domains.md +25 -3
  183. package/dist/global-skills/resend/references/installation.md +7 -7
  184. package/dist/global-skills/setup-payments/SKILL.md +4 -5
  185. package/dist/global-skills/stripe-best-practices/SKILL.md +32 -7
  186. package/dist/global-skills/stripe-best-practices/references/billing.md +11 -1
  187. package/dist/global-skills/stripe-best-practices/references/payments.md +23 -2
  188. package/dist/global-skills/stripe-best-practices/references/security.md +21 -11
  189. package/dist/global-skills/stripe-best-practices/references/tax.md +116 -11
  190. package/dist/global-skills/supabase/CHANGELOG.md +25 -0
  191. package/dist/global-skills/supabase/SKILL.md +5 -1
  192. package/dist/global-skills/supabase-postgres-best-practices/CHANGELOG.md +30 -0
  193. package/dist/global-skills/supabase-postgres-best-practices/SKILL.md +1 -1
  194. package/dist/global-skills/use-kit-mcp/SKILL.md +5 -5
  195. package/dist/global-skills/use-railway/SKILL.md +4 -4
  196. package/dist/global-skills/use-railway/references/iac.md +206 -57
  197. package/dist/global-skills/use-railway/scripts/railway-api.sh +7 -2
  198. package/dist/global-skills/xcode-project-setup/SKILL.md +2 -0
  199. package/package.json +12 -9
  200. package/LICENSE.md +0 -47
  201. package/dist/global-skills/eas-workflows/scripts/validate.js +0 -84
  202. package/dist/global-skills/expo-skill-feedback/scripts/skill-feedback.cjs +0 -118
  203. package/dist/global-skills/neon-functions/references/hono-websockets.md +0 -145
@@ -4,15 +4,17 @@ Concrete errors seen while validating this flow, and the fix.
4
4
 
5
5
  | Symptom | Cause | Fix |
6
6
  |---|---|---|
7
- | `Command simulator:start not found` | `eas-cli` too old (commands are hidden but present from ≥ 20.3.0) | Run via `npx eas-cli@latest …`, or upgrade `eas-cli`. |
7
+ | `Command simulator:start not found` | `eas-cli` too old (commands are hidden but present from ≥ 20.3.0) | Run via `npx --yes eas-cli@latest …`, or upgrade `eas-cli`. |
8
+ | `simulator:start` rejects `--name` (e.g. `Nonexistent flag: --name`) | `eas-cli` too old — `--name` was added after `simulator:start` itself | Run via `npx --yes eas-cli@latest …`, or upgrade `eas-cli`. If you can't upgrade, retry once **without** `--name`; the session starts unnamed. |
8
9
  | `An Expo user account is required` / `whoami` shows logged-out | No browser login on a cloud/CI/headless box, or `EXPO_TOKEN` unset/invalid | Set **`EXPO_TOKEN`** (expo.dev → Account → Access Tokens) in the env; verify `npx --yes eas-cli@latest whoami`. (Interactive machines can `eas login`.) |
9
10
  | `simulator:start`/`build`: no linked project / missing `projectId` | A fresh `create-expo-app` isn't linked to EAS | `npx --yes eas-cli@latest init` to create/link it (writes `extra.eas.projectId`). |
10
11
  | `prebuild`/`eas build` prompts for or fails on a missing **iOS bundle identifier** | A fresh app often has no `ios.bundleIdentifier` | Set it in app config (e.g. `dev.<owner>.<slug>`); confirm via `npx expo config --json` (may live in `app.config.js`). |
11
- | `--max-duration-minutes` rejected, or `simulator:start` denied / not-allowlisted / quota error | The flag is **paid-plan only**, or the experimental feature isn't enabled for the account | Drop `--max-duration-minutes` for the default; check the plan / that EAS Simulator is enabled for the account. |
12
+ | `--max-duration-minutes` rejected | The flag is **paid-plan only** | Drop `--max-duration-minutes` to use the default. |
13
+ | `simulator:start` fails with `not enabled for this account` / not-allowlisted | EAS Simulator is limited-access and isn't enabled for this account | Don't retry. Confirm with `simulator:availability`, then hand off gracefully — tell the user and fall back to a local sim / EAS Build (see SKILL.md *Check availability first*). |
12
14
  | `start` keeps "Waiting for … session to be ready" but it never returns | `start`'s readiness poll can miss a session that's actually live | Don't rely on it — poll `npx --yes eas-cli@latest simulator:get --id <id> --json` for `status: IN_PROGRESS` + a populated `remoteConfig`. |
13
15
  | `ERR_NGROK_3200` / endpoint offline; `Remote daemon is unavailable` | The session's tunnel/daemon dropped — left idle and timed out, or the VM was torn down | A drop invalidates the **whole** session (installed app, `@e` refs, Metro). **Don't retry the failed verb** — start a fresh session, reset the dotenv, and re-run install→open→drive from the top, acting immediately. |
14
- | Two sessions running / orphaned session / surprise double billing | A second `start` (e.g. to "retry" a slow boot) creates a second billed session and overwrites the dotenv id, orphaning the first | Never `start` again to retry — poll the existing session instead. Find orphans with `simulator:list --status IN_PROGRESS` and stop each with `simulator:stop --id <id>`. |
15
- | A device verb hangs (no return for a minute+) | Slow daemon; `press`/`screenshot` can block ~90s | Wrap verbs in `timeout 120 …`; on timeout `snapshot -i` to see if the action landed before retrying (taps can double-fire). Don't blind-retry. |
16
+ | Two sessions running / orphaned session / surprise double billing | A second `start` (e.g. to "retry" a slow boot) creates a second billed session and overwrites the dotenv id, orphaning the first | Never `start` again to retry — poll the existing session instead. Find orphans with `simulator:list --status in-progress` and stop each with `simulator:stop --id <id>`. |
17
+ | A device verb hangs (no return for a minute+) | Slow daemon; `press`/`screenshot` can block ~90s | Bound it with agent-device's own `--timeout <ms>` (e.g. `--timeout 120000`) — **not** a shell `timeout` wrapper (macOS has no `timeout` binary, so `timeout 120 …` fails with `command not found` and skips the verb). On timeout `snapshot -i` to see if the action landed before retrying (taps can double-fire). Don't blind-retry. |
16
18
  | `install requires an active session or an explicit device selector` | `install` can't infer the device | Pass `--platform ios` (or `open` something first to establish a session). |
17
19
  | `Unknown command: tap` | The tap verb is `press` | Use `press <ref\|selector>` (e.g. `press @e2` or `press 'label="Open"'`). |
18
20
  | `SESSION_NOT_FOUND: No active session. Run open first.` | A verb (e.g. `screenshot`) ran before any app/session was opened | `open <app\|url>` first (or pass `--platform ios`). |
@@ -23,12 +25,13 @@ Concrete errors seen while validating this flow, and the fix.
23
25
  | (Mode C) Deep-link `open` lands on the dev-client launcher, not the app | Opening the deep link triggers a system "Open in '<app>'?" dialog; and the launcher only auto-discovers Metro on the LAN | `press 'label="Open"'` to dismiss the dialog, then "Enter URL manually" → `fill` the `https://<host>.on.expo.app` manifest URL → "Connect". |
24
26
  | (Mode C) App shows expo-router "Unmatched Route" | The connect URL was parsed as a route path | `press 'label="Go back"'` (or navigate to `/`). |
25
27
  | (Mode C) Dev client shows a `?` placeholder / blank after connect | Bundle not fetched yet | `press 'label="Reload"'` and wait ~40-60s for the first build+transfer over the tunnel. |
26
- | (Mode C) `expo start` fails: "port 8081 already in use" | A Metro is already bound to 8081 (often one from earlier in the session) | Don't relaunch onto it. Reuse if it's yours (`curl -sf localhost:8081/status`), else free it (`PIDS=$(lsof -ti:8081); [ -n "$PIDS" ] && kill $PIDS`) then start one. There is only ever one Metro. |
28
+ | (Mode C) `expo start` fails: "port 8081 already in use" | Something already owns that port (often another Metro) | Don't kill it start on another port (`--port <N>`, e.g. 8082). Each `expo start --tunnel` gets its own tunnel URL, so ports coexist. Only reuse a running Metro if you started it this session. |
27
29
  | (Mode C) `expo start` / `node` killed with **exit 137** | 137 = SIGKILL — almost always the **OOM killer** (memory pressure, common in constrained cloud sandboxes, esp. a native build + Metro at once). **Not** a port clash. | Reduce memory pressure: don't run a native build and Metro concurrently; give the sandbox more memory; retry. |
28
30
  | (Mode C) Edits won't live-reload no matter how often you reconnect | A **release** build is installed — its JS is baked in, so it ignores Metro | Stop reconnecting: **install the dev (Debug) build**, connect it to Metro, reload. Reconnecting a release build to Metro is a no-op. |
29
31
  | `expo start --tunnel` errors for a robot/`EXPO_TOKEN` user | The ngrok robot-user guard | Use tunnel v2: `EXPO_UNSTABLE_TUNNEL_V2=1 expo start --tunnel`. |
30
32
  | Unexpected charges / a session you forgot | `start --non-interactive` does NOT auto-stop | Always `npx --yes eas-cli@latest simulator:stop --id <id>`. List leftovers with `npx --yes eas-cli@latest simulator:list`. |
31
33
  | Screenshot shows **old content** / my recent edits don't appear | Running a **release build (Mode A/B)** whose JS was baked in *before* your edits — typically a reused/stale build | A/B reflect code at build time, not now. **Rebuild** (ensure the build's fingerprint matches current source), or use **Mode C** (dev + Metro) so live edits show via Fast Refresh. The screenshot itself is fresh — it's the build that's stale. (`9:41` in the status bar is the sim default, not staleness.) |
34
+ | (argent) Every `argent run`/`tools` call returns `401 Unauthorized` right after linking | `argent link` without `--yes` no-ops on an already-linked URL ("Already linked. No changes."), keeping a stale token from a previous session | Re-link with `--yes` so the new token is written — see the link command in [controllers.md](./controllers.md). |
32
35
 
33
36
  ## Performance expectations
34
37
 
@@ -228,3 +228,11 @@ Human-readable group details plus 30 days of launches/failures per platform —
228
228
  - **Fresh publishes** may show zeros for a short period while the metrics pipeline catches up.
229
229
  - **Installs are downloads, not launches**: the `installs` / "Launches" field counts users who downloaded the manifest and launch asset. A confirmed run only registers on the user's *next* update check (typically up to 24h later, depending on the app's update policy). So metrics lag the real-world state slightly.
230
230
  - **Crashes are self-reported**: `failedInstalls` / "Crashes" counts updates that errored during install/launch and were reported on the next update check. Crashes that don't trigger an update request (e.g. process kill before recovery) won't appear.
231
+
232
+ ## Submitting Feedback
233
+ If you encounter errors, misleading or outdated information in this skill, report it so Expo can improve:
234
+ ```bash
235
+ npx --yes submit-expo-feedback@latest --category skills --subject "eas-update-insights" "<actionable feedback>"
236
+ ```
237
+ Only submit when you have something specific and actionable to report. Include as much relevant context as possible.
238
+ If an AI agent repeatedly failed or the user had to take over an Expo task, load the expo-skill-feedback skill and follow its eval-candidate flow instead of reusing the command above.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: eas-workflows
3
3
  description: EAS service (paid). Helps understand and write EAS workflow YAML files for Expo projects. Use this skill when the user asks about CI/CD or workflows in an Expo or EAS context, mentions .eas/workflows/, or wants help with EAS build pipelines or deployment automation.
4
- allowed-tools: "Read,Write,Bash(node:*)"
4
+ allowed-tools: "Read,Write,Bash(node:*),Bash(npx *eas-cli@*)"
5
5
  version: 1.0.0
6
6
  license: MIT License
7
7
  ---
@@ -14,7 +14,7 @@ Help developers write and edit EAS CI/CD workflow YAML files.
14
14
 
15
15
  ## Reference Documentation
16
16
 
17
- Fetch these resources before generating or validating workflow files. First resolve this skill's directory, then use the fetch script in its `scripts/` directory. It is implemented using Node.js and caches responses using ETags for efficiency:
17
+ Fetch these resources before generating or editing workflow files, or when answering syntax questions. First resolve this skill's directory, then use the fetch script in its `scripts/` directory. It is implemented using Node.js and caches responses using ETags for efficiency:
18
18
 
19
19
  ```bash
20
20
  # Fetch resources
@@ -23,7 +23,7 @@ node <skill-dir>/scripts/fetch.js <url>
23
23
 
24
24
  1. **JSON Schema** — https://api.expo.dev/v2/workflows/schema
25
25
  - It is NECESSARY to fetch this schema
26
- - Source of truth for validation
26
+ - Source of truth for the workflow YAML structure; EAS CLI remains the authoritative final validator
27
27
  - All job types and their required/optional parameters
28
28
  - Trigger types and configurations
29
29
  - Runner types, VM images, and all enums
@@ -41,7 +41,7 @@ Do not rely on memorized values; these resources evolve as new features are adde
41
41
 
42
42
  ## Workflow File Location
43
43
 
44
- Workflows live in `.eas/workflows/*.yml` (or `.yaml`).
44
+ Workflows live in `.eas/workflows/*.yml` (or `.yaml`). Each file must be 16 KiB or smaller.
45
45
 
46
46
  ## Top-Level Structure
47
47
 
@@ -78,17 +78,22 @@ When generating or editing workflows:
78
78
 
79
79
  ## Validation
80
80
 
81
- After generating or editing a workflow file, validate it against the schema:
81
+ After generating or editing a workflow file, validate it with EAS CLI from the Expo project root:
82
82
 
83
83
  ```sh
84
- # Install dependencies if missing
85
- [ -d "<skill-dir>/scripts/node_modules" ] || npm install --prefix <skill-dir>/scripts
86
-
87
- node <skill-dir>/scripts/validate.js <workflow.yml> [workflow2.yml ...]
84
+ npx -y eas-cli@latest workflow:validate .eas/workflows/<workflow.yml> --non-interactive
88
85
  ```
89
86
 
90
- The validator fetches the latest schema and checks the YAML structure. Fix any reported errors before considering the workflow complete.
87
+ Run the command separately for each changed workflow file. It requires a logged-in EAS CLI session and a linked Expo project. Unlike schema-only validation, it also checks build profile references against the project's `eas.json` and performs EAS server-side validation. Fix every reported error and rerun the command until it prints `Workflow configuration YAML is valid.` Do not replace this command with a local YAML or JSON Schema validator.
91
88
 
92
89
  ## Answering Questions
93
90
 
94
91
  When users ask about available options (job types, triggers, runner types, etc.), fetch the schema and derive the answer from it rather than relying on potentially outdated information.
92
+
93
+ ## Submitting Feedback
94
+ If you encounter errors, misleading or outdated information in this skill, report it so Expo can improve:
95
+ ```bash
96
+ npx --yes submit-expo-feedback@latest --category skills --subject "eas-workflows" "<actionable feedback>"
97
+ ```
98
+ Only submit when you have something specific and actionable to report. Include as much relevant context as possible.
99
+ If an AI agent repeatedly failed or the user had to take over an Expo task, load the expo-skill-feedback skill and follow its eval-candidate flow instead of reusing the command above.
@@ -1,11 +1,6 @@
1
1
  {
2
- "name": "@expo/cicd-workflows-skill",
2
+ "name": "@expo/eas-workflows-skill",
3
3
  "version": "0.0.0",
4
4
  "private": true,
5
- "type": "module",
6
- "dependencies": {
7
- "ajv": "^8.17.1",
8
- "ajv-formats": "^3.0.1",
9
- "js-yaml": "^4.1.0"
10
- }
5
+ "type": "module"
11
6
  }
@@ -0,0 +1,30 @@
1
+ name: Sync Skills
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - 'SKILL.md'
8
+ - 'references/**'
9
+ - 'scripts/**'
10
+ - 'assets/**'
11
+ workflow_dispatch:
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ sync:
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - name: Trigger sync on resend-skills
21
+ env:
22
+ GH_TOKEN: ${{ secrets.SYNC_SKILLS_TO_RESEND_SKILLS }}
23
+ run: |
24
+ gh workflow run sync-from-repo.yml \
25
+ --repo resend/resend-skills \
26
+ --field repo=email-best-practices \
27
+ --field skill-path=. \
28
+ --field skill-name=email-best-practices \
29
+ --field sha=${{ github.sha }} \
30
+ --field reviewer=${{ github.actor }}
@@ -0,0 +1,63 @@
1
+ ```
2
+ ╔══════════════════════════════════════╗
3
+ ║ _____ __ __ _ ___ _ ║
4
+ ║ | ____| \/ | / \ |_ _| | ║
5
+ ║ | _| | |\/| | / _ \ | || | ║
6
+ ║ | |___| | | |/ ___ \ | || |___ ║
7
+ ║ |_____|_| |_/_/ \_\___|_____| ║
8
+ ║ ║
9
+ ║ Best Practices ║
10
+ ╚══════════════════════════════════════╝
11
+ ```
12
+
13
+ # Email Best Practices Skill
14
+
15
+ A comprehensive agent skill for building production-ready email systems. Covers everything from DNS authentication to webhook processing, with a focus on deliverability, compliance, and reliability.
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npx skills add resend/email-best-practices
21
+ ```
22
+
23
+ ## What This Skill Covers
24
+
25
+ **Getting Started**
26
+ - Planning which emails your app needs (password reset, verification, order confirmations)
27
+ - Setting up email authentication (SPF, DKIM, DMARC) so emails reach inboxes
28
+
29
+ **Sending Emails**
30
+ - Transactional email design (subject lines, content structure, mobile-first)
31
+ - Marketing email best practices (consent, segmentation, unsubscribe)
32
+ - Compliance requirements by region (CAN-SPAM, GDPR, CASL)
33
+
34
+ **Production Infrastructure**
35
+ - Idempotency and retry logic to prevent duplicates
36
+ - Webhook processing for delivery events
37
+ - Suppression lists and list hygiene automation
38
+
39
+ ## Structure
40
+
41
+ ```
42
+ email-best-practices/
43
+ ├── SKILL.md # Start here - routes to the right resource
44
+ └── references/
45
+ ├── deliverability.md # SPF/DKIM/DMARC, sender reputation
46
+ ├── transactional-emails.md # Password resets, OTPs, confirmations
47
+ ├── transactional-email-catalog.md # Email combinations by app type
48
+ ├── marketing-emails.md # Newsletters, campaigns, consent
49
+ ├── email-capture.md # Validation, verification, opt-in
50
+ ├── compliance.md # CAN-SPAM, GDPR, CASL
51
+ ├── email-types.md # Transactional vs marketing
52
+ ├── sending-reliability.md # Idempotency, retry logic, errors
53
+ ├── webhooks-events.md # Delivery events, webhook setup
54
+ └── list-management.md # Suppression lists, hygiene
55
+ ```
56
+
57
+ ## Quick Start
58
+
59
+ Open `SKILL.md` - it has a routing table that directs you to the right resource based on what you need to do.
60
+
61
+ ## License
62
+
63
+ MIT
@@ -0,0 +1,189 @@
1
+ # Email Accessibility
2
+
3
+ Emails must be readable by screen readers, dark-mode clients, translation tools, and AI agents — not just sighted readers on a default inbox. The rules below are mechanical. Apply them every time.
4
+
5
+ ## Rules
6
+
7
+ ### Set `lang` and `dir` on `<html>` and on `<body>`'s direct children (Serious)
8
+
9
+ Both attributes are needed in **two places**: on `<html>` *and* on the direct children of `<body>`. Several email clients strip the attributes from `<html>`, which is why duplicating them on the body's children is the single most common accessibility failure in production email.
10
+
11
+ ```html
12
+ <html lang="en" dir="ltr">
13
+ <head>
14
+ <title>Your weekly product updates</title>
15
+ </head>
16
+ <body>
17
+ <div lang="en" dir="ltr">
18
+ <!-- email content -->
19
+ </div>
20
+ </body>
21
+ </html>
22
+ ```
23
+
24
+ - `lang`: a [BCP 47 language tag](https://developer.mozilla.org/en-US/docs/Glossary/BCP_47_language_tag) (`en`, `pt-BR`, `ja`, `ar`)
25
+ - `dir`: `ltr`, `rtl`, or `auto`
26
+
27
+ **Fallbacks when the correct values aren't available** (use only when you genuinely don't know):
28
+
29
+ - `dir="auto"` — lets the user agent infer direction from content
30
+ - `lang="und"` — marks the language as undetermined
31
+
32
+ Both fallbacks are worse than the correct value but much better than nothing. For multi-locale templates, pass the locale through; do not hardcode `en`.
33
+
34
+ ### Mark layout tables as presentational (Serious)
35
+
36
+ Any `<table>` used for layout must have `role="presentation"` (or the equivalent `role="none"`). Otherwise screen readers announce "table, row 1 of N" for every layout row and the email becomes unusable. Prefer avoiding layout tables entirely; when you can't, mark them.
37
+
38
+ ```html
39
+ <table role="presentation" cellpadding="0" cellspacing="0" border="0">
40
+ <tr>
41
+ <td>...</td>
42
+ </tr>
43
+ </table>
44
+ ```
45
+
46
+ Leave a `<table>` without `role="presentation"` only when the data is genuinely tabular (line items, comparison rows). Tabular data should also use `<th scope="col">` for column headers.
47
+
48
+ ### Use a single `<h1>` and nest headings in order (Mild)
49
+
50
+ Most emails should have one `<h1>` that names the email, with subheadings nested in order:`<h1>` → `<h2>` → `<h3>`. Never skip levels. Never fake a heading with bold `<p>`.
51
+
52
+ ```html
53
+ <h1>Order confirmation</h1>
54
+ <h2>Items</h2>
55
+ <h2>Shipping</h2>
56
+ <h3>Address</h3>
57
+ <h3>Tracking</h3>
58
+ ```
59
+
60
+ Headings are how assistive tech and AI clients navigate and summarize the email.
61
+
62
+ **Exception:** very short messages (SMS-style notifications, single-sentence alerts) may not need a heading at all. If the email body is one or two sentences, skip the `<h1>` rather than wrap a heading around the only content.
63
+
64
+ ### Every link must have discernible text (Serious)
65
+
66
+ Every `<a>` must contain text content that a screen reader can announce. The most common failure is a linked image with no alt text.
67
+
68
+ A **linked image is never decorative.** It's functional, so its `alt` must describe what clicking does, not just what the image looks like.
69
+
70
+ ```html
71
+ <!-- Wrong: linked image with no accessible name -->
72
+ <a href="/order/123">
73
+ <img src="view-order.png" alt="">
74
+ </a>
75
+
76
+ <!-- Right: alt describes the action -->
77
+ <a href="/order/123">
78
+ <img src="view-order.png" alt="View order #123">
79
+ </a>
80
+
81
+ <!-- Also right: visible text alongside the image -->
82
+ <a href="/order/123">
83
+ <img src="icon.png" alt="">
84
+ View order #123
85
+ </a>
86
+ ```
87
+
88
+ When the visible link text can't carry enough information, add visually hidden text inside the `<a>` (see [goodemailcode.com/email-accessibility/visually-hidden-text](https://www.goodemailcode.com/email-accessibility/visually-hidden-text)). `aria-label` and `title` on `<a>` have limited support in email clients. Prefer real text content or visually hidden text.
89
+
90
+ ### Link text must describe the destination (Moderate)
91
+
92
+ Even when a link has text, it must describe where the link goes. Never use "click here," "learn more," "read more," or bare URLs. Screen reader users often navigate by jumping between link texts with no surrounding context.
93
+
94
+ ```html
95
+ <!-- Wrong -->
96
+ <a href="...">click here</a>
97
+ <a href="...">https://resend.com/blog/...</a>
98
+
99
+ <!-- Right -->
100
+ <a href="...">Read the 2026 accessibility report</a>
101
+ ```
102
+
103
+ ### Write meaningful alt text — and use `alt=""` for decorative images (Critical)
104
+
105
+ Two distinct rules, both mandatory. `alt` must always be present; the value depends on the image's role.
106
+
107
+ **Meaningful images** (product shots, charts, screenshots, anything carrying information): describe purpose and key details in context.
108
+
109
+ ```html
110
+ <!-- Wrong: redundant, vague -->
111
+ <img src="..." alt="image">
112
+ <img src="..." alt="photo of a bike">
113
+
114
+ <!-- Right: purpose + key details -->
115
+ <img src="..." alt="Red bicycle leaning against a brick wall on a rainy street">
116
+ ```
117
+
118
+ **Decorative images** (spacers, dividers, background flourishes, pure branding ornaments): use an empty `alt=""`. This tells screen readers to skip them. Never omit the attribute entirely — omitting it and `alt=""` are not equivalent; some screen readers announce the filename when `alt` is absent.
119
+
120
+ ```html
121
+ <img src="divider.png" alt="" role="presentation">
122
+ ```
123
+
124
+ If an image conveys no information that isn't already in the surrounding text, it is decorative. If it's inside an `<a>`, it is **not** decorative — see the "Every link must have discernible text" rule.
125
+
126
+ ### Include a `<title>` tag (Serious)
127
+
128
+ Many clients and assistive technologies read `<title>` before anything else. It's also shown when the email is viewed as a web page. Treat it like the subject line, not the brand name.
129
+
130
+ ```html
131
+ <head>
132
+ <title>Your weekly product updates from Resend</title>
133
+ </head>
134
+ ```
135
+
136
+ If the per-email title is hard to populate, a generic but specific fallback like `"Email from {Brand Name}"` still beats nothing.
137
+
138
+ ### Hit 4.5:1 color contrast, then check dark mode (Serious)
139
+
140
+ - Body text and links: **4.5:1** minimum against the background (WCAG AA)
141
+ - Large text (≥18pt, or ≥14pt bold): **3:1** minimum
142
+ - Never rely on color alone to convey meaning (error states, status badges) — pair it with text or an icon
143
+
144
+ Verify with the [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/) or browser devtools.
145
+
146
+ **Dark mode.** Outlook, Apple Mail, and others force dark mode and derive dark colors from your light ones. Healthy starting contrast keeps the auto-inverted version readable. Always preview in dark mode before shipping.
147
+
148
+ ## Priority order
149
+
150
+ When you can't fix everything, fix in this order:
151
+
152
+ 1. **Critical** — missing or misused `alt` on images
153
+ 2. **Serious** — `lang`/`dir` (on `<html>` and body children), `role="presentation"` on layout tables, links without discernible text, missing `<title>`, color contrast
154
+ 3. **Moderate** — non-descriptive link text ("click here")
155
+ 4. **Mild** — missing `<h1>` (skip the fix for very short messages)
156
+
157
+ ## Authoring checklist
158
+
159
+ Run this on every template:
160
+
161
+ - [ ] `<html>` has `lang` and `dir`; direct children of `<body>` also have `lang` and `dir`
162
+ - [ ] `<title>` set on `<head>`, specific to this email (not the brand name)
163
+ - [ ] Layout `<table>` elements have `role="presentation"` (or `role="none"`)
164
+ - [ ] At most one `<h1>` (or none, for very short messages); `<h2>`/`<h3>` nested in order
165
+ - [ ] Every `<a>` has discernible text — visible text, descriptive alt on linked images, or visually hidden text
166
+ - [ ] Every link describes its destination — no "click here," "learn more," or bare URLs
167
+ - [ ] Every meaningful image has descriptive `alt`; every decorative image has an explicit `alt=""`
168
+ - [ ] No linked image with `alt=""` (linked images are functional, never decorative)
169
+ - [ ] Body text passes 4.5:1 contrast and stays readable in dark mode
170
+ - [ ] Plain-text alternative is sent alongside the HTML version
171
+
172
+ ## Testing
173
+
174
+ - **Automated.** Run the email through [Parcel's accessibility checker](https://parcel.io/docs/dev-tools/accessibility-checker) (the same tool the EMC report uses; available on the free plan). It catches most of the rules above.
175
+ - **Screen reader pass.** macOS VoiceOver (`Cmd+F5`) or NVDA on Windows. Listen top to bottom; if anything is confusing, fix the markup.
176
+ - **Contrast.** [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/).
177
+ - **Dark mode.** Send a test to Outlook (Windows/web), Apple Mail with dark appearance, Gmail iOS and Android.
178
+
179
+ Automated tests do not catch everything. They will not tell you whether alt text actually matches the image, whether headings make semantic sense, or whether text inside an image is readable on narrow viewports. Even the three brands that passed every automated check in the EMC 2026 report had judgment-level issues like generic alt text on decorative images, alt text that didn't match the image, and 10px footer text. Treat automation as a floor, not a ceiling.
180
+
181
+ ## Related
182
+
183
+ - [Transactional Emails](./transactional-emails.md) — content patterns for password resets, OTPs, receipts
184
+ - [Marketing Emails](./marketing-emails.md) — newsletter and campaign best practices
185
+ - [Compliance](./compliance.md) — legal requirements that overlap with accessibility (e.g., clear unsubscribe text)
186
+
187
+ ## Tooling
188
+
189
+ When generating templates with React Email, the latest version handles several of the structural rules: `<Html>` sets `lang`/`dir`, `<Img>` defaults to `alt=""`, `<Markdown>` tables render `role="presentation"`, and `<Preview>` emits a `<title>`. Upgrade with `npm install react-email@latest`. The content rules — heading hierarchy, descriptive alt and link text, contrast, the linked-image rule — still have to be applied by hand.
@@ -0,0 +1,125 @@
1
+ # Email Compliance
2
+
3
+ Legal requirements for email by jurisdiction. **Not legal advice—consult an attorney for your specific situation.**
4
+
5
+ ## Quick Reference
6
+
7
+ | Law | Region | Key Requirement | Penalty |
8
+ |-----|--------|-----------------|---------|
9
+ | CAN-SPAM | US | Opt-out mechanism, physical address | $53k/email |
10
+ | GDPR | EU | Explicit opt-in consent | €20M or 4% revenue |
11
+ | CASL | Canada | Express consent, opt-out mechanism | $1M (individual) to $10M (organization) CAD |
12
+
13
+ ## CAN-SPAM (United States)
14
+
15
+ **Requirements:**
16
+ - Accurate header info (From, To, Reply-To)
17
+ - Non-deceptive subject lines
18
+ - Physical mailing address in every email
19
+ - Clear opt-out mechanism
20
+ - Honor opt-out within 10 business days
21
+
22
+ **Transactional emails:** Can send without opt-in if related to a transaction and not promotional.
23
+
24
+ ## GDPR (European Union)
25
+
26
+ **Requirements:**
27
+ - Explicit opt-in consent (not pre-checked boxes)
28
+ - Consent must be freely given, specific, informed
29
+ - Easy to withdraw consent (as easy as giving it)
30
+ - Right to access data and deletion ("right to be forgotten")
31
+ - Process unsubscribe immediately
32
+
33
+ **Consent records:** Document who, when, how, and what they consented to.
34
+
35
+ **Transactional emails:** Can send based on contract fulfillment or legitimate interest.
36
+
37
+ ## CASL (Canada)
38
+
39
+ **Consent types:**
40
+ - **Express consent:** Explicit opt-in (ideal)
41
+ - **Implied consent:** Existing business relationship (2 years) or inquiry (6 months)
42
+
43
+ **Requirements:**
44
+ - Clear sender identification that will be valid for 60 days after send
45
+ - Unsubscribe functional for 60 days after send
46
+ - Process unsubscribe no later than 10 business days
47
+ - Keep consent records 3 years after expiration
48
+
49
+ ## Other Regions
50
+
51
+ | Region | Law | Key Points |
52
+ |--------|-----|------------|
53
+ | Australia | Spam Act 2003 | Consent required, honor unsubscribe within 5 days |
54
+ | UK | PECR + GDPR | Same as GDPR |
55
+ | Brazil | LGPD | Similar to GDPR, explicit consent for marketing |
56
+
57
+ ## Unsubscribe Requirements Summary
58
+
59
+ | Law | Timing | Notes |
60
+ |-----|--------|-------|
61
+ | CAN-SPAM | 10 business days | Must work 30 days after send |
62
+ | GDPR | Immediately | Must be as easy as opting in |
63
+ | CASL | 10 business days | Must work 60 days after send |
64
+
65
+ **Universal best practices:** Prominent link, one-click when possible, no login required, free, confirm action.
66
+
67
+ ### List-Unsubscribe Header (Required for Bulk Senders)
68
+
69
+ Gmail, Yahoo, and Microsoft require `List-Unsubscribe` headers. Without them, bulk emails may be rejected or spam-filtered.
70
+
71
+ **Required headers:**
72
+
73
+ ```typescript
74
+ headers: {
75
+ 'List-Unsubscribe': '<https://example.com/unsubscribe>',
76
+ 'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click',
77
+ }
78
+ ```
79
+
80
+ **Your unsubscribe endpoint must:**
81
+ - Accept POST requests — return `200` or `202` with a blank page
82
+ - Display standard unsubscribe page for GET requests
83
+ - Stop sending within 48 hours of the request
84
+
85
+ ## Managing preferences vs Unsubscribe from all
86
+
87
+ Most legistlations require a one-click unsubscribe. `Managing preferences` is a nice-to-have and can lead to lower unsubscribe rate but doesn't replace `Unsubscribe`. If possible, offer both.
88
+
89
+ ## Consent Management
90
+
91
+ **Record:**
92
+ - Email address
93
+ - Date/time of consent
94
+ - Method (form, checkbox)
95
+ - What they consented to
96
+ - Source (which page/form)
97
+
98
+ **Storage:** Database with timestamps, audit trail of changes, link to user account.
99
+
100
+ ## Data Retention
101
+
102
+ | Law | Requirement |
103
+ |-----|-------------|
104
+ | GDPR | Keep only as long as necessary, delete when no longer needed |
105
+ | CASL | Keep consent records 3 years after expiration |
106
+
107
+ **Best practice:** Have clear retention policy, honor deletion requests promptly, review and clean regularly.
108
+
109
+ ## Privacy Policy Must Include
110
+
111
+ - What data you collect
112
+ - How you use data
113
+ - Who you share data with
114
+ - User rights (access, deletion)
115
+ - How to contact about privacy
116
+
117
+ ## International Sending
118
+
119
+ **Best practice:** Follow the most restrictive requirements (usually GDPR) to ensure compliance across all regions.
120
+
121
+ ## Related
122
+
123
+ - [Email Capture](./email-capture.md) - Implement consent forms and double opt-in
124
+ - [Marketing Emails](./marketing-emails.md) - Consent and unsubscribe requirements
125
+ - [List Management](./list-management.md) - Handle unsubscribes and deletion requests