dx-code-analyzer-custom-rule-create

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

dx-code-analyzer-custom-rule-create: Custom Code Analyzer Rule Authoring

dx-code-analyzer-custom-rule-create:自定义代码分析器规则编写

Ecosystem: This skill is part of a 3-skill Code Analyzer suite —
dx-code-analyzer-run
(scans & results) ·
dx-code-analyzer-configure
(setup, config, CI/CD) ·
dx-code-analyzer-custom-rule-create
(custom rule authoring).
Use this skill when the user needs to create a custom rule that enforces a pattern not covered by Code Analyzer's built-in rules. Supports Regex engine (text pattern matching) and PMD engine (structural XPath queries against the AST).
生态系统: 此技能属于包含3个技能的代码分析器套件 —
dx-code-analyzer-run
(扫描与结果)·
dx-code-analyzer-configure
(设置、配置、CI/CD)·
dx-code-analyzer-custom-rule-create
(自定义规则编写)。
当用户需要创建自定义规则以强制执行代码分析器内置规则未覆盖的模式时,使用此技能。支持Regex引擎(文本模式匹配)和PMD引擎(针对AST的结构化XPath查询)。

When This Skill Owns the Task

此技能负责的任务场景

Use
dx-code-analyzer-custom-rule-create
when the work involves:
  • Creating a new custom rule for Code Analyzer (any engine)
  • Enforcing team-specific coding standards via static analysis
  • Banning specific patterns (System.debug, hardcoded IDs, TODOs)
  • Writing XPath expressions for PMD rules (Apex or metadata XML)
  • Writing regex patterns for the Regex engine
  • Setting up custom ESLint rules/plugins for LWC/JavaScript
  • Enforcing metadata governance (API versions, field descriptions, dangerous permissions)
  • Overriding built-in rule thresholds (CyclomaticComplexity, ExcessiveParameterList, etc.)
  • Organizing multiple rules into shared rulesets
  • Iterating on a custom rule that isn't matching correctly
Delegate elsewhere when the user is:
  • Running a scan against existing rules →
    dx-code-analyzer-run
    skill
  • Configuring engines, prerequisites, CI/CD →
    dx-code-analyzer-configure
    skill
  • Explaining what an existing built-in rule means →
    dx-code-analyzer-run
    skill
  • Writing Apex code or tests →
    generating-apex
    /
    running-apex-tests
    skills

当工作涉及以下内容时,使用
dx-code-analyzer-custom-rule-create
  • 为代码分析器创建新的自定义规则(任意引擎)
  • 通过静态分析强制执行团队特定的编码标准
  • 禁用特定模式(System.debug、硬编码ID、TODO注释)
  • 为PMD规则编写XPath表达式(Apex或元数据XML)
  • 为Regex引擎编写正则表达式模式
  • 为LWC/JavaScript设置自定义ESLint规则/插件
  • 强制执行元数据管控(API版本、字段描述、危险权限)
  • 覆盖内置规则阈值(CyclomaticComplexity、ExcessiveParameterList等)
  • 将多个规则组织到共享规则集中
  • 迭代调整匹配不正确的自定义规则
当用户有以下需求时,转交至其他技能:
  • 根据现有规则运行扫描 →
    dx-code-analyzer-run
    技能
  • 配置引擎、前置条件、CI/CD →
    dx-code-analyzer-configure
    技能
  • 解释现有内置规则的含义 →
    dx-code-analyzer-run
    技能
  • 编写Apex代码或测试 →
    generating-apex
    /
    running-apex-tests
    技能

Required Context to Gather First

首先需要收集的必要上下文

Ask for or infer:
  • What pattern to catch — what code should be flagged? (If user selected code in their IDE, the selection IS the answer — do not re-ask.)
  • What to allow — any exceptions? (test classes, specific contexts)
  • File scope — which file types? (.cls, .trigger, .js, all?)
  • Severity — how critical? (default: 3/Moderate)
If the user selected code (IDE selection context present), treat it as the pattern definition. Skip clarification unless genuinely ambiguous about what aspect of the selection to target.
If the request is vague with NO selection ("add a rule for best practices"), ask ONE clarifying question:
"What specific pattern should this rule flag?"

询问或推断以下信息:
  • 需要捕获的模式 — 哪些代码需要被标记?(如果用户在IDE中选中了代码,该选中内容即为答案,无需再次询问。)
  • 允许的例外 — 是否有任何例外情况?(测试类、特定上下文)
  • 文件范围 — 针对哪些文件类型?(.cls、.trigger、.js,或全部?)
  • 严重程度 — 优先级如何?(默认:3/中等)
如果用户选中了代码(存在IDE选中上下文),将其视为模式定义。除非对选中内容的目标方面确实存在歧义,否则无需澄清。
如果请求模糊且无选中内容(例如“添加最佳实践规则”),仅询问一个澄清问题:
"此规则应标记哪些特定模式?"

Hard Constraints

硬性约束

These are non-negotiable rules. Violating any of them is a skill failure regardless of whether the output happens to work.
  1. ALWAYS run
    ast-dump
    before writing XPath.
    No exceptions. Do not use node names from memory, references, or prior conversations. The AST is the source of truth — run
    sf code-analyzer ast-dump
    , read the output, then write XPath that matches what you see. Even for "well-known" patterns like SOQL-in-loop, run ast-dump first. If you skip this step and the rule works, it is still a process failure.
  2. ALWAYS use the scripts to create rules. For regex rules, ALWAYS use
    create-regex-rule.js
    . For PMD rules, ALWAYS use
    create-pmd-rule.js
    . Do NOT manually edit
    code-analyzer.yml
    to add rule definitions — regex patterns in YAML cause escaping failures (quotes inside quotes, backslashes getting eaten). The scripts handle YAML serialization correctly every time.
  3. NEVER manually edit
    code-analyzer.yml
    after a script writes to it — even to fix a bad value.
    The scripts produce correctly-escaped YAML. If you then rewrite or restructure the file, you WILL break the escaping. If the user added top-level config (like
    ignores.files
    ), leave it alone too — only touch what you wrote.
    If a script's output looks wrong (rule fails to validate, YAML parse error, stray characters in the regex):
    • DO NOT patch the YAML by hand. That is exactly the failure mode this constraint exists to prevent.
    • Always delete the broken rule's entire YAML block, then re-invoke the script with corrected arguments. Removing a block you just wrote does not violate this rule; rewriting fields inside it does.
    • If the script accepted bad input and produced bad output, the input was wrong (e.g.,
      --regex "/.../ g"
      with a stray space — the flags must be
      /g
      exactly, no whitespace). Re-invoke with the corrected argument.
    • If you genuinely believe the script has a bug, STOP and surface it to the user. Do not hand-edit as a workaround.
  4. --regex
    must be
    /pattern/flags
    with NO whitespace.
    The script trims and validates flags strictly — only
    g
    ,
    i
    ,
    m
    ,
    s
    ,
    u
    ,
    y
    .
    /pat/ g
    (with a space) is rejected; so is
    /pat/x
    (invalid flag) and
    /pat/
    (no flags). The global flag
    g
    is mandatory. If validation fails, fix the argument — do NOT bypass by writing YAML directly.
  5. ALWAYS validate after creation. Run
    sf code-analyzer rules --rule-selector <engine>:<name>
    before testing. If
    Found 0 rules
    , the YAML didn't parse — delete the block, fix the argument, re-invoke the script.
  6. ALWAYS test against a sample file. Confirm at least one true positive and one true negative.
    For regex rules, the negative sample MUST NOT contain the pattern text anywhere — including inside comments and string literals. Regex engines scan raw text;
    // no System.debug here
    IS a match for
    /System\.debug/g
    . Trace your pattern against the negative file mentally before running it.
  7. Create rules ONE AT A TIME, sequentially. When the user requests multiple rules, create each rule individually through the full workflow (create → validate → test positive → test negative) before starting the next one. Do NOT batch-create rules — if one fails, it corrupts the config for all subsequent rules. Complete each rule end-to-end, confirm it works, then move to the next.
  8. For regex rules, exclude test classes via
    ignores.files
    regex_ignore
    does NOT do this.
    regex_ignore
    is a per-LINE filter (the line must match BOTH the rule and the ignore pattern); it cannot exclude an entire test class. If the user's intent is "skip test classes," add a top-level
    ignores.files
    block with globs like
    "**/*Test.cls"
    AFTER all rules are created — do not interleave config edits with script invocations.

