@salesforce/afv-skills 1.28.0 → 1.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/README.md +2 -0
  2. package/package.json +1 -1
  3. package/skills/agentforce-architecture-analyze/README.md +2 -8
  4. package/skills/agentforce-architecture-analyze/SKILL.md +2 -2
  5. package/skills/agentforce-architecture-analyze/references/contract.json +4 -4
  6. package/skills/agentforce-architecture-analyze/references/soql_fields.md +1 -1
  7. package/skills/data360-activate/README.md +0 -1
  8. package/skills/data360-activate/SKILL.md +0 -2
  9. package/skills/data360-connect/README.md +0 -2
  10. package/skills/data360-connect/SKILL.md +0 -2
  11. package/skills/data360-harmonize/SKILL.md +0 -1
  12. package/skills/data360-orchestrate/README.md +0 -7
  13. package/skills/data360-orchestrate/SKILL.md +0 -2
  14. package/skills/data360-prepare/README.md +0 -1
  15. package/skills/data360-prepare/SKILL.md +0 -1
  16. package/skills/data360-query/README.md +0 -1
  17. package/skills/data360-query/SKILL.md +0 -1
  18. package/skills/data360-segment/README.md +0 -1
  19. package/skills/data360-segment/SKILL.md +0 -2
  20. package/skills/dx-code-analyzer-configure/SKILL.md +31 -13
  21. package/skills/dx-code-analyzer-custom-rule-create/SKILL.md +484 -0
  22. package/skills/dx-code-analyzer-custom-rule-create/assets/pmd-ruleset-template.xml +31 -0
  23. package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-example-fields-api.md +87 -0
  24. package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-example-flows.md +105 -0
  25. package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-example-permissions.md +95 -0
  26. package/skills/dx-code-analyzer-custom-rule-create/examples/metadata-xml-examples.md +84 -0
  27. package/skills/dx-code-analyzer-custom-rule-create/examples/regex-examples.md +127 -0
  28. package/skills/dx-code-analyzer-custom-rule-create/examples/xpath-examples.md +227 -0
  29. package/skills/dx-code-analyzer-custom-rule-create/references/advanced-pmd-patterns.md +288 -0
  30. package/skills/dx-code-analyzer-custom-rule-create/references/apex-ast-reference.md +127 -0
  31. package/skills/dx-code-analyzer-custom-rule-create/references/eslint-custom-plugins.md +247 -0
  32. package/skills/dx-code-analyzer-custom-rule-create/references/eslint-rules-discovery.md +188 -0
  33. package/skills/dx-code-analyzer-custom-rule-create/references/eslint-tier2-configurable.md +114 -0
  34. package/skills/dx-code-analyzer-custom-rule-create/references/eslint-tier3-custom-plugins.md +113 -0
  35. package/skills/dx-code-analyzer-custom-rule-create/references/metadata-xml-rules.md +285 -0
  36. package/skills/dx-code-analyzer-custom-rule-create/references/regex-rule-schema.md +174 -0
  37. package/skills/dx-code-analyzer-custom-rule-create/references/troubleshooting.md +141 -0
  38. package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-governor-limits.md +83 -0
  39. package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-method-calls.md +108 -0
  40. package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-security.md +45 -0
  41. package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns-structure.md +127 -0
  42. package/skills/dx-code-analyzer-custom-rule-create/references/xpath-patterns.md +131 -0
  43. package/skills/dx-code-analyzer-custom-rule-create/scripts/create-pmd-rule.js +209 -0
  44. package/skills/dx-code-analyzer-custom-rule-create/scripts/create-regex-rule.js +220 -0
  45. package/skills/dx-code-analyzer-run/SKILL.md +41 -8
  46. package/skills/experience-cms-brand-apply/SKILL.md +47 -16
  47. package/skills/experience-lwc-generate/README.md +3 -2
  48. package/skills/experience-lwc-generate/references/async-notification-patterns.md +1 -1
  49. package/skills/experience-lwc-generate/references/component-patterns.md +3 -3
  50. package/skills/experience-lwc-generate/references/flow-integration-guide.md +1 -1
  51. package/skills/experience-lwc-generate/references/lwc-best-practices.md +4 -4
  52. package/skills/experience-lwc-generate/references/scoring-and-testing.md +1 -1
  53. package/skills/experience-lwc-generate/references/state-management.md +2 -2
  54. package/skills/experience-lwc-generate/references/triangle-pattern.md +3 -3
  55. package/skills/experience-lwc-generate/scripts/post-tool-validate.py +2 -2
  56. package/skills/experience-lwc-generate/scripts/template_validator.py +1 -1
  57. package/skills/external-diagram-mermaid-generate/README.md +2 -2
  58. package/skills/external-diagram-mermaid-generate/SKILL.md +1 -3
  59. package/skills/external-diagram-mermaid-generate/assets/agentforce/agent-flow.md +4 -4
  60. package/skills/external-diagram-mermaid-generate/assets/architecture/system-landscape.md +1 -1
  61. package/skills/external-diagram-mermaid-generate/assets/datamodel/b2b-commerce-erd.md +3 -3
  62. package/skills/external-diagram-mermaid-generate/assets/datamodel/campaigns-erd.md +1 -1
  63. package/skills/external-diagram-mermaid-generate/assets/datamodel/consent-erd.md +2 -2
  64. package/skills/external-diagram-mermaid-generate/assets/datamodel/files-erd.md +2 -2
  65. package/skills/external-diagram-mermaid-generate/assets/datamodel/forecasting-erd.md +2 -2
  66. package/skills/external-diagram-mermaid-generate/assets/datamodel/fsl-erd.md +2 -2
  67. package/skills/external-diagram-mermaid-generate/assets/datamodel/party-model-erd.md +1 -1
  68. package/skills/external-diagram-mermaid-generate/assets/datamodel/quote-order-erd.md +3 -3
  69. package/skills/external-diagram-mermaid-generate/assets/datamodel/revenue-cloud-erd.md +2 -2
  70. package/skills/external-diagram-mermaid-generate/assets/datamodel/sales-cloud-erd.md +1 -1
  71. package/skills/external-diagram-mermaid-generate/assets/datamodel/salesforce-erd.md +1 -1
  72. package/skills/external-diagram-mermaid-generate/assets/datamodel/scheduler-erd.md +2 -2
  73. package/skills/external-diagram-mermaid-generate/assets/datamodel/service-cloud-erd.md +3 -3
  74. package/skills/external-diagram-mermaid-generate/assets/datamodel/territory-management-erd.md +1 -1
  75. package/skills/external-diagram-mermaid-generate/assets/integration/api-sequence.md +1 -1
  76. package/skills/external-diagram-mermaid-generate/assets/oauth/authorization-code-pkce.md +1 -1
  77. package/skills/external-diagram-mermaid-generate/assets/oauth/authorization-code.md +1 -1
  78. package/skills/external-diagram-mermaid-generate/assets/oauth/client-credentials.md +1 -1
  79. package/skills/external-diagram-mermaid-generate/assets/oauth/device-authorization.md +2 -2
  80. package/skills/external-diagram-mermaid-generate/assets/oauth/jwt-bearer.md +2 -2
  81. package/skills/external-diagram-mermaid-generate/assets/oauth/refresh-token.md +1 -1
  82. package/skills/external-diagram-mermaid-generate/assets/oauth/user-agent-social-sign-on.md +2 -2
  83. package/skills/external-diagram-mermaid-generate/assets/role-hierarchy/user-hierarchy.md +1 -1
  84. package/skills/external-diagram-mermaid-generate/references/color-palette.md +13 -13
  85. package/skills/external-diagram-mermaid-generate/references/diagram-conventions.md +5 -5
  86. package/skills/external-diagram-mermaid-generate/references/erd-conventions.md +1 -1
  87. package/skills/external-diagram-mermaid-generate/references/mermaid-styling.md +2 -2
  88. package/skills/external-diagram-mermaid-generate/references/preview-guide.md +1 -1
  89. package/skills/external-diagram-mermaid-generate/references/usage-examples.md +9 -9
  90. package/skills/external-diagram-mermaid-generate/scripts/README.md +1 -1
  91. package/skills/integration-connectivity-connected-app-configure/README.md +4 -5
  92. package/skills/integration-connectivity-connected-app-configure/SKILL.md +2 -2
  93. package/skills/integration-connectivity-connected-app-configure/references/example-usage.md +7 -7
  94. package/skills/integration-connectivity-connected-app-configure/references/migration-guide.md +1 -1
  95. package/skills/integration-connectivity-connected-app-configure/references/oauth-flows-reference.md +1 -1
  96. package/skills/integration-connectivity-connected-app-configure/references/testing-validation-guide.md +3 -3
  97. package/skills/integration-connectivity-generate/README.md +1 -1
  98. package/skills/integration-connectivity-generate/SKILL.md +6 -0
  99. package/skills/integration-connectivity-generate/assets/callouts/rest-queueable-callout.cls +1 -1
  100. package/skills/integration-connectivity-generate/assets/external-services/external-service-operations.md +1 -1
  101. package/skills/integration-connectivity-generate/assets/soap/wsdl2apex-guide.md +1 -1
  102. package/skills/integration-connectivity-generate/references/callout-patterns.md +4 -0
  103. package/skills/integration-connectivity-generate/references/cdc-guide.md +2 -2
  104. package/skills/integration-connectivity-generate/references/event-driven-architecture-guide.md +5 -5
  105. package/skills/integration-connectivity-generate/references/event-patterns.md +3 -2
  106. package/skills/integration-connectivity-generate/references/external-services-guide.md +4 -1
  107. package/skills/integration-connectivity-generate/references/messaging-api-v2.md +2 -2
  108. package/skills/integration-connectivity-generate/references/named-credentials-automation.md +1 -1
  109. package/skills/integration-connectivity-generate/references/named-credentials-guide.md +12 -2
  110. package/skills/integration-connectivity-generate/references/platform-events-guide.md +3 -3
  111. package/skills/integration-connectivity-generate/references/rest-callout-patterns.md +1 -1
  112. package/skills/integration-connectivity-generate/references/scoring-rubric.md +1 -1
  113. package/skills/integration-connectivity-generate/references/security-best-practices.md +5 -2
  114. package/skills/integration-connectivity-generate/scripts/configure-named-credential.sh +16 -5
  115. package/skills/integration-connectivity-generate/scripts/set-api-credential.sh +11 -2
  116. package/skills/integration-connectivity-generate/scripts/suggest_credential_setup.py +4 -3
  117. package/skills/integration-connectivity-generate/scripts/validate_integration.py +1 -1
  118. package/skills/mobile-platform-native-capabilities-integrate/SKILL.md +3 -3
  119. package/skills/omnistudio-callable-apex-generate/README.md +1 -5
  120. package/skills/omnistudio-callable-apex-generate/SKILL.md +14 -4
  121. package/skills/omnistudio-callable-apex-generate/assets/pattern_callable_vanilla.cls +8 -1
  122. package/skills/omnistudio-callable-apex-generate/examples/Test_VlocityOpenInterface2Conversion/TRANSCRIPT.md +1 -1
  123. package/skills/omnistudio-callable-apex-generate/examples/Test_VlocityOpenInterfaceConversion/TRANSCRIPT.md +1 -1
  124. package/skills/omnistudio-datamapper-generate/SKILL.md +1 -1
  125. package/skills/omnistudio-datamapper-generate/assets/completion-summary-template.md +2 -2
  126. package/skills/omnistudio-datamapper-generate/references/best-practices.md +9 -9
  127. package/skills/omnistudio-datamapper-generate/references/naming-conventions.md +6 -6
  128. package/skills/omnistudio-datapacks-deploy/README.md +1 -1
  129. package/skills/omnistudio-datapacks-deploy/examples/business-internet-plus-bundle/deploy-business-internet-plus-bundle.yaml +1 -1
  130. package/skills/omnistudio-dependencies-analyze/SKILL.md +12 -5
  131. package/skills/omnistudio-dependencies-analyze/references/dependency-patterns.md +9 -9
  132. package/skills/omnistudio-dependencies-analyze/references/namespace-guide.md +1 -1
  133. package/skills/omnistudio-epc-catalog-generate/README.md +1 -1
  134. package/skills/omnistudio-flexcard-generate/SKILL.md +11 -4
  135. package/skills/omnistudio-flexcard-generate/references/best-practices.md +6 -6
  136. package/skills/omnistudio-flexcard-generate/references/data-binding-guide.md +9 -9
  137. package/skills/omnistudio-integration-procedure-generate/SKILL.md +9 -1
  138. package/skills/omnistudio-integration-procedure-generate/references/element-types.md +3 -3
  139. package/skills/omnistudio-omniscript-generate/SKILL.md +2 -2
  140. package/skills/omnistudio-omniscript-generate/references/best-practices.md +1 -1
  141. package/skills/omnistudio-omniscript-generate/scripts/check-duplicate-omniscript.sh +8 -0
  142. package/skills/omnistudio-omniscript-generate/scripts/deploy-omniscript.sh +8 -0
  143. package/skills/platform-apex-logs-debug/README.md +2 -2
  144. package/skills/platform-apex-logs-debug/references/cli-commands.md +2 -2
  145. package/skills/platform-apex-logs-debug/references/debug-log-reference.md +16 -16
  146. package/skills/platform-apex-logs-debug/references/log-analysis-tools.md +2 -2
  147. package/skills/platform-apex-test-run/README.md +1 -1
  148. package/skills/platform-apex-test-run/references/test-fix-loop.md +2 -2
  149. package/skills/platform-apex-test-run/references/testing-best-practices.md +3 -3
  150. package/skills/platform-custom-field-generate/SKILL.md +86 -126
  151. package/skills/platform-custom-field-generate/references/advanced-picklists.md +590 -0
  152. package/skills/platform-data-manage/README.md +4 -4
  153. package/skills/platform-data-manage/references/bulk-testing-example.md +1 -1
  154. package/skills/platform-data-manage/references/crud-workflow-example.md +2 -2
  155. package/skills/platform-data-manage/references/orchestration.md +6 -6
  156. package/skills/platform-flexipage-generate/SKILL.md +281 -346
  157. package/skills/platform-flexipage-generate/references/cli_commands.md +39 -0
  158. package/skills/platform-flexipage-generate/references/flexipage_fieldSection.md +162 -0
  159. package/skills/platform-flexipage-generate/references/flexipage_richText.md +84 -0
  160. package/skills/platform-flexipage-generate/references/identifiers_and_regions.md +167 -0
  161. package/skills/platform-flexipage-generate/references/mcp_action_examples.md +104 -0
  162. package/skills/platform-flexipage-generate/references/record_flexipage_dynamicHighlights.md +119 -0
  163. package/skills/platform-flexipage-generate/references/xml_rules.md +335 -0
  164. package/skills/platform-flexipage-generate/scripts/resolve-component-instructions.sh +42 -0
  165. package/skills/platform-flexipage-generate/scripts/scan-lwc-components.sh +113 -0
  166. package/skills/platform-metadata-deploy/README.md +2 -2
  167. package/skills/platform-metadata-deploy/references/agent-deployment-guide.md +2 -2
  168. package/skills/platform-metadata-deploy/references/deployment-workflows.md +16 -16
  169. package/skills/platform-metadata-deploy/references/orchestration.md +3 -3
  170. package/skills/platform-metadata-deploy/references/trigger-deployment-safety.md +2 -2
  171. package/skills/platform-models-api-configure/SKILL.md +140 -0
  172. package/skills/platform-models-api-configure/scripts/get-orgjwt.sh +48 -0
  173. package/skills/platform-policy-rule-generate/SKILL.md +260 -0
  174. package/skills/platform-policy-rule-generate/references/deploy-errors.md +54 -0
  175. package/skills/platform-policy-rule-generate/references/fixtures-index.md +29 -0
  176. package/skills/platform-policy-rule-generate/references/policy-schema-full.md +250 -0
  177. package/skills/platform-policy-rule-generate/references/templates-access.md +132 -0
  178. package/skills/platform-policy-rule-generate/references/templates-advanced.md +58 -0
  179. package/skills/platform-policy-rule-generate/references/templates-record.md +75 -0
  180. package/skills/platform-policy-rule-generate/references/templates-transform.md +70 -0
  181. package/skills/platform-policy-rule-generate/references/templates.md +10 -0
  182. package/skills/platform-policy-rule-generate/references/ui-compatibility.md +142 -0
  183. package/skills/platform-soql-query/README.md +1 -1
  184. package/skills/platform-soql-query/SKILL.md +1 -1
  185. package/skills/platform-soql-query/references/anti-patterns.md +1 -1
  186. package/skills/platform-soql-query/references/field-coverage-rules.md +1 -1
  187. package/skills/platform-soql-query/references/query-optimization.md +1 -1
  188. package/skills/platform-soql-query/scripts/post-tool-validate.py +3 -3
  189. package/skills/platform-value-set-generate/SKILL.md +305 -0
  190. package/skills/data360-activate/CREDITS.md +0 -5
  191. package/skills/data360-connect/CREDITS.md +0 -5
  192. package/skills/data360-harmonize/CREDITS.md +0 -3
  193. package/skills/data360-orchestrate/CREDITS.md +0 -15
  194. package/skills/data360-orchestrate/UPSTREAM.md +0 -45
  195. package/skills/data360-prepare/CREDITS.md +0 -7
  196. package/skills/data360-query/CREDITS.md +0 -7
  197. package/skills/data360-segment/CREDITS.md +0 -3
  198. package/skills/experience-lwc-generate/CREDITS.md +0 -5
  199. package/skills/external-diagram-mermaid-generate/CREDITS.md +0 -46
  200. package/skills/external-diagram-visual-generate/CREDITS.md +0 -80
  201. package/skills/external-diagram-visual-generate/README.md +0 -83
  202. package/skills/external-diagram-visual-generate/SKILL.md +0 -207
  203. package/skills/external-diagram-visual-generate/assets/architecture/integration-flow.md +0 -55
  204. package/skills/external-diagram-visual-generate/assets/erd/core-objects.md +0 -131
  205. package/skills/external-diagram-visual-generate/assets/erd/custom-objects.md +0 -60
  206. package/skills/external-diagram-visual-generate/assets/lwc/dashboard-card.md +0 -45
  207. package/skills/external-diagram-visual-generate/assets/lwc/data-table.md +0 -57
  208. package/skills/external-diagram-visual-generate/assets/lwc/record-form.md +0 -60
  209. package/skills/external-diagram-visual-generate/assets/review/apex-review.md +0 -57
  210. package/skills/external-diagram-visual-generate/assets/review/lwc-review.md +0 -48
  211. package/skills/external-diagram-visual-generate/references/architect-aesthetic-guide.md +0 -257
  212. package/skills/external-diagram-visual-generate/references/examples-index.md +0 -35
  213. package/skills/external-diagram-visual-generate/references/gemini-cli-setup.md +0 -65
  214. package/skills/external-diagram-visual-generate/references/interview-questions.md +0 -529
  215. package/skills/external-diagram-visual-generate/references/iteration-workflow.md +0 -173
  216. package/skills/external-diagram-visual-generate/scripts/check-prerequisites.sh +0 -101
  217. package/skills/external-diagram-visual-generate/scripts/generate_image.py +0 -243
  218. package/skills/integration-connectivity-connected-app-configure/CREDITS.md +0 -3
  219. package/skills/integration-connectivity-generate/CREDITS.md +0 -5
  220. package/skills/omnistudio-callable-apex-generate/CREDITS.md +0 -9
  221. package/skills/omnistudio-datamapper-generate/CREDITS.md +0 -5
  222. package/skills/omnistudio-datapacks-deploy/CREDITS.md +0 -5
  223. package/skills/omnistudio-dependencies-analyze/CREDITS.md +0 -5
  224. package/skills/omnistudio-epc-catalog-generate/CREDITS.md +0 -14
  225. package/skills/omnistudio-flexcard-generate/CREDITS.md +0 -5
  226. package/skills/omnistudio-integration-procedure-generate/CREDITS.md +0 -5
  227. package/skills/omnistudio-omniscript-generate/CREDITS.md +0 -5
  228. package/skills/platform-apex-generate/CREDITS.md +0 -5
  229. package/skills/platform-apex-logs-debug/CREDITS.md +0 -22
  230. package/skills/platform-apex-test-generate/CREDITS.md +0 -5
  231. package/skills/platform-apex-test-run/CREDITS.md +0 -22
  232. package/skills/platform-data-manage/CREDITS.md +0 -5
  233. package/skills/platform-metadata-deploy/CREDITS.md +0 -25
  234. package/skills/platform-soql-query/CREDITS.md +0 -21
  235. /package/skills/omnistudio-callable-apex-generate/examples/Test_VlocityOpenInterfaceConversion/{MyCustomVlocityOpenInterface2.cls → MyCustomClass.cls} +0 -0
