@salesforce/afv-skills 1.41.0 → 1.43.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.
Files changed (288) hide show
  1. package/package.json +2 -6
  2. package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +3 -0
  3. package/skills/automation-sandbox-post-copy-configure/SKILL.md +4 -0
  4. package/skills/design-systems-slds-validate/SKILL.md +2 -2
  5. package/skills/dx-app-analytics-query/SKILL.md +1 -3
  6. package/skills/dx-devops-conflict-resolve/SKILL.md +205 -0
  7. package/skills/dx-devops-conflict-resolve/examples/conflict-workflows.md +165 -0
  8. package/skills/dx-devops-conflict-resolve/references/deploy-failure-resolution.md +75 -0
  9. package/skills/dx-devops-conflict-resolve/references/git-conflict-resolution.md +124 -0
  10. package/skills/dx-devops-conflict-resolve/scripts/detect-conflicts.sh +91 -0
  11. package/skills/dx-devops-conflict-resolve/scripts/diagnose-deploy-failure.sh +156 -0
  12. package/skills/dx-devops-request-status/SKILL.md +160 -0
  13. package/skills/dx-devops-request-status/examples/polling-workflows.md +101 -0
  14. package/skills/dx-devops-request-status/references/cli-commands.md +176 -0
  15. package/skills/dx-devops-request-status/scripts/poll-status.sh +134 -0
  16. package/skills/dx-devops-test-failures-analyze/SKILL.md +6 -6
  17. package/skills/dx-devops-test-pipeline-configure/SKILL.md +7 -7
  18. package/skills/dx-devops-test-suite-assignments-configure/SKILL.md +6 -6
  19. package/skills/dx-devops-test-suite-run/SKILL.md +6 -6
  20. package/skills/dx-devops-work-item-manage/SKILL.md +2 -0
  21. package/skills/dx-org-manage/references/creating-scratch-org.md +5 -2
  22. package/skills/dx-org-manage/references/creating-snapshot.md +1 -0
  23. package/skills/dx-org-shape-manage/SKILL.md +152 -0
  24. package/skills/dx-org-shape-manage/examples/create_error_output.json +9 -0
  25. package/skills/dx-org-shape-manage/examples/create_success_output.json +9 -0
  26. package/skills/dx-org-shape-manage/examples/delete_output.json +11 -0
  27. package/skills/dx-org-shape-manage/examples/list_inactive_output.json +32 -0
  28. package/skills/dx-org-shape-manage/examples/list_output.json +24 -0
  29. package/skills/dx-org-shape-manage/references/cli_flags.md +135 -0
  30. package/skills/dx-org-switch/SKILL.md +2 -2
  31. package/skills/dx-org-trial-expiration-check/SKILL.md +2 -0
  32. package/skills/dx-pkg-post-install-configure/SKILL.md +3 -0
  33. package/skills/education-cloud-multi-campus-configure/SKILL.md +211 -0
  34. package/skills/education-cloud-multi-campus-configure/examples/hierarchy_visualization.md +174 -0
  35. package/skills/education-cloud-multi-campus-configure/examples/output_examples.md +54 -0
  36. package/skills/education-cloud-multi-campus-configure/examples/sample_hierarchy_input.csv +29 -0
  37. package/skills/education-cloud-multi-campus-configure/examples/sample_hierarchy_input_edgecases.csv +16 -0
  38. package/skills/education-cloud-multi-campus-configure/references/account_recordtype_prerequisite.md +44 -0
  39. package/skills/education-cloud-multi-campus-configure/references/delta_computation.md +33 -0
  40. package/skills/education-cloud-multi-campus-configure/references/error_handling.md +198 -0
  41. package/skills/education-cloud-multi-campus-configure/references/foundation_prerequisites.md +54 -0
  42. package/skills/education-cloud-multi-campus-configure/references/gotchas.md +16 -0
  43. package/skills/education-cloud-multi-campus-configure/references/hierarchy_parsing_rules.md +133 -0
  44. package/skills/education-cloud-multi-campus-configure/references/mcp-invocation.md +75 -0
  45. package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/SKILL.md +1 -1
  46. package/skills/experience-cms-brand-apply/SKILL.md +4 -1
  47. package/skills/experience-cms-brand-create/SKILL.md +197 -0
  48. package/skills/experience-cms-brand-create/assets/brand-template.json +329 -0
  49. package/skills/experience-cms-brand-create/references/brand-anatomy.md +120 -0
  50. package/skills/experience-cms-brand-create/references/disk-contract.md +84 -0
  51. package/skills/experience-cms-content-generate/SKILL.md +190 -0
  52. package/skills/experience-cms-content-generate/assets/display-formats.md +84 -0
  53. package/skills/experience-cms-content-generate/assets/payloads/create-content-bulk.json +11 -0
  54. package/skills/experience-cms-content-generate/assets/payloads/create-content-single.json +11 -0
  55. package/skills/experience-cms-content-generate/assets/payloads/publish-content.json +4 -0
  56. package/skills/experience-cms-content-generate/assets/payloads/update-content.json +9 -0
  57. package/skills/experience-cms-content-generate/assets/questions.md +241 -0
  58. package/skills/experience-cms-content-generate/examples/create-content-call.md +81 -0
  59. package/skills/experience-cms-content-generate/references/bulk-batching.md +56 -0
  60. package/skills/experience-cms-content-generate/references/content-type-classification.md +88 -0
  61. package/skills/experience-cms-content-generate/references/content-write-tool.md +95 -0
  62. package/skills/experience-cms-content-generate/references/delegation-protocol.md +37 -0
  63. package/skills/experience-cms-content-generate/references/edit-publish-workflow.md +63 -0
  64. package/skills/experience-cms-content-generate/references/error-recovery.md +72 -0
  65. package/skills/experience-cms-content-generate/references/identifier-resolution.md +26 -0
  66. package/skills/experience-cms-content-generate/references/intent-routing.md +45 -0
  67. package/skills/experience-cms-content-generate/references/principles.md +19 -0
  68. package/skills/experience-cms-content-generate/references/ux-rules.md +68 -0
  69. package/skills/experience-cms-content-generate/references/workspace-resolution.md +33 -0
  70. package/skills/experience-cms-content-type-generate/SKILL.md +449 -0
  71. package/skills/experience-cms-content-type-generate/assets/discovery-prompts.md +79 -0
  72. package/skills/experience-cms-content-type-generate/assets/schema-example.json +70 -0
  73. package/skills/experience-cms-content-type-generate/references/agent-checklist.md +50 -0
  74. package/skills/experience-cms-content-type-generate/references/anti-patterns.md +39 -0
  75. package/skills/experience-cms-content-type-generate/references/deployment-errors.md +27 -0
  76. package/skills/experience-cms-content-type-generate/references/discovery-details.md +106 -0
  77. package/skills/experience-cms-content-type-generate/references/discovery-query-rules.md +85 -0
  78. package/skills/experience-cms-content-type-generate/references/edit-fields-loop.md +48 -0
  79. package/skills/experience-cms-content-type-generate/references/pre-deploy-checklist.md +21 -0
  80. package/skills/experience-cms-content-type-generate/references/retrieve-and-reconcile.md +105 -0
  81. package/skills/experience-cms-content-type-generate/references/schema-rules.md +51 -0
  82. package/skills/experience-cms-content-type-generate/references/schema-summary-format.md +60 -0
  83. package/skills/experience-lds-graphql-generate/SKILL.md +4 -0
  84. package/skills/experience-lwc-accessibility-jest-run/SKILL.md +79 -0
  85. package/skills/experience-lwc-accessibility-jest-run/references/running-sa11y-jest-tests.md +205 -0
  86. package/skills/experience-lwc-design-generate/SKILL.md +5 -4
  87. package/skills/experience-lwc-generate/SKILL.md +10 -9
  88. package/skills/experience-ui-bundle-deploy/references/dev-preview.md +21 -0
  89. package/skills/experience-ui-bundle-deploy/references/social-login.md +1 -0
  90. package/skills/experience-ui-bundle-file-upload-generate/SKILL.md +6 -6
  91. package/skills/experience-ui-bundle-localize/SKILL.md +30 -37
  92. package/skills/experience-ui-bundle-localize/references/gotchas.md +23 -7
  93. package/skills/experience-ui-bundle-localize/references/i18n-setup.md +48 -4
  94. package/skills/experience-ui-bundle-localize/references/label-xml.md +22 -8
  95. package/skills/experience-ui-bundle-localize/references/verifying.md +39 -11
  96. package/skills/experience-ui-bundle-localize/scripts/detect-bundle-type.sh +118 -15
  97. package/skills/experience-ui-bundle-localize/scripts/tests/test-detect-bundle-type.sh +187 -0
  98. package/skills/experience-ui-bundle-mfa-configure/SKILL.md +13 -2
  99. package/skills/experience-ui-bundle-mfa-configure/references/social-login.md +1 -0
  100. package/skills/experience-ui-bundle-project-generate/SKILL.md +2 -2
  101. package/skills/integration-connectivity-generate/SKILL.md +11 -10
  102. package/skills/integration-eventing-cdc-configure/SKILL.md +6 -6
  103. package/skills/integration-eventing-subscription-configure/SKILL.md +6 -5
  104. package/skills/mobile-platform-native-capabilities-integrate/SKILL.md +2 -2
  105. package/skills/mobile-platform-offline-validate/scripts/package.json +1 -1
  106. package/skills/platform-agentexchange-partner-offers-configure/SKILL.md +8 -8
  107. package/skills/platform-custom-application-generate/SKILL.md +2 -0
  108. package/skills/platform-custom-field-generate/SKILL.md +8 -6
  109. package/skills/platform-custom-metadata-type-generate/SKILL.md +460 -0
  110. package/skills/platform-custom-metadata-type-generate/references/cmdt-records.md +251 -0
  111. package/skills/platform-custom-metadata-type-generate/scripts/sanitize-developer-name.sh +46 -0
  112. package/skills/platform-custom-setting-generate/SKILL.md +419 -0
  113. package/skills/platform-dataspace-access-configure/SKILL.md +3 -3
  114. package/skills/platform-sharing-owd-configure/SKILL.md +3 -0
  115. package/skills/platform-value-set-generate/SKILL.md +2 -0
  116. package/skills/service-agentforce-channel-configure/SKILL.md +29 -20
  117. package/skills/service-agentforce-channel-configure/assets/BotEmailDefinition.botEmailDefinition-meta.xml +36 -0
  118. package/skills/service-agentforce-channel-configure/assets/email/unfiled$public/AgentforceForServiceEmailTemplate.email +17 -0
  119. package/skills/service-agentforce-channel-configure/assets/email/unfiled$public/AgentforceForServiceEmailTemplate.email-meta.xml +29 -0
  120. package/skills/service-agentforce-channel-configure/assets/mdapi-package.xml +25 -0
  121. package/skills/service-agentforce-channel-configure/assets/settings-mdapi-package.xml +30 -0
  122. package/skills/service-agentforce-channel-configure/references/agent-wiring.md +20 -0
  123. package/skills/service-agentforce-channel-configure/references/botemaildefinition.md +107 -0
  124. package/skills/service-agentforce-channel-configure/references/channel-branch-email.md +203 -50
  125. package/skills/service-agentforce-channel-configure/references/channel-types.md +30 -3
  126. package/skills/service-agentforce-channel-configure/references/queue-resolution.md +17 -8
  127. package/skills/service-agentforce-channel-configure/references/routing-flow.md +14 -3
  128. package/skills/service-agentforce-channel-configure/scripts/validate-botemaildefinition.py +126 -0
  129. package/skills/service-agentforce-channel-configure/scripts/validate-emailtemplate.py +133 -0
  130. package/skills/service-de-channel-activate/SKILL.md +271 -0
  131. package/skills/service-de-channel-activate/references/gotchas.md +31 -0
  132. package/skills/service-de-channel-activate/references/phone-verification.md +96 -0
  133. package/skills/service-de-channel-activate/references/worked-examples.md +14 -0
  134. package/skills/service-de-channel-consent-configure/SKILL.md +230 -0
  135. package/skills/service-de-channel-consent-configure/references/gotchas.md +41 -0
  136. package/skills/service-de-channel-consent-configure/references/language-keywords.md +84 -0
  137. package/skills/service-de-channel-consent-configure/references/worked-examples.md +134 -0
  138. package/skills/service-de-channel-create/SKILL.md +369 -0
  139. package/skills/service-de-channel-create/references/apple.md +59 -0
  140. package/skills/service-de-channel-create/references/connect-insert.md +84 -0
  141. package/skills/service-de-channel-create/references/facebook.md +154 -0
  142. package/skills/service-de-channel-create/references/line.md +66 -0
  143. package/skills/service-de-channel-create/references/sms.md +98 -0
  144. package/skills/service-de-channel-create/references/whatsapp.md +98 -0
  145. package/skills/service-de-channel-create/references/worked-examples.md +70 -0
  146. package/skills/service-de-channel-routing-configure/SKILL.md +345 -0
  147. package/skills/service-de-channel-routing-configure/references/asa-routing.md +90 -0
  148. package/skills/service-de-channel-routing-configure/references/gotchas.md +27 -0
  149. package/skills/service-de-channel-routing-configure/references/queue-creation.md +123 -0
  150. package/skills/service-de-channel-routing-configure/references/target-locate.md +98 -0
  151. package/skills/service-de-channel-routing-configure/references/worked-examples.md +174 -0
  152. package/skills/service-de-headless-channel-configure/SKILL.md +305 -0
  153. package/skills/service-de-headless-channel-configure/references/gotchas.md +27 -0
  154. package/skills/service-de-headless-channel-configure/references/inputs.md +49 -0
  155. package/skills/service-de-headless-channel-configure/references/output-envelopes.md +53 -0
  156. package/skills/service-de-headless-channel-configure/references/partial-success.md +24 -0
  157. package/skills/service-de-headless-channel-configure/references/terms-and-conditions.md +54 -0
  158. package/skills/service-de-headless-channel-configure/references/worked-examples.md +108 -0
  159. package/skills/service-de-waba-integrate/SKILL.md +227 -0
  160. package/skills/service-digital-engagement-channel-configure/scripts/check-api-version.sh +29 -0
  161. package/skills/service-digital-engagement-messaging-site-integrate/SKILL.md +10 -9
  162. package/skills/service-digital-engagement-messaging-site-integrate/references/lwr_patch.md +39 -21
  163. package/skills/service-digital-engagement-messaging-site-integrate/scripts/patch_lwr_bundle.sh +157 -96
  164. package/skills/service-email-to-case-configure/SKILL.md +190 -0
  165. package/skills/service-email-to-case-configure/assets/CaseSettings.settings-meta.xml +45 -0
  166. package/skills/service-email-to-case-configure/examples/CaseSettings-two-addresses.settings-meta.xml +36 -0
  167. package/skills/service-email-to-case-configure/references/apply-mechanics.md +73 -0
  168. package/skills/service-email-to-case-configure/references/routing_address_reference.md +73 -0
  169. package/skills/service-email-to-case-configure/references/troubleshooting.md +25 -0
  170. package/skills/service-email-to-case-configure/scripts/apply-casesettings.py +1315 -0
  171. package/skills/service-email-to-case-configure/scripts/check-agent-email-capability.sh +26 -0
  172. package/skills/service-email-to-case-configure/scripts/tests/__init__.py +0 -0
  173. package/skills/service-email-to-case-configure/scripts/tests/_bootstrap.py +45 -0
  174. package/skills/service-email-to-case-configure/scripts/tests/_fakeorg.py +366 -0
  175. package/skills/service-email-to-case-configure/scripts/tests/_run.py +84 -0
  176. package/skills/service-email-to-case-configure/scripts/tests/test_apply_org_scenarios.py +655 -0
  177. package/skills/service-email-to-case-configure/scripts/tests/test_get_session.py +134 -0
  178. package/skills/service-email-to-case-configure/scripts/tests/test_new_capabilities.py +193 -0
  179. package/skills/service-email-to-case-configure/scripts/tests/test_validate_casesettings.py +170 -0
  180. package/skills/service-email-to-case-configure/scripts/validate-casesettings.py +229 -0
  181. package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +33 -15
  182. package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +63 -47
  183. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/SKILL.md +17 -12
  184. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/references/cli-invocation.md +5 -3
  185. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/scripts/classify-enable-plan.mjs +11 -3
  186. package/skills/service-itsm-agentic-setup-agentforce-studio-validate/SKILL.md +8 -6
  187. package/skills/service-itsm-agentic-setup-agentforce-studio-validate/references/cli-invocation.md +3 -3
  188. package/skills/service-itsm-agentic-setup-agentforce-studio-validate/scripts/classify-readiness.mjs +11 -3
  189. package/skills/service-itsm-agentic-setup-configure/SKILL.md +5 -5
  190. package/skills/service-itsm-agentic-setup-configure/examples/output-templates.md +5 -5
  191. package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +2 -2
  192. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/report-format.md +11 -7
  193. package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +20 -7
  194. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +10 -6
  195. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/action-availability.md +7 -7
  196. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/report-format.md +11 -7
  197. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/build-create-body.mjs +24 -17
  198. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-action-availability.mjs +98 -58
  199. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +20 -7
  200. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/strip-release-management.mjs +176 -0
  201. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/SKILL.md +19 -19
  202. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/cli-invocation.md +3 -4
  203. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/helper-contracts.md +6 -6
  204. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/permset-topology.md +3 -10
  205. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/scripts/classify-permset-availability.mjs +7 -9
  206. package/skills/service-itsm-incident-mgmt-configure/SKILL.md +2 -0
  207. package/skills/service-itsm-incident-priority-configure/SKILL.md +15 -12
  208. package/skills/service-itsm-incident-priority-configure/examples/matrix-operations.md +27 -1
  209. package/skills/service-itsm-incident-priority-configure/references/sf-cli-invocation.md +37 -6
  210. package/skills/service-native-voice-recording-transcription-configure/SKILL.md +146 -0
  211. package/skills/service-native-voice-recording-transcription-configure/assets/confirmation-output.json +16 -0
  212. package/skills/service-native-voice-recording-transcription-configure/assets/package.xml +22 -0
  213. package/skills/service-native-voice-recording-transcription-configure/references/thunderbird-voice-settings.md +101 -0
  214. package/skills/service-native-voice-recording-transcription-configure/scripts/enable-recording-transcription.sh +181 -0
  215. package/skills/data360-activate/README.md +0 -38
  216. package/skills/data360-activate/SKILL.md +0 -127
  217. package/skills/data360-connect/README.md +0 -57
  218. package/skills/data360-connect/SKILL.md +0 -163
  219. package/skills/data360-connect/examples/connections/heroku-postgres.json +0 -15
  220. package/skills/data360-connect/examples/connections/ingest-api-connection.json +0 -5
  221. package/skills/data360-connect/examples/connections/ingest-api-schema.json +0 -31
  222. package/skills/data360-connect/examples/connections/redshift.json +0 -16
  223. package/skills/data360-connect/examples/connections/sharepoint-unstructured.json +0 -20
  224. package/skills/data360-connect/examples/connections/snowflake-connection.json +0 -42
  225. package/skills/data360-harmonize/README.md +0 -31
  226. package/skills/data360-harmonize/SKILL.md +0 -126
  227. package/skills/data360-orchestrate/README.md +0 -120
  228. package/skills/data360-orchestrate/SKILL.md +0 -263
  229. package/skills/data360-orchestrate/assets/definitions/activation-target.template.json +0 -5
  230. package/skills/data360-orchestrate/assets/definitions/activation.template.json +0 -7
  231. package/skills/data360-orchestrate/assets/definitions/calculated-insight.template.json +0 -7
  232. package/skills/data360-orchestrate/assets/definitions/data-action-target.template.json +0 -5
  233. package/skills/data360-orchestrate/assets/definitions/data-action.template.json +0 -5
  234. package/skills/data360-orchestrate/assets/definitions/data-graph.template.json +0 -21
  235. package/skills/data360-orchestrate/assets/definitions/data-stream.template.json +0 -55
  236. package/skills/data360-orchestrate/assets/definitions/dmo.template.json +0 -17
  237. package/skills/data360-orchestrate/assets/definitions/identity-resolution.template.json +0 -30
  238. package/skills/data360-orchestrate/assets/definitions/mapping.template.json +0 -14
  239. package/skills/data360-orchestrate/assets/definitions/relationship.template.json +0 -12
  240. package/skills/data360-orchestrate/assets/definitions/search-index.template.json +0 -9
  241. package/skills/data360-orchestrate/assets/definitions/segment.template.json +0 -16
  242. package/skills/data360-orchestrate/references/feature-readiness.md +0 -157
  243. package/skills/data360-orchestrate/references/plugin-setup.md +0 -138
  244. package/skills/data360-orchestrate/scripts/bootstrap-plugin.sh +0 -53
  245. package/skills/data360-orchestrate/scripts/diagnose-org.mjs +0 -511
  246. package/skills/data360-orchestrate/scripts/generate-manifest.mjs +0 -68
  247. package/skills/data360-orchestrate/scripts/verify-plugin.sh +0 -58
  248. package/skills/data360-prepare/README.md +0 -50
  249. package/skills/data360-prepare/SKILL.md +0 -203
  250. package/skills/data360-prepare/examples/ingestion-api/.env.example +0 -8
  251. package/skills/data360-prepare/examples/ingestion-api/README.md +0 -48
  252. package/skills/data360-prepare/examples/ingestion-api/send-data.py +0 -144
  253. package/skills/data360-query/README.md +0 -43
  254. package/skills/data360-query/SKILL.md +0 -128
  255. package/skills/data360-query/examples/search-indexes/hybrid-structured.json +0 -44
  256. package/skills/data360-query/examples/search-indexes/vector-knowledge.json +0 -43
  257. package/skills/data360-segment/README.md +0 -35
  258. package/skills/data360-segment/SKILL.md +0 -123
  259. package/skills/service-helpagent-coordinate/references/channel-help-portal.md +0 -22
  260. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-1-1-non-text-content.md +0 -0
  261. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-i-lists.md +0 -0
  262. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-ii-tables.md +0 -0
  263. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-iii-form-labels.md +0 -0
  264. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-iv-regions.md +0 -0
  265. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-v-groups.md +0 -0
  266. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-5-identify-input.md +0 -0
  267. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-4-3-contrast.md +0 -0
  268. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-1-1-keyboard.md +0 -0
  269. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-4-4-link-purpose.md +0 -0
  270. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-4-6-headings-labels.md +0 -0
  271. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-1-pointer-gestures.md +0 -0
  272. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-2-pointer-cancellation.md +0 -0
  273. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-3-label-in-name.md +0 -0
  274. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-7-dragging-movement.md +0 -0
  275. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-2-1-on-focus.md +0 -0
  276. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-2-2-on-input.md +0 -0
  277. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-3-1-error-identification.md +0 -0
  278. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-3-2-labels-instructions.md +0 -0
  279. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-3-3-error-suggestion.md +0 -0
  280. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-4-1-2-i-name.md +0 -0
  281. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-4-1-2-ii-role.md +0 -0
  282. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-4-1-2-iii-value.md +0 -0
  283. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-1-1-non-text-content.md +0 -0
  284. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-1-use-of-color.md +0 -0
  285. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-10-resize-reflow.md +0 -0
  286. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-11-non-text-contrast.md +0 -0
  287. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-3-contrast.md +0 -0
  288. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/scripts/contrast-ratio.py +0 -0
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env bash
2
+ # Preflight: is this org entitled for Agentforce Email-to-Case (the BotEmailDefinition type)?
3
+ #
4
+ # describeMetadata gates BotEmailDefinition on BOTH orgAccess=orgHasAgentforceServiceAgentEmail
5
+ # (the composite ASA-email entitlement) AND API version >= 68. Neither is SOQL-queryable, but a
6
+ # read-only metadata describe surfaces the type only when both hold — so this single call subsumes
7
+ # the entitlement check and the v68 version gate, and needs no agent to exist yet.
8
+ #
9
+ # Exit 0 -> org is entitled; safe to delegate agent creation + channel wiring.
10
+ # Exit 3 -> type absent (not entitled, or below API v68); caller must STOP before creating an agent.
11
+ # Exit !=0/3 -> the org could not be reached/described; treat as inconclusive, not "not entitled".
12
+ set -euo pipefail
13
+
14
+ ORG="${1:?usage: check-agent-email-capability.sh <target-org-alias>}"
15
+
16
+ # Resolve the org's TRUE max supported API version and pin the describe to it. describeMetadata
17
+ # filters types by the request's API version (isInContextVersion), so running below v68 would filter
18
+ # BotEmailDefinition out even on an entitled org. Do NOT use `sf org display`'s apiVersion here: that
19
+ # is a cached instanceApiVersion that can lag the org's real max (and is not overridden by
20
+ # --api-version), so an upgraded-but-not-re-authed org could resolve low and yield a false "not
21
+ # entitled". Query /services/data (the live version list) and take the highest.
22
+ API=$(sf api request rest --target-org "$ORG" "/services/data" \
23
+ | python3 -c "import sys, json; print(max((v['version'] for v in json.load(sys.stdin)), key=float))")
24
+
25
+ sf org list metadata-types --target-org "$ORG" --api-version "$API" --json \
26
+ | python3 -c "import sys, json; types = {m['xmlName'] for m in json.load(sys.stdin)['result']['metadataObjects']}; sys.exit(0 if 'BotEmailDefinition' in types else 3)"
@@ -0,0 +1,45 @@
1
+ """Test bootstrap — loads the hyphenated skill scripts as importable modules.
2
+
3
+ The skill's scripts (``apply-casesettings.py``, ``validate-casesettings.py``)
4
+ have hyphens in their names, so they can't be imported with a normal
5
+ ``import``. Every ``test_*.py`` in this directory does::
6
+
7
+ from . import _bootstrap
8
+ apply_mod = _bootstrap.load_apply()
9
+ validate_mod = _bootstrap.load_validate()
10
+
11
+ to get the loaded modules. Loading is cached so repeated calls are cheap and
12
+ return the same module object.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ import importlib.util
17
+ from pathlib import Path
18
+
19
+ # scripts/tests/_bootstrap.py → scripts/
20
+ _SCRIPTS_DIR = Path(__file__).resolve().parent.parent
21
+
22
+ _CACHE: dict[str, object] = {}
23
+
24
+
25
+ def _load(module_name: str, filename: str):
26
+ if module_name in _CACHE:
27
+ return _CACHE[module_name]
28
+ path = _SCRIPTS_DIR / filename
29
+ spec = importlib.util.spec_from_file_location(module_name, path)
30
+ if spec is None or spec.loader is None:
31
+ raise ImportError(f"Could not load {filename} from {path}")
32
+ module = importlib.util.module_from_spec(spec)
33
+ spec.loader.exec_module(module)
34
+ _CACHE[module_name] = module
35
+ return module
36
+
37
+
38
+ def load_apply():
39
+ """Return the loaded ``apply-casesettings.py`` module."""
40
+ return _load("apply_casesettings", "apply-casesettings.py")
41
+
42
+
43
+ def load_validate():
44
+ """Return the loaded ``validate-casesettings.py`` module."""
45
+ return _load("validate_casesettings", "validate-casesettings.py")
@@ -0,0 +1,366 @@
1
+ """In-memory fake Salesforce org for driving apply-casesettings.py end to end
2
+ without a live org.
3
+
4
+ The skill script funnels ALL network I/O through three seams:
5
+ * ``post_soap(url, envelope, soap_action)`` — readMetadata / updateMetadata
6
+ * ``query_active_user(session_id, instance_url, username)``
7
+ * ``query_queue(session_id, instance_url, name)``
8
+ plus ``get_session`` for the CLI handshake. Patching those four lets the whole
9
+ ``main()`` flow run against this fake.
10
+
11
+ ``FakeOrg`` holds a CaseSettings state as a nested dict and applies
12
+ ``updateMetadata`` with the SAME merge semantics as the platform:
13
+ * top-level scalar fields merge at the field level (an omitted field keeps its
14
+ current value);
15
+ * inside ``emailToCase``, scalar children merge at the field level;
16
+ * the ``routingAddresses`` collection is REPLACED wholesale — but ONLY when the
17
+ payload's ``emailToCase`` actually contains routingAddresses. A payload whose
18
+ ``emailToCase`` omits routingAddresses (e.g. Phase A) leaves existing
19
+ addresses untouched.
20
+ * ORDERING drives multi-address preservation. An address already provisioned
21
+ in the org (matched by routingName/emailAddress) is DROPPED from the replaced
22
+ collection if any brand-new address appears AFTER it in document order; new
23
+ addresses always survive. Placing new addresses BEFORE existing ones (what
24
+ the skill's Phase B does) preserves everything. This was proven live: the
25
+ platform-managed read-only fields (emailServicesAddress/isVerified) are NOT
26
+ the cause — existing addresses dropped with those fields stripped and
27
+ survived with them kept; only order mattered. Surviving existing addresses
28
+ keep their minted read-only fields (so a verified address stays verified).
29
+
30
+ The ``drop_one_existing_on_write`` flag injects a fault: the org drops one
31
+ existing address on any multi-address write regardless of order, to exercise the
32
+ skill's preservation guard (its runtime safety net) independent of the ordering
33
+ model above.
34
+
35
+ Reads reflect current state. Active users / queues are configured per test.
36
+ """
37
+ from __future__ import annotations
38
+
39
+ import copy
40
+ import xml.etree.ElementTree as ET
41
+ from unittest import mock
42
+
43
+ META_NS = "http://soap.sforce.com/2006/04/metadata"
44
+
45
+
46
+ def _q(name: str) -> str:
47
+ return f"{{{META_NS}}}{name}"
48
+
49
+
50
+ def _local(tag: str) -> str:
51
+ return tag.split("}", 1)[-1] if "}" in tag else tag
52
+
53
+
54
+ class FakeOrg:
55
+ """Models a single org's CaseSettings for apply-casesettings.py tests."""
56
+
57
+ def __init__(self, case_settings=None, active_users=None, queues=None,
58
+ is_sandbox=True, org_type="Developer Edition", is_trial=False,
59
+ org_api_version="67.0", cases=None, email_messages=None,
60
+ drop_one_existing_on_write=False):
61
+ # Current CaseSettings state (nested dict; scalars are python str/bool).
62
+ self.state = copy.deepcopy(case_settings) if case_settings else {}
63
+ # Active usernames (exact match, case-insensitive handled below).
64
+ self.active_users = {u.lower() for u in (active_users or [])}
65
+ # Queue DeveloperNames (and Names) that resolve.
66
+ self.queues = set(queues or [])
67
+ # Record of every updateMetadata payload sent, as nested dicts, for
68
+ # asserting exactly what was written (e.g. stripped/kept fields).
69
+ self.writes: list[dict] = []
70
+ self.instance_url = "https://fake.my.salesforce.com"
71
+ self.auth_username = "authuser@fake.example.com"
72
+ # Organization row for the production-gate. is_sandbox=None models an
73
+ # unreadable Organization row (gate treats it as production).
74
+ self.is_sandbox = is_sandbox
75
+ self.org_type = org_type
76
+ self.is_trial = is_trial
77
+ # apiVersion the fake `sf org display` reports (for version derivation).
78
+ self.org_api_version = org_api_version
79
+ # Rows returned by the Act-3 verify-cases queries.
80
+ self.cases = list(cases or [])
81
+ self.email_messages = list(email_messages or [])
82
+ # Fault injection: when True, any multi-address write drops one existing
83
+ # address regardless of ordering, to exercise the skill's preservation
84
+ # guard independently of the (correctly-ordered) happy path.
85
+ self.drop_one_existing_on_write = drop_one_existing_on_write
86
+
87
+ # -- session / lookups -------------------------------------------------
88
+
89
+ def fake_get_session(self, target_org):
90
+ # Mirror the real get_session's version derivation (floor 67.0) so the
91
+ # metadata URL matches what a live run would produce. get_session is
92
+ # patched wholesale in tests, so resolve_api_version is unit-tested
93
+ # directly; this keeps the fake's URL faithful.
94
+ version = self.org_api_version
95
+ try:
96
+ major = int(str(version).split(".")[0])
97
+ if major < 67:
98
+ version = "67.0"
99
+ except (ValueError, IndexError):
100
+ version = "67.0"
101
+ return ("SESSIONID", f"{self.instance_url}/services/Soap/m/{version}",
102
+ self.instance_url, self.auth_username)
103
+
104
+ def fake_query_active_user(self, session_id, instance_url, username):
105
+ if username and username.lower() in self.active_users:
106
+ return username
107
+ return None
108
+
109
+ def fake_query_queue(self, session_id, instance_url, name):
110
+ return name if name in self.queues else None
111
+
112
+ def fake_query_org_info(self, session_id, instance_url):
113
+ return self.is_sandbox, self.org_type, self.is_trial
114
+
115
+ def fake_query_email_cases(self, session_id, instance_url,
116
+ supplied_email=None):
117
+ rows = self.cases
118
+ if supplied_email is not None:
119
+ rows = [c for c in rows if c.get("SuppliedEmail") == supplied_email]
120
+ return list(rows)
121
+
122
+ def fake_query_incoming_email_messages(self, session_id, instance_url,
123
+ case_ids):
124
+ ids = set(case_ids)
125
+ return [m for m in self.email_messages
126
+ if m.get("Incoming") and m.get("ParentId") in ids]
127
+
128
+ # -- SOAP -------------------------------------------------------------
129
+
130
+ def fake_post_soap(self, url, envelope, soap_action):
131
+ if soap_action == "readMetadata":
132
+ return self._read_response()
133
+ if soap_action == "updateMetadata":
134
+ return self._apply_update(envelope)
135
+ raise AssertionError(f"unexpected SOAPAction {soap_action!r}")
136
+
137
+ def _read_response(self):
138
+ """Build a SOAP-ish response whose .//records mirrors current state."""
139
+ env = ET.Element(_q("Envelope"))
140
+ body = ET.SubElement(env, _q("Body"))
141
+ resp = ET.SubElement(body, _q("readMetadataResponse"))
142
+ result = ET.SubElement(resp, _q("result"))
143
+ records = ET.SubElement(result, _q("records"))
144
+ self._dict_to_xml(records, self.state)
145
+ return env
146
+
147
+ def _apply_update(self, envelope):
148
+ """Parse the payload, record it, merge it into state, return success."""
149
+ root = ET.fromstring(envelope)
150
+ # find the <metadata> element under updateMetadata
151
+ metadata = None
152
+ for el in root.iter():
153
+ if _local(el.tag) == "metadata":
154
+ metadata = el
155
+ break
156
+ assert metadata is not None, "updateMetadata payload missing <metadata>"
157
+ payload = self._xml_to_dict(metadata)
158
+ self.writes.append(copy.deepcopy(payload))
159
+ self._merge(payload)
160
+ return self._success_response()
161
+
162
+ def _merge(self, payload):
163
+ for key, val in payload.items():
164
+ if key == "fullName":
165
+ continue
166
+ if key == "emailToCase" and isinstance(val, dict):
167
+ self._merge_e2c(val)
168
+ else:
169
+ # top-level scalar / block: field-level overwrite
170
+ self.state[key] = val
171
+
172
+ # Platform-managed read-only fields the org mints on a provisioned address.
173
+ _READONLY_ADDRESS_FIELDS = ("emailServicesAddress", "isVerified")
174
+
175
+ def _addr_key(self, addr):
176
+ """Identity used to match a payload address against a provisioned one."""
177
+ return addr.get("routingName") or addr.get("emailAddress")
178
+
179
+ def _existing_address_keys(self):
180
+ cur = self.state.get("emailToCase") or {}
181
+ addrs = cur.get("routingAddresses")
182
+ if addrs is None:
183
+ return set()
184
+ if isinstance(addrs, dict):
185
+ addrs = [addrs]
186
+ return {self._addr_key(a) for a in addrs}
187
+
188
+ def _merge_e2c(self, payload_e2c):
189
+ cur = self.state.setdefault("emailToCase", {})
190
+ for key, val in payload_e2c.items():
191
+ if key == "routingAddresses":
192
+ cur["routingAddresses"] = self._resolve_addresses(val)
193
+ else:
194
+ cur[key] = val
195
+
196
+ def _resolve_addresses(self, val):
197
+ """Model the platform's wholesale routingAddresses REPLACE.
198
+
199
+ The collection is replaced by the payload, but with the live-verified
200
+ ORDERING behavior: an address already provisioned in the org is DROPPED
201
+ if any brand-new address appears AFTER it in document order. New
202
+ addresses always survive. Surviving existing addresses keep the
203
+ platform-minted read-only fields they were provisioned with (so a
204
+ verified address stays verified); brand-new addresses get freshly minted
205
+ fields (unverified). Read-only fields declared in the payload are
206
+ ignored for survival — only order matters."""
207
+ items = [dict(a) for a in (val if isinstance(val, list) else [val])]
208
+ existing_keys = self._existing_address_keys()
209
+ prior = {self._addr_key(a): a
210
+ for a in self._existing_address_list()}
211
+
212
+ # Fault injection: drop one existing address regardless of order.
213
+ if self.drop_one_existing_on_write:
214
+ for i, addr in enumerate(items):
215
+ if self._addr_key(addr) in existing_keys:
216
+ items.pop(i)
217
+ break
218
+
219
+ survivors = []
220
+ for i, addr in enumerate(items):
221
+ key = self._addr_key(addr)
222
+ is_existing = key in existing_keys
223
+ if is_existing:
224
+ # Dropped if any brand-new address follows this one.
225
+ if any(self._addr_key(later) not in existing_keys
226
+ for later in items[i + 1:]):
227
+ continue
228
+ # Survives: carry forward its previously-minted read-only fields.
229
+ minted = dict(addr)
230
+ for f in self._READONLY_ADDRESS_FIELDS:
231
+ if f in prior.get(key, {}):
232
+ minted[f] = prior[key][f]
233
+ survivors.append(minted)
234
+ else:
235
+ # Brand-new address: platform mints fresh read-only fields.
236
+ minted = {k: v for k, v in addr.items()
237
+ if k not in self._READONLY_ADDRESS_FIELDS}
238
+ email = minted.get("emailAddress", "addr")
239
+ minted["emailServicesAddress"] = f"{email}.svc.example.com"
240
+ minted["isVerified"] = False
241
+ survivors.append(minted)
242
+ return survivors if len(survivors) != 1 else survivors[0]
243
+
244
+ def _existing_address_list(self):
245
+ cur = self.state.get("emailToCase") or {}
246
+ addrs = cur.get("routingAddresses")
247
+ if addrs is None:
248
+ return []
249
+ return [addrs] if isinstance(addrs, dict) else list(addrs)
250
+
251
+ @staticmethod
252
+ def _success_response():
253
+ env = ET.Element(_q("Envelope"))
254
+ body = ET.SubElement(env, _q("Body"))
255
+ resp = ET.SubElement(body, _q("updateMetadataResponse"))
256
+ result = ET.SubElement(resp, _q("result"))
257
+ ET.SubElement(result, _q("success")).text = "true"
258
+ ET.SubElement(result, _q("fullName")).text = "Case"
259
+ return env
260
+
261
+ # -- dict <-> xml -----------------------------------------------------
262
+
263
+ def _dict_to_xml(self, parent, value):
264
+ for key, val in value.items():
265
+ if isinstance(val, list):
266
+ for item in val:
267
+ child = ET.SubElement(parent, _q(key))
268
+ self._dict_to_xml(child, item)
269
+ elif isinstance(val, dict):
270
+ child = ET.SubElement(parent, _q(key))
271
+ self._dict_to_xml(child, val)
272
+ else:
273
+ child = ET.SubElement(parent, _q(key))
274
+ child.text = self._scalar_text(val)
275
+
276
+ @staticmethod
277
+ def _scalar_text(val):
278
+ if val is True:
279
+ return "true"
280
+ if val is False:
281
+ return "false"
282
+ return str(val)
283
+
284
+ def _xml_to_dict(self, elem):
285
+ children = list(elem)
286
+ if not children:
287
+ text = (elem.text or "").strip()
288
+ if text.lower() == "true":
289
+ return True
290
+ if text.lower() == "false":
291
+ return False
292
+ return text
293
+ result: dict = {}
294
+ for child in children:
295
+ tag = _local(child.tag)
296
+ sub = self._xml_to_dict(child)
297
+ if tag in result:
298
+ if not isinstance(result[tag], list):
299
+ result[tag] = [result[tag]]
300
+ result[tag].append(sub)
301
+ else:
302
+ result[tag] = sub
303
+ return result
304
+
305
+ # -- patching ---------------------------------------------------------
306
+
307
+ def patch(self, apply_mod):
308
+ """Return a context manager patching the four seams on apply_mod."""
309
+ return _Patches(self, apply_mod)
310
+
311
+
312
+ class _Patches:
313
+ def __init__(self, org: FakeOrg, apply_mod):
314
+ self.org = org
315
+ self.mod = apply_mod
316
+ self._patchers = []
317
+
318
+ def __enter__(self):
319
+ # get_session has a documented side-effect: unless --api-version was
320
+ # given, it sets the module's API_VERSION global to the org-derived,
321
+ # floored version. The real function is mocked here, so mirror that
322
+ # side-effect on the patched module using its own resolve_api_version.
323
+ mod = self.mod
324
+ org = self.org
325
+
326
+ def fake_get_session(target_org):
327
+ if not getattr(mod, "_API_VERSION_EXPLICIT", False):
328
+ mod.API_VERSION = mod.resolve_api_version(org.org_api_version)
329
+ return org.fake_get_session(target_org)
330
+
331
+ seams = {
332
+ "get_session": fake_get_session,
333
+ "post_soap": self.org.fake_post_soap,
334
+ "query_active_user": self.org.fake_query_active_user,
335
+ "query_queue": self.org.fake_query_queue,
336
+ "query_org_info": self.org.fake_query_org_info,
337
+ "query_email_cases": self.org.fake_query_email_cases,
338
+ "query_incoming_email_messages":
339
+ self.org.fake_query_incoming_email_messages,
340
+ }
341
+ for name, fn in seams.items():
342
+ p = mock.patch.object(self.mod, name, side_effect=fn)
343
+ p.start()
344
+ self._patchers.append(p)
345
+ return self
346
+
347
+ def __exit__(self, *exc):
348
+ for p in self._patchers:
349
+ p.stop()
350
+ return False
351
+
352
+
353
+ def last_e2c_write(org: FakeOrg) -> dict:
354
+ """The emailToCase block of the most recent updateMetadata payload."""
355
+ for w in reversed(org.writes):
356
+ if "emailToCase" in w:
357
+ return w["emailToCase"]
358
+ raise AssertionError("no updateMetadata write carried an emailToCase block")
359
+
360
+
361
+ def top_level_keys_written(org: FakeOrg) -> set:
362
+ """Union of all top-level keys across every updateMetadata payload."""
363
+ keys: set = set()
364
+ for w in org.writes:
365
+ keys |= set(w.keys())
366
+ return keys
@@ -0,0 +1,84 @@
1
+ """Helpers to drive apply-casesettings.py main() in tests: write a temp
2
+ CaseSettings input file and invoke main() with an argv, capturing the JSON
3
+ summary it prints. main() calls fail()->sys.exit(1) on error, and argparse may
4
+ sys.exit(2); both surface as SystemExit for the test to assert on.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import io
9
+ import json
10
+ import tempfile
11
+ from contextlib import redirect_stdout
12
+ from pathlib import Path
13
+ from unittest import mock
14
+
15
+
16
+ def write_input(tmpdir: str, xml: str) -> str:
17
+ path = Path(tmpdir) / "Case.settings-meta.xml"
18
+ path.write_text(xml, encoding="utf-8")
19
+ return str(path)
20
+
21
+
22
+ def run_main(apply_mod, argv):
23
+ """Run main() with the given argv (excluding program name). Returns the
24
+ parsed JSON summary dict printed to stdout. Raises SystemExit as-is."""
25
+ buf = io.StringIO()
26
+ with mock.patch("sys.argv", ["apply-casesettings.py", *argv]):
27
+ with redirect_stdout(buf):
28
+ apply_mod.main()
29
+ out = buf.getvalue().strip()
30
+ return json.loads(out) if out else {}
31
+
32
+
33
+ class Input:
34
+ """Fluent builder for a CaseSettings source XML string."""
35
+
36
+ def __init__(self):
37
+ self._top = {}
38
+ self._e2c = {"enableEmailToCase": "true",
39
+ "enableOnDemandEmailToCase": "true"}
40
+ self._addrs = []
41
+
42
+ def top(self, **kv):
43
+ self._top.update(kv)
44
+ return self
45
+
46
+ def e2c(self, **kv):
47
+ self._e2c.update(kv)
48
+ return self
49
+
50
+ def no_e2c_defaults(self):
51
+ self._e2c = {"enableEmailToCase": "true",
52
+ "enableOnDemandEmailToCase": "true"}
53
+ return self
54
+
55
+ def with_default_toggles(self):
56
+ self._e2c.update({
57
+ "enableHtmlEmail": "true",
58
+ "notifyOwnerOnNewCaseEmail": "true",
59
+ "enableE2CDeduplicateAttachments": "true",
60
+ "showWordCountInComposer": "true",
61
+ })
62
+ self._top["enableDraftEmails"] = "true"
63
+ return self
64
+
65
+ def address(self, **kv):
66
+ self._addrs.append(kv)
67
+ return self
68
+
69
+ def build(self) -> str:
70
+ lines = ['<?xml version="1.0" encoding="UTF-8"?>',
71
+ '<CaseSettings xmlns="http://soap.sforce.com/2006/04/metadata">']
72
+ for k, v in self._top.items():
73
+ lines.append(f" <{k}>{v}</{k}>")
74
+ lines.append(" <emailToCase>")
75
+ for k, v in self._e2c.items():
76
+ lines.append(f" <{k}>{v}</{k}>")
77
+ for addr in self._addrs:
78
+ lines.append(" <routingAddresses>")
79
+ for k, v in addr.items():
80
+ lines.append(f" <{k}>{v}</{k}>")
81
+ lines.append(" </routingAddresses>")
82
+ lines.append(" </emailToCase>")
83
+ lines.append("</CaseSettings>")
84
+ return "\n".join(lines) + "\n"