Skip to Content
CLIConfiguration

Configuration

Running sfdt init creates a .sfdt/ directory in your project root. Every command reads it.

The .sfdt/ directory

    • config.json
    • environments.json
    • test-config.json
    • pull-config.json
    • *.local.json
    • feature-flags.json
    • telemetry-snapshot.json
    • bridge-token
FileWhat it holds
config.jsonCore settings: org aliases, feature flags, AI provider, coverage threshold (see below)
environments.jsonNamed environments and org aliases
test-config.jsonTest classes, coverage threshold, test level, and parallelDelay (seconds between parallel test batches, default 1SFDT_PARALLEL_DELAY)
pull-config.jsonMetadata types to pull from the org (see Incremental Pull)
*.local.jsonOptional environment-specific overrides — git-ignore these
plugins/Optional local CLI plugins (.js/.mjs) — see Plugins
cache/Pull delta cache (SQLite) and parked MCP results
data/Data-set definitions for sf data tree
feature-flags.jsonKill-switch list for Chrome extension features
telemetry-snapshot.jsonLatest opt-in extension telemetry snapshot
bridge-tokenAuto-created 0600 bearer token for the local bridge

Add .sfdt/*.local.json to your .gitignore so machine-specific overrides never get committed.

config.json reference

A representative config.json:

{ "projectName": "My Salesforce Project", "defaultOrg": "my-dev-org", "deployment": { "coverageThreshold": 75, "backupBeforeRollback": true, "preflight": { "enforceTests": false, "enforceBranchNaming": false, "enforceChangelog": false } }, "features": { "ai": true, "notifications": false, "releaseManagement": true }, "ai": { "provider": "claude", "model": "" }, "pullCache": { "enabled": true, "parallelism": 5, "batchSize": 100 }, "mcp": { "enabled": false, "parking": { "enabled": true } }, "plugins": [] }

Top-level keys

KeyTypeDescription
projectNamestringDisplay name used in logs and notifications
defaultOrgstringDefault Salesforce org alias
releaseNotesDirstringRelease-notes output directory (default release-notes)
manifestDirstringManifest output directory (default manifest/release)
manifestLayoutenumflat (default) or subpath for per-package subdirectories
changelogDirstringPer-package changelog directory (default changelogs)
logDirstringCLI log directory (default logs)
defaultBranchstringDefault base branch for deploy --create-pr / release PRs (default main; a user-exported SFDT_DEFAULT_BRANCH wins)
smokeTests.testClassesarrayApex test classes run by sfdt smoke (reaches the script via SFDT_SMOKE_TESTS; a user-exported env value wins)
pluginsarraynpm package names to load as plugins — requires SFDT_ALLOW_UNSAFE_CONFIG=1 since 0.22.0 (why)
pluginOptions.autoDiscoverbooleanAuto-load sfdt-plugin-* packages and .sfdt/plugins/*.js (default false) — requires SFDT_ALLOW_UNSAFE_CONFIG=1 since 0.22.0

Repo-supplied settings that need an opt-in

.sfdt/config.json is meant to be committed — sfdt init recommends gitignoring only .sfdt/*.local.json — so it arrives with whatever repository you cloned, written by whoever wrote it. Since 0.22.0, the settings in it that execute code or choose where secrets are sent are refused at load time. sfdt names each one it skipped and how to allow it; everything else in the file loads normally.

SettingWhy it is guarded
plugins[]import()ed at CLI startup, before any command runs
pluginOptions.autoDiscoverImports every sfdt-plugin-* in node_modules/ and every file in .sfdt/plugins/
mcp.salesforce.command / argsSpawned as a process
ai.baseURL (non-loopback)Chooses where prompts — and any secret named by ai.apiKeyEnv / ai.headersEnv — are sent
A notification channel’s headersEnv beside a literal remote webhookUrl/urlReads the named env vars and posts them to a URL fixed by the repo

To allow them:

export SFDT_ALLOW_UNSAFE_CONFIG=1

The opt-in is an environment variable, not a config key — by necessity. A flag inside config.json would be set by the same person who set the guarded setting, which defeats the purpose. Put the export in your shell profile or the CI job that needs it, never in the repo’s own config.

Unaffected, no opt-in needed: ai.apiKeyEnv and ai.headersEnv (inert once the destination is gone), a loopback ai.baseURL — so Ollama, LM Studio, llama.cpp and vLLM keep working — a channel using webhookUrlEnv, an ordinary Slack webhookUrl with no headersEnv, and defaultOrg.

deployment

KeyTypeDefaultDescription
deployment.coverageThresholdnumber75Minimum Apex test coverage percent
deployment.backupBeforeRollbackbooleantrueCapture an org snapshot before sfdt rollback
deployment.keyClassesarray[]Key Apex class names for test-selection heuristics
deployment.preflight.enforceTestsbooleanfalsePromote “no tests” from warning to failure
deployment.preflight.enforceBranchNamingbooleanfalseEnforce branch-naming convention
deployment.preflight.enforceChangelogbooleanfalseRequire an [Unreleased] changelog entry
deployment.preflight.strictbooleanfalsePromote every preflight warning to a failure

strict overrides the per-check flags. A check you left as a warning by setting its enforceX: false is still promoted to a failure when strict: true. For the opt-in flags above, false behaves the same as omitting the key — it leaves the check as a non-fatal warning, it does not actively suppress it under strict mode.

These preflight flags are editable from the dashboard (Settings → Config) as of v0.15.1 — the dashboard shows an inline safety caution when you change one. (defaultOrg, plugins, and the MCP command settings remain file-only.)

features

Feature toggles. All default to true except notifications.

KeyDescription
features.aiAI-powered commands
features.notificationsSlack integration
features.releaseManagementRelease workflow
features.monitoringOrg monitoring
features.auditAudit-trail analysis
features.docsDocs generation
features.dataData import/export
features.scratchScratch-org management

ai

KeyTypeDescription
ai.providerenumclaude (default), gemini, openai, or http (text-only endpoint)
ai.modelstringOptional model override (the provider CLI handles selection)
ai.baseURLstringOpenAI-compatible endpoint URL (http provider only)
ai.apiKeyEnvstringName of the env var holding the API key (http provider only)

See AI Providers for full setup.

pullCache

KeyTypeDefaultDescription
pullCache.enabledbooleantrueEnable the SQLite-backed incremental pull cache
pullCache.parallelismnumber5Concurrent retrieval threads
pullCache.batchSizenumber100Components per retrieve batch
pullCache.retrieveTimeoutSecondsnumber360Retrieve timeout

mcp

KeyTypeDefaultDescription
mcp.enabledbooleanfalseEnable the MCP server
mcp.parking.enabledbooleantruePark oversized tool results
mcp.parking.thresholdBytesnumber50000Size limit before a result is parked
mcp.parking.ttlSecondsnumber86400TTL before parked cache files are deleted
mcp.parking.cacheScopeenumsessionSEP-2549 cache scope: global, user, or session

See the MCP Server page for details.

audit, quality, monitoring, docs, data, scratch, soql

KeyDefaultDescription
audit.auditTrailLookbackDays30Setup-audit-trail lookback window
audit.licenseWarnThreshold0.75License-usage warn threshold (0–1)
audit.inactiveUserDays90Days before a user is “inactive”
audit.minApiVersion45Minimum acceptable API version (the hard floor for audit api-versions and sfdt versions)
audit.apiVersionWarnBehind0When > 0, also warn on components more than N versions behind the org’s max API version (0 = off); findings are tagged below-floor vs behind-ceiling
audit.fieldDescriptionMaxMissing0Max custom fields allowed without a description before warning
audit.connectedAppFlagPermissivetrueFlag connected apps that permit all users (admin approval off)
audit.soapLoginLookbackDays30Lookback window for the soap-logins check (retiring SOAP login() traffic)
monitoring.backupDirmonitoring-backupMetadata-backup output directory
monitoring.limitWarnThreshold0.75Governor-limit warn threshold (0–1)
monitoring.errorLookbackDays7Apex-error lookback window
monitoring.healthMinScore80Minimum acceptable Security Health Check score
monitoring.orgInfoTrialWarnDays14Warn when a trial/expiration window is within this many days
monitoring.deployHistoryLookback20Number of recent deployments inspected by deploy-history
monitoring.deprecatedApiLookbackDays7Lookback window for deprecated-API traffic
docs.outputDirdocsGenerated-docs output directory
docs.aitrueInclude an AI overview in generated docs (requires features.ai)
docs.diagramstrueEmit the standalone ER-diagram page during docs generate
docs.roleGuidesfalseEmit per-component role guides during docs generate (requires features.ai)
docs.roles[]Which role guides to emit (developer, admin, user, devops); empty = all four
data.dir.sfdt/dataData-set definition directory
scratch.definitionFileconfig/project-scratch-def.jsonScratch-org definition
scratch.durationDays7Scratch-org lifetime
scratch.poolSize0Pre-created scratch-org pool size
soql.defaultLimit200Default row bound for sfdt soql query/sosl when --limit isn’t passed
soql.maxLimit2000Hard row cap for sfdt soql query/sosl — any requested or in-query LIMIT above it is clamped

audit.licenseWarnThreshold and monitoring.limitWarnThreshold both default to 0.75 so the CLI and the Chrome extension’s live org-health checks band org-health usage identically (shared via @sfdt/flow-core).

ci

Defaults for sfdt ci init. Flags override config; config overrides built-in defaults. (--type scratch also reads the existing scratch.definitionFile key.)

{ "ci": { "authMethod": "sfdx-url", // sfdx-url | jwt "environment": "production", // release approval environment "runner": "npx" // npx | docker | action } }

notifications

Multi-channel push to Slack, Microsoft Teams, Google Chat, email, a generic webhook, or Grafana Loki. Secrets are referenced by env-var name, never inline. The older single-Slack shape is still honored when features.notifications: true.

{ "features": { "notifications": true }, "notifications": { "enabled": false, // master switch for the channels[] system "summary": { "enabled": false }, // send AI executive summaries for snapshots "channels": [ // type: slack | teams | googlechat | email | webhook // common: name, severityThreshold (ok|warn|error|fail), events[] // slack/teams/googlechat: webhookUrl | webhookUrlEnv // webhook: url | urlEnv, format (json|loki), headers{}, headersEnv{} (auth — env-var NAMES) // email: from, to[], smtp{ hostEnv, portEnv, userEnv, passwordEnv, secure } ], // legacy single-Slack shape (still honored when features.notifications=true): "slack": { "webhookUrl": "", "webhookUrlEnv": "" } } }

See the Notifications page for channel fields and severity routing.

deployment.smart

Settings for Smart Deploy (sfdt deploy --smart).

{ "deployment": { "smart": { "enabled": false, "deltaBase": "main", "noOverwriteManifest": "manifest/package-no-overwrite.xml", "impactingTypes": ["ApexClass", "ApexTrigger", "Flow"], "downgradeTestsOnNonProd": true, "useRelevantTests": false, "assumeProd": false } } }

deployment.smart.useRelevantTests (default false) opts non-production orgs on API ≥ 66 into Salesforce’s beta RunRelevantTests in place of the RunLocalTests fallback — see Smart Deploy.

ai.headers / ai.headersEnv

Extra request headers for the http provider. headers takes literal values; headersEnv maps a header name to the name of an env var holding its value — use it for anything secret, so the token stays out of .sfdt/config.json. headersEnv overrides a literal of the same name, and an unset variable fails the request naming it rather than calling the endpoint without the header. See AI Providers.

{ "ai": { "headersEnv": { "X-Org-Id": "GATEWAY_ORG_ID" } } }

ai.agent

The bounded coding-agent auto-fix loop for sfdt deploy --smart --ai-fix. Off by default; requires both enabled and allowWrite. See Smart Deploy.

{ "ai": { "agent": { "enabled": false, "maxTurns": 3, "allowWrite": false } } }

Reading & writing config

Use dot notation from the command line:

sfdt config get defaultOrg sfdt config get deployment.coverageThreshold sfdt config set deployment.coverageThreshold 80 sfdt config set features.ai true

Values are coerced automatically: "true"/"false" become booleans, numeric strings become numbers, everything else stays a string.

Last updated on