@salesforce/afv-skills 1.31.0 → 1.32.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 (98) hide show
  1. package/package.json +1 -1
  2. package/skills/agentforce-bot-upgrade/SKILL.md +217 -0
  3. package/skills/agentforce-bot-upgrade/references/extraction-blueprint.md +242 -0
  4. package/skills/agentforce-bot-upgrade/references/generate-agent-spec.md +128 -0
  5. package/skills/agentforce-bot-upgrade/references/handoff-output-format.md +208 -0
  6. package/skills/agentforce-bot-upgrade/references/input-contract.md +101 -0
  7. package/skills/agentforce-bot-upgrade/references/mapping-rules.md +113 -0
  8. package/skills/agentforce-bot-upgrade/references/planner-workflow-reference.md +78 -0
  9. package/skills/agentforce-bot-upgrade/references/post-conversion-enhancements-reference.md +67 -0
  10. package/skills/agentforce-bot-upgrade/references/quality-checklist.md +33 -0
  11. package/skills/agentforce-bot-upgrade/references/sf-cli-bot-reference.md +116 -0
  12. package/skills/dx-devops-work-item-manage/SKILL.md +270 -0
  13. package/skills/dx-devops-work-item-manage/examples/common-workflows.md +479 -0
  14. package/skills/dx-devops-work-item-manage/references/cli-commands.md +476 -0
  15. package/skills/dx-org-manage/SKILL.md +8 -5
  16. package/skills/dx-org-trial-expiration-check/SKILL.md +239 -0
  17. package/skills/dx-org-trial-expiration-check/scripts/check_expiration.sh +588 -0
  18. package/skills/experience-ui-bundle-app-coordinate/SKILL.md +12 -9
  19. package/skills/experience-ui-bundle-custom-app-generate/SKILL.md +29 -5
  20. package/skills/experience-ui-bundle-custom-app-generate/references/configure-metadata-custom-application.md +1 -1
  21. package/skills/experience-ui-bundle-custom-app-generate/scripts/resolve-uibundle-path.sh +30 -0
  22. package/skills/experience-ui-bundle-features-generate/SKILL.md +86 -11
  23. package/skills/experience-ui-bundle-features-generate/references/conflict-resolution-schema.json +20 -0
  24. package/skills/experience-ui-bundle-features-generate/scripts/verify-react-bundle.sh +14 -0
  25. package/skills/experience-ui-bundle-frontend-generate/SKILL.md +6 -1
  26. package/skills/experience-ui-bundle-frontend-generate/references/component.md +34 -1
  27. package/skills/experience-ui-bundle-frontend-generate/references/page.md +1 -1
  28. package/skills/experience-ui-bundle-project-generate/SKILL.md +111 -0
  29. package/skills/experience-ui-bundle-project-generate/scripts/flatten-project.mjs +33 -0
  30. package/skills/platform-custom-lightning-type-generate/SKILL.md +9 -14
  31. package/skills/platform-custom-lightning-type-generate/references/widget-rendition.md +27 -111
  32. package/skills/platform-lightning-type-widget-coordinate/SKILL.md +214 -0
  33. package/skills/platform-lightning-type-widget-coordinate/examples/existing-lightning-type-with-widget-prompt.md +80 -0
  34. package/skills/platform-lightning-type-widget-coordinate/examples/new-lightning-type-with-widget-prompt.md +75 -0
  35. package/skills/platform-lightning-type-widget-coordinate/references/build-plan-format.md +67 -0
  36. package/skills/platform-lightning-type-widget-coordinate/references/lightning-type-discovery.md +124 -0
  37. package/skills/platform-lightning-type-widget-coordinate/references/validation-gates.md +130 -0
  38. package/skills/platform-sharing-owd-configure/SKILL.md +142 -0
  39. package/skills/platform-sharing-owd-configure/examples/get_owd_output.md +38 -0
  40. package/skills/platform-sharing-owd-configure/examples/update_owd_output.md +44 -0
  41. package/skills/platform-sharing-owd-configure/references/access_levels.md +49 -0
  42. package/skills/platform-sharing-owd-configure/references/metadata_api_approach.md +69 -0
  43. package/skills/platform-sharing-rules-generate/SKILL.md +89 -19
  44. package/skills/platform-sharing-rules-generate/examples/test-cases.md +381 -0
  45. package/skills/platform-widget-generate/SKILL.md +241 -0
  46. package/skills/platform-widget-generate/examples/conditional.json +73 -0
  47. package/skills/platform-widget-generate/examples/list-with-foreach.json +66 -0
  48. package/skills/platform-widget-generate/examples/single-object.json +64 -0
  49. package/skills/platform-widget-generate/references/schema-from-lightning-type.md +76 -0
  50. package/skills/platform-widget-generate/references/widget-bundle-layout.md +116 -0
  51. package/skills/platform-widget-generate/references/widget-meta-directives.md +130 -0
  52. package/skills/sales-agentforce-pipeline-management-configure/SKILL.md +268 -0
  53. package/skills/sales-agentforce-pipeline-management-configure/assets/field-completion-template.genAiPromptTemplate-meta.xml +141 -0
  54. package/skills/sales-agentforce-pipeline-management-configure/assets/pipeline_management_flow.flow-meta.xml +548 -0
  55. package/skills/sales-agentforce-pipeline-management-configure/assets/sales_management_agent.agent +2938 -0
  56. package/skills/sales-agentforce-pipeline-management-configure/assets/sales_management_agent.bundle-meta.xml +4 -0
  57. package/skills/sales-agentforce-pipeline-management-configure/assets/sfdx-project.json +12 -0
  58. package/skills/sales-agentforce-pipeline-management-configure/assets/stage-descriptions/bant.txt +11 -0
  59. package/skills/sales-agentforce-pipeline-management-configure/assets/stage-descriptions/meddic.txt +11 -0
  60. package/skills/sales-agentforce-pipeline-management-configure/assets/stage-descriptions/spiced.txt +11 -0
  61. package/skills/sales-agentforce-pipeline-management-configure/examples/custom-prompt-instructions.md +317 -0
  62. package/skills/sales-agentforce-pipeline-management-configure/references/admin-communication.md +355 -0
  63. package/skills/sales-agentforce-pipeline-management-configure/references/agent-creation.md +383 -0
  64. package/skills/sales-agentforce-pipeline-management-configure/references/auth-and-cli.md +146 -0
  65. package/skills/sales-agentforce-pipeline-management-configure/references/automation-matrix.md +23 -0
  66. package/skills/sales-agentforce-pipeline-management-configure/references/autonomous-updates.md +215 -0
  67. package/skills/sales-agentforce-pipeline-management-configure/references/canonical-agent-path.md +75 -0
  68. package/skills/sales-agentforce-pipeline-management-configure/references/data-sources.md +155 -0
  69. package/skills/sales-agentforce-pipeline-management-configure/references/field-completion-prompt-template.md +120 -0
  70. package/skills/sales-agentforce-pipeline-management-configure/references/field-completion-prompts.md +149 -0
  71. package/skills/sales-agentforce-pipeline-management-configure/references/flags.md +13 -0
  72. package/skills/sales-agentforce-pipeline-management-configure/references/flow-clone-from-template.md +447 -0
  73. package/skills/sales-agentforce-pipeline-management-configure/references/metadata-inventory.md +330 -0
  74. package/skills/sales-agentforce-pipeline-management-configure/references/opportunity-stages.md +246 -0
  75. package/skills/sales-agentforce-pipeline-management-configure/references/repair-diagnostics.md +265 -0
  76. package/skills/sales-agentforce-pipeline-management-configure/references/setup-order.md +263 -0
  77. package/skills/sales-agentforce-pipeline-management-configure/references/soap-api-enablement.md +528 -0
  78. package/skills/sales-agentforce-pipeline-management-configure/scripts/CHANGES.md +180 -0
  79. package/skills/sales-agentforce-pipeline-management-configure/scripts/README.md +56 -0
  80. package/skills/sales-agentforce-pipeline-management-configure/scripts/add-field-suggestion.sh +679 -0
  81. package/skills/sales-agentforce-pipeline-management-configure/scripts/create-agent.sh +302 -0
  82. package/skills/sales-agentforce-pipeline-management-configure/scripts/create-flow.sh +250 -0
  83. package/skills/sales-agentforce-pipeline-management-configure/scripts/define-agent-access.sh +430 -0
  84. package/skills/sales-agentforce-pipeline-management-configure/scripts/deploy-settings.sh +77 -0
  85. package/skills/sales-agentforce-pipeline-management-configure/scripts/enable-deal-agent.sh +241 -0
  86. package/skills/sales-agentforce-pipeline-management-configure/scripts/enable-prerequisites.sh +538 -0
  87. package/skills/sales-agentforce-pipeline-management-configure/scripts/flow-debug-and-verify.sh +385 -0
  88. package/skills/sales-agentforce-pipeline-management-configure/scripts/retrieve-settings.sh +131 -0
  89. package/skills/sales-agentforce-pipeline-management-configure/scripts/setup-all.sh +2791 -0
  90. package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/agent-bundle-publish.sh +189 -0
  91. package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/agent-detection.sh +239 -0
  92. package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/flow-builder.sh +238 -0
  93. package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/psg.sh +48 -0
  94. package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/soap.sh +140 -0
  95. package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/stage-descriptions.sh +448 -0
  96. package/skills/sales-agentforce-pipeline-management-configure/scripts/shared/test-opp.sh +166 -0
  97. package/skills/sales-agentforce-pipeline-management-configure/scripts/verify-all.sh +636 -0
  98. package/skills/sales-agentforce-pipeline-management-configure/scripts/verify-prompt-generation.sh +346 -0
