dx-apexguru-scan

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

ApexGuru Performance Scan Skill

ApexGuru 性能扫描技能

CRITICAL: Mandatory Script Usage

重要提示:必须使用指定脚本

Every step — token resolution, zipping, API calls, and report decoding — MUST go through the bundled scripts in
<skill_dir>/scripts/
. No exceptions.
每一个步骤——令牌解析、打包ZIP、API调用和报告解码——必须通过
<skill_dir>/scripts/
目录下的捆绑脚本执行,无例外。

WRONG — never do this:

错误示例——切勿这样做:

bash
undefined
bash
undefined

WRONG: hand-rolled curl to the API

错误:手动编写curl调用API

curl -X POST https://api.salesforce.com/... -F file=@x.zip
curl -X POST https://api.salesforce.com/... -F file=@x.zip

WRONG: inline base64 + jq to read the report

错误:直接使用base64 + jq读取报告

cat raw.json | jq -r .report | base64 -d | jq '.[]'
cat raw.json | jq -r .report | base64 -d | jq '.[]'

WRONG: reading the raw result file directly (report is a large base64 blob)

错误:直接读取原始结果文件(报告是大型Base64 blob)

Read tool → apexguru-raw-*.json
Read tool → apexguru-raw-*.json

WRONG: inline node/python to parse violations

错误:直接用node/python解析违规内容

node -e "const r = require('./raw.json'); ..."
undefined
node -e "const r = require('./raw.json'); ..."
undefined

RIGHT — always do this:

正确示例——务必这样做:

bash
undefined
bash
undefined

PREFERRED — one command runs all three steps (package → submit+poll →

推荐:一条命令完成所有三个步骤(打包→提交+轮询→解码+展示),最终在标准输出中打印可直接展示的报告。

decode+present) and prints the ready-to-show report as its final stdout.

初始扫描请使用该命令:它不会中途中断。

Use this for every initial scan: it cannot be left half-finished.

bash "<skill_dir>/scripts/scan.sh" "<project-root>"
bash "<skill_dir>/scripts/scan.sh" "<project-root>"

Optionally persist the presented markdown to a file as well:

可选:同时将生成的Markdown报告保存到文件中:

bash "<skill_dir>/scripts/scan.sh" "<project-root>" --out ./apexguru-report.md

The three underlying scripts still exist and `scan.sh` calls them in order.
Invoke them individually only for **drill-downs on an already-scanned result**
(Step 5), or when you deliberately need to inspect an intermediate artifact:

```bash
bash "<skill_dir>/scripts/scan.sh" "<project-root>" --out ./apexguru-report.md

三个底层脚本仍然存在,`scan.sh`会按顺序调用它们。仅在以下场景单独调用它们:**对已扫描结果进行深入分析**(步骤5),或者需要检查中间产物时:

```bash

Equivalent manual chain (scan.sh runs exactly these, in this order):

等效的手动执行流程(scan.sh完全按此顺序运行):

bash "<skill_dir>/scripts/build-zip.sh" "<project-root>" "./apexguru-<TS>.zip" bash "<skill_dir>/scripts/run-scan.sh" "./apexguru-<TS>.zip" "./apexguru-raw-<TS>.json" node "<skill_dir>/scripts/decode-report.js" "./apexguru-raw-<TS>.json" --present
bash "<skill_dir>/scripts/build-zip.sh" "<project-root>" "./apexguru-<TS>.zip" bash "<skill_dir>/scripts/run-scan.sh" "./apexguru-<TS>.zip" "./apexguru-raw-<TS>.json" node "<skill_dir>/scripts/decode-report.js" "./apexguru-raw-<TS>.json" --present

Drill into a subset WITHOUT re-scanning (reuse the raw file scan.sh left, or

不重新扫描的情况下深入分析子集(复用scan.sh生成的原始文件,或给scan.sh添加--raw参数将其保存到指定路径):

pass --raw to scan.sh to keep it at a known path):

node "<skill_dir>/scripts/decode-report.js" "./apexguru-raw-<TS>.json" --rule SOQL_IN_LOOP --full node "<skill_dir>/scripts/decode-report.js" "./apexguru-raw-<TS>.json" --group file --top 5

`<skill_dir>` is the absolute path to the directory containing this SKILL.md.
**Never** use `./scripts/` — that resolves against the user's CWD, not the skill dir.

