ApexGuru Performance Scan Skill CRITICAL: Mandatory Script Usage Every step — token resolution, zipping, API calls, and report decoding — MUST go through the bundled scripts in . No exceptions. WRONG — never do this: RIGHT — always do this: The three underlying scripts still exist and calls them in order. Invoke them individually only for drill-downs on an already-scanned result (Step 5), or when you deliberately need to inspect an intermediate artifact: 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 fil…

|| true)\n FILE_COUNT=${FILE_COUNT:-0}\n if [ \"$FILE_COUNT\" -gt 10 ]; then\n FAST=\"true\"\n echo \"Auto-enabling fast mode (detected $FILE_COUNT Apex files)\" >&2\n fi\nfi\n\n# --- 2. submit ---\necho \"Submitting scan to $BASE_URL/scan ...\" >&2\nSUBMIT_BODY=\"$(mktemp)\"\nFAST_ARG=()\n[ -n \"$FAST\" ] && FAST_ARG=(-F \"fastMode=true\")\n\nHTTP=$(curl -sS -o \"$SUBMIT_BODY\" -w '%{http_code}' -X POST \"$BASE_URL/scan\" \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -H \"x-apexguru-client: ApexGuru-Skill\" \\\n -F \"file=@${ZIP_PATH};type=application/zip\" \\\n ${FAST_ARG[@]+\"${FAST_ARG[@]}\"}) || err \"network error submitting scan\" 0 \"\" \"check connectivity to the API host\"\n\nif [ \"$HTTP\" != \"202\" ] && [ \"$HTTP\" != \"200\" ]; then\n MSG=\"$(jq -r '.message // .error // empty' \"$SUBMIT_BODY\" 2>/dev/null || true)\"\n rm -f \"$SUBMIT_BODY\"\n err \"submit failed: ${MSG:-HTTP $HTTP}\" \"$HTTP\" \"\" \"$(hint_for_status \"$HTTP\")\"\nfi\n\nSCAN_ID=\"$(jq -r '.scanId // empty' \"$SUBMIT_BODY\")\"\nrm -f \"$SUBMIT_BODY\"\n[ -n \"$SCAN_ID\" ] || err \"no scanId in submit response\" \"$HTTP\" \"\" \"unexpected API response shape\"\necho \"Accepted. scanId=$SCAN_ID (polling every ${INTERVAL}s, up to $MAX_POLLS times)...\" >&2\n\n# --- 3. poll ---\nPOLL_BODY=\"$(mktemp)\"\nSTATUS=\"QUEUED\"\ni=0\nwhile [ \"$i\" -lt \"$MAX_POLLS\" ]; do\n i=$((i + 1))\n sleep \"$INTERVAL\"\n HTTP=$(curl -sS -o \"$POLL_BODY\" -w '%{http_code}' -X GET \"$BASE_URL/scan/$SCAN_ID\" \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -H \"x-apexguru-client: ApexGuru-Skill\") || { echo \"poll $i: network hiccup, retrying\" >&2; continue; }\n\n if [ \"$HTTP\" != \"200\" ]; then\n MSG=\"$(jq -r '.message // .error // empty' \"$POLL_BODY\" 2>/dev/null || true)\"\n rm -f \"$POLL_BODY\"\n err \"poll failed: ${MSG:-HTTP $HTTP}\" \"$HTTP\" \"$STATUS\" \"$(hint_for_status \"$HTTP\")\"\n fi\n\n STATUS=\"$(jq -r '.status // \"UNKNOWN\"' \"$POLL_BODY\")\"\n echo \"poll $i/$MAX_POLLS: $STATUS\" >&2\n\n case \"$STATUS\" in\n SUCCEEDED)\n cp \"$POLL_BODY\" \"$RAW_OUT\"\n rm -f \"$POLL_BODY\"\n jq -c '{status, violationCount, filesScanned, rawResult: \"'\"$RAW_OUT\"'\"}' \"$RAW_OUT\"\n exit 0\n ;;\n FAILED)\n MSG=\"$(jq -r '.message // \"scan reported FAILED\"' \"$POLL_BODY\")\"\n rm -f \"$POLL_BODY\"\n err \"$MSG\" \"$HTTP\" \"FAILED\" \"Inspect the message; re-submit after addressing it.\"\n ;;\n QUEUED|RUNNING) : ;; # keep polling\n *) echo \"poll $i: unexpected status '$STATUS', continuing\" >&2 ;;\n esac\ndone\n\nrm -f \"$POLL_BODY\"\nerr \"scan did not finish within $((MAX_POLLS * INTERVAL))s (last status: $STATUS)\" 0 \"$STATUS\" \\\n \"Large projects can take longer — re-run with --max-polls higher, or use --fast.\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":6134,"content_sha256":"8be8a51c1736404095fb70b17de62d3d86c16d414c715c357075bfc5c3494d35"},{"filename":"scripts/scan.sh","content":"#!/usr/bin/env bash\n# One-shot ApexGuru performance scan: package → submit+poll → decode+present.\n#\n# This wrapper runs all three steps of the workflow in a single command so the\n# scan cannot be left half-finished (a bare run-scan.sh already prints a\n# violation count, which can read as \"done\" and tempt an early stop before the\n# report is decoded). The FINAL stdout of this script is the ready-to-present\n# markdown report from decode-report.js --present — print that to the user\n# verbatim.\n#\n# Usage:\n# bash scan.sh \u003cproject-root> [--out \u003creport.md>]\n# [--org \u003calias>] [--fast] [--raw \u003craw-out.json>]\n#\n# \u003cproject-root> folder that contains Apex somewhere beneath it (defaults to\n# CWD if omitted or given as \".\"). build-zip.sh collects every\n# .cls/.trigger under it, any layout.\n# --out FILE also write the presented markdown to FILE (e.g. report.md).\n# --org ALIAS forwarded to run-scan.sh (which sf org to derive the JWT from).\n# --fast forwarded to run-scan.sh (skip LLM-heavy fix generation).\n# --raw FILE keep the raw SUCCEEDED JSON at FILE (default: a temp file).\n#\n# The endpoint (prod/stage/dev) is chosen from the token's env by resolve-token.sh;\n# customers on a prod org always hit api.salesforce.com.\n#\n# Progress for each step streams to STDERR; only the final presented markdown\n# goes to STDOUT. On any step failure, the failing step's JSON error/hint is\n# surfaced to stderr and the script exits non-zero.\nset -euo pipefail\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\n\nPROJECT_ROOT=\"\"\nOUT_FILE=\"\"\nRAW_OUT=\"\"\nORG_ALIAS=\"\"\nFAST=\"\"\n\n# First non-flag arg is the project root.\nwhile [ $# -gt 0 ]; do\n case \"$1\" in\n --out) OUT_FILE=\"${2:-}\"; shift 2 ;;\n --raw) RAW_OUT=\"${2:-}\"; shift 2 ;;\n --org) ORG_ALIAS=\"${2:-}\"; shift 2 ;;\n --fast) FAST=\"--fast\"; shift ;;\n *) [ -z \"$PROJECT_ROOT\" ] && PROJECT_ROOT=\"$1\"; shift ;;\n esac\ndone\n\n[ -n \"$PROJECT_ROOT\" ] || PROJECT_ROOT=\"$(pwd)\"\n[ \"$PROJECT_ROOT\" = \".\" ] && PROJECT_ROOT=\"$(pwd)\"\n\nTS=\"$(date +%Y%m%d-%H%M%S)\"\nWORK_DIR=\"$(mktemp -d)\"\nZIP_PATH=\"$WORK_DIR/apexguru-${TS}.zip\"\n[ -n \"$RAW_OUT\" ] || RAW_OUT=\"$WORK_DIR/apexguru-raw-${TS}.json\"\n\ncleanup() { [ -n \"${WORK_DIR:-}\" ] && rm -rf \"$WORK_DIR\" 2>/dev/null || true; }\ntrap cleanup EXIT\n\n# --- Step 1: package the project's Apex into a zip ---\necho \"[scan] Step 1/3: packaging $PROJECT_ROOT ...\" >&2\nZIP_JSON=\"$(bash \"$SCRIPT_DIR/build-zip.sh\" \"$PROJECT_ROOT\" \"$ZIP_PATH\")\" || {\n echo \"$ZIP_JSON\" >&2\n echo \"[scan] packaging failed — see the error/hint above.\" >&2\n exit 1\n}\necho \"[scan] packaged: $(echo \"$ZIP_JSON\" | jq -r '.humanSize // \"?\"') → $(echo \"$ZIP_JSON\" | jq -r '.zip')\" >&2\n\n# --- Step 2: submit + poll to completion ---\necho \"[scan] Step 2/3: submitting and polling the scan ...\" >&2\nRUN_ARGS=(\"$ZIP_PATH\" \"$RAW_OUT\")\n[ -n \"$ORG_ALIAS\" ] && RUN_ARGS+=(--org \"$ORG_ALIAS\")\n[ -n \"$FAST\" ] && RUN_ARGS+=(\"$FAST\")\nRUN_JSON=\"$(bash \"$SCRIPT_DIR/run-scan.sh\" \"${RUN_ARGS[@]}\")\" || {\n echo \"$RUN_JSON\" >&2\n echo \"[scan] scan failed — see the error/hint above.\" >&2\n exit 1\n}\necho \"[scan] scan complete: $(echo \"$RUN_JSON\" | jq -r '.violationCount // \"?\"') violations\" >&2\n\n# --- Step 3: decode + present (this is the deliverable) ---\necho \"[scan] Step 3/3: decoding and presenting the report ...\" >&2\nPRESENTED=\"$(node \"$SCRIPT_DIR/decode-report.js\" \"$RAW_OUT\" --present)\" || {\n echo \"[scan] decoding failed against $RAW_OUT\" >&2\n exit 1\n}\n\n# Optionally persist the presented markdown (e.g. so it survives temp cleanup).\nif [ -n \"$OUT_FILE\" ]; then\n printf '%s\\n' \"$PRESENTED\" > \"$OUT_FILE\"\n echo \"[scan] report also written to $OUT_FILE\" >&2\nfi\n\n# FINAL stdout: the ready-to-present markdown. Print this to the user verbatim.\nprintf '%s\\n' \"$PRESENTED\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":3823,"content_sha256":"b42c9bcf11ad4f2be751a449c6ef2662a7a09a736962807bd931995b8a6f4350"},{"filename":"scripts/validate-token.js","content":"#!/usr/bin/env node\n// Locally sanity-check an SFAP JWT *before* any network call, so a bad token\n// fails fast with a clear reason instead of a bare HTTP 401 deep in the poll\n// loop. This does NOT verify the signature (only the server can) — it decodes\n// the freely-readable header/body claims to catch the two things that cause\n// almost every 401: missing sfap_api scope, or expired.\n//\n// The token's environment (prod/stage/dev) is detected from its tnk claim and\n// reported as detectedEnv so the caller can route to the matching endpoint.\n//\n// The token is read from STDIN (never argv) so it does not leak into the\n// process list. Usage:\n// printf '%s' \"$JWT\" | node validate-token.js [--now \u003cepoch>]\n//\n// Output (stdout, single-line JSON):\n// pass: {\"ok\":true,\"tnk\":\"...\",\"detectedEnv\":\"prod|stage|dev|null\",\"expiresInSeconds\":N,\"warnings\":[...]}\n// fail: {\"ok\":false,\"error\":\"...\",\"hint\":\"...\"} (exit 1)\n// A \"fail\" only happens for things we can PROVE wrong (expired / missing scope).\n// Anything we cannot read is a warning, not a block.\n\nfunction out(obj, code) {\n console.log(JSON.stringify(obj));\n process.exit(code);\n}\n\nconst args = process.argv.slice(2);\nlet now = Math.floor(Date.now() / 1000);\nfor (let i = 0; i \u003c args.length; i++) {\n if (args[i] === \"--now\") now = parseInt(args[++i], 10) || now;\n}\n\n// Read the token from stdin (keeps it out of argv / process list).\nlet token = \"\";\ntry {\n token = require(\"fs\").readFileSync(0, \"utf8\").trim();\n} catch {\n token = \"\";\n}\nif (!token) out({ ok: false, error: \"no token on stdin\", hint: \"resolve the token before validating\" }, 1);\n\n// A JWT is header.body.signature, each a base64url-encoded chunk.\nfunction decodeSegment(seg) {\n const b64 = seg.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const pad = b64 + \"=\".repeat((4 - (b64.length % 4)) % 4);\n return JSON.parse(Buffer.from(pad, \"base64\").toString(\"utf8\"));\n}\n\nconst parts = token.split(\".\");\nif (parts.length \u003c 2) {\n // Not a JWT we can read — don't block (token may be an opaque format).\n out({ ok: true, tnk: null, detectedEnv: null, expiresInSeconds: null, warnings: [\"token is not a readable JWT — skipping local checks\"] }, 0);\n}\n\nlet header, body;\ntry {\n header = decodeSegment(parts[0]);\n body = decodeSegment(parts[1]);\n} catch {\n out({ ok: true, tnk: null, detectedEnv: null, expiresInSeconds: null, warnings: [\"could not decode JWT claims — skipping local checks\"] }, 0);\n}\n\nconst warnings = [];\n\n// --- 1. tnk / environment. Per the SFAP contract, tnk lives on the HEADER and\n// looks like core/\u003cinstance>/\u003corgId> (e.g. core/prod/00D..., core/stagecomstg2/...,\n// core/falcondeva/...). The environment marker lives ONLY in the \u003cinstance>\n// segment — the trailing \u003corgId> (00D...) is an opaque id that must NOT be\n// scanned for substrings, or a prod org whose id happens to contain \"dev\"/\"stg\"\n// would be wrongly rejected. So we match markers against the instance segment\n// and the iss host only, never the whole claim string. The detected env is\n// reported (not rejected) so the caller can route to the matching endpoint. ---\nconst tnk = header.tnk || body.tnk || null;\nconst iss = body.iss || \"\";\n// tnk instance segment = the middle of core/\u003cinstance>/\u003corgId>; fall back to the\n// whole tnk only if it isn't in that 3-part shape.\nconst tnkParts = tnk ? String(tnk).split(\"/\") : [];\nconst tnkInstance = tnkParts.length >= 3 ? tnkParts[1] : tnk || \"\";\n// iss host (strip scheme/path) so we match the host label, not a full URL.\nconst issHost = String(iss).replace(/^https?:\\/\\//, \"\").split(/[/?#]/)[0];\nconst envHay = `${tnkInstance} ${issHost}`.toLowerCase();\nconst looksDev = /falcondeva|falcondev|falcontest|deva|(^|[^a-z])dev([^a-z]|$)/.test(envHay);\nconst looksStage = /stg|stage/.test(envHay);\n// Env inferred from the token's own claims (null when we can't tell). Production\n// instances carry no stg/dev marker, so a readable tnk with neither signal is prod.\nconst detectedEnv = looksStage && !looksDev\n ? \"stage\"\n : looksDev && !looksStage\n ? \"dev\"\n : tnk && !looksStage && !looksDev\n ? \"prod\"\n : null;\n\nif (!tnk) warnings.push(\"no tnk claim found — could not detect the token's environment; defaulting to prod endpoint\");\n\n// --- 2. scope must include sfap_api. Scope may be a space-delimited string\n// (scp/scope) or an array. Only fail if we can read it AND sfap_api is absent. ---\nconst scopeRaw = body.scp ?? body.scope ?? null;\nif (scopeRaw != null) {\n const scopes = Array.isArray(scopeRaw) ? scopeRaw : String(scopeRaw).split(/\\s+/);\n if (!scopes.includes(\"sfap_api\")) {\n out({ ok: false, error: \"token scope does not include sfap_api\",\n hint: \"Re-mint the token with the sfap_api scope. See references/authentication.md.\" }, 1);\n }\n} else {\n warnings.push(\"no scope claim found — could not confirm sfap_api scope\");\n}\n\n// --- 3. expiry. exp is epoch seconds. Fail if already past. ---\nlet expiresInSeconds = null;\nif (typeof body.exp === \"number\") {\n expiresInSeconds = body.exp - now;\n if (expiresInSeconds \u003c= 0) {\n out({ ok: false, error: `token expired ${Math.abs(expiresInSeconds)}s ago`,\n hint: \"Re-mint a fresh SFAP token and retry.\" }, 1);\n }\n if (expiresInSeconds \u003c 60) warnings.push(`token expires in ${expiresInSeconds}s — it may lapse mid-scan`);\n} else {\n warnings.push(\"no exp claim found — could not confirm the token is unexpired\");\n}\n\nout({ ok: true, tnk, detectedEnv, expiresInSeconds, warnings }, 0);\n","content_type":"text/javascript; charset=utf-8","language":"javascript","size":5455,"content_sha256":"7bb2fe7781cdf19558cbe6f6ff4a9282a3d563be9f1f0671b8ca2f75eeae0e49"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"ApexGuru Performance Scan Skill","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"CRITICAL: Mandatory Script Usage","type":"text"}]},{"type":"paragraph","content":[{"text":"Every step — token resolution, zipping, API calls, and report decoding — 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: hand-rolled curl to the API\ncurl -X POST https://api.salesforce.com/... -F [email protected]\n\n# WRONG: inline base64 + jq to read the report\ncat raw.json | jq -r .report | base64 -d | jq '.[]'\n\n# WRONG: reading the raw result file directly (report is a large base64 blob)\nRead tool → apexguru-raw-*.json\n\n# WRONG: inline node/python to parse violations\nnode -e \"const r = require('./raw.json'); ...\"","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":"# PREFERRED — one command runs all three steps (package → submit+poll →\n# decode+present) and prints the ready-to-show report as its final stdout.\n# Use this for every initial scan: it cannot be left half-finished.\nbash \"\u003cskill_dir>/scripts/scan.sh\" \"\u003cproject-root>\"\n\n# Optionally persist the presented markdown to a file as well:\nbash \"\u003cskill_dir>/scripts/scan.sh\" \"\u003cproject-root>\" --out ./apexguru-report.md","type":"text"}]},{"type":"paragraph","content":[{"text":"The three underlying scripts still exist and ","type":"text"},{"text":"scan.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" calls them in order. Invoke them individually only for ","type":"text"},{"text":"drill-downs on an already-scanned result","type":"text","marks":[{"type":"strong"}]},{"text":" (Step 5), or when you deliberately need to inspect an intermediate artifact:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Equivalent manual chain (scan.sh runs exactly these, in this order):\nbash \"\u003cskill_dir>/scripts/build-zip.sh\" \"\u003cproject-root>\" \"./apexguru-\u003cTS>.zip\"\nbash \"\u003cskill_dir>/scripts/run-scan.sh\" \"./apexguru-\u003cTS>.zip\" \"./apexguru-raw-\u003cTS>.json\"\nnode \"\u003cskill_dir>/scripts/decode-report.js\" \"./apexguru-raw-\u003cTS>.json\" --present\n\n# Drill into a subset WITHOUT re-scanning (reuse the raw file scan.sh left, or\n# pass --raw to scan.sh to keep it at a known path):\nnode \"\u003cskill_dir>/scripts/decode-report.js\" \"./apexguru-raw-\u003cTS>.json\" --rule SOQL_IN_LOOP --full\nnode \"\u003cskill_dir>/scripts/decode-report.js\" \"./apexguru-raw-\u003cTS>.json\" --group file --top 5","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 filter/rank/group question (\"which file has the most issues?\", \"show only SOQL-in-loop\", \"break down by severity\") is answered by re-running ","type":"text"},{"text":"decode-report.js","type":"text","marks":[{"type":"code_inline"}]},{"text":" with flags against the ","type":"text"},{"text":"same raw result file","type":"text","marks":[{"type":"strong"}]},{"text":" — never re-scan, never parse the JSON by hand.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"CRITICAL: Present ","type":"text"},{"text":"--present","type":"text","marks":[{"type":"code_inline"}]},{"text":" output verbatim — never condense it","type":"text"}]},{"type":"paragraph","content":[{"text":"decode-report.js --present","type":"text","marks":[{"type":"code_inline"}]},{"text":" (Step 4) already produces the final, ready-to-show markdown: severity legend, one detail card per violation (message, code, fix, resource link), and a closing summary table. That stdout ","type":"text"},{"text":"is","type":"text","marks":[{"type":"strong"}]},{"text":" the response. Print it to the user exactly as printed — do not rewrite it into a shorter table, do not drop the per-issue cards down to just the summary table, and do not wait for the user to ask \"explain a violation\" before including message/fix/resource. Condensing it defeats the entire point of ","type":"text"},{"text":"--present","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"The attribution is ","type":"text"},{"text":"already in that stdout","type":"text","marks":[{"type":"strong"}]},{"text":" — the summary line is the exact output that states the mode (e.g. \"ApexGuru (static analysis) is active. To unlock runtime intelligence…\"). Do ","type":"text"},{"text":"NOT","type":"text","marks":[{"type":"strong"}]},{"text":" prepend or append your own attribution sentence (no \"Attribution: analysisMode: static…\", no naming the org, no restating \"static-only findings\"). The script's line is the complete, approved wording; adding your own makes the output non-deterministic and off-message.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"WRONG — never do this:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"text"},"content":[{"text":"Top Issues (worst first)\n# Severity Rule Method Line\n1 Major UsingTheTestMethodKeyword legacy... 136\n...\nKey Antipatterns Detected:\n- SOQL/DML in loops (3 violations)","type":"text"}]},{"type":"paragraph","content":[{"text":"(a hand-built summary that drops every message/code/fix — even for violations that had one)","type":"text","marks":[{"type":"em"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"text"},"content":[{"text":"Attribution: analysisMode: static — source-only analysis. The scanned org\n(ag-skills-org) is not onboarded to ApexGuru's full runtime metrics, so\nthese are static-only findings.","type":"text"}]},{"type":"paragraph","content":[{"text":"(an agent-authored attribution line prepended to the report — the script's own summary line already states the mode; this duplicate is non-deterministic and names an org the script never had access to)","type":"text","marks":[{"type":"em"}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"RIGHT — always do this:","type":"text"}]},{"type":"paragraph","content":[{"text":"Paste the full stdout from ","type":"text"},{"text":"decode-report.js --present","type":"text","marks":[{"type":"code_inline"}]},{"text":" — every ","type":"text"},{"text":"### Issue N","type":"text","marks":[{"type":"code_inline"}]},{"text":" card and the closing ","type":"text"},{"text":"## Summary","type":"text","marks":[{"type":"code_inline"}]},{"text":" table — unedited, in one response.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Overview","type":"text"}]},{"type":"paragraph","content":[{"text":"ApexGuru detects ","type":"text"},{"text":"performance antipatterns","type":"text","marks":[{"type":"strong"}]},{"text":" in Apex (SOQL/DML in loops, ","type":"text"},{"text":"Schema.getGlobalDescribe()","type":"text","marks":[{"type":"code_inline"}]},{"text":", SOQL without ","type":"text"},{"text":"WHERE","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"LIMIT","type":"text","marks":[{"type":"code_inline"}]},{"text":", unused SOQL fields). This skill drives the ApexGuru ","type":"text"},{"text":"SFAP Scan API","type":"text","marks":[{"type":"strong"}]},{"text":": it packages the user's Apex (every ","type":"text"},{"text":".cls","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":".trigger","type":"text","marks":[{"type":"code_inline"}]},{"text":" under the project root, any layout) into a zip, submits it, polls until the scan finishes, decodes the base64-encoded report, and presents violations grouped by rule with severity, ","type":"text"},{"text":"file:line","type":"text","marks":[{"type":"code_inline"}]},{"text":", and suggested fixes.","type":"text"}]},{"type":"paragraph","content":[{"text":"Attribution is mandatory.","type":"text","marks":[{"type":"strong"}]},{"text":" The API returns ","type":"text"},{"text":"analysisMode","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"static","type":"text","marks":[{"type":"code_inline"}]},{"text":" → source-only analysis → label results ","type":"text"},{"text":"\"Static only\"","type":"text","marks":[{"type":"strong"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"full","type":"text","marks":[{"type":"code_inline"}]},{"text":" → enriched with runtime metrics from an org onboarded to ApexGuru → label results ","type":"text"},{"text":"\"Production insights\"","type":"text","marks":[{"type":"strong"}]},{"text":".","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"decode-report.js --present","type":"text","marks":[{"type":"code_inline"}]},{"text":" already renders this attribution into its summary line and title (\"Static only\" / \"Production insights\") — that satisfies the mandatory-attribution requirement. Print that line as the ","type":"text"},{"text":"exact output","type":"text","marks":[{"type":"strong"}]},{"text":"; do ","type":"text"},{"text":"not","type":"text","marks":[{"type":"strong"}]},{"text":" author your own attribution sentence or name the org. If the user expected ","type":"text"},{"text":"full","type":"text","marks":[{"type":"code_inline"}]},{"text":" but got ","type":"text"},{"text":"static","type":"text","marks":[{"type":"code_inline"}]},{"text":", the script's static-mode line already explains the org is not onboarded — point them to it rather than restating it (see error handling).","type":"text"}]},{"type":"paragraph","content":[{"text":"In scope:","type":"text","marks":[{"type":"strong"}]},{"text":" zipping a project's Apex, submitting/polling the scan, decoding + presenting violations, filtering/grouping existing results, troubleshooting API errors.","type":"text"}]},{"type":"paragraph","content":[{"text":"Out of scope:","type":"text","marks":[{"type":"strong"}]},{"text":" general static analysis / security / lint (→ ","type":"text"},{"text":"dx-code-analyzer-run","type":"text","marks":[{"type":"code_inline"}]},{"text":", which lists ApexGuru as an engine), applying fixes to code, onboarding an org to ApexGuru, minting SFAP tokens.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Prerequisites","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"An authenticated ","type":"text","marks":[{"type":"strong"}]},{"text":"sf","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" CLI org","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"sf org login web ...","type":"text","marks":[{"type":"code_inline"}]},{"text":"). ","type":"text"},{"text":"resolve-token.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" derives the SFAP JWT from it via ","type":"text"},{"text":"\u003cinstanceUrl>/ide/auth","type":"text","marks":[{"type":"code_inline"}]},{"text":" — this is the normal IDE-session path. Alternatively, set ","type":"text"},{"text":"APEXGURU_SFAP_TOKEN","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"APEXGURU_SFAP_TOKEN_FILE","type":"text","marks":[{"type":"code_inline"}]},{"text":" to supply a JWT directly (CI/headless). The org is derived from the token's ","type":"text"},{"text":"tnk","type":"text","marks":[{"type":"code_inline"}]},{"text":" claim — no org id is passed. Pass ","type":"text"},{"text":"--org \u003calias>","type":"text","marks":[{"type":"code_inline"}]},{"text":" to pick a specific org. See ","type":"text"},{"text":"\u003cskill_dir>/references/authentication.md","type":"text","marks":[{"type":"code_inline"}]},{"text":". If no token can be resolved, the script returns a clear error with a hint.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"sf","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":", ","type":"text","marks":[{"type":"strong"}]},{"text":"bash","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":", ","type":"text","marks":[{"type":"strong"}]},{"text":"curl","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":", ","type":"text","marks":[{"type":"strong"}]},{"text":"zip","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":", ","type":"text","marks":[{"type":"strong"}]},{"text":"jq","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":", ","type":"text","marks":[{"type":"strong"}]},{"text":"node","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" on PATH (standard on macOS/Linux dev boxes).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"A folder containing Apex","type":"text","marks":[{"type":"strong"}]},{"text":" — an sfdx project, a ","type":"text"},{"text":"force-app/","type":"text","marks":[{"type":"code_inline"}]},{"text":" subtree, or any folder with ","type":"text"},{"text":".cls","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":".trigger","type":"text","marks":[{"type":"code_inline"}]},{"text":" files. ","type":"text"},{"text":"build-zip.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" collects all Apex beneath it regardless of layout; the API walks the whole archive.","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Workflow","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 1: Identify the project root","type":"text"}]},{"type":"paragraph","content":[{"text":"The project root is any folder that ","type":"text"},{"text":"contains Apex","type":"text","marks":[{"type":"strong"}]},{"text":" somewhere beneath it (usually an sfdx project root next to ","type":"text"},{"text":"sfdx-project.json","type":"text","marks":[{"type":"code_inline"}]},{"text":", but a ","type":"text"},{"text":"force-app/","type":"text","marks":[{"type":"code_inline"}]},{"text":" subtree or a loose folder of ","type":"text"},{"text":".cls","type":"text","marks":[{"type":"code_inline"}]},{"text":" files works too). If the user gave a path, use it; otherwise use the current working directory. ","type":"text"},{"text":"build-zip.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" collects every ","type":"text"},{"text":".cls","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":".trigger","type":"text","marks":[{"type":"code_inline"}]},{"text":" under it (any layout) and fails clearly if none exists.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 2: Package the project","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"TS=$(date +%Y%m%d-%H%M%S)\nbash \"\u003cskill_dir>/scripts/build-zip.sh\" \"\u003cproject-root>\" \"./apexguru-${TS}.zip\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Output JSON gives ","type":"text"},{"text":"zip","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"bytes","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"humanSize","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"apexFileCount","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"scanRoot","type":"text","marks":[{"type":"code_inline"}]},{"text":". The script enforces the ","type":"text"},{"text":"200MB compressed","type":"text","marks":[{"type":"strong"}]},{"text":" limit and fails fast if exceeded. On error (","type":"text"},{"text":"error","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"hint","type":"text","marks":[{"type":"code_inline"}]},{"text":" fields), relay the hint and stop.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 3: Submit and poll","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"bash \"\u003cskill_dir>/scripts/run-scan.sh\" \"./apexguru-${TS}.zip\" \"./apexguru-raw-${TS}.json\"","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Add ","type":"text"},{"text":"--fast","type":"text","marks":[{"type":"code_inline"}]},{"text":" if the user wants a quicker/cheaper run (skips LLM-heavy fix generation).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"The endpoint follows the token's environment","type":"text","marks":[{"type":"strong"}]},{"text":" — the base URL is derived from the token's ","type":"text"},{"text":"tnk","type":"text","marks":[{"type":"code_inline"}]},{"text":" claim: a prod org hits ","type":"text"},{"text":"api.salesforce.com","type":"text","marks":[{"type":"code_inline"}]},{"text":", and an internal stage/dev org hits ","type":"text"},{"text":"stage.","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"dev.api.salesforce.com","type":"text","marks":[{"type":"code_inline"}]},{"text":". Customers authenticate a prod org, so they always hit prod; no extra flags or config.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"--org \u003calias>","type":"text","marks":[{"type":"code_inline"}]},{"text":" picks which authenticated ","type":"text"},{"text":"sf","type":"text","marks":[{"type":"code_inline"}]},{"text":" org the JWT is derived from (omit to use the CLI's default org).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Progress (","type":"text"},{"text":"QUEUED → RUNNING → SUCCEEDED","type":"text","marks":[{"type":"code_inline"}]},{"text":") streams to stderr; the script polls ~every 15s. Default ceiling is 10 min (","type":"text"},{"text":"--max-polls","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"--interval","type":"text","marks":[{"type":"code_inline"}]},{"text":" to adjust).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"On success, stdout is a one-line JSON summary and the full raw body is written to ","type":"text"},{"text":"apexguru-raw-${TS}.json","type":"text","marks":[{"type":"code_inline"}]},{"text":". On failure, stdout is ","type":"text"},{"text":"{error, httpStatus, status, hint}","type":"text","marks":[{"type":"code_inline"}]},{"text":" — relay the hint. For status-code specifics see ","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":"Foreground only.","type":"text","marks":[{"type":"strong"}]},{"text":" Do not background this; polling output must be observed.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"A SUCCEEDED scan is not the finish line.","type":"text","marks":[{"type":"strong"}]},{"text":" The raw result is a base64 blob, not a user-facing answer. Do not stop or report \"done\" after the scan succeeds — you MUST continue to Step 4 to decode and present the report. Ending the turn at Step 3 leaves the user with nothing readable.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 4: Decode and present","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"node \"\u003cskill_dir>/scripts/decode-report.js\" \"./apexguru-raw-${TS}.json\" --present","type":"text"}]},{"type":"paragraph","content":[{"text":"--present","type":"text","marks":[{"type":"code_inline"}]},{"text":" is the default way to decode for presentation: it implies ","type":"text"},{"text":"--full","type":"text","marks":[{"type":"code_inline"}]},{"text":" (no silent caps) and prints ready-to-show markdown directly — a severity legend (Minor / Major / Critical, plus a Tip marker when ","type":"text"},{"text":"analysisMode: full","type":"text","marks":[{"type":"code_inline"}]},{"text":" enriches severity from production metrics), one ","type":"text"},{"text":"### Issue N","type":"text","marks":[{"type":"code_inline"}]},{"text":" card per violation (message, current code, suggested fix, help-doc link) for the non-hotspot rules — capped at ","type":"text"},{"text":"--top","type":"text","marks":[{"type":"code_inline"}]},{"text":" (default 10) worst-first, with the cap stated in the heading — and a closing ","type":"text"},{"text":"## Summary","type":"text","marks":[{"type":"code_inline"}]},{"text":" table listing ","type":"text"},{"text":"every","type":"text","marks":[{"type":"strong"}]},{"text":" violation regardless of the card cap. ","type":"text"},{"text":"ExpensiveMethods","type":"text","marks":[{"type":"code_inline"}]},{"text":" (a per-method CPU-hotspot ranking from ","type":"text"},{"text":"full","type":"text","marks":[{"type":"code_inline"}]},{"text":" mode, not a line-level antipattern) is collapsed into its own ranked \"CPU Hotspots\" table instead of repeating a near-identical card per method. Print this output to the user verbatim — ","type":"text"},{"text":"present immediately — do not pause to ask, and do not re-summarize it into a shorter table.","type":"text","marks":[{"type":"strong"}]}]},{"type":"paragraph","content":[{"text":"For Step 5 drill-downs (filtering/grouping an existing result), the bare (non-","type":"text"},{"text":"--present","type":"text","marks":[{"type":"code_inline"}]},{"text":") JSON form is fine — see the reading rules below, which apply whenever you run the script without ","type":"text"},{"text":"--present","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"DO NOT:","type":"text","marks":[{"type":"strong"}]},{"text":" invent script code, use bare ","type":"text"},{"text":"./scripts/...","type":"text","marks":[{"type":"code_inline"}]},{"text":" paths, decode base64 inline, ","type":"text"},{"text":"jq","type":"text","marks":[{"type":"code_inline"}]},{"text":" the ","type":"text"},{"text":"report","type":"text","marks":[{"type":"code_inline"}]},{"text":" field, or Read the raw file directly.","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Instructions for reading bare (non-","type":"text"},{"text":"--present","type":"text","marks":[{"type":"code_inline"}]},{"text":") ","type":"text"},{"text":"decode-report.js","type":"text","marks":[{"type":"code_inline"}]},{"text":" output","type":"text"}]},{"type":"paragraph","content":[{"text":"The command prints one JSON object to stdout. Read it field by field before presenting anything — do not eyeball a partial view as complete:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Check ","type":"text","marks":[{"type":"strong"}]},{"text":"truncated","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" first, before anything else.","type":"text","marks":[{"type":"strong"}]},{"text":" If ","type":"text"},{"text":"true","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"groups","type":"text","marks":[{"type":"code_inline"}]},{"text":" was capped to the top ","type":"text"},{"text":"--top","type":"text","marks":[{"type":"code_inline"}]},{"text":" (default 10) rules, each group's ","type":"text"},{"text":"sample","type":"text","marks":[{"type":"code_inline"}]},{"text":" was capped to 3 items, and ","type":"text"},{"text":"topViolations","type":"text","marks":[{"type":"code_inline"}]},{"text":" was capped to ","type":"text"},{"text":"--top","type":"text","marks":[{"type":"code_inline"}]},{"text":" items. ","type":"text"},{"text":"Never present a ","type":"text","marks":[{"type":"strong"}]},{"text":"truncated:true","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" result as the full picture.","type":"text","marks":[{"type":"strong"}]},{"text":" Re-run the same command with ","type":"text"},{"text":"--full","type":"text","marks":[{"type":"code_inline"}]},{"text":" appended and use that output instead. Only skip this if the user explicitly asked for a quick/partial look.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"State attribution from ","type":"text","marks":[{"type":"strong"}]},{"text":"analysisMode","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":"/","type":"text","marks":[{"type":"strong"}]},{"text":"attribution","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" — ","type":"text"},{"text":"static","type":"text","marks":[{"type":"code_inline"}]},{"text":"/\"Static only\" or ","type":"text"},{"text":"full","type":"text","marks":[{"type":"code_inline"}]},{"text":"/\"Production insights\". This is mandatory on every response, per \"Attribution is mandatory\" above.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"serverViolationBreakdown","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" is the raw API's internal rule-code tally (e.g. ","type":"text"},{"text":"SOQL_IN_LOOP_1HOP","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"GGD","type":"text","marks":[{"type":"code_inline"}]},{"text":") — it's a sanity-check total (sums to ","type":"text"},{"text":"violationCount","type":"text","marks":[{"type":"code_inline"}]},{"text":"), not a display name. Never show these codes to the user; use the human-readable ","type":"text"},{"text":"groups[].key","type":"text","marks":[{"type":"code_inline"}]},{"text":" names instead (e.g. ","type":"text"},{"text":"SoqlInALoopOneHop","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"SchemaGetGlobalDescribeNotEfficient","type":"text","marks":[{"type":"code_inline"}]},{"text":").","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"severityCounts","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" (top-level) is the severity distribution across ALL violations — use it for the summary table. Each ","type":"text"},{"text":"groups[]","type":"text","marks":[{"type":"code_inline"}]},{"text":" entry has its own ","type":"text"},{"text":"severityCounts","type":"text","marks":[{"type":"code_inline"}]},{"text":" scoped to just that rule.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Build the \"Violations by Rule\" table from ","type":"text","marks":[{"type":"strong"}]},{"text":"groups","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":", one row per entry: ","type":"text"},{"text":"key","type":"text","marks":[{"type":"code_inline"}]},{"text":" → Rule, ","type":"text"},{"text":"count","type":"text","marks":[{"type":"code_inline"}]},{"text":" → Count, ","type":"text"},{"text":"severityCounts","type":"text","marks":[{"type":"code_inline"}]},{"text":" → Severity, and one ","type":"text"},{"text":"sample[0]","type":"text","marks":[{"type":"code_inline"}]},{"text":" (or ","type":"text"},{"text":"items[0]","type":"text","marks":[{"type":"code_inline"}]},{"text":" when ","type":"text"},{"text":"--full","type":"text","marks":[{"type":"code_inline"}]},{"text":") → Example (","type":"text"},{"text":"file:line","type":"text","marks":[{"type":"code_inline"}]},{"text":").","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Build the \"Top Issues\" table from ","type":"text","marks":[{"type":"strong"}]},{"text":"topViolations","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" — already sorted worst-severity-first. Use ","type":"text"},{"text":"rule","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"severity","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"file:line","type":"text","marks":[{"type":"code_inline"}]},{"text":", and the first entry of ","type":"text"},{"text":"fixes","type":"text","marks":[{"type":"code_inline"}]},{"text":" (if non-empty) as Suggested Fix. If ","type":"text"},{"text":"fixes","type":"text","marks":[{"type":"code_inline"}]},{"text":" is empty, omit that column's value rather than inventing a fix.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"When the user asks to explain a specific violation","type":"text","marks":[{"type":"strong"}]},{"text":" (\"what does this mean\", \"why is this flagged\"), surface that violation's ","type":"text"},{"text":"message","type":"text","marks":[{"type":"code_inline"}]},{"text":" (plain- language why) and ","type":"text"},{"text":"resources[0]","type":"text","marks":[{"type":"code_inline"}]},{"text":" (Help Doc URL) verbatim — both exist on every violation object but are intentionally left out of the summary tables in step 5/6 to keep those scannable. Fall back to ","type":"text"},{"text":"references/violation-catalog.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" only if ","type":"text"},{"text":"message","type":"text","marks":[{"type":"code_inline"}]},{"text":" is empty.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"fixes","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" being ","type":"text","marks":[{"type":"strong"}]},{"text":"[]","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" is expected, not an error — the API's ","type":"text"},{"text":"suggestions","type":"text","marks":[{"type":"code_inline"}]},{"text":" field (fix code) isn't populated for every rule (notably ","type":"text"},{"text":"ExpensiveMethods","type":"text","marks":[{"type":"code_inline"}]},{"text":", a CPU ranking with no single-line fix); don't say \"no fix available\", just omit the column.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"With ","type":"text"},{"text":"--full","type":"text","marks":[{"type":"code_inline"}]},{"text":", each group also carries an ","type":"text"},{"text":"items","type":"text","marks":[{"type":"code_inline"}]},{"text":" array (every violation for that rule, not just the 3-item ","type":"text"},{"text":"sample","type":"text","marks":[{"type":"code_inline"}]},{"text":") — use ","type":"text"},{"text":"items","type":"text","marks":[{"type":"code_inline"}]},{"text":" instead of ","type":"text"},{"text":"sample","type":"text","marks":[{"type":"code_inline"}]},{"text":" when the user wants the complete list for one rule (\"show me all the SOQL unused-fields ones\").","type":"text"}]}]}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Presentation template (fallback — only when NOT using ","type":"text"},{"text":"--present","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]},{"type":"paragraph","content":[{"text":"--present","type":"text","marks":[{"type":"code_inline"}]},{"text":" (the default, per Step 4 above) already renders the full severity-legend + issue-cards + summary-table output described in the \"Instructions for reading bare output\" section — just print its stdout verbatim. Only build a table by hand from bare JSON if ","type":"text"},{"text":"--present","type":"text","marks":[{"type":"code_inline"}]},{"text":" genuinely can't be used (e.g. scripting/CI context with no markdown renderer):","type":"text"}]},{"type":"paragraph","content":[{"text":"Filling the ","type":"text","marks":[{"type":"strong"}]},{"text":"\u003cStatic only | Production insights>","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" title placeholder:","type":"text","marks":[{"type":"strong"}]},{"text":" derive the label from the ","type":"text"},{"text":"attribution","type":"text","marks":[{"type":"code_inline"}]},{"text":" field (not ","type":"text"},{"text":"analysisMode","type":"text","marks":[{"type":"code_inline"}]},{"text":" alone) — it already encodes the three states:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Production insights\" (","type":"text"},{"text":"analysisMode: full","type":"text","marks":[{"type":"code_inline"}]},{"text":" ","type":"text"},{"text":"with","type":"text","marks":[{"type":"strong"}]},{"text":" runtime metrics) — enriched with production runtime metrics.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Static only\" + ","type":"text"},{"text":"analysisMode: full","type":"text","marks":[{"type":"code_inline"}]},{"text":" (","type":"text"},{"text":"no","type":"text","marks":[{"type":"strong"}]},{"text":" runtime metrics) — org is onboarded, but there's no runtime data for this code yet; generate a runtime report in Scale Center.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Static only\" + ","type":"text"},{"text":"analysisMode: static","type":"text","marks":[{"type":"code_inline"}]},{"text":" — source-only. Onboard the org to ApexGuru for production insights.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"The fenced block below is the literal rendered output — substitute the real values and print it; do not emit any of the guidance above:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"text"},"content":[{"text":"## ApexGuru Scan Complete — \u003cStatic only | Production insights>\n\n**Found X performance violations** across Y files.\n\n| Severity | Count |\n|----------|-------|\n| Critical (1) | X |\n| High (2) | X |\n| Moderate (3) | X |\n\n### Violations by Rule\n| Rule | Count | Severity | Example |\n|------|-------|----------|---------|\n| SOQL_IN_LOOP | 15 | High (2) | AccountService.cls:42 |\n| DML_IN_LOOP | 8 | Critical (1) | AccountService.cls:60 |\n| GGD | 2 | Moderate (3) | Utils.cls:12 |\n\n### Top Issues\n| # | Rule | Sev | File:Line | Suggested Fix |\n|---|------|-----|-----------|---------------|\n| 1 | DML_IN_LOOP | 1 | AccountService.cls:60 | Collect records; DML once after the loop |\n| ... up to 10 |\n\nRaw result: `./apexguru-raw-\u003cTS>.json`","type":"text"}]},{"type":"paragraph","content":[{"text":"Scale to result size: ","type":"text"},{"text":"0","type":"text","marks":[{"type":"strong"}]},{"text":" → \"no performance antipatterns found\"; ","type":"text"},{"text":"1–10","type":"text","marks":[{"type":"strong"}]},{"text":" → one table; ","type":"text"},{"text":"11+","type":"text","marks":[{"type":"strong"}]},{"text":" → severity counts + by-rule table + top 10. End with the raw result path. Do ","type":"text"},{"text":"not","type":"text","marks":[{"type":"strong"}]},{"text":" append your own follow-up offer (no \"I can drill in without re-scanning…\", no \"filter by rule / group by file / explain a violation\" menu) — ","type":"text"},{"text":"--present","type":"text","marks":[{"type":"code_inline"}]},{"text":" already prints the script's \"show all\" footer; that is the complete, approved closing line and adding your own makes the output non-deterministic. Rule-catalog details: ","type":"text"},{"text":"\u003cskill_dir>/references/violation-catalog.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 5: Drill into results (no re-scan)","type":"text"}]},{"type":"paragraph","content":[{"text":"Re-run ","type":"text"},{"text":"decode-report.js","type":"text","marks":[{"type":"code_inline"}]},{"text":" against the ","type":"text"},{"text":"same raw file","type":"text","marks":[{"type":"strong"}]},{"text":" with flags:","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":"Flags","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"show only SOQL-in-loop\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--rule SOQL_IN_LOOP --full","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 the critical ones\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--severity 1","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"what's in AccountService.cls?\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--file AccountService.cls --full","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"group by file\" / \"which file is worst?\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--group file --top 5","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"break down by severity\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--group severity","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"show me everything\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--present","type":"text","marks":[{"type":"code_inline"}]},{"text":" (or ","type":"text"},{"text":"--full","type":"text","marks":[{"type":"code_inline"}]},{"text":" for bare JSON)","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":"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":"Any project layout is fine","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"The API walks the whole archive for Apex; ","type":"text"},{"text":"build-zip.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" collects every ","type":"text"},{"text":".cls","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":".trigger","type":"text","marks":[{"type":"code_inline"}]},{"text":" under the root, no ","type":"text"},{"text":"force-app/","type":"text","marks":[{"type":"code_inline"}]},{"text":" required","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Never decode ","type":"text"},{"text":"report","type":"text","marks":[{"type":"code_inline"}]},{"text":" inline","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"It is a large base64 blob — always use ","type":"text"},{"text":"decode-report.js","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use ","type":"text"},{"text":"--present","type":"text","marks":[{"type":"code_inline"}]},{"text":" for the initial decode","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Implies ","type":"text"},{"text":"--full","type":"text","marks":[{"type":"code_inline"}]},{"text":" (no silent caps) and renders ready-to-show markdown directly — severity legend, per-issue cards, closing summary table — mirroring the reference MCP tool's presentation density","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Never re-scan to filter","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Step 5 re-decodes the existing raw file instantly","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Attribution is pre-rendered","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"--present","type":"text","marks":[{"type":"code_inline"}]},{"text":" already prints the mode line (\"Static only\" / \"Production insights\") — print it as the exact output; never author your own attribution sentence or name the org","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"static","type":"text","marks":[{"type":"code_inline"}]},{"text":" when ","type":"text"},{"text":"full","type":"text","marks":[{"type":"code_inline"}]},{"text":" expected","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Org not onboarded to ApexGuru — tell the user, don't treat as an error","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"401 / 403 / 404 / 400","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Token / org-ownership / scanId / zip issues — see references/error-handling.md","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Foreground only, ~15s polls","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Backgrounding loses progress; scans can take minutes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Token is a secret","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"resolve-token.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" never echoes it; don't print it or write it to result files","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Not a security/lint scanner","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"For PMD/ESLint/security, use ","type":"text"},{"text":"dx-code-analyzer-run","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"Reference & Script Index","type":"text"}]},{"type":"paragraph","content":[{"text":"Scripts","type":"text","marks":[{"type":"strong"}]},{"text":" (execute via ","type":"text"},{"text":"bash","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","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/resolve-token.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Resolve SFAP JWT + base URL (called by run-scan.sh)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\u003cskill_dir>/scripts/validate-token.js","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Local (no-network) JWT pre-flight: env/scope/expiry (called by resolve-token.sh)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\u003cskill_dir>/scripts/build-zip.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Step 2 — collect the project's Apex into a size-checked zip","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\u003cskill_dir>/scripts/run-scan.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Step 3 — submit + poll to completion","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\u003cskill_dir>/scripts/decode-report.js","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Steps 4–5 — decode base64 report, group/filter violations","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/authentication.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Where the SFAP JWT comes from; env-var/file setup","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/api-reference.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Endpoint contracts, request/response shapes, limits","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/violation-catalog.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ApexGuru rule meanings and typical fixes","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":"400/401/403/404, FAILED, timeout, static-vs-full diagnosis","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"examples/","type":"text","marks":[{"type":"code_inline"}]},{"text":" contains a sample SUCCEEDED response and a decoded-summary sample.","type":"text"}]}]},"metadata":{"date":"2026-11-02","name":"dx-apexguru-scan","author":"@skillopedia","source":{"stars":1139,"repo_name":"sf-skills","origin_url":"https://github.com/forcedotcom/sf-skills/blob/HEAD/skills/dx-apexguru-scan/SKILL.md","repo_owner":"forcedotcom","body_sha256":"9b9994ce1ead00afad651abe9a9b96ab5a9d1dc534e2c41245aba9014b666075","cluster_key":"5a1ea7dc69e17fd5a80c3ba6f2c1a6d803351a51b6c9f8c7d8b24b906c135b33","clean_bundle":{"format":"clean-skill-bundle-v1","source":"forcedotcom/sf-skills/skills/dx-apexguru-scan/SKILL.md","text_bytes":95567,"source_commit":"a8a18ce1e4ba12e69b4073eab98ce31bfbd8271e","skills_sh_rank":7077,"attachment_count":14,"text_attachments":14,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"skills/dx-apexguru-scan/SKILL.md","import_metadata":{"date":"2026-11-02","author":"@skillopedia","version":"v1","category":"devops-infrastructure","category_label":"DevOps"},"exact_dupes_collapsed_into_this":0},"version":"v1","category":"devops-infrastructure","metadata":{"domains":["Developer Experience"],"version":"1.1","cliTools":[{"tool":["curl"],"semver":">=7.0.0"},{"tool":["jq"],"semver":">=1.6.0"},{"tool":["node"],"semver":">=18.0.0"},{"tool":["sf"],"semver":">=2.0.0"}],"relatedSkills":["dx-code-analyzer-run"]},"import_tag":"skills-sh-top20k-2026-11","description":"Run an ApexGuru performance scan on a Salesforce Apex project via the ApexGuru SFAP Scan API. Zips the project's Apex (any layout), submits it, polls to completion, decodes the base64 report, and presents performance antipattern violations (SOQL in loop, DML in loop, Schema.getGlobalDescribe(), SOQL without WHERE/LIMIT, unused SOQL fields) grouped by rule with severity, file:line, and suggested fixes — clearly attributed as 'Static only' or 'Production insights'. TRIGGER when the user says 'run ApexGuru', 'ApexGuru scan', 'check Apex performance', 'find governor-limit / performance antipatterns', 'SOQL in loop', 'scan my Apex for performance', or 'ApexGuru performance insights'. DO NOT TRIGGER for general static analysis or security scans (use dx-code-analyzer-run), for fixing code without scanning, or for onboarding an org to ApexGuru.","allowed-tools":"Read, Bash(bash), Bash(node), Bash(curl), Bash(zip), Bash(unzip), Bash(jq), Bash(sf), Bash(date), Write","argument-hint":"[project-path] [--org \u003calias>] [--fast]"}},"renderedAt":1793666672708}

ApexGuru Performance Scan Skill CRITICAL: Mandatory Script Usage Every step — token resolution, zipping, API calls, and report decoding — MUST go through the bundled scripts in . No exceptions. WRONG — never do this: RIGHT — always do this: The three underlying scripts still exist and calls them in order. Invoke them individually only for drill-downs on an already-scanned result (Step 5), or when you deliberately need to inspect an intermediate artifact: 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 fil…