这些是不可协商的规则。违反任何一条都属于技能执行失败,无论输出是否碰巧可用。
  1. 编写XPath前必须运行
    ast-dump
    。无例外情况。不要凭记忆、参考资料或之前的对话使用节点名称。AST是唯一可信来源 — 运行
    sf code-analyzer ast-dump
    ,读取输出,然后编写与输出匹配的XPath。即使是“众所周知”的模式(如循环内的SOQL),也要先运行ast-dump。如果跳过此步骤但规则生效,仍属于流程失败。
  2. 必须使用脚本创建规则。对于Regex规则,必须使用
    create-regex-rule.js
    。对于PMD规则,必须使用
    create-pmd-rule.js
    。请勿手动编辑
    code-analyzer.yml
    添加规则定义 — YAML中的正则表达式会导致转义失败(引号嵌套、反斜杠丢失)。脚本每次都能正确处理YAML序列化。
  3. 脚本写入后切勿手动编辑
    code-analyzer.yml
    — 即使是修复错误值也不行
    。脚本会生成正确转义的YAML。如果之后重写或重构文件,肯定会破坏转义。如果用户添加了顶层配置(如
    ignores.files
    ),请勿修改 — 只改动你编写的内容。
    如果脚本输出看起来有误(规则验证失败、YAML解析错误、正则表达式中存在多余字符):
    • 请勿手动修补YAML。这正是此约束要避免的失败场景。
    • 始终删除错误规则的整个YAML块,然后使用修正后的参数重新调用脚本。删除刚编写的块不属于违反此规则;重写块内的字段才是。
    • 如果脚本接受了错误输入并生成错误输出,问题出在输入上(例如
      --regex "/.../ g"
      包含多余空格 — 标志必须严格为
      /g
      ,无空格)。使用修正后的参数重新调用。
    • 如果确实认为脚本存在bug,请停止操作并告知用户。请勿手动编辑作为变通方案。
  4. --regex
    必须为
    /pattern/flags
    格式,且无空格
    。脚本会严格修剪并验证标志 — 仅允许
    g
    i
    m
    s
    u
    y
    /pat/ g
    (带空格)会被拒绝;
    /pat/x
    (无效标志)和
    /pat/
    (无标志)也会被拒绝。全局标志
    g
    是必填项。如果验证失败,请修正参数 — 请勿直接编写YAML绕过验证。
  5. 创建后必须验证。在测试前运行
    sf code-analyzer rules --rule-selector <engine>:<name>
    。如果显示
    Found 0 rules
    ,说明YAML未解析 — 删除块,修正参数,重新调用脚本。
  6. 必须针对示例文件测试。确认至少一个真阳性和一个真阴性结果。
    对于Regex规则,阴性示例绝对不能包含任何模式文本 — 包括注释和字符串字面量内的文本。Regex引擎会扫描原始文本;
    // no System.debug here
    会匹配
    /System\.debug/g
    。运行前先在脑海中对照阴性文件检查你的模式。
  7. 一次只创建一个规则,按顺序执行。当用户请求多个规则时,在开始下一个规则前,完成每个规则的完整工作流程(创建→验证→阳性测试→阴性测试)。请勿批量创建规则 — 如果一个规则失败,会破坏后续所有规则的配置。完成每个规则的端到端流程,确认可用后再进行下一个。
  8. 对于Regex规则,通过
    ignores.files
    排除测试类 —
    regex_ignore
    无法实现此功能
    regex_ignore
    是逐行过滤器(该行必须同时匹配规则和忽略模式);它无法排除整个测试类。如果用户的意图是“跳过测试类”,在所有规则创建完成后,添加顶层
    ignores.files
    块,使用类似
    "**/*Test.cls"
    的通配符 — 请勿在脚本调用期间穿插配置编辑。

Engine Selection

引擎选择

Pattern TypeEngineWhy
Text/string pattern (TODO, hardcoded ID, keyword)RegexSimple, fast, no Java needed
Apex code structure (method calls, nesting, SOQL in loops)PMD/XPath (language=apex)Understands AST, not fooled by comments/strings
Metadata XML governance (API version, permissions, descriptions)PMD/XPath (language=xml)Structural XML matching with namespace handling
LWC/JavaScript/TypeScript patternsESLint*Standard JS tooling, plugin ecosystem
Both could work (Apex/metadata only)Regex firstSimpler to create and maintain
* For ESLint: ALWAYS check Tier 1 (built-in rules) and Tier 2 (configurable rules) BEFORE creating a custom plugin. See
references/eslint-rules-discovery.md
.
⚠️ "Both could work → Regex first" NEVER applies to JavaScript/LWC/TypeScript files. JS/LWC/TS patterns MUST use ESLint — Regex cannot distinguish code from comments/strings in JS and produces false positives. Do NOT rationalize Regex for JS files based on "simplicity" or "no npm dependencies."
Tell the user which engine you chose and why. Respect their preference if they disagree.
模式类型引擎原因
文本/字符串模式(TODO、硬编码ID、关键字)Regex简单、快速,无需Java
Apex代码结构(方法调用、嵌套、循环内的SOQL)PMD/XPath(language=apex)理解AST,不会被注释/字符串误导
元数据XML管控(API版本、权限、描述)PMD/XPath(language=xml)结构化XML匹配,支持命名空间处理
LWC/JavaScript/TypeScript模式ESLint*标准JS工具,插件生态丰富
两种引擎都适用(仅Apex/元数据)优先使用Regex创建和维护更简单
* 对于ESLint: 在创建自定义插件前,必须先检查Tier 1(内置规则)和Tier 2(可配置规则)。详见
references/eslint-rules-discovery.md
⚠️ “两种引擎都适用→优先Regex”绝不适用于JavaScript/LWC/TypeScript文件。JS/LWC/TS模式必须使用ESLint — Regex无法区分JS中的代码与注释/字符串,会产生误报。请勿以“简单”或“无npm依赖”为由为JS文件使用Regex。
告知用户你选择的引擎及原因。如果用户有不同偏好,请尊重其选择。

Excluding Test Classes — Strategy by Engine

排除测试类 — 按引擎划分的策略

When a rule should NOT apply to test classes, the approach differs by engine:
EngineHow to exclude test classesNotes
PMD (Apex)Add
[not(ancestor::UserClass[ModifierNode[@Test = true()]])]
to the XPath
Structural exclusion — works perfectly, no config changes needed
RegexUse
ignores.files
in
code-analyzer.yml
with globs like
**/*Test.cls
regex_ignore
is per-LINE, not per-FILE — it CANNOT exclude entire test classes. Only use
regex_ignore
for per-line patterns like
// NOPMD
ESLintUse
ignores
array in
eslint.config.js
Standard ESLint file-level ignores
⚠️
regex_ignore
is NOT file-level exclusion.
It only skips matches on lines that ALSO match the ignore pattern. Example:
regex_ignore: "/@isTest/i"
only suppresses violations on lines containing
@isTest
— a SOQL query on line 50 of a test class still flags because line 50 doesn't contain
@isTest
. To exclude test files from regex rules entirely, use:
yaml
ignores:
  files:
    - "**/*Test.cls"
    - "**/*_Test.cls"
