@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.
- package/package.json +1 -1
- package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +239 -0
- package/skills/automation-sandbox-post-copy-config-generate/assets/config_template.json +21 -0
- package/skills/automation-sandbox-post-copy-config-generate/assets/json_schema.json +90 -0
- package/skills/automation-sandbox-post-copy-config-generate/examples/sample_sop_excerpt.md +31 -0
- package/skills/automation-sandbox-post-copy-config-generate/examples/sample_sop_to_config.json +50 -0
- package/skills/automation-sandbox-post-copy-config-generate/references/configuration_catalog.md +76 -0
- package/skills/automation-sandbox-post-copy-config-generate/references/sop_parsing_patterns.md +157 -0
- package/skills/automation-sandbox-post-copy-config-generate/references/source_format_handling.md +230 -0
- package/skills/dx-apexguru-scan/SKILL.md +403 -0
- package/skills/dx-apexguru-scan/examples/README.md +54 -0
- package/skills/dx-apexguru-scan/examples/sample-decoded-summary.json +176 -0
- package/skills/dx-apexguru-scan/examples/sample-full-no-runtime-response.json +26 -0
- package/skills/dx-apexguru-scan/examples/sample-succeeded-response.json +15 -0
- package/skills/dx-apexguru-scan/references/api-reference.md +81 -0
- package/skills/dx-apexguru-scan/references/authentication.md +134 -0
- package/skills/dx-apexguru-scan/references/error-handling.md +56 -0
- package/skills/dx-apexguru-scan/references/violation-catalog.md +28 -0
- package/skills/dx-apexguru-scan/scripts/build-zip.sh +87 -0
- package/skills/dx-apexguru-scan/scripts/decode-report.js +389 -0
- package/skills/dx-apexguru-scan/scripts/resolve-token.sh +151 -0
- package/skills/dx-apexguru-scan/scripts/run-scan.sh +153 -0
- package/skills/dx-apexguru-scan/scripts/scan.sh +96 -0
- package/skills/dx-apexguru-scan/scripts/validate-token.js +121 -0
- package/skills/dx-app-analytics-query/SKILL.md +2 -0
- package/skills/dx-devops-pipeline-manage/SKILL.md +263 -0
- package/skills/dx-devops-pipeline-manage/examples/common-workflows.md +177 -0
- package/skills/dx-devops-pipeline-manage/references/cli-commands.md +298 -0
- package/skills/dx-devops-pipeline-manage/references/parsing-patterns.md +134 -0
- package/skills/dx-devops-pipeline-manage/scripts/check-activation-ready.sh +34 -0
- package/skills/dx-devops-pipeline-manage/scripts/validate-org-type.sh +17 -0
- package/skills/dx-devops-pipeline-manage/scripts/verify-operation.sh +82 -0
- package/skills/dx-devops-promote/SKILL.md +214 -0
- package/skills/dx-devops-promote/examples/promotion-workflows.md +212 -0
- package/skills/dx-devops-promote/references/cli-commands.md +303 -0
- package/skills/experience-lwc-base-components-integrate/SKILL.md +176 -0
- package/skills/experience-lwc-base-components-integrate/references/lbc-expert-guidance.md +127 -0
- package/skills/experience-lwc-base-components-integrate/references/lightning-component-index.md +179 -0
- package/skills/experience-lwc-base-components-integrate/references/lightning-components.md +5429 -0
- package/skills/experience-lwc-base-components-integrate/scripts/extract-component-docs.sh +61 -0
- package/skills/experience-lwc-rtl-validate/SKILL.md +149 -0
- package/skills/experience-lwc-rtl-validate/references/rtl-expert.md +892 -0
- package/skills/experience-lwc-rtl-validate/scripts/scan-rtl-css.sh +206 -0
- package/skills/experience-lwc-typescript-migrate/SKILL.md +207 -0
- package/skills/experience-lwc-typescript-migrate/assets/dts-template.ts +15 -0
- package/skills/experience-lwc-typescript-migrate/assets/type-patterns.ts +44 -0
- package/skills/experience-lwc-typescript-migrate/scripts/find-consumers.sh +128 -0
- package/skills/experience-ui-bundle-localize/SKILL.md +323 -0
- package/skills/experience-ui-bundle-localize/references/gotchas.md +249 -0
- package/skills/experience-ui-bundle-localize/references/i18n-setup.md +169 -0
- package/skills/experience-ui-bundle-localize/references/interpolation.md +311 -0
- package/skills/experience-ui-bundle-localize/references/label-xml.md +282 -0
- package/skills/experience-ui-bundle-localize/references/verifying.md +219 -0
- package/skills/experience-ui-bundle-localize/scripts/check-i18n-wired.sh +195 -0
- package/skills/experience-ui-bundle-localize/scripts/check-manifest-registered.sh +100 -0
- package/skills/experience-ui-bundle-localize/scripts/check-org-api-version.sh +40 -0
- package/skills/experience-ui-bundle-localize/scripts/detect-bundle-type.sh +57 -0
- package/skills/experience-ui-bundle-site-generate/SKILL.md +3 -0
- package/skills/mobile-apps-create/SKILL.md +1 -3
- package/skills/platform-custom-lightning-type-generate/SKILL.md +3 -0
- package/skills/platform-custom-lightning-type-generate/assets/primitive-types-and-constraints.md +1 -1
- package/skills/platform-mcp-tool-widget-coordinate/SKILL.md +250 -0
- package/skills/platform-mcp-tool-widget-coordinate/examples/action-name-source-prompt.md +74 -0
- package/skills/platform-mcp-tool-widget-coordinate/examples/apex-invocable-source-prompt.md +90 -0
- package/skills/platform-mcp-tool-widget-coordinate/examples/nested-object-source-prompt.md +191 -0
- package/skills/platform-mcp-tool-widget-coordinate/examples/pasted-tool-output-prompt.md +85 -0
- package/skills/platform-mcp-tool-widget-coordinate/references/build-plan-format.md +74 -0
- package/skills/platform-mcp-tool-widget-coordinate/references/mcp-tool-output-discovery.md +184 -0
- package/skills/platform-mcp-tool-widget-coordinate/references/two-clt-modeling.md +128 -0
- 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
|
|
package/skills/platform-custom-lightning-type-generate/assets/primitive-types-and-constraints.md
CHANGED
|
@@ -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;
|
|
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`)
|