Dylan AndersenDylan Andersen's Docs
Cursor + SalesforceExpert Cursor

Project Anatomy

A guided tour of force-app/main/default so you and the agent both know where things live

The first time you open a Salesforce project, force-app/main/default/ looks like a nested shoebox. It isn't. Every folder is a metadata type with a predictable layout, and once you can point at each one you can tell the agent exactly where to work.

Project Anatomy hero

The shape of a DX project

A default project scaffolded by sf project generate looks like this:

my-project/
├── config/
│   └── project-scratch-def.json
├── force-app/
│   └── main/
│       └── default/
│           ├── applications/
│           ├── classes/
│           ├── flexipages/
│           ├── flows/
│           ├── genAiFunctions/
│           ├── genAiPlugins/
│           ├── labels/
│           ├── layouts/
│           ├── lwc/
│           ├── objects/
│           ├── permissionsets/
│           ├── profiles/
│           ├── staticresources/
│           ├── tabs/
│           └── triggers/
├── scripts/
│   └── apex/
│       └── hello.apex
├── .forceignore
├── .gitignore
├── package.json
├── README.md
└── sfdx-project.json

Not every folder exists in every project. They appear as soon as you retrieve or create metadata of that type.

The folders an SE touches most

classes/

Apex. Each class is two files: MyClass.cls and MyClass.cls-meta.xml. The meta file carries the API version and status. Triggers live in triggers/ with the same shape.

<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>62.0</apiVersion>
    <status>Active</status>
</ApexClass>

When the agent writes a new Apex class and forgets the meta file, deploy will fail with No matching component found. Always check for both files.

lwc/

Lightning Web Components. Each component is its own folder with a minimum of three files:

lwc/
└── agentSummary/
    ├── agentSummary.html
    ├── agentSummary.js
    └── agentSummary.js-meta.xml

The .js-meta.xml controls where the component can appear (App Page, Home Page, Record Page, App Builder). Forget to expose it and the component won't show up as a draggable tile.

objects/

Custom objects and fields. The structure mirrors the object:

objects/
└── Customer_Insight__c/
    ├── Customer_Insight__c.object-meta.xml
    ├── fields/
    │   ├── Account__c.field-meta.xml
    │   └── Score__c.field-meta.xml
    ├── listViews/
    ├── recordTypes/
    └── validationRules/

This layout is why the Org Browser is useful. Telling the agent "look in force-app/main/default/objects/Account" only works if Account is retrieved. For standard objects you mostly won't have them locally.

flows/

One .flow-meta.xml per flow. These files are XML blobs that get long fast. The agent can read them, but try to describe what the flow should do in prose rather than asking it to read and edit raw XML.

permissionsets/

One .permissionset-meta.xml per permission set. These carry object, field, tab, and Apex class permissions. See Permission Sets & Access for how to build these cleanly.

genAiFunctions/, genAiPlugins/

Agentforce Builder-managed agents live here. Functions are individual actions. Plugins are groups of functions (used to be called topics).

.agent files

Agent Script DSL. If your project uses Agent Script, look for files with the .agent extension in force-app/main/default/agents/. These are plain-text FSM definitions. The sf-ai-agentscript skill knows how to edit them safely.

staticresources/

Images, JS libraries, CSS. Anything you want to serve up to an LWC from the org. Each resource is two files: the asset itself and a .resource-meta.xml pointing at its content type.

flexipages/

Lightning App Builder pages. When you drag an LWC onto a record page and save, Salesforce generates a FlexiPage. Rarely something an SE edits by hand.

Project-level config files

sfdx-project.json

The project manifest. Controls which folders hold source, the default API version, and namespace settings.

{
  "packageDirectories": [
    { "path": "force-app", "default": true }
  ],
  "name": "my-poc",
  "namespace": "",
  "sfdcLoginUrl": "https://login.salesforce.com",
  "sourceApiVersion": "62.0"
}

sourceApiVersion is the default that's stamped into new metadata files. Bump it once per season so the agent doesn't generate metadata two releases behind.

.forceignore

The gitignore of Salesforce metadata. Covered in detail on the Source Tracking & .forceignore page.

config/project-scratch-def.json

Feature flags and settings for any scratch org you spin up. This is where you turn on Agentforce, Data 360, Einstein, and any industry cloud features.

scripts/apex/

Plain .apex files run through sf apex run --file. A great place to store one-off data seeds, cleanups, and demo reset scripts. See Demo Reset & Repeatability.

Teaching the agent the map

You don't need to paste the whole tree into chat. Pick the level the agent should work at:

Look at @force-app/main/default/ and tell me what kinds of metadata are here. Then pick the three that matter for a POC demoing customer-service triage.

Use for onboarding the agent to an unfamiliar customer project.

All my Apex lives in @force-app/main/default/classes/. Summarise the service classes and identify which ones handle customer-service routing.

Use when you want the agent to stay focused on one type of work.

Here is the LWC I'm working on: @force-app/main/default/lwc/agentSummary. Read the template, JS, and meta file, and explain what it does today.

Use when you're iterating on a single component and don't want the agent wandering.

Draw the map for everyone else

When you hand a project off, a 30-second Mermaid diagram of the folder structure is worth an hour of screen-shares. The sf-diagram-mermaid skill will build one for you:

Activate the sf-diagram-mermaid skill. Generate a graph diagram that shows
which metadata types in @force-app/main/default/ depend on which. Group
the Apex classes, the LWCs, and the Agentforce metadata.

Paste the result into your hand-off doc. See POCs & Hand-off Docs for where that lives.

Next moves

On this page