Developer apex refactor
Skill PranavNagrecha/Salesforce-Intelligence/.claude/skills/developer-apex-refactor
Salesforce Org Intelligence for AI agents - a read-only, offline, source-available MCP server and CLI answering metadata, dependency, permission, Apex and Flow questions grounded in real retrieved org metadata.
npx -y skills add PranavNagrecha/Salesforce-Intelligence --skill developer-apex-refactorAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Answers code-source-focused developer questions across the full org code surface: Apex classes, Apex triggers, and the v1.4 frontend tier (LWC, Aura, Visualforce). Triggers: "where is field X used in Apex", "find LWC components reading this field", "what calls this Apex from VF", "Aura references to this object", "who reads/writes this field from code", "what classes call MyClass.someMethod", "is it safe to rename this method", "find all Apex references to this", "what Apex/LWC/VF touches this object". Calls `sfi.find_code_usages` (incoming readsFrom/writesTo/callsApex/references edges from ApexClass, ApexTrigger, LightningComponentBundle, AuraDefinitionBundle, VisualforcePage, or VisualforceComponent nodes) — the single code-usage tool. For strictly Apex-only questions, narrow it with `nodeTypes: ['ApexClass','ApexTrigger']` (the former `find_apex_usages` Apex-only view, now folded in). Optionally follows up with `sfi.search_apex_source` to surface the actual lines. Discloses the heuristic-confidence boundary explicitly: string-BUILT dynamic SOQL, dynamic field access, reflective `get()`, LWC `record[fieldName]` dynamic access, and Aura framework attributes are invisible to the scanners (inline static SOQL and constant-string `Database.query` fields ARE resolved at parsed confidence by the default-on AST pass); heuristic matches need a human spot-check before refactor commits.
SKILL.md
33.2 KB, as published. Nobody here has run it
Developer Apex refactor
Usage & discovery (§C3 contract)
For "where is X used / who references X / what depends on X" — for ANY component
type — call sfi.find_component_usages, or the family specialist
(find_code_usages for code, find_field_anywhere for a
field, layout_assignments for a layout). Route by VERB: describe questions
(what is / list / what values) use describe tools, NOT usage tools. Never
improvise a multi-tool fan-out without citing evidence tiers (graph edge
confidence + grep text-match). An empty result is "no static evidence in the
vault" — NEVER "nothing uses this".
Overview
The developer persona has one headline tool, sfi.find_code_usages,
with an Apex-only narrowing MODE.
sfi.find_code_usages(v1.4 broaden) is the default. Given atargetId, it walks incoming edges of typereadsFrom,writesTo,callsApex, orreferenceswhose source node is any ofApexClass,ApexTrigger,LightningComponentBundle,AuraDefinitionBundle,VisualforcePage, orVisualforceComponentand returns each referrer alongside the edge's metadata. It answers "which source files anywhere in the code surface touch this component?".- Apex-only narrowing. Pass
nodeTypes: ['ApexClass','ApexTrigger'](and, if desired,edgeTypes: ['readsFrom','writesTo','callsApex']) to get the Apex-source-only view — the same answer the formersfi.find_apex_usagestool gave (now folded into this tool as a hidden back-compat alias). Use when the user's question is explicitly Apex-scoped ("where is X used in Apex", "refactor Apex method", "Apex callers only") — the smaller result list keeps the surface focused.
sfi.find_code_usages answers the developer-persona question — "which
source files touch this component?" — that developers ask before
renaming a field, refactoring a method, or removing dead code.
Edge confidence varies by producer and edge type. Apex edges come
from two producers. The default-on parser-grade Apex AST pass
(source: 'apex-ast', confidence: 'parsed') resolves receivers
through a symbol table and emits most of the field reads/writes and
cross-class calls on a current vault; it is supplemented by a
heuristic recall scanner (source: 'apex-scanner', confidence: 'heuristic') that backfills files the AST could not parse and
patterns it does not resolve. Cite each edge's own confidence — do
not assume Apex means heuristic.
The LWC/Aura/VF scanner emits declared confidence for the
unambiguous cases (LWC @salesforce/apex/Class.method imports, VF
controller=/extensions= attributes) and heuristic confidence
for everything else (LWC field reads, Aura attribute references, VF
expression bindings). The result lists from either tool will include
false positives and miss real references. A clean find means "the
scanner saw these matches; verify before refactoring," not "these are
the only places this is referenced." The Boundary disclosure
section below names what the scanners cannot see.
v1.4 broadens to LWC/Aura/VF
sfi.find_code_usages is the v1.4 broader tool. Use it for any
code-persona question that is not explicitly Apex-scoped — and even
in many Apex-scoped questions, the broad (default) result is the
"right" answer because real refactors care about LWC and VF callers
too. Narrow to nodeTypes: ['ApexClass','ApexTrigger'] only when the
user pins the answer to Apex.
Key confidence rules to cite when reporting v1.4 results:
- LWC apex imports are
declared. A statement likeimport processOpportunity from '@salesforce/apex/OpportunityService.process'produces acallsApexedge fromLightningComponentBundle:opportunityCardtoApexClass:OpportunityServiceatconfidence: 'declared'— the import path is the declaration; there is no inference. Cite as such. - LWC field reads are
heuristic. Per the v1.4 R3a report, the scanner does not distinguish a schema-import field (e.g.,import FIELD from '@salesforce/schema/Account.Industry') from a body-text field reference; both default toheuristic. State the caveat explicitly when reporting. - Aura field accesses are
heuristic. The scanner pattern-matchesObject.Field__cshapes in the markup and the controller/helper JS; it does not resolvev.{attr}framework attributes (those are intentionally excluded — see Boundary disclosure). - VF controller / extension references are
declared. A<apex:page controller="OpportunityController" extensions="OppExt1,OppExt2">produces three declaredreferencesedges from the VF page to the three classes. The attribute IS the declaration. - All VF/Aura field touches and expressions in HTML are
heuristic. Field name strings inside JS literals, dynamic binding expressions, and attribute-embedded computed names all default to heuristic — the scanner cannot resolve them symbolically.
When the user's question is broad ("what touches this from code?"),
default to sfi.find_code_usages and surface the per-tier breakdown.
When the user pins to a single tier ("what LWC components touch
this?"), call sfi.find_code_usages with nodeTypes: ['LightningComponentBundle']. Only narrow to nodeTypes: ['ApexClass','ApexTrigger'] when the user is explicit about Apex-only.
When to fire
Fire on developer-flavored phrasing. Concrete triggers:
- "Where is X used in Apex…" — "where is
Account.Industry__cused in Apex?", "where do we read/write this field from code?", "what Apex code touches this object?". →sfi.find_code_usages. - "Where is X used in code…" — "where is
Industry__ctouched from the codebase?", "what reads this field anywhere in code?", "every code reference toMyClass". →sfi.find_code_usages. - "Find LWC / Aura / VF referrers…" — "find LWC components
reading this field", "which LWC bundles call this Apex method?",
"what Aura components reference this object?", "what VF pages
touch this field?", "what Visualforce uses
OpportunityController?". →sfi.find_code_usageswithnodeTypesnarrowed (e.g.,['LightningComponentBundle']). - "Who reads / writes / calls…" — "who reads
Contact.Tier__c?", "what classes write toIndustry__c?", "what classes callOpportunityService.process?", "who invokesMyClass.run?". →sfi.find_code_usages(default broad answer) or narrow tonodeTypes: ['ApexClass','ApexTrigger']if the user pins to Apex. - "What Apex/code references / touches…" — "find all Apex
references to this field", "what code touches this trigger?",
"what references
MyClass?". → broad vs. narrow split as above. - "Is it safe to rename…" — "is it safe to rename this Apex
method?", "can I rename
processOpportunity?", "what breaks in code if I rename this field?". →sfi.find_code_usagesis the safer default because a rename breaks LWC@salesforce/apeximports and VF controller attributes too, not just Apex callers. - "Refactor / trace…" — "refactor
Industry__cacross Apex", "trace this method through the codebase", "show me the call sites forMyClass.helper". → broad by default; narrow if user pins.
When NOT to fire
Defer to another skill when:
- The user asks for full cross-metadata impact ("what breaks if I
change X?", "what depends on this field?", "is it safe to
deprecate?"). Those questions need Flow, layout, validation rule,
and permission edges too — defer to
architect-impact-analysis→sfi.get_impact. - The user asks a schema lookup question ("what fields does
Account have?", "what's the type of
Industry__c?"). Defer toanswering-org-questions→sfi.list_componentsorsfi.get_component. - The user asks about a naming pattern or convention ("what's our
convention for status fields?"). Defer to
recognizing-naming-conventions→sfi.get_naming_convention_report. - The user asks where a literal string appears in Apex ("find any
class that mentions
Database.upsert"). That's a grep question forsfi.search_apex_sourcedirectly — no graph lookup needed. - The user wants Flow callers ("what flows call this Apex
class?").
sfi.find_code_usages(in any node-type narrowing) deliberately excludes Flow-emittedcallsApexedges; route througharchitect-impact-analysisfor the fullcallsApexpicture.
Steps
Walk these in order. The pattern is "tool gives the file; grep gives the line."
Step 1 — Identify the target component ID
Translate the user's reference into a canonical ID. Both tools accept
the same targetId formats — what differs is the referrer type,
not the target. Common targets:
CustomField:{Object}.{Field}— e.g.,CustomField:Account.Industry__c.ApexClass:{ClassName}— e.g.,ApexClass:OpportunityService. The v1.4 broadening means LWC@salesforce/apexcallers and VFcontroller=references will now surface here too.ApexTrigger:{TriggerName}— e.g.,ApexTrigger:AccountTrigger.CustomObject:{ApiName}— e.g.,CustomObject:Account(returns code referrers of the object itself; rare).LightningComponentBundle:{name},AuraDefinitionBundle:{name},VisualforcePage:{Name},VisualforceComponent:{Name}— when the target IS a frontend component (e.g., "what references this LWC bundle?").
If the user's phrasing is unambiguous, translate directly. If it
could match multiple components, run sfi.search_components first
and confirm the top match with the user before proceeding.
Step 2 — Call sfi.find_code_usages
Default invocation (broad — the v1.4 tool):
{ "targetId": "CustomField:Account.Industry__c" }
LWC-only narrowing:
{
"targetId": "CustomField:Account.Industry__c",
"nodeTypes": ["LightningComponentBundle"]
}
Apex-only question (narrow nodeTypes for the smaller, Apex-scoped
surface — same parsed-plus-heuristic edges, just filtered to
ApexClass/ApexTrigger referrers; this is the folded-in
find_apex_usages view):
{
"targetId": "CustomField:Account.Industry__c",
"nodeTypes": ["ApexClass", "ApexTrigger"]
}
Optional parameters (sfi.find_code_usages):
limit— defaults to 50, max 500. Real components rarely have more than a handful of code referrers; the default is almost always right. Raise it only when the user asks for an exhaustive sweep.edgeTypes— defaults to all four (readsFrom,writesTo,callsApex,references). Narrow only when the user pins the question ("which classes write to this field?" →['writesTo']). An empty array is allowed and returns an empty result.nodeTypes— defaults to all six code node types. Narrow to a single tier when the user pins it (['LightningComponentBundle']for LWC-only,['VisualforcePage', 'VisualforceComponent']for VF,['ApexClass', 'ApexTrigger']for the Apex-only view — the folded-infind_apex_usagesbehavior). An empty array is allowed and returns an empty result.
The hidden sfi.find_apex_usages back-compat alias still resolves by
exact name (it delegates here with nodeTypes fixed to
['ApexClass','ApexTrigger'] and edgeTypes restricted to the Apex
triad), but it is un-advertised — prefer sfi.find_code_usages with
the narrowing above.
The response shape:
{
"data": {
"usages": [
{
"id": "ApexClass:OpportunityService",
"type": "ApexClass",
"apiName": "OpportunityService",
"edgeType": "readsFrom",
"source": "apex-ast",
"confidence": "parsed",
"properties": { "offset": 412, "length": 18 }
},
{
"id": "LightningComponentBundle:accountTile",
"type": "LightningComponentBundle",
"apiName": "accountTile",
"edgeType": "readsFrom",
"source": "lwc-aura-vf-scanner",
"confidence": "heuristic",
"properties": { "wirePath": "Account.Industry" }
}
]
}
}
An empty usages list means "the scanner saw no matching
references." It does not mean "no code references this
component" — see the boundary disclosure below.
Step 3 — Group by edgeType and present
Bucket usages by edgeType so the developer sees reads, writes,
and calls separately. Within each bucket, sort by id (the API
already returns the list sorted by (id, edgeType) ASC; preserve
that order).
Every referrer gets its canonical id in backticks. Every edge gets
its own confidence cited explicitly, every time — parsed (with
source: apex-ast) for AST-resolved Apex edges, heuristic (with
source: apex-scanner) for the recall-scanner backfill and for
LWC/Aura/VF field reads, declared for LWC apex imports and VF
controller/extension attributes. Never blanket-label the bucket
heuristic.
Step 4 — Optionally call sfi.search_apex_source to surface lines
The scanner gives you the file that referenced the target. To
surface the actual line, follow up with sfi.search_apex_source
using the field or method name as the query:
{ "query": "Industry__c", "limit": 25 }
This is the "tool gives the file; grep gives the line" pattern. Run the second call when the result list is short and the developer is likely to act on it (rename, refactor, deprecate). Skip it when there are many matches and the developer is just scanning the impact surface.
Reporting format
Worked example. User asks: "Where do we write to
Account.Industry__c from Apex?"
Apex usages of
CustomField:Account.Industry__c(per-edge confidence — mostparsedfrom the default-on Apex AST, oneheuristicscanner-backfill):Writes (writesTo) — 2 referrers
ApexClass:AccountTriggerHandler—source: apex-ast,confidence: parsed,properties: { offset: 412, length: 23 }. The AST resolved the receiver toAccount.ApexClass:OpportunityService—source: apex-ast,confidence: parsed,properties: { offset: 1287, length: 19 }.Reads (readsFrom) — 1 referrer
ApexClass:AccountReportBuilder—source: apex-scanner,confidence: heuristic,properties: { offset: 215, length: 18 }. This file fell back to the recall scanner (AST parse failure), so the read is a heuristic match onacc.Industry__cwithaccunresolved — spot-check withsfi.search_apex_source({ query: 'Industry__c' }).The two
parsedwrites are high-confidence; the oneheuristicread needs verification.Boundary: the default-on Apex AST pass resolves inline static SOQL (
[SELECT ... WHERE Industry__c ...]) and CONSTANT-stringDatabase.query('SELECT Industry__c FROM Account')field references atconfidence: 'parsed'— those appear as real edges above. Still invisible: string-BUILT dynamic SOQL (Database.query('SELECT ' + f + ...)), dynamic field access (record.get('Industry__c')), and reflective writes viaSObject.put. Real references may exist that this list misses; files the AST failed to parse fall back to the regex scanner only (the manifest'sapexAstblock counts them).
Group by edgeType with counts. Cite every component by canonical
ID. State each edge's own confidence and source (parsed /
apex-ast for AST-resolved edges, heuristic / apex-scanner for
scanner-backfill edges) — never blanket-label the bucket. Name the
boundary; point the developer at the second-step grep.
Boundary disclosure
The Apex tier runs a parser-grade AST pass by default (source: apex-ast, confidence: parsed) with a heuristic recall scanner
(source: apex-scanner, confidence: heuristic) backfilling files
the AST cannot parse and patterns it does not resolve. The frontend
LWC/Aura/VF tier is a regex scanner — intentionally heuristic for the
common cases, with only the unambiguous declared cases (LWC
@salesforce/apex imports, VF controller/extension attributes) at
confidence: 'declared'. The AST resolves dot-access, cross-class
calls, and inline static SOQL, but it is a parser, not a full
dataflow engine — the residual limits below stay invisible. Surface
this list whenever the developer is about to act on the result.
Apex tier limits (residual, after the default-on AST pass)
- Dynamic field access is invisible.
record.get('Industry__c')andSObject.put('Industry__c', value)are string arguments; the scanner blanks strings before pattern-matching. Zero edges. - Only string-BUILT SOQL is invisible. The default-on AST pass
(source:
apex-ast,confidence: 'parsed') fully parses inline static SOQL in[ ... ]brackets — SELECT, WHERE, ORDER BY, and GROUP BY fields all yield field-level edges — and also parses CONSTANT-stringDatabase.query('SELECT ...')literals. What remains invisible is SOQL assembled from string concatenation or variables (Database.query('SELECT ' + f + ' FROM ...')). On a file the AST failed to parse (counted in the manifestapexAstblock), only the regex scanner ran and SOQL field references for that file are unreliable. - Reflective method dispatch is invisible. A helper like
getFieldValue(record, 'Industry__c')produces no edges; the field name is a string argument. - Intra-class self-calls produce no cross-class edge. The AST
recognizes a bare
doStuff(x)as a self-call, but a class calling its own method is not acallsApexgraph edge (there's no second class to point at), so it won't surface as a referrer of another component. Cross-class calls (ClassName.method() and instance calls on a typed receiver ARE resolved and emitparsededges. - Receiver resolution is a symbol-table pass, not full type
inference. The default-on AST resolves
acc.Industry__ctoCustomField:Account.Industry__c(aparsededge) whenaccis declared, parameter-typed, or for-each-bound asAccount. A receiver whose type the local symbol table cannot establish (deep generics, chained returns, untypedsObject) falls to the recall scanner, which emits aheuristicedge keyed on the variable name (CustomField:acc.Industry__c) — treat those as approximate.
LWC scanner (v1.4) limits
- LWC
record[fieldName]dynamic field access is invisible.record[this.fieldName]andrecord[someVar]are computed property accesses; the scanner cannot resolve the bracket expression. Zero edges. - LWC
@wire(getRecord, ...)static-field detection works for fixed lists.@wire(getRecord, { recordId: '$recordId', fields: [INDUSTRY_FIELD] })resolvesINDUSTRY_FIELDwhen the symbol comes from an@salesforce/schema/Account.Industryimport. If the fields list is built dynamically (fields: this.fieldsToFetch), the scanner sees no field reference. - LWC field-import vs. body-text reads are not distinguished.
Per the v1.4 R3a report, both default to
confidence: 'heuristic'. A schema-import field that is only ever imported (not used in the body) still produces a heuristic edge; consumers cannot use confidence alone to separate "imported but unused" from "imported and read."
Aura scanner (v1.4) limits
- Aura framework-internal
v.{attr}is intentionally NOT extracted.v.recordId,v.records, and other component-local attribute references are framework-level state; they do not correspond to aCustomFieldorCustomObjectand emitting edges for them would produce massive false-positive noise. The scanner silently skips them. - Aura dynamic field expressions are invisible.
v.record[fieldName]and{!v.record[expr]}are runtime-evaluated; the scanner does not resolve them.
Visualforce scanner (v1.4) limits
- VF expressions inside HTML attribute strings work. A binding
like
<apex:inputField value="{!account.Industry__c}"/>produces a heuristicreadsFromedge — the{! }shape is recognised inside attribute strings. - VF expressions inside JS string literals don't. A block like
<script>var x = '{!account.Industry__c}';</script>is a JS string literal; the scanner blanks JS strings the same way it blanks Apex strings. Zero edges for that line. - VF controller and extension references are
declared. Thecontroller=andextensions=attributes ARE the declaration; the edge confidence is declared, not heuristic.
Treat missing edges as "may exist; the static passes couldn't see
them." For present edges, read the confidence: a parsed
(apex-ast) edge is a high-confidence, symbol-resolved match; a
heuristic (apex-scanner or frontend-scanner) edge is a best-effort
pattern match that needs a human pass before any irreversible change.
Heuristic does not mean "approximately correct." Declared edges (LWC
apex imports, VF controller/extension attributes) still need
spot-checks when the referenced class is renamed, but they will not
produce false positives — the import or attribute name IS the
contract.
Anti-patterns
| Mistake | Why it's wrong |
|---|---|
Blanket-labeling every Apex edge heuristic. | Read the per-edge confidence. Most Apex reads/writes/calls on a current vault are parsed (source: apex-ast) — the AST resolved the receiver. Only the recall-scanner backfill (source: apex-scanner) is heuristic. Cite each edge's own tier; don't demote a parsed edge. |
Presenting a heuristic scanner edge as ground truth ("OpportunityService writes to this field"). | A heuristic (apex-scanner) edge is a fallback pattern match. Say so: "the scanner saw OpportunityService write to a .Industry__c literal but couldn't resolve the receiver — verify the variable is an Account reference before acting." (A parsed apex-ast edge already resolved the receiver — cite it as high-confidence.) |
Omitting the sfi.search_apex_source follow-up on a non-trivial match. | The scanner gives the file; the grep gives the line. A developer who is about to rename a method needs the exact call sites, not just a list of files. |
| Conflating Apex referrers with Flow referrers. | sfi.find_code_usages excludes Flow-emitted callsApex edges by design (in any node-type narrowing). If the user wants "what calls this Apex from anywhere including Flow," route to architect-impact-analysis → sfi.get_impact. |
Narrowing to nodeTypes: ['ApexClass','ApexTrigger'] when the user's question is code-broad. | The Apex-only narrowing returns only ApexClass/ApexTrigger referrers. A rename that breaks an LWC @salesforce/apex import or a VF controller= attribute will not show up. Default to the BROAD sfi.find_code_usages (all node types) for any "where is X used in code" or "is it safe to rename" question; only narrow when the user pins to Apex. |
Citing an LWC callsApex edge as heuristic. | LWC's @salesforce/apex/Class.method imports produce confidence: 'declared' edges. Cite them as such — the import path IS the declaration. Same for VF controller= and extensions= attribute references. |
| Claiming "no code references this" when the result is empty. | An empty list means "the static passes saw nothing" (the parsed AST pass covers dot-access + inline static SOQL + constant-string Database.query; the regex scanners cover the rest). String-BUILT dynamic SOQL, dynamic access, reflective calls, LWC record[fieldName], Aura v.{attr}, and VF JS string expressions are all invisible. Restate the boundary; suggest sfi.search_apex_source to grep raw source. |
Going to limit: 500 "to be thorough" by default. | The default 50 is the right call. Raise it only when the user asks for an exhaustive sweep. |
| Skipping the boundary disclosure because the result list is non-empty. | The disclosure is the developer's protection against false confidence. A non-empty list is not a complete list — the developer needs both halves to make a safe refactor decision. |
Example interaction
User: "Is it safe to rename processOpportunity on
OpportunityService? I want to make sure no Apex calls it."
Claude's flow:
- Translate →
targetId: 'ApexClass:OpportunityService',edgeTypes: ['callsApex'](the question pins to call sites),nodeTypes: ['ApexClass','ApexTrigger'](pinned to Apex callers). - Call
sfi.find_code_usageswith that input. - Receive (hypothetical): two
callsApexreferrers. - Call
sfi.search_apex_sourcewithquery: 'OpportunityService.processOpportunity'to surface the lines. - Respond:
Apex call sites for
ApexClass:OpportunityService(filtered tocallsApex; both AST-resolved atconfidence: parsed):Calls (callsApex) — 2 referrers
ApexClass:OpportunityController—source: apex-ast,confidence: parsed,properties.methodName: 'processOpportunity'.ApexTrigger:OpportunityTrigger—source: apex-ast,confidence: parsed,properties.methodName: 'processOpportunity'.Grep follow-up (
sfi.search_apex_source, queryOpportunityService.processOpportunity):
source/classes/OpportunityController.cls:42—OpportunityService.processOpportunity(opp);source/triggers/OpportunityTrigger.trigger:8—OpportunityService.processOpportunity(Trigger.new);Both edges are
parsed: the AST resolved theOpportunityServicereceiver and theprocessOpportunitycall-site. It does not, however, resolve dynamic dispatch — before renaming, check for indirect invocation (Type.forName(...).newInstance(), reflective dispatch), which no static pass sees.
Example interaction — code-broad field rename (v1.4)
User: "I want to rename Account.Industry__c to
Account.IndustrySegment__c. What touches this field anywhere in
code?"
Claude's flow:
- Translate →
targetId: 'CustomField:Account.Industry__c'. The question is code-broad ("anywhere in code"), so call the broader tool with default edges. - Call
sfi.find_code_usageswith{ targetId: 'CustomField:Account.Industry__c' }. - Receive (hypothetical): one Apex read, one LWC read via
@wire(getRecord), one VF page binding. - Call
sfi.search_apex_sourcewithquery: 'Industry__c', limit: 25to surface the Apex lines. - Respond:
Code usages of
CustomField:Account.Industry__cacross all six code surfaces (default broad query, nonodeTypesfilter):Reads (readsFrom) — 3 referrers across 3 tiers
ApexClass:AccountReportBuilder—source: apex-ast,confidence: parsed,properties: { line: 215 }. The AST resolvedaccto Account. High-confidence.LightningComponentBundle:accountTile—source: lwc-aura-vf-scanner,confidence: heuristic,properties: { wirePath: 'Account.Industry' }. The scanner saw the schema-import field in the@wire(getRecord, ...)fields list.VisualforcePage:AccountReport—source: lwc-aura-vf-scanner,confidence: heuristic,properties: { line: 22 }. The scanner saw{!account.Industry__c}in an attribute string.Grep follow-up (
sfi.search_apex_source, queryIndustry__c):
source/classes/AccountReportBuilder.cls:215—report.industry = acc.Industry__c;The Apex read is
parsed(high-confidence); the two frontend edges areheuristic. Before renaming:
- The LWC
@wire(getRecord)schema import must be updated; the v1.4 scanner could not distinguish schema-import from body-text reads, so checkaccountTile/accountTile.jsfor an@salesforce/schema/Account.Industryimport line.- The VF binding
{!account.Industry__c}inAccountReport.page:22must be updated by hand.- Inline static SOQL and constant-string
Database.queryreferences ARE covered (parsed-confidenceapex-astedges). Still invisible: string-BUILT dynamic SOQL, LWCrecord[fieldName]dynamic access, Aurav.{attr}framework attributes, and VF<script>JS string literals. Runsfi.search_apex_sourceforIndustry__cto catch concatenated SOQL strings, and do a manual grep across LWC/Aura/VF source for bracket and template-string references.
Example interaction — LWC-only narrowing (v1.4)
User: "Which LWC components read Account.Industry__c?"
Claude's flow:
- Translate →
targetId: 'CustomField:Account.Industry__c',nodeTypes: ['LightningComponentBundle'],edgeTypes: ['readsFrom'](the question pins both axes). - Call
sfi.find_code_usageswith that input. - Receive (hypothetical): one LWC bundle.
- Respond:
LWC bundles reading
CustomField:Account.Industry__c(v1.4 scanner,confidence: heuristic, filtered tonodeTypes: ['LightningComponentBundle']andedgeTypes: ['readsFrom']):Reads (readsFrom) — 1 referrer
LightningComponentBundle:accountTile—source: lwc-aura-vf-scanner,confidence: heuristic,properties: { wirePath: 'Account.Industry' }.Heuristic boundary: the v1.4 LWC scanner cannot distinguish a schema-import field that is imported-but-unused from one that is imported-and-read in the template/JS body. The edge may be a false positive if the bundle imports
@salesforce/schema/Account.Industrybut never references the value. Also invisible: anyrecord[fieldName]dynamic access in the bundle's controller JS.
Verification
Before sending a response, confirm:
- I translated the user's reference into a canonical
targetId(CustomField:Object.Field,ApexClass:Name,ApexTrigger:Name,LightningComponentBundle:name,AuraDefinitionBundle:name,VisualforcePage:Name, orVisualforceComponent:Name), confirming viasfi.search_componentswhen ambiguous. - I picked the right SCOPE by user intent: broad
sfi.find_code_usages(all node types) for any code-broad question (default), narrowed tonodeTypes: ['ApexClass','ApexTrigger']only when the user explicitly pins to Apex. - When the user pinned to a single code tier (e.g., "what LWC
bundles touch this?"), I narrowed with
nodeTypes: ['LightningComponentBundle'](or the appropriate subset) rather than calling the broad tool and post-filtering by hand. - I called the tool and grouped results by
edgeType(and by code tier when the result spans multiple tiers). - For non-trivial result lists, I followed up with
sfi.search_apex_sourceto surface the lines. - I cited every referrer by canonical ID and labeled the edges
with their actual per-edge confidence:
parsedfor AST-resolved Apex edges (source: apex-ast— the default and dominant case);heuristicfor the Apex recall-scanner backfill and for LWC/Aura/VF field reads and dynamic expressions;declaredfor LWC@salesforce/apexcallsApex edges and VF controller/ extension references. I did NOT blanket-label Apex edgesheuristic. - I appended the boundary disclosure relevant to the result
tier — Apex-scanner invisible patterns when Apex referrers are
present; LWC scanner limits (
record[fieldName], schema-import vs. body-read indistinguishability) when LWC referrers are present; Aurav.{attr}exclusion when Aura referrers are present; VF JS-string blanking when VF referrers are present. - I did not present a heuristic edge as ground truth, and I did not claim an empty result means "no references exist."
- I did not present a declared edge with a heuristic disclaimer (LWC apex imports and VF controller/extension attributes do not need the "scanner may be wrong" caveat).
Grounding & routing (shared contract). For a vague or broad ask, call sfi.route_question first — in the default hybrid mode it returns a meaning-ranked toolCandidates shortlist (which YOU pick from) plus a suggested plane and a route hint (and whether to sfi.resolve a name first). Every org fact must come from an sfi.* tool call, cited by its canonical id — never from memory. Build the answer only from what the tools returned, then pass it through sfi.synthesize_answer, which flags any hallucinatedIds (canonical ids no tool produced). Full cascade: using-sf-intelligence.