Any filter/rank/group question ("which file has the most issues?", "show only
SOQL-in-loop", "break down by severity") is answered by re-running
`decode-report.js` with flags against the **same raw result file** — never re-scan,
never parse the JSON by hand.

---
node "<skill_dir>/scripts/decode-report.js" "./apexguru-raw-<TS>.json" --rule SOQL_IN_LOOP --full node "<skill_dir>/scripts/decode-report.js" "./apexguru-raw-<TS>.json" --group file --top 5

`<skill_dir>`是包含此SKILL.md文件的绝对路径。**切勿**使用`./scripts/`——它会解析为用户的当前工作目录,而非技能目录。

任何过滤/排序/分组请求(如“哪个文件的问题最多?”、“仅显示循环中的SOQL”、“按严重程度分类”)都应通过对**同一原始结果文件**重新运行带参数的`decode-report.js`来处理——切勿重新扫描,切勿手动解析JSON。

---

CRITICAL: Present
--present
output verbatim — never condense it

重要提示:原样展示
--present
的输出——切勿精简

decode-report.js --present
(Step 4) already produces the final, ready-to-show markdown: severity legend, one detail card per violation (message, code, fix, resource link), and a closing summary table. That stdout is the response. Print it to the user exactly as printed — do not rewrite it into a shorter table, do not drop the per-issue cards down to just the summary table, and do not wait for the user to ask "explain a violation" before including message/fix/resource. Condensing it defeats the entire point of
--present
.
The attribution is already in that stdout — the summary line is the exact output that states the mode (e.g. "ApexGuru (static analysis) is active. To unlock runtime intelligence…"). Do NOT prepend or append your own attribution sentence (no "Attribution: analysisMode: static…", no naming the org, no restating "static-only findings"). The script's line is the complete, approved wording; adding your own makes the output non-deterministic and off-message.
decode-report.js --present
(步骤4)已经生成了最终的、可直接展示的Markdown内容:严重程度图例、每个违规项的详情卡片(消息、代码、修复方案、资源链接),以及结尾的汇总表格。该标准输出就是最终响应。请原封不动地打印给用户——不要重写为更简短的表格,不要只保留汇总表格而丢弃每个问题的详情卡片,不要等用户询问“解释某个违规项”才包含消息/修复方案/资源链接。精简内容会完全违背
--present
的设计初衷。
归属信息已包含在该标准输出中——汇总行明确标注了模式(例如“ApexGuru (static analysis) is active. To unlock runtime intelligence…”)。请勿添加自己的归属语句(不要写“归属:analysisMode: static…”,不要提及组织名称,不要重复“仅静态分析结果”)。脚本生成的行是完整且经过批准的表述;添加自定义内容会导致输出非确定性且偏离规范。

WRONG — never do this:

错误示例——切勿这样做:

text
Top Issues (worst first)
text
Top Issues (worst first)

Severity Rule Method Line

Severity Rule Method Line

1 Major UsingTheTestMethodKeyword legacy... 136 ... Key Antipatterns Detected:
  • SOQL/DML in loops (3 violations)
*(a hand-built summary that drops every message/code/fix — even for
violations that had one)*

```text
Attribution: analysisMode: static — source-only analysis. The scanned org
(ag-skills-org) is not onboarded to ApexGuru's full runtime metrics, so
these are static-only findings.
(an agent-authored attribution line prepended to the report — the script's own summary line already states the mode; this duplicate is non-deterministic and names an org the script never had access to)
1 Major UsingTheTestMethodKeyword legacy... 136 ... Key Antipatterns Detected:
  • SOQL/DML in loops (3 violations)
*(手动构建的汇总,丢弃了所有消息/代码/修复方案——即使违规项包含这些内容)*

```text
归属:analysisMode: static — 仅基于源代码的分析。扫描的组织(ag-skills-org)未接入ApexGuru的完整运行时指标,因此这些是仅静态分析的结果。
(由助手添加的归属语句,放在报告前——脚本自身的汇总行已标注模式;此重复内容非确定性且提及了脚本无法访问的组织名称)

RIGHT — always do this:

正确示例——务必这样做:

Paste the full stdout from
decode-report.js --present
— every
### Issue N
card and the closing
## Summary
table — unedited, in one response.

粘贴
decode-report.js --present
的完整标准输出——包括所有
### Issue N
卡片和结尾的
## Summary
表格——未经编辑,一次性返回给用户。

Overview

概述

ApexGuru detects performance antipatterns in Apex (SOQL/DML in loops,
Schema.getGlobalDescribe()
, SOQL without
WHERE
/
LIMIT
, unused SOQL fields). This skill drives the ApexGuru SFAP Scan API: it packages the user's Apex (every
.cls
/
.trigger
under the project root, any layout) into a zip, submits it, polls until the scan finishes, decodes the base64-encoded report, and presents violations grouped by rule with severity,
file:line
, and suggested fixes.
Attribution is mandatory. The API returns
analysisMode
:
  • static
    → source-only analysis → label results "Static only".
  • full
    → enriched with runtime metrics from an org onboarded to ApexGuru → label results "Production insights".
