@salesforce/afv-skills 1.37.0 → 1.38.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 (247) hide show
  1. package/package.json +1 -1
  2. package/skills/agentforce-generate/README.md +20 -3
  3. package/skills/agentforce-generate/SKILL.md +253 -440
  4. package/skills/agentforce-generate/assets/agent-spec-template.md +6 -2
  5. package/skills/agentforce-generate/assets/agents/order-service.agent +16 -12
  6. package/skills/agentforce-generate/assets/agents/production-faq.agent +4 -4
  7. package/skills/agentforce-generate/assets/agents/router-first.agent +5 -5
  8. package/skills/agentforce-generate/assets/agents/template-single-subagent.agent +4 -4
  9. package/skills/agentforce-generate/assets/agents/verification-gate.agent +8 -6
  10. package/skills/agentforce-generate/assets/agents/voice-knowledge-grounded.agent +9 -9
  11. package/skills/agentforce-generate/assets/agents/voice-service-agent.agent +7 -7
  12. package/skills/agentforce-generate/assets/patterns/README.md +3 -3
  13. package/skills/agentforce-generate/assets/patterns/action-callbacks.agent +5 -5
  14. package/skills/agentforce-generate/assets/patterns/advanced-input-bindings.agent +6 -8
  15. package/skills/agentforce-generate/assets/patterns/bidirectional-routing.agent +11 -13
  16. package/skills/agentforce-generate/assets/patterns/critical-input-collection.agent +11 -16
  17. package/skills/agentforce-generate/assets/patterns/lifecycle-events.agent +2 -3
  18. package/skills/agentforce-generate/assets/patterns/llm-controlled-actions.agent +6 -7
  19. package/skills/agentforce-generate/assets/patterns/open-gate-routing.agent +3 -3
  20. package/skills/agentforce-generate/assets/patterns/prompt-template-action.agent +14 -19
  21. package/skills/agentforce-generate/assets/patterns/system-instruction-overrides.agent +11 -20
  22. package/skills/agentforce-generate/references/actions-reference.md +2 -2
  23. package/skills/agentforce-generate/references/agent-audit-and-repair.md +135 -0
  24. package/skills/agentforce-generate/references/agent-audit-candidate-verification.md +160 -0
  25. package/skills/agentforce-generate/references/agent-audit-diagnostic-catalog.md +156 -0
  26. package/skills/agentforce-generate/references/agent-audit-diagnostics-actions-state.md +176 -0
  27. package/skills/agentforce-generate/references/agent-audit-diagnostics-architecture-evaluation.md +68 -0
  28. package/skills/agentforce-generate/references/agent-audit-diagnostics-instructions-routing.md +283 -0
  29. package/skills/agentforce-generate/references/agent-audit-evaluation-loop.md +191 -0
  30. package/skills/agentforce-generate/references/agent-audit-repair-report.md +143 -0
  31. package/skills/agentforce-generate/references/agent-audit-scope-path-review.md +180 -0
  32. package/skills/agentforce-generate/references/agent-design-and-spec-creation.md +105 -59
  33. package/skills/agentforce-generate/references/agent-script-core-language.md +144 -61
  34. package/skills/agentforce-generate/references/agent-subagent-map-diagrams.md +33 -23
  35. package/skills/agentforce-generate/references/agent-validation-and-debugging.md +37 -11
  36. package/skills/agentforce-generate/references/agentscript-toolchain.md +112 -0
  37. package/skills/agentforce-generate/references/architecture-patterns.md +81 -18
  38. package/skills/agentforce-generate/references/common-control-flow-pitfalls.md +255 -0
  39. package/skills/agentforce-generate/references/control-flow-actions-sequencing.md +198 -0
  40. package/skills/agentforce-generate/references/control-flow-lifecycle-side-effects.md +87 -0
  41. package/skills/agentforce-generate/references/examples.md +22 -22
  42. package/skills/agentforce-generate/references/instruction-resolution.md +123 -83
  43. package/skills/agentforce-generate/references/known-issues.md +1 -2
  44. package/skills/agentforce-generate/references/optimization-pattern-1-data-flow.md +4 -0
  45. package/skills/agentforce-generate/references/optimization-pattern-2-deterministic-logic.md +33 -0
  46. package/skills/agentforce-generate/references/optimization-pattern-3-reference-syntax.md +32 -3
  47. package/skills/agentforce-generate/references/optimization-pattern-4-escalation.md +2 -2
  48. package/skills/agentforce-generate/references/patterns-by-requirement.md +3 -1
  49. package/skills/agentforce-generate/references/posture-and-determinism.md +103 -22
  50. package/skills/agentforce-generate/references/reference-map.md +19 -3
  51. package/skills/agentforce-generate/references/scoring-rubric.md +1 -1
  52. package/skills/agentforce-generate/references/voice-latency-heuristics.md +4 -0
  53. package/skills/agentforce-generate/references/voice-modality-reference.md +4 -4
  54. package/skills/agentforce-generate/references/zen-of-agentscript.md +139 -23
  55. package/skills/agentforce-generate/scripts/agentscript-sdk-loader.mjs +133 -0
  56. package/skills/agentforce-generate/scripts/index-agent.mjs +141 -0
  57. package/skills/agentforce-generate/scripts/setup-agentscript-sdk.mjs +337 -0
  58. package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +1 -1
  59. package/skills/automation-sandbox-post-copy-configure/SKILL.md +433 -0
  60. package/skills/automation-sandbox-post-copy-configure/assets/api_request_templates.json +89 -0
  61. package/skills/automation-sandbox-post-copy-configure/examples/sample_config_input.json +40 -0
  62. package/skills/automation-sandbox-post-copy-configure/examples/sample_execution_summary.md +79 -0
  63. package/skills/automation-sandbox-post-copy-configure/references/api_endpoints.md +273 -0
  64. package/skills/automation-sandbox-post-copy-configure/references/authentication.md +94 -0
  65. package/skills/automation-sandbox-post-copy-configure/references/execution_phasing.md +93 -0
  66. package/skills/automation-sandbox-post-copy-configure/references/rules_gotchas.md +35 -0
  67. package/skills/automation-sandbox-post-copy-configure/scripts/classify-patch-result.mjs +88 -0
  68. package/skills/automation-sandbox-post-copy-configure/scripts/map-metadata-key.mjs +98 -0
  69. package/skills/automation-sandbox-post-copy-configure/scripts/plan-phases.mjs +98 -0
  70. package/skills/automation-sandbox-post-copy-configure/scripts/resolve-target-org.mjs +56 -0
  71. package/skills/design-systems-slds-validate/SKILL.md +14 -13
  72. package/skills/dx-code-analyzer-custom-rule-create/examples/xpath-examples.md +1 -1
  73. package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-security.md +1 -1
  74. package/skills/dx-org-devhub-configure/SKILL.md +268 -0
  75. package/skills/dx-org-devhub-configure/examples/status-output.md +39 -0
  76. package/skills/dx-org-devhub-configure/scripts/devhub.sh +650 -0
  77. package/skills/dx-org-devhub-configure/scripts/test-devhub.sh +116 -0
  78. package/skills/dx-org-manage/SKILL.md +135 -42
  79. package/skills/dx-org-manage/assets/derive-alias.sh +95 -0
  80. package/skills/dx-org-manage/assets/scratch-def.seed.json +6 -0
  81. package/skills/dx-org-manage/examples/README.md +1 -1
  82. package/skills/dx-org-manage/examples/scratch-orgs/delete_output.json +8 -0
  83. package/skills/dx-org-manage/examples/scratch-orgs/display_output.json +23 -0
  84. package/skills/dx-org-manage/examples/scratch-orgs/list_output.json +58 -0
  85. package/skills/dx-org-manage/examples/scratch-orgs/resume_output.json +38 -0
  86. package/skills/dx-org-manage/examples/scratch-orgs/success_definition_file.json +1 -1
  87. package/skills/dx-org-manage/examples/scratch-orgs/success_edition.json +1 -1
  88. package/skills/dx-org-manage/examples/scratch-orgs/success_shape.json +41 -0
  89. package/skills/dx-org-manage/examples/scratch-orgs/success_snapshot.json +1 -1
  90. package/skills/dx-org-manage/examples/snapshots/error_output.json +3 -3
  91. package/skills/dx-org-manage/references/creating-scratch-org.md +2 -2
  92. package/skills/dx-org-manage/references/creating-snapshot.md +1 -2
  93. package/skills/dx-org-manage/references/definition_file_options.md +24 -0
  94. package/skills/dx-org-manage/references/edition_types.md +10 -8
  95. package/skills/dx-org-manage/references/opening-org.md +11 -12
  96. package/skills/dx-org-manage/references/scratch-org-create.md +303 -0
  97. package/skills/dx-org-manage/references/scratch-org-operations.md +135 -0
  98. package/skills/experience-aura-lwc-migrate/SKILL.md +120 -0
  99. package/skills/experience-aura-lwc-migrate/references/aura-api-expert.md +170 -0
  100. package/skills/experience-aura-lwc-migrate/references/aura-data-expert.md +172 -0
  101. package/skills/experience-aura-lwc-migrate/references/aura-migration-guidelines.md +299 -0
  102. package/skills/experience-aura-lwc-migrate/references/aura-prd-framework.md +79 -0
  103. package/skills/experience-aura-lwc-migrate/references/aura-redundant-code-expert.md +24 -0
  104. package/skills/experience-aura-lwc-migrate/references/aura-reference-expert.md +140 -0
  105. package/skills/experience-aura-lwc-migrate/references/aura-resolver-expert.md +115 -0
  106. package/skills/experience-aura-lwc-migrate/references/aura-slots-expert.md +67 -0
  107. package/skills/experience-aura-lwc-migrate/references/aura-style-expert.md +62 -0
  108. package/skills/experience-aura-lwc-migrate/references/aura-to-lwc-completeness-checklist.md +188 -0
  109. package/skills/experience-aura-lwc-migrate/references/aura-values-expert.md +67 -0
  110. package/skills/experience-content-media-search/SKILL.md +17 -12
  111. package/skills/experience-content-media-stock-image-search/SKILL.md +192 -0
  112. package/skills/experience-content-media-stock-image-search/scripts/download-stock-image.py +102 -0
  113. package/skills/experience-lds-best-practices-apply/SKILL.md +245 -0
  114. package/skills/experience-lds-best-practices-apply/references/adapter-apis.md +1640 -0
  115. package/skills/experience-lds-best-practices-apply/references/lds-data-consistency.md +126 -0
  116. package/skills/experience-lds-best-practices-apply/references/lds-expert.md +429 -0
  117. package/skills/experience-lds-best-practices-apply/references/lds-referential-integrity.md +322 -0
  118. package/skills/experience-lds-best-practices-apply/references/wire-adapter-types.md +1511 -0
  119. package/skills/experience-lds-graphql-generate/SKILL.md +222 -0
  120. package/skills/experience-lds-graphql-generate/references/generation-guide.md +236 -0
  121. package/skills/experience-lds-graphql-generate/references/generation-mutation.md +277 -0
  122. package/skills/experience-lds-graphql-generate/references/generation-query.md +237 -0
  123. package/skills/experience-lds-graphql-generate/scripts/fetch-lds-graphql-schema.sh +230 -0
  124. package/skills/experience-lds-graphql-generate/scripts/test-lds-graphql-query.sh +128 -0
  125. package/skills/experience-lwc-accessibility-validate/SKILL.md +112 -0
  126. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-1-1-1-non-text-content.md +88 -0
  127. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-1-3-1-i-lists.md +52 -0
  128. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-1-3-1-ii-tables.md +106 -0
  129. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-1-3-1-iii-form-labels.md +78 -0
  130. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-1-3-1-iv-regions.md +26 -0
  131. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-1-3-1-v-groups.md +66 -0
  132. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-1-3-5-identify-input.md +70 -0
  133. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-1-4-3-contrast.md +115 -0
  134. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-2-1-1-keyboard.md +47 -0
  135. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-2-4-4-link-purpose.md +39 -0
  136. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-2-4-6-headings-labels.md +50 -0
  137. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-2-5-1-pointer-gestures.md +54 -0
  138. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-2-5-2-pointer-cancellation.md +49 -0
  139. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-2-5-3-label-in-name.md +77 -0
  140. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-2-5-7-dragging-movement.md +45 -0
  141. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-3-2-1-on-focus.md +55 -0
  142. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-3-2-2-on-input.md +51 -0
  143. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-3-3-1-error-identification.md +84 -0
  144. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-3-3-2-labels-instructions.md +50 -0
  145. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-3-3-3-error-suggestion.md +64 -0
  146. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-4-1-2-i-name.md +105 -0
  147. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-4-1-2-ii-role.md +99 -0
  148. package/skills/experience-lwc-accessibility-validate/references/reviewers/sc-4-1-2-iii-value.md +97 -0
  149. package/skills/experience-lwc-accessibility-validate/references/vision/sc-1-1-1-non-text-content.md +83 -0
  150. package/skills/experience-lwc-accessibility-validate/references/vision/sc-1-4-1-use-of-color.md +62 -0
  151. package/skills/experience-lwc-accessibility-validate/references/vision/sc-1-4-10-resize-reflow.md +18 -0
  152. package/skills/experience-lwc-accessibility-validate/references/vision/sc-1-4-11-non-text-contrast.md +43 -0
  153. package/skills/experience-lwc-accessibility-validate/references/vision/sc-1-4-3-contrast.md +25 -0
  154. package/skills/experience-lwc-accessibility-validate/scripts/contrast-ratio.py +153 -0
  155. package/skills/experience-lwc-design-generate/SKILL.md +261 -0
  156. package/skills/experience-lwc-design-generate/references/figma-to-prd-blueprint.md +66 -0
  157. package/skills/experience-lwc-design-generate/references/prd-analysis-template.md +17 -0
  158. package/skills/experience-lwc-design-generate/scripts/check-component-name.sh +75 -0
  159. package/skills/experience-lwc-design-generate/scripts/detect-project-tools.sh +97 -0
  160. package/skills/experience-lwc-runtime-observe/SKILL.md +244 -0
  161. package/skills/experience-lwc-runtime-observe/examples/component-preview-and-dom.md +41 -0
  162. package/skills/experience-lwc-runtime-observe/scripts/extract-dom.sh +79 -0
  163. package/skills/experience-lwc-runtime-observe/scripts/open-frontdoor.sh +46 -0
  164. package/skills/experience-lwc-runtime-observe/scripts/verify-toolchain.sh +48 -0
  165. package/skills/experience-lwc-security-validate/SKILL.md +151 -0
  166. package/skills/experience-lwc-security-validate/examples/review-report.md +6 -0
  167. package/skills/experience-lwc-security-validate/examples/score-report.sarif.json +32 -0
  168. package/skills/experience-lwc-security-validate/references/lws-security-expert.md +986 -0
  169. package/skills/experience-lwc-security-validate/references/security-analysis.md +611 -0
  170. package/skills/experience-lwc-security-validate/scripts/check-lwc-import.sh +166 -0
  171. package/skills/experience-lwc-security-validate/scripts/validate-sarif.sh +131 -0
  172. package/skills/experience-ui-bundle-2gp-deploy/SKILL.md +454 -0
  173. package/skills/experience-ui-bundle-2gp-deploy/assets/CustomApplication.app-meta.xml +12 -0
  174. package/skills/experience-ui-bundle-2gp-deploy/assets/PermissionSet.permissionset-meta.xml +9 -0
  175. package/skills/experience-ui-bundle-2gp-deploy/scripts/find-bundle-package-dir.sh +53 -0
  176. package/skills/experience-ui-bundle-agentforce-client-generate/SKILL.md +104 -26
  177. package/skills/experience-ui-bundle-agentforce-client-generate/references/constraints.md +35 -24
  178. package/skills/experience-ui-bundle-agentforce-client-generate/references/examples.md +92 -1
  179. package/skills/experience-ui-bundle-agentforce-client-generate/references/style-tokens.md +2 -0
  180. package/skills/experience-ui-bundle-agentforce-client-generate/references/troubleshooting.md +14 -1
  181. package/skills/experience-ui-bundle-agentforce-client-generate/scripts/detect-framework.sh +67 -0
  182. package/skills/experience-ui-bundle-deploy/SKILL.md +83 -14
  183. package/skills/experience-ui-bundle-deploy/assets/Communities.settings-meta.xml +19 -0
  184. package/skills/experience-ui-bundle-deploy/assets/org-setup.config.template.json +5 -0
  185. package/skills/experience-ui-bundle-deploy/assets/social-login-auth-providers.apex +158 -0
  186. package/skills/experience-ui-bundle-deploy/references/config-scaffold.md +13 -1
  187. package/skills/experience-ui-bundle-deploy/references/social-login.md +179 -0
  188. package/skills/experience-ui-bundle-frontend-generate/SKILL.md +14 -0
  189. package/skills/experience-ui-bundle-mfa-configure/SKILL.md +6 -5
  190. package/skills/experience-ui-bundle-mfa-configure/references/social-login.md +15 -7
  191. package/skills/experience-ui-bundle-salesforce-data-access/SKILL.md +41 -5
  192. package/skills/experience-ui-bundle-salesforce-data-access/references/caching.md +10 -22
  193. package/skills/experience-ui-bundle-salesforce-data-access/references/graphql-hand-authoring.md +4 -19
  194. package/skills/experience-ui-bundle-salesforce-data-access/references/migration.md +5 -0
  195. package/skills/experience-ui-bundle-salesforce-data-access/references/sdk-api.md +33 -150
  196. package/skills/mobile-platform-native-capabilities-integrate/references/nfc.md +35 -0
  197. package/skills/platform-apex-generate/SKILL.md +8 -7
  198. package/skills/platform-apex-test-generate/SKILL.md +3 -1
  199. package/skills/platform-apex-test-run/SKILL.md +9 -8
  200. package/skills/platform-lightning-type-widget-coordinate/SKILL.md +1 -1
  201. package/skills/platform-lightning-type-widget-coordinate/examples/existing-lightning-type-with-widget-prompt.md +1 -1
  202. package/skills/platform-lightning-type-widget-coordinate/examples/new-lightning-type-with-widget-prompt.md +1 -1
  203. package/skills/platform-lightning-type-widget-coordinate/references/validation-gates.md +3 -3
  204. package/skills/platform-mcp-tool-widget-coordinate/SKILL.md +43 -67
  205. package/skills/platform-mcp-tool-widget-coordinate/examples/action-name-source-prompt.md +1 -2
  206. package/skills/platform-mcp-tool-widget-coordinate/examples/apex-invocable-source-prompt.md +2 -3
  207. package/skills/platform-mcp-tool-widget-coordinate/examples/nested-object-list-source-prompt.md +163 -0
  208. package/skills/platform-mcp-tool-widget-coordinate/examples/nested-object-single-source-prompt.md +198 -0
  209. package/skills/platform-mcp-tool-widget-coordinate/examples/pasted-tool-output-prompt.md +0 -1
  210. package/skills/platform-mcp-tool-widget-coordinate/references/build-plan-format.md +17 -11
  211. package/skills/platform-mcp-tool-widget-coordinate/references/mcp-tool-output-discovery.md +48 -15
  212. package/skills/platform-mcp-tool-widget-coordinate/references/two-clt-modeling.md +73 -10
  213. package/skills/platform-mcp-tool-widget-coordinate/references/validation-gates.md +48 -8
  214. package/skills/platform-policy-rule-generate/SKILL.md +22 -12
  215. package/skills/platform-policy-rule-generate/references/deploy-errors.md +1 -1
  216. package/skills/platform-policy-rule-generate/references/policy-schema-full.md +8 -29
  217. package/skills/platform-policy-rule-generate/references/templates-advanced.md +1 -1
  218. package/skills/platform-sharing-owd-configure/SKILL.md +20 -8
  219. package/skills/platform-sharing-owd-configure/references/access_levels.md +14 -1
  220. package/skills/platform-sharing-rules-generate/SKILL.md +67 -35
  221. package/skills/platform-sharing-rules-generate/examples/create-cases.md +16 -17
  222. package/skills/platform-sharing-rules-generate/examples/delete-cases.md +34 -79
  223. package/skills/platform-sharing-rules-generate/examples/edit-cases.md +13 -26
  224. package/skills/platform-sharing-rules-generate/scripts/count-remaining-rules.sh +33 -0
  225. package/skills/platform-widget-generate/SKILL.md +3 -5
  226. package/skills/platform-widget-generate/examples/conditional.json +18 -13
  227. package/skills/platform-widget-generate/examples/list-with-foreach.json +2 -2
  228. package/skills/platform-widget-generate/examples/single-object.json +2 -2
  229. package/skills/platform-widget-generate/references/schema-from-lightning-type.md +27 -6
  230. package/skills/platform-widget-generate/references/widget-bundle-layout.md +4 -3
  231. package/skills/service-itsm-agentic-setup-cmdb-access-assign/SKILL.md +287 -0
  232. package/skills/service-itsm-agentic-setup-cmdb-access-assign/references/mcp-invocation.md +260 -0
  233. package/skills/service-itsm-agentic-setup-cmdb-bundle-deploy/SKILL.md +252 -0
  234. package/skills/service-itsm-agentic-setup-cmdb-bundle-deploy/references/mcp-invocation.md +204 -0
  235. package/skills/service-itsm-agentic-setup-cmdb-configure/SKILL.md +259 -0
  236. package/skills/service-itsm-agentic-setup-cmdb-configure/references/mcp-invocation.md +188 -0
  237. package/skills/service-itsm-agentic-setup-cmdb-coordinate/SKILL.md +197 -0
  238. package/skills/service-itsm-agentic-setup-cmdb-coordinate/examples/output-templates.md +77 -0
  239. package/skills/service-itsm-agentic-setup-cmdb-discovery-configure/SKILL.md +316 -0
  240. package/skills/service-itsm-agentic-setup-cmdb-discovery-configure/references/mcp-invocation.md +221 -0
  241. package/skills/service-itsm-incident-priority-configure/SKILL.md +168 -0
  242. package/skills/service-itsm-incident-priority-configure/examples/matrix-operations.md +200 -0
  243. package/skills/service-itsm-incident-priority-configure/examples/render-matrix.md +54 -0
  244. package/skills/service-itsm-incident-priority-configure/examples/seed-full-matrix.md +52 -0
  245. package/skills/service-itsm-incident-priority-configure/references/sf-cli-invocation.md +264 -0
  246. package/skills/platform-mcp-tool-widget-coordinate/examples/nested-object-source-prompt.md +0 -191
  247. package/skills/platform-policy-rule-generate/references/fixtures-index.md +0 -29
