@salesforce/afv-skills 1.34.0 → 1.35.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 (67) 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-devops-pipeline-manage/SKILL.md +263 -0
  26. package/skills/dx-devops-pipeline-manage/examples/common-workflows.md +177 -0
  27. package/skills/dx-devops-pipeline-manage/references/cli-commands.md +298 -0
  28. package/skills/dx-devops-pipeline-manage/references/parsing-patterns.md +134 -0
  29. package/skills/dx-devops-pipeline-manage/scripts/check-activation-ready.sh +34 -0
  30. package/skills/dx-devops-pipeline-manage/scripts/validate-org-type.sh +17 -0
  31. package/skills/dx-devops-pipeline-manage/scripts/verify-operation.sh +82 -0
  32. package/skills/dx-devops-promote/SKILL.md +214 -0
  33. package/skills/dx-devops-promote/examples/promotion-workflows.md +212 -0
  34. package/skills/dx-devops-promote/references/cli-commands.md +303 -0
  35. package/skills/experience-lwc-base-components-integrate/SKILL.md +176 -0
  36. package/skills/experience-lwc-base-components-integrate/references/lbc-expert-guidance.md +127 -0
  37. package/skills/experience-lwc-base-components-integrate/references/lightning-component-index.md +179 -0
  38. package/skills/experience-lwc-base-components-integrate/references/lightning-components.md +5429 -0
  39. package/skills/experience-lwc-base-components-integrate/scripts/extract-component-docs.sh +61 -0
  40. package/skills/experience-lwc-rtl-validate/SKILL.md +149 -0
  41. package/skills/experience-lwc-rtl-validate/references/rtl-expert.md +892 -0
  42. package/skills/experience-lwc-rtl-validate/scripts/scan-rtl-css.sh +206 -0
  43. package/skills/experience-lwc-typescript-migrate/SKILL.md +207 -0
  44. package/skills/experience-lwc-typescript-migrate/assets/dts-template.ts +15 -0
  45. package/skills/experience-lwc-typescript-migrate/assets/type-patterns.ts +44 -0
  46. package/skills/experience-lwc-typescript-migrate/scripts/find-consumers.sh +128 -0
  47. package/skills/experience-ui-bundle-localize/SKILL.md +323 -0
  48. package/skills/experience-ui-bundle-localize/references/gotchas.md +249 -0
  49. package/skills/experience-ui-bundle-localize/references/i18n-setup.md +169 -0
  50. package/skills/experience-ui-bundle-localize/references/interpolation.md +311 -0
  51. package/skills/experience-ui-bundle-localize/references/label-xml.md +282 -0
  52. package/skills/experience-ui-bundle-localize/references/verifying.md +219 -0
  53. package/skills/experience-ui-bundle-localize/scripts/check-i18n-wired.sh +195 -0
  54. package/skills/experience-ui-bundle-localize/scripts/check-manifest-registered.sh +100 -0
  55. package/skills/experience-ui-bundle-localize/scripts/check-org-api-version.sh +40 -0
  56. package/skills/experience-ui-bundle-localize/scripts/detect-bundle-type.sh +57 -0
  57. package/skills/platform-custom-lightning-type-generate/SKILL.md +3 -0
  58. package/skills/platform-custom-lightning-type-generate/assets/primitive-types-and-constraints.md +1 -1
  59. package/skills/platform-mcp-tool-widget-coordinate/SKILL.md +250 -0
  60. package/skills/platform-mcp-tool-widget-coordinate/examples/action-name-source-prompt.md +74 -0
  61. package/skills/platform-mcp-tool-widget-coordinate/examples/apex-invocable-source-prompt.md +90 -0
  62. package/skills/platform-mcp-tool-widget-coordinate/examples/nested-object-source-prompt.md +191 -0
  63. package/skills/platform-mcp-tool-widget-coordinate/examples/pasted-tool-output-prompt.md +85 -0
  64. package/skills/platform-mcp-tool-widget-coordinate/references/build-plan-format.md +74 -0
  65. package/skills/platform-mcp-tool-widget-coordinate/references/mcp-tool-output-discovery.md +184 -0
  66. package/skills/platform-mcp-tool-widget-coordinate/references/two-clt-modeling.md +128 -0
  67. package/skills/platform-mcp-tool-widget-coordinate/references/validation-gates.md +181 -0
