mcpField
D
dx-apexguru-scan

dx-apexguru-scan

github.com/forcedotcom测试与质量1.1k2.7k 次安装1 天前更新
代码仓库

关于 dx-apexguru-scan

面向 Salesforce Apex 项目的技能,通过 ApexGuru SFAP Scan API 扫描性能反模式,并将违规及建议修复整理成报告。

主要功能

  1. 01打包项目中的 Apex 文件
  2. 02提交扫描并轮询状态
  3. 03解码并呈现违规报告
  4. 04按规则筛选现有结果

适用场景

  1. 01检查 SOQL 或 DML 循环
  2. 02查找缺少 WHERE 或 LIMIT 的 SOQL
  3. 03按严重性筛选扫描结果

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/forcedotcom/sf-skills --skill dx-apexguru-scan

安装后在 Claude Code 中输入/dx-apexguru-scan确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

examples/
README.md
sample-decoded-summary.json
sample-full-no-runtime-response.json
sample-succeeded-response.json
references/
api-reference.md
authentication.md
error-handling.md
violation-catalog.md
scripts/
build-zip.sh
decode-report.js
resolve-token.sh
run-scan.sh
scan.sh
validate-token.js

详细介绍

ApexGuru 性能扫描技能

严重:必须使用脚本

每个步骤——令牌解析、压缩、API 调用和报告解码——都必须通过 <skill_dir>/scripts/ 中附带的脚本完成。没有例外。

错误做法——切勿这样做:
# WRONG: hand-rolled curl to the API
curl -X POST https://api.salesforce.com/... -F [email protected]

# WRONG: inline base64 + jq to read the report
cat raw.json | jq -r .report | base64 -d | jq '.[]'

# WRONG: reading the raw result file directly (report is a large base64 blob)
Read tool → apexguru-raw-*.json

# WRONG: inline node/python to parse violations
node -e "const r = require('./raw.json'); ..."
正确做法——始终这样做:
# 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>"

# Optionally persist the presented markdown to a file as well:
bash "<skill_dir>/scripts/scan.sh" "<project-root>" --out ./apexguru-report.md

底层的三个脚本仍然存在,且 scan.sh 会按顺序调用它们。仅在对已扫描结果进行深入分析(第 5 步)时,或确实需要检查中间产物时,才单独调用这些脚本:

# Equivalent manual chain (scan.sh runs exactly these, in this order):
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
# 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> 是包含此 SKILL.md 的目录的绝对路径。 切勿使用 ./scripts/——它会相对于用户的当前工作目录解析,而不是技能目录。

任何筛选/排序/分组问题(“哪个文件的问题最多?”、“仅显示循环中的 SOQL”、“按严重性细分”)都应通过使用标志重新运行 decode-report.js,并针对同一个原始结果文件作答——绝不重新扫描,也绝不手动解析 JSON。


严重:原样呈现 --present 的输出——切勿精简

decode-report.js --present(第 4 步)已生成可直接展示的最终 Markdown:严重性图例、每项违规对应的一张详情卡片(消息、代码、修复方法、资源链接),以及结尾的汇总表。其标准输出就是回复内容。 必须将其输出原样展示给用户——不要改写成更短的表格,不要删掉每项问题的详情卡片而只保留汇总表,也不要等用户询问“解释某项违规”后才提供消息/修复方法/资源链接。精简会完全违背 --present 的目的。

归属说明已包含在该标准输出中——汇总行明确说明了模式(例如:“ApexGuru (static analysis) is active. To unlock runtime intelligence…”)。不要在报告前后添加自己的归属说明句(不要写“归属:analysisMode: static…”,不要提及组织名称,也不要重述“仅静态分析结果”)。脚本输出的这一行就是完整且经批准的措辞;自行添加会导致输出不确定且偏离原意。

错误做法——切勿这样做:
Top Issues (worst first)
#  Severity   Rule                      Method    Line
1  Major      UsingTheTestMethodKeyword legacy... 136
...
Key Antipatterns Detected:
- SOQL/DML in loops (3 violations)

(手动编写的汇总,删掉了所有消息/代码/修复方法——即使违规项原本包含这些内容)

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.

(在报告前添加由 Agent 编写的归属说明行——脚本自己的汇总行已说明模式;这条重复内容会导致输出不确定,还会提及脚本无法访问的组织)