⚠️
ignores.files
is GLOBAL
— it affects ALL engines and ALL rules. If you need test-class exclusion for some rules but not others (e.g., exclude tests from SOQL rules but still scan tests for @AuraEnabled), use PMD with XPath for the rules that need selective exclusion. PMD's XPath can structurally check
@Test = true()
per-method or per-class — Regex cannot.
Decision guide for Apex rules that should skip test classes:
  • If the pattern is structural (method calls, annotations, nesting) → use PMD. XPath handles test-class exclusion natively.
  • If the pattern is purely textual AND all regex rules should skip tests → use Regex +
    ignores.files
    .
  • If you have a mix (some rules skip tests, others don't) → use PMD for the test-sensitive rules, Regex for the others.

当规则不应应用于测试类时,不同引擎的处理方式不同:
引擎排除测试类的方法说明
PMD (Apex)在XPath中添加
[not(ancestor::UserClass[ModifierNode[@Test = true()]])]
结构化排除 — 完全有效,无需修改配置
Regex
code-analyzer.yml
中使用
ignores.files
,配置类似
**/*Test.cls
的通配符
regex_ignore
是逐行生效,而非逐文件 — 它无法排除整个测试类。仅将
regex_ignore
用于
// NOPMD
这类逐行模式
ESLint
eslint.config.js
中使用
ignores
数组
标准ESLint文件级忽略方式
⚠️
regex_ignore
不是文件级排除
。它仅跳过同时匹配忽略模式的行上的匹配项。示例:
regex_ignore: "/@isTest/i"
仅抑制包含
@isTest
的行上的违规 — 测试类第50行的SOQL查询仍会被标记,因为第50行不包含
@isTest
。要从Regex规则中完全排除测试文件,请使用:
yaml
ignores:
  files:
    - "**/*Test.cls"
    - "**/*_Test.cls"
⚠️
ignores.files
是全局设置
— 它会影响所有引擎和所有规则。如果需要部分规则排除测试类,而其他规则不排除(例如,SOQL规则排除测试类,但仍扫描测试类中的@AuraEnabled),请对需要选择性排除的规则使用PMD + XPath。PMD的XPath可以按方法或类结构化检查
@Test = true()
— Regex无法实现此功能。
针对应跳过测试类的Apex规则的决策指南:
  • 如果模式是结构化的(方法调用、注解、嵌套)→ 使用PMD。XPath原生支持测试类排除。
  • 如果模式是纯文本且所有Regex规则都应跳过测试 → 使用Regex +
    ignores.files
  • 如果存在混合需求(部分规则跳过测试,其他规则不跳过)→ 对测试敏感的规则使用PMD,其他规则使用Regex。

Workflow

工作流程

When User Selects Code (IDE Selection)

用户选中代码时(IDE选中场景)

When the user highlights a code block in their editor and asks to "catch this", "flag this pattern", "create a rule for this", or similar:
  1. The selection IS your positive sample. Do NOT ask "what pattern should this rule flag?" — the user already showed you. Do NOT write a new sample from scratch.
  2. Identify what's structural vs. incidental in the selection:
    • Structural (rule-worthy): the method call, the loop pattern, the missing keyword, the nesting
    • Incidental (ignore): specific variable names, string values, parameter counts
    • Ask ONE question if ambiguous: "Should the rule catch all
      System.debug
      calls, or only those without a LoggingLevel parameter?"
  3. Ast-dump the ACTUAL file the user has open (not a new sample file):
    bash
    sf code-analyzer ast-dump --file <the-open-file.cls> --output-file <ast.xml>
  4. Find the selection in the AST output — locate the nodes corresponding to the highlighted lines. These are your target nodes.
  5. Generalize the XPath — write XPath that matches the structural pattern, NOT the specific instance. Replace specific variable names with wildcards, keep structural nodes and discriminating attributes.
  6. Continue with standard workflow (negative sample, create rule, validate, test positive + negative).
Example flow:
  • User selects:
    Database.query('SELECT Id FROM ' + objectName)
  • Structural pattern:
    Database.query
    call (dynamic SOQL)
  • Incidental: the specific string concatenation inside
  • Engine: PMD (structural call detection)
  • XPath:
    //MethodCallExpression[@FullMethodName='Database.query']
  • NOT: regex matching
    Database.query
    (would miss multiline, match comments)
Example flow (block selection):
  • User selects a 5-line block with SOQL inside a for-each loop
  • Structural: SOQL query as descendant of loop body
  • Incidental: specific query fields, variable names
  • Engine: PMD
  • Ast-dump the open file → find ForEachStatement + SoqlExpression in body
  • XPath:
    //ForEachStatement/BlockStatement//SoqlExpression

当用户在编辑器中高亮代码块并要求“捕获此内容”、“标记此模式”、“为此创建规则”或类似请求时:
  1. 选中内容即为阳性样本。请勿询问“此规则应标记哪些模式?” — 用户已经展示给你了。请勿从头编写新样本。
  2. 识别选中内容中的结构化元素与偶然元素
    • 结构化元素(值得规则关注):方法调用、循环模式、缺失的关键字、嵌套结构
    • 偶然元素(忽略):特定变量名、字符串值、参数数量
    • 如果存在歧义,仅问一个问题:“规则应捕获所有
      System.debug
      调用,还是仅捕获没有LoggingLevel参数的调用?”
  3. 对用户打开的实际文件执行ast-dump(不是新样本文件):
    bash
    sf code-analyzer ast-dump --file <the-open-file.cls> --output-file <ast.xml>
  4. 在AST输出中找到选中内容 — 定位与高亮行对应的节点。这些是你的目标节点。
  5. 泛化XPath — 编写匹配结构化模式的XPath,而非特定实例。将特定变量名替换为通配符,保留结构化节点和区分属性。
  6. 继续执行标准工作流程(阴性样本、创建规则、验证、阳性+阴性测试)。
示例流程:
  • 用户选中:
    Database.query('SELECT Id FROM ' + objectName)
  • 结构化模式:
    Database.query
    调用(动态SOQL)
  • 偶然元素:内部的特定字符串拼接
  • 引擎:PMD(结构化调用检测)
  • XPath:
    //MethodCallExpression[@FullMethodName='Database.query']
  • 错误做法:使用正则匹配
    Database.query
    (会遗漏多行调用,匹配注释)
示例流程(块选中):
  • 用户选中包含for-each循环内SOQL的5行代码块
  • 结构化模式:循环体的后代节点中的SOQL查询
  • 偶然元素:特定查询字段、变量名
  • 引擎:PMD
  • 对打开的文件执行ast-dump → 在body中找到ForEachStatement + SoqlExpression
  • XPath:
    //ForEachStatement/BlockStatement//SoqlExpression

For Regex Rules

针对Regex规则

  1. Write a positive sample (5-10 lines) demonstrating the violation. Write sample files inside the project workspace (e.g., a temporary
    samples/
    directory at the project root) so Code Analyzer can target them.
  2. Write a SEPARATE negative sample file — code that looks similar but must NOT be flagged. Test your regex mentally against this file BEFORE creating the rule.
  3. Build and create the rule — read
    references/regex-rule-schema.md
    for the complete schema, then run the script:
    bash
    node "<skill_dir>/scripts/create-regex-rule.js" \
      --name "<RuleName>" --regex "<pattern>" --description "<desc>" \
      --severity <1-5> --file-extensions ".cls,.trigger"
    ⚠️ ALWAYS use the script. Do NOT manually write regex patterns into
    code-analyzer.yml
    — regex characters (quotes, backslashes, braces) inside YAML cause parsing failures. The script handles serialization correctly.
  4. Validate
    sf code-analyzer rules --rule-selector regex:<RuleName>
  5. Test positive
    sf code-analyzer run --rule-selector regex:<RuleName> --target <violation-sample>
    — must find violations
  6. Test negative
    sf code-analyzer run --rule-selector regex:<RuleName> --target <clean-sample>
    — must find 0 violations. If it flags clean code, your regex is too broad — go back and tighten the pattern.
  7. Iterate if test fails (adjust regex, add
    regex_ignore
    , narrow extensions)
  8. Cleanup — delete ALL sample files you created. Do not leave temporary test fixtures in the user's project.
  1. 编写阳性样本(5-10行)展示违规情况。在项目工作区中编写样本文件(例如,项目根目录下的临时
    samples/
    目录),以便代码分析器可以扫描到它们。
  2. 编写单独的阴性样本文件 — 看起来相似但绝对不能被标记的代码。在创建规则前,先在脑海中对照此文件测试你的正则表达式。
  3. 构建并创建规则 — 阅读
    references/regex-rule-schema.md
    获取完整 schema,然后运行脚本:
    bash
    node "<skill_dir>/scripts/create-regex-rule.js" \
      --name "<RuleName>" --regex "<pattern>" --description "<desc>" \
      --severity <1-5> --file-extensions ".cls,.trigger"
    ⚠️ 必须使用脚本。请勿手动将正则表达式写入
    code-analyzer.yml
    — YAML中的正则字符(引号、反斜杠、大括号)会导致解析失败。脚本会正确处理序列化。
  4. 验证
    sf code-analyzer rules --rule-selector regex:<RuleName>
  5. 阳性测试
    sf code-analyzer run --rule-selector regex:<RuleName> --target <violation-sample>
    — 必须找到违规
  6. 阴性测试
    sf code-analyzer run --rule-selector regex:<RuleName> --target <clean-sample>
    — 必须找到0个违规。如果标记了干净代码,说明你的正则表达式范围太广 — 返回调整模式。
  7. 迭代(如果测试失败,调整正则表达式、添加
    regex_ignore
    、缩小文件扩展名范围)
  8. 清理 — 删除你创建的所有样本文件。不要在用户项目中留下临时测试文件。

For PMD/XPath Rules (Apex)

针对PMD/XPath规则(Apex)

  1. Write a minimal sample (5-10 lines) demonstrating the violation. Write sample files inside the project workspace (e.g., a temporary
    samples/
    directory at the project root) so Code Analyzer can target them. After both positive and negative tests pass, delete ALL sample files you created — both the original samples and any copies made during testing. Do not leave temporary test fixtures in the user's project. For loop-based rules, the positive sample MUST include all 3 Apex loop types (
    for-each
    , traditional
    for
    , and
    while
    ) — omitting any loop type means the XPath won't be validated against it and may silently miss violations.
  2. ⚠️ MANDATORY: Dump the AST
    sf code-analyzer ast-dump --file <sample.cls> --output-file <ast.xml>
    — this step is NOT optional. Do NOT skip it even if you "already know" the node names. Run it, read the output, confirm the exact node names and attributes.
  3. Read the AST output — identify the target node and its attributes from the ACTUAL ast-dump output (not from memory). Use
    references/apex-ast-reference.md
    and
    references/xpath-patterns.md
    as supplementary context only.
  4. Write XPath — target the smallest stable node with discriminating attributes. Every node name in your XPath MUST appear verbatim in the ast-dump output you just read. Use
    /Child
    (direct child) vs
    //Descendant
    deliberately — check the ast-dump to understand which nodes are siblings vs nested.
  5. ⚠️ BEFORE creating the rule: Write a SEPARATE negative sample file (5-10 lines) showing code that is CORRECT and must NOT be flagged. This MUST be a distinct file from the positive sample — do NOT combine positive and negative cases into one file. For loop-based rules, include
    for (x : [SELECT...])
    idiom. Run
    sf code-analyzer ast-dump
    on this negative sample file too. Read the output and trace your XPath against it — confirm it does NOT match any node in the negative AST. If it would match, go back to step 4 and tighten the XPath BEFORE proceeding. Do NOT skip this step or defer it until after rule creation. The negative file will be used again in step 9 for an explicit zero-violation confirmation.
  6. Create the rule — run the script:
    bash
    node "<skill_dir>/scripts/create-pmd-rule.js" \
      --name "<RuleName>" --xpath "<expression>" --message "<msg>" \
      --language apex --priority <1-5>
  7. Validate
    sf code-analyzer rules --rule-selector pmd:<RuleName>
  8. Test positive
    sf code-analyzer run --rule-selector pmd:<RuleName> --target <violation-sample.cls>
    — must find violations
  9. Test negative
    sf code-analyzer run --rule-selector pmd:<RuleName> --target <clean-sample.cls>
    — must find 0 violations. If it flags clean code, your XPath is too broad — go back to step 4.
  10. Iterate if test fails (re-examine AST, adjust XPath, check node names)
  11. Cleanup — delete ALL sample files you created (both positive and negative samples, and any ast-dump output files). Do not leave temporary test fixtures in the user's project.
  1. 编写最小样本(5-10行)展示违规情况。在项目工作区中编写样本文件(例如,项目根目录下的临时
    samples/
    目录),以便代码分析器可以扫描到它们。阳性和阴性测试通过后,删除你创建的所有样本文件 — 包括原始样本和测试期间创建的任何副本。不要在用户项目中留下临时测试文件。对于基于循环的规则,阳性样本必须包含所有3种Apex循环类型(
    for-each
    、传统
    for
    while
    ) — 遗漏任何一种循环类型意味着XPath不会针对其验证,可能会静默遗漏违规。
  2. ⚠️ 必须执行:导出AST
    sf code-analyzer ast-dump --file <sample.cls> --output-file <ast.xml>
    — 此步骤不可选。即使你“已经知道”节点名称,也请勿跳过。运行它,读取输出,确认确切的节点名称和属性。
  3. 读取AST输出 — 从实际的ast-dump输出(而非记忆)中识别目标节点及其属性。仅将
    references/apex-ast-reference.md
    references/xpath-patterns.md
    作为补充参考。
  4. 编写XPath — 定位具有区分属性的最小稳定节点。XPath中的每个节点名称必须与你刚读取的ast-dump输出完全一致。有意使用
    /Child
    (直接子节点)与
    //Descendant
    (后代节点) — 查看ast-dump以了解哪些节点是兄弟节点,哪些是嵌套节点。
  5. ⚠️ 创建规则前:编写单独的阴性样本文件(5-10行)展示正确且不能被标记的代码。此文件必须与阳性样本分开 — 请勿将阳性和阴性情况合并到一个文件中。对于基于循环的规则,包含
    for (x : [SELECT...])
    写法。对阴性样本文件也运行
    sf code-analyzer ast-dump
    。读取输出并对照它跟踪你的XPath — 确认它不会匹配阴性AST中的任何节点。如果会匹配,返回步骤4并在继续前收紧XPath。请勿跳过此步骤或推迟到规则创建后。阴性文件将在步骤9中用于明确确认零违规。
  6. 创建规则 — 运行脚本:
    bash
    node "<skill_dir>/scripts/create-pmd-rule.js" \
      --name "<RuleName>" --xpath "<expression>" --message "<msg>" \
      --language apex --priority <1-5>
  7. 验证
    sf code-analyzer rules --rule-selector pmd:<RuleName>
  8. 阳性测试
    sf code-analyzer run --rule-selector pmd:<RuleName> --target <violation-sample.cls>
    — 必须找到违规
  9. 阴性测试
    sf code-analyzer run --rule-selector pmd:<RuleName> --target <clean-sample.cls>
    — 必须找到0个违规。如果标记了干净代码,说明你的XPath范围太广 — 返回步骤4。
  10. 迭代(如果测试失败,重新检查AST、调整XPath、检查节点名称)
  11. 清理 — 删除你创建的所有样本文件(阳性和阴性样本,以及任何ast-dump输出文件)。不要在用户项目中留下临时测试文件。