decode-report.js --present
already renders this attribution into its summary line and title ("Static only" / "Production insights") — that satisfies the mandatory-attribution requirement. Print that line as the exact output; do not author your own attribution sentence or name the org. If the user expected
full
but got
static
, the script's static-mode line already explains the org is not onboarded — point them to it rather than restating it (see error handling).
In scope: zipping a project's Apex, submitting/polling the scan, decoding + presenting violations, filtering/grouping existing results, troubleshooting API errors.
Out of scope: general static analysis / security / lint (→
dx-code-analyzer-run
, which lists ApexGuru as an engine), applying fixes to code, onboarding an org to ApexGuru, minting SFAP tokens.

ApexGuru检测Apex代码中的性能反模式(循环中的SOQL/DML、
Schema.getGlobalDescribe()
、无
WHERE
/
LIMIT
的SOQL、未使用的SOQL字段)。该技能调用ApexGuru SFAP Scan API:将用户的Apex代码(项目根目录下所有
.cls
/
.trigger
文件,任意目录结构)打包为ZIP,提交扫描请求,轮询直至扫描完成,解码Base64编码的报告,并按规则分组展示违规情况,包括严重程度、
file:line
以及建议修复方案。
归属信息是必填项。API会返回
analysisMode
  • static
    → 仅基于源代码的分析 → 将结果标注为**“仅静态分析”**。
  • full
    → 结合已接入ApexGuru的组织的运行时指标 → 将结果标注为**“生产环境洞察”**。
decode-report.js --present
已将此归属信息渲染到汇总行和标题中(“仅静态分析”/“生产环境洞察”)——这已满足归属信息的必填要求。请原样打印该行;请勿自行编写归属语句或提及组织名称。如果用户期望
full
模式但得到
static
模式,脚本的静态模式行已解释组织未接入ApexGuru——请引导用户查看该行,而非重复说明(参见错误处理部分)。
适用范围:打包项目的Apex代码、提交并轮询扫描、解码并展示违规情况、过滤/分组现有结果、排查API错误。
不适用范围:通用静态分析/安全检查/代码规范检查(→ 使用
dx-code-analyzer-run
,其中ApexGuru作为引擎之一)、直接修复代码、将组织接入ApexGuru、生成SFAP令牌。

Prerequisites

前置条件

  • An authenticated
    sf
    CLI org
    (
    sf org login web ...
    ).
    resolve-token.sh
    derives the SFAP JWT from it via
    <instanceUrl>/ide/auth
    — this is the normal IDE-session path. Alternatively, set
    APEXGURU_SFAP_TOKEN
    /
    APEXGURU_SFAP_TOKEN_FILE
    to supply a JWT directly (CI/headless). The org is derived from the token's
    tnk
    claim — no org id is passed. Pass
    --org <alias>
    to pick a specific org. See
    <skill_dir>/references/authentication.md
    . If no token can be resolved, the script returns a clear error with a hint.
  • sf
    ,
    bash
    ,
    curl
    ,
    zip
    ,
    jq
    ,
    node
    on PATH (standard on macOS/Linux dev boxes).
  • A folder containing Apex — an sfdx project, a
    force-app/
    subtree, or any folder with
    .cls
    /
    .trigger
    files.
    build-zip.sh
    collects all Apex beneath it regardless of layout; the API walks the whole archive.

  • 已认证的
    sf
    CLI组织
    sf org login web ...
    )。
    resolve-token.sh
    通过
    <instanceUrl>/ide/auth
    从该组织获取SFAP JWT——这是IDE会话的常规路径。或者,直接设置
    APEXGURU_SFAP_TOKEN
    /
    APEXGURU_SFAP_TOKEN_FILE
    环境变量提供JWT(适用于CI/无头环境)。组织信息从令牌的
    tnk
    声明中获取——无需传递组织ID。添加
    --org <alias>
    参数可指定特定组织。详情请见
    <skill_dir>/references/authentication.md
    。如果无法解析令牌,脚本会返回清晰的错误提示。
  • **
    sf
    bash
    curl
    zip
    jq
    node
    **已添加到PATH中(macOS/Linux开发环境默认已包含)。
  • 包含Apex代码的文件夹——可以是sfdx项目、
    force-app/
    子目录,或任何包含
    .cls
    /
    .trigger
    文件的文件夹。
    build-zip.sh
    会收集该文件夹下所有Apex代码,无论目录结构如何;API会遍历整个压缩包。

Workflow

工作流程

Step 1: Identify the project root

步骤1:确定项目根目录

