@sfdt/cli
v0.18.1
Published
Salesforce DevTools — Production-grade CLI for Salesforce DX deployment, testing, quality analysis, and release management
Maintainers
Readme
SFDT — Salesforce DevTools
Deploy, test, and ship Salesforce changes with confidence.
@sfdt/cli is the command-line core of the SFDT suite — a production-grade CLI for Salesforce DX
deployment, testing, quality analysis, and release management. Pairs with the SFDT Chrome
extension and the VS Code extension.
📖 Read the docs at sfdt.dev → · npm · Usage guide
Features
- Interactive deployment workflows with preflight validation, tagging, and PR creation
- Automated release manifest generation from git diffs
- Parallel Apex test execution with configurable coverage enforcement
- Code and test quality analysis with AI-powered fix plans
- Pre-release validation checklist (
sfdt preflight) - Deployment rollback with pre-rollback org state backup
- Post-deploy smoke testing
- Org metadata drift detection
- Multi-package project support — scope manifests and deploys to individual
packageDirectorieswith--packageand--name; deploy source folders directly with--source-dir - Smart package.xml generator from git diffs with AI dependency cleanup (
sfdt manifest) - AI deployment error log interpreter with heuristic fallback for offline use (
sfdt explain) - AI-generated PR descriptions and Slack messages from deployment changes (
sfdt pr-description) - AI-powered code review, test failure analysis, changelog generation, and release notes — optional, works with Claude, Gemini, or OpenAI
- Org metadata comparison — diff two orgs or local source vs org with optional package.xml export (
sfdt compare) - Local web dashboard for test results, preflight, drift monitoring, and org comparison (
sfdt ui) - Smart delta deployments — minimal git-delta package with overwrite protection, automatic test-level selection, optional AI / coding-agent auto-fix (
sfdt deploy --smart) - Native org health & operations suite — diagnose (
sfdt audit), monitor/backup (sfdt monitor), dependency analysis (sfdt dependencies), and Apex coverage gating (sfdt coverage) - CI/CD pipeline templates for GitHub, GitLab, Azure, and Bitbucket (
sfdt ci init— monitor, PR validation, approval-gated release, scratch-org CI), plus a published GitHub Action (uses: scoobydrew83/sfdt@v0); PR decoration (sfdt pr comment) and cross-org retrofit (sfdt retrofit) - Multi-channel notifications — Slack, MS Teams, Google Chat, email, webhook, and Grafana Loki, with optional AI executive-summary digests (
sfdt notify) - Plugin architecture — extend sfdt with
sfdt-plugin-*npm packages or local.sfdt/plugins/scripts, plus a Salesforce CLI plugin exposing every command assf sfdt <command>(sf plugins install @sfdt/plugin) - Works with any Salesforce DX project — no project-specific values hardcoded
For in-depth command walkthroughs and workflow examples, see docs/USAGE.md.
Repository layout
@sfdt/cli is one of six workspaces in this monorepo:
| Workspace | What it is | Status |
|---|---|---|
| @sfdt/cli (/src, /bin, /scripts) | The npm CLI documented below. | Published to npm |
| @sfdt/extension (/extension) | Chrome extension for Salesforce Flow Builder + Setup productivity. Talks to the CLI via the local bridge for deploy / rollback / quality / AI features. See extension/README.md and extension/PRIVACY.md. | Pre-Web-Store |
| sfdt-devtools (/vscode) | VS Code extension — a CLI-backed command center (Org Health sidebar, command palette, CodeLens, diagnostics, embedded dashboard); it drives the sfdt binary and reimplements no logic. See vscode/README.md. | Published to the VS Code Marketplace |
| @sfdt/host (/host) | Native messaging host used as the extension's fallback transport when sfdt ui isn't running. Installed with sfdt extension install-host. | Bundled with CLI |
| @sfdt/flow-core (/packages/flow-core) | Shared TypeScript library — Flow normalization, rules engine, scoring, and the versioned bridge contract. Consumed by both CLI and extension. | Published to npm (publishes alongside the CLI) |
| @sfdt/plugin (/packages/plugin) | Salesforce CLI plugin — a thin oclif wrapper exposing every command as sf sfdt <command>. Forwards argv to the bundled sfdt binary; reimplements no logic. | Published to npm (publishes alongside the CLI) |
The CLI's sfdt ui command starts a local web dashboard (/gui) that exposes the same bridge endpoints the extension uses.
Quick Start
npm install -g @sfdt/cli
cd your-salesforce-project
sfdt init
sfdt deployOther install methods
# Bootstrap script (checks prerequisites, then installs via npm)
curl -fsSL https://raw.githubusercontent.com/scoobydrew83/sfdt/main/install.sh | bash
# Homebrew (macOS/Linux) — tap once, then install by name
brew tap scoobydrew83/sfdt
brew install sfdt
# (Third-party tap: it won't show in brew's website search, which only lists homebrew-core.)
# Docker (official multi-arch image)
docker run --rm -v "$PWD:/project" ghcr.io/scoobydrew83/sfdt:latest --help
# Salesforce CLI plugin — run every command as `sf sfdt <command>`
sf plugins install @sfdt/plugin
sf sfdt deploy --dry-runThe
@sfdt/pluginpackage is a thin wrapper over@sfdt/cli(it shells out to the same binary), so every command, flag, and--jsonoutput is identical to runningsfdtdirectly.--jsoncommands emit a Salesforce-native{ status, result, warnings }envelope. As an unsigned third-party plugin,sfshows a one-time security prompt on install.
npm alternatives: pnpm add -g @sfdt/cli · yarn global add @sfdt/cli · one-off npx @sfdt/cli --help.
From source (contributors):
git clone https://github.com/scoobydrew83/sfdt.git && cd sfdt
npm install
npm link # makes `sfdt` available globally from your checkout
npm run build:gui # build the local web dashboardFull install reference — every method plus CI usage — at sfdt.dev/cli/installation.
Commands
The full command reference — every command, flag, and default, generated from the CLI's own
command registry so it can never drift — lives at
sfdt.dev/cli/commands. Locally, sfdt --help (and
sfdt <command> --help) is always current. The machine-readable inventory is
generated/commands.json.
By area:
- Deploy & release — smart git-delta deploys with overwrite protection and automatic test-level selection, preflight validation, rollback with pre-rollback backup, smoke tests, release manifests + AI release notes, CI pipeline generation for four providers, and PR decoration.
- Metadata & source — incremental pull with a SQLite delta cache, drift detection,
org-to-org compare, full inventory scans,
package.xmlfrom git diffs, dependency tracing (including source-parsed gaps the Tooling API misses), and cross-org retrofit. - Testing & quality — the enhanced Apex runner, unified Apex + Flow logic tests, local LWC (Jest) tests, Agentforce agent tests as CI gates, Code Analyzer v5 scans (SARIF output for code scanning), coverage gates, and changelog management.
- Environments & data — scratch-org creation, deletion, listing, and pooling (
sfdt scratch), plus org data set import/export oversf data tree(sfdt data). - AI — code review, deployment-error analysis, PR descriptions, fix plans, and ad-hoc prompts across Claude / Gemini / Codex CLI providers or any OpenAI-compatible endpoint.
- Org health —
auditandmonitorcheck suites with multi-channel notifications, metadata backup, run history trending, and generated org documentation with ER diagrams. - Platform — the local web dashboard (
sfdt ui), MCP server for agents, Chrome-extension bridge + native messaging host management, feature kill-switch, environment doctor, shell completion, and a plugin scaffold.
Configuration
Running sfdt init creates a .sfdt/ directory in your project root:
.sfdt/
config.json # Core settings: org aliases, feature flags, AI provider, coverage threshold
environments.json # Named environments and org aliases
test-config.json # Test classes, coverage threshold, test level
pull-config.json # Metadata types to pull from orgconfig.json
{
"projectName": "My Salesforce Project",
"defaultOrg": "my-dev-org",
"deployment": {
"coverageThreshold": 75,
"preflight": {
"enforceTests": false,
"enforceBranchNaming": false,
"enforceChangelog": false
}
},
"features": {
"ai": true,
"notifications": false,
"releaseManagement": true
},
"ai": {
"provider": "claude",
"model": "",
"baseURL": "",
"apiKeyEnv": "",
"headers": {},
"timeoutMs": 300000
},
"plugins": []
}
baseURL/apiKeyEnv/headers/timeoutMsapply only to thehttpprovider; CLI providers (claude/gemini/openai) ignore them. See AI Features.
AI Features
AI-powered commands (review, explain, manifest --ai-cleanup, quality --fix-plan, pr-description, changelog generate, release) work with Claude, Gemini, OpenAI/Codex CLI providers, or any OpenAI-compatible HTTP endpoint (Ollama, OpenRouter, MiniMax, …). The provider is configured during sfdt init or by editing .sfdt/config.json.
Claude (default)
Requires the Claude Code CLI:
npm install -g @anthropic-ai/claude-code{ "ai": { "provider": "claude" } }Claude's interactive mode lets AI commands read your repository files directly with tool use.
Gemini
Requires the Gemini CLI:
npm install -g @google/gemini-cli{ "ai": { "provider": "gemini", "model": "" } }Authentication and model selection are handled by the Gemini CLI.
OpenAI
Requires the Codex CLI:
npm install -g @openai/codex{ "ai": { "provider": "openai", "model": "" } }Authentication and model selection are handled by the Codex CLI.
HTTP (OpenAI-compatible)
A single http provider talks to any endpoint exposing POST /chat/completions — local (Ollama) or cloud (OpenRouter, MiniMax, or any gateway). No extra CLI is needed; it uses Node's built-in fetch.
// Ollama (local, no API key)
{ "ai": { "provider": "http", "baseURL": "http://localhost:11434/v1", "model": "llama3.1" } }
// OpenRouter / MiniMax (cloud — key comes from an env var, never stored in config)
{ "ai": {
"provider": "http",
"baseURL": "https://openrouter.ai/api/v1",
"model": "openrouter/auto",
"apiKeyEnv": "OPENROUTER_API_KEY"
} }apiKeyEnv names the environment variable holding your key (export OPENROUTER_API_KEY=…); the key itself is never written to .sfdt/config.json. Optional headers (object) and timeoutMs (default 300000) are also supported.
Because an HTTP model can't read files or run git/sf itself, sfdt pre-gathers the needed context (git history, test results) and injects it into the prompt for agentic commands. Note: with a cloud endpoint, prompt content (diffs, git logs) is transmitted to that service — sensitive values are redacted before sending.
Disabling AI
Set features.ai to false to disable all AI prompts. Heuristic fallbacks still run in sfdt explain.
{ "features": { "ai": false } }Web Dashboard (sfdt ui)
sfdt ui starts a local Express server and opens the SFDT dashboard in your browser:
sfdt ui # opens http://localhost:7654
sfdt ui --port 8080 # custom port
sfdt ui --no-open # start server without opening browserThe dashboard has 25 pages, grouped in the sidebar. The authoritative list is
generated/gui-pages.json:
- Observe — Dashboard, Drift, Org Audit, Org Monitor, Test Runs, Coverage, Logs
- Analyze — Compare, Scan, Preflight, Manifests, Quality, Agent Test, Pull, Review, Explain, Flow Intelligence, Dependency Graph, Scratch Orgs, Data Sets, Documentation
- Release — Release Hub, Retrofit
- Config — Notifications, Settings
Most pages both read the latest snapshot and can trigger the underlying command directly from the UI.
The dashboard reads log files from the project's configured logDir (defaults to <project>/logs). Data appears automatically after running sfdt test, sfdt preflight, or sfdt drift when those commands write JSON result files.
Build the GUI (required after cloning from source):
npm run build:guiThe pre-built gui/dist/ is included in the published npm package so end users don't need to build it.
Plugin Architecture
Extend sfdt with custom subcommands by creating a plugin.
npm package plugin
Create a package named sfdt-plugin-<name> and publish it to npm:
// sfdt-plugin-my-thing/index.js
export function register(program) {
program
.command('my-thing')
.description('My custom command')
.action(async () => {
console.log('Hello from my-thing!');
});
}Install it in your Salesforce project:
npm install --save-dev sfdt-plugin-my-thingsfdt auto-discovers all sfdt-plugin-* packages in your project's node_modules/ on startup — no config required.
Local plugin
Drop a .js file in .sfdt/plugins/:
// .sfdt/plugins/custom-deploy.js
export function register(program) {
program.command('custom-deploy').action(async () => { ... });
}Explicit plugin list
List plugin package names in config.plugins to load them in a specific order:
{ "plugins": ["sfdt-plugin-my-thing", "@myorg/sfdt-plugin-audit"] }GitHub Action
The repository doubles as a composite GitHub Action: run any sfdt command as a
single uses: step, with org authentication handled for you. Pinning the
action tag pins the CLI version (cli-version: auto installs the version
shipped at that ref — no unpinned @latest).
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Smart delta validation
uses: scoobydrew83/sfdt@v0
with:
args-json: '["deploy","--smart","--org","ci","--delta-base","origin/${{ github.event.pull_request.base.ref }}","--dry-run"]'
auth-method: sfdx-url
sfdx-auth-url: ${{ secrets.SFDX_AUTH_URL }}
org-alias: ciargs-json (a JSON array of arguments) is executed with no shell, so values can
never be interpreted as shell syntax. The legacy command string input still
works for shell-neutral strings but is deprecated; strings needing quoting or
expansion additionally require allow-shell-command: 'true' (trusted input only).
JWT bearer flow instead of an auth URL:
auth-method: jwt
consumer-key: ${{ secrets.SFDX_CONSUMER_KEY }}
jwt-secret-key: ${{ secrets.SFDX_JWT_SECRET_KEY }}
username: ${{ secrets.SFDX_USERNAME }}sfdt ci init --provider github --runner action generates complete workflows
built on the action (monitor, deploy, release). The floating v0 tag tracks
the newest stable release; pin an exact tag (e.g. @v0.16.2) or a commit SHA
if you prefer immutable references. Always pass secrets as ${{ secrets.X }}
expressions, and never feed untrusted input to command — it runs as a shell
command line.
Docker
An official Docker image is available for CI/CD pipelines. It ships Node 22, Salesforce CLI, git, bash, and jq.
Build from source:
docker build -t sfdt .Run against a mounted project:
docker run --rm \
-v "$(pwd):/project" \
-e SFDX_AUTH_URL="$SFDX_AUTH_URL" \
sfdt deployGitHub Actions example:
- name: Deploy
run: |
docker run --rm \
-v "${{ github.workspace }}:/project" \
-e SF_ORG_INSTANCE_URL="${{ secrets.SF_ORG_INSTANCE_URL }}" \
sfdt deploy --skip-preflightInstall the selected AI provider CLI in the container when using AI features:
docker run --rm -v "$(pwd):/project" \
sfdt explain --latestPull Groups
Pull groups let you define named sets of metadata types in .sfdt/pull-config.json:
{
"metadataTypes": [
"ApexClass",
"ApexTrigger",
"LightningComponentBundle",
"CustomObject",
"CustomField",
"Layout",
"FlexiPage",
"PermissionSet",
"Flow"
],
"targetDir": "force-app/main/default"
}Use sfdt pull to retrieve all configured metadata types from the default org.
Pull Cache
sfdt pull uses a SQLite cache (stored in .sfdt/cache/) to track retrieved components and their modification dates. On subsequent runs, only components that have changed in the org are re-fetched, significantly reducing retrieval time for large orgs.
Cache behavior is controlled via pullCache in .sfdt/config.json:
{
"pullCache": {
"enabled": true,
"parallelism": 5,
"batchSize": 100
}
}| Flag | Description |
|------|-------------|
| sfdt pull | Incremental pull — only retrieves changed components |
| sfdt pull --full | Force full retrieve and rebuild the cache |
| sfdt pull --status | Show cache status (last pull time, component counts) |
Requirements
- Node.js >= 22.15.0 (uses the built-in
node:sqlitemodule, unflagged in Node 22.15) - Salesforce CLI (
sf) installed and authenticated to target orgs - bash 4.0+ (macOS users:
brew install bash) - jq 1.6+ (required by several shell scripts)
- Optional: Claude Code CLI for AI features with the
claudeprovider - Optional: Gemini CLI or Codex CLI for AI features with the
geminioropenaiprovider - Optional: an OpenAI-compatible HTTP endpoint (Ollama, OpenRouter, MiniMax, …) for AI features with the
httpprovider — no extra CLI required - Optional: GitHub CLI (
gh) for PR creation during deployments
Development
git clone https://github.com/scoobydrew83/sfdt.git
cd sfdt
npm install
npm link # makes `sfdt` available globally from your checkout
# Build the web dashboard
npm run build:gui
# Run tests
npm test
# Lint
npm run lintAfter npm link, the sfdt command points to your local checkout.
Surface catalogs
generated/ holds machine-generated catalogs of every public surface — commands, MCP tools, GUI
pages, VS Code commands, Chrome features, CI capabilities, and the parity matrix. Code is
authoritative; the catalogs are derived and checked in. Never edit generated/* by hand.
If you add or change a public surface (a command, a flag, an MCP tool, a GUI page), regenerate and commit the diff:
npm run generate:catalogs # rewrite generated/* from the code
npm run check:all-contracts # what CI runs — fails on driftCI fails on catalog drift, so a surface change without a regenerated catalog will not merge.
Contributing
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/your-feature) - Make your changes with tests
- Run the test suite (
npm test) and linter (npm run lint) - Commit with a descriptive message
- Push to your fork and open a Pull Request
Please ensure all tests pass and linting is clean before submitting.
Security
To report a vulnerability, use GitHub's private security advisory feature rather than opening a public issue. See SECURITY.md for the full policy.