@@ -0,0 +1,650 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # devhub.sh — Enable Dev Hub and view scratch org allocation for a Salesforce
4
+ # org, via the Salesforce CLI (sf).
5
+ #
6
+ # Dev Hub is the org feature that lets you create and manage scratch orgs and
7
+ # second-generation (2GP)/unlocked packages. This script reports whether Dev Hub
8
+ # is enabled, ENABLES it (or configures its sub-preferences) via a Metadata API
9
+ # deploy, shows the org's scratch org ALLOCATION (Active/Daily limits and how
10
+ # many remain), and lists the active scratch orgs created from the Dev Hub.
11
+ #
12
+ # How enablement works: the Dev Hub master switch is the deployable metadata
13
+ # field DevHubSettings.enableScratchOrgManagementPref (there is NO "enableDevHub"
14
+ # field, and no dedicated `sf` command). Deploying it true is exactly what the
15
+ # Setup toggle does. This script builds a DevHub.settings-meta.xml and deploys
16
+ # it. Enabling requires a user with ModifyAllData or ModifyMetadata (System
17
+ # Administrator has these); the org must NOT have a registered namespace and must
18
+ # NOT be a sandbox; and once on, Dev Hub CANNOT be turned off.
19
+ #
20
+ # How enablement is detected: the standard object ScratchOrgInfo is provisioned
21
+ # and becomes queryable only when Dev Hub is enabled. A successful "SELECT
22
+ # COUNT() FROM ScratchOrgInfo" means Dev Hub is ON; an INVALID_TYPE error means
23
+ # it is OFF. (Note: `sf org list limits` reports ActiveScratchOrgs/
24
+ # DailyScratchOrgs rows even on non-Dev-Hub orgs, so limits alone are NOT a
25
+ # reliable enablement test.)
26
+ #
27
+ # Usage:
28
+ # devhub.sh [org] # status: is Dev Hub on? + allocation
29
+ # devhub.sh [org] --allocation # scratch org allocation only
30
+ # devhub.sh [org] --list-scratch # list this Dev Hub's active scratch orgs
31
+ # devhub.sh [org] --enable # validate enabling Dev Hub (dry run)
32
+ # devhub.sh [org] --enable --apply # actually enable Dev Hub (irreversible)
33
+ # devhub.sh [org] --enable --packaging --apply # enable + turn on 2GP
34
+ # devhub.sh [org] --configure --snapshots --apply # set a sub-pref (Dev Hub already on)
35
+ # devhub.sh [org] --json # machine-readable status
36
+ # devhub.sh --fail-if-disabled # exit 3 if Dev Hub is not enabled (CI gate)
37
+ # devhub.sh [alias] --instance-url https://my-domain.my.salesforce.com --enable --apply
38
+ # # log in to that instance, then enable Dev Hub
39
+ # devhub.sh https://my-domain.my.salesforce.com # bare URL: log in to that instance
40
+ # devhub.sh myalias --login # just authenticate an org via the browser
41
+ #
42
+ # "org" is an alias or username. If omitted, the default target-org is used,
43
+ # and if that is unset, the default target-dev-hub. With --login/--instance-url,
44
+ # a supplied positional is used as the CLI alias for the org being logged in to.
45
+ # A bare positional that looks like a URL (contains "://") is treated as an
46
+ # --instance-url to log in to, not as an org alias.
47
+ #
48
+ # Options:
49
+ # --allocation, -A Show only the scratch org allocation (Active/Daily).
50
+ # --list-scratch, -l List active scratch orgs created from this Dev Hub.
51
+ # --enable, -e Enable Dev Hub (deploys enableScratchOrgManagementPref
52
+ # =true). Defaults to a validate-only dry run; add
53
+ # --apply to actually enable. May be combined with the
54
+ # sub-pref flags below to enable and configure at once.
55
+ # --configure, -c Configure Dev Hub sub-preferences without (re)enabling
56
+ # the master switch. Pair with one or more pref flags.
57
+ # Defaults to a dry run; add --apply to deploy.
58
+ # --packaging enablePackaging2=true (Unlocked + 2GP packages)
59
+ # --snapshots enableScratchOrgSnapshotPref=true
60
+ # --shape enableShapeExportPref=true
61
+ # --scratch-management enableScratchOrgManagementPref=true (the Dev Hub switch)
62
+ # --pref KEY=VALUE Any DevHubSettings sub-pref (KEY must start with
63
+ # "enable"; VALUE is true|false). Repeatable.
64
+ # --apply Actually deploy (default for --enable/--configure is a
65
+ # validate-only dry run).
66
+ # --login Authenticate an org first via `sf org login web`
67
+ # (opens a browser). Runs before the requested action.
68
+ # --instance-url URL Log in to a Salesforce instance (My Domain, sandbox,
69
+ # or pre-release/scratch instance) with
70
+ # `sf org login web --instance-url URL`. Implies --login.
71
+ # --json Emit machine-readable JSON.
72
+ # --fail-if-disabled Exit 3 if Dev Hub is not enabled on the target org.
73
+ # --help, -h Show this help.
74
+ #
75
+ # Exit codes:
76
+ # 0 success
77
+ # 1 the org could not be queried, or a deploy failed (auth/connection/deploy)
78
+ # 2 bad usage / missing dependency (sf or jq)
79
+ # 3 Dev Hub is not enabled (only when --fail-if-disabled is set)
80
+
81
+ set -uo pipefail
82
+
83
+ die() { echo "Error: $*" >&2; exit 2; }
84
+
85
+ # The skill must be invoked by ABSOLUTE path (see SKILL.md): a relative path like
86
+ # ./scripts/devhub.sh resolves against the caller's CWD and can run the wrong
87
+ # file. Enforce it here so the guarantee is deterministic and not left to the
88
+ # model. (A leading "/" is the portable test for an absolute path.)
89
+ case "$0" in
90
+ /*) : ;;
91
+ *) die "Run this script by its absolute path (got: '$0'). Example: bash \"/abs/path/to/skills/dx-org-devhub-configure/scripts/devhub.sh\"" ;;
92
+ esac
93
+
94
+ command -v sf >/dev/null 2>&1 || die "Salesforce CLI ('sf') not found on PATH."
95
+ command -v jq >/dev/null 2>&1 || die "'jq' not found on PATH."
96
+
97
+ # Keep CLI update chatter out of captured JSON/stdout.
98
+ export SF_AUTOUPDATE_DISABLE=true
99
+
100
+ # --- constants -----------------------------------------------------------
101
+ SETTINGS_API_VERSION="47.0" # DevHubSettings requires API >= 47.0
102
+ DEVHUB_SWITCH="enableScratchOrgManagementPref"
103
+
104
+ # --- option state --------------------------------------------------------
105
+ TARGET="" # org alias/username (positional or resolved default)
106
+ TARGET_EXPLICIT=0 # 1 when the caller supplied an org alias/username
107
+ MODE="status" # status | allocation | list | deploy
108
+ MODE_EXPLICIT=0 # 1 when the caller selected an operation
109
+ ENABLING=0 # 1 when --enable was passed (seeds the master switch)
110
+ OUTPUT="human" # human | json
111
+ APPLY=0 # deploy: 0 = dry run (validate only), 1 = real deploy
112
+ FAIL_IF_DISABLED=0
113
+ PREFS=() # requested DevHubSettings prefs (KEY=VALUE)
114
+ DO_LOGIN=0 # 1 when --login or --instance-url requests a browser login
115
+ LOGIN_INSTANCE_URL="" # instance URL for `sf org login web --instance-url`
116
+
117
+ # --- run state -----------------------------------------------------------
118
+ DEVHUB_STATE="" # enabled | disabled | auth_error
119
+ DEVHUB_ERRNAME="" # error .name when auth_error
120
+ DEVHUB_ERRMSG=""
121
+ SCRATCH_COUNT="" # ScratchOrgInfo count when enabled
122
+ INSTANCE_URL="" # resolved org instance URL (for the Setup deep link)
123
+
124
+ usage() {
125
+ sed -n '2,/^set -uo/p' "$0" | sed '/^set -uo/d; s/^# \{0,1\}//; s/^#//'
126
+ }
127
+
128
+ # Set MODE, rejecting a second conflicting mode selection.
129
+ set_mode() {
130
+ if [ "$MODE" != "status" ] && [ "$MODE" != "$1" ]; then
131
+ die "Choose only one of --allocation, --list-scratch, --enable/--configure."
132
+ fi
133
+ MODE="$1"
134
+ }
135
+
136
+ # True if PREFS already contains a KEY (any value).
137
+ has_pref_key() {
138
+ local p
139
+ for p in "${PREFS[@]:-}"; do
140
+ [ -n "$p" ] || continue
141
+ [ "${p%%=*}" = "$1" ] && return 0
142
+ done
143
+ return 1
144
+ }
145
+
146
+ # Register a pref for deploy, validating shape and giving actionable guidance
147
+ # for the common wrong field name. De-dupes on key (later value wins).
148
+ add_pref() {
149
+ local kv="$1" key val existing kept
150
+ key="${kv%%=*}"; val="${kv#*=}"
151
+ case "$key" in
152
+ enableDevHub)
153
+ die "There is no 'enableDevHub' field. Use --enable (which sets $DEVHUB_SWITCH=true)." ;;
154
+ "$DEVHUB_SWITCH")
155
+ [ "$val" = "true" ] || die "$DEVHUB_SWITCH cannot be set to false because Dev Hub enablement is irreversible."
156
+ ENABLING=1 ;;
157
+ enable*) : ;;
158
+ *) die "--pref key must be a DevHubSettings field starting with 'enable' (got: '$key')." ;;
159
+ esac
160
+ case "$val" in
161
+ true|false) : ;;
162
+ *) die "--pref value must be true or false (got: '$key=$val')." ;;
163
+ esac
164
+ kept=()
165
+ for existing in "${PREFS[@]:-}"; do
166
+ [ -n "$existing" ] || continue
167
+ [ "${existing%%=*}" = "$key" ] && continue
168
+ kept+=("$existing")
169
+ done
170
+ kept+=("$key=$val")
171
+ PREFS=("${kept[@]}")
172
+ }
173
+
174
+ # --- argument parsing ----------------------------------------------------
175
+ while [ $# -gt 0 ]; do
176
+ case "$1" in
177
+ --allocation|-A) MODE_EXPLICIT=1; set_mode allocation; shift ;;
178
+ --list-scratch|-l) MODE_EXPLICIT=1; set_mode list; shift ;;
179
+ --enable|-e) MODE_EXPLICIT=1; ENABLING=1; set_mode deploy; shift ;;
180
+ --configure|-c) MODE_EXPLICIT=1; set_mode deploy; shift ;;
181
+ --packaging) MODE_EXPLICIT=1; add_pref "enablePackaging2=true"; set_mode deploy; shift ;;
182
+ --snapshots) MODE_EXPLICIT=1; add_pref "enableScratchOrgSnapshotPref=true"; set_mode deploy; shift ;;
183
+ --shape) MODE_EXPLICIT=1; add_pref "enableShapeExportPref=true"; set_mode deploy; shift ;;
184
+ --scratch-management) MODE_EXPLICIT=1; add_pref "$DEVHUB_SWITCH=true"; set_mode deploy; shift ;;
185
+ --pref)
186
+ shift
187
+ [ $# -gt 0 ] || die "--pref requires a KEY=VALUE argument (e.g. --pref enablePackaging2=true)."
188
+ case "$1" in *=*) : ;; *) die "--pref expects KEY=VALUE (got: '$1')." ;; esac
189
+ MODE_EXPLICIT=1; add_pref "$1"; set_mode deploy; shift ;;
190
+ --pref=*)
191
+ kv="${1#*=}"
192
+ case "$kv" in *=*) : ;; *) die "--pref expects KEY=VALUE (got: '$kv')." ;; esac
193
+ MODE_EXPLICIT=1; add_pref "$kv"; set_mode deploy; shift ;;
194
+ --apply) APPLY=1; shift ;;
195
+ --login) DO_LOGIN=1; shift ;;
196
+ --instance-url)
197
+ shift
198
+ [ $# -gt 0 ] || die "--instance-url requires a URL (e.g. --instance-url https://my-domain.my.salesforce.com)."
199
+ [ -n "$LOGIN_INSTANCE_URL" ] && die "Specify only one instance URL. Got extra: $1"
200
+ LOGIN_INSTANCE_URL="$1"; DO_LOGIN=1; shift ;;
201
+ --instance-url=*)
202
+ [ -n "$LOGIN_INSTANCE_URL" ] && die "Specify only one instance URL. Got extra: ${1#*=}"
203
+ LOGIN_INSTANCE_URL="${1#*=}"; DO_LOGIN=1; shift ;;
204
+ --json) OUTPUT="json"; shift ;;
205
+ --fail-if-disabled) FAIL_IF_DISABLED=1; shift ;;
206
+ --help|-h) usage; exit 0 ;;
207
+ --) shift; break ;;
208
+ -*) die "Unknown option: $1 (try --help)" ;;
209
+ *://*)
210
+ # A bare URL positional is an instance URL to log in to (My Domain,
211
+ # sandbox, or pre-release/scratch instance), not an org alias — an alias or
212
+ # username never contains "://". Route it to the login path.
213
+ [ -n "$LOGIN_INSTANCE_URL" ] && die "Specify only one instance URL. Got extra: $1"
214
+ LOGIN_INSTANCE_URL="$1"; DO_LOGIN=1; shift ;;
215
+ *)
216
+ [ -n "$TARGET" ] && die "Specify only one org. Got extra: $1"
217
+ TARGET="$1"; TARGET_EXPLICIT=1; shift ;;
218
+ esac
219
+ done
220
+
221
+ # --enable always includes the master switch.
222
+ if [ "$ENABLING" -eq 1 ] && ! has_pref_key "$DEVHUB_SWITCH"; then
223
+ add_pref "$DEVHUB_SWITCH=true"
224
+ fi
225
+
226
+ if [ "$MODE" = "deploy" ] && [ "${#PREFS[@]}" -eq 0 ]; then
227
+ die "--configure needs at least one preference (e.g. --packaging, --snapshots, or --pref KEY=VALUE). To enable Dev Hub, use --enable."
228
+ fi
229
+
230
+ if [ "$APPLY" -eq 1 ] && [ "$MODE" != "deploy" ]; then
231
+ die "--apply is valid only with --enable, --configure, or a preference flag."
232
+ fi
233
+
234
+ if [ "$APPLY" -eq 1 ] && [ "$TARGET_EXPLICIT" -ne 1 ]; then
235
+ die "--apply requires an explicit org alias or username; refusing to mutate an implicit default org."
236
+ fi
237
+
238
+ # A supplied instance URL must use an HTTPS Salesforce-owned hostname.
239
+ if [ -n "$LOGIN_INSTANCE_URL" ]; then
240
+ case "$LOGIN_INSTANCE_URL" in
241
+ https://*.salesforce.com|https://*.force.com|https://*.salesforce.mil) : ;;
242
+ *) die "--instance-url must use an https:// Salesforce hostname (got: '$LOGIN_INSTANCE_URL')." ;;
243
+ esac
244
+ fi
245
+
246
+ # --- org resolution ------------------------------------------------------
247
+ resolve_target() {
248
+ [ -n "$TARGET" ] && return 0
249
+ TARGET="$(sf config get target-org --json 2>/dev/null | jq -r '.result[0].value // empty')"
250
+ [ -n "$TARGET" ] && return 0
251
+ TARGET="$(sf config get target-dev-hub --json 2>/dev/null | jq -r '.result[0].value // empty')"
252
+ [ -n "$TARGET" ] && return 0
253
+ echo "No org specified and no default target-org / target-dev-hub is set." >&2
254
+ echo >&2
255
+ echo "Pass an org alias/username, or set a default:" >&2
256
+ echo " sf org login web --alias <my-alias> # authenticate an org" >&2
257
+ echo " sf config set target-org <my-alias> # make it the default" >&2
258
+ exit 2
259
+ }
260
+
261
+ # --- interactive login ---------------------------------------------------
262
+ # Authenticate an org with `sf org login web`, honoring an optional
263
+ # --instance-url (My Domain, sandbox, or pre-release/scratch instance) and, when
264
+ # an org identifier was supplied, using it as the CLI alias. Opens a browser for
265
+ # the OAuth flow; returns non-zero if the login fails or times out.
266
+ do_login() {
267
+ local args=(org login web)
268
+ [ -n "$LOGIN_INSTANCE_URL" ] && args+=(--instance-url "$LOGIN_INSTANCE_URL")
269
+ [ -n "$TARGET" ] && args+=(--alias "$TARGET")
270
+
271
+ if [ "$OUTPUT" != "json" ]; then
272
+ echo "Authenticating with the Salesforce CLI (a browser window will open)..."
273
+ [ -n "$LOGIN_INSTANCE_URL" ] && echo " instance URL: $LOGIN_INSTANCE_URL"
274
+ [ -n "$TARGET" ] && echo " alias: $TARGET"
275
+ echo " Complete the login in the browser promptly (the session times out)."
276
+ echo
277
+ fi
278
+
279
+ local out ok
280
+ out="$(sf "${args[@]}" --json 2>/dev/null)"
281
+ ok="$(printf '%s' "$out" | jq -r 'if .status==0 then "true" else "false" end' 2>/dev/null)"
282
+
283
+ if [ "$ok" != "true" ]; then
284
+ local msg
285
+ msg="$(printf '%s' "$out" | jq -r '.message // empty' 2>/dev/null)"
286
+ if [ "$OUTPUT" = "json" ]; then
287
+ jq -n --arg msg "${msg:-login failed}" '{status:"login_failed", error:$msg}'
288
+ else
289
+ echo "Login failed: ${msg:-unknown error}." >&2
290
+ echo " Retry in your own terminal, or use the device flow if the browser flow keeps timing out:" >&2
291
+ echo " sf org login device${LOGIN_INSTANCE_URL:+ --instance-url $(shell_quote "$LOGIN_INSTANCE_URL")}${TARGET:+ --alias $(shell_quote "$TARGET")}" >&2
292
+ fi
293
+ return 1
294
+ fi
295
+
296
+ # Adopt the authenticated username as the target when none was supplied, so the
297
+ # rest of the run operates on the org the user just logged in to.
298
+ if [ -z "$TARGET" ]; then
299
+ TARGET="$(printf '%s' "$out" | jq -r '.result.username // empty' 2>/dev/null)"
300
+ fi
301
+ [ "$OUTPUT" != "json" ] && echo "Authenticated: ${TARGET:-(org)}" && echo
302
+ return 0
303
+ }
304
+
305
+ # Render one dynamic value as a safe, copy-pasteable shell argument.
306
+ shell_quote() {
307
+ printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\\\\''/g")"
308
+ }
309
+
310
+ # --- Dev Hub detection ---------------------------------------------------
311
+ # Sets DEVHUB_STATE (+ SCRATCH_COUNT when enabled, or error fields on failure).
312
+ # ScratchOrgInfo is queryable ONLY on a Dev Hub, so a clean COUNT == enabled and
313
+ # INVALID_TYPE == not enabled; any other failure is treated as an auth/conn error.
314
+ detect_devhub() {
315
+ local out rc
316
+ out="$(sf data query --query "SELECT COUNT() FROM ScratchOrgInfo" \
317
+ --target-org "$TARGET" --json 2>/dev/null)"
318
+ rc=$?
319
+ if [ "$rc" -eq 0 ]; then
320
+ DEVHUB_STATE="enabled"
321
+ SCRATCH_COUNT="$(printf '%s' "$out" | jq -r '.result.totalSize // empty')"
322
+ return 0
323
+ fi
324
+ local ename emsg
325
+ ename="$(printf '%s' "$out" | jq -r '.name // empty' 2>/dev/null)"
326
+ emsg="$(printf '%s' "$out" | jq -r '.message // empty' 2>/dev/null | tr '\t\n' ' ')"
327
+ case "$ename" in
328
+ INVALID_TYPE)
329
+ DEVHUB_STATE="disabled"; return 0 ;;
330
+ *)
331
+ DEVHUB_STATE="auth_error"; DEVHUB_ERRNAME="$ename"; DEVHUB_ERRMSG="$emsg"; return 1 ;;
332
+ esac
333
+ }
334
+
335
+ # Echoes TSV rows "name<TAB>remaining<TAB>max" for the two scratch org limits,
336
+ # or nothing if the org could not be queried.
337
+ allocation_rows() {
338
+ local out
339
+ out="$(sf org list limits --target-org "$TARGET" --json 2>/dev/null)" || return 1
340
+ printf '%s' "$out" | jq -r '
341
+ .result[]?
342
+ | select(.name=="ActiveScratchOrgs" or .name=="DailyScratchOrgs")
343
+ | [.name, (.remaining|tostring), (.max|tostring)] | @tsv'
344
+ }
345
+
346
+ # Resolve the org instance URL for the Setup deep link (best effort).
347
+ resolve_instance_url() {
348
+ [ -n "$INSTANCE_URL" ] && return 0
349
+ INSTANCE_URL="$(sf org display --target-org "$TARGET" --json 2>/dev/null \
350
+ | jq -r '.result.instanceUrl // empty')"
351
+ }
352
+
353
+ # --- auth error reporting ------------------------------------------------
354
+ print_auth_error() {
355
+ if [ "$OUTPUT" = "json" ]; then
356
+ jq -n --arg org "$TARGET" --arg name "$DEVHUB_ERRNAME" --arg msg "$DEVHUB_ERRMSG" \
357
+ '{org:$org, devHubEnabled:null, status:"auth_error", error:{name:$name, message:$msg}}'
358
+ return
359
+ fi
360
+ printf " %-30s could not query org (%s)\n" "$TARGET" "${DEVHUB_ERRNAME:-error}"
361
+ [ -n "$DEVHUB_ERRMSG" ] && printf " %-30s (%s)\n" "" "$DEVHUB_ERRMSG"
362
+ echo
363
+ if [ -n "$LOGIN_INSTANCE_URL" ]; then
364
+ # An instance URL was provided — show the exact command for it, and offer to
365
+ # let the script run it directly via --login.
366
+ echo " To authenticate against $LOGIN_INSTANCE_URL, run:"
367
+ echo " sf org login web --instance-url $(shell_quote "$LOGIN_INSTANCE_URL")${TARGET:+ --alias $(shell_quote "$TARGET")}"
368
+ echo " Or let this script log you in and continue in one step:"
369
+ echo " devhub.sh ${TARGET:+$(shell_quote "$TARGET") }--instance-url $(shell_quote "$LOGIN_INSTANCE_URL") --login"
370
+ else
371
+ echo " To authenticate, run one of:"
372
+ echo " sf org login web --alias $(shell_quote "$TARGET") # interactive browser login"
373
+ echo " sf org login web --alias <my-alias> --instance-url https://MYDOMAIN.my.salesforce.com # sandbox/My Domain"
374
+ echo " Or let this script log you in and continue in one step:"
375
+ echo " devhub.sh $(shell_quote "$TARGET") --login # prod/DE/trial"
376
+ echo " devhub.sh <my-alias> --instance-url https://MYDOMAIN.my.salesforce.com --login # sandbox/My Domain"
377
+ fi
378
+ echo " Then re-run. List existing logins with: sf org list"
379
+ }
380
+
381
+ # --- Setup UI fallback text ----------------------------------------------
382
+ print_ui_fallback() {
383
+ resolve_instance_url
384
+ echo " Manual alternative (Setup UI):"
385
+ echo " 1. Log in as a System Administrator."
386
+ echo " 2. Setup > Quick Find > \"Dev Hub\" > Dev Hub > turn Enable Dev Hub On."
387
+ if [ -n "$INSTANCE_URL" ]; then
388
+ echo " Open it directly: sf org open --path \"lightning/setup/DevHub/home\" --target-org \"$TARGET\""
389
+ fi
390
+ }
391
+
392
+ # --- allocation rendering ------------------------------------------------
393
+ render_allocation() {
394
+ local rows a_max a_rem d_max d_rem
395
+ rows="$(allocation_rows)" || { echo "Could not read org limits for '$TARGET'." >&2; return 1; }
396
+ a_rem="$(printf '%s\n' "$rows" | awk -F'\t' '$1=="ActiveScratchOrgs"{print $2}')"
397
+ a_max="$(printf '%s\n' "$rows" | awk -F'\t' '$1=="ActiveScratchOrgs"{print $3}')"
398
+ d_rem="$(printf '%s\n' "$rows" | awk -F'\t' '$1=="DailyScratchOrgs"{print $2}')"
399
+ d_max="$(printf '%s\n' "$rows" | awk -F'\t' '$1=="DailyScratchOrgs"{print $3}')"
400
+
401
+ if [ "$OUTPUT" = "json" ]; then
402
+ jq -n --arg org "$TARGET" \
403
+ --argjson am "${a_max:-null}" --argjson ar "${a_rem:-null}" \
404
+ --argjson dm "${d_max:-null}" --argjson dr "${d_rem:-null}" '
405
+ {org:$org,
406
+ allocation:{
407
+ activeScratchOrgs:{max:$am, remaining:$ar, used:(if $am!=null and $ar!=null then ($am-$ar) else null end)},
408
+ dailyScratchOrgs:{max:$dm, remaining:$dr, used:(if $dm!=null and $dr!=null then ($dm-$dr) else null end)}
409
+ }}'
410
+ return 0
411
+ fi
412
+
413
+ echo "Scratch org allocation for '$TARGET':"
414
+ if [ -z "$a_max" ] && [ -z "$d_max" ]; then
415
+ echo " No scratch org limits reported (org may not be a Dev Hub, or could not be queried)."
416
+ return 0
417
+ fi
418
+ printf " %-22s %10s %10s %10s\n" "Limit" "Used" "Remaining" "Max"
419
+ [ -n "$a_max" ] && printf " %-22s %10s %10s %10s\n" "Active scratch orgs" "$(( a_max - a_rem ))" "$a_rem" "$a_max"
420
+ [ -n "$d_max" ] && printf " %-22s %10s %10s %10s\n" "Daily scratch orgs" "$(( d_max - d_rem ))" "$d_rem" "$d_max"
421
+ echo
422
+ echo " Active = orgs alive right now; Daily = successful creations in a rolling 24h window."
423
+ echo " Allocation is set by the Dev Hub's edition (e.g. Developer/trial 3 & 6;"
424
+ echo " Enterprise 40 & 80; Unlimited/Performance 100 & 200) plus any purchased add-on."
425
+ }
426
+
427
+ # --- active scratch org listing ------------------------------------------
428
+ render_list() {
429
+ local out rc
430
+ out="$(sf data query --target-org "$TARGET" \
431
+ --query "SELECT SignupUsername, OrgName, Edition, ExpirationDate, ScratchOrg FROM ActiveScratchOrg ORDER BY ExpirationDate NULLS LAST" \
432
+ --json 2>/dev/null)"
433
+ rc=$?
434
+ if [ "$rc" -ne 0 ]; then
435
+ if [ "$OUTPUT" = "json" ]; then echo "[]"; else
436
+ echo "Could not list scratch orgs for '$TARGET' — is Dev Hub enabled and the org authenticated?" >&2
437
+ fi
438
+ return 1
439
+ fi
440
+ if [ "$OUTPUT" = "json" ]; then
441
+ printf '%s' "$out" | jq '[.result.records[]? | {signupUsername:.SignupUsername, orgName:.OrgName, edition:.Edition, expirationDate:.ExpirationDate, scratchOrgId:.ScratchOrg}]'
442
+ return 0
443
+ fi
444
+ local total
445
+ total="$(printf '%s' "$out" | jq -r '.result.totalSize // 0')"
446
+ echo "Active scratch orgs on Dev Hub '$TARGET': $total"
447
+ if [ "$total" = "0" ]; then
448
+ echo " (none active)"
449
+ return 0
450
+ fi
451
+ printf '%s' "$out" | jq -r '.result.records[]?
452
+ | " \(.SignupUsername // "?") edition=\(.Edition // "?") expires=\(.ExpirationDate // "?") id=\(.ScratchOrg // "?")"'
453
+ }
454
+
455
+ # --- deploy (enable / configure sub-prefs) -------------------------------
456
+ DEPLOY_TEMP_DIR=""
457
+ cleanup_deploy_temp() {
458
+ [ -n "$DEPLOY_TEMP_DIR" ] && rm -rf -- "$DEPLOY_TEMP_DIR"
459
+ }
460
+
461
+ render_deploy() {
462
+ # Sub-prefs (packaging, snapshots, shape) require Dev Hub to be on. If the
463
+ # user is configuring one without also enabling, and Dev Hub is off, warn.
464
+ if [ "$ENABLING" -eq 0 ] && [ "$DEVHUB_STATE" = "disabled" ] && [ "$OUTPUT" != "json" ]; then
465
+ echo "Note: Dev Hub is not enabled on '$TARGET'. Sub-preferences require Dev Hub — add --enable, or run --enable first." >&2
466
+ echo >&2
467
+ fi
468
+
469
+ local tmp
470
+ tmp="$(mktemp -d 2>/dev/null)" || die "Could not create a temporary directory for the deploy."
471
+ DEPLOY_TEMP_DIR="$tmp"
472
+ trap cleanup_deploy_temp EXIT
473
+
474
+ mkdir -p "$tmp/force-app/main/default/settings"
475
+ cat > "$tmp/sfdx-project.json" <<JSON
476
+ { "packageDirectories": [{ "path": "force-app", "default": true }], "sourceApiVersion": "$SETTINGS_API_VERSION" }
477
+ JSON
478
+ {
479
+ echo '<?xml version="1.0" encoding="UTF-8"?>'
480
+ echo '<DevHubSettings xmlns="http://soap.sforce.com/2006/04/metadata">'
481
+ local kv key val
482
+ for kv in "${PREFS[@]}"; do
483
+ key="${kv%%=*}"; val="${kv#*=}"
484
+ echo " <$key>$val</$key>"
485
+ done
486
+ echo '</DevHubSettings>'
487
+ } > "$tmp/force-app/main/default/settings/DevHub.settings-meta.xml"
488
+
489
+ local dry="--dry-run" action="Validating (dry run)"
490
+ if [ "$APPLY" -eq 1 ]; then dry=""; action="Deploying"; fi
491
+
492
+ if [ "$OUTPUT" != "json" ]; then
493
+ if [ "$ENABLING" -eq 1 ]; then
494
+ echo "$action Dev Hub enablement on '$TARGET':"
495
+ else
496
+ echo "$action DevHubSettings on '$TARGET':"
497
+ fi
498
+ local kv2; for kv2 in "${PREFS[@]}"; do echo " $kv2"; done
499
+ if [ "$ENABLING" -eq 1 ] && [ "$APPLY" -eq 1 ]; then
500
+ echo
501
+ echo " WARNING: enabling Dev Hub is IRREVERSIBLE — it cannot be turned off."
502
+ echo " Requires ModifyAllData or ModifyMetadata (System Administrator)."
503
+ echo " Not allowed in a sandbox, or in an org with a registered namespace."
504
+ fi
505
+ echo
506
+ fi
507
+
508
+ local out ok verified="unknown"
509
+ out="$( cd "$tmp" && sf project deploy start $dry --source-dir force-app \
510
+ --target-org "$TARGET" --json 2>/dev/null )"
511
+ ok="$(printf '%s' "$out" | jq -r '.result.success // false')"
512
+
513
+ # On a real, successful enablement, re-detect so we report verified state.
514
+ if [ "$APPLY" -eq 1 ] && [ "$ENABLING" -eq 1 ] && [ "$ok" = "true" ]; then
515
+ detect_devhub >/dev/null 2>&1
516
+ verified="$DEVHUB_STATE"
517
+ fi
518
+
519
+ if [ "$OUTPUT" = "json" ]; then
520
+ printf '%s' "$out" | jq \
521
+ --arg applied "$([ "$APPLY" -eq 1 ] && echo true || echo false)" \
522
+ --arg enabling "$([ "$ENABLING" -eq 1 ] && echo true || echo false)" \
523
+ --arg verified "$verified" '
524
+ {applied:($applied=="true"),
525
+ enabling:($enabling=="true"),
526
+ success:(.result.success // false),
527
+ checkOnly:(.result.checkOnly // null),
528
+ devHubVerifiedState:(if $verified=="unknown" then null else $verified end),
529
+ failures:[.result.details.componentFailures[]? | {fullName, problem}],
530
+ message:(.message // null)}'
531
+ [ "$ok" = "true" ] && return 0 || return 1
532
+ fi
533
+
534
+ if [ "$ok" = "true" ]; then
535
+ if [ "$APPLY" -eq 1 ]; then
536
+ if [ "$ENABLING" -eq 1 ]; then
537
+ echo " Success — Dev Hub enabled (deploy succeeded)."
538
+ if [ "$verified" = "enabled" ]; then
539
+ echo " Verified: ScratchOrgInfo is now queryable."
540
+ else
541
+ echo " Note: provisioning may take a moment; re-run 'devhub.sh \"$TARGET\"' to confirm."
542
+ fi
543
+ echo " Set it as your default Dev Hub: sf config set target-dev-hub \"$TARGET\""
544
+ else
545
+ echo " Success — Dev Hub preferences deployed."
546
+ fi
547
+ else
548
+ echo " Dry run passed — valid. Re-run with --apply to deploy."
549
+ [ "$ENABLING" -eq 1 ] && echo " (Enabling Dev Hub is irreversible; --apply commits it.)"
550
+ fi
551
+ return 0
552
+ fi
553
+
554
+ echo " Deployment did not succeed:"
555
+ printf '%s' "$out" | jq -r '.result.details.componentFailures[]? | " - \(.fullName): \(.problem)"' 2>/dev/null
556
+ printf '%s' "$out" | jq -r '.message // empty' 2>/dev/null | sed 's/^/ /'
557
+ # Common cause: missing perms. Offer the UI route.
558
+ if printf '%s' "$out" | grep -qi "INSUFFICIENT_ACCESS\|ModifyAllData\|ModifyMetadata"; then
559
+ echo
560
+ echo " This looks like a permissions issue — the deploy needs ModifyAllData or ModifyMetadata."
561
+ [ "$ENABLING" -eq 1 ] && print_ui_fallback
562
+ fi
563
+ return 1
564
+ }
565
+
566
+ # --- status (default) ----------------------------------------------------
567
+ render_status() {
568
+ if [ "$OUTPUT" = "json" ]; then
569
+ local rows a_max a_rem d_max d_rem
570
+ rows="$(allocation_rows 2>/dev/null || true)"
571
+ a_rem="$(printf '%s\n' "$rows" | awk -F'\t' '$1=="ActiveScratchOrgs"{print $2}')"
572
+ a_max="$(printf '%s\n' "$rows" | awk -F'\t' '$1=="ActiveScratchOrgs"{print $3}')"
573
+ d_rem="$(printf '%s\n' "$rows" | awk -F'\t' '$1=="DailyScratchOrgs"{print $2}')"
574
+ d_max="$(printf '%s\n' "$rows" | awk -F'\t' '$1=="DailyScratchOrgs"{print $3}')"
575
+ jq -n --arg org "$TARGET" --arg state "$DEVHUB_STATE" \
576
+ --argjson count "${SCRATCH_COUNT:-null}" \
577
+ --argjson am "${a_max:-null}" --argjson ar "${a_rem:-null}" \
578
+ --argjson dm "${d_max:-null}" --argjson dr "${d_rem:-null}" '
579
+ {org:$org,
580
+ devHubEnabled:($state=="enabled"),
581
+ status:$state,
582
+ activeScratchOrgSignups:$count,
583
+ allocation:{
584
+ activeScratchOrgs:{max:$am, remaining:$ar},
585
+ dailyScratchOrgs:{max:$dm, remaining:$dr}
586
+ }}'
587
+ return 0
588
+ fi
589
+
590
+ echo "Dev Hub status for '$TARGET':"
591
+ if [ "$DEVHUB_STATE" = "enabled" ]; then
592
+ echo " Dev Hub: ENABLED (ScratchOrgInfo is queryable)"
593
+ [ -n "$SCRATCH_COUNT" ] && echo " Scratch org signup records: $SCRATCH_COUNT"
594
+ echo
595
+ render_allocation
596
+ else
597
+ echo " Dev Hub: NOT ENABLED"
598
+ echo
599
+ echo " Enable it (deploys $DEVHUB_SWITCH=true; irreversible):"
600
+ echo " devhub.sh \"$TARGET\" --enable # validate first (dry run)"
601
+ echo " devhub.sh \"$TARGET\" --enable --apply # actually enable"
602
+ echo " Requires ModifyAllData or ModifyMetadata (System Administrator);"
603
+ echo " not allowed in a sandbox or an org with a registered namespace."
604
+ echo
605
+ print_ui_fallback
606
+ fi
607
+ }
608
+
609
+ # --- dispatch ------------------------------------------------------------
610
+ # Authenticate first when requested. Login can supply the org, so it runs before
611
+ # resolve_target's "no org" guard; a bare positional (no other mode) with
612
+ # --login/--instance-url is treated as the alias to log in under.
613
+ if [ "$DO_LOGIN" -eq 1 ]; then
614
+ do_login || exit 1
615
+ if [ "$MODE_EXPLICIT" -eq 0 ] && [ "$FAIL_IF_DISABLED" -eq 0 ]; then
616
+ exit 0
617
+ fi
618
+ fi
619
+
620
+ resolve_target
621
+
622
+ # status/deploy need the enablement state; allocation and list query the org
623
+ # directly and surface their own errors.
624
+ case "$MODE" in
625
+ status|deploy)
626
+ if ! detect_devhub && [ "$DEVHUB_STATE" = "auth_error" ]; then
627
+ print_auth_error
628
+ exit 1
629
+ fi
630
+ ;;
631
+ esac
632
+
633
+ rc=0
634
+ case "$MODE" in
635
+ status) render_status || rc=1 ;;
636
+ allocation) render_allocation || rc=1 ;;
637
+ list) render_list || rc=1 ;;
638
+ deploy) render_deploy || rc=1 ;;
639
+ esac
640
+
641
+ # CI gate: exit 3 if Dev Hub is not enabled (takes precedence for alerting).
642
+ if [ "$FAIL_IF_DISABLED" -eq 1 ]; then
643
+ [ -z "$DEVHUB_STATE" ] && detect_devhub >/dev/null 2>&1
644
+ if [ "$DEVHUB_STATE" != "enabled" ]; then
645
+ echo "ALERT: Dev Hub is not enabled on '$TARGET'." >&2
646
+ exit 3
647
+ fi
648
+ fi
649
+
650
+ exit "$rc"