正确做法——始终这样做:

粘贴 decode-report.js --present 的完整标准输出——包括每个 ### Issue N 卡片和结尾的 ## Summary 表格;不得编辑,并在一条回复中呈现。


概览

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 错误。

**不在范围内:**通用静态分析/安全/lint(→ dx-code-analyzer-run,该工具将 ApexGuru 列为一个引擎)、对代码应用修复、将组织接入 ApexGuru、生成 SFAP 令牌。


前提条件

  • 已通过身份验证的 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。如果无法解析令牌,脚本会返回清晰的错误提示。
  • PATH 中需包含 sf、bash、curl、zip、jq、node(macOS/Linux 开发环境中通常已具备)。
  • 包含 Apex 的文件夹——可以是 sfdx 项目、force-app/ 子目录,或任何包含 .cls/.trigger 文件的文件夹。无论目录布局如何,build-zip.sh 都会收集其中所有 Apex 文件;API 会遍历整个归档。

工作流

步骤 1:确定项目根目录

项目根目录可以是其下任意位置包含 Apex 的文件夹(通常是与 sfdx-project.json 同级的 sfdx 项目根目录,但 force-app/ 子目录或只包含 .cls 文件的普通文件夹也可以)。如果用户提供了路径,则使用该路径;否则使用当前工作目录。build-zip.sh 会收集该目录下的所有 .cls/.trigger 文件(无论目录布局如何),如果不存在此类文件则会明确报错。

步骤 2:打包项目
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 字段),请转达提示并停止。

步骤 3:提交并轮询
bash "<skill_dir>/scripts/run-scan.sh" "./apexguru-${TS}.zip" "./apexguru-raw-${TS}.json"
  • 如果用户希望运行更快/成本更低,请添加 --fast(跳过耗费大量 LLM 资源的修复生成)。
  • 端点遵循令牌的环境——基础 URL 根据令牌的 tnk 声明派生:生产组织访问 api.salesforce.com,内部 stage/dev 组织访问 stage./dev.api.salesforce.com。客户使用生产组织进行身份验证,因此始终访问生产环境;无需额外标志或配置。
  • --org <alias> 用于选择从哪个已通过身份验证的 sf 组织派生 JWT(省略时使用 CLI 的默认组织)。
  • 进度(QUEUED → RUNNING → SUCCEEDED)会输出到 stderr;脚本大约每 15 秒轮询一次。默认上限为 10 min(使用 --max-polls、--interval 调整)。
  • 成功时,stdout 会输出一行 JSON 摘要,并将完整原始响应正文写入 apexguru-raw-${TS}.json。失败时,stdout 为 {error, httpStatus, status, hint}——转达其中的提示。有关状态码的详细信息,请参阅 <skill_dir>/references/error-handling.md。
  • 仅限前台运行。 不要将其放到后台;必须观察轮询输出。
  • 扫描状态为 SUCCEEDED 并不代表流程结束。 原始结果是 base64 数据块,不是面向用户的答案。扫描成功后不要停止或报告“已完成”——必须继续执行步骤 4,解码并呈现报告。在步骤 3 结束本轮对话,用户将得不到任何可读内容。
步骤 4:解码并呈现
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、对 report 字段使用 jq,或直接读取原始文件。

阅读裸(非 --present)decode-report.js 输出的说明

该命令会向 stdout 输出一个 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。
演示模板(备用 — 仅在不使用 --present 时)

按照上文步骤 4,--present(默认选项)已渲染出“读取裸输出说明”部分所述的完整输出,包括严重性图例、问题卡片和汇总表 — 只需原样打印其标准输出。仅当确实无法使用 --present 时(例如没有 Markdown 渲染器的脚本/CI 环境),才根据裸 JSON 手动构建表格:

填写 <Static only | Production insights> 标题占位符: 根据 attribution 字段(而非仅根据 analysisMode)推导标签 — 该字段已编码这三种状态:

  • “Production insights”(analysisMode: full 且包含运行时指标)— 已补充生产环境运行时指标。
  • “Static only” + analysisMode: full(不含运行时指标)— 组织已接入,但此代码尚无运行时数据;请在 Scale Center 中生成运行时报告。
  • “Static only” + analysisMode: static — 仅源代码。请将组织接入 ApexGuru,以获取生产环境洞察。