@@ -0,0 +1,131 @@
1
+ # XPath Patterns for Apex Custom Rules
2
+
3
+ Pre-validated PMD XPath patterns for Salesforce Apex code. Every pattern below has been verified against actual `sf code-analyzer ast-dump` output. Use these directly — but still run `ast-dump` on YOUR code to confirm node names haven't changed in newer PMD versions.
4
+
5
+ ## XPath Syntax Quick Reference
6
+
7
+ | Syntax | Meaning |
8
+ |--------|---------|
9
+ | `//Node` | Find Node anywhere in tree |
10
+ | `//Node[@attr='value']` | Node with specific string attribute value |
11
+ | `//Node[@attr = true()]` | Node with boolean attribute = true (**PMD 7 requirement**) |
12
+ | `//Parent//Child` | Child anywhere inside Parent (any descendant) |
13
+ | `//Parent/Child` | **Direct child only** — use this to avoid false positives |
14
+ | `//Node[not(...)]` | Node where condition is NOT true |
15
+ | `//Node[ancestor::Other]` | Node that has Other as an ancestor |
16
+ | `//Node[.//Other]` | Node that contains Other somewhere inside |
17
+ | `@Image` | The name/text of the node |
18
+ | `@FullMethodName` | Full qualified method name (e.g., 'System.debug') |
19
+ | `@LiteralType` | Type of literal: STRING, INTEGER, BOOLEAN, TRUE, FALSE, NULL |
20
+
21
+ ### PMD 7 Boolean Attributes — MUST use `true()` / `false()`
22
+
23
+ In PMD 7, many node attributes are **boolean typed**, not strings — even though `ast-dump` renders them as `Nested='false'` or `Test='true'` in the XML output. String attributes (`@FullMethodName`, `@Name`, `@Image`, `@LiteralType`, etc.) are safe to copy from the AST dump as-is. But **attributes whose values are `true` or `false` in the dump are boolean-typed** — comparing them with string literals (`@Nested='false'`) causes the error: "Cannot compare xs:boolean to xs:string".
24
+
25
+ Known boolean attributes by node:
26
+
27
+ | Node | Boolean Attributes |
28
+ |------|--------------------|
29
+ | `ModifierNode` | `@Test`, `@Public`, `@Private`, `@Protected`, `@Static`, `@Abstract`, `@Final`, `@Global`, `@WithSharing`, `@WithoutSharing`, `@InheritedSharing` |
30
+ | `UserClass` | `@Nested` |
31
+ | `Method` | `@Constructor` |
32
+
33
+ | WRONG | CORRECT |
34
+ |-------|---------|
35
+ | `UserClass[@Nested='false']` | `UserClass[@Nested = false()]` |
36
+ | `UserClass[@Nested='true']` | `UserClass[@Nested = true()]` |
37
+ | `ModifierNode[@Test='true']` | `ModifierNode[@Test = true()]` |
38
+ | `ModifierNode[@Static='false']` | `ModifierNode[@Static = false()]` |
39
+ | `Method[@Constructor = false()]` | `Method[@Constructor = false()]` |
40
+
41
+ `true()` and `false()` are XPath boolean functions. If you see an attribute that looks boolean in the AST dump, assume it is typed as boolean and use `true()`/`false()`.
42
+
43
+ ---
44
+
45
+ ### `/` vs `//` — The Most Common Source of False Positives
46
+
47
+ | XPath | Meaning | Risk |
48
+ |-------|---------|------|
49
+ | `//ForEachStatement//SoqlExpression` | SOQL anywhere inside ForEachStatement | ❌ Matches iterable position too |
50
+ | `//ForEachStatement/BlockStatement//SoqlExpression` | SOQL inside loop **body** only | ✅ Correct |
51
+
52
+ **Rule of thumb:** When matching inside a structural node (loop, if, try), always scope to `/BlockStatement//` to target the body, not sibling children like iterables or conditions.
53
+
54
+ ---
55
+
56
+ ## Pattern Categories (By Topic)
57
+
58
+ For detailed patterns in each category, see the dedicated files below:
59
+
60
+ | Category | File | Contents |
61
+ |----------|------|----------|
62
+ | Governor Limits | [xpath-patterns-governor-limits.md](xpath-patterns-governor-limits.md) | SOQL/DML in loops, Database methods in loops |
63
+ | Method Calls & Annotations | [xpath-patterns-method-calls.md](xpath-patterns-method-calls.md) | Ban specific methods, @AuraEnabled, @future, @IsTest, @SuppressWarnings patterns |
64
+ | Security | [xpath-patterns-security.md](xpath-patterns-security.md) | Sharing declarations, SOQL security modes, hardcoded IDs |
65
+ | Code Structure, Tests & Naming | [xpath-patterns-structure.md](xpath-patterns-structure.md) | DML error handling, empty catches, test assertions, naming conventions |
66
+
67
+ ---
68
+
69
+ ## Key Apex AST Node Names
70
+
71
+ | Apex Construct | AST Node | Key Attributes |
72
+ |---|---|---|
73
+ | Class | `UserClass` | `Image`, `SuperClassName`, `InterfaceNames`, `Nested` |
74
+ | Interface | `UserInterface` | `Image`, `SuperInterfaceName` |
75
+ | Method | `Method` | `Image`, `Arity`, `ReturnType`, `Constructor` |
76
+ | Trigger | `UserTrigger` | `Image`, `TargetName` |
77
+ | SOQL query | `SoqlExpression` | `Query`, `CanonicalQuery` |
78
+ | DML insert | `DmlInsertStatement` | |
79
+ | DML update | `DmlUpdateStatement` | |
80
+ | DML delete | `DmlDeleteStatement` | |
81
+ | DML upsert | `DmlUpsertStatement` | |
82
+ | Method call | `MethodCallExpression` | `FullMethodName`, `MethodName`, `InputParametersSize` |
83
+ | For-each loop | `ForEachStatement` | children: VariableDeclarationStatements, VariableExpression, **BlockStatement** (body), iterable |
84
+ | For loop | `ForLoopStatement` | children: init, condition, update, **BlockStatement** (body) |
85
+ | While loop | `WhileLoopStatement` | children: condition, **BlockStatement** (body) |
86
+ | If/else | `IfElseBlockStatement` > `IfBlockStatement` | `ElseStatement` |
87
+ | Try-catch | `TryCatchFinallyBlockStatement` | |
88
+ | Catch block | `CatchBlockStatement` | `ExceptionType`, `VariableName` |
89
+ | RunAs | `RunAsBlockStatement` | |
90
+ | String literal | `LiteralExpression` | `@LiteralType='STRING'`, `@Image` (value without quotes) |
91
+ | Integer literal | `LiteralExpression` | `@LiteralType='INTEGER'`, `@Image` |
92
+ | Boolean true | `LiteralExpression` | `@LiteralType='TRUE'` |
93
+ | Boolean false | `LiteralExpression` | `@LiteralType='FALSE'` |
94
+ | Null | `LiteralExpression` | `@LiteralType='NULL'` |
95
+ | Variable | `VariableExpression` | `@Image` (name) |
96
+ | Assignment | `AssignmentExpression` | `@Op` (=, +=, etc.) |
97
+ | Binary expression | `BinaryExpression` | `@Op` (+, -, *, /) |
98
+ | Boolean expression | `BooleanExpression` | `@Op` (>, <, ==, !=, >=, <=) |
99
+ | New object | `NewKeyValueObjectExpression` | `@Type` |
100
+ | New object (no-arg) | `NewObjectExpression` | `@Type` |
101
+ | Return | `ReturnStatement` | |
102
+ | Annotation | `Annotation` | `@Name` (IsTest, AuraEnabled, Future, SuppressWarnings) |
103
+ | Annotation param | `AnnotationParameter` | `@Name`, `@Value` |
104
+ | Modifier | `ModifierNode` | `Public`, `Private`, `Static`, `Test`, `WithSharing`, `Global`, etc. |
105
+ | Parameter | `Parameter` | `@Image` (name), `@Type` |
106
+ | New list | `NewListInitExpression` | |
107
+ | New map | `NewMapInitExpression` | |
108
+
109
+ ---
110
+
111
+ ## XPath Best Practices
112
+
113
+ 1. **ALWAYS run `ast-dump` first** — never guess node names, even for patterns listed here
114
+ 2. **Use `/BlockStatement//` for loop/if body** — avoids matching iterables, conditions, etc.
115
+ 3. **Use `@FullMethodName`** for method calls — not `@Image` or `@MethodName` alone
116
+ 4. **Exclude test classes** with `[not(ancestor::UserClass[ModifierNode[@Test = true()]])]`
117
+ 5. **Test with BOTH positive AND negative cases** — ensure no false positives
118
+ 6. **Prefer `//Node` over absolute paths** — code structure varies
119
+ 7. **Use `ancestor::` / `not(ancestor::)`** for structural exclusions (try-catch, test class)
120
+ 8. **Keep XPath simple** — complex expressions are fragile and hard to maintain
121
+
122
+ ## Common False Positive Traps
123
+
124
+ | Pattern | Trap | Fix |
125
+ |---------|------|-----|
126
+ | SOQL in loop | `//ForEachStatement//SoqlExpression` matches iterable | Use `/BlockStatement//` |
127
+ | DML in loop | Same as above | Use `/BlockStatement//` |
128
+ | Ban method in all code | Flags test code too | Add `[not(ancestor::UserClass[ModifierNode[@Test = true()]])]` |
129
+ | Empty block detection | Matches intentional empty constructors | Add `[@Constructor = false()]` or exclude specific patterns |
130
+ | No sharing declaration | Flags inner classes (which inherit) | Add `[@Nested = false()]` |
131
+ | String literal length check | Matches test data strings | Exclude test classes |
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env node
2
+ // Creates a PMD XPath custom rule (XML ruleset file + config reference)
3
+ // Usage: node create-pmd-rule.js --name <name> --xpath <expression> --message <msg> [options]
4
+
5
+ const fs = require("fs");
6
+ const path = require("path");
7
+
8
+ function printUsage() {
9
+ console.error(`Usage: node create-pmd-rule.js --name <name> --xpath <expression> --message <msg> [options]
10
+
11
+ Required:
12
+ --name <name> Rule name (PascalCase, no spaces)
13
+ --xpath <expression> XPath expression to match violations
14
+ --message <msg> Violation message shown to users
15
+
16
+ Optional:
17
+ --description <desc> Detailed rule description (default: same as message)
18
+ --language <lang> PMD language (default: apex)
19
+ --priority <1-5> PMD priority (default: 3)
20
+ --example <code> Example violating code snippet
21
+ --config-file <path> Path to code-analyzer.yml (default: ./code-analyzer.yml)
22
+ --ruleset-dir <dir> Directory for ruleset XML (default: ./custom-rules)
23
+
24
+ Examples:
25
+ node create-pmd-rule.js --name NoSystemDebug --xpath "//MethodCallExpression[@FullMethodName='System.debug']" --message "System.debug not allowed" --priority 3
26
+ node create-pmd-rule.js --name SoqlInLoop --xpath "//ForEachStatement//SoqlExpression" --message "SOQL inside loop" --priority 2`);
27
+ process.exit(1);
28
+ }
29
+
30
+ // Parse arguments
31
+ const args = process.argv.slice(2);
32
+ if (args.length < 1 || args[0] === "--help" || args[0] === "-h") {
33
+ printUsage();
34
+ }
35
+
36
+ const options = {
37
+ name: null,
38
+ xpath: null,
39
+ message: null,
40
+ description: null,
41
+ language: "apex",
42
+ priority: 3,
43
+ example: null,
44
+ configFile: "./code-analyzer.yml",
45
+ rulesetDir: "./custom-rules",
46
+ };
47
+
48
+ for (let i = 0; i < args.length; i++) {
49
+ switch (args[i]) {
50
+ case "--name": options.name = args[++i]; break;
51
+ case "--xpath": options.xpath = args[++i]; break;
52
+ case "--message": options.message = args[++i]; break;
53
+ case "--description": options.description = args[++i]; break;
54
+ case "--language": options.language = args[++i]; break;
55
+ case "--priority": options.priority = parseInt(args[++i], 10); break;
56
+ case "--example": options.example = args[++i]; break;
57
+ case "--config-file": options.configFile = args[++i]; break;
58
+ case "--ruleset-dir": options.rulesetDir = args[++i]; break;
59
+ default:
60
+ console.error(`Unknown option: ${args[i]}`);
61
+ printUsage();
62
+ }
63
+ }
64
+
65
+ // Validate required fields
66
+ if (!options.name) { console.error("Error: --name is required"); process.exit(1); }
67
+ if (!options.xpath) { console.error("Error: --xpath is required"); process.exit(1); }
68
+ if (!options.message) { console.error("Error: --message is required"); process.exit(1); }
69
+
70
+ // Validate rule name
71
+ const RULE_NAME_PATTERN = /^[A-Za-z@][A-Za-z_0-9@\-/]*$/;
72
+ if (!RULE_NAME_PATTERN.test(options.name)) {
73
+ console.error(`Error: Invalid rule name "${options.name}". Must match: ${RULE_NAME_PATTERN}`);
74
+ process.exit(1);
75
+ }
76
+
77
+ // Validate priority
78
+ if (options.priority < 1 || options.priority > 5) {
79
+ console.error("Error: Priority must be 1-5");
80
+ process.exit(1);
81
+ }
82
+
83
+ // Validate language
84
+ const VALID_LANGUAGES = ["apex", "visualforce", "html", "xml", "javascript"];
85
+ if (!VALID_LANGUAGES.includes(options.language.toLowerCase())) {
86
+ console.error(`Error: Invalid language "${options.language}". Supported: ${VALID_LANGUAGES.join(", ")}`);
87
+ process.exit(1);
88
+ }
89
+
90
+ // Set defaults
91
+ if (!options.description) {
92
+ options.description = options.message;
93
+ }
94
+
95
+ // Generate the PMD ruleset XML
96
+ function buildRulesetXml() {
97
+ const escXpath = options.xpath.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
98
+ const escMessage = options.message.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
99
+ const escDescription = options.description.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
100
+
101
+ let xml = `<?xml version="1.0" encoding="UTF-8"?>
102
+ <ruleset name="${options.name}CustomRules"
103
+ xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
104
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
105
+ xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
106
+
107
+ <description>Custom rules for ${options.name}</description>
108
+
109
+ <rule name="${options.name}"
110
+ language="${options.language}"
111
+ message="${escMessage}"
112
+ class="net.sourceforge.pmd.lang.rule.xpath.XPathRule">
113
+
114
+ <description>${escDescription}</description>
115
+ <priority>${options.priority}</priority>
116
+
117
+ <properties>
118
+ <property name="xpath">
119
+ <value><![CDATA[
120
+ ${options.xpath}
121
+ ]]></value>
122
+ </property>
123
+ </properties>`;
124
+
125
+ if (options.example) {
126
+ xml += `
127
+
128
+ <example>
129
+ <![CDATA[
130
+ ${options.example}
131
+ ]]>
132
+ </example>`;
133
+ }
134
+
135
+ xml += `
136
+ </rule>
137
+ </ruleset>
138
+ `;
139
+
140
+ return xml;
141
+ }
142
+
143
+ // Create ruleset directory if needed
144
+ const rulesetDir = path.resolve(options.rulesetDir);
145
+ if (!fs.existsSync(rulesetDir)) {
146
+ fs.mkdirSync(rulesetDir, { recursive: true });
147
+ }
148
+
149
+ // Write ruleset XML
150
+ const rulesetFileName = `${options.name}-pmd-ruleset.xml`;
151
+ const rulesetPath = path.join(rulesetDir, rulesetFileName);
152
+
153
+ if (fs.existsSync(rulesetPath)) {
154
+ console.error(`Error: Ruleset file already exists: ${rulesetPath}`);
155
+ process.exit(1);
156
+ }
157
+
158
+ const rulesetXml = buildRulesetXml();
159
+ fs.writeFileSync(rulesetPath, rulesetXml, "utf8");
160
+
161
+ // Update code-analyzer.yml to reference the ruleset
162
+ const configPath = path.resolve(options.configFile);
163
+ const rulesetRelativePath = path.relative(path.dirname(configPath), rulesetPath);
164
+ let configContent = "";
165
+
166
+ if (fs.existsSync(configPath)) {
167
+ configContent = fs.readFileSync(configPath, "utf8");
168
+ }
169
+
170
+ // Check if this ruleset path is already referenced in config (deduplication)
171
+ const alreadyReferenced = configContent.includes(rulesetRelativePath);
172
+
173
+ if (!configContent) {
174
+ // Create new config
175
+ configContent = `engines:\n pmd:\n custom_rulesets:\n - "${rulesetRelativePath}"\n`;
176
+ } else if (alreadyReferenced) {
177
+ // Ruleset path already in config — skip adding duplicate entry
178
+ // (This happens when the XML was deleted and recreated during iteration)
179
+ } else if (configContent.includes("custom_rulesets:") && configContent.includes("pmd:")) {
180
+ // Add to existing custom_rulesets
181
+ const insertPoint = configContent.indexOf("custom_rulesets:");
182
+ const afterLine = configContent.indexOf("\n", insertPoint) + 1;
183
+ configContent = configContent.slice(0, afterLine) + ` - "${rulesetRelativePath}"\n` + configContent.slice(afterLine);
184
+ } else if (configContent.includes("pmd:")) {
185
+ // Add custom_rulesets under pmd
186
+ const insertPoint = configContent.indexOf("pmd:");
187
+ const afterLine = configContent.indexOf("\n", insertPoint) + 1;
188
+ configContent = configContent.slice(0, afterLine) + ` custom_rulesets:\n - "${rulesetRelativePath}"\n` + configContent.slice(afterLine);
189
+ } else if (configContent.includes("engines:")) {
190
+ // Add pmd section under engines
191
+ const insertPoint = configContent.indexOf("engines:");
192
+ const afterLine = configContent.indexOf("\n", insertPoint) + 1;
193
+ configContent = configContent.slice(0, afterLine) + ` pmd:\n custom_rulesets:\n - "${rulesetRelativePath}"\n` + configContent.slice(afterLine);
194
+ } else {
195
+ // Append engines section
196
+ configContent += `\nengines:\n pmd:\n custom_rulesets:\n - "${rulesetRelativePath}"\n`;
197
+ }
198
+
199
+ fs.writeFileSync(configPath, configContent, "utf8");
200
+
201
+ console.log(JSON.stringify({
202
+ status: "success",
203
+ ruleName: options.name,
204
+ engine: "pmd",
205
+ language: options.language,
206
+ rulesetFile: rulesetPath,
207
+ configFile: configPath,
208
+ message: `Rule "${options.name}" created. Validate with: sf code-analyzer rules --rule-selector pmd:${options.name}`
209
+ }));
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env node
2
+ // Creates a regex custom rule in code-analyzer.yml
3
+ // Usage: node create-regex-rule.js --name <name> --regex <pattern> --description <desc> [options]
4
+
5
+ const fs = require("fs");
6
+ const path = require("path");
7
+
8
+ function printUsage() {
9
+ console.error(`Usage: node create-regex-rule.js --name <name> --regex <pattern> --description <desc> [options]
10
+
11
+ Required:
12
+ --name <name> Rule name (PascalCase, no spaces)
13
+ --regex <pattern> Regex in /pattern/flags format
14
+ --description <desc> What the rule checks
15
+
16
+ Optional:
17
+ --violation-message <msg> Message shown on violation
18
+ --severity <1-5> Severity level (default: 3)
19
+ --tags <tag1,tag2> Comma-separated tags (default: Recommended,Custom)
20
+ --file-extensions <exts> Comma-separated extensions (e.g., .cls,.trigger)
21
+ --regex-ignore <pattern> Negative pattern to exclude matches
22
+ --config-file <path> Path to code-analyzer.yml (default: ./code-analyzer.yml)
23
+
24
+ Examples:
25
+ node create-regex-rule.js --name NoHardcodedIds --regex "/[0-9a-zA-Z]{18}/g" --description "Detects hardcoded IDs" --severity 2 --file-extensions ".cls,.trigger"
26
+ node create-regex-rule.js --name NoTodos --regex "/TODO|FIXME/gi" --description "Flags TODO comments" --severity 4`);
27
+ process.exit(1);
28
+ }
29
+
30
+ // Parse arguments
31
+ const args = process.argv.slice(2);
32
+ if (args.length < 1 || args[0] === "--help" || args[0] === "-h") {
33
+ printUsage();
34
+ }
35
+
36
+ const options = {
37
+ name: null,
38
+ regex: null,
39
+ description: null,
40
+ violationMessage: null,
41
+ severity: 3,
42
+ tags: ["Recommended", "Custom"],
43
+ fileExtensions: null,
44
+ regexIgnore: null,
45
+ configFile: "./code-analyzer.yml",
46
+ };
47
+
48
+ for (let i = 0; i < args.length; i++) {
49
+ switch (args[i]) {
50
+ case "--name": options.name = args[++i]; break;
51
+ case "--regex": options.regex = args[++i]; break;
52
+ case "--description": options.description = args[++i]; break;
53
+ case "--violation-message": options.violationMessage = args[++i]; break;
54
+ case "--severity": options.severity = parseInt(args[++i], 10); break;
55
+ case "--tags": options.tags = args[++i].split(",").map(t => t.trim()); break;
56
+ case "--file-extensions": options.fileExtensions = args[++i].split(",").map(e => e.trim()); break;
57
+ case "--regex-ignore": options.regexIgnore = args[++i]; break;
58
+ case "--config-file": options.configFile = args[++i]; break;
59
+ default:
60
+ console.error(`Unknown option: ${args[i]}`);
61
+ printUsage();
62
+ }
63
+ }
64
+
65
+ // Validate required fields
66
+ if (!options.name) { console.error("Error: --name is required"); process.exit(1); }
67
+ if (!options.regex) { console.error("Error: --regex is required"); process.exit(1); }
68
+ if (!options.description) { console.error("Error: --description is required"); process.exit(1); }
69
+
70
+ // Validate rule name
71
+ const RULE_NAME_PATTERN = /^[A-Za-z@][A-Za-z_0-9@\-/]*$/;
72
+ if (!RULE_NAME_PATTERN.test(options.name)) {
73
+ console.error(`Error: Invalid rule name "${options.name}". Must match: ${RULE_NAME_PATTERN}`);
74
+ process.exit(1);
75
+ }
76
+
77
+ // Validate regex format — must be /pattern/flags with no surrounding whitespace
78
+ // and the flags portion must contain only valid JavaScript regex flag characters.
79
+ options.regex = options.regex.trim();
80
+ if (!options.regex.startsWith("/") || options.regex.lastIndexOf("/") <= 0) {
81
+ console.error(`Error: Regex must be in /pattern/flags format. Got: "${options.regex}"`);
82
+ process.exit(1);
83
+ }
84
+ const lastSlash = options.regex.lastIndexOf("/");
85
+ const flags = options.regex.slice(lastSlash + 1);
86
+ if (!flags) {
87
+ console.error(`Error: Regex must include flags after the closing /. Use /pattern/g at minimum. Got: "${options.regex}"`);
88
+ process.exit(1);
89
+ }
90
+ // Strict flags validation — only valid JS regex flag chars allowed, no spaces, no junk.
91
+ if (!/^[gimsuy]+$/.test(flags)) {
92
+ console.error(`Error: Invalid regex flags "${flags}". Allowed: g, i, m, s, u, y (no spaces, no other characters). Got: "${options.regex}"`);
93
+ process.exit(1);
94
+ }
95
+ // Code Analyzer regex rules require the global flag.
96
+ if (!flags.includes("g")) {
97
+ console.error(`Error: Regex must include the global flag 'g'. Got flags: "${flags}"`);
98
+ process.exit(1);
99
+ }
100
+
101
+ // Validate severity
102
+ if (options.severity < 1 || options.severity > 5) {
103
+ console.error("Error: Severity must be 1-5");
104
+ process.exit(1);
105
+ }
106
+
107
+ // Validate file extensions
108
+ if (options.fileExtensions) {
109
+ for (const ext of options.fileExtensions) {
110
+ if (!ext.startsWith(".")) {
111
+ console.error(`Error: File extension must start with dot: "${ext}"`);
112
+ process.exit(1);
113
+ }
114
+ }
115
+ }
116
+
117
+ // Safely quote a string for YAML output.
118
+ // CRITICAL: regex patterns contain backslashes (`\.`, `\d`, `\\`, etc.).
119
+ // Double-quoted YAML treats `\` as an escape introducer — `\.` is an unknown
120
+ // escape and YAML rejects the file with "unknown escape sequence". Single-quoted
121
+ // YAML treats backslash as literal, which is exactly what regex needs.
122
+ // Strategy:
123
+ // - If value contains a backslash → ALWAYS use single quotes (escape ' as '')
124
+ // - Else if value has no quotes → double quotes (simplest, most readable)
125
+ // - Else → single quotes (escape ' as '')
126
+ function yamlQuote(value) {
127
+ const hasBackslash = value.includes("\\");
128
+ const hasSingle = value.includes("'");
129
+ const hasDouble = value.includes('"');
130
+
131
+ if (!hasBackslash && !hasSingle && !hasDouble) {
132
+ return `"${value}"`;
133
+ }
134
+
135
+ // Single-quoted YAML: only ' needs escaping (as ''). Backslashes pass through.
136
+ const escaped = value.replace(/'/g, "''");
137
+ return `'${escaped}'`;
138
+ }
139
+
140
+ // Build the rule YAML block
141
+ function buildRuleYaml() {
142
+ const indent = " ";
143
+ const lines = [];
144
+ lines.push(` ${options.name}:`);
145
+ lines.push(`${indent} regex: ${yamlQuote(options.regex)}`);
146
+ lines.push(`${indent} description: ${yamlQuote(options.description)}`);
147
+
148
+ if (options.violationMessage) {
149
+ lines.push(`${indent} violation_message: ${yamlQuote(options.violationMessage)}`);
150
+ }
151
+
152
+ lines.push(`${indent} severity: ${options.severity}`);
153
+
154
+ if (options.tags && options.tags.length > 0) {
155
+ lines.push(`${indent} tags:`);
156
+ options.tags.forEach(tag => lines.push(`${indent} - "${tag}"`));
157
+ }
158
+
159
+ if (options.fileExtensions && options.fileExtensions.length > 0) {
160
+ lines.push(`${indent} file_extensions:`);
161
+ options.fileExtensions.forEach(ext => lines.push(`${indent} - "${ext}"`));
162
+ }
163
+
164
+ if (options.regexIgnore) {
165
+ lines.push(`${indent} regex_ignore: ${yamlQuote(options.regexIgnore)}`);
166
+ }
167
+
168
+ return lines.join("\n");
169
+ }
170
+
171
+ // Read or create config file
172
+ const configPath = path.resolve(options.configFile);
173
+ let configContent = "";
174
+
175
+ if (fs.existsSync(configPath)) {
176
+ configContent = fs.readFileSync(configPath, "utf8");
177
+
178
+ // Check if rule already exists
179
+ if (configContent.includes(`${options.name}:`)) {
180
+ console.error(`Error: Rule "${options.name}" already exists in ${configPath}`);
181
+ process.exit(1);
182
+ }
183
+ }
184
+
185
+ const ruleYaml = buildRuleYaml();
186
+
187
+ // Upsert into config
188
+ if (!configContent) {
189
+ // Create new file
190
+ configContent = `engines:\n regex:\n custom_rules:\n${ruleYaml}\n`;
191
+ } else if (configContent.includes("custom_rules:") && configContent.includes("regex:")) {
192
+ // Add to existing custom_rules section
193
+ const insertPoint = configContent.indexOf("custom_rules:");
194
+ const afterCustomRules = configContent.indexOf("\n", insertPoint) + 1;
195
+ configContent = configContent.slice(0, afterCustomRules) + ruleYaml + "\n" + configContent.slice(afterCustomRules);
196
+ } else if (configContent.includes("regex:")) {
197
+ // Add custom_rules section under regex
198
+ const insertPoint = configContent.indexOf("regex:");
199
+ const afterRegex = configContent.indexOf("\n", insertPoint) + 1;
200
+ configContent = configContent.slice(0, afterRegex) + " custom_rules:\n" + ruleYaml + "\n" + configContent.slice(afterRegex);
201
+ } else if (configContent.includes("engines:")) {
202
+ // Add regex section under engines
203
+ const insertPoint = configContent.indexOf("engines:");
204
+ const afterEngines = configContent.indexOf("\n", insertPoint) + 1;
205
+ configContent = configContent.slice(0, afterEngines) + " regex:\n custom_rules:\n" + ruleYaml + "\n" + configContent.slice(afterEngines);
206
+ } else {
207
+ // Append engines section
208
+ configContent += "\nengines:\n regex:\n custom_rules:\n" + ruleYaml + "\n";
209
+ }
210
+
211
+ // Write config
212
+ fs.writeFileSync(configPath, configContent, "utf8");
213
+
214
+ console.log(JSON.stringify({
215
+ status: "success",
216
+ ruleName: options.name,
217
+ engine: "regex",
218
+ configFile: configPath,
219
+ message: `Rule "${options.name}" created. Validate with: sf code-analyzer rules --rule-selector regex:${options.name}`
220
+ }));
@@ -1,10 +1,18 @@
1
1
  ---
2
2
  name: dx-code-analyzer-run
3
- description: "Run Salesforce Code Analyzer to scan code for security, performance, best practice, and code style violations. Supports all engines (PMD, ESLint, CPD, RetireJS, Flow, SFGE, ApexGuru), targets (files, folders, git diff), categories, and severities. Also handles post-scan exploration: filtering results by engine/severity/category/file, and explaining what specific rules mean. TRIGGER when: user says 'scan my code', 'check for security issues', 'run PMD/ESLint', 'find duplicates', 'analyze Flows', 'check vulnerable libraries', 'AppExchange review', 'lint my LWC', 'static analysis', 'code quality', 'show only security violations', 'what is this rule', 'explain ApexCRUDViolation', 'filter results', or mentions engines/file types (.cls, .trigger, .js, .flow-meta.xml). Use this skill for scanning, exploring results, understanding rules, and listing available rules. DO NOT TRIGGER when: user wants to fix code without scanning, or asks ONLY about installation/configuration."
4
- allowed-tools: Read, Bash(sf code-analyzer), Bash(node), Bash(git diff), Bash(date), Write, Edit
3
+ description: "Run Salesforce Code Analyzer to scan code for security, performance, best practice, and code style violations. Supports all engines (PMD, ESLint, CPD, RetireJS, Flow, SFGE, ApexGuru), targets (files, folders, git diff), categories, and severities. Also handles post-scan exploration: filtering results by engine/severity/category/file, and explaining what rules mean. TRIGGER when: user says 'scan my code', 'check security issues', 'run PMD/ESLint', 'find duplicates', 'analyze Flows', 'check vulnerable libraries', 'AppExchange review', 'lint my LWC', 'static analysis', 'code quality', 'show security violations', 'what is this rule', 'explain ApexCRUDViolation', 'filter results', or mentions engines/file types (.cls, .trigger, .js, .flow-meta.xml). Use this skill for scanning, exploring results, and listing rules. DO NOT TRIGGER when: user asks only about installation/configuration (use dx-code-analyzer-configure), or wants to create a custom rule (use dx-code-analyzer-custom-rule-create)."
5
4
  metadata:
6
5
  version: "1.0"
7
- argument-hint: "[target-path] [--engine pmd|eslint|cpd|retire-js|regex|flow|sfge|apexguru] [--category Security|Performance|BestPractices|...] [--severity 1-5] [--diff]"
6
+ relatedSkills:
7
+ - "dx-code-analyzer-configure"
8
+ - "dx-code-analyzer-custom-rule-create"
9
+ cliTools:
10
+ - tool: ["sf"]
11
+ semver: ">=2.0.0"
12
+ - tool: ["node"]
13
+ semver: ">=18.0.0"
14
+ - tool: ["git"]
15
+ semver: ">=2.0.0"
8
16
  ---
9
17
 
10
18
  # Running Code Analyzer Skill
@@ -67,11 +75,13 @@ Any aggregation, filter, or rank question ("which file has the most violations?"
67
75
 
68
76
  ## Overview
69
77
 
78
+ > **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).
79
+
70
80
  This skill translates natural-language requests ("scan for security issues", "check my changes") into the correct `sf code-analyzer run` command, executes scans across any combination of engines/targets/severities, and presents actionable results. When engine-provided fixes are available, it discovers them, asks for user confirmation, applies them safely, and offers verification. Use it for static analysis, security reviews, AppExchange certification, code-quality checks, and finding duplicates/vulnerabilities in Salesforce projects.
71
81
 
72
82
  **In scope:** running scans, parsing/filtering/ranking results, applying engine auto-fixes, diff-based scans, all output formats (JSON/HTML/SARIF/CSV/XML), describing/listing rules, scan-failure troubleshooting.
73
83
 
74
- **Out of scope:** installing/configuring `sf` or the plugin (→ `dx-code-analyzer-configure`), writing custom rules/engines, AI-generated fixes beyond engine-provided ones, deep refactoring, CI/CD setup (→ `dx-code-analyzer-configure`).
84
+ **Out of scope:** installing/configuring `sf` or the plugin (→ `dx-code-analyzer-configure`), writing custom rules/engines (→ `dx-code-analyzer-custom-rule-create`), AI-generated fixes beyond engine-provided ones, deep refactoring, CI/CD setup (→ `dx-code-analyzer-configure`).
75
85
 
76
86
  **Allowed tools:** Bash (`sf code-analyzer`, `node`, `git diff`, `date`), Read, Write, Edit. **Forbidden:** any MCP tool, Agent tool, web tools, other skills, Python, `jq`, inline scripts/heredocs. This skill owns the complete scan-fix-verify-query-explain workflow end-to-end.
77
87
 
@@ -203,7 +213,7 @@ Use the **Bash tool only** — never the `run_code_analyzer` MCP tool.
203
213
 
204
214
  1. Generate the timestamp via Bash: `date +%Y%m%d-%H%M%S` → e.g. `20260512-143022`.
205
215
  2. Tell the user:
206
- ```
216
+ ```text
207
217
  Starting scan...
208
218
  Results: ./code-analyzer-results-20260512-143022.json
209
219
  Log: ./code-analyzer-results-20260512-143022.log
@@ -237,7 +247,7 @@ node "<skill_dir>/scripts/parse-results.js" "./code-analyzer-results-TIMESTAMP.j
237
247
 
238
248
  ### Presentation template
239
249
 
240
- ```
250
+ ```text
241
251
  ## Scan Complete
242
252
 
243
253
  **Found X violations** across Y files.
@@ -296,7 +306,7 @@ node "<skill_dir>/scripts/discover-fixes.js" "./code-analyzer-results-TIMESTAMP.
296
306
 
297
307
  ### 6.3 Present + ASK (then STOP)
298
308
 
299
- ```
309
+ ```text
300
310
  ### Engine-Provided Fixes Available
301
311
  **X of Y violations** have auto-fixes provided by the analysis engine:
302
312
 
@@ -327,7 +337,7 @@ node "<skill_dir>/scripts/summarize-fixes.js" "./code-analyzer-results-TIMESTAMP
327
337
 
328
338
  Then present:
329
339
 
330
- ```
340
+ ```text
331
341
  ### Engine-Provided Fixes Applied Successfully ✓
332
342
  **Applied X auto-fixes across Y files.**
333
343
 
@@ -439,6 +449,29 @@ node "<skill_dir>/scripts/list-rules.js" "<selector>" [options]
439
449
 
440
450
  Filters: `--engine`, `--severity`, `--top` (default 100), `--count-only`. The script pre-validates selector tokens (catches typos like `secruity`) before calling the CLI. Presentation: `<skill_dir>/references/post-scan-workflows.md`.
441
451
 
452
+ ---
453
+ ## Cross-Skill Integration
454
+
455
+ This skill is part of a 3-skill Code Analyzer ecosystem. Hand off cleanly rather than attempting work that belongs to another skill.
456
+
457
+ ### When THIS skill delegates to `dx-code-analyzer-configure`:
458
+
459
+ - Pre-flight check fails (CLI missing, plugin not installed, engine prereqs broken) → stop, delegate, return here after fix
460
+ - User asks to set up CI/CD, edit `code-analyzer.yml`, change severities, or disable engines → delegate entirely
461
+
462
+ ### When THIS skill delegates to `dx-code-analyzer-custom-rule-create`:
463
+
464
+ - User asks to create a new rule, write XPath, write a regex rule, or enforce a pattern not covered by built-in rules → delegate entirely. Do NOT attempt to create rules here.
465
+
466
+ ### When other skills hand off HERE:
467
+
468
+ - `dx-code-analyzer-configure` completes setup → proceed with scan (Step 1–5)
469
+ - `dx-code-analyzer-custom-rule-create` finishes creating a rule → proceed with scan targeting the new rule (e.g., `--rule-selector pmd:<RuleName>`) to verify it works
470
+
471
+ ### Ownership boundary
472
+
473
+ This skill owns the complete **scan → explore → fix** workflow end-to-end. It does NOT own installation, config file management, or rule authoring.
474
+
442
475
  ---
443
476
 
444
477
  ## Constraints & Gotchas