For PMD/XPath Rules (Metadata XML)

针对PMD/XPath规则(元数据XML)

  1. Identify the metadata file type (field, permissionset, profile, flow, etc.)
  2. Dump the XML AST
    sf code-analyzer ast-dump --file <file>-meta.xml --language xml --output-file <ast.xml>
    . If ast-dump fails with an error (e.g.,
    "XmlEncoding is not a valid XML name"
    ), fall back to reading the raw XML file directly — the XML DOM structure IS the AST for metadata files (what you see in the file is what PMD sees). Read the file, note element names, nesting, and text content.
  3. Read the DOM structure — confirm element names, nesting, and text content from the ast-dump output OR the raw file. Use
    references/metadata-xml-rules.md
    as supplementary context only.
  4. Write XPath for PMD 7 — CRITICAL rules for metadata XML XPath:
    • All element matching MUST use
      local-name()='ElementName'
      (namespace blocks bare names)
    • Text content matching MUST use
      @Text
      attribute
      (NOT
      text()
      — the
      text()
      function does not work in PMD 7's XML language). Example:
      //*[@Text='ModifyAllData']
    • Navigate from text nodes UP to parent elements using
      ../..
      (text node → element → parent element)
    • Check sibling conditions via
      .//*[@Text='value']
      on the parent
    • See
      references/metadata-xml-rules.md
      for the complete PMD 7 XPath pattern
  5. Configure file extensions — PMD's XML language only processes
    .xml
    by default. Salesforce metadata files use compound extensions (
    .permissionset-meta.xml
    ,
    .field-meta.xml
    , etc.) but
    path.extname()
    returns
    .xml
    from these, so
    .xml
    is sufficient:
    yaml
    engines:
      pmd:
        file_extensions:
          xml: [".xml"]
    ⚠️ Do NOT add compound extensions like
    .permissionset-meta.xml
    — Code Analyzer's validator rejects them (
    /^[.][a-zA-Z0-9]+$/
    pattern). Just
    .xml
    covers all Salesforce metadata files automatically.
  6. Write a SEPARATE negative sample file — same as Apex rules: create a metadata file that is correct and must NOT be flagged. Verify the XPath does not match it BEFORE creating the rule. Both positive and negative samples should be
    .xml
    extension files in the workspace so PMD can scan them directly.
  7. Create the rule — run the script:
    bash
    node "<skill_dir>/scripts/create-pmd-rule.js" \
      --name "<RuleName>" --xpath "<expression>" --message "<msg>" \
      --language xml --priority <1-5>
  8. Validate
    sf code-analyzer rules --rule-selector pmd:<RuleName>
  9. Test positive
    sf code-analyzer run --rule-selector pmd:<RuleName> --target <violation-sample>.xml
    — must find violations
  10. Test negative
    sf code-analyzer run --rule-selector pmd:<RuleName> --target <clean-sample>.xml
    — must find 0 violations
  11. Iterate if test fails (check
    @Text
    vs
    text()
    , verify
    local-name()
    usage, check file extensions config)
  12. Cleanup — delete ALL sample files you created (both positive and negative samples, and any
    .xml
    copies made for testing). Do not leave temporary test fixtures in the user's project.
  1. 识别元数据文件类型(字段、权限集、配置文件、流等)
  2. 导出XML AST
    sf code-analyzer ast-dump --file <file>-meta.xml --language xml --output-file <ast.xml>
    。如果ast-dump失败并报错(例如
    "XmlEncoding is not a valid XML name"
    ),回退到直接读取原始XML文件 — 对于元数据文件,XML DOM结构就是AST(文件中的内容就是PMD看到的内容)。读取文件,记录元素名称、嵌套结构和文本内容。
  3. 读取DOM结构 — 从ast-dump输出或原始文件中确认元素名称、嵌套结构和文本内容。仅将
    references/metadata-xml-rules.md
    作为补充参考。
  4. 为PMD 7编写XPath — 元数据XML XPath的关键规则:
    • 所有元素匹配必须使用
      local-name()='ElementName'
      (命名空间会阻止裸名称匹配)
    • 文本内容匹配必须使用
      @Text
      属性
      (不能使用
      text()
      text()
      函数在PMD 7的XML语言中不起作用)。示例:
      //*[@Text='ModifyAllData']
    • 使用
      ../..
      从文本节点向上导航到父元素(文本节点→元素→父元素)
    • 通过父元素的
      .//*[@Text='value']
      检查兄弟条件
    • 完整的PMD 7 XPath模式详见
      references/metadata-xml-rules.md
  5. 配置文件扩展名 — PMD的XML语言默认仅处理
    .xml
    文件。Salesforce元数据文件使用复合扩展名(
    .permissionset-meta.xml
    .field-meta.xml
    等),但
    path.extname()
    会从这些文件中返回
    .xml
    ,因此
    .xml
    已足够:
    yaml
    engines:
      pmd:
        file_extensions:
          xml: [".xml"]
    ⚠️ 请勿添加
    .permissionset-meta.xml
    这类复合扩展名 — 代码分析器的验证器会拒绝它们(匹配
    /^[.][a-zA-Z0-9]+$/
    模式)。仅
    .xml
    就会自动覆盖所有Salesforce元数据文件。
  6. 编写单独的阴性样本文件 — 与Apex规则相同:创建一个正确且不能被标记的元数据文件。在创建规则前验证XPath不会匹配它。阳性和阴性样本都应该是工作区中的
    .xml
    扩展名文件,以便PMD可以直接扫描它们。
  7. 创建规则 — 运行脚本:
    bash
    node "<skill_dir>/scripts/create-pmd-rule.js" \
      --name "<RuleName>" --xpath "<expression>" --message "<msg>" \
      --language xml --priority <1-5>
  8. 验证
    sf code-analyzer rules --rule-selector pmd:<RuleName>
  9. 阳性测试
    sf code-analyzer run --rule-selector pmd:<RuleName> --target <violation-sample>.xml
    — 必须找到违规
  10. 阴性测试
    sf code-analyzer run --rule-selector pmd:<RuleName> --target <clean-sample>.xml
    — 必须找到0个违规
  11. 迭代(如果测试失败,检查
    @Text
    text()
    的使用、验证
    local-name()
    的使用、检查文件扩展名配置)
  12. 清理 — 删除你创建的所有样本文件(阳性和阴性样本,以及任何用于测试的
    .xml
    副本)。不要在用户项目中留下临时测试文件。

For ESLint Rules (LWC/JavaScript/TypeScript)

针对ESLint规则(LWC/JavaScript/TypeScript)

ESLint Discovery Workflow (Read First)

ESLint发现工作流程(先阅读)