The project root is any folder that contains Apex somewhere beneath it (usually an sfdx project root next to
sfdx-project.json
, but a
force-app/
subtree or a loose folder of
.cls
files works too). If the user gave a path, use it; otherwise use the current working directory.
build-zip.sh
collects every
.cls
/
.trigger
under it (any layout) and fails clearly if none exists.
项目根目录是任何在其下包含Apex代码的文件夹(通常是
sfdx-project.json
所在的sfdx项目根目录,但
force-app/
子目录或零散的
.cls
文件文件夹也可以)。如果用户提供了路径,则使用该路径;否则使用当前工作目录。
build-zip.sh
会收集该目录下所有
.cls
/
.trigger
文件(任意目录结构),如果未找到则会明确报错。

Step 2: Package the project

步骤2:打包项目

bash
TS=$(date +%Y%m%d-%H%M%S)
bash "<skill_dir>/scripts/build-zip.sh" "<project-root>" "./apexguru-${TS}.zip"
Output JSON gives
zip
,
bytes
,
humanSize
,
apexFileCount
,
scanRoot
. The script enforces the 200MB compressed limit and fails fast if exceeded. On error (
error
/
hint
fields), relay the hint and stop.
bash
TS=$(date +%Y%m%d-%H%M%S)
bash "<skill_dir>/scripts/build-zip.sh" "<project-root>" "./apexguru-${TS}.zip"
输出JSON包含
zip
bytes
humanSize
apexFileCount
scanRoot
字段。脚本会强制执行压缩后200MB的限制,如果超出则立即报错。如果返回错误(包含
error
/
hint
字段),请传达提示信息并停止操作。

Step 3: Submit and poll

步骤3:提交并轮询扫描

bash
bash "<skill_dir>/scripts/run-scan.sh" "./apexguru-${TS}.zip" "./apexguru-raw-${TS}.json"
  • Add
    --fast
    if the user wants a quicker/cheaper run (skips LLM-heavy fix generation).
  • The endpoint follows the token's environment — the base URL is derived from the token's
    tnk
    claim: a prod org hits
    api.salesforce.com
    , and an internal stage/dev org hits
    stage.
    /
    dev.api.salesforce.com
    . Customers authenticate a prod org, so they always hit prod; no extra flags or config.
  • --org <alias>
    picks which authenticated
    sf
    org the JWT is derived from (omit to use the CLI's default org).
  • Progress (
    QUEUED → RUNNING → SUCCEEDED
    ) streams to stderr; the script polls ~every 15s. Default ceiling is 10 min (
    --max-polls
    ,
    --interval
    to adjust).
  • On success, stdout is a one-line JSON summary and the full raw body is written to
    apexguru-raw-${TS}.json
    . On failure, stdout is
    {error, httpStatus, status, hint}
    — relay the hint. For status-code specifics see
    <skill_dir>/references/error-handling.md
    .
  • Foreground only. Do not background this; polling output must be observed.
  • A SUCCEEDED scan is not the finish line. The raw result is a base64 blob, not a user-facing answer. Do not stop or report "done" after the scan succeeds — you MUST continue to Step 4 to decode and present the report. Ending the turn at Step 3 leaves the user with nothing readable.
bash
bash "<skill_dir>/scripts/run-scan.sh" "./apexguru-${TS}.zip" "./apexguru-raw-${TS}.json"
  • 如果用户希望更快/更轻量的扫描(跳过LLM生成修复方案的步骤),添加
    --fast
    参数。
  • 端点地址由令牌的环境决定——基础URL从令牌的
    tnk
    声明中获取:生产组织访问
    api.salesforce.com
    ,内部测试/开发组织访问
    stage.
    /
    dev.api.salesforce.com
    。客户认证生产组织,因此始终访问生产环境;无需额外参数或配置。
  • --org <alias>
    参数指定从哪个已认证的
    sf
    组织获取JWT(省略则使用CLI的默认组织)。
  • 扫描进度(
    QUEUED → RUNNING → SUCCEEDED
    )会输出到标准错误流;脚本大约每15秒轮询一次。默认超时时间为10分钟(可通过
    --max-polls
    --interval
    参数调整)。
  • 成功时,标准输出为单行JSON汇总,完整的原始响应会写入
    apexguru-raw-${TS}.json
    文件。失败时,标准输出为
    {error, httpStatus, status, hint}
    ——请传达提示信息。状态码详情请见
    <skill_dir>/references/error-handling.md
  • 仅在前台运行。请勿后台运行;必须观察轮询输出。
  • 扫描成功并不代表完成。原始结果是Base64 blob,并非面向用户的可读内容。扫描成功后请勿停止操作或报告“完成”——必须继续执行步骤4来解码并展示报告。在步骤3结束会导致用户无法获取可读结果。

Step 4: Decode and present

步骤4:解码并展示

bash
node "<skill_dir>/scripts/decode-report.js" "./apexguru-raw-${TS}.json" --present
--present
is the default way to decode for presentation: it implies
--full
(no silent caps) and prints ready-to-show markdown directly — a severity legend (Minor / Major / Critical, plus a Tip marker when
analysisMode: full
enriches severity from production metrics), one
### Issue N
card per violation (message, current code, suggested fix, help-doc link) for the non-hotspot rules — capped at
--top
(default 10) worst-first, with the cap stated in the heading — and a closing
## Summary
table listing every violation regardless of the card cap.
ExpensiveMethods
(a per-method CPU-hotspot ranking from
full
mode, not a line-level antipattern) is collapsed into its own ranked "CPU Hotspots" table instead of repeating a near-identical card per method. Print this output to the user verbatim — present immediately — do not pause to ask, and do not re-summarize it into a shorter table.
For Step 5 drill-downs (filtering/grouping an existing result), the bare (non-
--present
) JSON form is fine — see the reading rules below, which apply whenever you run the script without
--present
.
DO NOT: invent script code, use bare
./scripts/...
paths, decode base64 inline,
jq
the
report
field, or Read the raw file directly.
bash
node "<skill_dir>/scripts/decode-report.js" "./apexguru-raw-${TS}.json" --present
--present
是用于展示的默认解码方式:它隐含
--full
参数(无静默截断),直接打印可展示的Markdown内容——包括严重程度图例(Minor / Major / Critical,当
analysisMode: full
时,会结合生产指标丰富严重程度,添加Tip标记)、每个违规项的
### Issue N
详情卡片(消息、当前代码、建议修复方案、帮助文档链接)(针对非热点规则,按严重程度从高到低最多显示
--top
个,默认10个,标题中会标注截断数量),以及结尾的
## Summary
表格,列出所有违规项,不受详情卡片数量限制。
ExpensiveMethods
full
模式下的方法级CPU热点排名,并非行级反模式)会折叠为单独的“CPU热点”排名表格,而非为每个方法重复生成几乎相同的卡片。请原样打印该输出给用户——立即展示,不要暂停询问,不要重写为更简短的表格
对于步骤5的深入分析(过滤/分组现有结果),可以使用不带
--present
参数的纯JSON输出——请遵循以下读取规则,适用于所有不带
--present
参数的脚本运行场景。
请勿:自行编写脚本代码、使用相对路径
./scripts/...
、直接解码Base64、使用
jq
处理
report
字段、直接读取原始文件。

