Org Health & Operations
Most commands here accept --org <alias> (defaults to config.defaultOrg) and --json.
sfdt audit
Diagnoses org health. Run a single check or all.
sfdt audit all
sfdt audit mfa
sfdt audit licenses --org production --json
sfdt audit all --notify # also push the snapshot to notification channels| Check | What it inspects |
|---|---|
audittrail | Recent Setup Audit Trail entries (lookback audit.auditTrailLookbackDays) |
licenses | License usage vs audit.licenseWarnThreshold |
mfa | Active standard users with no registered MFA method |
mfa-readiness | Users without phishing-resistant MFA (security key, WebAuthn, or built-in authenticator) ahead of the July 2026 enforcement |
soap-logins | SOAP login() traffic on API versions 31–64 (retiring Summer ‘27; lookback audit.soapLoginLookbackDays) |
unused-apex | Apex classes with zero test coverage (heuristic) |
apex-unreferenced | Apex classes with no inbound metadata dependency |
inactive-users | Active users with no recent login (beyond audit.inactiveUserDays) |
api-versions | Apex classes/triggers, Flows, LWC, and Aura below audit.minApiVersion (Flow/LWC/Aura degrade independently if an org can’t query them) |
inactive-flows | Flow definitions with no active version |
inactive-validations | Validation rules that are not active |
inactive-workflows | Workflow rules that are not active (legacy automation) |
unused-permsets | Permission sets with no user or group assignment |
connected-apps | Connected apps that permit all users (admin approval off); also notes the External Client Apps migration |
field-descriptions | Custom fields missing a description |
lint-access | Custom objects with no Read access granted |
lint-access-fields | Custom fields with no Read access granted (field-level) |
Results are written to logs/audit-latest.json (consumed by the dashboard and the VS Code Org
Health sidebar). audit all exits non-zero when any check is fail or error, so it doubles as a
CI gate.
| Option | Description |
|---|---|
--notify | Push the snapshot to configured notification channels |
--org <alias> | Target org |
--json | Structured output |
sfdt monitor
Monitors org operational health. Run a single check or all.
sfdt monitor all
sfdt monitor limits
sfdt monitor all --backup # include a metadata backup in the run
sfdt monitor all --notify # push the snapshot to notification channels| Check | What it inspects |
|---|---|
limits | Governor-limit usage vs monitoring.limitWarnThreshold; reports the Summer ‘26 elastic async Apex entries when present and warns in the overflow band |
errors | Recent failed async Apex jobs (last monitoring.errorLookbackDays days) |
health | Security Health Check score vs monitoring.healthMinScore |
org-info | Instance, edition, trial/expiration window (monitoring.orgInfoTrialWarnDays), and the org’s Salesforce release version and preview status |
deploy-history | Recent deployment success/failure (latest failed → fail) |
deprecated-api | Traffic on deprecated API versions (ApiTotalUsage logs) |
flow-errors | Paused (potentially stuck) flow interviews |
backup | Metadata backup (see below) |
| Option | Description |
|---|---|
--backup | Include a metadata backup when running all |
--notify | Push the snapshot to configured notification channels |
--org <alias> | Target org |
--json | Structured output |
Results land in logs/monitor-latest.json. monitor all exits non-zero when any check is fail
or error.
Graceful degradation
Some checks depend on Beta or license-gated objects (MetadataComponentDependency,
DeployRequest, FlowInterview, ConnectedApplication, ValidationRule/WorkflowRule,
EventLogFile). When an org can’t run one of these, the check returns warn (“unavailable …”)
rather than error, so audit all / monitor all never fail CI just because an org lacks a
particular API or license.
sfdt monitor backup
Retrieves a full metadata backup into monitoring.backupDir.
sfdt monitor backup
sfdt monitor backup --org production --jsonsfdt docs
Generates documentation and diagrams from your org/source.
sfdt docs generate # objects, Apex, flows (MkDocs-compatible)
sfdt docs generate --ai # force the AI overview on
sfdt docs generate --no-ai # force the AI overview off
sfdt docs generate --roles developer,admin # per-component role guides (AI)
sfdt docs generate --no-diagrams # skip the standalone ER-diagram page
sfdt docs diagram # print a Mermaid ER diagram
sfdt docs diagram --output erd.mmd| Subcommand | Options |
|---|---|
docs generate | --ai / --no-ai, --roles [list], --no-diagrams, --json |
docs diagram | --output <file>, --json |
Config-driven docs. With no flags, docs generate follows config: the AI overview is on when
features.ai && docs.ai !== false (--no-ai/--ai override either way); per-component
Developer/Admin/User/DevOps guides are emitted when docs.roleGuides is enabled (scoped by
docs.roles, or --roles [list]); and the standalone ER-diagram page (diagrams/erd.md, linked
in the MkDocs nav) is emitted when docs.diagrams is enabled (--no-diagrams to opt out). Role
guides and AI overviews both require features.ai.
sfdt data
Manages data sets via the native sf data tree, for seeding sandboxes and scratch orgs.
sfdt data list
sfdt data export AccountsAndContacts
sfdt data import AccountsAndContacts
sfdt data delete AccountsAndContacts --yes| Subcommand | Description |
|---|---|
list | List defined data sets |
export [set] | Export records to a data set |
import [set] | Import a data set into the org |
load [set] | Load a data set over Bulk API v2 (insert or upsert) |
delete [set] | Delete records the set’s queries match |
Common options: --org <alias>, --json.
load and delete both write, so both carry a production guard (--production) and a
confirmation that refuses rather than auto-confirms when non-interactive (--yes to skip it).
load inserts or upserts, and an upsert overwrites records that are already there. Neither is
recorded in the ledger: sfdt ledger undo covers org configuration, not bulk data writes.
sfdt scratch
Creates, deletes, lists, and pools scratch orgs.
sfdt scratch create --alias feature-x --days 7
sfdt scratch list
sfdt scratch delete --alias feature-x
sfdt scratch pool --size 3| Subcommand | Description |
|---|---|
create | Create a scratch org from scratch.definitionFile |
delete | Delete a scratch org |
list | List active scratch orgs |
pool | Manage a pre-created pool |
Options: --alias <name>, --days <n>, --size <n>, --json.
sfdt notify
Pushes a deployment-lifecycle event or an org-health snapshot to configured notification channels (Slack, Microsoft Teams, Google Chat, email, generic webhook, or Grafana Loki).
sfdt notify deploy-success
sfdt notify deploy-failure --org production --version 1.5.0
sfdt notify test-failure --message "Coverage dropped below threshold"
sfdt notify release-created --version 1.5.0
sfdt notify snapshot --type monitor # push the latest monitor snapshot
sfdt notify snapshot --type audit| Argument | Values |
|---|---|
<event> | deploy-success, deploy-failure, test-failure, release-created, harness-escalation, snapshot |
| Option | Description |
|---|---|
--type <kind> | For snapshot: audit or monitor (which snapshot to push) |
--version <ver> | Version label to include |
--org <alias> | Org alias to display |
--message <msg> | Custom message body |
Configure channels, severity routing, and optional AI executive-summary digests on the
Notifications page. audit all / monitor all accept --notify to run
and push in one step. The legacy single-Slack config (notifications.slack.webhookUrl with
features.notifications: true) is still honored.
sfdt automation
The on/off grid across every kind of Salesforce automation, and the toggles behind it.
sfdt automation list
sfdt automation list --type validation-rule
sfdt automation disable validation-rule Account.Region_Required --dry-run
sfdt automation disable validation-rule Account.Region_Required
sfdt automation enable flow Set_Region| Subcommand | Description |
|---|---|
list | Every flow, validation rule, duplicate rule, workflow rule and Apex trigger, with its state |
enable <type> <name> | Turn one component on |
disable <type> <name> | Turn one component off |
The five types do not cost the same to toggle, and the grid says so at the point of use. Flows, validation rules and duplicate rules are Tooling record writes. Workflow rules and Apex triggers need a metadata deploy — and a production deploy runs tests, so toggling a trigger there is a deployment with everything that implies.
Toggling automation changes how the org behaves for every user, immediately. A production org
requires --production, and detection fails safe: an org whose sandbox status cannot be read is
treated as production. Every change is recorded in the ledger,
so sfdt ledger undo restores the prior state.
Common options: --org <alias>, --dry-run, --yes, --production, --json.
sfdt permissions
Object and field access — what is granted, by which profile or permission set, and where the org disagrees with your repository.
sfdt permissions matrix --object Account
sfdt permissions drift Account
sfdt permissions grant Account.Region__c --parent "Sales Ops" --level edit
sfdt permissions revoke Account.SSN__c --parent "Sales Ops"
sfdt permissions fix Account --dry-run| Subcommand | Description |
|---|---|
matrix | What each profile and permission set grants |
drift <object> | Compare org grants against the repository |
grant <Object.Field...> | Grant field access on a permission set |
revoke <Object.Field...> | Remove field access |
fix <object> | Apply the grants your repo declares but the org is missing |
Reports what is granted, never “effective”: muting permission sets cannot be queried, and a stronger claim would be an upper bound presented as a fact.
Profiles are refused by name. Salesforce does not permit direct DML on profile-owned permission
entries — those must go through the Metadata API, so change them in source and deploy. Refusing up
front beats an opaque INSUFFICIENT_ACCESS_OR_READONLY that reads like a problem with your own access.
fix applies only missing-in-org rows. Grants the org has that source does not are deliberately
left alone — removing access nobody asked to remove is a different, and riskier, decision.
Common options: --org <alias>, --dry-run, --yes, --production, --json.
sfdt record
Read, edit, or clone a single record, using the same editability model as the Chrome extension’s inspector — so a field refused here is refused for the identical stated reason in the browser.
sfdt record get 001800000000001AAA
sfdt record edit 001800000000001AAA --set Name="Acme Corp" --set AnnualRevenue=250000
sfdt record clone 001800000000001AAA --set Name="Acme Corp (copy)"| Subcommand | Description |
|---|---|
get <id> | Read a record and show which fields are editable, and why the rest are not |
edit <id> | Update fields (--set Field=Value, repeatable) |
clone <id> | Create a copy from the record’s createable fields |
Non-editable fields are refused locally, by name, before anything reaches the org — formula
fields, auto-numbers, and fields your permissions do not cover. A field hidden from you by
field-level security is never written as null over a value you were not allowed to see.
A save reports exactly one of three outcomes: saved, nothing was saved, or outcome
unknown — the last when a request times out and the write may in fact have committed. Exiting 0
on an unknown outcome would tell a script the change landed.
Common options: --org <alias>, --sobject <name>, --dry-run, --json.
sfdt events
Platform Events and Change Data Capture.
sfdt events list
sfdt events tail Order_Placed__e
sfdt events tail AccountChangeEvent --replay all
sfdt events publish Order_Placed__e --field Order_Id__c=A-1| Subcommand | Description |
|---|---|
list | Every subscribable channel, with its Bayeux path |
tail <channel> | Subscribe and print events as they arrive |
publish <Event__e> | Publish one platform event |
A channel whose query is refused is reported as unchecked — it never silently becomes “your org has none”.
publish fires every subscriber on the channel: flows, Apex triggers, and any external
listener. A delivered event cannot be recalled, which is why it is gated going in — a production
org requires --production, and there is no undo.
Common options: --org <alias>, --dry-run, --yes, --production, --json.
sfdt packages
Installed package inventory, your own annotations, and version drift between orgs.
sfdt packages list
sfdt packages compare --source prod --target uat
sfdt packages note ecflc --owner "Platform team" --latest 1.7.0| Subcommand | Description |
|---|---|
list | Installed packages with versions and any notes |
compare | Version drift between two orgs |
note <namespace> | Record vendor URL, latest known version, and internal owner |
note writes .sfdt/packages.json, a committed file — so an annotation is shared and
code-reviewed rather than trapped on one machine. Merges are additive: fields you do not supply are
left alone, including keys written by a newer sfdt that this one does not understand.
Common options: --org <alias>, --json.
sfdt history
Shows recent runs from the local run index so org-health, coverage, test, and deploy outcomes can
be trended over time instead of only reading the latest snapshot. Every audit, monitor, quality,
test, deploy, rollback, and agent-test run is recorded to a compact SQLite index
(logs/history.db); audit and monitor also archive full timestamped snapshots under
logs/audit-results/ and logs/monitor-results/. Recording is best-effort — a history failure
never fails the command.
sfdt history
sfdt history --type audit
sfdt history --type deploy --limit 50
sfdt history --json| Option | Description |
|---|---|
--type <type> | Filter to one run type (e.g. audit, monitor, quality, test-run, deploy, agent-test, agent-fix) |
--limit <n> | Maximum rows to show (default 30) |
--json | Emit the result as JSON |
sfdt versions
Audit Salesforce API versions across local source and the org: which versions your Apex
classes/triggers, Flows, LWC, and Aura components are on, compared against the org’s max API
version. The local scan reads only the -meta.xml sidecars, so it works fully offline;
components whose meta omits <apiVersion> are reported as unspecified (they inherit
sourceApiVersion at deploy time) and never counted as outdated.
sfdt versions # local scan + org comparison (default org)
sfdt versions --org uat # against a specific org
sfdt versions --local-only # fully offline — no org calls
sfdt versions --json # full report for scripting / MCP| Option | Description |
|---|---|
--org <alias> | Target org (default config.defaultOrg); unreachable orgs degrade to a local-only report |
--local-only | Skip the org side even when an org is configured |
--advise | AI upgrade advice for local components below the target version (requires features.ai) |
--target <version> | For --advise: target API version (default: the org max, or the registry max offline) |
--type <family> | For --advise: restrict to one family — apex | flow | lwc |
--json | Emit the full report (histograms, outliers, thresholds) as JSON |
Output shows per-type version histograms (red = below audit.minApiVersion, yellow = behind
the org ceiling when audit.apiVersionWarnBehind is set), the org’s max API version and
release (e.g. Org max: v67 — Summer '26), and the outlier components with a
below-floor / behind-ceiling reason each.
The report is informational (exit 0) — the CI-gating surface is
sfdt audit api-versions, which shares the same thresholds. The MCP server
exposes the same report as the read-only sfdt_api_versions tool, and the Chrome extension’s
api-version-audit feature shows the org-side histograms as a Setup pill.
--advise — registry-grounded AI upgrade advice
With features.ai enabled, sfdt versions --advise explains what upgrading unlocks and what
breaks: value, risks, breaking changes in the upgrade path, effort (S/M/L), minimal code
changes, an upgrade order across component types, and verification steps (including
sfdt quality --api67 when the path crosses v67).
sfdt versions --advise # advise toward the org max (or registry max offline)
sfdt versions --advise --target 67 # explicit target version
sfdt versions --advise --type apex # only Apex classes/triggersThe advice is grounded by a curated per-version registry shipped with the CLI: the AI may
cite version facts only from the registry, and answers “unknown — not in registry” for
anything it lacks — it never states version facts from model memory. The advisory is strictly
read-only: CLI providers may read component source to ground their analysis (which is how the
advice can cite your actual files and lines), but nothing is ever written or auto-fixed. The
prompt is user-editable as api-upgrade-advisor in the prompt editor.