@@ -0,0 +1,588 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # check_expiration.sh — Report when Salesforce orgs expire, prioritized by urgency.
4
+ #
5
+ # Determines expiration from the authoritative source per org kind:
6
+ # - Trial / Developer Edition orgs: Organization.TrialExpirationDate (SOQL).
7
+ # Null for paid/production orgs, which report "no trial expiration".
8
+ # - Scratch orgs: the expirationDate reported by `sf org list`.
9
+ #
10
+ # Output is sorted soonest-first (already-expired first) and grouped by
11
+ # urgency, with a one-line summary. Structured --json / --csv output is
12
+ # available for dashboards, alerts, and cron. Optional guidance:
13
+ # --preserve print commands to back up an at-risk org before it lapses
14
+ # --renew print how to extend / reactivate a trial or DE org
15
+ # --fail-if-expiring [N] exit non-zero if any org expires within N days
16
+ # (default 7) — for cron / CI gating
17
+ #
18
+ # Usage:
19
+ # check_expiration.sh [org-alias-or-username] # one org
20
+ # check_expiration.sh --all # every authenticated org
21
+ # check_expiration.sh # default org (target-org)
22
+ # check_expiration.sh --all --within 30 # expiring within 30 days
23
+ # check_expiration.sh --all --json # machine-readable
24
+ # check_expiration.sh --all --fail-if-expiring 7 # cron watchdog
25
+ #
26
+ # Options:
27
+ # --all, -a Check every authenticated org.
28
+ # --within <days>, -w Only show orgs expiring within <days> days
29
+ # (includes already-expired; omits paid/production).
30
+ # --json Emit a JSON array of org records (data only).
31
+ # --csv Emit CSV rows (data only).
32
+ # --preserve Print backup commands for expiring/expired orgs.
33
+ # --renew Print trial/DE extension & reactivation guidance.
34
+ # --fail-if-expiring[=N] Exit 3 if any org expires within N days (default 7).
35
+ # --no-scratch Exclude scratch orgs (trial/DE only).
36
+ # --help, -h Show this help.
37
+ #
38
+ # Exit codes:
39
+ # 0 success
40
+ # 1 an org could not be queried (auth/connection error)
41
+ # 2 bad usage / missing dependency (sf or jq)
42
+ # 3 --fail-if-expiring threshold breached (at least one org expiring)
43
+
44
+ set -uo pipefail
45
+
46
+ die() { echo "Error: $*" >&2; exit 2; }
47
+
48
+ command -v sf >/dev/null 2>&1 || die "Salesforce CLI ('sf') not found on PATH."
49
+ command -v jq >/dev/null 2>&1 || die "'jq' not found on PATH."
50
+
51
+ SEP="$(printf '\037')" # US (unit separator): non-whitespace so empty
52
+ # fields are preserved by `read` (a whitespace IFS
53
+ # like TAB collapses runs, dropping empty columns).
54
+
55
+ # Sentinels used so records sort predictably by "days remaining" (ascending):
56
+ # real days (negative = expired) sort first, then unparseable-date, then
57
+ # no-expiration, then auth errors. Distinct values keep same-group records
58
+ # contiguous so group headers are never duplicated.
59
+ DATEONLY_DAYS=999999997 # a date exists but could not be parsed
60
+ NO_EXP_DAYS=999999998 # paid/production (no TrialExpirationDate)
61
+ AUTH_DAYS=999999999 # org could not be queried
62
+ SENTINEL_MIN=999999997 # daysRemaining >= this is a sentinel, not real
63
+ SOON_DAYS=7 # threshold for the "expiring soon" warning
64
+
65
+ # --- option state --------------------------------------------------------
66
+ MODE="" # "all" | "" (single/default)
67
+ TARGET="" # org alias/username for single mode
68
+ WITHIN_DAYS="" # display filter (empty = show everything)
69
+ WITHIN_SEEN=0 # whether --within was passed (to reject empty value)
70
+ FAIL_SEEN=0 # whether --fail-if-expiring was passed
71
+ OUTPUT="human" # human | json | csv
72
+ DO_PRESERVE=0
73
+ DO_RENEW=0
74
+ FAIL_WITHIN="" # --fail-if-expiring threshold (empty = off)
75
+ INCLUDE_SCRATCH=1 # scratch orgs included by default (--no-scratch off)
76
+
77
+ # --- run state -----------------------------------------------------------
78
+ RECORDS="" # accumulated TSV rows (one org per line)
79
+ HAD_AUTH_ERR=0
80
+ MATCH_COUNT=0
81
+ # compute() outputs:
82
+ DAYS=""
83
+ STATUS=""
84
+
85
+ usage() {
86
+ sed -n '2,/^set -uo/p' "$0" | sed '/^set -uo/d; s/^# \{0,1\}//; s/^#//'
87
+ }
88
+
89
+ # --- date helpers (macOS/BSD and GNU/Linux) ------------------------------
90
+ now_epoch() { date "+%s"; }
91
+
92
+ # Convert an ISO-ish date (YYYY-MM-DD[ T...]) to epoch seconds.
93
+ date_to_epoch() {
94
+ local d="$1"
95
+ d="${d%%T*}"; d="${d%% *}"
96
+ [ -z "$d" ] && return 1
97
+ if date -j >/dev/null 2>&1; then
98
+ date -j -f "%Y-%m-%d" "$d" "+%s" 2>/dev/null # BSD/macOS
99
+ else
100
+ date -d "$d" "+%s" 2>/dev/null # GNU/Linux
101
+ fi
102
+ }
103
+
104
+ # Given a date, set DAYS (integer; negative = expired) and STATUS.
105
+ # Sets STATUS=DATEONLY / DAYS=NO_EXP_DAYS when the date cannot be parsed.
106
+ compute() {
107
+ local d="$1" epoch now
108
+ epoch="$(date_to_epoch "$d")"
109
+ if [ -z "$epoch" ]; then DAYS="$DATEONLY_DAYS"; STATUS="DATEONLY"; return 0; fi
110
+ now="$(now_epoch)"
111
+ DAYS=$(( (epoch - now) / 86400 ))
112
+ if [ "$DAYS" -lt 0 ]; then STATUS="EXPIRED"
113
+ elif [ "$DAYS" -le "$SOON_DAYS" ]; then STATUS="SOON"
114
+ else STATUS="OK"
115
+ fi
116
+ }
117
+
118
+ # Append a record. Args: days label kind exp status orgid errname errmsg
119
+ add_rec() {
120
+ RECORDS="${RECORDS}$(printf '%s\037%s\037%s\037%s\037%s\037%s\037%s\037%s' \
121
+ "$1" "$2" "$3" "$4" "$5" "$6" "$7" "$8")
122
+ "
123
+ }
124
+
125
+ # --- collection ----------------------------------------------------------
126
+ # Determine expiration for one org and record it.
127
+ # Args: label isScratch(true|false) listExpirationDate listOrgId
128
+ collect_one() {
129
+ local label="$1" is_scratch="$2" list_exp="$3" orgid="$4"
130
+
131
+ if [ "$is_scratch" = "true" ]; then
132
+ local d="${list_exp%%T*}"; d="${d%% *}"
133
+ if [ -z "$d" ] || [ "$d" = "null" ]; then
134
+ add_rec "$NO_EXP_DAYS" "$label" "scratch" "" "NONE" "$orgid" "" ""
135
+ else
136
+ compute "$d"
137
+ add_rec "$DAYS" "$label" "scratch" "$d" "$STATUS" "$orgid" "" ""
138
+ fi
139
+ return 0
140
+ fi
141
+
142
+ # Non-scratch: TrialExpirationDate is authoritative (null = paid/production).
143
+ local out rc
144
+ out="$(sf data query \
145
+ --query "SELECT Id, TrialExpirationDate FROM Organization" \
146
+ --target-org "$label" --json 2>/dev/null)"
147
+ rc=$?
148
+ if [ "$rc" -ne 0 ]; then
149
+ local en em
150
+ en="$(printf '%s' "$out" | jq -r '.name // empty' 2>/dev/null)"
151
+ em="$(printf '%s' "$out" | jq -r '.message // empty' 2>/dev/null | tr '\t\n' ' ')"
152
+ add_rec "$AUTH_DAYS" "$label" "unknown" "" "AUTH_ERROR" "" "$en" "$em"
153
+ HAD_AUTH_ERR=1
154
+ return 1
155
+ fi
156
+
157
+ local id texp
158
+ id="$(printf '%s' "$out" | jq -r '.result.records[0].Id // empty')"
159
+ texp="$(printf '%s' "$out" | jq -r '.result.records[0].TrialExpirationDate // empty')"
160
+ [ -n "$id" ] && orgid="$id"
161
+
162
+ if [ -z "$texp" ] || [ "$texp" = "null" ]; then
163
+ add_rec "$NO_EXP_DAYS" "$label" "paid" "" "NONE" "$orgid" "" ""
164
+ else
165
+ local d="${texp%%T*}"; d="${d%% *}"
166
+ compute "$d"
167
+ add_rec "$DAYS" "$label" "trial" "$d" "$STATUS" "$orgid" "" ""
168
+ fi
169
+ return 0
170
+ }
171
+
172
+ # Build a TSV table of authenticated orgs from `sf org list`.
173
+ # Columns: label username isScratch expirationDate orgId
174
+ org_table() {
175
+ local list
176
+ list="$(sf org list --json 2>/dev/null)" || return 1
177
+ printf '%s' "$list" | jq -r --arg sep "$SEP" '
178
+ .result
179
+ | [ (.nonScratchOrgs // []), (.scratchOrgs // []), (.sandboxes // []), (.other // []) ]
180
+ | add
181
+ | map(select((.alias // .username // "") != ""))
182
+ | unique_by(.username // .alias)
183
+ | .[]
184
+ | [ (.alias // .username), (.username // ""),
185
+ ((.isScratch // false) | tostring),
186
+ (.expirationDate // ""), (.orgId // "") ]
187
+ | join($sep)'
188
+ }
189
+
190
+ # --- output helpers ------------------------------------------------------
191
+ print_header() {
192
+ [ "$OUTPUT" = "human" ] || return 0
193
+ if [ -n "$WITHIN_DAYS" ]; then
194
+ echo "Orgs expiring within $WITHIN_DAYS day(s):"
195
+ elif [ "$1" = "all" ]; then
196
+ echo "Org expiration status for all authenticated orgs:"
197
+ else
198
+ echo "Org expiration status:"
199
+ fi
200
+ }
201
+
202
+ group_of() {
203
+ case "$1" in
204
+ EXPIRED) echo 1 ;;
205
+ SOON) echo 2 ;;
206
+ OK|DATEONLY) echo 3 ;;
207
+ NONE) echo 4 ;;
208
+ *) echo 5 ;;
209
+ esac
210
+ }
211
+
212
+ group_header() {
213
+ case "$1" in
214
+ 1) echo "Expired:" ;;
215
+ 2) echo "Expiring soon (within $SOON_DAYS day(s)):" ;;
216
+ 3) echo "Active (not expiring soon):" ;;
217
+ 4) echo "No trial expiration (paid/production):" ;;
218
+ 5) echo "Could not query:" ;;
219
+ esac
220
+ }
221
+
222
+ # Actionable authentication guidance for an org that could not be queried.
223
+ auth_help() {
224
+ local label="$1" err_name="$2" err_msg="$3"
225
+ case "$err_name" in
226
+ NamedOrgNotFoundError|NoOrgFound*)
227
+ printf " %-30s not authenticated — no saved credentials for this org\n" "$label" ;;
228
+ *)
229
+ printf " %-30s could not authenticate to org\n" "$label" ;;
230
+ esac
231
+ [ -n "$err_msg" ] && printf " %-30s (%s)\n" "" "$err_msg"
232
+ echo
233
+ echo " To authenticate, run one of:"
234
+ echo " sf org login web --alias \"$label\" # interactive browser login"
235
+ echo " sf org login web --alias <my-alias> --instance-url https://test.salesforce.com # sandbox/My Domain"
236
+ echo " Then re-run this check. List existing logins with: sf org list"
237
+ }
238
+
239
+ kind_label() {
240
+ case "$1" in
241
+ trial) echo "trial/DE edition" ;;
242
+ scratch) echo "scratch org" ;;
243
+ *) echo "" ;;
244
+ esac
245
+ }
246
+
247
+ print_line() {
248
+ local days="$1" label="$2" kind="$3" exp="$4" status="$5" en="$6" em="$7"
249
+ local k; k="$(kind_label "$kind")"
250
+ case "$status" in
251
+ EXPIRED) printf " %-30s %s expired %s (EXPIRED %s day(s) ago)\n" "$label" "$k" "$exp" "$(( -days ))" ;;
252
+ SOON)
253
+ if [ "$days" -eq 0 ]; then
254
+ printf " %-30s %s expires %s (EXPIRES TODAY Warning)\n" "$label" "$k" "$exp"
255
+ else
256
+ printf " %-30s %s expires %s (expires in %s day(s) Warning)\n" "$label" "$k" "$exp" "$days"
257
+ fi ;;
258
+ OK) printf " %-30s %s expires %s (expires in %s day(s))\n" "$label" "$k" "$exp" "$days" ;;
259
+ DATEONLY) printf " %-30s %s expires %s\n" "$label" "$k" "$exp" ;;
260
+ NONE) printf " %-30s no trial expiration (paid/production org)\n" "$label" ;;
261
+ AUTH_ERROR) auth_help "$label" "$en" "$em" ;;
262
+ esac
263
+ }
264
+
265
+ sort_records() {
266
+ printf '%s' "$RECORDS" | grep -v "^[[:space:]]*$" | sort -t"$SEP" -k1,1n
267
+ }
268
+
269
+ # jq program that turns the sorted TSV into structured records (with optional
270
+ # --within filtering applied via $within).
271
+ JQ_RECORDS='
272
+ def dnum($d): ($d|tonumber) as $n | if $n >= 999999997 then null else $n end;
273
+ split("\n") | map(select(length>0)) | map(split($sep))
274
+ | map({
275
+ org: .[1], kind: .[2],
276
+ expirationDate: (if .[3]=="" then null else .[3] end),
277
+ daysRemaining: dnum(.[0]),
278
+ status: (.[4] | if .=="EXPIRED" then "expired"
279
+ elif .=="SOON" then "expiring_soon"
280
+ elif .=="OK" or .=="DATEONLY" then "active"
281
+ elif .=="NONE" then "no_expiration"
282
+ else "auth_error" end),
283
+ orgId: (if .[5]=="" then null else .[5] end)
284
+ })
285
+ | (if $within == null then .
286
+ else map(select(.daysRemaining != null and .daysRemaining <= $within)) end)'
287
+
288
+ render_json() {
289
+ sort_records | jq -R -s --arg sep "$SEP" --argjson within "${WITHIN_DAYS:-null}" "$JQ_RECORDS"
290
+ }
291
+
292
+ render_csv() {
293
+ printf 'org,kind,expiration_date,days_remaining,status,org_id\n'
294
+ sort_records | jq -R -s -r --arg sep "$SEP" --argjson within "${WITHIN_DAYS:-null}" \
295
+ "$JQ_RECORDS"' | .[]
296
+ | [ .org, .kind, (.expirationDate // ""),
297
+ (.daysRemaining | if .==null then "" else tostring end),
298
+ .status, (.orgId // "") ] | @csv'
299
+ }
300
+
301
+ print_filter_summary() {
302
+ [ -n "$WITHIN_DAYS" ] || return 0
303
+ echo
304
+ if [ "$MATCH_COUNT" -eq 0 ]; then
305
+ echo "No orgs expire within $WITHIN_DAYS day(s)."
306
+ else
307
+ echo "$MATCH_COUNT org(s) expire within $WITHIN_DAYS day(s)."
308
+ fi
309
+ }
310
+
311
+ # Human, prioritized, grouped output (soonest/expired first).
312
+ render_human() {
313
+ local sorted; sorted="$(sort_records)"
314
+ print_header "${1:-single}"
315
+ [ -n "$WITHIN_DAYS" ] && echo
316
+
317
+ local prev="" n_exp=0 n_soon=0 n_ok=0 n_none=0 n_auth=0
318
+ local days label kind exp status orgid errname errmsg grp
319
+ while IFS="$SEP" read -r days label kind exp status orgid errname errmsg; do
320
+ [ -z "${label:-}" ] && continue
321
+
322
+ if [ -n "$WITHIN_DAYS" ]; then
323
+ case "$status" in EXPIRED|SOON|OK) ;; *) continue ;; esac
324
+ [ "$days" -gt "$WITHIN_DAYS" ] && continue
325
+ MATCH_COUNT=$(( MATCH_COUNT + 1 ))
326
+ print_line "$days" "$label" "$kind" "$exp" "$status" "$errname" "$errmsg"
327
+ continue
328
+ fi
329
+
330
+ grp="$(group_of "$status")"
331
+ if [ "$grp" != "$prev" ]; then
332
+ printf '\n%s\n' "$(group_header "$grp")"
333
+ prev="$grp"
334
+ fi
335
+ print_line "$days" "$label" "$kind" "$exp" "$status" "$errname" "$errmsg"
336
+
337
+ case "$status" in
338
+ EXPIRED) n_exp=$(( n_exp + 1 )) ;;
339
+ SOON) n_soon=$(( n_soon + 1 )) ;;
340
+ OK|DATEONLY) n_ok=$(( n_ok + 1 )) ;;
341
+ NONE) n_none=$(( n_none + 1 )) ;;
342
+ AUTH_ERROR) n_auth=$(( n_auth + 1 )) ;;
343
+ esac
344
+ done <<EOF
345
+ $sorted
346
+ EOF
347
+
348
+ if [ -n "$WITHIN_DAYS" ]; then
349
+ print_filter_summary
350
+ else
351
+ echo
352
+ local summary="Summary: $n_exp expired, $n_soon expiring within $SOON_DAYS day(s), $n_ok active, $n_none with no expiration"
353
+ [ "$n_auth" -gt 0 ] && summary="$summary, $n_auth unreachable"
354
+ echo "$summary."
355
+ fi
356
+ }
357
+
358
+ # --preserve: backup commands for expiring/expired orgs (human mode only).
359
+ render_preserve() {
360
+ [ "$OUTPUT" = "human" ] || return 0
361
+ local sorted; sorted="$(sort_records)"
362
+ local printed=0 days label kind exp status orgid errname errmsg
363
+ while IFS="$SEP" read -r days label kind exp status orgid errname errmsg; do
364
+ [ -z "${label:-}" ] && continue
365
+ case "$status" in EXPIRED|SOON) ;; *) continue ;; esac
366
+ if [ "$printed" -eq 0 ]; then
367
+ echo
368
+ echo "── Preserve your work (at-risk orgs) ────────────────────────────────"
369
+ echo "When an org lapses you lose access to its metadata and data. Back it up:"
370
+ printed=1
371
+ fi
372
+ echo
373
+ echo " $label:"
374
+ echo " # 1. Capture a manifest of all metadata, then retrieve it locally:"
375
+ echo " sf project generate manifest --from-org \"$label\" --name package.xml"
376
+ echo " sf project retrieve start --target-org \"$label\" --manifest package.xml --output-dir \"./backup-$label\""
377
+ echo " # 2. Export record data per object (repeat for each object you need):"
378
+ echo " sf data export bulk --target-org \"$label\" --query \"SELECT Id, Name FROM Account\" --output-file \"./backup-$label/Account.csv\" --wait 10"
379
+ done <<EOF
380
+ $sorted
381
+ EOF
382
+ }
383
+
384
+ # --renew: trial/DE extension & reactivation guidance (human mode only).
385
+ render_renew() {
386
+ [ "$OUTPUT" = "human" ] || return 0
387
+ echo
388
+ echo "── Extend / renew a Salesforce trial or Developer Edition org ────────"
389
+ echo
390
+ echo " Partners — extend a Trial Org up to +12 months"
391
+ echo " (eligible while active or expired < 30 days; not for LDV orgs):"
392
+ echo " 1. Log in to https://partners.salesforce.com"
393
+ echo " 2. Click \"Ask Agentforce\" (bottom-right of the page)"
394
+ echo " 3. Say: \"I want to extend my Trial Org\""
395
+ echo " 4. Provide your 15- or 18-character Org ID (shown below if known)"
396
+ echo " You'll get an email once the extension is confirmed."
397
+ echo " Ref: Salesforce Help article 000387818"
398
+ echo
399
+ echo " Nonprofits — trial extension (works even if already expired):"
400
+ echo " Email your Account Executive (Setup > Your Account) with the"
401
+ echo " subject \"Trial Extension\" and include:"
402
+ echo " - Email of the customer that started the trial"
403
+ echo " - Org ID (or your org's name if you can't access the Org ID)"
404
+ echo " - Username"
405
+ echo " AMER: PowerOfUsDesk@salesforce.com"
406
+ echo " Other regions: myaccount@salesforce.com or 1-800-NO-SOFTWARE"
407
+ echo " Ref: Salesforce Help article 004754220"
408
+ echo
409
+ echo " Developer Edition orgs:"
410
+ echo " DE orgs have no subscription clock, but are deactivated after a"
411
+ echo " prolonged period (~180 days) with no login. Log in periodically to"
412
+ echo " keep yours active. If it is locked for inactivity (not yet deleted),"
413
+ echo " open a case with Salesforce Customer Support to reactivate it — once"
414
+ echo " permanently deleted an org cannot be recovered."
415
+ echo
416
+ echo " To keep a standard trial's data and configuration, convert/subscribe"
417
+ echo " to a paid edition before it lapses."
418
+
419
+ # Surface Org IDs for at-risk orgs so the steps above are actionable.
420
+ local sorted ids="" days label kind exp status orgid errname errmsg
421
+ sorted="$(sort_records)"
422
+ while IFS="$SEP" read -r days label kind exp status orgid errname errmsg; do
423
+ [ -z "${label:-}" ] && continue
424
+ case "$status" in EXPIRED|SOON) ;; *) continue ;; esac
425
+ [ -n "$orgid" ] && ids="${ids} ${label}: ${orgid}
426
+ "
427
+ done <<EOF
428
+ $sorted
429
+ EOF
430
+ if [ -n "$ids" ]; then
431
+ echo
432
+ echo " Org IDs for your at-risk orgs:"
433
+ printf '%s' "$ids"
434
+ fi
435
+ }
436
+
437
+ # --fail-if-expiring: exit 3 if any org expires within FAIL_WITHIN days.
438
+ check_fail_threshold() {
439
+ [ -n "$FAIL_WITHIN" ] || return 0
440
+ local sorted breach="" days label kind exp status orgid errname errmsg
441
+ sorted="$(sort_records)"
442
+ while IFS="$SEP" read -r days label kind exp status orgid errname errmsg; do
443
+ [ -z "${label:-}" ] && continue
444
+ case "$status" in EXPIRED|SOON|OK) ;; *) continue ;; esac
445
+ [ "$days" -le "$FAIL_WITHIN" ] && breach="${breach} ${label}"
446
+ done <<EOF
447
+ $sorted
448
+ EOF
449
+ if [ -n "$breach" ]; then
450
+ echo "ALERT: org(s) expiring within $FAIL_WITHIN day(s):${breach}" >&2
451
+ return 3
452
+ fi
453
+ return 0
454
+ }
455
+
456
+ # Render whichever output mode is selected. Args: context ("all"|"single")
457
+ render() {
458
+ case "$OUTPUT" in
459
+ json) render_json ;;
460
+ csv) render_csv ;;
461
+ *)
462
+ render_human "$1"
463
+ [ "$DO_PRESERVE" -eq 1 ] && render_preserve
464
+ [ "$DO_RENEW" -eq 1 ] && render_renew
465
+ ;;
466
+ esac
467
+ }
468
+
469
+ # --- argument parsing ----------------------------------------------------
470
+ while [ $# -gt 0 ]; do
471
+ case "$1" in
472
+ --all|-a) MODE="all"; shift ;;
473
+ --within|-w)
474
+ shift
475
+ [ $# -gt 0 ] || die "--within requires a number of days (e.g. --within 30)."
476
+ WITHIN_DAYS="$1"; WITHIN_SEEN=1; shift ;;
477
+ --within=*) WITHIN_DAYS="${1#*=}"; WITHIN_SEEN=1; shift ;;
478
+ --json) OUTPUT="json"; shift ;;
479
+ --csv) OUTPUT="csv"; shift ;;
480
+ --preserve) DO_PRESERVE=1; shift ;;
481
+ --renew) DO_RENEW=1; shift ;;
482
+ --no-scratch) INCLUDE_SCRATCH=0; shift ;;
483
+ --fail-if-expiring)
484
+ shift
485
+ FAIL_SEEN=1
486
+ if [ $# -gt 0 ] && printf '%s' "$1" | grep -qE '^[0-9]+$'; then
487
+ FAIL_WITHIN="$1"; shift
488
+ else
489
+ FAIL_WITHIN="$SOON_DAYS"
490
+ fi ;;
491
+ --fail-if-expiring=*) FAIL_WITHIN="${1#*=}"; FAIL_SEEN=1; shift ;;
492
+ --help|-h) usage; exit 0 ;;
493
+ --) shift; break ;;
494
+ -*) die "Unknown option: $1 (try --help)" ;;
495
+ *)
496
+ [ -n "$TARGET" ] && die "Specify only one org (or use --all). Got extra: $1"
497
+ TARGET="$1"; shift ;;
498
+ esac
499
+ done
500
+
501
+ # Validate integer flags. Validate whenever the flag was seen (so an empty
502
+ # value from --within= / --fail-if-expiring= is rejected, not silently ignored).
503
+ if [ "$WITHIN_SEEN" -eq 1 ]; then
504
+ case "$WITHIN_DAYS" in
505
+ ''|*[!0-9]*) die "--within expects a non-negative integer number of days (got: '$WITHIN_DAYS')." ;;
506
+ esac
507
+ fi
508
+ if [ "$FAIL_SEEN" -eq 1 ]; then
509
+ case "$FAIL_WITHIN" in
510
+ ''|*[!0-9]*) die "--fail-if-expiring expects a non-negative integer number of days (got: '$FAIL_WITHIN')." ;;
511
+ esac
512
+ fi
513
+
514
+ if [ "$OUTPUT" != "human" ]; then
515
+ if [ "$DO_PRESERVE" -eq 1 ] || [ "$DO_RENEW" -eq 1 ]; then
516
+ echo "Note: --preserve/--renew produce human-readable guidance and are ignored with --json/--csv." >&2
517
+ fi
518
+ fi
519
+
520
+ # --- dispatch ------------------------------------------------------------
521
+ [ "$MODE" = "all" ] && [ -n "$TARGET" ] && \
522
+ die "Cannot combine --all with a specific org ($TARGET)."
523
+
524
+ if [ "$MODE" = "all" ]; then
525
+ ORG_TSV="$(org_table)" || die "Could not list orgs (is the Salesforce CLI configured?)."
526
+ if [ "$INCLUDE_SCRATCH" -eq 0 ]; then
527
+ ORG_TSV="$(printf '%s' "$ORG_TSV" | awk -F"$SEP" '$3 != "true"')"
528
+ fi
529
+ if [ -z "$ORG_TSV" ]; then
530
+ # Machine-readable modes must stay parseable even with nothing to report.
531
+ case "$OUTPUT" in
532
+ json) echo "[]"; exit 0 ;;
533
+ csv) echo "org,kind,expiration_date,days_remaining,status,org_id"; exit 0 ;;
534
+ esac
535
+ if [ "$INCLUDE_SCRATCH" -eq 0 ]; then
536
+ echo "No authenticated non-scratch orgs found."
537
+ else
538
+ echo "No authenticated orgs found."
539
+ fi
540
+ echo
541
+ echo "To authenticate, run: sf org login web --alias <my-alias>"
542
+ exit 1
543
+ fi
544
+ while IFS="$SEP" read -r label username is_scratch list_exp orgid; do
545
+ [ -z "${label:-}" ] && continue
546
+ collect_one "$label" "$is_scratch" "$list_exp" "$orgid" || true
547
+ done <<EOF
548
+ $ORG_TSV
549
+ EOF
550
+ render all
551
+ rc=0; [ "$HAD_AUTH_ERR" -eq 1 ] && rc=1
552
+ check_fail_threshold || rc=3
553
+ exit "$rc"
554
+ fi
555
+
556
+ # Single org (explicit or default). Look it up in `sf org list` to learn
557
+ # whether it is a scratch org and to pick up its expirationDate / orgId.
558
+ if [ -z "$TARGET" ]; then
559
+ TARGET="$(sf config get target-org --json 2>/dev/null | jq -r '.result[0].value // empty')"
560
+ if [ -z "$TARGET" ]; then
561
+ echo "No org specified and no default 'target-org' set." >&2
562
+ echo >&2
563
+ echo "Either pass an org alias/username, use --all, or set a default:" >&2
564
+ echo " sf org login web --alias <my-alias> # authenticate a new org" >&2
565
+ echo " sf config set target-org <my-alias> # make it the default" >&2
566
+ exit 2
567
+ fi
568
+ fi
569
+
570
+ ORG_TSV="$(org_table 2>/dev/null || true)"
571
+ match="$(printf '%s' "$ORG_TSV" | awk -F"$SEP" -v t="$TARGET" '$1==t || $2==t {print; exit}')"
572
+ if [ -n "$match" ]; then
573
+ is_scratch="$(printf '%s' "$match" | awk -F"$SEP" '{print $3}')"
574
+ list_exp="$(printf '%s' "$match" | awk -F"$SEP" '{print $4}')"
575
+ orgid="$(printf '%s' "$match" | awk -F"$SEP" '{print $5}')"
576
+ if [ "$INCLUDE_SCRATCH" -eq 0 ] && [ "$is_scratch" = "true" ]; then
577
+ die "'$TARGET' is a scratch org and --no-scratch was given."
578
+ fi
579
+ collect_one "$TARGET" "$is_scratch" "$list_exp" "$orgid" || true
580
+ else
581
+ # Not in the org list — treat as a direct username; SOQL surfaces auth errors.
582
+ collect_one "$TARGET" "false" "" "" || true
583
+ fi
584
+
585
+ render single
586
+ rc=0; [ "$HAD_AUTH_ERR" -eq 1 ] && rc=1
587
+ check_fail_threshold || rc=3
588
+ exit "$rc"
@@ -3,14 +3,17 @@ name: experience-ui-bundle-app-coordinate
3
3
  description: "MUST activate when the user wants to build, create, or generate a React application, React app, web application, single-page application (SPA), or frontend application — even if no project files exist yet. MUST also activate when the project contains a uiBundles/*/src/ directory or sfdx-project.json and the prompt says create, build, construct, or generate a new app, site, or page from scratch — even if the prompt also describes visual styling. MUST also activate when the task spans more than one ui-bundle skill. Use this skill when building a complete app end-to-end. Do NOT use for Lightning Experience apps with custom objects (use platform-lightning-app-coordinate). Do NOT use for single-concern edits to an existing page (use experience-ui-bundle-frontend-generate)."