Instructions for reading bare (non-
--present
)
decode-report.js
output

读取不带
--present
参数的
decode-report.js
输出的说明

The command prints one JSON object to stdout. Read it field by field before presenting anything — do not eyeball a partial view as complete:
  1. Check
    truncated
    first, before anything else.
    If
    true
    ,
    groups
    was capped to the top
    --top
    (default 10) rules, each group's
    sample
    was capped to 3 items, and
    topViolations
    was capped to
    --top
    items. Never present a
    truncated:true
    result as the full picture.
    Re-run the same command with
    --full
    appended and use that output instead. Only skip this if the user explicitly asked for a quick/partial look.
  2. State attribution from
    analysisMode
    /
    attribution
    static
    /"Static only" or
    full
    /"Production insights". This is mandatory on every response, per "Attribution is mandatory" above.
  3. serverViolationBreakdown
    is the raw API's internal rule-code tally (e.g.
    SOQL_IN_LOOP_1HOP
    ,
    GGD
    ) — it's a sanity-check total (sums to
    violationCount
    ), not a display name. Never show these codes to the user; use the human-readable
    groups[].key
    names instead (e.g.
    SoqlInALoopOneHop
    ,
    SchemaGetGlobalDescribeNotEfficient
    ).
  4. severityCounts
    (top-level) is the severity distribution across ALL violations — use it for the summary table. Each
    groups[]
    entry has its own
    severityCounts
    scoped to just that rule.
  5. Build the "Violations by Rule" table from
    groups
    , one row per entry:
    key
    → Rule,
    count
    → Count,
    severityCounts
    → Severity, and one
    sample[0]
    (or
    items[0]
    when
    --full
    ) → Example (
    file:line
    ).
  6. Build the "Top Issues" table from
    topViolations
    — already sorted worst-severity-first. Use
    rule
    ,
    severity
    ,
    file:line
    , and the first entry of
    fixes
    (if non-empty) as Suggested Fix. If
    fixes
    is empty, omit that column's value rather than inventing a fix.
  7. When the user asks to explain a specific violation ("what does this mean", "why is this flagged"), surface that violation's
    message
    (plain- language why) and
    resources[0]
    (Help Doc URL) verbatim — both exist on every violation object but are intentionally left out of the summary tables in step 5/6 to keep those scannable. Fall back to
    references/violation-catalog.md
    only if
    message
    is empty.
  8. fixes
    being
    []
    is expected, not an error — the API's
    suggestions
    field (fix code) isn't populated for every rule (notably
    ExpensiveMethods
    , a CPU ranking with no single-line fix); don't say "no fix available", just omit the column.
  9. With
    --full
    , each group also carries an
    items
    array (every violation for that rule, not just the 3-item
    sample
    ) — use
    items
    instead of
    sample
    when the user wants the complete list for one rule ("show me all the SOQL unused-fields ones").
