@salesforce/afv-skills 1.34.0 → 1.36.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 (70) hide show
  1. package/package.json +1 -1
  2. package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +239 -0
  3. package/skills/automation-sandbox-post-copy-config-generate/assets/config_template.json +21 -0
  4. package/skills/automation-sandbox-post-copy-config-generate/assets/json_schema.json +90 -0
  5. package/skills/automation-sandbox-post-copy-config-generate/examples/sample_sop_excerpt.md +31 -0
  6. package/skills/automation-sandbox-post-copy-config-generate/examples/sample_sop_to_config.json +50 -0
  7. package/skills/automation-sandbox-post-copy-config-generate/references/configuration_catalog.md +76 -0
  8. package/skills/automation-sandbox-post-copy-config-generate/references/sop_parsing_patterns.md +157 -0
  9. package/skills/automation-sandbox-post-copy-config-generate/references/source_format_handling.md +230 -0
  10. package/skills/dx-apexguru-scan/SKILL.md +403 -0
  11. package/skills/dx-apexguru-scan/examples/README.md +54 -0
  12. package/skills/dx-apexguru-scan/examples/sample-decoded-summary.json +176 -0
  13. package/skills/dx-apexguru-scan/examples/sample-full-no-runtime-response.json +26 -0
  14. package/skills/dx-apexguru-scan/examples/sample-succeeded-response.json +15 -0
  15. package/skills/dx-apexguru-scan/references/api-reference.md +81 -0
  16. package/skills/dx-apexguru-scan/references/authentication.md +134 -0
  17. package/skills/dx-apexguru-scan/references/error-handling.md +56 -0
  18. package/skills/dx-apexguru-scan/references/violation-catalog.md +28 -0
  19. package/skills/dx-apexguru-scan/scripts/build-zip.sh +87 -0
  20. package/skills/dx-apexguru-scan/scripts/decode-report.js +389 -0
  21. package/skills/dx-apexguru-scan/scripts/resolve-token.sh +151 -0
  22. package/skills/dx-apexguru-scan/scripts/run-scan.sh +153 -0
  23. package/skills/dx-apexguru-scan/scripts/scan.sh +96 -0
  24. package/skills/dx-apexguru-scan/scripts/validate-token.js +121 -0
  25. package/skills/dx-app-analytics-query/SKILL.md +2 -0
  26. package/skills/dx-devops-pipeline-manage/SKILL.md +263 -0
  27. package/skills/dx-devops-pipeline-manage/examples/common-workflows.md +177 -0
  28. package/skills/dx-devops-pipeline-manage/references/cli-commands.md +298 -0
  29. package/skills/dx-devops-pipeline-manage/references/parsing-patterns.md +134 -0
  30. package/skills/dx-devops-pipeline-manage/scripts/check-activation-ready.sh +34 -0
  31. package/skills/dx-devops-pipeline-manage/scripts/validate-org-type.sh +17 -0
  32. package/skills/dx-devops-pipeline-manage/scripts/verify-operation.sh +82 -0
  33. package/skills/dx-devops-promote/SKILL.md +214 -0
  34. package/skills/dx-devops-promote/examples/promotion-workflows.md +212 -0
  35. package/skills/dx-devops-promote/references/cli-commands.md +303 -0
  36. package/skills/experience-lwc-base-components-integrate/SKILL.md +176 -0
  37. package/skills/experience-lwc-base-components-integrate/references/lbc-expert-guidance.md +127 -0
  38. package/skills/experience-lwc-base-components-integrate/references/lightning-component-index.md +179 -0
  39. package/skills/experience-lwc-base-components-integrate/references/lightning-components.md +5429 -0
  40. package/skills/experience-lwc-base-components-integrate/scripts/extract-component-docs.sh +61 -0
  41. package/skills/experience-lwc-rtl-validate/SKILL.md +149 -0
  42. package/skills/experience-lwc-rtl-validate/references/rtl-expert.md +892 -0
  43. package/skills/experience-lwc-rtl-validate/scripts/scan-rtl-css.sh +206 -0
  44. package/skills/experience-lwc-typescript-migrate/SKILL.md +207 -0
  45. package/skills/experience-lwc-typescript-migrate/assets/dts-template.ts +15 -0
  46. package/skills/experience-lwc-typescript-migrate/assets/type-patterns.ts +44 -0
  47. package/skills/experience-lwc-typescript-migrate/scripts/find-consumers.sh +128 -0
  48. package/skills/experience-ui-bundle-localize/SKILL.md +323 -0
  49. package/skills/experience-ui-bundle-localize/references/gotchas.md +249 -0
  50. package/skills/experience-ui-bundle-localize/references/i18n-setup.md +169 -0
  51. package/skills/experience-ui-bundle-localize/references/interpolation.md +311 -0
  52. package/skills/experience-ui-bundle-localize/references/label-xml.md +282 -0
  53. package/skills/experience-ui-bundle-localize/references/verifying.md +219 -0
  54. package/skills/experience-ui-bundle-localize/scripts/check-i18n-wired.sh +195 -0
  55. package/skills/experience-ui-bundle-localize/scripts/check-manifest-registered.sh +100 -0
  56. package/skills/experience-ui-bundle-localize/scripts/check-org-api-version.sh +40 -0
  57. package/skills/experience-ui-bundle-localize/scripts/detect-bundle-type.sh +57 -0
  58. package/skills/experience-ui-bundle-site-generate/SKILL.md +3 -0
  59. package/skills/mobile-apps-create/SKILL.md +1 -3
  60. package/skills/platform-custom-lightning-type-generate/SKILL.md +3 -0
  61. package/skills/platform-custom-lightning-type-generate/assets/primitive-types-and-constraints.md +1 -1
  62. package/skills/platform-mcp-tool-widget-coordinate/SKILL.md +250 -0
  63. package/skills/platform-mcp-tool-widget-coordinate/examples/action-name-source-prompt.md +74 -0
  64. package/skills/platform-mcp-tool-widget-coordinate/examples/apex-invocable-source-prompt.md +90 -0
  65. package/skills/platform-mcp-tool-widget-coordinate/examples/nested-object-source-prompt.md +191 -0
  66. package/skills/platform-mcp-tool-widget-coordinate/examples/pasted-tool-output-prompt.md +85 -0
  67. package/skills/platform-mcp-tool-widget-coordinate/references/build-plan-format.md +74 -0
  68. package/skills/platform-mcp-tool-widget-coordinate/references/mcp-tool-output-discovery.md +184 -0
  69. package/skills/platform-mcp-tool-widget-coordinate/references/two-clt-modeling.md +128 -0
  70. package/skills/platform-mcp-tool-widget-coordinate/references/validation-gates.md +181 -0
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail # exit on error (-e), undefined vars (-u), and propagate pipeline failures (-o pipefail)
3
+ #
4
+ # check-i18n-wired.sh — Confirm the i18n init exists and is called at boot.
5
+ #
6
+ # Whether the app already has i18n wiring is a file-existence plus call-site
7
+ # check, so run this rather than reading for it by hand. It looks for an init
8
+ # file that defines initI18n() and for a boot-time initI18n() call in an entry
9
+ # file.
10
+ #
11
+ # Usage (run from the UI bundle dir, or pass its src path):
12
+ # bash <skill-dir>/scripts/check-i18n-wired.sh [src-dir]
13
+ #
14
+ # src-dir defaults to "src".
15
+ #
16
+ # When wiring exists, it also reports whether the label manifest is imported AND
17
+ # actually passed into the SalesforceBackend's backendOptions (not merely
18
+ # imported and left unused), so the reconcile decision is a script result rather
19
+ # than a by-hand read.
20
+ #
21
+ # Exit codes (each actionable state has its own code so the workflow branches on
22
+ # the code, not on the message text):
23
+ # 0 fully wired — initI18n() defined, called at boot, AND the manifest is
24
+ # passed into the SalesforceBackend's backendOptions. Just add new keys.
25
+ # 1 no i18n init found — no initI18n() definition. Scaffold the whole setup
26
+ # (see references/i18n-setup.md).
27
+ # 2 init defined but not called at boot — add only the boot-time initI18n()
28
+ # call in the entry file; do NOT re-scaffold or overwrite the init.
29
+ # 3 wired at boot but the manifest could not be confirmed as passed into the
30
+ # backend (not imported, no backendOptions/SalesforceBackend config, or
31
+ # imported but not seen inside a backendOptions array). This is a textual
32
+ # heuristic: the manifest may be wired via a variable/spread this check
33
+ # cannot see. VERIFY by hand; reconcile only if it genuinely dangles, and
34
+ # never clobber the existing init.
35
+ # 64 usage error — the source dir does not exist (bad argument or wrong cwd).
36
+ # This is NOT a "no init found" result; do not scaffold. Fix the path and
37
+ # re-run. (64 = EX_USAGE, kept out of the 0-3 semantic range so exit 1
38
+ # uniquely means "no init found".)
39
+ # The message still names the specific file/symbol/gap for the report, but the
40
+ # decision is the exit code.
41
+
42
+ SRC_DIR="${1:-src}"
43
+ if [ ! -d "$SRC_DIR" ]; then
44
+ echo "ERROR: source dir not found: $SRC_DIR (run from the UI bundle dir, or pass its src path)" >&2
45
+ exit 64
46
+ fi
47
+
48
+ # An init file that DEFINES initI18n (function/const declaration, not just a call).
49
+ # Use POSIX character classes, not \s / \b: BSD/macOS grep -E does not honor those.
50
+ # A trailing [^A-Za-z0-9_] (or end of line) stands in for the word boundary so
51
+ # initI18nFoo does not match.
52
+ DEFINES=$(grep -rlE '(function|const)[[:space:]]+initI18n([^A-Za-z0-9_]|$)|initI18n[[:space:]]*=' "$SRC_DIR" \
53
+ --include='*.ts' --include='*.tsx' 2>/dev/null | head -1 || true)
54
+
55
+ # A boot-time call to initI18n() somewhere other than its own definition file.
56
+ CALLS=$(grep -rlE '(^|[^A-Za-z0-9_])initI18n[[:space:]]*\(' "$SRC_DIR" \
57
+ --include='*.ts' --include='*.tsx' 2>/dev/null \
58
+ | grep -vF "${DEFINES:-/dev/null}" | head -1 || true)
59
+
60
+ if [ -n "$DEFINES" ] && [ -n "$CALLS" ]; then
61
+ # Wiring exists. Determine whether the manifest is already reconciled into the
62
+ # backend. Importing the manifest is not enough: an init can `import
63
+ # { labelManifest }` and still omit it from backendOptions, in which case the
64
+ # SalesforceBackend fetches nothing and every key renders as its own literal
65
+ # name. So confirm three things, not just token presence:
66
+ # 1. the manifest is imported (capture the symbol it binds),
67
+ # 2. a backendOptions construction exists (where the SDK backend is fed),
68
+ # 3. that symbol appears INSIDE the backendOptions array, not merely
69
+ # somewhere in the source.
70
+ # (3) is what separates "imported and wired" from "imported but dropped". A
71
+ # bare reference elsewhere (e.g. console.log(labelManifest)) must not count:
72
+ # the backend only receives the manifest if it sits in backendOptions.
73
+ # Search the WHOLE src tree, not just the init file: a valid app may define
74
+ # initI18n() in one file and build the SalesforceBackend (with the manifest)
75
+ # in a separate backend module or the entry file. Scanning only $DEFINES
76
+ # falsely reports "not reconciled" for that layout. Find the manifest import
77
+ # wherever it lives, and the file that actually constructs backendOptions.
78
+ # Is the manifest imported anywhere at all? (Used only for the "manifest
79
+ # import missing" message; the wired/dangling decision is made per-file below.)
80
+ MANIFEST_IMPORT_LINE=$(grep -rhnE '^[[:space:]]*import[^A-Za-z0-9_].*(label-manifest|labelManifest)' "$SRC_DIR" \
81
+ --include='*.ts' --include='*.tsx' 2>/dev/null | head -1 || true)
82
+ # EVERY file that constructs backendOptions, not just the first: a decoy (a
83
+ # comment, a types file, an unused second backend) can sort earlier in the
84
+ # tree walk, so we check whether the manifest lands in ANY file's backendOptions.
85
+ BACKEND_FILES=$(grep -rlE 'backendOptions' "$SRC_DIR" \
86
+ --include='*.ts' --include='*.tsx' 2>/dev/null || true)
87
+ # Any backend config token at all, for the "no backend config" message.
88
+ BACKEND_OPTS=$(grep -rlE 'backendOptions|SalesforceBackend' "$SRC_DIR" \
89
+ --include='*.ts' --include='*.tsx' 2>/dev/null | head -1 || true)
90
+
91
+ # sed program that resolves the manifest's LOCAL name from an import line.
92
+ # Two forms bind a local name, plus the default import:
93
+ # named: import { ..., labelManifest, ... } -> labelManifest
94
+ # aliased: import { ..., labelManifest as foo } -> foo
95
+ # default: import labelManifest from ... -> labelManifest
96
+ # Avoid \b: BSD/macOS sed does not support it.
97
+ RESOLVE_SYMBOL='s/.*[{,][[:space:]]*labelManifest[[:space:]]+as[[:space:]]+([A-Za-z0-9_]+).*/\1/; t
98
+ s/.*[{,][[:space:]]*labelManifest[[:space:]]*[,}].*/labelManifest/; t
99
+ s/.*import[[:space:]]+labelManifest[[:space:]]+from.*/labelManifest/; t
100
+ s/.*/labelManifest/'
101
+
102
+ # Determine whether the manifest is actually passed into a backendOptions
103
+ # array (not merely imported and left unused). Importing is not enough: an
104
+ # init can `import { labelManifest }` and still omit it from backendOptions,
105
+ # in which case the backend fetches nothing and every key renders as its own
106
+ # literal name. Resolve the symbol PER FILE from that file's own import: an
107
+ # imported binding can only be used in the file that imports it, so the name
108
+ # passed into a file's backendOptions is whatever that same file bound (named,
109
+ # aliased, or default). Resolving per file avoids a cross-file alias mismatch
110
+ # (file A imports `labelManifest`, file B imports it `as m` and uses `m`).
111
+ # The awk emits every backendOptions array block in the file (bracket-balanced
112
+ # from each "[" after a "backendOptions" token); we grep those blocks for the
113
+ # file's own symbol. A bare reference elsewhere (console.log(labelManifest))
114
+ # is outside every block, so it does not count.
115
+ MANIFEST_USED=""
116
+ MANIFEST_SYMBOL="labelManifest" # fallback for the message if no hit is found
117
+ if [ -n "$BACKEND_FILES" ]; then
118
+ while IFS= read -r BACKEND_FILE; do
119
+ [ -z "$BACKEND_FILE" ] && continue
120
+ FILE_IMPORT=$(grep -nE '^[[:space:]]*import[^A-Za-z0-9_].*(label-manifest|labelManifest)' \
121
+ "$BACKEND_FILE" 2>/dev/null | head -1 || true)
122
+ [ -z "$FILE_IMPORT" ] && continue # this file cannot use a binding it never imported
123
+ SYM=$(printf '%s\n' "$FILE_IMPORT" | sed -E "$RESOLVE_SYMBOL")
124
+ [ -z "$SYM" ] && continue
125
+ # Emit every backendOptions array block. State (started/seeking/capturing/
126
+ # depth/out) is awk-global and persists across lines so a multi-line array
127
+ # is captured whole. After a "backendOptions" token we SEEK its opening
128
+ # "[": only whitespace and ":" may sit between token and bracket. If any
129
+ # other non-space char comes first, the token is not followed by an array
130
+ # literal (e.g. `backendOptions: buildOpts()`), so abandon it and re-scan —
131
+ # without this, `started` would leak forward and capture an unrelated later
132
+ # array as if it were the options array (a false "wired" result).
133
+ # Known limit: a "]" inside a string literal in the array (e.g.
134
+ # `["a]b", labelManifest]`) closes the block early and can miss the symbol.
135
+ # That only ever pushes toward exit 3 (verify by hand), never a false exit
136
+ # 0, so it is safe under the heuristic framing; not worth a string parser.
137
+ BLOCKS=$(sed -E 's://.*$::' "$BACKEND_FILE" | awk '
138
+ {
139
+ line = $0; pos = 1; n = length(line)
140
+ while (pos <= n) {
141
+ if (!seeking && !capturing) {
142
+ idx = index(substr(line, pos), "backendOptions")
143
+ if (idx == 0) break
144
+ seeking = 1
145
+ pos = pos + idx - 1 + 14 # 14 = length("backendOptions")
146
+ continue
147
+ }
148
+ c = substr(line, pos, 1)
149
+ if (seeking) {
150
+ if (c == "[") { seeking = 0; capturing = 1; depth = 1; out = c; pos++; continue }
151
+ if (c == " " || c == "\t" || c == ":") { pos++; continue }
152
+ seeking = 0; continue # not an array literal -> re-scan for next token
153
+ }
154
+ # capturing
155
+ out = out c
156
+ if (c == "[") depth++
157
+ if (c == "]") { depth--; if (depth <= 0) { print out; out=""; capturing=0 } }
158
+ pos++
159
+ }
160
+ if (capturing) out = out "\n"
161
+ }
162
+ ')
163
+ if printf '%s' "$BLOCKS" \
164
+ | grep -qE "(^|[^A-Za-z0-9_])${SYM}([^A-Za-z0-9_]|$)" 2>/dev/null; then
165
+ MANIFEST_USED="yes"; MANIFEST_SYMBOL="$SYM"; break
166
+ fi
167
+ done <<EOF
168
+ $BACKEND_FILES
169
+ EOF
170
+ fi
171
+
172
+ if [ -n "$MANIFEST_USED" ]; then
173
+ echo "i18n wired: initI18n() defined in $DEFINES, called at boot in $CALLS; the manifest ($MANIFEST_SYMBOL) is imported and passed into the backend config -> add new keys to the manifest, do not clobber the init"
174
+ exit 0
175
+ fi
176
+ # Could not confirm the manifest is passed into a backendOptions array. This
177
+ # is a heuristic textual check, so treat exit 3 as "verify, do not clobber":
178
+ # the manifest may be wired through a form this script cannot see (passed via
179
+ # a variable, spread, or helper). Report the specific gap for the human/agent
180
+ # to confirm before editing; only reconcile if it genuinely dangles.
181
+ MISSING=""
182
+ [ -z "$MANIFEST_IMPORT_LINE" ] && MISSING="manifest import"
183
+ [ -z "$BACKEND_OPTS" ] && MISSING="${MISSING:+$MISSING and }backendOptions/SalesforceBackend config"
184
+ [ -n "$MANIFEST_IMPORT_LINE" ] && [ -n "$BACKEND_OPTS" ] \
185
+ && MISSING="${MISSING:+$MISSING and }could not confirm the manifest is passed into backendOptions (it may dangle unused, or be wired via a variable this check cannot see)"
186
+ echo "i18n wired: initI18n() defined in $DEFINES, called at boot in $CALLS; but $MISSING -> verify by hand; if it genuinely dangles, reconcile (import the manifest AND pass it into the SalesforceBackend's backendOptions) without clobbering"
187
+ exit 3
188
+ fi
189
+
190
+ if [ -z "$DEFINES" ]; then
191
+ echo "no i18n init found (no initI18n() definition under $SRC_DIR) -> scaffold it"
192
+ exit 1
193
+ fi
194
+ echo "initI18n() defined in $DEFINES but not called at boot -> add the boot-time call"
195
+ exit 2
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail # exit on error (-e), undefined vars (-u), and propagate pipeline failures (-o pipefail)
3
+ #
4
+ # check-manifest-registered.sh — Confirm every t("Key") call site is registered
5
+ # in label-manifest.ts.
6
+ #
7
+ # An unregistered key renders as its own literal name at runtime with no error —
8
+ # the central silent-failure trap this skill exists to prevent. That check is a
9
+ # pure set comparison (call-site keys vs manifest keys), so run this rather than
10
+ # counting entries by hand.
11
+ #
12
+ # Keys are compared on their trailing segment: a manifest entry "c:Welcome_Text"
13
+ # matches a call site t("Welcome_Text") or t("c:Welcome_Text") either way. The
14
+ # manifest's boilerplate example comment is stripped first so a commented-out
15
+ # sample key does not mask a genuinely missing one.
16
+ #
17
+ # Usage (run from the UI bundle dir, or pass its src path):
18
+ # bash <skill-dir>/scripts/check-manifest-registered.sh [src-dir]
19
+ #
20
+ # src-dir defaults to "src". label-manifest.ts is found anywhere under it.
21
+ #
22
+ # Exit codes (kept aligned with check-i18n-wired.sh so the workflow branches on
23
+ # the code, not the message text):
24
+ # 0 every t() key is registered (or there are no t() calls / no manifest to
25
+ # cross-check — nothing to gate)
26
+ # 1 one or more t() keys are missing from the manifest — stop and register them
27
+ # 64 usage error — the source dir does not exist (bad argument or wrong cwd).
28
+ # This is NOT a "keys missing" result; do not scaffold or register. Fix the
29
+ # path and re-run. (64 = EX_USAGE, kept out of the 0/1 semantic range so
30
+ # exit 1 uniquely means "keys missing".)
31
+
32
+ SRC_DIR="${1:-src}"
33
+ if [ ! -d "$SRC_DIR" ]; then
34
+ echo "ERROR: source dir not found: $SRC_DIR (run from the UI bundle dir, or pass its src path)" >&2
35
+ exit 64
36
+ fi
37
+
38
+ MANIFEST=$(find "$SRC_DIR" -type f -name 'label-manifest.ts' | head -1 || true)
39
+
40
+ # Collect t("Key") / t('Key') call sites from component files, keeping the
41
+ # trailing key segment (drop any "ns:" prefix). Use POSIX character classes and
42
+ # an explicit boundary, not \b / \s: BSD/macOS grep -E silently ignores those,
43
+ # which would miss call sites on a Mac (the platform this skill targets) and let
44
+ # an unregistered key slip through — the exact silent-fail this script guards.
45
+ # (^|[^A-Za-z0-9_]) before t( stops "insertText(" / "print(" matching as t(.
46
+ CALLED=$(grep -rhoE "(^|[^A-Za-z0-9_])t\([[:space:]]*[\"'\`][^\"'\`]+[\"'\`]" "$SRC_DIR" \
47
+ --include='*.tsx' --include='*.jsx' 2>/dev/null \
48
+ | sed -E "s/.*[\"'\`]([^\"'\`]+)[\"'\`].*/\1/; s/.*://" \
49
+ | sort -u || true)
50
+
51
+ if [ -z "$CALLED" ]; then
52
+ echo "no t() call sites to check (scenario may not require the manifest) -> proceed"
53
+ exit 0
54
+ fi
55
+ if [ -z "$MANIFEST" ]; then
56
+ echo "ERROR: t() call sites exist but no label-manifest.ts was found under $SRC_DIR -> register them" >&2
57
+ echo "$CALLED" | sed 's/^/ missing: /' >&2
58
+ exit 1
59
+ fi
60
+
61
+ # Manifest keys: strip comments first so a boilerplate example key (the scaffold
62
+ # ships one) does not count as registered and mask a genuinely missing key — the
63
+ # exact silent-fail this script guards. Block comments can span lines, so a
64
+ # line-based `s:/\*.*\*/::` misses a multi-line `/* ... */` around an example
65
+ # key; use an awk state machine that carries the in-comment flag across lines,
66
+ # then strip `//` line comments. Keys hold no `/`, so this never eats a key.
67
+ REGISTERED=$(awk '
68
+ { s = $0 }
69
+ {
70
+ out = ""
71
+ i = 1
72
+ n = length(s)
73
+ while (i <= n) {
74
+ if (incomment) {
75
+ if (substr(s, i, 2) == "*/") { incomment = 0; i += 2 }
76
+ else { i++ }
77
+ } else if (substr(s, i, 2) == "/*") {
78
+ incomment = 1; i += 2
79
+ } else {
80
+ out = out substr(s, i, 1); i++
81
+ }
82
+ }
83
+ print out
84
+ }
85
+ ' "$MANIFEST" \
86
+ | sed -E 's://.*$::' \
87
+ | grep -oE "[\"'\`][^\"'\`]+[\"'\`]" \
88
+ | sed -E "s/[\"'\`]//g; s/.*://" \
89
+ | sort -u || true)
90
+
91
+ MISSING=$(comm -23 <(echo "$CALLED") <(echo "$REGISTERED") || true)
92
+
93
+ if [ -n "$MISSING" ]; then
94
+ echo "ERROR: t() keys not registered in label-manifest.ts (they render as literal key names at runtime):" >&2
95
+ echo "$MISSING" | sed 's/^/ /' >&2
96
+ exit 1
97
+ fi
98
+
99
+ echo "all $(echo "$CALLED" | wc -l | tr -d ' ') t() key(s) registered in the manifest -> proceed"
100
+ exit 0
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail # exit on error (-e), undefined vars (-u), and propagate pipeline failures (-o pipefail)
3
+ #
4
+ # check-org-api-version.sh — Verify a target org supports API v68.0+ (Release 264).
5
+ #
6
+ # Runtime label resolution over GraphQL for UI Bundles ships in Salesforce
7
+ # Release 264 (API v68.0+). A `sourceApiVersion` in sfdx-project.json records
8
+ # what you declared, not what the org supports, so a newer CLI pointed at an
9
+ # older org can pass a static file check and then fail at runtime. This queries
10
+ # the org's actual maximum API version before you wire anything.
11
+ #
12
+ # `sf api request rest` keeps authentication at the CLI transport layer, so no
13
+ # access token enters this script's context. Do not extract a token and curl.
14
+ #
15
+ # Usage:
16
+ # bash <skill-dir>/scripts/check-org-api-version.sh <org-alias-or-username>
17
+ #
18
+ # Exit codes:
19
+ # 0 org supports v68.0+ — proceed
20
+ # 1 org's max API version is below v68.0, or it could not be determined — stop
21
+
22
+ if [ "$#" -ne 1 ] || [ -z "${1:-}" ]; then
23
+ echo "ERROR: pass exactly one org alias or username. Usage: check-org-api-version.sh <org-alias-or-username>" >&2
24
+ exit 1
25
+ fi
26
+ ORG="$1"
27
+
28
+ # FORCE_COLOR=0 stops the CLI from emitting ANSI color codes into stdout JSON,
29
+ # which would otherwise make jq fail with "Invalid numeric literal".
30
+ MAX_API=$(FORCE_COLOR=0 sf api request rest "/services/data/" --target-org "$ORG" \
31
+ | jq -r '[.[].version | tonumber] | max') \
32
+ || { echo "ERROR: could not determine the target org's API version (auth or network problem?). Verify the org is reachable, then retry." >&2; exit 1; }
33
+
34
+ [ -n "$MAX_API" ] && [ "$MAX_API" != "null" ] \
35
+ || { echo "ERROR: the org returned an empty API version. Verify the target org and retry." >&2; exit 1; }
36
+
37
+ awk -v v="$MAX_API" 'BEGIN{ exit !(v+0 >= 68.0) }' \
38
+ || { echo "ERROR: runtime label localization for UI Bundles requires Salesforce Release 264 (API v68.0+). Target org's maximum supported API version is v${MAX_API}. Retarget a Release 264+ org, or upgrade the org before adding localization." >&2; exit 1; }
39
+
40
+ echo "OK: target org '${ORG}' supports API v${MAX_API} (>= v68.0). Proceed."
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail # exit on error (-e), undefined vars (-u), and propagate pipeline failures (-o pipefail)
3
+ #
4
+ # detect-bundle-type.sh — Classify a UI Bundle as authenticated (supported) or
5
+ # a site (not yet supported) before localizing it.
6
+ #
7
+ # The bundle's type is decided by deterministic file and string checks, so run
8
+ # this rather than interpreting them by hand. It classifies in a fixed order and
9
+ # stops at the first hit:
10
+ # - in-core internal: ui-bundle.json accessCheck starts with "LwrInternalApp." -> proceed
11
+ # - B2E: a CustomApplication whose <uiBundle> names this bundle -> proceed
12
+ # - site: a digitalExperienceConfigs/*.digitalExperienceConfig-meta.xml -> NOT supported, stop
13
+ # - anything else: unbound / cannot auto-detect -> ask the user
14
+ #
15
+ # The B2E and site checks are package-local: CustomApplication and site config
16
+ # metadata are siblings of uiBundles/ under the package source path
17
+ # (e.g. force-app/main/default/{applications,uiBundles}/), so the script derives
18
+ # that metadata root from the bundle path rather than the current directory. If
19
+ # the bundle is bound elsewhere the script returns 2 (ask the user), the correct
20
+ # fallback. Do not gate on a <target> element inside *.uibundle-meta.xml;
21
+ # CLI-generated bundles do not carry it.
22
+ #
23
+ # Usage (pass the full path to the bundle dir; cwd does not matter):
24
+ # bash <skill-dir>/scripts/detect-bundle-type.sh <path-to-uiBundles/<name>/ dir>
25
+ #
26
+ # Exit codes:
27
+ # 0 authenticated app (in-core internal or B2E) — proceed
28
+ # 1 site (B2C/B2B) — NOT supported; stop and tell the user
29
+ # 2 unbound / cannot auto-detect — ask the user to confirm the bundle is
30
+ # an authenticated app (B2E or in-core internal); stop if they cannot
31
+
32
+ if [ "$#" -ne 1 ] || [ -z "${1:-}" ]; then
33
+ echo "ERROR: pass exactly one bundle directory. Usage: detect-bundle-type.sh <path-to-uiBundles/<name>/ dir>" >&2
34
+ exit 2
35
+ fi
36
+ bundleDir="${1%/}"
37
+ bundle=$(basename "$bundleDir")
38
+
39
+ # CustomApplication and site metadata sit beside uiBundles/ under the package
40
+ # source path, not at the cwd. Derive that metadata root from the bundle path:
41
+ # it's the parent of the uiBundles/ segment. Fall back to the cwd for a
42
+ # flattened layout with no uiBundles/ segment.
43
+ metaRoot="."
44
+ case "$bundleDir" in
45
+ */uiBundles/*) metaRoot="${bundleDir%/uiBundles/*}" ;;
46
+ esac
47
+
48
+ if [ -f "${bundleDir}/ui-bundle.json" ] \
49
+ && jq -e '(.accessCheck // "") | startswith("LwrInternalApp.")' "${bundleDir}/ui-bundle.json" >/dev/null 2>&1; then
50
+ echo "in-core internal app -> proceed"; exit 0
51
+ elif grep -rlq --include='*.app-meta.xml' "<uiBundle>${bundle}</uiBundle>" "${metaRoot}/applications" 2>/dev/null; then
52
+ echo "B2E (CustomApplication-bound) -> proceed"; exit 0
53
+ elif ls "${metaRoot}"/digitalExperienceConfigs/*.digitalExperienceConfig-meta.xml >/dev/null 2>&1; then
54
+ echo "site (B2C/B2B) -> NOT supported; stop"; exit 1
55
+ else
56
+ echo "unbound / cannot auto-detect -> ask the user"; exit 2
57
+ fi
@@ -3,6 +3,9 @@ name: experience-ui-bundle-site-generate
3
3
  description: "MUST activate when the project contains a uiBundles/*/src/ directory and the task involves creating or configuring site infrastructure. Use this skill when creating or configuring a Salesforce Digital Experience Site for hosting a UI bundle. Activate when files matching digitalExperiences/, networks/, customSite/, or DigitalExperienceBundle exist and need modification, or when the user wants to publish, host, or configure guest access for their app."
4
4
  metadata:
5
5
  version: "1.0"
6
+ cliTools:
7
+ - tool: ["sf"]
8
+ semver: ">=2.0.0"
6
9
  ---
7
10
 
8
11
  # Digital Experience Site for React UI Bundles
@@ -2,12 +2,10 @@
2
2
  name: mobile-apps-create
3
3
  description: "The entry point for building any Salesforce native mobile app on iOS or Android. TRIGGER when the user says: \"build a Salesforce iOS app\", \"add Salesforce login to my Android app\", \"set up Mobile SDK\", \"add MobileSync / SmartStore offline storage\", \"embed an Agentforce agent in my mobile app\", \"add Agentforce chat to iOS/Android\", or otherwise asks to create, extend, or integrate a Salesforce mobile experience in Swift or Kotlin (MSDK, Agentforce SDK, or both). SKIP when the user is building a non-Salesforce mobile app, using React Native / Flutter / Ionic without Salesforce integration, asking about generic mobile UI design, or working on a Salesforce-adjacent web/desktop surface (LWC, Experience Cloud, Mobile Publisher branding-only)."
4
4
  metadata:
5
+ version: "1.0"
5
6
  cliTools:
6
7
  - tool: ["npx"]
7
8
  semver: ">=9.0.0"
8
- relatedSkills:
9
- - "mobile-platform-native-capabilities-integrate"
10
- version: "1.0"
11
9
  ---
12
10
 
13
11
  # Salesforce Mobile
@@ -6,6 +6,7 @@ metadata:
6
6
  minApiVersion: "60.0"
7
7
  relatedSkills:
8
8
  - "platform-lightning-type-widget-coordinate"
9
+ - "platform-mcp-tool-widget-coordinate"
9
10
  - "platform-widget-generate"
10
11
  ---
11
12
 
@@ -57,6 +58,7 @@ Custom Lightning Types (CLTs) are JSON Schema-based type definitions used by the
57
58
  - **Apex class CLTs are minimal**:
58
59
  - Include **only** `title`, `description` (optional), and `lightning:type` set to `@apexClassType/...`.
59
60
  - Do **not** add `type`, `properties`, `required`, or `unevaluatedProperties`.
61
+ - **Custom LWC renderers/editors on an Apex class CLT MUST NOT use `attributes` in the root override — this overrides any prompt wording to the contrary.** Since the schema has no `properties` block, there is nothing for `{!$attrs.<name>}` to resolve against — `unevaluatedProperties: false` will reject any attribute key (e.g. `"You can't add the flightId property ... because the unevaluatedProperties keyword value is set to false"`). Use `"componentOverrides": { "$": { "definition": "c/<yourComponent>" } }` with **no `attributes` key** at all. **If the user's prompt explicitly asks for attribute mappings to specific fields (e.g. "with attribute mappings for fieldA, fieldB") on an Apex-class CLT renderer/editor, do NOT comply literally** — omit `attributes` from the root override anyway, and say so in your response (e.g. "Note: attribute mappings were omitted because the backing type is an Apex-class CLT, which has no `properties` block to bind against").
60
62
  - **No shell metacharacters that trigger the Vibes safe-shell filter.** In any Bash tool call emitted by this skill, do NOT use command substitution (`$(…)` or backticks), process substitution (`<(…)`, `>(…)`), brace expansion (`{a,b,c}` or `{1..N}`), or `eval` / `exec`. Vibes forces manual approval on these patterns even under Bypass mode and stalls the eval. Emit separate commands (`mkdir -p a && mkdir -p b`) or print each value with its own command and reason about the output rather than capturing it in a shell variable.
61
63
 
62
64
  ## Additional CLT Metaschema Validations
@@ -158,6 +160,7 @@ When you need the full list of supported primitive `lightning:type` identifiers,
158
160
  | `additionalProperties` error on layout attributes | Adding `label` or other attributes to `lightning/propertyLayout` | Only use `property` attribute in `lightning/propertyLayout`. Remove `label`, `title`, or any other attributes |
159
161
  | Invalid target configuration for custom LWC | Custom LWC component's `-meta.xml` missing required target (`lightning__AgentforceInput` or `lightning__AgentforceOutput`) | Add correct target to LWC's `-meta.xml`: use `lightning__AgentforceInput` for editors, `lightning__AgentforceOutput` for renderers |
160
162
  | Attribute mapping doesn't exist in type schema | Using `{!$attrs.propertyName}` where `propertyName` is not defined in schema | Ensure all attribute mappings reference actual properties in your type schema's `properties` section |
163
+ | `unevaluatedProperties` error on custom LWC renderer for an Apex class CLT | Root override `attributes` mapping used on an Apex class CLT, which has no `properties` block to validate against | Remove `attributes` entirely from the root override; use `"componentOverrides": { "$": { "definition": "c/<component>" } }` only |
161
164
  | `additionalProperties` error with deprecated keys | Using `propertyRenderers` or `view` in editor/renderer config | Replace deprecated `propertyRenderers` with `componentOverrides` and `view` with `layout` |
162
165
  | Type mismatch in component attributes | Passing wrong type for component attribute (e.g., integer instead of string) | Ensure attribute values match the expected type defined by the component |
163
166
 
@@ -38,4 +38,4 @@ When strict validation is enabled (`unevaluatedProperties: false`), keep each pr
38
38
  - `const`, `enum`
39
39
  - `lightning:textIndexed`, `lightning:supportsPersonalization`, `lightning:localizable`
40
40
  - `lightning:uiOptions`, `lightning:allowedUrlSchemes`
41
- - `lightning:tags` (metaschema restricts values; currently `flow` is the only known allowed tag)
41
+ - `lightning:tags` (metaschema restricts values; known allowed tags are `flow` and `mcp` — use `mcp` on object-based CLTs that model an MCP-tool envelope/response, per `platform-mcp-tool-widget-coordinate`)