@@ -0,0 +1,403 @@
1
+ ---
2
+ name: dx-apexguru-scan
3
+ description: "Run an ApexGuru performance scan on a Salesforce Apex project via the ApexGuru SFAP Scan API. Zips the project's Apex (any layout), submits it, polls to completion, decodes the base64 report, and presents performance antipattern violations (SOQL in loop, DML in loop, Schema.getGlobalDescribe(), SOQL without WHERE/LIMIT, unused SOQL fields) grouped by rule with severity, file:line, and suggested fixes — clearly attributed as 'Static only' or 'Production insights'. TRIGGER when the user says 'run ApexGuru', 'ApexGuru scan', 'check Apex performance', 'find governor-limit / performance antipatterns', 'SOQL in loop', 'scan my Apex for performance', or 'ApexGuru performance insights'. DO NOT TRIGGER for general static analysis or security scans (use dx-code-analyzer-run), for fixing code without scanning, or for onboarding an org to ApexGuru."
4
+ allowed-tools: Read, Bash(bash), Bash(node), Bash(curl), Bash(zip), Bash(unzip), Bash(jq), Bash(sf), Bash(date), Write
5
+ argument-hint: "[project-path] [--org <alias>] [--fast]"
6
+ metadata:
7
+ version: "1.1"
8
+ relatedSkills:
9
+ - "dx-code-analyzer-run"
10
+ cliTools:
11
+ - tool: ["curl"]
12
+ semver: ">=7.0.0"
13
+ - tool: ["jq"]
14
+ semver: ">=1.6.0"
15
+ - tool: ["node"]
16
+ semver: ">=18.0.0"
17
+ - tool: ["sf"]
18
+ semver: ">=2.0.0"
19
+ ---
20
+
21
+ # ApexGuru Performance Scan Skill
22
+
23
+ ## CRITICAL: Mandatory Script Usage
24
+
25
+ Every step — token resolution, zipping, API calls, and report decoding — MUST go
26
+ through the bundled scripts in `<skill_dir>/scripts/`. No exceptions.
27
+
28
+ ### WRONG — never do this:
29
+
30
+ ```bash
31
+ # WRONG: hand-rolled curl to the API
32
+ curl -X POST https://api.salesforce.com/... -F file=@x.zip
33
+
34
+ # WRONG: inline base64 + jq to read the report
35
+ cat raw.json | jq -r .report | base64 -d | jq '.[]'
36
+
37
+ # WRONG: reading the raw result file directly (report is a large base64 blob)
38
+ Read tool → apexguru-raw-*.json
39
+
40
+ # WRONG: inline node/python to parse violations
41
+ node -e "const r = require('./raw.json'); ..."
42
+ ```
43
+
44
+ ### RIGHT — always do this:
45
+
46
+ ```bash
47
+ # PREFERRED — one command runs all three steps (package → submit+poll →
48
+ # decode+present) and prints the ready-to-show report as its final stdout.
49
+ # Use this for every initial scan: it cannot be left half-finished.
50
+ bash "<skill_dir>/scripts/scan.sh" "<project-root>"
51
+
52
+ # Optionally persist the presented markdown to a file as well:
53
+ bash "<skill_dir>/scripts/scan.sh" "<project-root>" --out ./apexguru-report.md
54
+ ```
55
+
56
+ The three underlying scripts still exist and `scan.sh` calls them in order.
57
+ Invoke them individually only for **drill-downs on an already-scanned result**
58
+ (Step 5), or when you deliberately need to inspect an intermediate artifact:
59
+
60
+ ```bash
61
+ # Equivalent manual chain (scan.sh runs exactly these, in this order):
62
+ bash "<skill_dir>/scripts/build-zip.sh" "<project-root>" "./apexguru-<TS>.zip"
63
+ bash "<skill_dir>/scripts/run-scan.sh" "./apexguru-<TS>.zip" "./apexguru-raw-<TS>.json"
64
+ node "<skill_dir>/scripts/decode-report.js" "./apexguru-raw-<TS>.json" --present
65
+
66
+ # Drill into a subset WITHOUT re-scanning (reuse the raw file scan.sh left, or
67
+ # pass --raw to scan.sh to keep it at a known path):
68
+ node "<skill_dir>/scripts/decode-report.js" "./apexguru-raw-<TS>.json" --rule SOQL_IN_LOOP --full
69
+ node "<skill_dir>/scripts/decode-report.js" "./apexguru-raw-<TS>.json" --group file --top 5
70
+ ```
71
+
72
+ `<skill_dir>` is the absolute path to the directory containing this SKILL.md.
73
+ **Never** use `./scripts/` — that resolves against the user's CWD, not the skill dir.
74
+
75
+ Any filter/rank/group question ("which file has the most issues?", "show only
76
+ SOQL-in-loop", "break down by severity") is answered by re-running
77
+ `decode-report.js` with flags against the **same raw result file** — never re-scan,
78
+ never parse the JSON by hand.
79
+
80
+ ---
81
+
82
+ ## CRITICAL: Present `--present` output verbatim — never condense it
83
+
84
+ `decode-report.js --present` (Step 4) already produces the final, ready-to-show
85
+ markdown: severity legend, one detail card per violation (message, code, fix,
86
+ resource link), and a closing summary table. That stdout **is** the response.
87
+ Print it to the user exactly as printed — do not rewrite it into a shorter
88
+ table, do not drop the per-issue cards down to just the summary table, and do
89
+ not wait for the user to ask "explain a violation" before including
90
+ message/fix/resource. Condensing it defeats the entire point of `--present`.
91
+
92
+ The attribution is **already in that stdout** — the summary line is the exact
93
+ output that states the mode (e.g. "ApexGuru (static analysis) is active. To
94
+ unlock runtime intelligence…"). Do **NOT** prepend or append your own attribution sentence
95
+ (no "Attribution: analysisMode: static…", no naming the org, no restating
96
+ "static-only findings"). The script's line is the complete, approved wording;
97
+ adding your own makes the output non-deterministic and off-message.
98
+
99
+ ### WRONG — never do this:
100
+
101
+ ```text
102
+ Top Issues (worst first)
103
+ # Severity Rule Method Line
104
+ 1 Major UsingTheTestMethodKeyword legacy... 136
105
+ ...
106
+ Key Antipatterns Detected:
107
+ - SOQL/DML in loops (3 violations)
108
+ ```
109
+ *(a hand-built summary that drops every message/code/fix — even for
110
+ violations that had one)*
111
+
112
+ ```text
113
+ Attribution: analysisMode: static — source-only analysis. The scanned org
114
+ (ag-skills-org) is not onboarded to ApexGuru's full runtime metrics, so
115
+ these are static-only findings.
116
+ ```
117
+ *(an agent-authored attribution line prepended to the report — the script's
118
+ own summary line already states the mode; this duplicate is non-deterministic
119
+ and names an org the script never had access to)*
120
+
121
+ ### RIGHT — always do this:
122
+
123
+ Paste the full stdout from `decode-report.js --present` — every `### Issue N`
124
+ card and the closing `## Summary` table — unedited, in one response.
125
+
126
+ ---
127
+
128
+ ## Overview
129
+
130
+ ApexGuru detects **performance antipatterns** in Apex (SOQL/DML in loops,
131
+ `Schema.getGlobalDescribe()`, SOQL without `WHERE`/`LIMIT`, unused SOQL fields).
132
+ This skill drives the ApexGuru **SFAP Scan API**: it packages the user's Apex
133
+ (every `.cls`/`.trigger` under the project root, any layout) into a zip, submits
134
+ it, polls until the scan finishes, decodes the base64-encoded report, and
135
+ presents violations grouped by rule with severity, `file:line`, and suggested
136
+ fixes.
137
+
138
+ **Attribution is mandatory.** The API returns `analysisMode`:
139
+ - `static` → source-only analysis → label results **"Static only"**.
140
+ - `full` → enriched with runtime metrics from an org onboarded to ApexGuru →
141
+ label results **"Production insights"**.
142
+
143
+ `decode-report.js --present` already renders this attribution into its summary
144
+ line and title ("Static only" / "Production insights") — that satisfies the
145
+ mandatory-attribution requirement. Print that line as the **exact output**; do **not**
146
+ author your own attribution sentence or name the org. If the user expected
147
+ `full` but got `static`, the script's static-mode line already explains the org
148
+ is not onboarded — point them to it rather than restating it (see error handling).
149
+
150
+ **In scope:** zipping a project's Apex, submitting/polling the scan, decoding + presenting
151
+ violations, filtering/grouping existing results, troubleshooting API errors.
152
+
153
+ **Out of scope:** general static analysis / security / lint (→ `dx-code-analyzer-run`,
154
+ which lists ApexGuru as an engine), applying fixes to code, onboarding an org to
155
+ ApexGuru, minting SFAP tokens.
156
+
157
+ ---
158
+
159
+ ## Prerequisites
160
+
161
+ - **An authenticated `sf` CLI org** (`sf org login web ...`). `resolve-token.sh`
162
+ derives the SFAP JWT from it via `<instanceUrl>/ide/auth` — this is the normal
163
+ IDE-session path. Alternatively, set `APEXGURU_SFAP_TOKEN` / `APEXGURU_SFAP_TOKEN_FILE`
164
+ to supply a JWT directly (CI/headless). The org is derived from the token's `tnk`
165
+ claim — no org id is passed. Pass `--org <alias>` to pick a specific org.
166
+ See `<skill_dir>/references/authentication.md`. If no token can be resolved, the
167
+ script returns a clear error with a hint.
168
+ - **`sf`, `bash`, `curl`, `zip`, `jq`, `node`** on PATH (standard on macOS/Linux dev boxes).
169
+ - **A folder containing Apex** — an sfdx project, a `force-app/` subtree, or any
170
+ folder with `.cls`/`.trigger` files. `build-zip.sh` collects all Apex beneath
171
+ it regardless of layout; the API walks the whole archive.
172
+
173
+ ---
174
+
175
+ ## Workflow
176
+
177
+ ### Step 1: Identify the project root
178
+
179
+ The project root is any folder that **contains Apex** somewhere beneath it
180
+ (usually an sfdx project root next to `sfdx-project.json`, but a `force-app/`
181
+ subtree or a loose folder of `.cls` files works too). If the user gave a path,
182
+ use it; otherwise use the current working directory. `build-zip.sh` collects
183
+ every `.cls`/`.trigger` under it (any layout) and fails clearly if none exists.
184
+
185
+ ### Step 2: Package the project
186
+
187
+ ```bash
188
+ TS=$(date +%Y%m%d-%H%M%S)
189
+ bash "<skill_dir>/scripts/build-zip.sh" "<project-root>" "./apexguru-${TS}.zip"
190
+ ```
191
+
192
+ Output JSON gives `zip`, `bytes`, `humanSize`, `apexFileCount`, `scanRoot`. The script enforces
193
+ the **200MB compressed** limit and fails fast if exceeded. On error (`error`/`hint`
194
+ fields), relay the hint and stop.
195
+
196
+ ### Step 3: Submit and poll
197
+
198
+ ```bash
199
+ bash "<skill_dir>/scripts/run-scan.sh" "./apexguru-${TS}.zip" "./apexguru-raw-${TS}.json"
200
+ ```
201
+
202
+ - Add `--fast` if the user wants a quicker/cheaper run (skips LLM-heavy fix
203
+ generation).
204
+ - **The endpoint follows the token's environment** — the base URL is derived
205
+ from the token's `tnk` claim: a prod org hits `api.salesforce.com`, and an
206
+ internal stage/dev org hits `stage.`/`dev.api.salesforce.com`. Customers
207
+ authenticate a prod org, so they always hit prod; no extra flags or config.
208
+ - `--org <alias>` picks which authenticated `sf` org the JWT is derived from
209
+ (omit to use the CLI's default org).
210
+ - Progress (`QUEUED → RUNNING → SUCCEEDED`) streams to stderr; the script polls
211
+ ~every 15s. Default ceiling is 10 min (`--max-polls`, `--interval` to adjust).
212
+ - On success, stdout is a one-line JSON summary and the full raw body is written to
213
+ `apexguru-raw-${TS}.json`. On failure, stdout is `{error, httpStatus, status, hint}` —
214
+ relay the hint. For status-code specifics see `<skill_dir>/references/error-handling.md`.
215
+ - **Foreground only.** Do not background this; polling output must be observed.
216
+ - **A SUCCEEDED scan is not the finish line.** The raw result is a base64 blob,
217
+ not a user-facing answer. Do not stop or report "done" after the scan
218
+ succeeds — you MUST continue to Step 4 to decode and present the report.
219
+ Ending the turn at Step 3 leaves the user with nothing readable.
220
+
221
+ ### Step 4: Decode and present
222
+
223
+ ```bash
224
+ node "<skill_dir>/scripts/decode-report.js" "./apexguru-raw-${TS}.json" --present
225
+ ```
226
+
227
+ `--present` is the default way to decode for presentation: it implies `--full`
228
+ (no silent caps) and prints ready-to-show markdown directly — a severity
229
+ legend (Minor / Major / Critical, plus a Tip marker when `analysisMode: full`
230
+ enriches severity from production metrics), one `### Issue N` card per
231
+ violation (message, current code, suggested fix, help-doc link) for the
232
+ non-hotspot rules — capped at `--top` (default 10) worst-first, with the cap
233
+ stated in the heading — and a closing `## Summary` table listing **every**
234
+ violation regardless of the card cap. `ExpensiveMethods` (a per-method
235
+ CPU-hotspot ranking from `full` mode, not a line-level antipattern) is
236
+ collapsed into its own ranked "CPU Hotspots" table instead of repeating a
237
+ near-identical card per method. Print this output to the user verbatim —
238
+ **present immediately — do not pause to ask, and do not re-summarize it into
239
+ a shorter table.**
240
+
241
+ For Step 5 drill-downs (filtering/grouping an existing result), the bare
242
+ (non-`--present`) JSON form is fine — see the reading rules below, which apply
243
+ whenever you run the script without `--present`.
244
+
245
+ **DO NOT:** invent script code, use bare `./scripts/...` paths, decode base64
246
+ inline, `jq` the `report` field, or Read the raw file directly.
247
+
248
+ #### Instructions for reading bare (non-`--present`) `decode-report.js` output
249
+
250
+ The command prints one JSON object to stdout. Read it field by field before
251
+ presenting anything — do not eyeball a partial view as complete:
252
+
253
+ 1. **Check `truncated` first, before anything else.** If `true`, `groups` was
254
+ capped to the top `--top` (default 10) rules, each group's `sample` was capped
255
+ to 3 items, and `topViolations` was capped to `--top` items. **Never present a
256
+ `truncated:true` result as the full picture.** Re-run the same command with
257
+ `--full` appended and use that output instead. Only skip this if the user
258
+ explicitly asked for a quick/partial look.
259
+ 2. **State attribution from `analysisMode`/`attribution`** — `static`/"Static
260
+ only" or `full`/"Production insights". This is mandatory on every response,
261
+ per "Attribution is mandatory" above.
262
+ 3. **`serverViolationBreakdown`** is the raw API's internal rule-code tally
263
+ (e.g. `SOQL_IN_LOOP_1HOP`, `GGD`) — it's a sanity-check total (sums to
264
+ `violationCount`), not a display name. Never show these codes to the user;
265
+ use the human-readable `groups[].key` names instead (e.g.
266
+ `SoqlInALoopOneHop`, `SchemaGetGlobalDescribeNotEfficient`).
267
+ 4. **`severityCounts`** (top-level) is the severity distribution across ALL
268
+ violations — use it for the summary table. Each `groups[]` entry has its own
269
+ `severityCounts` scoped to just that rule.
270
+ 5. **Build the "Violations by Rule" table from `groups`**, one row per entry:
271
+ `key` → Rule, `count` → Count, `severityCounts` → Severity, and one
272
+ `sample[0]` (or `items[0]` when `--full`) → Example (`file:line`).
273
+ 6. **Build the "Top Issues" table from `topViolations`** — already sorted
274
+ worst-severity-first. Use `rule`, `severity`, `file:line`, and the first
275
+ entry of `fixes` (if non-empty) as Suggested Fix. If `fixes` is empty, omit
276
+ that column's value rather than inventing a fix.
277
+ 7. **When the user asks to explain a specific violation** ("what does this
278
+ mean", "why is this flagged"), surface that violation's `message` (plain-
279
+ language why) and `resources[0]` (Help Doc URL) verbatim — both exist on
280
+ every violation object but are intentionally left out of the summary tables
281
+ in step 5/6 to keep those scannable. Fall back to
282
+ `references/violation-catalog.md` only if `message` is empty.
283
+ 8. **`fixes` being `[]`** is expected, not an error — the API's `suggestions`
284
+ field (fix code) isn't populated for every rule (notably `ExpensiveMethods`,
285
+ a CPU ranking with no single-line fix); don't say "no fix available", just
286
+ omit the column.
287
+ 9. With `--full`, each group also carries an `items` array (every violation for
288
+ that rule, not just the 3-item `sample`) — use `items` instead of `sample`
289
+ when the user wants the complete list for one rule ("show me all the SOQL
290
+ unused-fields ones").
291
+
292
+ #### Presentation template (fallback — only when NOT using `--present`)
293
+
294
+ `--present` (the default, per Step 4 above) already renders the full
295
+ severity-legend + issue-cards + summary-table output described in the
296
+ "Instructions for reading bare output" section — just print its stdout
297
+ verbatim. Only build a table by hand from bare JSON if `--present` genuinely
298
+ can't be used (e.g. scripting/CI context with no markdown renderer):
299
+
300
+ **Filling the `<Static only | Production insights>` title placeholder:** derive
301
+ the label from the `attribution` field (not `analysisMode` alone) — it already
302
+ encodes the three states:
303
+ - "Production insights" (`analysisMode: full` **with** runtime metrics) —
304
+ enriched with production runtime metrics.
305
+ - "Static only" + `analysisMode: full` (**no** runtime metrics) — org is
306
+ onboarded, but there's no runtime data for this code yet; generate a runtime
307
+ report in Scale Center.
308
+ - "Static only" + `analysisMode: static` — source-only. Onboard the org to
309
+ ApexGuru for production insights.
310
+
311
+ The fenced block below is the literal rendered output — substitute the real
312
+ values and print it; do not emit any of the guidance above:
313
+
314
+ ```text
315
+ ## ApexGuru Scan Complete — <Static only | Production insights>
316
+
317
+ **Found X performance violations** across Y files.
318
+
319
+ | Severity | Count |
320
+ |----------|-------|
321
+ | Critical (1) | X |
322
+ | High (2) | X |
323
+ | Moderate (3) | X |
324
+
325
+ ### Violations by Rule
326
+ | Rule | Count | Severity | Example |
327
+ |------|-------|----------|---------|
328
+ | SOQL_IN_LOOP | 15 | High (2) | AccountService.cls:42 |
329
+ | DML_IN_LOOP | 8 | Critical (1) | AccountService.cls:60 |
330
+ | GGD | 2 | Moderate (3) | Utils.cls:12 |
331
+
332
+ ### Top Issues
333
+ | # | Rule | Sev | File:Line | Suggested Fix |
334
+ |---|------|-----|-----------|---------------|
335
+ | 1 | DML_IN_LOOP | 1 | AccountService.cls:60 | Collect records; DML once after the loop |
336
+ | ... up to 10 |
337
+
338
+ Raw result: `./apexguru-raw-<TS>.json`
339
+ ```
340
+
341
+ Scale to result size: **0** → "no performance antipatterns found"; **1–10** → one
342
+ table; **11+** → severity counts + by-rule table + top 10. End with the raw result
343
+ path. Do **not** append your own follow-up offer (no "I can drill in without
344
+ re-scanning…", no "filter by rule / group by file / explain a violation" menu) —
345
+ `--present` already prints the script's "show all" footer; that is the complete,
346
+ approved closing line and adding your own makes the output non-deterministic.
347
+ Rule-catalog details: `<skill_dir>/references/violation-catalog.md`.
348
+
349
+ ### Step 5: Drill into results (no re-scan)
350
+
351
+ Re-run `decode-report.js` against the **same raw file** with flags:
352
+
353
+ | User says | Flags |
354
+ |-----------|-------|
355
+ | "show only SOQL-in-loop" | `--rule SOQL_IN_LOOP --full` |
356
+ | "just the critical ones" | `--severity 1` |
357
+ | "what's in AccountService.cls?" | `--file AccountService.cls --full` |
358
+ | "group by file" / "which file is worst?" | `--group file --top 5` |
359
+ | "break down by severity" | `--group severity` |
360
+ | "show me everything" | `--present` (or `--full` for bare JSON) |
361
+
362
+ ---
363
+
364
+ ## Constraints & Gotchas
365
+
366
+ | Item | Why / Fix |
367
+ |------|-----------|
368
+ | Run scripts with absolute `<skill_dir>` path | `./scripts/` resolves against the user's CWD, not the skill dir |
369
+ | Any project layout is fine | The API walks the whole archive for Apex; `build-zip.sh` collects every `.cls`/`.trigger` under the root, no `force-app/` required |
370
+ | Never decode `report` inline | It is a large base64 blob — always use `decode-report.js` |
371
+ | Use `--present` for the initial decode | Implies `--full` (no silent caps) and renders ready-to-show markdown directly — severity legend, per-issue cards, closing summary table — mirroring the reference MCP tool's presentation density |
372
+ | Never re-scan to filter | Step 5 re-decodes the existing raw file instantly |
373
+ | Attribution is pre-rendered | `--present` already prints the mode line ("Static only" / "Production insights") — print it as the exact output; never author your own attribution sentence or name the org |
374
+ | `static` when `full` expected | Org not onboarded to ApexGuru — tell the user, don't treat as an error |
375
+ | 401 / 403 / 404 / 400 | Token / org-ownership / scanId / zip issues — see references/error-handling.md |
376
+ | Foreground only, ~15s polls | Backgrounding loses progress; scans can take minutes |
377
+ | Token is a secret | `resolve-token.sh` never echoes it; don't print it or write it to result files |
378
+ | Not a security/lint scanner | For PMD/ESLint/security, use `dx-code-analyzer-run` |
379
+
380
+ ---
381
+
382
+ ## Reference & Script Index
383
+
384
+ **Scripts** (execute via `bash`/`node` with the absolute `<skill_dir>/` prefix, never Read):
385
+
386
+ | File | When to use |
387
+ |------|-------------|
388
+ | `<skill_dir>/scripts/resolve-token.sh` | Resolve SFAP JWT + base URL (called by run-scan.sh) |
389
+ | `<skill_dir>/scripts/validate-token.js` | Local (no-network) JWT pre-flight: env/scope/expiry (called by resolve-token.sh) |
390
+ | `<skill_dir>/scripts/build-zip.sh` | Step 2 — collect the project's Apex into a size-checked zip |
391
+ | `<skill_dir>/scripts/run-scan.sh` | Step 3 — submit + poll to completion |
392
+ | `<skill_dir>/scripts/decode-report.js` | Steps 4–5 — decode base64 report, group/filter violations |
393
+
394
+ **References** (read on demand):
395
+
396
+ | File | When to read |
397
+ |------|--------------|
398
+ | `references/authentication.md` | Where the SFAP JWT comes from; env-var/file setup |
399
+ | `references/api-reference.md` | Endpoint contracts, request/response shapes, limits |
400
+ | `references/violation-catalog.md` | ApexGuru rule meanings and typical fixes |
401
+ | `references/error-handling.md` | 400/401/403/404, FAILED, timeout, static-vs-full diagnosis |
402
+
403
+ `examples/` contains a sample SUCCEEDED response and a decoded-summary sample.
@@ -0,0 +1,54 @@
1
+ # Examples — dx-apexguru-scan
2
+
3
+ Sample data and eval prompts for the ApexGuru performance-scan skill.
4
+
5
+ ## Files
6
+
7
+ | File | Purpose |
8
+ |------|---------|
9
+ | [`sample-succeeded-response.json`](sample-succeeded-response.json) | A realistic API `SUCCEEDED` body (`analysisMode: static`) with a base64-encoded `report`. Feed it to `decode-report.js` to validate parsing offline. |
10
+ | [`sample-decoded-summary.json`](sample-decoded-summary.json) | The bare (non-`--present`) JSON output of `decode-report.js` on the sample response — the shape drill-down queries (Step 5) work against. |
11
+ | [`sample-full-no-runtime-response.json`](sample-full-no-runtime-response.json) | A real production `SUCCEEDED` body from scanning the eval seed class (`analysisMode: full`, no runtime metrics → renders as **Static only**). Use it to exercise `decode-report.js --present` offline, without a live org. Contains no token/secret. |
12
+
13
+ ## Try it offline (no token / no API needed)
14
+
15
+ ```bash
16
+ node ../scripts/decode-report.js sample-succeeded-response.json --present # rendered markdown (default presentation)
17
+ node ../scripts/decode-report.js sample-succeeded-response.json # bare JSON
18
+ node ../scripts/decode-report.js sample-succeeded-response.json --group file --top 5
19
+ node ../scripts/decode-report.js sample-succeeded-response.json --rule DML_IN_LOOP --full
20
+ ```
21
+
22
+ ## How the eval scores this skill
23
+
24
+ The eval judges the `dx-apexguru-scan` agent's output against each dataset's `prompt.md`
25
+ and `instruction.md` rubric — grouped-by-rule findings with severity, `file:line`, and a
26
+ suggested fix — rather than diffing against a fixed reference file. This suits the scan,
27
+ whose output varies per run and per org.
28
+
29
+ The sample JSON fixtures here are for exercising `decode-report.js` offline (see
30
+ "Try it offline" above); they are not eval references.
31
+
32
+ ## Eval prompts
33
+
34
+ Per the authoring quality bar, the skill should pass at least these three evals.
35
+
36
+ ### 1. Happy path (should trigger)
37
+ > "Run ApexGuru on my Apex project and show me the performance issues."
38
+
39
+ Expected: zips the project's Apex, submits, polls to `SUCCEEDED`, decodes the report, and
40
+ presents violations grouped by rule with severity, `file:line`, and fixes — labeled
41
+ **Static only** or **Production insights** per `analysisMode`.
42
+
43
+ ### 2. Edge case (should trigger, handle gracefully)
44
+ > "ApexGuru scan — but I expected production insights and only got static results."
45
+
46
+ Expected: recognizes `analysisMode: static`, explains the org isn't onboarded to
47
+ ApexGuru, does **not** treat it as an error or retry. (Also covers the token-missing
48
+ and 401/403/404/400 paths → surface the hint from `run-scan.sh`.)
49
+
50
+ ### 3. Should NOT trigger
51
+ > "Run a security scan on my Apex classes and check for CRUD/FLS violations."
52
+
53
+ Expected: defers to `dx-code-analyzer-run` (security/lint/PMD) — this skill is
54
+ performance-antipattern-only and should not activate.
@@ -0,0 +1,176 @@
1
+ {
2
+ "scanId": "scan-7f3a91",
3
+ "analysisMode": "static",
4
+ "attribution": "Static only",
5
+ "violationCount": 4,
6
+ "filesScanned": 3,
7
+ "serverViolationBreakdown": {
8
+ "SOQL_IN_LOOP": 1,
9
+ "DML_IN_LOOP": 1,
10
+ "SOQL_NO_WHERE_OR_LIMIT": 1,
11
+ "GGD": 1
12
+ },
13
+ "severityCounts": {
14
+ "1": 1,
15
+ "2": 2,
16
+ "3": 1
17
+ },
18
+ "groupedBy": "rule",
19
+ "groups": [
20
+ {
21
+ "key": "SOQL_IN_LOOP",
22
+ "count": 1,
23
+ "severityCounts": {
24
+ "2": 1
25
+ },
26
+ "sample": [
27
+ {
28
+ "rule": "SOQL_IN_LOOP",
29
+ "message": "SOQL query executed inside a for-loop over Accounts",
30
+ "severity": "2",
31
+ "file": "AccountService.cls",
32
+ "line": 42,
33
+ "method": "",
34
+ "originalCode": "",
35
+ "cpuTimePercentage": null,
36
+ "fixes": [
37
+ "Move the query outside the loop; build a Map<Id,Contact> for in-loop lookups"
38
+ ],
39
+ "resources": [
40
+ "https://developer.salesforce.com/docs/apexguru/soql-in-loop"
41
+ ]
42
+ }
43
+ ]
44
+ },
45
+ {
46
+ "key": "DML_IN_LOOP",
47
+ "count": 1,
48
+ "severityCounts": {
49
+ "1": 1
50
+ },
51
+ "sample": [
52
+ {
53
+ "rule": "DML_IN_LOOP",
54
+ "message": "update statement inside a loop",
55
+ "severity": "1",
56
+ "file": "AccountService.cls",
57
+ "line": 60,
58
+ "method": "",
59
+ "originalCode": "",
60
+ "cpuTimePercentage": null,
61
+ "fixes": [
62
+ "Collect records into a List and update once after the loop"
63
+ ],
64
+ "resources": []
65
+ }
66
+ ]
67
+ },
68
+ {
69
+ "key": "SOQL_NO_WHERE_OR_LIMIT",
70
+ "count": 1,
71
+ "severityCounts": {
72
+ "2": 1
73
+ },
74
+ "sample": [
75
+ {
76
+ "rule": "SOQL_NO_WHERE_OR_LIMIT",
77
+ "message": "SELECT with neither WHERE nor LIMIT on Opportunity",
78
+ "severity": "2",
79
+ "file": "ReportBuilder.cls",
80
+ "line": 15,
81
+ "method": "",
82
+ "originalCode": "",
83
+ "cpuTimePercentage": null,
84
+ "fixes": [
85
+ "Add a selective WHERE on an indexed field and a LIMIT"
86
+ ],
87
+ "resources": []
88
+ }
89
+ ]
90
+ },
91
+ {
92
+ "key": "GGD",
93
+ "count": 1,
94
+ "severityCounts": {
95
+ "3": 1
96
+ },
97
+ "sample": [
98
+ {
99
+ "rule": "GGD",
100
+ "message": "Schema.getGlobalDescribe() called on every request",
101
+ "severity": "3",
102
+ "file": "Utils.cls",
103
+ "line": 12,
104
+ "method": "",
105
+ "originalCode": "",
106
+ "cpuTimePercentage": null,
107
+ "fixes": [
108
+ "Use SObjectType.getDescribe() for the specific object"
109
+ ],
110
+ "resources": []
111
+ }
112
+ ]
113
+ }
114
+ ],
115
+ "topViolations": [
116
+ {
117
+ "rule": "DML_IN_LOOP",
118
+ "message": "update statement inside a loop",
119
+ "severity": "1",
120
+ "file": "AccountService.cls",
121
+ "line": 60,
122
+ "method": "",
123
+ "originalCode": "",
124
+ "cpuTimePercentage": null,
125
+ "fixes": [
126
+ "Collect records into a List and update once after the loop"
127
+ ],
128
+ "resources": []
129
+ },
130
+ {
131
+ "rule": "SOQL_IN_LOOP",
132
+ "message": "SOQL query executed inside a for-loop over Accounts",
133
+ "severity": "2",
134
+ "file": "AccountService.cls",
135
+ "line": 42,
136
+ "method": "",
137
+ "originalCode": "",
138
+ "cpuTimePercentage": null,
139
+ "fixes": [
140
+ "Move the query outside the loop; build a Map<Id,Contact> for in-loop lookups"
141
+ ],
142
+ "resources": [
143
+ "https://developer.salesforce.com/docs/apexguru/soql-in-loop"
144
+ ]
145
+ },
146
+ {
147
+ "rule": "SOQL_NO_WHERE_OR_LIMIT",
148
+ "message": "SELECT with neither WHERE nor LIMIT on Opportunity",
149
+ "severity": "2",
150
+ "file": "ReportBuilder.cls",
151
+ "line": 15,
152
+ "method": "",
153
+ "originalCode": "",
154
+ "cpuTimePercentage": null,
155
+ "fixes": [
156
+ "Add a selective WHERE on an indexed field and a LIMIT"
157
+ ],
158
+ "resources": []
159
+ },
160
+ {
161
+ "rule": "GGD",
162
+ "message": "Schema.getGlobalDescribe() called on every request",
163
+ "severity": "3",
164
+ "file": "Utils.cls",
165
+ "line": 12,
166
+ "method": "",
167
+ "originalCode": "",
168
+ "cpuTimePercentage": null,
169
+ "fixes": [
170
+ "Use SObjectType.getDescribe() for the specific object"
171
+ ],
172
+ "resources": []
173
+ }
174
+ ],
175
+ "truncated": false
176
+ }