命令会向标准输出打印一个JSON对象。在展示任何内容前,请逐字段读取——不要只看部分内容就认为是完整结果:
  1. 首先检查
    truncated
    字段
    。如果为
    true
    ,则
    groups
    仅包含前
    --top
    个规则(默认10个),每个组的
    sample
    仅包含3个条目,
    topViolations
    也仅包含前
    --top
    个条目。切勿将
    truncated:true
    的结果作为完整内容展示
    。请重新运行相同命令并添加
    --full
    参数,使用新的输出。仅当用户明确要求快速/部分查看时可跳过此步骤。
  2. analysisMode
    /
    attribution
    字段获取归属信息
    ——
    static
    /“仅静态分析”或
    full
    /“生产环境洞察”。根据前文“归属信息是必填项”的要求,每次响应都必须包含此信息。
  3. **
    serverViolationBreakdown
    **是API内部的规则代码统计(例如
    SOQL_IN_LOOP_1HOP
    GGD
    )——这是用于校验的总数(总和等于
    violationCount
    ),并非展示名称。切勿向用户展示这些代码;请使用易读的
    groups[].key
    名称(例如
    SoqlInALoopOneHop
    SchemaGetGlobalDescribeNotEfficient
    )。
  4. **顶层的
    severityCounts
    **是所有违规项的严重程度分布——用于汇总表格。每个
    groups[]
    条目包含自己的
    severityCounts
    ,仅针对该规则。
  5. groups
    构建“按规则分类的违规项”表格
    ,每个条目对应一行:
    key
    → 规则名称,
    count
    → 数量,
    severityCounts
    → 严重程度,
    sample[0]
    (或
    --full
    参数下的
    items[0]
    ) → 示例(
    file:line
    )。
  6. topViolations
    构建“顶级问题”表格
    ——已按严重程度从高到低排序。使用
    rule
    severity
    file:line
    ,以及
    fixes
    的第一个条目(如果非空)作为建议修复方案。如果
    fixes
    为空,则省略该列的值,不要自行编造修复方案。
  7. 当用户要求解释特定违规项(如“这是什么意思”、“为什么会被标记”),请原样展示该违规项的
    message
    (通俗易懂的原因)和
    resources[0]
    (帮助文档URL)——每个违规项对象都包含这两个字段,但为了让汇总表格更简洁,步骤5/6中未包含它们。仅当
    message
    为空时,才参考
    references/violation-catalog.md
  8. fixes
    []
    是正常情况,并非错误
    ——API的
    suggestions
    字段(修复代码)并非对所有规则都填充(尤其是
    ExpensiveMethods
    ,这是CPU排名,没有单行修复方案);不要说“无可用修复方案”,只需省略该列即可。
  9. 使用
    --full
    参数时,每个组还包含
    items
    数组(该规则的所有违规项,而非仅3个条目的
    sample
    )——当用户需要某个规则的完整列表时(如“显示所有未使用SOQL字段的违规项”),请使用
    items
    而非
    sample

Presentation template (fallback — only when NOT using
--present
)

展示模板(备用——仅当无法使用
--present
时)

--present
(the default, per Step 4 above) already renders the full severity-legend + issue-cards + summary-table output described in the "Instructions for reading bare output" section — just print its stdout verbatim. Only build a table by hand from bare JSON if
--present
genuinely can't be used (e.g. scripting/CI context with no markdown renderer):
Filling the
<Static only | Production insights>
title placeholder:
derive the label from the
attribution
field (not
analysisMode
alone) — it already encodes the three states:
  • "Production insights" (
    analysisMode: full
    with runtime metrics) — enriched with production runtime metrics.
  • "Static only" +
    analysisMode: full
    (no runtime metrics) — org is onboarded, but there's no runtime data for this code yet; generate a runtime report in Scale Center.
  • "Static only" +
    analysisMode: static
    — source-only. Onboard the org to ApexGuru for production insights.