4
4
  metadata:
5
5
  version: "1.0"
6
- relatedSkills: experience-ui-bundle-project-generate, experience-ui-bundle-metadata-generate, experience-ui-bundle-features-generate, experience-ui-bundle-salesforce-data-access, experience-ui-bundle-frontend-generate, experience-ui-bundle-agentforce-client-generate, experience-ui-bundle-file-upload-generate, experience-ui-bundle-deploy, experience-ui-bundle-site-generate, experience-ui-bundle-custom-app-generate
7
- cliTools:
8
- - tool: ["sf"]
9
- semver: ">=2.0.0"
10
- - tool: ["npm"]
11
- semver: ">=7.0.0"
12
- - tool: ["npx"]
13
- semver: ">=7.0.0"
6
+ relatedSkills:
7
+ - "experience-ui-bundle-project-generate"
8
+ - "experience-ui-bundle-metadata-generate"
9
+ - "experience-ui-bundle-features-generate"
10
+ - "experience-ui-bundle-salesforce-data-access"
11
+ - "experience-ui-bundle-frontend-generate"
12
+ - "experience-ui-bundle-agentforce-client-generate"
13
+ - "experience-ui-bundle-file-upload-generate"
14
+ - "experience-ui-bundle-deploy"
15
+ - "experience-ui-bundle-site-generate"
16
+ - "experience-ui-bundle-custom-app-generate"
14
17
  ---
15
18
 
16
19
  # Building a UI Bundle App
@@ -126,7 +129,7 @@ Search, describe, and install features (auth, shadcn, search, navigation, GraphQ
126
129
  v
127
130
  Resolve conflicts (two-pass: --on-conflict error, then --conflict-resolution)
128
131
  v
129
- Integrate __example__ files into target files, then delete them
132
+ Integrate __examples__ files into target files (verify build succeeds), then delete them
130
133
  ```
131
134
 
132
135
  Installs pre-built, tested feature packages. See "Prompt Classification Keywords" above for the full trigger keyword list and negative-phrasing handling — these features provide the foundation that UI components build on top of.