下方代码围栏中的内容是实际渲染的输出 — 替换为真实值并打印;不要输出以上任何说明:

## ApexGuru Scan Complete — <Static only | Production insights>

**Found X performance violations** across Y files.

| Severity | Count |
|----------|-------|
| Critical (1) | X |
| High (2) | X |
| Moderate (3) | X |

### Violations by Rule
| Rule | Count | Severity | Example |
|------|-------|----------|---------|
| SOQL_IN_LOOP | 15 | High (2) | AccountService.cls:42 |
| DML_IN_LOOP | 8 | Critical (1) | AccountService.cls:60 |
| GGD | 2 | Moderate (3) | Utils.cls:12 |

### Top Issues
| # | Rule | Sev | File:Line | Suggested Fix |
|---|------|-----|-----------|---------------|
| 1 | DML_IN_LOOP | 1 | AccountService.cls:60 | Collect records; DML once after the loop |
| ... up to 10 |

Raw result: `./apexguru-raw-<TS>.json`

按结果规模调整输出:0 → “未发现性能反模式”;1–10 → 一张表;11+ → 严重性计数 + 按规则分类的表 + 前 10 条。以原始结果路径结尾。不要附加自己的后续帮助提议(例如“我可以不重新扫描就深入分析……”,或“按规则筛选 / 按文件分组 / 解释违规”的选项菜单)— --present 已打印脚本的“显示全部”页脚;这是完整且获准的结尾语句,添加自己的内容会导致输出不确定。 规则目录详情:<skill_dir>/references/violation-catalog.md。

步骤 5:深入查看结果(不重新扫描)

使用以下标志,针对同一个原始文件重新运行 decode-report.js:

用户说标志
“只显示循环中的 SOQL”--rule SOQL_IN_LOOP --full
“只看严重的”--severity 1
“AccountService.cls 中有什么?”--file AccountService.cls --full
“按文件分组” / “哪个文件问题最严重?”--group file --top 5
“按严重性细分”--group severity
“全部显示”--present(或使用 --full 输出裸 JSON)

限制与注意事项

项目原因 / 解决方法
使用绝对 <skill_dir> 路径运行脚本./scripts/ 会相对于用户的当前工作目录解析,而非 skill 目录
任意项目布局均可API 会遍历整个压缩包查找 Apex;build-zip.sh 会收集根目录下的所有 .cls/.trigger,不要求存在 force-app/
切勿内联解码 report它是一个很大的 base64 数据块 — 始终使用 decode-report.js
初始解码时使用 --present它隐含 --full(不会静默截断),并直接渲染可直接展示的 Markdown — 包含严重性图例、逐项问题卡片和结尾汇总表,呈现密度与参考 MCP 工具一致
切勿重新扫描以筛选步骤 5 会即时重新解码现有原始文件
归属信息已预先渲染--present 已打印模式行(“Static only” / “Production insights”)— 按原样输出;切勿自行撰写归属说明句或提及组织名称
预期为 full,结果却是 static组织尚未接入 ApexGuru — 告知用户,不要将其视为错误
401 / 403 / 404 / 400令牌 / 组织所有权 / scanId / zip 问题 — 请参阅 references/error-handling.md
仅在前台运行,轮询间隔约 15 秒切换到后台会丢失进度;扫描可能需要几分钟
令牌是机密信息resolve-token.sh 不会回显令牌;不要打印令牌,也不要将其写入结果文件
不是安全/代码检查扫描器对于 PMD/ESLint/安全扫描,请使用 dx-code-analyzer-run

参考资料与脚本索引

脚本(使用绝对路径前缀 <skill_dir>/,通过 bash/node 执行,切勿使用 Read):

文件使用时机
<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.mdSFAP JWT 的来源;环境变量/文件配置
references/api-reference.md端点约定、请求/响应结构、限制
references/violation-catalog.mdApexGuru 规则含义和常见修复方法
references/error-handling.md400/401/403/404、FAILED、超时、静态分析与完整分析的诊断

examples/ 包含一个 SUCCEEDED 响应示例和一个解码后的摘要示例。

评论

支持 Markdown