The fenced block below is the literal rendered output — substitute the real values and print it; do not emit any of the guidance above:
text
undefined
--present
(步骤4中的默认方式)已根据“读取不带
--present
参数的输出的说明”生成了完整的严重程度图例+问题卡片+汇总表格输出——只需原样打印其标准输出即可。仅当确实无法使用
--present
时(例如无Markdown渲染器的脚本/CI环境),才从纯JSON手动构建表格:
填充
<仅静态分析 | 生产环境洞察>
标题占位符
:从
attribution
字段获取标签(而非仅
analysisMode
)——它已包含三种状态:
  • “生产环境洞察”(
    analysisMode: full
    包含运行时指标)——结合了生产环境运行时指标。
  • “仅静态分析” +
    analysisMode: full
    运行时指标)——组织已接入ApexGuru,但该代码暂无运行时数据;请在Scale Center生成运行时报告。
  • “仅静态分析” +
    analysisMode: static
    ——仅基于源代码。请将组织接入ApexGuru以获取生产环境洞察。
以下是字面渲染的输出——替换真实值后打印;不要输出上述指导内容:
text
undefined

ApexGuru Scan Complete — <Static only | Production insights>

ApexGuru扫描完成 — <仅静态分析 | 生产环境洞察>

Found X performance violations across Y files.
SeverityCount
Critical (1)X
High (2)X
Moderate (3)X
共发现X个性能违规项,分布在Y个文件中。
严重程度数量
Critical (1)X
High (2)X
Moderate (3)X

Violations by Rule

按规则分类的违规项

RuleCountSeverityExample
SOQL_IN_LOOP15High (2)AccountService.cls:42
DML_IN_LOOP8Critical (1)AccountService.cls:60
GGD2Moderate (3)Utils.cls:12
规则数量严重程度示例
SOQL_IN_LOOP15High (2)AccountService.cls:42
DML_IN_LOOP8Critical (1)AccountService.cls:60
GGD2Moderate (3)Utils.cls:12

Top Issues

顶级问题

#RuleSevFile:LineSuggested Fix
1DML_IN_LOOP1AccountService.cls:60Collect records; DML once after the loop
... up to 10
Raw result:
./apexguru-raw-<TS>.json