ESLint has 200+ built-in rules plus thousands more from plugins. DO NOT attempt to create a custom plugin before checking if a built-in rule exists. The discovery workflow is:
  1. Tier 1 (Built-In Rules) — 70% of requests
    • Run:
      sf code-analyzer rules --rule-selector eslint
    • Search for keywords (e.g., "console", "unused", "equal")
    • Check LWC plugin rules (
      @lwc/lwc/*
      )
    • If found: Configure and STOP
  2. Tier 2 (Configurable Rules) — 20% of requests
    • Pattern is "ban function X"? →
      no-restricted-globals
    • Pattern is "ban syntax Y"? →
      no-restricted-syntax
    • Pattern is "ban property Z"? →
      no-restricted-properties
    • If applicable: Configure and STOP
  3. Tier 3 (Custom Plugins) — 10% of requests
    • Only for domain-specific multi-node patterns
    • Requires Node.js code, testing, meta.docs metadata
    • See:
      references/eslint-custom-plugins.md
See:
references/eslint-rules-discovery.md
for complete discovery guide with examples.

  1. ⚠️ MANDATORY: Run the ESLint discovery workflow FIRST. 90% of ESLint requests are solved by built-in rules (Tier 1) or configurable rules like
    no-restricted-syntax
    (Tier 2). Creating a custom plugin (Tier 3) is a LAST RESORT. Before proceeding:
    a) Run:
    sf code-analyzer rules --rule-selector eslint
    b) Search the output for keywords from the user's request (e.g., "console", "equal", "unused") c) Check LWC plugin rules: grep for
    @lwc/lwc/
    in the output d) If found: Configure it (see
    references/eslint-rules-discovery.md
    ) and STOP. Do NOT create a custom plugin. e) If not found: Check if
    no-restricted-globals
    ,
    no-restricted-syntax
    , or
    no-restricted-properties
    can express the pattern (Tier 2). f) Only if no Tier 1 or Tier 2 solution exists: Proceed to custom plugin creation.
    Validation: After configuration, run
    sf code-analyzer rules --rule-selector eslint:<ruleName>
    to confirm it appears. If it doesn't, the config is wrong.
    ⚠️ Skipping this discovery workflow and creating a custom plugin when a built-in rule exists is a skill failure, regardless of whether the custom plugin works.
⚠️ Built-in ESLint rules vs. configurable ESLint rules: Code Analyzer bundles a SUBSET of ESLint rules in its base config. Rules like
no-restricted-globals
,
no-restricted-syntax
, and
no-restricted-properties
are core ESLint rules but are NOT active until you enable them in an
eslint.config.js
. They WILL appear in
sf code-analyzer rules
output ONLY after you configure them. Always validate (step 4) to confirm the rule actually loaded — do NOT assume a rule exists just because it's a core ESLint rule.
  1. Install the ESLint plugin (skip if using a built-in/core ESLint rule) —
    npm install --save-dev eslint-plugin-<name>
  2. Create/update
    eslint.config.js
    — add plugin and rule configuration (or just enable the built-in rule with
    "error"
    severity). The file MUST exist before configuring
    engines.eslint.eslint_config_file
    in
    code-analyzer.yml
    — Code Analyzer validates the path and fails if the file is missing.
  3. Configure Code Analyzer — set
    engines.eslint.eslint_config_file
    in
    code-analyzer.yml
    . Do this AFTER the file exists.
  4. Validate
    sf code-analyzer rules --rule-selector eslint:<ruleName>
    . If the rule does NOT appear, the config is wrong — do NOT proceed to testing. Check: (a) Is the
    eslint.config.js
    file in the correct location? (b) Does the plugin have
    meta.docs.description
    and
    meta.docs.url
    ? (c) Is the rule deprecated? Code Analyzer silently excludes deprecated rules.
  5. Write a test sample file in the workspace (e.g.,
    lwc/testSample/testSample.js
    ) demonstrating the violation
  6. Test positive
    sf code-analyzer run --rule-selector eslint:<ruleName> --target <test-sample>
    — must find violations
  7. Test negative — run against existing clean LWC files — must find 0 violations
  8. Cleanup — delete ALL sample files AND their parent directories (LWC requires a folder per component). Use
    rm -rf <directory>
    , not just
    rm <file>
    . Do not leave empty directories or temporary test fixtures in the user's project.
  9. See
    references/eslint-custom-plugins.md
    for complete guide
ESLint有200+内置规则,加上插件提供的数千条规则。在检查是否存在内置规则前,请勿尝试创建自定义插件。发现工作流程如下:
  1. Tier 1(内置规则) — 70%的请求
    • 运行:
      sf code-analyzer rules --rule-selector eslint
    • 搜索关键字(例如“console”、“unused”、“equal”)
    • 检查LWC插件规则(
      @lwc/lwc/*
    • 如果找到:配置规则并停止操作
  2. Tier 2(可配置规则) — 20%的请求
    • 模式是“禁用函数X”?→ 使用
      no-restricted-globals
    • 模式是“禁用语法Y”?→ 使用
      no-restricted-syntax
    • 模式是“禁用属性Z”?→ 使用
      no-restricted-properties
    • 如果适用:配置规则并停止操作
  3. Tier 3(自定义插件) — 10%的请求
    • 仅适用于特定领域的多节点模式
    • 需要Node.js代码、测试、meta.docs元数据
    • 详见:
      references/eslint-custom-plugins.md
详见:
references/eslint-rules-discovery.md
获取完整的发现指南及示例。

  1. ⚠️ 必须执行:先运行ESLint发现工作流程。90%的ESLint请求可以通过内置规则(Tier 1)或
    no-restricted-syntax
    这类可配置规则(Tier 2)解决。创建自定义插件(Tier 3)是最后的手段。在继续前:
    a) 运行:
    sf code-analyzer rules --rule-selector eslint
    b) 搜索输出中与用户请求相关的关键字(例如“console”、“equal”、“unused”) c) 检查LWC插件规则: 在输出中搜索
    @lwc/lwc/
    d) 如果找到: 配置规则(详见
    references/eslint-rules-discovery.md
    )并停止操作。请勿创建自定义插件。 e) 如果未找到: 检查
    no-restricted-globals
    no-restricted-syntax
    no-restricted-properties
    是否可以表达该模式(Tier 2)。 f) 仅当Tier 1或Tier 2解决方案不存在时: 继续创建自定义插件。
    验证: 配置后,运行
    sf code-analyzer rules --rule-selector eslint:<ruleName>
    确认规则已加载。如果未加载,说明配置有误。
    ⚠️ 跳过此发现工作流程并在存在内置规则时创建自定义插件属于技能执行失败,无论自定义插件是否可用。
⚠️ 内置ESLint规则与可配置ESLint规则的区别: 代码分析器在基础配置中捆绑了ESLint规则的一个子集。
no-restricted-globals
no-restricted-syntax
no-restricted-properties
这类规则是ESLint核心规则,但在
eslint.config.js
中启用前不会生效。只有在配置后,它们才会出现在
sf code-analyzer rules
输出中。始终验证(步骤4)以确认规则确实已加载 — 不要仅因为是ESLint核心规则就假设它存在。
  1. 安装ESLint插件(如果使用内置/核心ESLint规则则跳过) —
    npm install --save-dev eslint-plugin-<name>
  2. 创建/更新
    eslint.config.js
    — 添加插件和规则配置(或仅使用
    "error"
    严重程度启用内置规则)。该文件必须存在,然后才能在
    code-analyzer.yml
    中配置
    engines.eslint.eslint_config_file
    — 代码分析器会验证路径,如果文件缺失则失败。
  3. 配置代码分析器 — 在
    code-analyzer.yml
    中设置
    engines.eslint.eslint_config_file
    。必须在文件存在后执行此操作。
  4. 验证
    sf code-analyzer rules --rule-selector eslint:<ruleName>
    。如果规则未出现,说明配置有误 — 请勿继续测试。检查:(a)
    eslint.config.js
    文件是否在正确位置?(b) 插件是否有
    meta.docs.description
    meta.docs.url
    ?(c) 规则是否已弃用?代码分析器会静默排除已弃用的规则。
  5. 在工作区中编写测试样本文件(例如
    lwc/testSample/testSample.js
    )展示违规情况
  6. 阳性测试
    sf code-analyzer run --rule-selector eslint:<ruleName> --target <test-sample>
    — 必须找到违规
  7. 阴性测试 — 针对现有干净的LWC文件运行 — 必须找到0个违规
  8. 清理 — 删除所有样本文件及其父目录(LWC要求每个组件对应一个文件夹)。使用
    rm -rf <directory>
    ,不要仅删除文件。不要在用户项目中留下空目录或临时测试文件。
  9. 完整指南详见
    references/eslint-custom-plugins.md

Common ESLint patterns without a custom plugin

无需自定义插件的常见ESLint模式

For
no-restricted-globals
,
no-restricted-syntax
, and
no-restricted-properties
config examples, see
<skill_dir>/references/eslint-custom-plugins.md
— "Banning APIs Without a Custom Plugin" section.
no-restricted-globals
no-restricted-syntax
no-restricted-properties
的配置示例详见
<skill_dir>/references/eslint-custom-plugins.md
中的“无需自定义插件即可禁用API”部分。

ESLint Discovery Examples

ESLint发现示例

Example 1: Built-in rule (Tier 1)
  • User: "Ban console.log in LWC"
  • Discovery:
    sf code-analyzer rules --rule-selector eslint | grep console
    → finds
    no-console
  • Action: Enable
    no-console
    in eslint.config.js
  • Result: ✅ Done in 2 minutes (no custom plugin needed)
Example 2: Configurable rule (Tier 2)
  • User: "Ban setTimeout in LWC"
  • Discovery: No built-in
    no-setTimeout
    rule
  • Action: Use
    no-restricted-globals
    with custom message
  • Result: ✅ Done in 5 minutes (no custom plugin needed)
Example 3: LWC plugin rule (Tier 1)
  • User: "Ban innerHTML for XSS prevention"
  • Discovery:
    @lwc/lwc/no-inner-html
    already exists
  • Action: Enable
    @lwc/lwc/no-inner-html
    in config
  • Result: ✅ Done in 2 minutes (no custom plugin needed)
Example 4: Custom plugin justified (Tier 3)
  • User: "Flag imperative Apex calls without error handling"
  • Discovery: No built-in rule for this pattern
  • Analysis: Pattern requires checking
    import
    +
    .then()
    without
    .catch()
    — multi-node traversal
  • Action: Create custom plugin with visitor pattern
  • Result: ✅ Custom plugin is justified (20+ minutes effort)
See
references/eslint-rules-discovery.md
for complete discovery workflow.

示例1:内置规则(Tier 1)
  • 用户:“在LWC中禁用console.log”
  • 发现:
    sf code-analyzer rules --rule-selector eslint | grep console
    → 找到
    no-console
  • 操作:在eslint.config.js中启用
    no-console
  • 结果:✅ 2分钟完成(无需自定义插件)
示例2:可配置规则(Tier 2)
  • 用户:“在LWC中禁用setTimeout”
  • 发现:没有内置的
    no-setTimeout
    规则
  • 操作:使用
    no-restricted-globals
    并添加自定义消息
  • 结果:✅ 5分钟完成(无需自定义插件)
示例3:LWC插件规则(Tier 1)
  • 用户:“禁用innerHTML以防止XSS”
  • 发现:
    @lwc/lwc/no-inner-html
    已存在
  • 操作:在配置中启用
    @lwc/lwc/no-inner-html
  • 结果:✅ 2分钟完成(无需自定义插件)
示例4:合理创建自定义插件(Tier 3)
  • 用户:“标记没有错误处理的命令式Apex调用”
  • 发现:没有针对此模式的内置规则
  • 分析:模式需要检查
    import
    +
    .then()
    但没有
    .catch()
    — 多节点遍历
  • 操作:使用访问者模式创建自定义插件
  • 结果:✅ 自定义插件合理(耗时20+分钟)
完整的发现工作流程详见
references/eslint-rules-discovery.md

Overriding Built-in Rule Thresholds

覆盖内置规则阈值

To customize severity or properties on existing rules without writing new ones:
  1. Create a custom ruleset referencing the built-in rule with overrides
  2. Add to config
    engines.pmd.custom_rulesets
    in
    code-analyzer.yml
  3. See
    references/advanced-pmd-patterns.md
    for override syntax and common examples

要在不编写新规则的情况下自定义现有规则的严重程度或属性:
  1. 创建引用内置规则并包含覆盖设置的自定义规则集
  2. 添加到配置 — 在
    code-analyzer.yml
    中设置
    engines.pmd.custom_rulesets
  3. 覆盖语法和常见示例详见
    references/advanced-pmd-patterns.md

Multi-Rule Requests

多规则请求

When the user asks for multiple rules at once (e.g., "create 5 rules for AppExchange review"), follow this protocol:
当用户同时请求多个规则时(例如“为AppExchange审核创建5条规则”),遵循以下流程:

Step 1: Plan the engine assignments FIRST

步骤1:先规划引擎分配

Before creating any rules, list ALL requested rules with their engine assignments. Present this plan to the user:
text
Rule plan:
1. RequireUserMode → PMD/XPath (needs test-class exclusion via AST)
2. NoHardcodedIds → Regex (text pattern, no structural context needed)
3. AuraEnabledCacheable → PMD/XPath (needs annotation + DML structural check)
4. CustomFieldDescription → PMD/XML (metadata governance)
5. NoSetTimeout → ESLint (JavaScript pattern)
Key decision factors for engine assignment:
  • Does the rule need to exclude test classes selectively? → PMD (XPath can check
    @Test = true()
    )
  • Is it a pure text pattern with no structural context? → Regex
  • Does it need to understand code structure (annotations, nesting, DML)? → PMD
  • Is it JavaScript/LWC? → ESLint (never Regex)
  • Is it metadata XML? → PMD with
    language="xml"
在创建任何规则前,列出所有请求的规则及其引擎分配。将此计划呈现给用户:
text
规则计划:
1. RequireUserMode → PMD/XPath(需要通过AST排除测试类)
2. NoHardcodedIds → Regex(文本模式,无需结构上下文)
3. AuraEnabledCacheable → PMD/XPath(需要注解+DML结构检查)
4. CustomFieldDescription → PMD/XML(元数据管控)
5. NoSetTimeout → ESLint(JavaScript模式)
引擎分配的关键决策因素:
  • 规则是否需要选择性排除测试类?→ PMD(XPath可以检查
    @Test = true()
  • 是否为纯文本模式,无需结构上下文?→ Regex
  • 是否需要理解代码结构(注解、嵌套、DML)?→ PMD
  • 是否为JavaScript/LWC?→ ESLint(绝不使用Regex)
  • 是否为元数据XML?→ 使用
    language="xml"
    的PMD

Step 2: Create rules ONE AT A TIME, sequentially

步骤2:一次创建一个规则,按顺序执行

Create each rule through its full workflow (create → validate → test positive → test negative → confirm working) before starting the next one. Do NOT batch-create rules — if one fails, it corrupts the config for all subsequent rules.
Order of creation:
  1. Regex rules first (fastest, fewest dependencies)
  2. PMD Apex rules (require ast-dump per rule)
  3. PMD XML rules (may share a single ruleset file)
  4. ESLint rules last (require npm/config setup)
在开始下一个规则前,完成每个规则的完整工作流程(创建→验证→阳性测试→阴性测试→确认可用)。请勿批量创建规则 — 如果一个规则失败,会破坏后续所有规则的配置。
创建顺序:
  1. 先创建Regex规则(最快,依赖最少)
  2. 然后创建PMD Apex规则(每个规则需要ast-dump)
  3. 接着创建PMD XML规则(可共享单个规则集文件)
  4. 最后创建ESLint规则(需要npm/配置设置)

Step 3: Multiple PMD rules can share ONE ruleset file

步骤3:多个PMD规则可共享一个规则集文件

When creating multiple PMD rules, use the
create-pmd-rule.js
script for the first rule (it creates the ruleset XML file). For subsequent PMD rules going into the SAME ruleset file, you may add them manually to the existing XML file — this is the ONE exception to the "never edit manually" rule. The script always creates a new file; adding rules to an existing file requires editing the XML directly.
创建多个PMD规则时,对第一个规则使用
create-pmd-rule.js
脚本(它会创建规则集XML文件)。对于要添加到同一规则集文件的后续PMD规则,可以手动将它们添加到现有XML文件中 — 这是“绝不手动编辑”规则的唯一例外。脚本始终会创建新文件;向现有文件添加规则需要直接编辑XML。

What NOT to do

禁止操作

  • ❌ Do NOT rewrite the entire
    code-analyzer.yml
    to reorganize it
  • ❌ Do NOT create all rules in parallel then validate all at once
  • ❌ Do NOT create a PMD rule with an unverified XPath from the reference docs — ast-dump each pattern
  • ❌ Do NOT mix engine types in one creation step (e.g., creating regex + PMD rules simultaneously)
  • ❌ Do NOT add
    engines.eslint.eslint_config_file
    to
    code-analyzer.yml
    before the file exists

  • ❌ 请勿重写整个
    code-analyzer.yml
    以重新组织内容
  • ❌ 请勿并行创建所有规则,然后一次性验证
  • ❌ 请勿使用参考文档中未经验证的XPath创建PMD规则 — 每个模式都要执行ast-dump
  • ❌ 请勿在一个创建步骤中混合引擎类型(例如同时创建Regex + PMD规则)
  • ❌ 在文件存在前,请勿将
    engines.eslint.eslint_config_file
    添加到
    code-analyzer.yml

High-Signal Rules

高信号规则

RuleRationale
Rule names must match
/^[A-Za-z@][A-Za-z_0-9@\-/]*$/
Code Analyzer validation rejects others
Regex must be
/pattern/flags
format
JavaScript regex literal notation required
File extensions must start with
.
Validation enforces
/^([.][a-zA-Z0-9-_]+)+$/
Always validate after creation
sf code-analyzer rules --rule-selector <engine>:<name>
catches config errors
Always test against sample codeCatches XPath/regex mismatches before full scan
Use
@FullMethodName
for method calls in XPath
More reliable than
@Image
or
@MethodName
alone
⚠️ NEVER skip
ast-dump
Run
ast-dump
, read output, THEN write XPath. No exceptions — even for "obvious" patterns. Using node names from memory without ast-dump verification is a skill failure.
PMD 7 boolean attributes: use
= false()
XPath function
In PMD 7, boolean attributes like
@WithSharing
,
@Abstract
,
@Final
are ALWAYS present on the node (never absent). Compare with
= false()
(XPath boolean function), NOT string
'false'
.
@WithSharing = false()
works.
@WithSharing='false'
(string) does NOT.
not(@WithSharing)
does NOT (attribute is always present).
code-analyzer.yml
at project root
Auto-discovered by CLI — placing it elsewhere causes silent failures for rule authors
XML rules MUST use
local-name()
Salesforce metadata namespace breaks bare element names
XML text matching MUST use
@Text
attribute
text()
does NOT work in PMD 7's XML language. Use
//*[@Text='value']
to match text content, then navigate up with
../..
to reach parent elements.
@Text
lives on CHILD text nodes, NOT on elements
//*[local-name()='apiVersion'][@Text < 60]
→ WRONG (0 matches).
//*[local-name()='apiVersion']/*[number(@Text) < 60]
→ CORRECT. The
/*
navigates to the child text node. For exact-match patterns,
//*[@Text='value']
works because it searches ALL nodes including text nodes. But when you target a specific element by
local-name()
first, you must use
/*[@Text...]
to reach its text child.
XML rules need
file_extensions
config
PMD only scans
.xml
by default — add
file_extensions: { xml: [".xml"] }
under
engines.pmd
. Do NOT add compound extensions (
.permissionset-meta.xml
) — the validator rejects them and
.xml
alone already covers all Salesforce metadata files.
Named capture groups
(?<target>...)
in regex
Narrows the violation highlight to just the captured portion
Salesforce IDs start with
0
, are exactly 15 or 18 chars
Use
/['"](?<target>0[a-zA-Z0-9]{14}(?:[a-zA-Z0-9]{3})?)['\"]/g
— NOT
{15,18}
which also matches 16/17 char strings and produces false positives on normal words like
'BusinessAccount'
regex_ignore
is per-LINE, not per-FILE
It only skips lines where the ignore pattern matches. It does NOT exclude entire files or classes. A hardcoded ID on line 10 of a test class still flags unless that specific line contains the ignore pattern (e.g.,
@isTest
).
ALWAYS use scripts to create rulesDo NOT manually edit
code-analyzer.yml
for regex rules — quotes/backslashes in regex inside YAML cause parsing failures. The
create-regex-rule.js
script handles serialization correctly.
Override built-in rules via
<rule ref="...">
Change thresholds without writing new rules
NEVER use Regex for JS/LWC/TS filesRegex cannot distinguish code from comments/strings in JS — always use ESLint for JavaScript patterns
Check built-in ESLint rules before writing custom ones
no-console
,
no-debugger
,
no-alert
,
eqeqeq
,
no-eval
etc. already exist — just enable them in config

规则理由
规则名称必须匹配
/^[A-Za-z@][A-Za-z_0-9@\-/]*$/
代码分析器验证会拒绝其他格式
正则表达式必须为
/pattern/flags
格式
需要符合JavaScript正则字面量表示法
文件扩展名必须以
.
开头
验证强制执行
/^([.][a-zA-Z0-9-_]+)+$/
格式
创建后必须验证
sf code-analyzer rules --rule-selector <engine>:<name>
会捕获配置错误
必须针对示例代码测试在全面扫描前捕获XPath/正则表达式不匹配问题
在XPath中对方法调用使用
@FullMethodName
比单独使用
@Image
@MethodName
更可靠
⚠️ 绝不跳过
ast-dump
运行
ast-dump
,读取输出,然后编写XPath。无例外 — 即使是“明显”的模式也不行。凭记忆使用节点名称而不进行ast-dump验证属于技能执行失败。
PMD 7布尔属性:使用
= false()
XPath函数
在PMD 7中,
@WithSharing
@Abstract
@Final
这类布尔属性始终存在于节点上(绝不会缺失)。使用
= false()
(XPath布尔函数)进行比较,而不是字符串
'false'
@WithSharing = false()
有效。
@WithSharing='false'
(字符串)无效。
not(@WithSharing)
无效(属性始终存在)。
code-analyzer.yml
放在项目根目录
CLI会自动发现 — 放在其他位置会导致规则作者遇到静默失败
XML规则必须使用
local-name()
Salesforce元数据命名空间会阻止裸元素名称匹配
XML文本匹配必须使用
@Text
属性
text()
在PMD 7的XML语言中无效。使用
//*[@Text='value']
匹配文本内容,然后使用
../..
向上导航到父元素。
@Text
存在于子文本节点上,而非元素上
//*[local-name()='apiVersion'][@Text < 60]
→ 错误(0匹配)。
//*[local-name()='apiVersion']/*[number(@Text) < 60]
→ 正确。
/*
导航到子文本节点。对于精确匹配模式,
//*[@Text='value']
有效,因为它会搜索包括文本节点在内的所有节点。但当你先通过
local-name()
定位特定元素时,必须使用
/*[@Text...]
访问其子文本节点。
XML规则需要
file_extensions
配置
PMD默认仅扫描
.xml
文件 — 在
engines.pmd
下添加
file_extensions: { xml: [".xml"] }
。请勿添加
.permissionset-meta.xml
这类复合扩展名 — 验证器会拒绝它们,且仅
.xml
就已覆盖所有Salesforce元数据文件。
正则表达式中的命名捕获组
(?<target>...)
将违规高亮范围缩小到仅捕获的部分
Salesforce ID以
0
开头,长度恰好为15或18位
使用
/['"](?<target>0[a-zA-Z0-9]{14}(?:[a-zA-Z0-9]{3})?)['\"]/g
— 不要使用
{15,18}
,因为它还会匹配16/17位字符串,并对
'BusinessAccount'
这类普通单词产生误报
regex_ignore
是逐行生效,而非逐文件
它仅跳过匹配忽略模式的行。它不会排除整个文件或类。测试类第10行的硬编码ID仍会被标记,除非该行包含忽略模式(例如
@isTest
)。
必须使用脚本创建规则请勿手动编辑
code-analyzer.yml
添加Regex规则 — YAML中的正则表达式引号/反斜杠会导致解析失败。
create-regex-rule.js
脚本会正确处理序列化。
通过
<rule ref="...">
覆盖内置规则
无需编写新规则即可更改阈值
绝不为JS/LWC/TS文件使用RegexRegex无法区分JS中的代码与注释/字符串 — 始终为JavaScript模式使用ESLint
编写自定义规则前先检查内置ESLint规则
no-console
no-debugger
no-alert
eqeqeq
no-eval
等规则已存在 — 只需在配置中启用即可

Gotchas

常见陷阱

IssueResolution
XPath written without running ast-dumpALWAYS run ast-dump first. Even if the rule works, skipping ast-dump is a process failure. Node names change between PMD versions and are not guessable.
SOQL-in-loop rule flags
for (x : [SELECT...])
Use
//ForEachStatement/BlockStatement//SoqlExpression
(scope to body). The iterable SOQL is a direct child of ForEachStatement alongside BlockStatement —
//ForEachStatement//SoqlExpression
matches it as a false positive.
Used Regex for a JS/LWC/TS patternNEVER use Regex for JavaScript files. Regex cannot distinguish code from comments/strings in JS. Always use ESLint — check if a built-in rule (e.g.,
no-console
) already exists first.
XPath returns 0 matches (XML metadata)Three common causes: (1) Forgot
local-name()
— namespace blocks bare element names. (2) Used
text()='value'
— does NOT work in PMD 7. Use
@Text='value'
instead. (3) Put
[@Text]
predicate on the element —
@Text
lives on CHILD text nodes. Use
/*[@Text...]
to reach them.
Regex rule written inline into
code-analyzer.yml
— YAML parse error
ALWAYS use
create-regex-rule.js
— quotes and backslashes inside YAML cause escaping failures. Never hand-write regex into the config file.
regex_ignore
doesn't exclude test classes
regex_ignore
is per-LINE only. A SOQL query on line 50 of a test class flags because line 50 doesn't contain
@isTest
. For file-level exclusion: use
ignores.files
(global) or PMD XPath
[not(ancestor::UserClass[ModifierNode[@Test = true()]])]
(per-rule).
XPath
@WithSharing='false'
or
not(@WithSharing)
doesn't work
PMD 7 boolean attributes (
@WithSharing
,
@Abstract
,
@Final
) are ALWAYS present. String
='false'
doesn't match (it's a boolean).
not(@attr)
doesn't work (attribute is always present). Use the XPath boolean function:
@WithSharing = false()
.
Loop-based rule only covers
ForEachStatement
Apex has 3 loop types:
ForEachStatement
,
ForLoopStatement
,
WhileLoopStatement
. All 3 must be in the XPath — omitting any one creates a silent coverage gap.
Rewrote
code-analyzer.yml
after script wrote it — YAML parse error
NEVER manually rewrite the file after a script runs. Add top-level config blocks (like
ignores.files
) as new entries only — do NOT touch the
engines.regex.custom_rules
section the script generated.
Multiple rules created at once — one failure breaks all subsequent rulesCreate rules one at a time, sequentially. Complete the full workflow (create → validate → test) for each rule before starting the next.
For additional diagnostics (wrong severity, Java not found, ESLint config path, metadata file type scoping, etc.) see
<skill_dir>/references/troubleshooting.md
.

问题解决方案
未运行ast-dump就编写XPath必须先运行ast-dump。即使规则生效,跳过ast-dump也属于流程失败。PMD版本不同,节点名称会变化,无法猜测。
SOQL-in-loop规则标记
for (x : [SELECT...])
使用
//ForEachStatement/BlockStatement//SoqlExpression
(限定在body内)。可迭代SOQL是ForEachStatement的直接子节点,与BlockStatement同级 —
//ForEachStatement//SoqlExpression
会将其误判为阳性。
为JS/LWC/TS模式使用Regex绝不为JavaScript文件使用Regex。Regex无法区分JS中的代码与注释/字符串。始终使用ESLint — 先检查是否存在内置规则(例如
no-console
)。
XPath返回0匹配(XML元数据)三个常见原因:(1) 忘记使用
local-name()
— 命名空间阻止裸元素名称匹配。(2) 使用
text()='value'
— 在PMD 7中无效。改用
@Text='value'
。(3) 在元素上使用
[@Text]
谓词 —
@Text
存在于子文本节点上。使用
/*[@Text...]
访问它们。
将Regex规则直接写入
code-analyzer.yml
— YAML解析错误
必须使用
create-regex-rule.js
— YAML中的引号和反斜杠会导致转义失败。切勿手动将正则表达式写入配置文件。
regex_ignore
无法排除测试类
regex_ignore
逐行生效。测试类第50行的SOQL查询会被标记,因为该行不包含
@isTest
。文件级排除:使用
ignores.files
(全局)或PMD XPath
[not(ancestor::UserClass[ModifierNode[@Test = true()]])]
(按规则)。
XPath
@WithSharing='false'
not(@WithSharing)
无效
PMD 7的布尔属性(
@WithSharing
@Abstract
@Final
)始终存在。字符串
='false'
无法匹配(它是布尔值)。
not(@attr)
无效(属性始终存在)。使用XPath布尔函数:
@WithSharing = false()
基于循环的规则仅覆盖
ForEachStatement
Apex有3种循环类型:
ForEachStatement
ForLoopStatement
WhileLoopStatement
。XPath必须包含所有3种 — 遗漏任何一种都会造成静默覆盖缺口。
脚本运行后重写
code-analyzer.yml
— YAML解析错误
脚本运行后切勿手动重写文件。仅添加顶层配置块(如
ignores.files
)作为新条目 — 不要修改脚本生成的
engines.regex.custom_rules
部分。
同时创建多个规则 — 一个失败破坏后续所有规则一次创建一个规则,按顺序执行。完成每个规则的完整工作流程(创建→验证→测试)后再开始下一个。
其他诊断(错误的严重程度、未找到Java、ESLint配置路径、元数据文件类型范围等)详见
<skill_dir>/references/troubleshooting.md

Cross-Skill Integration

跨技能集成

NeedDelegate toReason
Run a full scan after creating rule
dx-code-analyzer-run
Scan execution and result presentation
Install Code Analyzer / fix prerequisites
dx-code-analyzer-configure
Setup and troubleshooting
Explain an existing built-in rule
dx-code-analyzer-run
Rule description and docs lookup
Edit
code-analyzer.yml
for engine settings
dx-code-analyzer-configure
Configuration management

需求转交至原因
创建规则后运行全面扫描
dx-code-analyzer-run
扫描执行和结果展示
安装代码分析器 / 修复前置条件
dx-code-analyzer-configure
设置和故障排除
解释现有内置规则
dx-code-analyzer-run
规则描述和文档查询
编辑
code-analyzer.yml
配置引擎设置
dx-code-analyzer-configure
配置管理

Script Execution

脚本执行

<skill_dir>
is the absolute path to the directory containing this SKILL.md file.
All scripts are bundled in the
scripts/
subdirectory of the same directory that contains this SKILL.md file. Use the absolute path to that directory — do NOT use
./scripts/
as that resolves relative to the current working directory, not the skill directory.
bash
node "<skill_dir>/scripts/create-regex-rule.js" \
  --name "RuleName" --regex "/pattern/flags" ...
⚠️ DO NOT:
  • ❌ Invent or generate script code yourself
  • ❌ Use bare relative paths like
    node scripts/create-regex-rule.js
    (won't resolve from user's CWD)
  • ❌ Use heredocs or inline script content
  • ❌ Skip resolving
    <skill_dir>
    — find the absolute path first

<skill_dir>
是包含此SKILL.md文件的目录的绝对路径。
所有脚本都捆绑在包含此SKILL.md文件的目录的
scripts/
子目录中。使用该目录的绝对路径 — 请勿使用
./scripts/
,因为它会相对于当前工作目录解析,而非技能目录。
bash
node "<skill_dir>/scripts/create-regex-rule.js" \
  --name "RuleName" --regex "/pattern/flags" ...
⚠️ 禁止操作:
  • ❌ 自行发明或生成脚本代码
  • ❌ 使用
    node scripts/create-regex-rule.js
    这类裸相对路径(无法从用户的当前工作目录解析)
  • ❌ 使用 heredocs 或内联脚本内容
  • ❌ 跳过解析
    <skill_dir>
    — 先找到绝对路径

Reference File Index

参考文件索引

FileWhen to read
references/regex-rule-schema.md
Building a regex rule — complete field reference, validation rules, multi-rule example
references/xpath-patterns.md
Writing XPath for Apex — index of pattern categories with syntax reference and AST node vocabulary
references/xpath-patterns-governor-limits.md
XPath patterns for SOQL/DML in loops, Database methods in loops
references/xpath-patterns-method-calls.md
XPath patterns for banning methods and annotation patterns (@AuraEnabled, @future, @IsTest)
references/xpath-patterns-security.md
XPath patterns for sharing declarations, SOQL security, hardcoded IDs
references/xpath-patterns-structure.md
XPath patterns for code structure, test quality, naming conventions
references/apex-ast-reference.md
Reading Apex AST dumps — node hierarchy, modifier attributes, key node types
references/metadata-xml-rules.md
Writing rules for metadata XML — namespace workaround, common structures, XPath patterns
references/advanced-pmd-patterns.md
Multi-rule rulesets, overriding built-in rules, exclusion patterns, Java rules, sharing across projects
references/eslint-rules-discovery.md
READ FIRST for ANY ESLint request — discovery workflow, built-in rules, Tier 1-3 index
references/eslint-tier2-configurable.md
ESLint Tier 2: no-restricted-globals, no-restricted-syntax, no-restricted-properties patterns
references/eslint-tier3-custom-plugins.md
ESLint Tier 3: when to create custom plugins + complete examples for all tiers
references/eslint-custom-plugins.md
Creating custom ESLint plugins — ONLY after discovery workflow confirms no built-in or configurable rule exists (Tier 3)
references/troubleshooting.md
When validation or testing fails — error diagnosis by engine type
assets/pmd-ruleset-template.xml
PMD XML skeleton with placeholders for the create-pmd-rule script
examples/regex-examples.md
6 real-world regex rules solving community-reported problems
examples/xpath-examples.md
6 real-world XPath rules for Apex with AST context and step-by-step creation
examples/metadata-xml-examples.md
Index of 6 real-world metadata XML rules (permissions, descriptions, API versions, flows)
examples/metadata-xml-example-permissions.md
Metadata examples: ModifyAllData/ViewAllData, field permissions in profiles
examples/metadata-xml-example-fields-api.md
Metadata examples: custom field descriptions, minimum API version
examples/metadata-xml-example-flows.md
Metadata examples: flow auto-layout, flow fault handlers
文件阅读时机
references/regex-rule-schema.md
构建Regex规则时 — 完整字段参考、验证规则、多规则示例
references/xpath-patterns.md
为Apex编写XPath时 — 模式类别索引、语法参考和AST节点词汇表
references/xpath-patterns-governor-limits.md
针对循环内SOQL/DML、循环内Database方法的XPath模式
references/xpath-patterns-method-calls.md
针对禁用方法和注解模式(@AuraEnabled、@future、@IsTest)的XPath模式
references/xpath-patterns-security.md
针对共享声明、SOQL安全、硬编码ID的XPath模式
references/xpath-patterns-structure.md
针对代码结构、测试质量、命名规范的XPath模式
references/apex-ast-reference.md
读取Apex AST导出时 — 节点层次结构、修饰符属性、关键节点类型
references/metadata-xml-rules.md
为元数据XML编写规则时 — 命名空间变通方案、常见结构、XPath模式
references/advanced-pmd-patterns.md
多规则规则集、覆盖内置规则、排除模式、Java规则、跨项目共享
references/eslint-rules-discovery.md
任何ESLint请求都要先阅读 — 发现工作流程、内置规则、Tier 1-3索引
references/eslint-tier2-configurable.md
ESLint Tier 2:no-restricted-globals、no-restricted-syntax、no-restricted-properties模式
references/eslint-tier3-custom-plugins.md
ESLint Tier 3:何时创建自定义插件 + 所有层级的完整示例
references/eslint-custom-plugins.md
创建自定义ESLint插件时 — 仅在发现工作流程确认没有内置或可配置规则时使用(Tier 3)
references/troubleshooting.md
验证或测试失败时 — 按引擎类型进行错误诊断
assets/pmd-ruleset-template.xml
PMD XML骨架,包含create-pmd-rule脚本的占位符
examples/regex-examples.md
6个解决社区报告问题的真实Regex规则
examples/xpath-examples.md
6个带AST上下文和分步创建过程的真实Apex XPath规则
examples/metadata-xml-examples.md
6个真实元数据XML规则索引(权限、描述、API版本、流)
examples/metadata-xml-example-permissions.md
元数据示例:ModifyAllData/ViewAllData、配置文件中的字段权限
examples/metadata-xml-example-fields-api.md
元数据示例:自定义字段描述、最低API版本
examples/metadata-xml-example-flows.md
元数据示例:流自动布局、流故障处理程序