@salesforce/afv-skills 1.55.0 → 1.57.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 (274) hide show
  1. package/package.json +1 -1
  2. package/skills/agentforce-generate/SKILL.md +8 -0
  3. package/skills/agentforce-generate/references/actions-reference.md +2 -2
  4. package/skills/agentforce-generate/references/voice-modality-reference.md +51 -15
  5. package/skills/agentforce-generate/references/voice-telephony-cli.md +193 -0
  6. package/skills/agentforce-observe/SKILL.md +13 -9
  7. package/skills/agentforce-observe/references/ahm-alerts.md +193 -14
  8. package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +24 -15
  9. package/skills/automation-sandbox-post-copy-config-generate/assets/config_template.json +11 -0
  10. package/skills/automation-sandbox-post-copy-config-generate/assets/json_schema.json +33 -1
  11. package/skills/automation-sandbox-post-copy-config-generate/references/configuration_catalog.md +32 -1
  12. package/skills/automation-sandbox-post-copy-configure/SKILL.md +75 -71
  13. package/skills/automation-sandbox-post-copy-configure/assets/scheduled_apex_template.apex +13 -0
  14. package/skills/automation-sandbox-post-copy-configure/examples/sample_scheduled_apex_config.json +13 -0
  15. package/skills/automation-sandbox-post-copy-configure/references/scheduled_apex_path.md +155 -0
  16. package/skills/automation-sandbox-post-copy-configure/scripts/build-scheduled-apex.mjs +62 -0
  17. package/skills/automation-sandbox-post-copy-configure/scripts/soql-escape-job-name.mjs +28 -0
  18. package/skills/commerce-b2b-open-code-components-integrate/SKILL.md +1 -1
  19. package/skills/commerce-b2b-open-code-components-replace/SKILL.md +16 -16
  20. package/skills/consumer-goods-accruals-datakit-deploy/SKILL.md +383 -0
  21. package/skills/consumer-goods-accruals-datakit-deploy/scripts/setup.js +324 -0
  22. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/00-check-prerequisites.js +63 -0
  23. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/01-download-static-resource.js +100 -0
  24. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/02-replace-org-id.js +46 -0
  25. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/03-deploy-metadata.js +46 -0
  26. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/04-deploy-engine.js +198 -0
  27. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/05-deploy-tpm-accruals.js +231 -0
  28. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/06-create-dataspace.js +82 -0
  29. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/07-deploy-accruals-reports.js +517 -0
  30. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/08-deploy-ui.js +416 -0
  31. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/09-completion.js +44 -0
  32. package/skills/consumer-goods-accruals-datakit-deploy/scripts/steps/utils.js +514 -0
  33. package/skills/consumer-goods-sync-management-configure/SKILL.md +137 -0
  34. package/skills/consumer-goods-sync-management-configure/examples/scenarios.md +294 -0
  35. package/skills/consumer-goods-sync-management-configure/references/act1-setup-sync.md +333 -0
  36. package/skills/consumer-goods-sync-management-configure/references/act2-assign-users.md +324 -0
  37. package/skills/consumer-goods-sync-management-configure/references/act3-plan-verify.md +113 -0
  38. package/skills/consumer-goods-sync-management-configure/references/readiness-and-enablement.md +164 -0
  39. package/skills/consumer-goods-sync-management-configure/references/sync-management-app-install-backbone.md +159 -0
  40. package/skills/consumer-goods-sync-management-configure/references/transports-and-namespace.md +220 -0
  41. package/skills/consumer-goods-sync-management-configure/references/verify-and-smoke.md +175 -0
  42. package/skills/dx-devops-project-manage/SKILL.md +1 -1
  43. package/skills/dx-org-analyze/SKILL.md +0 -1
  44. package/skills/dx-org-analyze/scripts/collect_org_data.py +2 -2
  45. package/skills/dx-org-analyze/scripts/compute_diff.py +2 -4
  46. package/skills/dx-org-analyze/scripts/introspect_org.py +4 -40
  47. package/skills/dx-org-manage/SKILL.md +6 -50
  48. package/skills/dx-org-manage/examples/README.md +10 -16
  49. package/skills/dx-org-manage/references/scratch-org-create.md +1 -1
  50. package/skills/dx-org-manage/references/scratch-org-operations.md +1 -1
  51. package/skills/dx-org-shape-manage/SKILL.md +4 -3
  52. package/skills/education-cloud-domain-configure/references/gotchas-extended.md +1 -0
  53. package/skills/experience-accessibility-validate/SKILL.md +8 -5
  54. package/skills/experience-accessibility-validate/references/reviewers/sc-1-1-1-non-text-content.md +1 -4
  55. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-i-lists.md +0 -4
  56. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-ii-tables.md +20 -85
  57. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iii-form-labels.md +4 -4
  58. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-iv-regions.md +0 -4
  59. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-1-v-groups.md +2 -4
  60. package/skills/experience-accessibility-validate/references/reviewers/sc-1-3-5-identify-input.md +0 -4
  61. package/skills/experience-accessibility-validate/references/reviewers/sc-1-4-3-contrast.md +4 -14
  62. package/skills/experience-accessibility-validate/references/reviewers/sc-2-1-1-keyboard.md +9 -5
  63. package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-4-link-purpose.md +1 -4
  64. package/skills/experience-accessibility-validate/references/reviewers/sc-2-4-6-headings-labels.md +0 -4
  65. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-1-pointer-gestures.md +0 -4
  66. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-2-pointer-cancellation.md +0 -4
  67. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-3-label-in-name.md +0 -4
  68. package/skills/experience-accessibility-validate/references/reviewers/sc-2-5-7-dragging-movement.md +0 -4
  69. package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-1-on-focus.md +0 -4
  70. package/skills/experience-accessibility-validate/references/reviewers/sc-3-2-2-on-input.md +1 -4
  71. package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-1-error-identification.md +1 -4
  72. package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-2-labels-instructions.md +1 -4
  73. package/skills/experience-accessibility-validate/references/reviewers/sc-3-3-3-error-suggestion.md +1 -4
  74. package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-i-name.md +4 -5
  75. package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-ii-role.md +1 -4
  76. package/skills/experience-accessibility-validate/references/reviewers/sc-4-1-2-iii-value.md +0 -4
  77. package/skills/experience-cms-content-generate/SKILL.md +1 -1
  78. package/skills/experience-cms-content-render/SKILL.md +3 -3
  79. package/skills/experience-cms-content-render/assets/angular/MediaRenderer.component.ts +35 -7
  80. package/skills/experience-cms-content-render/assets/angular/cms-item.service.ts +13 -4
  81. package/skills/experience-cms-content-render/assets/react/MediaRenderer.tsx +30 -8
  82. package/skills/experience-cms-content-render/assets/react/useCmsItem.ts +14 -4
  83. package/skills/experience-cms-content-render/assets/shared/cmsCore.types.ts +11 -1
  84. package/skills/experience-cms-content-render/assets/shared/externalRefs.ts +1 -1
  85. package/skills/experience-cms-content-render/assets/shared/mediaLabels.ts +62 -0
  86. package/skills/experience-cms-content-render/references/init-scaffold.md +13 -6
  87. package/skills/experience-content-media-stock-image-search/SKILL.md +0 -1
  88. package/skills/experience-lwc-accessibility-jest-run/SKILL.md +104 -25
  89. package/skills/experience-lwc-accessibility-jest-run/assets/jest.config.js +4 -0
  90. package/skills/experience-lwc-accessibility-jest-run/assets/sa11y-jest-setup.js +5 -0
  91. package/skills/experience-lwc-design-generate/SKILL.md +1 -1
  92. package/skills/experience-lwc-legacy-migrate/SKILL.md +212 -0
  93. package/skills/experience-lwc-legacy-migrate/assets/lo20-host-page-template.html +192 -0
  94. package/skills/experience-lwc-legacy-migrate/references/aura-to-lwc-completeness-checklist.md +188 -0
  95. package/skills/experience-lwc-legacy-migrate/references/lightning-out-2-system-reference.md +495 -0
  96. package/skills/experience-lwc-legacy-migrate/references/lightning-out-beta-to-2-migration.md +824 -0
  97. package/skills/experience-lwc-legacy-migrate/scripts/convert-lo-names.py +94 -0
  98. package/skills/experience-lwc-legacy-migrate/scripts/validate-lo20-page.py +133 -0
  99. package/skills/experience-ui-bundle-app-coordinate/SKILL.md +5 -5
  100. package/skills/experience-ui-bundle-features-generate/SKILL.md +65 -36
  101. package/skills/experience-ui-bundle-features-generate/references/angular/features.md +74 -0
  102. package/skills/experience-ui-bundle-features-generate/references/react/features.md +59 -0
  103. package/skills/experience-ui-bundle-features-generate/scripts/detect-framework.sh +73 -0
  104. package/skills/experience-ui-bundle-frontend-generate/SKILL.md +2 -2
  105. package/skills/experience-ui-bundle-metadata-generate/SKILL.md +26 -14
  106. package/skills/experience-ui-bundle-metadata-generate/references/angular-metadata-generate.md +37 -0
  107. package/skills/experience-ui-bundle-metadata-generate/references/react-metadata-generate.md +32 -0
  108. package/skills/experience-ui-bundle-metadata-generate/scripts/detect-framework.sh +73 -0
  109. package/skills/experience-ui-bundle-metadata-generate/scripts/verify-bundle-location.sh +30 -8
  110. package/skills/experience-ui-bundle-project-generate/SKILL.md +1 -1
  111. package/skills/field-service-data-capture-migrate/SKILL.md +391 -0
  112. package/skills/field-service-data-capture-migrate/assets/sample-legacy-flow.flow-meta.xml +163 -0
  113. package/skills/field-service-data-capture-migrate/references/architecture-notes.md +40 -0
  114. package/skills/field-service-data-capture-migrate/references/component-mapping.md +278 -0
  115. package/skills/field-service-data-capture-migrate/references/converter-builder-contract.md +183 -0
  116. package/skills/field-service-data-capture-migrate/references/dc-platform-rules.md +130 -0
  117. package/skills/field-service-data-capture-migrate/references/fsm-dc-capability-catalog.md +215 -0
  118. package/skills/field-service-data-capture-migrate/references/known-deploy-errors.md +36 -0
  119. package/skills/field-service-data-capture-migrate/references/migration-checklist.md +121 -0
  120. package/skills/field-service-data-capture-migrate/references/transformer-rules.md +132 -0
  121. package/skills/field-service-data-capture-migrate/scripts/analyze_flow.py +277 -0
  122. package/skills/field-service-data-capture-migrate/scripts/convert_to_dc_spec.py +1311 -0
  123. package/skills/field-service-data-capture-migrate/scripts/cud_analysis.py +402 -0
  124. package/skills/field-service-data-capture-migrate/scripts/deploy_flow.sh +230 -0
  125. package/skills/field-service-data-capture-migrate/scripts/discover_subflow_tree.py +230 -0
  126. package/skills/field-service-data-capture-migrate/scripts/flow_input.py +186 -0
  127. package/skills/field-service-data-capture-migrate/scripts/flow_xml_utils.py +39 -0
  128. package/skills/field-service-data-capture-migrate/scripts/fsm_dc_catalog.py +413 -0
  129. package/skills/field-service-data-capture-migrate/scripts/generate_summary.py +199 -0
  130. package/skills/field-service-data-capture-migrate/scripts/json_to_flow_xml.py +146 -0
  131. package/skills/field-service-data-capture-migrate/scripts/manual_review_flags.py +42 -0
  132. package/skills/field-service-data-capture-migrate/scripts/retrieve_flow_rest.sh +155 -0
  133. package/skills/field-service-data-capture-migrate/scripts/subflow_names.py +49 -0
  134. package/skills/field-service-data-capture-migrate/scripts/transform_flow.py +1661 -0
  135. package/skills/field-service-data-capture-migrate/scripts/validate_flow.sh +147 -0
  136. package/skills/field-service-data-capture-migrate/scripts/vendor/build_flow.py +1258 -0
  137. package/skills/integration-connectivity-connected-app-configure/SKILL.md +5 -5
  138. package/skills/integration-connectivity-generate/SKILL.md +10 -10
  139. package/skills/integration-connectivity-generate/scripts/configure-named-credential.sh +2 -2
  140. package/skills/life-sciences-fieldsalesrep-coordinate/SKILL.md +1 -1
  141. package/skills/life-sciences-fieldsalesrep-coordinate/references/orchestration-flow.md +1 -1
  142. package/skills/life-sciences-kam-coordinate/references/stage-5-data-and-plan-templates-overview.md +14 -1
  143. package/skills/mobile-apps-create/SKILL.md +1 -1
  144. package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/SKILL.md +7 -7
  145. package/skills/platform-apex-test-generate/SKILL.md +1 -1
  146. package/skills/platform-apex-test-run/SKILL.md +1 -1
  147. package/skills/platform-custom-field-generate/SKILL.md +1 -1
  148. package/skills/platform-custom-lightning-type-generate/SKILL.md +1 -1
  149. package/skills/platform-custom-metadata-type-generate/SKILL.md +1 -1
  150. package/skills/platform-custom-object-generate/SKILL.md +1 -1
  151. package/skills/platform-custom-report-type-generate/SKILL.md +1 -1
  152. package/skills/platform-custom-setting-generate/SKILL.md +1 -1
  153. package/skills/platform-data-and-tooling-api-context-get/SKILL.md +1 -1
  154. package/skills/platform-data-manage/SKILL.md +10 -10
  155. package/skills/platform-flexipage-generate/SKILL.md +1 -1
  156. package/skills/platform-lightning-app-coordinate/SKILL.md +1 -1
  157. package/skills/platform-metadata-api-context-get/SKILL.md +1 -1
  158. package/skills/platform-metadata-retrieve/SKILL.md +1 -1
  159. package/skills/platform-permission-set-generate/SKILL.md +1 -1
  160. package/skills/platform-report-generate/SKILL.md +1 -1
  161. package/skills/platform-salesforce-connect-adapter-generate/SKILL.md +4 -4
  162. package/skills/platform-sandbox-configure/SKILL.md +108 -133
  163. package/skills/platform-sandbox-configure/references/api-response-shapes.md +39 -0
  164. package/skills/platform-sandbox-configure/references/definition-file-approach.md +73 -0
  165. package/skills/platform-sharing-owd-configure/SKILL.md +1 -1
  166. package/skills/platform-sharing-rules-generate/SKILL.md +1 -1
  167. package/skills/platform-soql-query/SKILL.md +1 -1
  168. package/skills/platform-value-set-generate/SKILL.md +1 -1
  169. package/skills/platform-widget-generate/SKILL.md +26 -18
  170. package/skills/platform-widget-generate/examples/formulas.json +191 -0
  171. package/skills/platform-widget-generate/references/widget-formulas.md +123 -0
  172. package/skills/platform-widget-generate/references/widget-meta-directives.md +4 -4
  173. package/skills/sales-call-scoring-configure/SKILL.md +318 -0
  174. package/skills/sales-call-scoring-configure/assets/best-practice-competencies.json +67 -0
  175. package/skills/sales-call-scoring-configure/assets/ootb-competencies.json +58 -0
  176. package/skills/sales-call-scoring-configure/references/admin-communication.md +79 -0
  177. package/skills/sales-call-scoring-configure/references/competency-crud.md +130 -0
  178. package/skills/sales-call-scoring-configure/scripts/create-competency.sh +166 -0
  179. package/skills/sales-call-scoring-configure/scripts/create-custom-competency.sh +166 -0
  180. package/skills/sales-call-scoring-configure/scripts/edit-competency.sh +185 -0
  181. package/skills/sales-call-scoring-configure/scripts/edit-custom-competency.sh +190 -0
  182. package/skills/sales-call-scoring-configure/scripts/enable-call-scoring.sh +436 -0
  183. package/skills/sales-call-scoring-configure/scripts/get-competency.sh +72 -0
  184. package/skills/sales-call-scoring-configure/scripts/get-custom-competency.sh +78 -0
  185. package/skills/sales-call-scoring-configure/scripts/install-best-practice-competencies.sh +223 -0
  186. package/skills/sales-call-scoring-configure/scripts/install-ootb-competencies.sh +237 -0
  187. package/skills/sales-call-scoring-configure/scripts/list-competencies.sh +99 -0
  188. package/skills/sales-call-scoring-configure/scripts/shared/auth.sh +116 -0
  189. package/skills/sales-call-scoring-configure/scripts/shared/cap.sh +86 -0
  190. package/skills/sales-call-scoring-configure/scripts/shared/soap.sh +267 -0
  191. package/skills/sales-call-scoring-configure/scripts/shared/soql.sh +25 -0
  192. package/skills/sales-call-scoring-configure/scripts/toggle-competency.sh +112 -0
  193. package/skills/sales-call-scoring-configure/scripts/toggle-custom-competency.sh +125 -0
  194. package/skills/sales-call-scoring-configure/scripts/verify-ootb-competencies.sh +116 -0
  195. package/skills/service-agentforce-human-escalation-configure/SKILL.md +4 -0
  196. package/skills/service-agentforce-human-escalation-configure/scripts/tests/_bootstrap.py +26 -6
  197. package/skills/service-agentforce-human-escalation-configure/scripts/tests/test_escalation_contracts.py +5 -4
  198. package/skills/service-concierge-portal-generate/references/portal-deploy-runbook.md +1 -0
  199. package/skills/service-de-channel-activate/SKILL.md +6 -5
  200. package/skills/service-de-channel-create/SKILL.md +3 -3
  201. package/skills/service-de-channel-routing-configure/SKILL.md +3 -3
  202. package/skills/service-de-channel-settings-configure/SKILL.md +195 -0
  203. package/skills/service-de-channel-settings-configure/references/channel-settings-facets.md +152 -0
  204. package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/gotchas.md +3 -3
  205. package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/language-keywords.md +4 -4
  206. package/skills/{service-de-channel-consent-configure → service-de-channel-settings-configure}/references/worked-examples.md +14 -14
  207. package/skills/service-de-headless-channel-configure/SKILL.md +6 -6
  208. package/skills/service-de-headless-channel-configure/references/worked-examples.md +1 -1
  209. package/skills/service-de-waba-integrate/SKILL.md +2 -1
  210. package/skills/service-digital-engagement-deployment-configure/SKILL.md +2 -0
  211. package/skills/service-digital-engagement-deployment-configure/scripts/check-api-version.sh +29 -0
  212. package/skills/service-email-to-case-configure/SKILL.md +1 -1
  213. package/skills/service-helpagent-coordinate/SKILL.md +3 -2
  214. package/skills/service-helpagent-coordinate/references/channel-voice.md +15 -4
  215. package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +49 -31
  216. package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +71 -6
  217. package/skills/service-itsm-agentic-setup-agentforce-coordinate/scripts/verify-child-verdict.mjs +26 -4
  218. package/skills/service-itsm-agentic-setup-cmdb-access-assign/SKILL.md +35 -10
  219. package/skills/service-itsm-agentic-setup-cmdb-access-assign/references/mcp-invocation.md +25 -5
  220. package/skills/service-itsm-agentic-setup-cmdb-bundle-deploy/SKILL.md +1 -1
  221. package/skills/service-itsm-agentic-setup-cmdb-configure/SKILL.md +5 -5
  222. package/skills/service-itsm-agentic-setup-cmdb-configure/references/mcp-invocation.md +1 -1
  223. package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +2 -2
  224. package/skills/service-itsm-agentic-setup-cmdb-coordinate/examples/output-templates.md +1 -1
  225. package/skills/service-itsm-agentic-setup-cmdb-discovery-configure/SKILL.md +25 -15
  226. package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +8 -5
  227. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/report-format.md +4 -2
  228. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/workflow-detail.md +1 -1
  229. package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +29 -16
  230. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +8 -5
  231. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/report-format.md +4 -2
  232. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/workflow-detail.md +1 -1
  233. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +29 -16
  234. package/skills/service-itsm-agentic-setup-incident-management/SKILL.md +138 -68
  235. package/skills/service-itsm-agentic-setup-incident-management/examples/output-templates.md +31 -15
  236. package/skills/service-itsm-agentic-setup-incident-management/references/incident-persona-psg.md +156 -0
  237. package/skills/service-itsm-agentic-setup-incident-management/references/incident-preferences.md +148 -0
  238. package/skills/service-itsm-agentic-setup-incident-management/references/major-incident-management.md +162 -0
  239. package/skills/service-itsm-agentic-setup-incident-management/references/service-mgmt-privilege.md +149 -0
  240. package/skills/service-itsm-agentic-setup-incident-sla-configure/SKILL.md +103 -89
  241. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/attach-milestone.json +2 -2
  242. package/skills/service-itsm-agentic-setup-incident-sla-configure/assets/predefined-incident-policy.json +21 -39
  243. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/milestone-patterns.md +3 -3
  244. package/skills/service-itsm-agentic-setup-incident-sla-configure/examples/output-templates.md +18 -1
  245. package/skills/service-itsm-agentic-setup-incident-sla-configure/references/mcp-invocation.md +63 -37
  246. package/skills/service-omni-attribute-routing-configure/SKILL.md +53 -0
  247. package/skills/service-omni-attribute-routing-configure/references/api-notes.md +7 -0
  248. package/skills/service-omni-attribute-routing-configure/scripts/configure-and-report.sh +224 -0
  249. package/skills/service-omni-attribute-routing-configure/scripts/tests/test_attribute_routing_contracts.py +137 -0
  250. package/skills/service-omni-channel-inventory-analyze/SKILL.md +96 -0
  251. package/skills/service-omni-channel-inventory-analyze/references/api-notes.md +52 -0
  252. package/skills/service-omni-channel-inventory-analyze/scripts/analyze.sh +185 -0
  253. package/skills/service-omni-channel-inventory-analyze/scripts/tests/_bootstrap.py +84 -0
  254. package/skills/service-omni-channel-inventory-analyze/scripts/tests/test_inventory_analyze_contracts.py +141 -0
  255. package/skills/service-omni-channel-limits-analyze/SKILL.md +70 -0
  256. package/skills/service-omni-channel-limits-analyze/references/api-notes.md +22 -0
  257. package/skills/service-omni-channel-limits-analyze/scripts/analyze.sh +159 -0
  258. package/skills/service-omni-channel-limits-analyze/scripts/tests/_bootstrap.py +87 -0
  259. package/skills/service-omni-channel-limits-analyze/scripts/tests/test_limits_analyze_contracts.py +99 -0
  260. package/skills/service-omni-channel-setup-coordinate/SKILL.md +12 -20
  261. package/skills/service-omni-channel-setup-coordinate/scripts/integration-driver.sh +55 -5
  262. package/skills/service-omni-channel-setup-coordinate/scripts/tests/test_omni_contracts.py +38 -0
  263. package/skills/service-omni-command-center-analyze/SKILL.md +2 -2
  264. package/skills/service-omni-command-center-analyze/scripts/analyze.sh +3 -3
  265. package/skills/dx-org-manage/examples/snapshots/error_output.json +0 -9
  266. package/skills/dx-org-manage/examples/snapshots/success_output.json +0 -15
  267. package/skills/dx-org-manage/references/cli_flags.md +0 -67
  268. package/skills/dx-org-manage/references/creating-snapshot.md +0 -103
  269. package/skills/dx-org-manage/references/snapshot_usage.md +0 -74
  270. package/skills/experience-accessibility-validate/scripts/contrast-ratio.py +0 -153
  271. package/skills/experience-lwc-accessibility-jest-run/references/running-sa11y-jest-tests.md +0 -205
  272. package/skills/experience-ui-bundle-features-generate/scripts/verify-react-bundle.sh +0 -14
  273. /package/skills/experience-ui-bundle-features-generate/references/{conflict-resolution-schema.json → common/conflict-resolution-schema.json} +0 -0
  274. /package/skills/{platform-agentsetup-categories-fetch → platform-agenticsetup-categories-get}/references/api-response-schema.md +0 -0
