Running Code Analyzer Skill CRITICAL: Mandatory Script Usage Every interaction with Code Analyzer results MUST go through the bundled scripts in . No exceptions. WRONG — never do this: Also forbidden: and any tool — Bash only. RIGHT — always do this: is the absolute path to the directory containing this SKILL.md. Never use — that resolves against the user's CWD, not the skill dir. Any aggregation, filter, or rank question ("which file has the most violations?", "how many PMD issues?", "top rules by count", "break down by severity") is answered by — its output already includes , , and . --- Ov…

\n ```\n3. If no scannable files changed: \"No scannable files in your diff. Code Analyzer supports: .cls, .trigger, .js, .ts, .html, .css, .xml, .flow-meta.xml\"\n4. Pass filtered files as comma-separated `--target` value\n\n## Large Result Sets (500+ violations)\n\n- Summarize: top 10 rules by frequency, top 10 files by violation count\n- Offer: \"Want me to export the full results? Or focus on a specific category/file?\"\n- Don't try to display all 500+ violations inline\n\n## Mega Result Sets (5000+ violations)\n\n- Same as above, but also proactively suggest narrowing scope:\n - \"This is a very large number of violations. Want me to focus on just Critical/High severity, a specific category like Security, or a specific folder?\"\n- If the user originally said \"scan and fix everything\", still follow the full flow (scan → present → discover fixes → ask → apply → summarize) — do NOT shortcut any steps just because the result set is large\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":4731,"content_sha256":"ad1dc6ccf7e6d204073cc8d8e46caa24dc436d6a000928614d8db78227108ea1"},{"filename":"references/vendor-file-handling.md","content":"# Vendor File Handling\n\n## Problem\n\nCode Analyzer scans all JavaScript files, including third-party vendor libraries like jQuery, Bootstrap, Lodash, Handlebars, etc. These libraries often trigger thousands of violations, especially:\n\n- **no-var** (legacy `var` declarations)\n- **prefer-const** (variables that could be const)\n- **code style** (indentation, quotes, semicolons)\n\nA typical scan might find:\n- **9,714 total violations**\n- **9,089 in vendor files** (jQuery UI, Bootstrap, tablesorter)\n- **634 in project source** (your Aura/LWC components)\n\n## Why You Shouldn't Fix Vendor Files\n\n| Risk | Impact |\n|------|--------|\n| **Breaks upgrades** | Modified vendor files can't be cleanly upgraded to newer versions |\n| **Untested changes** | Libraries weren't designed for strict mode or modern JS patterns |\n| **Subtle bugs** | Converting `var` to `let/const` can change scope/hoisting behavior in legacy code |\n| **Maintainability** | Future developers won't know the file was modified and why |\n| **Wasted effort** | The next library upgrade will overwrite your fixes anyway |\n\n## Solutions\n\n### Solution 1: Re-scan with --target (Fastest)\n\nIf you know your project source locations upfront:\n\n```bash\nsf code-analyzer run --rule-selector \u003cselector> \\\n --target \"force-app/main/default/aura,force-app/main/default/lwc\" \\\n --output-file \"./results-project-only.json\" \\\n --include-fixes\n```\n\n**Pros:**\n- Only scans what you need\n- Faster execution\n- Cleaner results\n\n**Cons:**\n- Must know target directories upfront\n- Doesn't show you what violations exist in vendor files (for awareness)\n\n### Solution 2: Intelligent Filtering (Most Accurate)\n\nScan everything first, then use the intelligent filter script to separate vendor from project:\n\n```bash\n# 1. Run full scan\nsf code-analyzer run --rule-selector \u003cselector> \\\n --output-file \"./results-all.json\" --include-fixes\n\n# 2. Filter to project files only\nnode \"\u003cskill_dir>/scripts/filter-violations.js\" \\\n \"./results-all.json\" \\\n \"./results-project.json\" \\\n --report\n\n# 3. Apply fixes to filtered results\nnode \"\u003cskill_dir>/scripts/apply-fixes.js\" \"./results-project.json\"\n```\n\n**Pros:**\n- Intelligent classification using multiple heuristics\n- Shows you vendor vs project breakdown\n- Handles uncertain files (30-70% confidence)\n- No manual pattern maintenance\n\n**Cons:**\n- Scans more files than necessary\n- Takes longer for large codebases\n\n## How the Intelligent Filter Works\n\nThe `filter-violations.js` script uses a **multi-heuristic confidence scoring system**:\n\n### 1. Path-Based Signals (30% weight)\n\n```javascript\n// High confidence vendor indicators\nnode_modules/ → 100% vendor\nbower_components/ → 100% vendor\nvendor/ → 95% vendor\nthird-party/ → 95% vendor\nStaticResourceSources/ → 70% vendor\n\n// Project source indicators\nforce-app/main/default/aura/ → Project\nforce-app/main/default/lwc/ → Project\n```\n\n### 2. Name-Based Signals (30% weight)\n\n```javascript\n// Filename patterns\n*.min.js → 95% vendor (minified)\n*-1.12.1.js → 85% vendor (version in name)\njquery*.js → 85% vendor (known library)\nbootstrap*.js → 85% vendor (known library)\n\n// Checked against package.json dependencies\n```\n\n### 3. Content-Based Signals (40% weight)\n\n```javascript\n// License headers\nMIT License, Apache, BSD, GPL → 80% vendor\n\n// Minification indicators\nAverage line length > 500 chars → 90% vendor\n\u003c 10 lines but > 5KB file → 85% vendor\n\n// Library patterns\nUMD/AMD/CommonJS wrapper → 70% vendor\n@version x.x.x → 65% vendor\n@author (non-project) → 50% vendor\n```\n\n### Final Score\n\n```text\nWeighted Score = (PathScore × 0.3) + (NameScore × 0.3) + (ContentScore × 0.4)\n\n> 70% = Vendor file\n\u003c 30% = Project file\n30-70% = Uncertain (manual review)\n```\n\n## Example Output\n\n```text\n=== INTELLIGENT VENDOR FILE DETECTION ===\n\nOriginal violations: 9714\nFiltered violations: 634\nReduction: 9080 (93.5%)\n\n📦 Vendor files excluded: 127\n 610 violations | 95% confidence | jquery-ui-1.12.1.js\n located in vendor directory, version number in filename, minified file\n 525 violations | 98% confidence | jquery-ui-1.12.1.min.js\n located in vendor directory, minified file (.min.js)\n ... and 125 more vendor files\n\n✅ Project files included: 39\n 157 violations | CRLP_RollupHelper.js\n 103 violations | HH_ContainerHelper.js\n 84 violations | CRLP_FilterGroupHelper.js\n ...\n\n⚠️ Uncertain files: 2\n These files have 30-70% vendor confidence - review manually:\n 45 violations | 55% vendor | customUtility.js\n located in vendor directory\n\n✓ Filtered results written to: ./results-project.json\n```\n\n## Workflow Integration\n\n### When to Use Each Approach\n\n| Scenario | Recommended Approach |\n|----------|---------------------|\n| User says \"fix no-var in my code\" | Use intelligent filter (excludes vendor by default) |\n| User says \"fix all no-var\" | Ask: \"Including vendor files (jQuery, Bootstrap)?\" |\n| User specifies path | Use --target directly |\n| User wants report first | Full scan → intelligent filter → show breakdown |\n\n### Step-by-Step Workflow\n\n```markdown\n1. Run Code Analyzer scan\n2. Parse results\n3. **Check violation distribution:**\n - If 50%+ are in vendor files → offer intelligent filtering\n - If user said \"my code\" or \"project\" → automatically filter\n4. Discover fixes (on filtered or unfiltered results)\n5. Apply fixes\n6. Summarize\n```\n\n## Edge Cases\n\n### Case 1: Vendored Modified Libraries\n\n**Scenario:** Your org has modified a copy of jQuery\n\n**Solution:** The intelligent filter will classify it as vendor, but violations may be legitimate. Options:\n1. Fix manually after filter identifies it\n2. Re-run with --target excluding that specific file\n3. Add to project exceptions in filter script\n\n### Case 2: Project Code in Static Resources\n\n**Scenario:** Your custom JavaScript is in `staticresources/` alongside vendor libs\n\n**Solution:** The filter checks content + name, not just path. Custom code without vendor markers scores as \"project\" or \"uncertain\" for manual review.\n\n### Case 3: Uncertain Classifications\n\n**Scenario:** File scores 30-70% vendor confidence\n\n**Action:** Filter script reports these separately. Review manually:\n- Check file purpose\n- Look for original source/documentation\n- Decide whether to fix or exclude\n\n## Configuration (Future Enhancement)\n\nThe filter script could accept custom patterns:\n\n```bash\nnode filter-violations.js results.json filtered.json \\\n --exclude-patterns \"*.min.js,jquery*,bootstrap*\" \\\n --include-patterns \"force-app/main/default/aura/**,force-app/main/default/lwc/**\"\n```\n\nCurrently uses intelligent defaults and doesn't require configuration.\n\n## Testing the Filter\n\n```bash\n# Run with detailed report\nnode scripts/filter-violations.js \\\n ./code-analyzer-results-20260519-133252.json \\\n ./filtered-output.json \\\n --report\n\n# Check the output\nnode scripts/parse-results.js ./filtered-output.json\n```\n\nCompare before/after violation counts to verify filtering accuracy.\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":7105,"content_sha256":"4740ad9b6b514515eafee32978e09ecff55bf519baea361af06b3ed6deb62637"},{"filename":"scripts/apply-fixes.js","content":"#!/usr/bin/env node\n// Apply engine-provided auto-fixes to source files\n// Usage: node apply-fixes.js \u003cpath-to-results.json>\n// WARNING: This modifies files in place. Ensure you have backups or are using version control.\n\nconst fs = require(\"fs\");\nconst path = require(\"path\");\n\nif (process.argv.length \u003c 3) {\n console.error(\"Usage: node apply-fixes.js \u003cresults-file.json>\");\n process.exit(1);\n}\n\nconst filePath = process.argv[2];\nconst data = JSON.parse(fs.readFileSync(filePath, \"utf8\"));\nconst runDir = data.runDir || \"\";\n\n// Fix targets are confined to the current working directory (the project being\n// fixed). loc.file and runDir come from the results JSON and are untrusted;\n// resolving each target against cwd and rejecting anything that escapes it\n// stops a crafted results file from writing arbitrary paths (e.g. /etc/passwd\n// or ../../outside-project).\nconst baseDir = fs.realpathSync(process.cwd());\n\nfunction isOutside(base, target) {\n const rel = path.relative(base, target);\n return rel === \"\" || rel.startsWith(\"..\") || path.isAbsolute(rel);\n}\n\nfunction safeResolve(candidate) {\n if (typeof candidate !== \"string\" || !candidate) return null;\n let rel = candidate;\n // Engine output often uses absolute paths under the scan runDir; normalize\n // those to project-relative before confinement.\n if (runDir && rel.startsWith(runDir)) rel = rel.substring(runDir.length + 1);\n const resolved = path.resolve(baseDir, rel);\n // Lexical guard: rejects absolute paths and `..` traversal.\n if (isOutside(baseDir, resolved)) return null;\n // Symlink-aware guard: path.resolve/relative do not follow symlinks, so a\n // symlinked path component (e.g. `link/victim` where `link` -> outside the\n // tree) would pass the lexical check and let writeFileSync escape. Fix\n // targets are read before being written, so the file exists — resolve its\n // real path and re-check. Writing to the realpath also avoids following a\n // symlink at write time. A missing target (realpathSync throws) is skipped.\n let real;\n try {\n real = fs.realpathSync(resolved);\n } catch {\n return null;\n }\n if (isOutside(baseDir, real)) return null;\n return real;\n}\n\n// Group fixes by confined, absolute file path\nconst fileFixesMap = new Map();\nlet fixesSkippedUnsafe = 0;\ndata.violations.forEach(v => {\n if (v.fixes && v.fixes.length > 0) {\n v.fixes.forEach(fix => {\n const loc = fix.location;\n const filePath = safeResolve(loc.file);\n if (!filePath) {\n fixesSkippedUnsafe++;\n return;\n }\n\n if (!fileFixesMap.has(filePath)) fileFixesMap.set(filePath, []);\n fileFixesMap.get(filePath).push({\n startLine: loc.startLine,\n startColumn: loc.startColumn,\n endLine: loc.endLine,\n endColumn: loc.endColumn,\n fixedCode: fix.fixedCode,\n rule: v.rule\n });\n });\n }\n});\n\n// Sort fixes by line/column (descending) to apply bottom-up\n// This ensures earlier fixes don't shift line numbers for later ones\nfileFixesMap.forEach((fixes, file) => {\n fixes.sort((a, b) => {\n if (b.startLine !== a.startLine) return b.startLine - a.startLine;\n return b.startColumn - a.startColumn;\n });\n});\n\n// Apply fixes to each file\nlet filesModified = 0;\nlet fixesApplied = 0;\nlet fixesSkipped = 0;\n\nfileFixesMap.forEach((fixes, filePath) => {\n try {\n const content = fs.readFileSync(filePath, \"utf8\");\n const lines = content.split(\"\\n\");\n\n fixes.forEach(fix => {\n const startIdx = fix.startLine - 1;\n const endIdx = fix.endLine - 1;\n if (startIdx \u003c 0 || endIdx >= lines.length || startIdx > endIdx) {\n fixesSkipped++;\n return;\n }\n\n // Handle multi-line replacements: splice out old lines, insert new content\n const firstLine = lines[startIdx];\n const lastLine = lines[endIdx];\n const before = firstLine.substring(0, fix.startColumn - 1);\n const after = lastLine.substring(fix.endColumn - 1);\n const replacement = before + fix.fixedCode + after;\n\n // Remove the spanned lines and insert the replacement\n lines.splice(startIdx, endIdx - startIdx + 1, replacement);\n fixesApplied++;\n });\n\n fs.writeFileSync(filePath, lines.join(\"\\n\"), \"utf8\");\n filesModified++;\n } catch (err) {\n console.error(\"Error fixing \" + filePath + \": \" + err.message);\n }\n});\n\nconsole.log(JSON.stringify({ success: true, filesModified, fixesApplied, fixesSkipped, fixesSkippedUnsafe, totalFixableFiles: fileFixesMap.size }));\n","content_type":"text/javascript; charset=utf-8","language":"javascript","size":4485,"content_sha256":"88fe46553fb6948b26895ddf80d8599b33d8ef4a887b770fa178d6fe4b999952"},{"filename":"scripts/describe-rule.js","content":"#!/usr/bin/env node\n// Version: v1.1 | SHA256: placeholder\n// Get detailed description and documentation for a Code Analyzer rule\n// Usage: node describe-rule.js \u003crule-name> [--engine \u003cengine>]\n//\n// This script runs `sf code-analyzer rules --view detail` with a targeted\n// selector and parses the output to extract rule details including description,\n// severity, tags, and documentation resources.\n//\n// The CLI output format is:\n// === 1. RuleName\n// severity: 2 (High)\n// engine: pmd\n// tags: Recommended, Security, Apex\n// resource: https://...\n// description: Some description text\n\nconst { execFileSync } = require(\"child_process\");\n\n/**\n * Run `sf code-analyzer rules ...` safely.\n *\n * Arguments are passed as an argv array (NOT a shell string), so a rule name\n * or --engine value can never be interpreted as shell syntax. Returns the\n * command's stdout as text; never throws (a non-zero exit still yields the\n * output the caller wants to parse).\n */\nfunction runCodeAnalyzerRules(extraArgs) {\n try {\n return execFileSync(\"sf\", [\"code-analyzer\", \"rules\", ...extraArgs], {\n encoding: \"utf8\",\n timeout: 60000,\n maxBuffer: 2 * 1024 * 1024,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n } catch (err) {\n // Preserve the original `2>&1` behavior: a non-zero exit that emitted\n // partial stdout plus a diagnostic on stderr must surface BOTH streams, in\n // order, so a genuine failure isn't misparsed as \"rule not found\".\n const combined = String(err.stdout || \"\") + String(err.stderr || \"\");\n return combined || (err.output && err.output.filter(Boolean).join(\"\")) || \"\";\n }\n}\n\nfunction printUsage() {\n console.error(`Usage: node describe-rule.js \u003crule-name> [--engine \u003cengine>]\n\nArguments:\n \u003crule-name> The rule name to look up (case-insensitive partial match)\n\nOptions:\n --engine \u003cengine> Narrow lookup to a specific engine (pmd, eslint, cpd, etc.)\n\nExamples:\n node describe-rule.js ApexCRUDViolation\n node describe-rule.js ApexCRUDViolation --engine pmd\n node describe-rule.js no-var --engine eslint\n node describe-rule.js OperationWithLimitsInLoop`);\n process.exit(1);\n}\n\n// Parse CLI arguments\nconst args = process.argv.slice(2);\nif (args.length \u003c 1 || args[0] === \"--help\" || args[0] === \"-h\") {\n printUsage();\n}\n\nconst ruleName = args[0];\nlet engine = null;\n\nfor (let i = 1; i \u003c args.length; i++) {\n if (args[i] === \"--engine\" && args[i + 1]) {\n engine = args[++i].toLowerCase();\n }\n}\n\n// Build the rule selector for the lookup\nconst selector = engine ? `${engine}:${ruleName}` : ruleName;\n\n// Run `sf code-analyzer rules` with --view detail to get full rule info.\n// `selector` is passed as a single argv element, so it cannot inject shell\n// commands even if it contains metacharacters.\nconst rawOutput = runCodeAnalyzerRules([\n \"--rule-selector\",\n selector,\n \"--view\",\n \"detail\",\n]);\nif (!rawOutput) {\n console.log(JSON.stringify({\n status: \"error\",\n message: `Failed to run sf code-analyzer rules for selector \"${selector}\"`,\n }));\n process.exit(0);\n}\n\n// Parse the detail view output\n// Format: === N. RuleName\\n key: value\\n key: value\\n\nconst rules = parseDetailOutput(rawOutput);\n\nif (rules.length === 0) {\n // Try grep fallback for partial/substring match\n const grepResult = tryGrepFallback(ruleName, engine);\n if (grepResult) {\n console.log(JSON.stringify(grepResult));\n process.exit(0);\n }\n\n // Try fuzzy match as final fallback (catches typos like \"Violtion\" → \"Violation\")\n const fuzzyResult = tryFuzzyFallback(ruleName, engine);\n if (fuzzyResult) {\n console.log(JSON.stringify(fuzzyResult));\n process.exit(0);\n }\n\n console.log(JSON.stringify({\n status: \"not_found\",\n message: `Rule \"${ruleName}\" not found${engine ? ` in engine \"${engine}\"` : \"\"}. Verify the rule name with: sf code-analyzer rules --rule-selector ${engine || \"all\"} 2>&1 | grep -i \"${ruleName}\"`,\n }));\n process.exit(0);\n}\n\n// Find exact match (case-insensitive)\nconst exactMatch = rules.find(\n (r) => r.name.toLowerCase() === ruleName.toLowerCase()\n);\n\nif (exactMatch) {\n console.log(JSON.stringify({\n status: \"success\",\n rule: exactMatch,\n }));\n} else if (rules.length === 1) {\n // Single result, use it\n console.log(JSON.stringify({\n status: \"success\",\n rule: rules[0],\n }));\n} else {\n // Multiple matches\n console.log(JSON.stringify({\n status: \"multiple_matches\",\n message: `Found ${rules.length} rules matching \"${ruleName}\":`,\n candidates: rules.map((r) => ({\n name: r.name,\n engine: r.engine,\n severity: r.severity,\n tags: r.tags.join(\", \"),\n })),\n }));\n}\n\n/**\n * Parse the `sf code-analyzer rules --view detail` output format.\n *\n * Expected format:\n * === 1. RuleName\n * severity: 2 (High)\n * engine: pmd\n * tags: Recommended, Security, Apex\n * resource: https://pmd.github.io/...\n * description: Validates that CRUD permissions...\n */\nfunction parseDetailOutput(output) {\n const rules = [];\n const lines = output.split(\"\\n\");\n\n let currentRule = null;\n\n for (let i = 0; i \u003c lines.length; i++) {\n const line = lines[i];\n\n // Match rule header: === N. RuleName (must have a number prefix)\n // Skip \"=== Summary\" which is the footer section\n const headerMatch = line.match(/^===\\s+(\\d+)\\.\\s+(.+)$/);\n if (headerMatch) {\n if (currentRule && currentRule.name) {\n rules.push(currentRule);\n }\n currentRule = {\n name: headerMatch[2].trim(),\n engine: \"unknown\",\n severity: \"unknown\",\n tags: [],\n description: \"\",\n resources: [],\n };\n continue;\n }\n\n // Skip lines if no current rule context\n if (!currentRule) continue;\n\n // Match key-value pairs (indented with spaces):\n // severity: 3 (Moderate)\n // engine: eslint\n // tags: Recommended, BestPractices, JavaScript\n // resource: https://...\n // description: Some text here\n const kvMatch = line.match(/^\\s{2,}(\\w+):\\s+(.+)$/);\n if (kvMatch) {\n const key = kvMatch[1].toLowerCase();\n const value = kvMatch[2].trim();\n\n switch (key) {\n case \"severity\":\n currentRule.severity = value;\n break;\n case \"engine\":\n currentRule.engine = value;\n break;\n case \"tags\":\n currentRule.tags = value.split(\",\").map((t) => t.trim()).filter(Boolean);\n break;\n case \"resource\":\n currentRule.resources.push(value);\n break;\n case \"description\":\n currentRule.description = value;\n // Description may continue on next lines (indented further)\n while (\n i + 1 \u003c lines.length &&\n lines[i + 1].match(/^\\s{14,}/) &&\n !lines[i + 1].match(/^\\s{2,}\\w+:/)\n ) {\n i++;\n currentRule.description += \" \" + lines[i].trim();\n }\n break;\n }\n }\n }\n\n // Push the last rule\n if (currentRule && currentRule.name) {\n rules.push(currentRule);\n }\n\n return rules;\n}\n\n/**\n * Fallback: grep the full rule list for partial matches\n */\nfunction tryGrepFallback(ruleName, engine) {\n try {\n const sel = engine || \"Recommended\";\n const output = runCodeAnalyzerRules([\"--rule-selector\", sel]);\n\n // Case-insensitive substring filter done in JS. Previously this shelled out\n // to `... | grep -i \"${ruleName}\"`, interpolating the untrusted rule name\n // into a shell command; matching here removes that injection sink.\n const needle = ruleName.toLowerCase();\n const grepOutput = output\n .split(\"\\n\")\n .filter((line) => line.toLowerCase().includes(needle))\n .join(\"\\n\");\n\n if (!grepOutput.trim()) return null;\n\n // Parse table output lines\n // Format: index name engine severity tags\n const candidates = grepOutput\n .trim()\n .split(\"\\n\")\n .filter((line) => line.trim() && !line.startsWith(\"─\") && !line.startsWith(\"=\"))\n .slice(0, 10)\n .map((line) => {\n const parts = line.trim().split(/\\s{2,}/);\n // Try to identify which part is the rule name (usually index 1 after the row number)\n if (parts.length >= 4) {\n return {\n name: parts[1] || parts[0],\n engine: parts[2] || \"unknown\",\n severity: parts[3] || \"unknown\",\n tags: parts[4] || \"\",\n };\n }\n return { name: line.trim(), engine: \"unknown\", severity: \"unknown\", tags: \"\" };\n });\n\n if (candidates.length === 0) return null;\n\n return {\n status: \"multiple_matches\",\n message: `Rule \"${ruleName}\" not found as exact match. Found ${candidates.length} potential matches:`,\n candidates,\n };\n } catch (err) {\n return null;\n }\n}\n\n/**\n * Fuzzy fallback: get all rule names and find closest matches by edit distance.\n * Catches typos like \"ApexCRUDVioltion\" → \"ApexCRUDViolation\"\n */\nfunction tryFuzzyFallback(ruleName, engine) {\n try {\n const sel = engine || \"Recommended\";\n const output = runCodeAnalyzerRules([\"--rule-selector\", sel]);\n\n // Extract rule names from table output\n // Lines with rule data have: index name engine severity tags\n const ruleNames = [];\n const ruleInfo = {};\n output.split(\"\\n\").forEach((line) => {\n const parts = line.trim().split(/\\s{2,}/);\n if (parts.length >= 4 && /^\\d+$/.test(parts[0])) {\n const name = parts[1];\n ruleNames.push(name);\n ruleInfo[name] = {\n name: name,\n engine: parts[2] || \"unknown\",\n severity: parts[3] || \"unknown\",\n tags: parts[4] || \"\",\n };\n }\n });\n\n if (ruleNames.length === 0) return null;\n\n // Score each rule by edit distance to the query\n const queryLower = ruleName.toLowerCase();\n const scored = ruleNames\n .map((name) => ({\n name,\n distance: levenshtein(queryLower, name.toLowerCase()),\n // Also check if query is a subsequence (handles missing chars)\n containsSubseq: isSubsequence(queryLower, name.toLowerCase()),\n }))\n .filter((r) => {\n // Only include if distance is reasonable (within 30% of query length)\n const maxDistance = Math.max(3, Math.floor(ruleName.length * 0.3));\n return r.distance \u003c= maxDistance || r.containsSubseq;\n })\n .sort((a, b) => a.distance - b.distance)\n .slice(0, 5);\n\n if (scored.length === 0) return null;\n\n const candidates = scored.map((s) => ({\n ...ruleInfo[s.name],\n distance: s.distance,\n }));\n\n // If the best match is very close (distance \u003c= 2), mark as likely match\n const best = scored[0];\n if (best.distance \u003c= 2) {\n return {\n status: \"multiple_matches\",\n message: `Rule \"${ruleName}\" not found. Did you mean \"${best.name}\"? (${best.distance} character${best.distance === 1 ? \"\" : \"s\"} different)`,\n candidates,\n };\n }\n\n return {\n status: \"multiple_matches\",\n message: `Rule \"${ruleName}\" not found. Closest matches by name similarity:`,\n candidates,\n };\n } catch (err) {\n return null;\n }\n}\n\n/**\n * Levenshtein edit distance between two strings\n */\nfunction levenshtein(a, b) {\n const m = a.length;\n const n = b.length;\n const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));\n\n for (let i = 0; i \u003c= m; i++) dp[i][0] = i;\n for (let j = 0; j \u003c= n; j++) dp[0][j] = j;\n\n for (let i = 1; i \u003c= m; i++) {\n for (let j = 1; j \u003c= n; j++) {\n if (a[i - 1] === b[j - 1]) {\n dp[i][j] = dp[i - 1][j - 1];\n } else {\n dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);\n }\n }\n }\n\n return dp[m][n];\n}\n\n/**\n * Check if 'query' is a subsequence of 'target' (handles missing chars)\n * e.g., \"CRUDVioltion\" is a subsequence of \"CRUDViolation\"\n */\nfunction isSubsequence(query, target) {\n let qi = 0;\n for (let ti = 0; ti \u003c target.length && qi \u003c query.length; ti++) {\n if (query[qi] === target[ti]) qi++;\n }\n // Consider it a match if at least 80% of query chars appear in order\n return qi >= query.length * 0.8;\n}\n","content_type":"text/javascript; charset=utf-8","language":"javascript","size":12192,"content_sha256":"94d5bc5334684b857ec35bf64ea9f2da6ecfc9aa6e14649e09f15a9e1392b36d"},{"filename":"scripts/discover-fixes.js","content":"#!/usr/bin/env node\n// Version: v1.0 | SHA256: 19ec035f7132dc162b54a931cfdd882aa473ad80aa21a8a4be1f1127d75168e5\n// Discover which violations have engine-provided auto-fixes\n// Usage: node discover-fixes.js \u003cpath-to-results.json>\n\nconst fs = require(\"fs\");\n\nif (process.argv.length \u003c 3) {\n console.error(\"Usage: node discover-fixes.js \u003cresults-file.json>\");\n process.exit(1);\n}\n\nconst filePath = process.argv[2];\nconst data = JSON.parse(fs.readFileSync(filePath, \"utf8\"));\nconst runDir = data.runDir || \"\";\n\nconst fixesByRule = {};\nlet totalFixable = 0;\n\ndata.violations.forEach(v => {\n if (v.fixes && v.fixes.length > 0) {\n totalFixable++;\n const rule = v.rule;\n if (!fixesByRule[rule]) fixesByRule[rule] = { engine: v.engine, count: 0, severity: v.severity };\n fixesByRule[rule].count++;\n }\n});\n\nconst topRules = Object.entries(fixesByRule)\n .sort((a, b) => b[1].count - a[1].count)\n .slice(0, 10)\n .map(([rule, info]) => ({ rule, ...info }));\n\nconsole.log(JSON.stringify({ totalFixable, totalViolations: data.violations.length, topRules }));\n","content_type":"text/javascript; charset=utf-8","language":"javascript","size":1063,"content_sha256":"c364214f9600342b2ea7d076c7b2691bee8c75d5bc9e8c691dcdaa8a2c3b826b"},{"filename":"scripts/filter-violations.js","content":"#!/usr/bin/env node\n/**\n * Intelligent vendor file detection for Code Analyzer results\n * Uses multiple heuristics to classify files as vendor vs project code\n *\n * Usage: node filter-violations.js \u003cinput.json> \u003coutput.json> [--report]\n */\n\nconst fs = require('fs');\nconst path = require('path');\n\nclass VendorDetector {\n constructor(projectRoot) {\n this.projectRoot = projectRoot;\n this.packageNames = this.loadPackageNames();\n this.fileContentCache = new Map();\n }\n\n /**\n * Load known third-party package names from package.json\n */\n loadPackageNames() {\n const names = new Set();\n const packageJsonPath = path.join(this.projectRoot, 'package.json');\n\n if (fs.existsSync(packageJsonPath)) {\n try {\n const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));\n Object.keys(pkg.dependencies || {}).forEach(d => names.add(d.toLowerCase()));\n Object.keys(pkg.devDependencies || {}).forEach(d => names.add(d.toLowerCase()));\n } catch (err) {\n // Ignore parsing errors\n }\n }\n\n return names;\n }\n\n /**\n * Main classification method - returns classification with confidence score\n */\n classifyFile(filePath) {\n const scores = {\n pathBased: this.scoreByPath(filePath),\n nameBased: this.scoreByName(filePath),\n contentBased: this.scoreByContent(filePath),\n };\n\n // Weighted average (content analysis is more reliable when available)\n const weights = {\n pathBased: 0.3,\n nameBased: 0.3,\n contentBased: 0.4,\n };\n\n const weightedScore = Object.entries(scores).reduce(\n (sum, [key, score]) => sum + score * weights[key],\n 0\n );\n\n const reasons = this.explainScores(scores, filePath);\n\n return {\n isVendor: weightedScore > 50,\n confidence: weightedScore,\n scores,\n reasons,\n classification: this.getClassificationLabel(weightedScore),\n };\n }\n\n /**\n * Score based on directory/path patterns (0-100)\n */\n scoreByPath(filePath) {\n let score = 0;\n const normalizedPath = filePath.toLowerCase();\n\n // Absolute vendor indicators\n if (/node_modules/.test(normalizedPath)) return 100;\n if (/bower_components/.test(normalizedPath)) return 100;\n if (/vendor\\//.test(normalizedPath)) return 95;\n if (/third[_-]party/.test(normalizedPath)) return 95;\n\n // Common vendor directory names\n if (/\\/lib\\//i.test(normalizedPath)) score += 60;\n if (/StaticResourceSources/i.test(normalizedPath)) score += 70;\n if (/CumulusStaticResources/i.test(normalizedPath)) score += 70;\n\n // Salesforce-specific: project source is typically in aura/ or lwc/\n if (/force-app\\/main\\/default\\/(aura|lwc)\\/[^/]+\\//.test(filePath)) {\n // In an Aura or LWC component directory - likely project code\n return Math.min(score, 20); // Cap score at 20 for project paths\n }\n\n // Outside of component directories but in staticresources\n if (/staticresources/.test(normalizedPath)) score += 50;\n\n return Math.min(score, 100);\n }\n\n /**\n * Score based on filename patterns (0-100)\n */\n scoreByName(filePath) {\n let score = 0;\n const basename = path.basename(filePath).toLowerCase();\n\n // Minified files are almost always vendor code\n if (/\\.min\\.js$/.test(basename)) return 95;\n if (/\\.bundle\\.js$/.test(basename)) score += 80;\n if (/-min\\.js$/.test(basename)) return 95;\n\n // Version numbers in filename (e.g., jquery-1.12.1.js)\n if (/-\\d+\\.\\d+(\\.\\d+)?\\.js$/.test(basename)) score += 85;\n if (/\\d+\\.\\d+\\.\\d+/.test(basename)) score += 70;\n\n // Known library name patterns\n const knownLibs = [\n 'jquery', 'lodash', 'underscore', 'moment', 'angular', 'react', 'vue',\n 'bootstrap', 'foundation', 'handlebars', 'backbone', 'ember', 'knockout',\n 'typeahead', 'select2', 'datatables', 'chart', 'arbor', 'd3', 'raphael',\n 'leaflet', 'mapbox', 'three', 'pixi', 'phaser', 'babylonjs',\n ];\n\n for (const lib of knownLibs) {\n if (new RegExp(`\\\\b${lib}\\\\b`, 'i').test(basename)) {\n score += 85;\n break;\n }\n }\n\n // Check against package.json dependencies\n for (const pkgName of this.packageNames) {\n if (basename.includes(pkgName)) {\n score += 80;\n break;\n }\n }\n\n return Math.min(score, 100);\n }\n\n /**\n * Score based on file content analysis (0-100)\n */\n scoreByContent(filePath) {\n try {\n const absolutePath = path.isAbsolute(filePath)\n ? filePath\n : path.join(this.projectRoot, filePath);\n\n if (!fs.existsSync(absolutePath)) {\n return 0; // Can't score if file doesn't exist\n }\n\n const content = fs.readFileSync(absolutePath, 'utf8');\n let score = 0;\n\n // Check first 2000 characters for header information\n const header = content.slice(0, 2000);\n\n // License headers strongly indicate vendor code\n if (/\\b(MIT License|Apache License|BSD License|GPL|ISC License|Mozilla Public License)\\b/i.test(header)) {\n score += 80;\n }\n if (/@license\\b/i.test(header)) score += 75;\n\n // Copyright notices (but not from the current org)\n if (/@copyright\\b(?!.*salesforce\\.com)/i.test(header)) score += 60;\n\n // Version stamps\n if (/@version\\s+\\d+\\.\\d+\\.\\d+/.test(header)) score += 65;\n if (/\\bv\\d+\\.\\d+\\.\\d+\\b/.test(header)) score += 55;\n\n // Author field that's not project-specific\n if (/@author\\b/i.test(header) && !/salesforce/i.test(header)) score += 50;\n\n // Check for minification indicators\n const lines = content.split('\\n');\n const avgLineLength = content.length / lines.length;\n\n if (avgLineLength > 500) score += 90; // Extremely long lines = minified\n if (avgLineLength > 200) score += 70;\n if (lines.length \u003c 10 && content.length > 5000) score += 85; // Very dense\n\n // UMD/AMD/CommonJS wrapper patterns (common in libraries)\n if (/\\(function\\s*\\([^)]*\\)\\s*\\{[\\s\\S]{0,200}(typeof\\s+define|typeof\\s+module|typeof\\s+exports)/.test(header)) {\n score += 70;\n }\n\n // IIFE wrapping entire file (very common in libraries)\n const trimmed = content.trim();\n if (/^\\(function\\s*\\(/.test(trimmed) && /\\}\\s*\\)\\s*\\([^)]*\\)\\s*;?\\s*$/.test(trimmed)) {\n score += 60;\n }\n\n // Banner comments with project URLs\n if (/\\bhttps?:\\/\\/(github\\.com|npmjs\\.com|unpkg\\.com|cdnjs\\.com)/i.test(header)) {\n score += 75;\n }\n\n return Math.min(score, 100);\n } catch (err) {\n // If we can't read the file, don't penalize it\n return 0;\n }\n }\n\n /**\n * Generate human-readable reasons for the classification\n */\n explainScores(scores, filePath) {\n const reasons = [];\n const basename = path.basename(filePath);\n\n if (scores.pathBased > 70) {\n reasons.push('located in vendor directory');\n }\n if (scores.nameBased > 70) {\n if (/\\.min\\.js$/.test(basename)) {\n reasons.push('minified file (.min.js)');\n } else if (/\\d+\\.\\d+\\.\\d+/.test(basename)) {\n reasons.push('version number in filename');\n } else {\n reasons.push('matches known library name');\n }\n }\n if (scores.contentBased > 70) {\n reasons.push('contains vendor markers (license, version, minification)');\n }\n\n if (reasons.length === 0) {\n if (scores.pathBased \u003c 30 && scores.nameBased \u003c 30) {\n reasons.push('appears to be project source code');\n } else {\n reasons.push('unclear classification');\n }\n }\n\n return reasons;\n }\n\n /**\n * Get classification label based on confidence score\n */\n getClassificationLabel(score) {\n if (score > 70) return 'vendor';\n if (score \u003c 30) return 'project';\n return 'uncertain';\n }\n}\n\n/**\n * Filter violations from Code Analyzer results\n */\nfunction filterViolations(inputFile, outputFile, options = {}) {\n const results = JSON.parse(fs.readFileSync(inputFile, 'utf8'));\n const projectRoot = results.runDir || process.cwd();\n\n const detector = new VendorDetector(projectRoot);\n\n // Group violations by file\n const fileViolations = {};\n for (const v of results.violations) {\n const file = v.locations[v.primaryLocationIndex].file;\n if (!fileViolations[file]) {\n fileViolations[file] = {\n violations: [],\n classification: null,\n };\n }\n fileViolations[file].violations.push(v);\n }\n\n // Classify each file\n const analysis = {\n vendor: [],\n project: [],\n uncertain: [],\n };\n\n for (const [file, data] of Object.entries(fileViolations)) {\n const classification = detector.classifyFile(file);\n data.classification = classification;\n\n const entry = {\n file,\n violationCount: data.violations.length,\n ...classification,\n };\n\n if (classification.classification === 'vendor') {\n analysis.vendor.push(entry);\n } else if (classification.classification === 'project') {\n analysis.project.push(entry);\n } else {\n analysis.uncertain.push(entry);\n }\n }\n\n // Filter violations to project files only\n const projectFiles = new Set(analysis.project.map(e => e.file));\n const filteredViolations = results.violations.filter(v => {\n const file = v.locations[v.primaryLocationIndex].file;\n return projectFiles.has(file);\n });\n\n // Recalculate severity counts\n const severityCounts = { sev1: 0, sev2: 0, sev3: 0, sev4: 0, sev5: 0 };\n for (const v of filteredViolations) {\n const sev = `sev${v.severity}`;\n if (severityCounts[sev] !== undefined) {\n severityCounts[sev]++;\n }\n }\n\n // Create filtered results\n const filteredResults = {\n ...results,\n violations: filteredViolations,\n violationCounts: {\n total: filteredViolations.length,\n ...severityCounts,\n },\n filterMetadata: {\n filteredAt: new Date().toISOString(),\n originalViolations: results.violations.length,\n filteredViolations: filteredViolations.length,\n vendorFilesExcluded: analysis.vendor.length,\n projectFilesIncluded: analysis.project.length,\n uncertainFiles: analysis.uncertain.length,\n },\n };\n\n fs.writeFileSync(outputFile, JSON.stringify(filteredResults, null, 2));\n\n // Print summary\n printSummary(results, filteredResults, analysis, options);\n\n return filteredResults;\n}\n\n/**\n * Print detailed summary report\n */\nfunction printSummary(original, filtered, analysis, options) {\n console.log('\\n=== INTELLIGENT VENDOR FILE DETECTION ===\\n');\n console.log(`Original violations: ${original.violations.length}`);\n console.log(`Filtered violations: ${filtered.violations.length}`);\n console.log(`Reduction: ${original.violations.length - filtered.violations.length} (${((1 - filtered.violations.length / original.violations.length) * 100).toFixed(1)}%)\\n`);\n\n console.log(`📦 Vendor files excluded: ${analysis.vendor.length}`);\n if (analysis.vendor.length > 0 && options.report) {\n const top = analysis.vendor.sort((a, b) => b.violationCount - a.violationCount).slice(0, 10);\n top.forEach(e => {\n console.log(` ${e.violationCount.toString().padStart(4)} violations | ${e.confidence.toFixed(0)}% confidence | ${e.file}`);\n console.log(` ${e.reasons.join(', ')}`);\n });\n if (analysis.vendor.length > 10) {\n console.log(` ... and ${analysis.vendor.length - 10} more vendor files`);\n }\n }\n\n console.log(`\\n✅ Project files included: ${analysis.project.length}`);\n if (analysis.project.length > 0 && options.report) {\n const top = analysis.project.sort((a, b) => b.violationCount - a.violationCount).slice(0, 10);\n top.forEach(e => {\n console.log(` ${e.violationCount.toString().padStart(4)} violations | ${e.file}`);\n });\n }\n\n if (analysis.uncertain.length > 0) {\n console.log(`\\n⚠️ Uncertain files: ${analysis.uncertain.length}`);\n console.log(' These files have 30-70% vendor confidence - review manually:');\n analysis.uncertain.forEach(e => {\n console.log(` ${e.violationCount.toString().padStart(4)} violations | ${e.confidence.toFixed(0)}% vendor | ${e.file}`);\n console.log(` ${e.reasons.join(', ')}`);\n });\n }\n\n console.log(`\\n✓ Filtered results written to: ${outputFile}`);\n}\n\n// CLI\nconst args = process.argv.slice(2);\nif (args.length \u003c 2) {\n console.error('Usage: node filter-violations.js \u003cinput.json> \u003coutput.json> [--report]');\n console.error('');\n console.error('Options:');\n console.error(' --report Show detailed file-by-file analysis');\n process.exit(1);\n}\n\nconst [inputFile, outputFile] = args;\nconst options = {\n report: args.includes('--report'),\n};\n\ntry {\n filterViolations(inputFile, outputFile, options);\n} catch (err) {\n console.error('Error:', err.message);\n process.exit(1);\n}\n","content_type":"text/javascript; charset=utf-8","language":"javascript","size":12707,"content_sha256":"f2f21980f70d8324a3ceb21702d299131d0fc78b1ecec4700cc958b0816c36ff"},{"filename":"scripts/list-rules.js","content":"#!/usr/bin/env node\n// Version: v1.0 | SHA256: placeholder\n// List Code Analyzer rules matching a selector and return structured JSON\n// Usage: node list-rules.js \u003cselector> [options]\n//\n// This script runs `sf code-analyzer rules --rule-selector \u003cselector>` and\n// parses the table output into structured JSON for presentation.\n//\n// The CLI table format is:\n// # Name Engine Severity Tags\n// 1 @lwc/lwc/no-inner-html eslint 2 (High) Recommended, LWC, Security\n//\n// Output: JSON with {status, totalRules, rules: [{name, engine, severity, severityNum, tags}], engines, summary}\n\nconst { execSync } = require(\"child_process\");\n\nfunction printUsage() {\n console.error(`Usage: node list-rules.js \u003cselector> [options]\n\nArguments:\n \u003cselector> Rule selector (same syntax as --rule-selector)\n Examples: \"Security\", \"pmd\", \"eslint:Recommended\",\n \"(pmd,eslint):Security:(1,2)\", \"Apex\", \"JavaScript\"\n\nOptions:\n --engine \u003cname> Filter results to a specific engine after listing\n --severity \u003cn> Filter results to specific severity (1-5, comma-separated)\n --top \u003cn> Return at most N rules (default: 100)\n --count-only Return only counts by engine/severity/category (no rule list)\n\nValid selector tokens:\n Engines: eslint, regex, retire-js, flow, pmd, cpd, sfge\n Severities: Critical/1, High/2, Moderate/3, Low/4, Info/5\n Categories: Security, Performance, BestPractices, CodeStyle, Design, ErrorProne, Documentation\n Languages: Apex, JavaScript, TypeScript, HTML, CSS, Visualforce, XML\n Tags: Recommended, Custom, All, DevPreview, LWC, Fixable\n\nExamples:\n node list-rules.js \"Security\"\n node list-rules.js \"pmd:Security\"\n node list-rules.js \"eslint:Recommended\"\n node list-rules.js \"(pmd,eslint):Security:(1,2)\"\n node list-rules.js \"Apex\"\n node list-rules.js \"JavaScript:BestPractices\"\n node list-rules.js \"Recommended\" --count-only\n node list-rules.js \"all\" --engine pmd --severity 1,2 --top 10`);\n process.exit(1);\n}\n\n// Parse CLI arguments\nconst args = process.argv.slice(2);\nif (args.length \u003c 1 || args[0] === \"--help\" || args[0] === \"-h\") {\n printUsage();\n}\n\nconst selector = args[0];\nconst options = {\n engine: null,\n severity: null,\n top: 100,\n countOnly: false,\n};\n\nfor (let i = 1; i \u003c args.length; i++) {\n switch (args[i]) {\n case \"--engine\":\n options.engine = (args[++i] || \"\").toLowerCase();\n break;\n case \"--severity\":\n options.severity = (args[++i] || \"\")\n .split(\",\")\n .map((s) => parseInt(s.trim(), 10))\n .filter((n) => n >= 1 && n \u003c= 5);\n break;\n case \"--top\":\n options.top = parseInt(args[++i] || \"25\", 10);\n break;\n case \"--count-only\":\n options.countOnly = true;\n break;\n default:\n console.error(`Unknown option: ${args[i]}`);\n printUsage();\n }\n}\n\n// Validate selector tokens before running CLI\nconst validationError = validateSelector(selector);\nif (validationError) {\n console.log(JSON.stringify({\n status: \"invalid_selector\",\n message: validationError,\n hint: \"Valid tokens: engines (pmd, eslint, cpd, retire-js, regex, flow, sfge), severities (1-5 or Critical/High/Moderate/Low/Info), categories (Security, Performance, BestPractices, CodeStyle, Design, ErrorProne, Documentation), languages (Apex, JavaScript, TypeScript, HTML, CSS, Visualforce, XML), tags (Recommended, Custom, All, DevPreview, LWC, Fixable)\",\n }));\n process.exit(0);\n}\n\n// Run `sf code-analyzer rules`\nlet rawOutput;\ntry {\n const cmd = `sf code-analyzer rules --rule-selector \"${selector}\" 2>&1`;\n rawOutput = execSync(cmd, {\n encoding: \"utf8\",\n timeout: 60000,\n maxBuffer: 2 * 1024 * 1024,\n });\n} catch (err) {\n rawOutput = err.stdout || err.stderr || (err.output && err.output.join(\"\")) || \"\";\n if (!rawOutput) {\n console.log(JSON.stringify({\n status: \"error\",\n message: `Failed to run sf code-analyzer rules: ${err.message}`,\n }));\n process.exit(0);\n }\n}\n\n// Parse the table output\nconst rules = parseTableOutput(rawOutput);\n\nif (rules.length === 0) {\n console.log(JSON.stringify({\n status: \"no_rules_found\",\n message: `No rules matched selector \"${selector}\". Check the selector syntax or try a broader query.`,\n hint: \"Use tokens like: Security, pmd, eslint:Recommended, (1,2), Apex\",\n }));\n process.exit(0);\n}\n\n// Apply post-filters\nlet filtered = rules;\nif (options.engine) {\n filtered = filtered.filter((r) => r.engine.toLowerCase() === options.engine);\n}\nif (options.severity) {\n filtered = filtered.filter((r) => options.severity.includes(r.severityNum));\n}\n\n// Compute summary stats\nconst engineCounts = {};\nconst severityCounts = {};\nconst categoryCounts = {};\nfiltered.forEach((r) => {\n engineCounts[r.engine] = (engineCounts[r.engine] || 0) + 1;\n const sevKey = `${r.severityNum} (${severityName(r.severityNum)})`;\n severityCounts[sevKey] = (severityCounts[sevKey] || 0) + 1;\n (r.tags || []).forEach((tag) => {\n const t = tag.trim();\n if ([\"Security\", \"Performance\", \"BestPractices\", \"CodeStyle\", \"Design\", \"ErrorProne\", \"Documentation\"].includes(t)) {\n categoryCounts[t] = (categoryCounts[t] || 0) + 1;\n }\n });\n});\n\n// Build result\nconst result = {\n status: \"success\",\n selector,\n totalRules: filtered.length,\n summary: {\n byEngine: engineCounts,\n bySeverity: severityCounts,\n byCategory: categoryCounts,\n },\n};\n\nif (!options.countOnly) {\n result.rules = filtered.slice(0, options.top).map((r) => ({\n name: r.name,\n engine: r.engine,\n severity: r.severity,\n severityNum: r.severityNum,\n tags: r.tags,\n }));\n if (filtered.length > options.top) {\n result.truncated = true;\n result.showing = options.top;\n }\n}\n\nconsole.log(JSON.stringify(result));\n\n// --- Helper Functions ---\n\nfunction parseTableOutput(output) {\n const rules = [];\n const lines = output.split(\"\\n\");\n\n for (const line of lines) {\n // Match table rows: starts with spaces + number\n // Format: \" 1 ruleName engine severity tags\"\n const match = line.match(/^\\s+(\\d+)\\s{2,}(\\S+)\\s{2,}(\\S+)\\s{2,}(\\d+\\s*\\([^)]+\\))\\s{2,}(.+)$/);\n if (match) {\n const severityStr = match[4].trim();\n const sevNumMatch = severityStr.match(/^(\\d)/);\n rules.push({\n name: match[2].trim(),\n engine: match[3].trim(),\n severity: severityStr,\n severityNum: sevNumMatch ? parseInt(sevNumMatch[1], 10) : 0,\n tags: match[5].trim().split(\",\").map((t) => t.trim()).filter(Boolean),\n });\n }\n }\n\n return rules;\n}\n\nfunction validateSelector(selector) {\n if (!selector || !selector.trim()) {\n return \"Selector cannot be empty.\";\n }\n\n const VALID_TOKENS = new Set([\n // Engines\n \"eslint\", \"regex\", \"retire-js\", \"flow\", \"pmd\", \"cpd\", \"sfge\",\n // Severity names\n \"critical\", \"high\", \"moderate\", \"low\", \"info\",\n // Severity numbers\n \"1\", \"2\", \"3\", \"4\", \"5\",\n // General tags\n \"recommended\", \"custom\", \"all\",\n // Categories\n \"bestpractices\", \"codestyle\", \"design\", \"documentation\", \"errorprone\", \"security\", \"performance\",\n // Languages\n \"apex\", \"css\", \"html\", \"javascript\", \"typescript\", \"visualforce\", \"xml\",\n // Engine-specific\n \"devpreview\", \"lwc\", \"fixable\",\n ]);\n\n // Split by : (AND), then handle () groups (OR)\n const groups = selector.split(\":\").map((s) => s.trim()).filter(Boolean);\n const invalid = [];\n\n for (const group of groups) {\n let tokens;\n if (group.startsWith(\"(\") && group.endsWith(\")\")) {\n // OR group: (token1,token2)\n tokens = group.slice(1, -1).split(\",\").map((t) => t.trim()).filter(Boolean);\n } else {\n tokens = [group];\n }\n\n for (const token of tokens) {\n if (!VALID_TOKENS.has(token.toLowerCase())) {\n invalid.push(token);\n }\n }\n }\n\n if (invalid.length > 0) {\n return `Invalid selector token(s): ${invalid.join(\", \")}. Did you misspell a token?`;\n }\n return null;\n}\n\nfunction severityName(num) {\n const names = { 1: \"Critical\", 2: \"High\", 3: \"Moderate\", 4: \"Low\", 5: \"Info\" };\n return names[num] || \"Unknown\";\n}\n","content_type":"text/javascript; charset=utf-8","language":"javascript","size":8111,"content_sha256":"23f773234b80680ada77564fa59634f6d3db584e81abc7621410eee6c9f0a758"},{"filename":"scripts/parse-results.js","content":"#!/usr/bin/env node\n// Version: v1.0 | SHA256: 077933925fea8efb4bcfd2c2fd59d4589a0b31ae34d5c1ac68c2080c8af7d74d\n// Parse Code Analyzer JSON results and extract summary data\n// Usage: node parse-results.js \u003cpath-to-results.json>\n\nconst fs = require(\"fs\");\n\nif (process.argv.length \u003c 3) {\n console.error(\"Usage: node parse-results.js \u003cresults-file.json>\");\n process.exit(1);\n}\n\nconst filePath = process.argv[2];\nconst data = JSON.parse(fs.readFileSync(filePath, \"utf8\"));\nconst c = data.violationCounts;\nconst runDir = data.runDir || \"\";\n\n// Summary counts\nconst summary = {\n total: c.total, sev1: c.sev1, sev2: c.sev2, sev3: c.sev3, sev4: c.sev4, sev5: c.sev5,\n topViolations: [],\n topRules: [],\n topFiles: []\n};\n\n// Top 10 violations sorted by severity\nconst sorted = data.violations.slice().sort((a, b) => a.severity - b.severity || a.rule.localeCompare(b.rule));\nsorted.slice(0, 10).forEach(v => {\n const loc = v.locations && v.locations[0] || {};\n let file = loc.file || \"unknown\";\n if (runDir && file.startsWith(runDir)) file = file.substring(runDir.length + 1);\n file = file.split(\"/\").pop();\n summary.topViolations.push({ rule: v.rule, engine: v.engine, sev: v.severity, file: file, line: loc.startLine || 0 });\n});\n\n// Top 10 rules by frequency\nconst ruleCounts = {};\nconst ruleEngines = {};\ndata.violations.forEach(v => {\n ruleCounts[v.rule] = (ruleCounts[v.rule] || 0) + 1;\n if (!ruleEngines[v.rule]) ruleEngines[v.rule] = v.engine;\n});\nObject.entries(ruleCounts).sort((a, b) => b[1] - a[1]).slice(0, 10).forEach(([rule, count]) => {\n summary.topRules.push({ rule, engine: ruleEngines[rule], count });\n});\n\n// Top 5 files by violation count\nconst fileCounts = {};\ndata.violations.forEach(v => {\n const loc = v.locations && v.locations[0] || {};\n let file = loc.file || \"unknown\";\n if (runDir && file.startsWith(runDir)) file = file.substring(runDir.length + 1);\n fileCounts[file] = (fileCounts[file] || 0) + 1;\n});\nObject.entries(fileCounts).sort((a, b) => b[1] - a[1]).slice(0, 5).forEach(([file, count]) => {\n summary.topFiles.push({ file, count });\n});\n\nconsole.log(JSON.stringify(summary));\n","content_type":"text/javascript; charset=utf-8","language":"javascript","size":2123,"content_sha256":"6f62d17170bace8af3ffaa0cee19458d9a173998e148529f3ad08d9d790e0154"},{"filename":"scripts/query-results.js","content":"#!/usr/bin/env node\n// Version: v1.0 | SHA256: placeholder\n// Query and filter Code Analyzer results JSON with rich filtering capabilities\n// Usage: node query-results.js \u003cresults-file.json> [options]\n//\n// Options:\n// --engine \u003cname> Filter by engine (pmd, eslint, cpd, retire-js, etc.)\n// --severity \u003cn> Filter by severity (1-5, comma-separated for multiple)\n// --category \u003ctag> Filter by category/tag (Security, Performance, etc.)\n// --rule \u003cname> Filter by exact rule name (case-insensitive)\n// --file \u003csubstring> Filter by file path substring\n// --top \u003cn> Return top N results (default: 10)\n// --sort \u003cfield> Sort by: severity, rule, engine, file (default: severity)\n// --sort-dir \u003cdir> Sort direction: asc, desc (default: asc)\n// --summary Show only summary counts (no individual violations)\n\nconst fs = require(\"fs\");\nconst path = require(\"path\");\n\nfunction printUsage() {\n console.error(`Usage: node query-results.js \u003cresults-file.json> [options]\n\nOptions:\n --engine \u003cname> Filter by engine (pmd, eslint, cpd, retire-js, etc.)\n --severity \u003cn> Filter by severity (1-5, comma-separated for multiple: 1,2)\n --category \u003ctag> Filter by category/tag (Security, Performance, BestPractices, etc.)\n --rule \u003cname> Filter by exact rule name (case-insensitive)\n --file \u003csubstring> Filter by file path substring (case-insensitive)\n --top \u003cn> Return top N results (default: 10)\n --sort \u003cfield> Sort by: severity, rule, engine, file (default: severity)\n --sort-dir \u003cdir> Sort direction: asc, desc (default: asc)\n --summary Show only summary counts (no individual violations)\n\nExamples:\n node query-results.js results.json --engine pmd --severity 1,2\n node query-results.js results.json --category Security --top 20\n node query-results.js results.json --file AccountService.cls\n node query-results.js results.json --rule ApexCRUDViolation\n node query-results.js results.json --summary`);\n process.exit(1);\n}\n\n// Parse CLI arguments\nconst args = process.argv.slice(2);\nif (args.length \u003c 1 || args[0] === \"--help\" || args[0] === \"-h\") {\n printUsage();\n}\n\nconst filePath = args[0];\nconst options = {\n engine: null,\n severity: null,\n category: null,\n rule: null,\n file: null,\n top: 10,\n sort: \"severity\",\n sortDir: \"asc\",\n summary: false,\n};\n\n// Parse named options\nfor (let i = 1; i \u003c args.length; i++) {\n const arg = args[i];\n switch (arg) {\n case \"--engine\":\n options.engine = (args[++i] || \"\").toLowerCase();\n break;\n case \"--severity\":\n options.severity = (args[++i] || \"\")\n .split(\",\")\n .map((s) => parseInt(s.trim(), 10))\n .filter((n) => n >= 1 && n \u003c= 5);\n break;\n case \"--category\":\n options.category = (args[++i] || \"\").toLowerCase();\n break;\n case \"--rule\":\n options.rule = (args[++i] || \"\").toLowerCase();\n break;\n case \"--file\":\n options.file = (args[++i] || \"\").toLowerCase();\n break;\n case \"--top\":\n options.top = parseInt(args[++i] || \"10\", 10);\n break;\n case \"--sort\":\n options.sort = args[++i] || \"severity\";\n break;\n case \"--sort-dir\":\n options.sortDir = args[++i] || \"asc\";\n break;\n case \"--summary\":\n options.summary = true;\n break;\n default:\n console.error(`Unknown option: ${arg}`);\n printUsage();\n }\n}\n\n// Read and parse results file\nlet data;\ntry {\n data = JSON.parse(fs.readFileSync(filePath, \"utf8\"));\n} catch (err) {\n console.error(`Error reading results file: ${err.message}`);\n process.exit(1);\n}\n\nconst runDir = data.runDir || \"\";\nconst violations = data.violations || [];\n\n// Apply filters\nlet filtered = violations.filter((v) => {\n if (options.engine && v.engine.toLowerCase() !== options.engine) return false;\n if (options.severity && !options.severity.includes(v.severity)) return false;\n if (options.category) {\n const tags = (v.tags || []).map((t) => t.toLowerCase());\n if (!tags.includes(options.category)) return false;\n }\n if (options.rule && v.rule.toLowerCase() !== options.rule) return false;\n if (options.file) {\n const loc = v.locations && v.locations[v.primaryLocationIndex || 0];\n const fileLower = ((loc && loc.file) || \"\").toLowerCase();\n if (!fileLower.includes(options.file)) return false;\n }\n return true;\n});\n\n// Sort\nconst sortMul = options.sortDir === \"desc\" ? -1 : 1;\nfiltered.sort((a, b) => {\n let cmp = 0;\n switch (options.sort) {\n case \"severity\":\n cmp = a.severity - b.severity;\n break;\n case \"rule\":\n cmp = a.rule.localeCompare(b.rule);\n break;\n case \"engine\":\n cmp = a.engine.localeCompare(b.engine);\n break;\n case \"file\": {\n const aLoc = a.locations && a.locations[a.primaryLocationIndex || 0];\n const bLoc = b.locations && b.locations[b.primaryLocationIndex || 0];\n const aFile = (aLoc && aLoc.file) || \"\";\n const bFile = (bLoc && bLoc.file) || \"\";\n cmp = aFile.localeCompare(bFile);\n break;\n }\n }\n if (cmp !== 0) return cmp * sortMul;\n // Secondary sort: severity ascending\n return (a.severity - b.severity) * sortMul;\n});\n\n// Build output\nconst totalMatches = filtered.length;\nconst limited = filtered.slice(0, options.top);\n\n// Compute severity breakdown of matches\nconst sevCounts = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 };\nfiltered.forEach((v) => {\n if (sevCounts[v.severity] !== undefined) sevCounts[v.severity]++;\n});\n\n// Compute rule frequency for matches\nconst ruleCounts = {};\nconst ruleEngines = {};\nfiltered.forEach((v) => {\n ruleCounts[v.rule] = (ruleCounts[v.rule] || 0) + 1;\n if (!ruleEngines[v.rule]) ruleEngines[v.rule] = v.engine;\n});\nconst topRules = Object.entries(ruleCounts)\n .sort((a, b) => b[1] - a[1])\n .slice(0, options.top)\n .map(([rule, count]) => ({ rule, engine: ruleEngines[rule], count }));\n\n// Compute file frequency for matches\nconst fileCounts = {};\nfiltered.forEach((v) => {\n const loc = v.locations && v.locations[v.primaryLocationIndex || 0];\n let file = (loc && loc.file) || \"unknown\";\n if (runDir && file.startsWith(runDir)) file = file.substring(runDir.length + 1);\n fileCounts[file] = (fileCounts[file] || 0) + 1;\n});\nconst topFiles = Object.entries(fileCounts)\n .sort((a, b) => b[1] - a[1])\n .slice(0, options.top)\n .map(([file, count]) => ({ file, count }));\n\n// Build result object\nconst result = {\n query: {\n engine: options.engine,\n severity: options.severity,\n category: options.category,\n rule: options.rule,\n file: options.file,\n top: options.top,\n sort: options.sort,\n sortDir: options.sortDir,\n },\n totalViolations: violations.length,\n totalMatches,\n severityCounts: sevCounts,\n topRules,\n topFiles,\n};\n\nif (!options.summary) {\n result.violations = limited.map((v) => {\n const loc = v.locations && v.locations[v.primaryLocationIndex || 0];\n let file = (loc && loc.file) || \"unknown\";\n if (runDir && file.startsWith(runDir)) file = file.substring(runDir.length + 1);\n return {\n rule: v.rule,\n engine: v.engine,\n severity: v.severity,\n message: v.message,\n file: file,\n startLine: (loc && loc.startLine) || 0,\n tags: v.tags || [],\n };\n });\n}\n\nconsole.log(JSON.stringify(result));\n","content_type":"text/javascript; charset=utf-8","language":"javascript","size":7324,"content_sha256":"46de23caa3e9755f5eaa241e8962ae22a28afaf535007aab1e3abf0ab09f0d41"},{"filename":"scripts/summarize-fixes.js","content":"#!/usr/bin/env node\n// Summarize applied fixes by severity and rule\n// Usage: node summarize-fixes.js \u003cpath-to-results.json>\n\nconst fs = require(\"fs\");\n\nif (process.argv.length \u003c 3) {\n console.error(\"Usage: node summarize-fixes.js \u003cresults-file.json>\");\n process.exit(1);\n}\n\nconst filePath = process.argv[2];\nconst data = JSON.parse(fs.readFileSync(filePath, \"utf8\"));\n\nconst fixesByRule = {};\nconst fixesBySeverity = { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 };\n\ndata.violations.forEach(v => {\n if (v.fixes && v.fixes.length > 0) {\n const fixCount = v.fixes.length;\n fixesByRule[v.rule] = (fixesByRule[v.rule] || 0) + fixCount;\n fixesBySeverity[v.severity] += fixCount;\n }\n});\n\nconst topRules = Object.entries(fixesByRule)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 10)\n .map(([rule, count]) => ({ rule, count }));\n\nconsole.log(JSON.stringify({ fixesByRule: topRules, fixesBySeverity }));\nconsole.error(\"SUCCESS: Summary script completed. Present results to user and offer re-scan (Step 6.7).\");\n","content_type":"text/javascript; charset=utf-8","language":"javascript","size":1000,"content_sha256":"080c9fcd20eb7862384eddd54091690814376980da74821f7f3ec9e397be7a9a"},{"filename":"scripts/verify-execution.sh","content":"#!/bin/bash\n# Verification script to ensure scripts are executed from files, not inline\n# Usage: source this at the start of SKILL.md execution\n\nSKILL_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\n\nverify_script_execution() {\n local script_name=\"$1\"\n local expected_path=\"${SKILL_DIR}/scripts/${script_name}\"\n\n if [[ ! -f \"$expected_path\" ]]; then\n echo \"❌ ERROR: Script file not found: $expected_path\"\n echo \"This skill requires script files to be present in the deployment.\"\n return 1\n fi\n\n # Check if script has expected header\n if ! head -1 \"$expected_path\" | grep -q \"#!/usr/bin/env node\"; then\n echo \"⚠️ WARNING: Script missing proper header: $expected_path\"\n fi\n\n echo \"✓ Script file verified: $script_name\"\n return 0\n}\n\n# Export function for use in skill execution\nexport -f verify_script_execution\nexport SKILL_DIR\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":863,"content_sha256":"601004b07fd57d6c33fadd8e52e7dae209a657f1bf494d0fc8ed947d9611b87a"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Running Code Analyzer Skill","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"CRITICAL: Mandatory Script Usage","type":"text"}]},{"type":"paragraph","content":[{"text":"Every interaction with Code Analyzer results MUST go through the bundled scripts in ","type":"text"},{"text":"\u003cskill_dir>/scripts/","type":"text","marks":[{"type":"code_inline"}]},{"text":". No exceptions.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"WRONG — never do this:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# WRONG: inline Python to parse results\npython3 -c \"import json; data = json.load(open('results.json'))...\"\n\n# WRONG: inline Node.js to parse results\nnode -e \"const data = require('./results.json')...\"\n\n# WRONG: jq to filter results\ncat results.json | jq '.violations[] | select(.engine==\"pmd\")'\n\n# WRONG: reading the results file directly (it can be 10MB+)\nRead tool → code-analyzer-results-*.json","type":"text"}]},{"type":"paragraph","content":[{"text":"Also forbidden: ","type":"text"},{"text":"run_code_analyzer","type":"text","marks":[{"type":"code_inline"}]},{"text":" and any ","type":"text"},{"text":"mcp__*","type":"text","marks":[{"type":"code_inline"}]},{"text":" tool — Bash only.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"RIGHT — always do this:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Summarize scan results\nnode \"\u003cskill_dir>/scripts/parse-results.js\" \"./code-analyzer-results-TIMESTAMP.json\"\n\n# Filter/rank/query results (by engine, severity, file, rule, category)\nnode \"\u003cskill_dir>/scripts/query-results.js\" \"./code-analyzer-results-TIMESTAMP.json\" --engine pmd --summary\n\n# List/browse available rules (by engine, category, language, severity)\nnode \"\u003cskill_dir>/scripts/list-rules.js\" \"Security\" --top 10\n\n# Look up what a rule means\nnode \"\u003cskill_dir>/scripts/describe-rule.js\" \"ApexCRUDViolation\" --engine pmd\n\n# Discover fixable violations\nnode \"\u003cskill_dir>/scripts/discover-fixes.js\" \"./code-analyzer-results-TIMESTAMP.json\"\n\n# Apply fixes (after user confirms)\nnode \"\u003cskill_dir>/scripts/apply-fixes.js\" \"./code-analyzer-results-TIMESTAMP.json\"\n\n# Summarize applied fixes\nnode \"\u003cskill_dir>/scripts/summarize-fixes.js\" \"./code-analyzer-results-TIMESTAMP.json\"\n\n# Filter vendor files (jQuery, Bootstrap, *.min.js) before applying fixes\nnode \"\u003cskill_dir>/scripts/filter-violations.js\" \"./code-analyzer-results-TIMESTAMP.json\" \"./code-analyzer-results-TIMESTAMP-filtered.json\" --report","type":"text"}]},{"type":"paragraph","content":[{"text":"\u003cskill_dir>","type":"text","marks":[{"type":"code_inline"}]},{"text":" is the absolute path to the directory containing this SKILL.md. ","type":"text"},{"text":"Never","type":"text","marks":[{"type":"strong"}]},{"text":" use ","type":"text"},{"text":"./scripts/","type":"text","marks":[{"type":"code_inline"}]},{"text":" — that resolves against the user's CWD, not the skill dir.","type":"text"}]},{"type":"paragraph","content":[{"text":"Any aggregation, filter, or rank question (\"which file has the most violations?\", \"how many PMD issues?\", \"top rules by count\", \"break down by severity\") is answered by ","type":"text"},{"text":"query-results.js","type":"text","marks":[{"type":"code_inline"}]},{"text":" — its output already includes ","type":"text"},{"text":"topRules","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"topFiles","type":"text","marks":[{"type":"code_inline"}]},{"text":", and ","type":"text"},{"text":"severityCounts","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Overview","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Ecosystem:","type":"text","marks":[{"type":"strong"}]},{"text":" This skill is part of a 3-skill Code Analyzer suite — ","type":"text"},{"text":"dx-code-analyzer-run","type":"text","marks":[{"type":"code_inline"}]},{"text":" (scans & results) · ","type":"text"},{"text":"dx-code-analyzer-configure","type":"text","marks":[{"type":"code_inline"}]},{"text":" (setup, config, CI/CD) · ","type":"text"},{"text":"dx-code-analyzer-custom-rule-create","type":"text","marks":[{"type":"code_inline"}]},{"text":" (custom rule authoring).","type":"text"}]}]},{"type":"paragraph","content":[{"text":"This skill translates natural-language requests (\"scan for security issues\", \"check my changes\") into the correct ","type":"text"},{"text":"sf code-analyzer run","type":"text","marks":[{"type":"code_inline"}]},{"text":" 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.","type":"text"}]},{"type":"paragraph","content":[{"text":"In scope:","type":"text","marks":[{"type":"strong"}]},{"text":" 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.","type":"text"}]},{"type":"paragraph","content":[{"text":"Out of scope:","type":"text","marks":[{"type":"strong"}]},{"text":" installing/configuring ","type":"text"},{"text":"sf","type":"text","marks":[{"type":"code_inline"}]},{"text":" or the plugin (→ ","type":"text"},{"text":"dx-code-analyzer-configure","type":"text","marks":[{"type":"code_inline"}]},{"text":"), writing custom rules/engines (→ ","type":"text"},{"text":"dx-code-analyzer-custom-rule-create","type":"text","marks":[{"type":"code_inline"}]},{"text":"), AI-generated fixes beyond engine-provided ones, deep refactoring, CI/CD setup (→ ","type":"text"},{"text":"dx-code-analyzer-configure","type":"text","marks":[{"type":"code_inline"}]},{"text":").","type":"text"}]},{"type":"paragraph","content":[{"text":"Allowed tools:","type":"text","marks":[{"type":"strong"}]},{"text":" Bash (","type":"text"},{"text":"sf code-analyzer","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"node","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"git diff","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"date","type":"text","marks":[{"type":"code_inline"}]},{"text":"), Read, Write, Edit. ","type":"text"},{"text":"Forbidden:","type":"text","marks":[{"type":"strong"}]},{"text":" any MCP tool, Agent tool, web tools, other skills, Python, ","type":"text"},{"text":"jq","type":"text","marks":[{"type":"code_inline"}]},{"text":", inline scripts/heredocs. This skill owns the complete scan-fix-verify-query-explain workflow end-to-end.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Command Syntax Rules (READ FIRST — ABSOLUTE)","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"The command is ","type":"text"},{"text":"sf code-analyzer run","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" — NOT ","type":"text"},{"text":"sf scanner run","type":"text","marks":[{"type":"code_inline"}]},{"text":" (deprecated v3).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No ","type":"text","marks":[{"type":"strong"}]},{"text":"--format","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" flag.","type":"text","marks":[{"type":"strong"}]},{"text":" Use ","type":"text"},{"text":"--output-file \u003cpath>.\u003cext>","type":"text","marks":[{"type":"code_inline"}]},{"text":"; the extension determines the format.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always","type":"text","marks":[{"type":"strong"}]},{"text":" pass ","type":"text"},{"text":"--output-file","type":"text","marks":[{"type":"code_inline"}]},{"text":" with a timestamped name (e.g., ","type":"text"},{"text":"./code-analyzer-results-20260512-143022.json","type":"text","marks":[{"type":"code_inline"}]},{"text":") — do not rely on stdout.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Foreground only","type":"text","marks":[{"type":"strong"}]},{"text":" (no ","type":"text"},{"text":"run_in_background","type":"text","marks":[{"type":"code_inline"}]},{"text":"); timeout 1200000ms for large scans.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Invalid v3 flags","type":"text","marks":[{"type":"strong"}]},{"text":" that cause errors: ","type":"text"},{"text":"--format","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"--engine","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"--category","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"--json","type":"text","marks":[{"type":"code_inline"}]},{"text":". Use ","type":"text"},{"text":"--rule-selector","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"--output-file","type":"text","marks":[{"type":"code_inline"}]},{"text":" instead.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Tool restriction:","type":"text","marks":[{"type":"strong"}]},{"text":" Bash, Read, Write, Edit only. No MCP tools, no Agent tool, no web tools, no other skills.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Why: the v4+ CLI redesigned the flag interface; v3 flags now error.","type":"text"}]},{"type":"paragraph","content":[{"text":"Full flag/selector docs: ","type":"text"},{"text":"\u003cskill_dir>/references/flag-reference.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Prerequisites","type":"text"}]},{"type":"paragraph","content":[{"text":"User needs: ","type":"text"},{"text":"Salesforce CLI","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"sf","type":"text","marks":[{"type":"code_inline"}]},{"text":"), ","type":"text"},{"text":"@salesforce/plugin-code-analyzer","type":"text","marks":[{"type":"strong"}]},{"text":" (v5.x+), ","type":"text"},{"text":"Java 11+","type":"text","marks":[{"type":"strong"}]},{"text":" (PMD/CPD/SFGE), ","type":"text"},{"text":"Node.js 18+","type":"text","marks":[{"type":"strong"}]},{"text":" (ESLint/RetireJS), ","type":"text"},{"text":"Python 3","type":"text","marks":[{"type":"strong"}]},{"text":" (Flow), ","type":"text"},{"text":"authenticated org","type":"text","marks":[{"type":"strong"}]},{"text":" (ApexGuru).","type":"text"}]},{"type":"paragraph","content":[{"text":"Pre-flight: run ","type":"text"},{"text":"sf code-analyzer --help 2>&1 | head -1","type":"text","marks":[{"type":"code_inline"}]},{"text":". If that fails, or if a scan reports an engine startup error (e.g., \"PMD failed to start\", \"java: command not found\", \"SFGE failed\"):","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Stop","type":"text","marks":[{"type":"strong"}]},{"text":" — do not attempt to install/diagnose prerequisites yourself.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Delegate to ","type":"text","marks":[{"type":"strong"}]},{"text":"dx-code-analyzer-configure","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" — it handles all setup.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"After it finishes, return here and re-run the scan.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"If a scan fails for other reasons, see ","type":"text"},{"text":"\u003cskill_dir>/references/error-handling.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Start: Common Patterns","type":"text"}]},{"type":"paragraph","content":[{"text":"Match the request below; if it matches, jump to Step 3 (Build Command). Otherwise, walk Step 1.","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"User Says","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Rule Selector","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Notes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"scan my code\" / \"run code analyzer\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Recommended","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Curated set, all file types","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"check for security issues\" / \"security review\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"all:Security:(1,2)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"All engines, Critical+High","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"scan my changes\" / \"check the diff\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"(see Step 1.5)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Get files via ","type":"text"},{"text":"git diff","type":"text","marks":[{"type":"code_inline"}]},{"text":", filter to scannable types, pass via ","type":"text"},{"text":"--target","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"run PMD\" / \"check my Apex\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pmd","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Apex classes and triggers","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"lint my LWC\" / \"check my JavaScript\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"eslint","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"JavaScript/TypeScript/LWC","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"find duplicates\" / \"check for copy-paste\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"cpd","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Code clones","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"check for vulnerabilities\" / \"scan libraries\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"retire-js","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"JavaScript library CVEs","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"deep analysis\" / \"data flow analysis\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"sfge","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Java 11+, 10–20 min, use ","type":"text"},{"text":"--workspace \"force-app\"","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"performance analysis\" / \"governor limits\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"apexguru","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Authenticated org required","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"analyze my Flows\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"flow","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--target **/*.flow-meta.xml","type":"text","marks":[{"type":"code_inline"}]},{"text":", Python 3","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"AppExchange security review\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"all:Security:(1,2)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"See ","type":"text"},{"text":"\u003cskill_dir>/references/special-behaviors.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" → AppExchange","type":"text"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Step 1: Parse the User's Intent","type":"text"}]},{"type":"paragraph","content":[{"text":"Analyze the request along these 7 dimensions; any can combine.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1.1 ENGINE","type":"text"}]},{"type":"paragraph","content":[{"text":"PMD/Apex → ","type":"text"},{"text":"pmd","type":"text","marks":[{"type":"code_inline"}]},{"text":" · ESLint/JS/TS/lint → ","type":"text"},{"text":"eslint","type":"text","marks":[{"type":"code_inline"}]},{"text":" · Flows → ","type":"text"},{"text":"flow","type":"text","marks":[{"type":"code_inline"}]},{"text":" · duplicates/CPD → ","type":"text"},{"text":"cpd","type":"text","marks":[{"type":"code_inline"}]},{"text":" · vulnerabilities/CVE/RetireJS → ","type":"text"},{"text":"retire-js","type":"text","marks":[{"type":"code_inline"}]},{"text":" · SFGE/data flow → ","type":"text"},{"text":"sfge","type":"text","marks":[{"type":"code_inline"}]},{"text":" · performance/ApexGuru → ","type":"text"},{"text":"apexguru","type":"text","marks":[{"type":"code_inline"}]},{"text":" · regex → ","type":"text"},{"text":"regex","type":"text","marks":[{"type":"code_inline"}]},{"text":" · everything → ","type":"text"},{"text":"all","type":"text","marks":[{"type":"code_inline"}]},{"text":" · unspecified → ","type":"text"},{"text":"Recommended","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1.2 CATEGORY","type":"text"}]},{"type":"paragraph","content":[{"text":"security/OWASP → ","type":"text"},{"text":"Security","type":"text","marks":[{"type":"code_inline"}]},{"text":" · performance → ","type":"text"},{"text":"Performance","type":"text","marks":[{"type":"code_inline"}]},{"text":" · best practices → ","type":"text"},{"text":"BestPractices","type":"text","marks":[{"type":"code_inline"}]},{"text":" · style/format → ","type":"text"},{"text":"CodeStyle","type":"text","marks":[{"type":"code_inline"}]},{"text":" · design/complexity → ","type":"text"},{"text":"Design","type":"text","marks":[{"type":"code_inline"}]},{"text":" · bugs → ","type":"text"},{"text":"ErrorProne","type":"text","marks":[{"type":"code_inline"}]},{"text":" · docs → ","type":"text"},{"text":"Documentation","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1.3 SEVERITY","type":"text"}]},{"type":"paragraph","content":[{"text":"1=Critical · 2=High · 3=Moderate · 4=Low · 5=Info. \"critical only\" → ","type":"text"},{"text":"1","type":"text","marks":[{"type":"code_inline"}]},{"text":" · \"critical+high\" → ","type":"text"},{"text":"(1,2)","type":"text","marks":[{"type":"code_inline"}]},{"text":" · \"moderate and above\" → ","type":"text"},{"text":"(1,2,3)","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1.4 SPECIFIC RULE","type":"text"}]},{"type":"paragraph","content":[{"text":"If the user names a rule (e.g., \"ApexCRUDViolation\", \"no-unused-vars\"): ","type":"text"},{"text":"--rule-selector \u003cengine>:\u003cruleName>","type":"text","marks":[{"type":"code_inline"}]},{"text":", or just ","type":"text"},{"text":"\u003cruleName>","type":"text","marks":[{"type":"code_inline"}]},{"text":" if engine is ambiguous.","type":"text"}]},{"type":"paragraph","content":[{"text":"Partial names:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"--rule-selector","type":"text","marks":[{"type":"code_inline"}]},{"text":" requires the ","type":"text"},{"text":"exact full","type":"text","marks":[{"type":"strong"}]},{"text":" rule name (e.g., ","type":"text"},{"text":"@salesforce-ux/slds/no-hardcoded-values-slds2","type":"text","marks":[{"type":"code_inline"}]},{"text":", not ","type":"text"},{"text":"no-hardcoded-values","type":"text","marks":[{"type":"code_inline"}]},{"text":"). No wildcards. If you are not 100% certain, look it up first — ","type":"text"},{"text":"do not guess","type":"text","marks":[{"type":"strong"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"sf code-analyzer rules --rule-selector all 2>&1 | grep -i \"USER_KEYWORD\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Multiple matches → ask the user which. Zero matches → tell the user nothing matched.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1.5 TARGET","type":"text"}]},{"type":"paragraph","content":[{"text":"specific path → ","type":"text"},{"text":"--target \u003cpath>","type":"text","marks":[{"type":"code_inline"}]},{"text":" · glob (\"all Apex\") → ","type":"text"},{"text":"--target **/*.cls,**/*.trigger","type":"text","marks":[{"type":"code_inline"}]},{"text":" · \"my changes\"/\"diff\" → ","type":"text"},{"text":"git diff --name-only [base]...HEAD","type":"text","marks":[{"type":"code_inline"}]},{"text":", filter to scannable types, pass as ","type":"text"},{"text":"--target","type":"text","marks":[{"type":"code_inline"}]},{"text":" · \"LWC\" → ","type":"text"},{"text":"--target **/lwc/**","type":"text","marks":[{"type":"code_inline"}]},{"text":" · \"Flows\" → ","type":"text"},{"text":"--target **/*.flow-meta.xml","type":"text","marks":[{"type":"code_inline"}]},{"text":" · unspecified → omit (entire workspace).","type":"text"}]},{"type":"paragraph","content":[{"text":"Diff-filtering details: ","type":"text"},{"text":"\u003cskill_dir>/references/special-behaviors.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1.6 OUTPUT","type":"text"}]},{"type":"paragraph","content":[{"text":"Default JSON.","type":"text","marks":[{"type":"strong"}]},{"text":" Only change if the user explicitly asks. Name: ","type":"text"},{"text":"./code-analyzer-results-\u003cYYYYMMDD-HHmmss>.\u003cext>","type":"text","marks":[{"type":"code_inline"}]},{"text":" via ","type":"text"},{"text":"TIMESTAMP=$(date +%Y%m%d-%H%M%S)","type":"text","marks":[{"type":"code_inline"}]},{"text":". Formats: ","type":"text"},{"text":".json","type":"text","marks":[{"type":"code_inline"}]},{"text":" (default), ","type":"text"},{"text":".html","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":".sarif","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":".csv","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":".xml","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1.7 COMPARISON / DELTA","type":"text"}]},{"type":"paragraph","content":[{"text":"\"new since main\" → ","type":"text"},{"text":"git diff --name-only main...HEAD","type":"text","marks":[{"type":"code_inline"}]},{"text":" → scan those · \"since last commit\" → ","type":"text"},{"text":"HEAD~1","type":"text","marks":[{"type":"code_inline"}]},{"text":" · \"vs develop\" → ","type":"text"},{"text":"develop...HEAD","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Step 2: Build the Rule Selector","type":"text"}]},{"type":"paragraph","content":[{"text":"Syntax: ","type":"text"},{"text":":","type":"text","marks":[{"type":"code_inline"}]},{"text":" = AND, ","type":"text"},{"text":",","type":"text","marks":[{"type":"code_inline"}]},{"text":" = OR, ","type":"text"},{"text":"()","type":"text","marks":[{"type":"code_inline"}]},{"text":" = grouping.","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Engine only: ","type":"text"},{"text":"pmd","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Engine + category: ","type":"text"},{"text":"pmd:Security","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Engine + severity: ","type":"text"},{"text":"pmd:2","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Complex: ","type":"text"},{"text":"(pmd,eslint):Security:(1,2)","type":"text","marks":[{"type":"code_inline"}]},{"text":" = (PMD or ESLint) AND Security AND sev (1 or 2)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Specific rule: ","type":"text"},{"text":"pmd:ApexCRUDViolation","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"All: ","type":"text"},{"text":"all","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"More: ","type":"text"},{"text":"\u003cskill_dir>/references/command-examples.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Step 3: Build the Full Command","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"TIMESTAMP=$(date +%Y%m%d-%H%M%S)\nsf code-analyzer run \\\n --rule-selector \u003cselector> \\\n --target \u003ctargets> \\ # optional\n --output-file \"./code-analyzer-results-${TIMESTAMP}.json\" \\ # default JSON\n --include-fixes \\ # always\n --workspace \u003cpath> # optional","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Default to timestamped JSON; only change format on explicit request.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always pass ","type":"text"},{"text":"--include-fixes","type":"text","marks":[{"type":"code_inline"}]},{"text":" (enables Step 6 auto-fix).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Omit ","type":"text"},{"text":"--target","type":"text","marks":[{"type":"code_inline"}]},{"text":" to scan the whole workspace.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Diff scans: ","type":"text"},{"text":"git diff --name-only","type":"text","marks":[{"type":"code_inline"}]},{"text":" → filter scannable types → pass as ","type":"text"},{"text":"--target","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Special cases (SFGE/ApexGuru/AppExchange/diff): ","type":"text"},{"text":"\u003cskill_dir>/references/special-behaviors.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Step 4: Execute the Scan","type":"text"}]},{"type":"paragraph","content":[{"text":"Use the ","type":"text"},{"text":"Bash tool only","type":"text","marks":[{"type":"strong"}]},{"text":" — never the ","type":"text"},{"text":"run_code_analyzer","type":"text","marks":[{"type":"code_inline"}]},{"text":" MCP tool.","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Generate the timestamp via Bash: ","type":"text"},{"text":"date +%Y%m%d-%H%M%S","type":"text","marks":[{"type":"code_inline"}]},{"text":" → e.g. ","type":"text"},{"text":"20260512-143022","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Tell the user:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"text"},"content":[{"text":"Starting scan...\nResults: ./code-analyzer-results-20260512-143022.json\nLog: ./code-analyzer-results-20260512-143022.log\nMay take several minutes for large codebases.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run with the ","type":"text"},{"text":"literal","type":"text","marks":[{"type":"strong"}]},{"text":" timestamp baked in (not ","type":"text"},{"text":"$TIMESTAMP","type":"text","marks":[{"type":"code_inline"}]},{"text":"), foreground, timeout 1200000ms, ","type":"text"},{"text":"tee","type":"text","marks":[{"type":"code_inline"}]},{"text":" to a ","type":"text"},{"text":".log","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"sf code-analyzer run --rule-selector Recommended \\\n --output-file \"./code-analyzer-results-20260512-143022.json\" \\\n --include-fixes 2>&1 | tee \"./code-analyzer-results-20260512-143022.log\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Exit 0 = success. On error, read both the log file and ","type":"text"},{"text":"\u003cskill_dir>/references/error-handling.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Immediately","type":"text","marks":[{"type":"strong"}]},{"text":" parse results (Step 5) — do not ask the user what to do next.","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Step 5: Parse and Present Results","type":"text"}]},{"type":"paragraph","content":[{"text":"Run the parse script straight after the scan — do not pause to ask:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"node \"\u003cskill_dir>/scripts/parse-results.js\" \"./code-analyzer-results-TIMESTAMP.json\"","type":"text"}]},{"type":"paragraph","content":[{"text":"DO NOT:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Invent or generate script code yourself","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use bare relative paths like ","type":"text"},{"text":"node scripts/parse-results.js","type":"text","marks":[{"type":"code_inline"}]},{"text":" (won't resolve from user's CWD)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use heredocs or inline script content","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"jq","type":"text","marks":[{"type":"code_inline"}]},{"text":" as a substitute for the parse script (shell quoting will break)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Read the JSON file directly","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Presentation template","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"text"},"content":[{"text":"## Scan Complete\n\n**Found X violations** across Y files.\n\n| Severity | Count |\n|----------|-------|\n| Critical (1) | X |\n| High (2) | X |\n| Moderate (3) | X |\n| Low (4) | X |\n| Info (5) | X |\n\n### Top Issues\n| # | Rule | Engine | Sev | File | Line |\n|---|------|--------|-----|------|------|\n| 1 | ApexCRUDViolation | pmd | 2 | AccountService.cls | 42 |\n| ... up to 10 most critical |\n\n### Top Rules by Frequency\n| Rule | Engine | Count |\n|------|--------|-------|\n| no-var | eslint | 170 |\n| ... |\n\nFull results: `./code-analyzer-results-20260512-143022.json`","type":"text"}]},{"type":"paragraph","content":[{"text":"Scale to result size: ","type":"text"},{"text":"0","type":"text","marks":[{"type":"strong"}]},{"text":" → \"no violations found\"; ","type":"text"},{"text":"1–10","type":"text","marks":[{"type":"strong"}]},{"text":" → all in one table; ","type":"text"},{"text":"11–50","type":"text","marks":[{"type":"strong"}]},{"text":" → severity counts + top 10; ","type":"text"},{"text":"50–5000","type":"text","marks":[{"type":"strong"}]},{"text":" → counts + top 10 violations + top 10 rules + top 5 files; ","type":"text"},{"text":"5000+","type":"text","marks":[{"type":"strong"}]},{"text":" → same, plus suggest narrowing scope (severity/category/folder). Always end with the output path and offer next actions: filter / explain rule / apply fixes.","type":"text"}]},{"type":"paragraph","content":[{"text":"Large-result handling: ","type":"text"},{"text":"\u003cskill_dir>/references/special-behaviors.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Step 6: Apply Engine-Provided Fixes (Post-Scan)","type":"text"}]},{"type":"paragraph","content":[{"text":"Engine-provided fixes are ","type":"text"},{"text":"deterministic","type":"text","marks":[{"type":"strong"}]},{"text":" (not AI-generated). Flow: vendor filter (if needed) → discover → present → ","type":"text"},{"text":"wait for user confirmation","type":"text","marks":[{"type":"strong"}]},{"text":" → apply → summarize.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"6.1 Vendor file filter (when needed)","type":"text"}]},{"type":"paragraph","content":[{"text":"Run if the user said \"fix my code\" / \"project source\", or if top-violation files are vendor libs (jQuery, Bootstrap, ","type":"text"},{"text":"*.min.js","type":"text","marks":[{"type":"code_inline"}]},{"text":"):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"node \"\u003cskill_dir>/scripts/filter-violations.js\" \\\n \"./code-analyzer-results-TIMESTAMP.json\" \\\n \"./code-analyzer-results-TIMESTAMP-filtered.json\" \\\n --report","type":"text"}]},{"type":"paragraph","content":[{"text":"Report: \"Excluded X vendor files (Y violations) — jQuery, Bootstrap, etc. Applying fixes to Z project files only.\" Use the filtered file in 6.2+. Detection logic: ","type":"text"},{"text":"\u003cskill_dir>/references/vendor-file-handling.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"6.2 Discover","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"node \"\u003cskill_dir>/scripts/discover-fixes.js\" \"./code-analyzer-results-TIMESTAMP.json\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"6.3 Present + ASK (then STOP)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"text"},"content":[{"text":"### Engine-Provided Fixes Available\n**X of Y violations** have auto-fixes provided by the analysis engine:\n\n| Rule | Engine | Sev | Fixable Count |\n|------|--------|-----|---------------|\n| no-var | eslint | 3 | 170 |\n| ... |\n\nThese are safe, deterministic fixes generated by the engines (not AI-generated).\n\nWould you like me to apply these fixes? (yes / no / select specific rules)","type":"text"}]},{"type":"paragraph","content":[{"text":"Stop and wait for the user's reply, even if they originally said \"scan and fix everything\".","type":"text","marks":[{"type":"strong"}]},{"text":" Apply only on a fresh \"yes\" / \"apply\" / \"go ahead\" in the next turn.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"6.4 Apply","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"node \"\u003cskill_dir>/scripts/apply-fixes.js\" \"./code-analyzer-results-TIMESTAMP.json\"","type":"text"}]},{"type":"paragraph","content":[{"text":"(Filtered file if 6.1 created one.)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"6.5 Summarize (MANDATORY immediately after 6.4)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"node \"\u003cskill_dir>/scripts/summarize-fixes.js\" \"./code-analyzer-results-TIMESTAMP.json\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Then present:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"text"},"content":[{"text":"### Engine-Provided Fixes Applied Successfully\n**Applied X auto-fixes across Y files.**\n\n| Severity | Fixes Applied |\n|----------|---------------|\n| Critical (1) | X |\n| ... |\n\n| Rule | Fixes Applied |\n|------|---------------|\n| no-var | 169 |\n| ... |\n\nWant me to re-run the scan to verify the fixes resolved the violations?","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"6.6 — Handling the user's choice","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Decline / \"no\":","type":"text","marks":[{"type":"strong"}]},{"text":" skip apply, skip summarize. Do not re-scan.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Select rules\":","type":"text","marks":[{"type":"strong"}]},{"text":" filter the discovery list to those rules and pass the filtered file to ","type":"text"},{"text":"apply-fixes.js","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"All\" / \"yes\":","type":"text","marks":[{"type":"strong"}]},{"text":" run ","type":"text"},{"text":"apply-fixes.js","type":"text","marks":[{"type":"code_inline"}]},{"text":" against the full (or vendor-filtered) results file as-is.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"6.7 — Optional re-scan for verification","type":"text"}]},{"type":"paragraph","content":[{"text":"If the user accepts the offer in 6.5, re-run the same scan with a ","type":"text"},{"text":"new timestamp","type":"text","marks":[{"type":"strong"}]},{"text":" (do not overwrite the original). Compare violation counts before vs. after and show the delta — fixes that resolved cleanly will drop out; remaining violations either need manual remediation or are unrelated.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Step 7: Query and Filter Existing Results","type":"text"}]},{"type":"paragraph","content":[{"text":"After Step 5, the user may want to ","type":"text"},{"text":"drill into specific subsets","type":"text","marks":[{"type":"strong"}]},{"text":" without re-running the entire scan. This step handles all result-exploration requests.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"When to trigger","type":"text"}]},{"type":"paragraph","content":[{"text":"Activate when the user asks to slice, filter, rank, or explore existing results:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Show me just the security violations\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"What's in AccountService.cls?\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Show only PMD issues\" / \"Filter to critical and high\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"What ESLint rules fired?\" / \"Show violations in the lwc folder\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Top 20 most severe\" / \"Which file has the most violations?\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"What are the most common rules?\" / \"How many violations per engine?\" / \"Break it down by severity\"","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Important:","type":"text","marks":[{"type":"strong"}]},{"text":" Any question about existing scan results — filtering, ranking, counting, aggregating — MUST use ","type":"text"},{"text":"query-results.js","type":"text","marks":[{"type":"code_inline"}]},{"text":". NEVER write inline Python, ","type":"text"},{"text":"jq","type":"text","marks":[{"type":"code_inline"}]},{"text":", or ad-hoc scripts to parse the results JSON. The query script already provides ","type":"text"},{"text":"topRules","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"topFiles","type":"text","marks":[{"type":"code_inline"}]},{"text":", and ","type":"text"},{"text":"severityCounts","type":"text","marks":[{"type":"code_inline"}]},{"text":" in its output.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"How to execute","type":"text"}]},{"type":"paragraph","content":[{"text":"Run the query script against the ","type":"text"},{"text":"same results file","type":"text","marks":[{"type":"strong"}]},{"text":" from Step 4 (no re-scan needed):","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"node \"\u003cskill_dir>/scripts/query-results.js\" \"./code-analyzer-results-TIMESTAMP.json\" [options]","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"User says","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Options","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"security violations\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--category Security","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"PMD issues only\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--engine pmd","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"critical and high\" / \"sev 1-2\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--severity 1,2","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"in AccountService.cls\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--file AccountService.cls","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"the ApexCRUDViolation rule\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--rule ApexCRUDViolation","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"top 20\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--top 20","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"sort by file\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--sort file","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"just give me counts\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--summary","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"which file has the most violations?\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--sort file --summary","type":"text","marks":[{"type":"code_inline"}]},{"text":" (read ","type":"text"},{"text":"topFiles","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"which file has most PMD violations?\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--engine pmd --summary","type":"text","marks":[{"type":"code_inline"}]},{"text":" (read ","type":"text"},{"text":"topFiles","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"most common rules?\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--summary","type":"text","marks":[{"type":"code_inline"}]},{"text":" (read ","type":"text"},{"text":"topRules","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"how many per engine?\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"use Step 5's summary, or run with ","type":"text"},{"text":"--engine X --summary","type":"text","marks":[{"type":"code_inline"}]},{"text":" per engine","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Combinations","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--engine pmd --severity 1,2 --top 5","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"paragraph","content":[{"text":"Output format and presentation templates: ","type":"text"},{"text":"\u003cskill_dir>/references/post-scan-workflows.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Step 8: Describe a Rule","type":"text"}]},{"type":"paragraph","content":[{"text":"When the user asks \"what does this rule mean?\" or \"how do I fix this?\", use this step to look up and explain a specific rule.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"When to trigger","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"What is ApexCRUDViolation?\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Explain this rule\" / \"Why is this flagged?\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"What does no-var mean?\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"How do I fix OperationWithLimitsInLoop?\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Tell me about this violation\"","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"How to execute","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"node \"\u003cskill_dir>/scripts/describe-rule.js\" \"\u003crule-name>\" [--engine \u003cengine>]","type":"text"}]},{"type":"paragraph","content":[{"text":"Pass ","type":"text"},{"text":"--engine","type":"text","marks":[{"type":"code_inline"}]},{"text":" when known (from scan context); omit for a broader search. Returns one of ","type":"text"},{"text":"success","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"multiple_matches","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"not_found","type":"text","marks":[{"type":"code_inline"}]},{"text":". Status handling and templates: ","type":"text"},{"text":"\u003cskill_dir>/references/post-scan-workflows.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Step 9: List Available Rules","type":"text"}]},{"type":"paragraph","content":[{"text":"Triggers: \"what security rules are available?\", \"list all PMD rules\", \"rules for JavaScript\", \"Recommended rules\", \"how many ESLint rules?\", \"rules for Apex\".","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"node \"\u003cskill_dir>/scripts/list-rules.js\" \"\u003cselector>\" [options]","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"User says","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Selector","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Options","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"security rules\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Security","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph"}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"PMD rules\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pmd","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph"}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"ESLint security rules\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"eslint:Security","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph"}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"JavaScript rules\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"JavaScript","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph"}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"Apex rules\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Apex","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph"}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"Recommended rules\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Recommended","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph"}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"high severity rules\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"(1,2)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph"}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"just give me counts\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Recommended","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--count-only","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"top 10 security rules\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Security","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--top 10","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"paragraph","content":[{"text":"Filters: ","type":"text"},{"text":"--engine","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"--severity","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"--top","type":"text","marks":[{"type":"code_inline"}]},{"text":" (default 100), ","type":"text"},{"text":"--count-only","type":"text","marks":[{"type":"code_inline"}]},{"text":". The script pre-validates selector tokens (catches typos like ","type":"text"},{"text":"secruity","type":"text","marks":[{"type":"code_inline"}]},{"text":") before calling the CLI. Presentation: ","type":"text"},{"text":"\u003cskill_dir>/references/post-scan-workflows.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Cross-Skill Integration","type":"text"}]},{"type":"paragraph","content":[{"text":"This skill is part of a 3-skill Code Analyzer ecosystem. Hand off cleanly rather than attempting work that belongs to another skill.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"When THIS skill delegates to ","type":"text"},{"text":"dx-code-analyzer-configure","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pre-flight check fails (CLI missing, plugin not installed, engine prereqs broken) → stop, delegate, return here after fix","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"User asks to set up CI/CD, edit ","type":"text"},{"text":"code-analyzer.yml","type":"text","marks":[{"type":"code_inline"}]},{"text":", change severities, or disable engines → delegate entirely","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"When THIS skill delegates to ","type":"text"},{"text":"dx-code-analyzer-custom-rule-create","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"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.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"When other skills hand off HERE:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"dx-code-analyzer-configure","type":"text","marks":[{"type":"code_inline"}]},{"text":" completes setup → proceed with scan (Step 1–5)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"dx-code-analyzer-custom-rule-create","type":"text","marks":[{"type":"code_inline"}]},{"text":" finishes creating a rule → proceed with scan targeting the new rule (e.g., ","type":"text"},{"text":"--rule-selector pmd:\u003cRuleName>","type":"text","marks":[{"type":"code_inline"}]},{"text":") to verify it works","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Ownership boundary","type":"text"}]},{"type":"paragraph","content":[{"text":"This skill owns the complete ","type":"text"},{"text":"scan → explore → fix","type":"text","marks":[{"type":"strong"}]},{"text":" workflow end-to-end. It does NOT own installation, config file management, or rule authoring.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Constraints & Gotchas","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Item","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Why / Fix","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use timestamped JSON + ","type":"text"},{"text":".log","type":"text","marks":[{"type":"code_inline"}]},{"text":" via ","type":"text"},{"text":"tee","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Prevents overwrite; matches log to results","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--format","type":"text","marks":[{"type":"code_inline"}]},{"text":" flag","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Removed in v4+; use ","type":"text"},{"text":"--output-file \u003cpath>.\u003cext>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Foreground, 1200000ms timeout","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SFGE can take 10–20 min; backgrounding loses output","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Run scripts with absolute ","type":"text"},{"text":"\u003cskill_dir>","type":"text","marks":[{"type":"code_inline"}]},{"text":" path","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"./scripts/","type":"text","marks":[{"type":"code_inline"}]},{"text":" resolves against the user's CWD, not the skill dir","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Never apply fixes without confirmation","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"User must approve code modifications","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Vendor file check before fixes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"If 50%+ vendor (jQuery/Bootstrap/","type":"text"},{"text":"*.min.js","type":"text","marks":[{"type":"code_inline"}]},{"text":"), filter first","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fix-script order: filter (if needed) → discover → apply → summarize","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Skipping summary leaves the user without an outcome report","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SFGE needs explicit ","type":"text"},{"text":"--workspace","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Otherwise template files cause compilation errors","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Look up partial rule names first","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Guessing returns 0 results; use ","type":"text"},{"text":"sf code-analyzer rules","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ONLY","type":"text","marks":[{"type":"code_inline"}]},{"text":" Bash tool, never MCP","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"run_code_analyzer","type":"text","marks":[{"type":"code_inline"}]},{"text":" and other MCP tools bypass the script workflow","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Never invoke other skills for fixes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"This skill owns the full workflow end-to-end","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Query existing results, don't re-scan","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Step 7 filters existing JSON instantly","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Scan returns 0 results","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Invalid rule selector — verify with ","type":"text"},{"text":"sf code-analyzer rules --rule-selector \u003cselector>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"jq","type":"text","marks":[{"type":"code_inline"}]},{"text":" parsing fails","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Shell quoting — use ","type":"text"},{"text":"parse-results.js","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"query-results.js","type":"text","marks":[{"type":"code_inline"}]},{"text":" instead","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Inline scripts written by LLM","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Never write scripts — use existing ones in ","type":"text"},{"text":"\u003cskill_dir>/scripts/","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Ranking/aggregation answered by ad-hoc Python","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Always use ","type":"text"},{"text":"query-results.js","type":"text","marks":[{"type":"code_inline"}]},{"text":"; output already has ","type":"text"},{"text":"topFiles","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"topRules","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"severityCounts","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Reference File Index","type":"text"}]},{"type":"paragraph","content":[{"text":"Scripts","type":"text","marks":[{"type":"strong"}]},{"text":" (always execute via ","type":"text"},{"text":"node","type":"text","marks":[{"type":"code_inline"}]},{"text":" with the absolute ","type":"text"},{"text":"\u003cskill_dir>/","type":"text","marks":[{"type":"code_inline"}]},{"text":" prefix, never Read):","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"File","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"When to use","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\u003cskill_dir>/scripts/parse-results.js","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Step 5 — extract summary from scan JSON","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\u003cskill_dir>/scripts/filter-violations.js","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Step 6.1 — exclude vendor files (jQuery, Bootstrap) from fixes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\u003cskill_dir>/scripts/discover-fixes.js","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Step 6.2 — identify fixable violations","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\u003cskill_dir>/scripts/apply-fixes.js","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Step 6.4 — apply engine fixes after user confirms","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\u003cskill_dir>/scripts/summarize-fixes.js","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Step 6.5 — summarize applied changes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\u003cskill_dir>/scripts/query-results.js","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Step 7 — filter/drill into existing results without re-scanning","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\u003cskill_dir>/scripts/describe-rule.js","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Step 8 — look up rule description and documentation","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\u003cskill_dir>/scripts/list-rules.js","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Step 9 — list/browse available rules by selector with validation","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"References","type":"text","marks":[{"type":"strong"}]},{"text":" (read on demand):","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"File","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"When to read","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/quick-start.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Command-syntax templates","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/flag-reference.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Full flag docs, rule-selector syntax","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/error-handling.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Scan-failure diagnosis","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/engine-reference.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Engine capabilities, file types, rule tags","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/command-examples.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Less-common command scenarios","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/special-behaviors.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SFGE/ApexGuru/AppExchange/diff/large scans","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/vendor-file-handling.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Vendor-file detection and filtering","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/post-scan-workflows.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Steps 7–9 — querying, rule description, rule listing","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"examples/","type":"text","marks":[{"type":"code_inline"}]},{"text":" contains output-structure validation and command patterns (basic/large/security scans, fix workflows).","type":"text"}]}]},"metadata":{"date":"2026-11-02","name":"dx-code-analyzer-run","author":"@skillopedia","source":{"stars":1139,"repo_name":"sf-skills","origin_url":"https://github.com/forcedotcom/sf-skills/blob/HEAD/skills/dx-code-analyzer-run/SKILL.md","repo_owner":"forcedotcom","body_sha256":"47b51d3d0a4f70078ef5a192fb8e5be59a8362dc652e203296096e5995125eac","cluster_key":"484625d31e3c7d920882614cb1659ee87e1d81588f455d0bf527608882100e54","clean_bundle":{"format":"clean-skill-bundle-v1","source":"forcedotcom/sf-skills/skills/dx-code-analyzer-run/SKILL.md","text_bytes":104504,"source_commit":"a8a18ce1e4ba12e69b4073eab98ce31bfbd8271e","skills_sh_rank":3522,"attachment_count":23,"text_attachments":23,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"skills/dx-code-analyzer-run/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"testing-qa","category_label":"Testing"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"testing-qa","metadata":{"domains":["Developer Experience"],"version":"1.0","cliTools":[{"tool":["git"],"semver":">=2.0.0"},{"tool":["java"],"semver":">=11.0.0"},{"tool":["jq"],"semver":">=1.6.0"},{"tool":["node"],"semver":">=18.0.0"},{"tool":["npm"],"semver":">=9.0.0"},{"tool":["python3"],"semver":">=3.9.0"},{"tool":["sf"],"semver":">=2.0.0"}],"relatedSkills":["dx-code-analyzer-configure","dx-code-analyzer-custom-rule-create"]},"import_tag":"skills-sh-top20k-2026-11","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)."}},"renderedAt":1793661355010}

Running Code Analyzer Skill CRITICAL: Mandatory Script Usage Every interaction with Code Analyzer results MUST go through the bundled scripts in . No exceptions. WRONG — never do this: Also forbidden: and any tool — Bash only. RIGHT — always do this: is the absolute path to the directory containing this SKILL.md. Never use — that resolves against the user's CWD, not the skill dir. Any aggregation, filter, or rank question ("which file has the most violations?", "how many PMD issues?", "top rules by count", "break down by severity") is answered by — its output already includes , , and . --- Ov…