Scale to result size: **0** → "no performance antipatterns found"; **1–10** → one
table; **11+** → severity counts + by-rule table + top 10. End with the raw result
path. Do **not** append your own follow-up offer (no "I can drill in without
re-scanning…", no "filter by rule / group by file / explain a violation" menu) —
`--present` already prints the script's "show all" footer; that is the complete,
approved closing line and adding your own makes the output non-deterministic.
Rule-catalog details: `<skill_dir>/references/violation-catalog.md`.
#规则严重程度文件:行号建议修复方案
1DML_IN_LOOP1AccountService.cls:60收集记录;循环结束后执行一次DML
... 最多显示10个
原始结果文件:
./apexguru-raw-<TS>.json

根据结果规模调整展示方式:**0个违规项** → “未发现性能反模式”;**1–10个** → 一个表格;**11个及以上** → 严重程度统计+按规则分类表格+前10个顶级问题。结尾附上原始结果文件路径。**请勿**添加自定义的后续提示(不要写“我可以不重新扫描进行深入分析…”,不要提供“按规则过滤/按文件分组/解释违规项”菜单)——`--present`已打印脚本的“显示全部”页脚;这是完整且经过批准的结束语,添加自定义内容会导致输出非确定性。规则详情请见:`<skill_dir>/references/violation-catalog.md`。

Step 5: Drill into results (no re-scan)

步骤5:深入分析结果(无需重新扫描)

Re-run
decode-report.js
against the same raw file with flags:
User saysFlags
"show only SOQL-in-loop"
--rule SOQL_IN_LOOP --full
"just the critical ones"
--severity 1
"what's in AccountService.cls?"
--file AccountService.cls --full
"group by file" / "which file is worst?"
--group file --top 5
"break down by severity"
--group severity
"show me everything"
--present
(or
--full
for bare JSON)

同一原始文件重新运行带参数的
decode-report.js
用户指令参数
“仅显示循环中的SOQL”
--rule SOQL_IN_LOOP --full
“仅显示严重级别为Critical的项”
--severity 1
“AccountService.cls中有什么问题?”
--file AccountService.cls --full
“按文件分组” / “哪个文件问题最严重?”
--group file --top 5
“按严重程度分类”
--group severity
“显示所有内容”
--present
(或
--full
获取纯JSON)

Constraints & Gotchas

约束与注意事项

ItemWhy / Fix
Run scripts with absolute
<skill_dir>
path
./scripts/
resolves against the user's CWD, not the skill dir
Any project layout is fineThe API walks the whole archive for Apex;
build-zip.sh
collects every
.cls
/
.trigger
under the root, no
force-app/
required
Never decode
report
inline
It is a large base64 blob — always use
decode-report.js
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
Never re-scan to filterStep 5 re-decodes the existing raw file instantly
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
static
when
full
expected
Org not onboarded to ApexGuru — tell the user, don't treat as an error
401 / 403 / 404 / 400Token / org-ownership / scanId / zip issues — see references/error-handling.md
Foreground only, ~15s pollsBackgrounding loses progress; scans can take minutes
Token is a secret
resolve-token.sh
never echoes it; don't print it or write it to result files
Not a security/lint scannerFor PMD/ESLint/security, use
dx-code-analyzer-run

事项原因/修复方案
使用绝对路径
<skill_dir>
运行脚本
./scripts/
会解析为用户的当前工作目录,而非技能目录
支持任意项目目录结构API会遍历整个压缩包查找Apex代码;
build-zip.sh
会收集根目录下所有
.cls
/
.trigger
文件,无需
force-app/
目录
切勿直接解码
report
字段
它是大型Base64 blob——请始终使用
decode-report.js
初始解码使用
--present
参数
隐含
--full
参数(无静默截断),直接生成可展示的Markdown内容——包括严重程度图例、每个问题的详情卡片、结尾汇总表格——与参考MCP工具的展示密度一致
过滤时切勿重新扫描步骤5重新解码现有原始文件可瞬间完成
归属信息已预渲染
--present
已打印模式行(“仅静态分析”/“生产环境洞察”)——请原样打印;切勿自行编写归属语句或提及组织名称
期望
full
模式但得到
static
模式
组织未接入ApexGuru——告知用户,不要视为错误
401 / 403 / 404 / 400错误令牌/组织所有权/扫描ID/ZIP文件问题——请见references/error-handling.md
仅在前台运行,约15秒轮询一次后台运行会丢失进度;扫描可能需要数分钟
令牌是敏感信息
resolve-token.sh
永远不会回显令牌;不要打印或写入结果文件
并非安全/代码规范扫描工具如需PMD/ESLint/安全检查,请使用
dx-code-analyzer-run

Reference & Script Index

参考资料与脚本索引

Scripts (execute via
bash
/
node
with the absolute
<skill_dir>/
prefix, never Read):
FileWhen to use
<skill_dir>/scripts/resolve-token.sh
Resolve SFAP JWT + base URL (called by run-scan.sh)
<skill_dir>/scripts/validate-token.js
Local (no-network) JWT pre-flight: env/scope/expiry (called by resolve-token.sh)
<skill_dir>/scripts/build-zip.sh
Step 2 — collect the project's Apex into a size-checked zip
<skill_dir>/scripts/run-scan.sh
Step 3 — submit + poll to completion
<skill_dir>/scripts/decode-report.js
Steps 4–5 — decode base64 report, group/filter violations
References (read on demand):
FileWhen to read
references/authentication.md
Where the SFAP JWT comes from; env-var/file setup
references/api-reference.md
Endpoint contracts, request/response shapes, limits
references/violation-catalog.md
ApexGuru rule meanings and typical fixes
references/error-handling.md
400/401/403/404, FAILED, timeout, static-vs-full diagnosis
examples/
contains a sample SUCCEEDED response and a decoded-summary sample.
脚本(使用
bash
/
node
执行,必须添加绝对路径
<skill_dir>/
前缀,切勿直接读取):
文件使用场景
<skill_dir>/scripts/resolve-token.sh
解析SFAP JWT + 基础URL(由run-scan.sh调用)
<skill_dir>/scripts/validate-token.js
本地(无网络)JWT预检查:环境/权限/有效期(由resolve-token.sh调用)
<skill_dir>/scripts/build-zip.sh
步骤2——将项目的Apex代码收集到经过大小检查的ZIP文件中
<skill_dir>/scripts/run-scan.sh
步骤3——提交扫描请求并轮询直至完成
<skill_dir>/scripts/decode-report.js
步骤4–5——解码Base64报告,分组/过滤违规项
参考资料(按需阅读):
文件阅读场景
references/authentication.md
SFAP JWT的来源;环境变量/文件配置
references/api-reference.md
端点契约、请求/响应格式、限制
references/violation-catalog.md
ApexGuru规则的含义和典型修复方案
references/error-handling.md
400/401/403/404错误、扫描失败、超时、静态/全模式诊断
examples/
目录包含扫描成功的示例响应和解码后的汇总示例。