@@ -0,0 +1,96 @@
1
+ ---
2
+ name: service-omni-channel-inventory-analyze
3
+ description: "Use to inventory configured Omni-Channel instances across Voice, Email, Chat, SMS, Facebook, WhatsApp, and In-App, including each channel's Queue, Flow, or Other routing target, without changing the org. TRIGGER when: list Omni channels, inventory Omni routing, inspect channel routing targets, analyze Omni-Channel setup, or find configured service channels. Do not use to create, update, activate, or delete channels or routing configuration."
4
+ allowed-tools: Bash Read Write Grep Glob
5
+ metadata:
6
+ version: "1.0"
7
+ domains: ["Service"]
8
+ minApiVersion: "66.0"
9
+ relatedSkills:
10
+ - "service-omni-channel-setup-coordinate"
11
+ - "service-omni-queue-routing-config-deploy"
12
+ - "service-omni-routing-flow-deploy"
13
+ - "service-omni-service-channel-configure"
14
+ accessCheck:
15
+ - type: license
16
+ value: ServiceCloud
17
+ cliTools:
18
+ - tool: ["jq"]
19
+ semver: ">=1.6"
20
+ - tool: ["python3"]
21
+ semver: ">=3.8"
22
+ - tool: ["sf"]
23
+ semver: ">=2.139.6"
24
+ ---
25
+
26
+ # service-omni-channel-inventory-analyze
27
+
28
+ Read the org's complete Omni-Channel setup-home inventory without making changes. The supported headless operation combines Voice, Email, Live Chat, and Messaging channel families and returns their derived routing type and routing target. Use this analyzer before changing an unfamiliar org or when a user asks what Omni channels and routing are already configured.
29
+
30
+ ## Inputs
31
+
32
+ ```bash
33
+ bash scripts/analyze.sh <org-alias> [--type Voice|Email|Chat|SMS|Facebook|WhatsApp|InApp]
34
+ ```
35
+
36
+ - `org-alias` is required and must already be authenticated with Salesforce CLI.
37
+ - `--type` is optional and case-insensitive. It filters the returned inventory without changing the source data.
38
+
39
+ ## Preconditions and safety
40
+
41
+ - Target org authenticated through `sf` CLI.
42
+ - API v66.0 or later.
43
+ - The running user must have **View Setup and Configuration** and **Customize Application**.
44
+ - This skill is read-only. The shared Headless dispatcher uses an HTTP POST envelope, but the invoked `getChannelInstances` controller action performs no org mutation.
45
+ - Use `sf api request rest`; never extract or print an access token.
46
+
47
+ ## Run
48
+
49
+ Invoke `scripts/analyze.sh` with the target org and optional channel type. The script POSTs the approved `OmniChannelInstancesController.getChannelInstances` envelope to the shared Headless dispatcher, validates the inner response, optionally filters it, and returns a stable summary plus the matching channel instances.
50
+
51
+ The inventory includes these source fields when the platform supplies them:
52
+
53
+ | Field | Meaning |
54
+ |---|---|
55
+ | `id`, `label` | Channel identity and display label |
56
+ | `omniChannelInstanceType` | Voice, Email, Chat, SMS, Facebook, WhatsApp, or InApp |
57
+ | `routingType` | Queue, Flow, or Other |
58
+ | `routingRequirementId`, `routingRequirementLabel` | Queue, flow, or other routing target |
59
+ | `channelUrl`, `routingRequirementUrl` | Setup navigation links synthesized by the platform |
60
+ | `icon`, `messages` | Display metadata and platform-provided notices |
61
+
62
+ ## Output contract
63
+
64
+ The script emits one JSON object:
65
+
66
+ - `skill`: `service-omni-channel-inventory-analyze`
67
+ - `status`: `analyzed` or `blocked`
68
+ - `filter`: canonical channel type or `null`
69
+ - `summary.total_configured`: number of instances returned by the platform before filtering
70
+ - `summary.returned`: number of instances in `instances`
71
+ - `summary.by_channel_type`: counts for the returned inventory
72
+ - `summary.by_routing_type`: Queue, Flow, and Other counts for the returned inventory
73
+ - `instances`: normalized channel instance objects
74
+ - `blocking_issue`: actionable failure reason or `null`
75
+
76
+ An empty `instances` array with `status: analyzed` is a valid inventory result. Authentication, authorization, endpoint, or malformed-response failures return `status: blocked` and exit non-zero; never reinterpret them as an empty org.
77
+
78
+ ## Boundaries
79
+
80
+ - Do not replace this operation with independent SOQL queries. No single public object API reproduces the platform's cross-family aggregation, routing-type derivation, and setup URL generation.
81
+ - Do not infer missing routing fields. Preserve `null` when the platform does not supply a value.
82
+ - Do not use the returned setup URLs as mutation endpoints.
83
+ - For configuration changes, hand off to the appropriate leaf skill only after presenting the inventory to the user.
84
+
85
+ | Requested follow-up | Skill |
86
+ |---|---|
87
+ | Coordinate a complete Omni setup | `service-omni-channel-setup-coordinate` |
88
+ | Create or reuse a Service Channel | `service-omni-service-channel-configure` |
89
+ | Create or update a queue routing configuration | `service-omni-queue-routing-config-deploy` |
90
+ | Deploy a QueueBased or SkillsBased routing flow | `service-omni-routing-flow-deploy` |
91
+
92
+ ## References
93
+
94
+ | File | When to read |
95
+ |---|---|
96
+ | `references/api-notes.md` | Supported endpoint, permission boundary, response fields, and why the headless operation is canonical |
@@ -0,0 +1,52 @@
1
+ # Omni-Channel inventory API notes
2
+
3
+ ## Supported operation
4
+
5
+ ```text
6
+ POST /services/data/v66.0/headless/invoke
7
+ ```
8
+
9
+ Call it through `sf api request rest --method POST --target-org <alias>` with this invoke envelope:
10
+
11
+ ```json
12
+ {
13
+ "className": "ui.omnichannel.home.controller.OmniChannelInstancesController",
14
+ "action": "getChannelInstances"
15
+ }
16
+ ```
17
+
18
+ The Project Codey route is logically read-only even though the shared dispatcher transport is POST-only. Salesforce CLI owns authentication, so do not extract the org access token or build an Authorization header. Treat the inner `status_code` as authoritative and read the inventory from `body`.
19
+
20
+ Project Codey's approved `OmniChannelHome` source-of-record identifies this Aura-backed headless operation as canonical. The owning-team review was approved on 2026-08-20. The operation aggregates providers for Live Chat buttons, Messaging channels, Service channels, and Voice, then derives routing labels and setup URLs. Independent Connect, Tooling, Metadata, or sObject calls do not reproduce that contract.
21
+
22
+ ## Permission boundary
23
+
24
+ The running user needs both customer-facing permissions:
25
+
26
+ - View Setup and Configuration
27
+ - Customize Application
28
+
29
+ A permission failure is not evidence that the org has no channel instances. Return `status: blocked` and ask the user to run with an appropriately authorized setup user.
30
+
31
+ ## Response fields
32
+
33
+ Each channel instance can include:
34
+
35
+ ```text
36
+ id
37
+ label
38
+ channelUrl
39
+ omniChannelInstanceType
40
+ routingType
41
+ icon
42
+ routingRequirementId
43
+ routingRequirementLabel
44
+ routingRequirementUrl
45
+ messages
46
+ ```
47
+
48
+ Known channel-type labels are `Voice`, `Email`, `Chat`, `SMS`, `Facebook`, `WhatsApp`, and `InApp`. Known routing labels are `Queue`, `Flow`, and `Other`. The script accepts these labels case-insensitively and emits their canonical display spelling.
49
+
50
+ ## Response handling
51
+
52
+ The supported operation's contract is an array of channel instances. The analyzer also tolerates a raw array or the same array nested under a standard `body`, `result`, `data`, `items`, `records`, `value`, or `channelInstances` envelope so Salesforce CLI response wrapping does not silently turn valid results into failures. Any other shape is blocked as malformed.
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ usage() {
5
+ echo "Usage: bash analyze.sh <org-alias> [--type Voice|Email|Chat|SMS|Facebook|WhatsApp|InApp]" >&2
6
+ }
7
+
8
+ emit_blocked() {
9
+ local issue="$1"
10
+ jq -n \
11
+ --argjson filter "$FILTER_JSON" \
12
+ --arg issue "$issue" \
13
+ '{
14
+ skill: "service-omni-channel-inventory-analyze",
15
+ status: "blocked",
16
+ filter: $filter,
17
+ summary: {total_configured: null, returned: 0, by_channel_type: {}, by_routing_type: {}},
18
+ instances: [],
19
+ blocking_issue: $issue
20
+ }'
21
+ }
22
+
23
+ canonical_type() {
24
+ local normalized
25
+ normalized=$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')
26
+ case "$normalized" in
27
+ voice) echo "Voice" ;;
28
+ email) echo "Email" ;;
29
+ chat) echo "Chat" ;;
30
+ sms) echo "SMS" ;;
31
+ facebook) echo "Facebook" ;;
32
+ whatsapp) echo "WhatsApp" ;;
33
+ inapp) echo "InApp" ;;
34
+ *) return 1 ;;
35
+ esac
36
+ }
37
+
38
+ if [ $# -lt 1 ]; then
39
+ usage
40
+ exit 1
41
+ fi
42
+
43
+ ORG="$1"
44
+ shift
45
+ FILTER=""
46
+ FILTER_JSON="null"
47
+
48
+ while [ $# -gt 0 ]; do
49
+ case "$1" in
50
+ --type)
51
+ if [ $# -lt 2 ] || [ -z "$2" ]; then
52
+ usage
53
+ exit 1
54
+ fi
55
+ if ! FILTER=$(canonical_type "$2"); then
56
+ FILTER_JSON="null"
57
+ emit_blocked "Unsupported channel type '$2'. Use Voice, Email, Chat, SMS, Facebook, WhatsApp, or InApp."
58
+ exit 1
59
+ fi
60
+ FILTER_JSON=$(jq -n --arg value "$FILTER" '$value')
61
+ shift 2
62
+ ;;
63
+ --help|-h)
64
+ usage
65
+ exit 0
66
+ ;;
67
+ *)
68
+ usage
69
+ exit 1
70
+ ;;
71
+ esac
72
+ done
73
+
74
+ if ! FORCE_COLOR=0 sf org display --target-org "$ORG" --json >/dev/null 2>&1; then
75
+ emit_blocked "The target org is not authenticated or cannot be reached. Authenticate it with Salesforce CLI and retry."
76
+ exit 1
77
+ fi
78
+
79
+ WORK_DIR=$(mktemp -d "${TMPDIR:-/tmp}/omni-channel-inventory.XXXXXX")
80
+ trap 'rm -rf "$WORK_DIR"' EXIT
81
+ RAW_RESPONSE="$WORK_DIR/response.json"
82
+ ERROR_RESPONSE="$WORK_DIR/error.txt"
83
+ ENDPOINT="/services/data/v66.0/headless/invoke"
84
+ INVOKE_BODY=$(jq -cn '{
85
+ className:"ui.omnichannel.home.controller.OmniChannelInstancesController",
86
+ action:"getChannelInstances"
87
+ }')
88
+
89
+ if ! FORCE_COLOR=0 sf api request rest "$ENDPOINT" \
90
+ --method POST \
91
+ --body "$INVOKE_BODY" \
92
+ --target-org "$ORG" \
93
+ >"$RAW_RESPONSE" 2>"$ERROR_RESPONSE"; then
94
+ if grep -qiE 'insufficient|permission|access denied|forbidden' "$ERROR_RESPONSE"; then
95
+ emit_blocked "The inventory read was denied. Run it as a user with View Setup and Configuration and Customize Application."
96
+ else
97
+ emit_blocked "The supported Omni-Channel inventory operation failed. Verify org connectivity and feature availability, then retry."
98
+ fi
99
+ exit 1
100
+ fi
101
+
102
+ if ! jq -e . "$RAW_RESPONSE" >/dev/null 2>&1; then
103
+ emit_blocked "The inventory operation returned malformed JSON. No channel state was inferred."
104
+ exit 1
105
+ fi
106
+
107
+ INNER_STATUS=$(jq -r 'if type=="object" then .status_code // empty else empty end' "$RAW_RESPONSE")
108
+ if [ -n "$INNER_STATUS" ] && { ! [[ "$INNER_STATUS" =~ ^[0-9]+$ ]] || [ "$INNER_STATUS" -lt 200 ] || [ "$INNER_STATUS" -ge 300 ]; }; then
109
+ INNER_ERROR=$(jq -r '.body.message // .body[0].message // .body.error // "unknown error"' "$RAW_RESPONSE")
110
+ if printf '%s' "$INNER_ERROR" | grep -qiE 'insufficient|permission|access denied|forbidden'; then
111
+ emit_blocked "The inventory read was denied. Run it as a user with View Setup and Configuration and Customize Application."
112
+ else
113
+ emit_blocked "The supported Omni-Channel inventory operation failed: $INNER_ERROR."
114
+ fi
115
+ exit 1
116
+ fi
117
+
118
+ EXTRACT_FILTER='def inventory:
119
+ if type == "array" then .
120
+ elif (.body? | type) == "array" then .body
121
+ elif (.channelInstances? | type) == "array" then .channelInstances
122
+ elif (.result? | type) == "array" then .result
123
+ elif (.result?.channelInstances? | type) == "array" then .result.channelInstances
124
+ elif (.data? | type) == "array" then .data
125
+ elif (.items? | type) == "array" then .items
126
+ elif (.records? | type) == "array" then .records
127
+ elif (.value? | type) == "array" then .value
128
+ else null
129
+ end;
130
+ inventory'
131
+
132
+ if ! jq -e "$EXTRACT_FILTER | type == \"array\" and all(.[]; type == \"object\")" "$RAW_RESPONSE" >/dev/null 2>&1; then
133
+ emit_blocked "The inventory operation returned an unsupported response shape. No channel state was inferred."
134
+ exit 1
135
+ fi
136
+
137
+ jq \
138
+ --arg filter "$FILTER" \
139
+ "$EXTRACT_FILTER as \$source |
140
+ def channel_type:
141
+ (. // null) as \$value |
142
+ if (\$value | type) != \"string\" then null
143
+ elif (\$value | ascii_downcase) == \"voice\" then \"Voice\"
144
+ elif (\$value | ascii_downcase) == \"email\" then \"Email\"
145
+ elif (\$value | ascii_downcase) == \"chat\" then \"Chat\"
146
+ elif (\$value | ascii_downcase) == \"sms\" then \"SMS\"
147
+ elif (\$value | ascii_downcase) == \"facebook\" then \"Facebook\"
148
+ elif (\$value | ascii_downcase) == \"whatsapp\" then \"WhatsApp\"
149
+ elif (\$value | ascii_downcase) == \"inapp\" then \"InApp\"
150
+ else \$value
151
+ end;
152
+ def routing_type:
153
+ (. // null) as \$value |
154
+ if (\$value | type) != \"string\" then null
155
+ elif (\$value | ascii_downcase) == \"queue\" then \"Queue\"
156
+ elif (\$value | ascii_downcase) == \"flow\" then \"Flow\"
157
+ elif (\$value | ascii_downcase) == \"other\" then \"Other\"
158
+ else \$value
159
+ end;
160
+ [\$source[] | {
161
+ id: (.id // null),
162
+ label: (.label // null),
163
+ channelUrl: (.channelUrl // null),
164
+ omniChannelInstanceType: (.omniChannelInstanceType | channel_type),
165
+ routingType: (.routingType | routing_type),
166
+ icon: (.icon // null),
167
+ routingRequirementId: (.routingRequirementId // null),
168
+ routingRequirementLabel: (.routingRequirementLabel // null),
169
+ routingRequirementUrl: (.routingRequirementUrl // null),
170
+ messages: (if (.messages | type) == \"array\" then .messages else [] end)
171
+ }] as \$all |
172
+ (if \$filter == \"\" then \$all else [\$all[] | select(.omniChannelInstanceType == \$filter)] end) as \$returned |
173
+ {
174
+ skill: \"service-omni-channel-inventory-analyze\",
175
+ status: \"analyzed\",
176
+ filter: (if \$filter == \"\" then null else \$filter end),
177
+ summary: {
178
+ total_configured: (\$all | length),
179
+ returned: (\$returned | length),
180
+ by_channel_type: (reduce \$returned[] as \$item ({}; .[(\$item.omniChannelInstanceType // \"Unknown\")] += 1)),
181
+ by_routing_type: (reduce \$returned[] as \$item ({}; .[(\$item.routingType // \"Unknown\")] += 1))
182
+ },
183
+ instances: \$returned,
184
+ blocking_issue: null
185
+ }" "$RAW_RESPONSE"
@@ -0,0 +1,84 @@
1
+ """Fake Salesforce CLI harness for the Omni-Channel inventory analyzer."""
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ import os
6
+ import shutil
7
+ import subprocess
8
+ import tempfile
9
+ from pathlib import Path
10
+
11
+ SKILLS_ROOT = Path(__file__).resolve().parents[3]
12
+ SCRIPT = SKILLS_ROOT / "service-omni-channel-inventory-analyze/scripts/analyze.sh"
13
+
14
+ _FAKE_SF = r'''#!/usr/bin/env bash
15
+ command_name="${1:-} ${2:-}"
16
+ case "$command_name" in
17
+ "org display")
18
+ if [ "${FAKE_AUTH:-ok}" = "fail" ]; then
19
+ echo '{"status":1,"message":"not authenticated"}' >&2
20
+ exit 1
21
+ fi
22
+ echo '{"status":0,"result":{}}'
23
+ ;;
24
+ "api request")
25
+ printf '%s\n' "$*" >> "${FAKE_SF_LOG:?}"
26
+ if [ "${FAKE_API_STATUS:-ok}" = "fail" ]; then
27
+ echo "${FAKE_API_ERROR:-request failed}" >&2
28
+ exit 1
29
+ fi
30
+ printf '%s\n' "${FAKE_API_BODY:-[]}"
31
+ ;;
32
+ *)
33
+ echo '{"status":1,"message":"unexpected command"}' >&2
34
+ exit 1
35
+ ;;
36
+ esac
37
+ '''
38
+
39
+
40
+ class FakeSf:
41
+ def __enter__(self):
42
+ self._directory = tempfile.mkdtemp(prefix="omni-inventory-fake-sf-")
43
+ self.log_path = Path(self._directory) / "commands.log"
44
+ sf_path = Path(self._directory) / "sf"
45
+ sf_path.write_text(_FAKE_SF)
46
+ sf_path.chmod(0o755)
47
+ return self
48
+
49
+ def run(self, args: list[str], **values):
50
+ env = dict(os.environ)
51
+ env["PATH"] = self._directory + os.pathsep + env.get("PATH", "")
52
+ env["FAKE_SF_LOG"] = str(self.log_path)
53
+ for key, value in values.items():
54
+ env[key] = json.dumps(value) if key == "FAKE_API_BODY" and not isinstance(value, str) else str(value)
55
+ process = subprocess.run(
56
+ ["bash", str(SCRIPT), *args],
57
+ capture_output=True,
58
+ text=True,
59
+ env=env,
60
+ cwd=str(SKILLS_ROOT),
61
+ )
62
+ commands = self.log_path.read_text() if self.log_path.exists() else ""
63
+ return process.returncode, process.stdout + process.stderr, commands
64
+
65
+ def __exit__(self, *_):
66
+ shutil.rmtree(self._directory, ignore_errors=True)
67
+
68
+
69
+ def last_json(output: str) -> dict:
70
+ decoder = json.JSONDecoder()
71
+ parsed = None
72
+ position = 0
73
+ while position < len(output):
74
+ if output[position] == "{":
75
+ try:
76
+ candidate, end = decoder.raw_decode(output, position)
77
+ parsed = candidate
78
+ position = end
79
+ continue
80
+ except json.JSONDecodeError:
81
+ pass
82
+ position += 1
83
+ assert parsed is not None, f"No JSON object found in output:\n{output}"
84
+ return parsed
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import unittest
5
+
6
+ from _bootstrap import FakeSf, last_json
7
+
8
+
9
+ CHANNELS = [
10
+ {
11
+ "id": "0N9xx0000000001AAA",
12
+ "label": "Case Email",
13
+ "channelUrl": "/lightning/setup/ServiceChannelSettings/page?address=one",
14
+ "omniChannelInstanceType": "EMAIL",
15
+ "routingType": "QUEUE",
16
+ "routingRequirementId": "00Gxx0000000001AAA",
17
+ "routingRequirementLabel": "Case Queue",
18
+ "routingRequirementUrl": "/lightning/setup/Queues/page?address=one",
19
+ "messages": [],
20
+ },
21
+ {
22
+ "id": "0M0xx0000000002AAA",
23
+ "label": "Support Voice",
24
+ "omniChannelInstanceType": "Voice",
25
+ "routingType": "Flow",
26
+ "routingRequirementLabel": "Voice Routing",
27
+ "messages": [{"severity": "info"}],
28
+ },
29
+ ]
30
+
31
+
32
+ class InputContractTests(unittest.TestCase):
33
+ def test_missing_org_is_rejected(self):
34
+ with FakeSf() as fake:
35
+ return_code, output, commands = fake.run([])
36
+ self.assertEqual(return_code, 1, output)
37
+ self.assertIn("Usage", output)
38
+ self.assertEqual(commands, "")
39
+
40
+ def test_invalid_type_is_blocked_before_api_call(self):
41
+ with FakeSf() as fake:
42
+ return_code, output, commands = fake.run(["org", "--type", "CarrierPigeon"])
43
+ self.assertEqual(return_code, 1, output)
44
+ result = last_json(output)
45
+ self.assertEqual(result["status"], "blocked")
46
+ self.assertIn("Unsupported channel type", result["blocking_issue"])
47
+ self.assertEqual(commands, "")
48
+
49
+ def test_unauthenticated_org_is_blocked(self):
50
+ with FakeSf() as fake:
51
+ return_code, output, commands = fake.run(["org"], FAKE_AUTH="fail")
52
+ self.assertEqual(return_code, 1, output)
53
+ self.assertEqual(last_json(output)["status"], "blocked")
54
+ self.assertEqual(commands, "")
55
+
56
+
57
+ class InventoryContractTests(unittest.TestCase):
58
+ def test_raw_array_is_normalized_and_summarized(self):
59
+ with FakeSf() as fake:
60
+ return_code, output, commands = fake.run(["org"], FAKE_API_BODY=CHANNELS)
61
+ self.assertEqual(return_code, 0, output)
62
+ result = last_json(output)
63
+ self.assertEqual(result["status"], "analyzed")
64
+ self.assertEqual(result["summary"]["total_configured"], 2)
65
+ self.assertEqual(result["summary"]["returned"], 2)
66
+ self.assertEqual(result["summary"]["by_channel_type"], {"Email": 1, "Voice": 1})
67
+ self.assertEqual(result["summary"]["by_routing_type"], {"Flow": 1, "Queue": 1})
68
+ self.assertIn("--method POST", commands)
69
+ self.assertIn(
70
+ "/services/data/v66.0/headless/invoke",
71
+ commands,
72
+ )
73
+ self.assertIn("OmniChannelInstancesController", commands)
74
+ self.assertIn("getChannelInstances", commands)
75
+ self.assertNotRegex(commands, r"--method (GET|PATCH|DELETE)")
76
+
77
+ def test_direct_invocation_envelope_is_unwrapped(self):
78
+ with FakeSf() as fake:
79
+ return_code, output, _ = fake.run(
80
+ ["org"],
81
+ FAKE_API_BODY={"status_code": 200, "body": CHANNELS},
82
+ )
83
+ self.assertEqual(return_code, 0, output)
84
+ result = last_json(output)
85
+ self.assertEqual(result["status"], "analyzed")
86
+ self.assertEqual(result["summary"]["total_configured"], 2)
87
+
88
+ def test_type_filter_is_case_insensitive(self):
89
+ with FakeSf() as fake:
90
+ return_code, output, _ = fake.run(["org", "--type", "voice"], FAKE_API_BODY={"result": CHANNELS})
91
+ self.assertEqual(return_code, 0, output)
92
+ result = last_json(output)
93
+ self.assertEqual(result["filter"], "Voice")
94
+ self.assertEqual(result["summary"]["total_configured"], 2)
95
+ self.assertEqual(result["summary"]["returned"], 1)
96
+ self.assertEqual(result["instances"][0]["label"], "Support Voice")
97
+
98
+ def test_empty_inventory_is_valid(self):
99
+ with FakeSf() as fake:
100
+ return_code, output, _ = fake.run(["org"], FAKE_API_BODY=[])
101
+ self.assertEqual(return_code, 0, output)
102
+ result = last_json(output)
103
+ self.assertEqual(result["status"], "analyzed")
104
+ self.assertEqual(result["instances"], [])
105
+ self.assertEqual(result["summary"]["total_configured"], 0)
106
+
107
+ def test_permission_failure_is_not_reported_as_empty(self):
108
+ with FakeSf() as fake:
109
+ return_code, output, _ = fake.run(
110
+ ["org"],
111
+ FAKE_API_STATUS="fail",
112
+ FAKE_API_ERROR="INSUFFICIENT_ACCESS: permission denied",
113
+ )
114
+ self.assertEqual(return_code, 1, output)
115
+ result = last_json(output)
116
+ self.assertEqual(result["status"], "blocked")
117
+ self.assertIsNone(result["summary"]["total_configured"])
118
+ self.assertIn("View Setup", result["blocking_issue"])
119
+
120
+ def test_inner_dispatch_failure_is_blocked(self):
121
+ with FakeSf() as fake:
122
+ return_code, output, _ = fake.run(
123
+ ["org"],
124
+ FAKE_API_BODY={"status_code": 403, "body": {"message": "permission denied"}},
125
+ )
126
+ self.assertEqual(return_code, 1, output)
127
+ result = last_json(output)
128
+ self.assertEqual(result["status"], "blocked")
129
+ self.assertIn("View Setup", result["blocking_issue"])
130
+
131
+ def test_malformed_response_is_blocked(self):
132
+ with FakeSf() as fake:
133
+ return_code, output, _ = fake.run(["org"], FAKE_API_BODY={"unexpected": "shape"})
134
+ self.assertEqual(return_code, 1, output)
135
+ result = last_json(output)
136
+ self.assertEqual(result["status"], "blocked")
137
+ self.assertIn("unsupported response shape", result["blocking_issue"])
138
+
139
+
140
+ if __name__ == "__main__":
141
+ unittest.main(verbosity=2)
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: service-omni-channel-limits-analyze
3
+ description: "Use to report Omni-Channel Pending Service Routing usage without changing the org: query the current PendingServiceRouting count, compare it with an admin-supplied maximum or a labeled routing-model reference default, and explain which live values are unavailable through supported APIs. TRIGGER when: check Omni-Channel limits, inspect PSR limits, analyze PendingServiceRouting usage, review pending routing capacity, or report queued work usage. Do not use to change limits or scrape Salesforce Setup."
4
+ allowed-tools: Bash Read Write Grep Glob
5
+ metadata:
6
+ version: "1.0"
7
+ domains: ["Service"]
8
+ minApiVersion: "66.0"
9
+ relatedSkills:
10
+ - "service-omni-channel-inventory-analyze"
11
+ - "service-omni-channel-setup-coordinate"
12
+ accessCheck:
13
+ - type: license
14
+ value: ServiceCloud
15
+ cliTools:
16
+ - tool: ["jq"]
17
+ semver: ">=1.6"
18
+ - tool: ["python3"]
19
+ semver: ">=3.8"
20
+ - tool: ["sf"]
21
+ semver: ">=2.139.6"
22
+ ---
23
+
24
+ # service-omni-channel-limits-analyze
25
+
26
+ Report the Pending Service Routing usage that can be obtained safely through supported customer-org APIs. The Salesforce **Setup → Omni-Channel → Limits** page is read-only and also displays internal values that are not exposed through REST, Tooling, Metadata, or Connect APIs. This skill reports the live PSR count and labels every non-verifiable value explicitly.
27
+
28
+ ## Inputs
29
+
30
+ ```bash
31
+ bash scripts/analyze.sh <org-alias> [--routing-model enhanced|legacy] [--max-psrs <integer>]
32
+ ```
33
+
34
+ - `org-alias` is required and must already be authenticated with Salesforce CLI.
35
+ - `--routing-model` defaults to `enhanced`. It selects only the reference maximum used when `--max-psrs` is absent.
36
+ - `--max-psrs` accepts an administrator-provided maximum from 1 through 1,000,000. It is still labeled as not API-verified.
37
+
38
+ ## Run
39
+
40
+ Invoke `scripts/analyze.sh` with the target org and rely on its JSON result for the live count, labeled comparison values, unavailable hourly values, and blocking details. The script invokes `sf org display` and `sf data query` internally; do not reproduce their query or calculation logic outside the script.
41
+
42
+ To verify the bundled script contract after a change, run `python3 scripts/tests/test_limits_analyze_contracts.py`; the test module loads its local `scripts/tests/_bootstrap.py` fixture automatically.
43
+
44
+ ## Output contract
45
+
46
+ The script emits one JSON object:
47
+
48
+ - `skill`: `service-omni-channel-limits-analyze`
49
+ - `status`: `analyzed` or `blocked`
50
+ - `routing_model`: `enhanced` or `legacy`
51
+ - `metrics.current_pending_service_routings`: live count, comparison maximum, approximate percentage, and source labels
52
+ - `metrics.pending_service_routing_rate_per_hour`: unavailable live value, reference maximum, and source labels
53
+ - `caveats`: mandatory accuracy boundaries
54
+ - `blocking_issue`: actionable failure reason or `null`
55
+
56
+ If `PendingServiceRouting` cannot be queried, return `status: blocked`. Do not reinterpret an unavailable object, missing feature, or permission failure as zero usage.
57
+
58
+ ## Boundaries
59
+
60
+ - Never scrape, replay, or submit the Visualforce Setup page. Its session state and controller are not a supported customer API.
61
+ - Never describe a reference default as the org's configured maximum.
62
+ - Never fabricate the live hourly rate or compute its utilization percentage.
63
+ - Never mutate routing, queues, agents, limits, or metadata.
64
+ - Treat the PSR count as a point-in-time snapshot that can change while work is routed.
65
+ - For a complete channel and routing inventory, use `service-omni-channel-inventory-analyze`.
66
+ - For a user-approved end-to-end Omni setup, hand off to `service-omni-channel-setup-coordinate`; this limits analyzer remains read-only.
67
+
68
+ ## Reference
69
+
70
+ Read `references/api-notes.md` when explaining why the report cannot exactly reproduce every value on the Setup page.
@@ -0,0 +1,22 @@
1
+ # Omni-Channel limits API notes
2
+
3
+ ## Supported value
4
+
5
+ `SELECT COUNT() FROM PendingServiceRouting` provides a live, point-in-time PSR row count when Omni-Channel is enabled and the running user can access the object.
6
+
7
+ ## Values not exposed by a supported API
8
+
9
+ The read-only Omni-Channel Limits Setup page also displays the org's effective maximum PSR count and a rolling hourly enqueue meter. Those values are read by Salesforce's internal page controller and are not exposed as fields on `PendingServiceRouting`, Tooling objects, Metadata types, Connect resources, or the standard REST org-limits response.
10
+
11
+ The skill therefore uses these values only as labeled references:
12
+
13
+ | Routing model | Reference maximum PSRs |
14
+ |---|---:|
15
+ | Enhanced | 300,000 |
16
+ | Legacy | 200,000 |
17
+
18
+ The reference hourly maximum is 45,000. Actual org values can differ, and live hourly usage is not retrievable. An administrator can supply a known PSR maximum with `--max-psrs`, but the script still records that it was not verified through an API.
19
+
20
+ ## Full-fidelity product path
21
+
22
+ Exact reporting requires a Salesforce Core product API that exposes both maximums and the rolling hourly meter, ideally through the standard REST org-limits resource. Until that contract exists, do not scrape Setup HTML or model a fictional limits sObject.