<div align="center">

<img src=".github/assets/logo.png" alt="SFDT logo" width="96" height="96" />

# 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](https://marketplace.visualstudio.com/items?itemName=sfdt.sfdt-devtools).

📖 **[Read the docs at sfdt.dev →](https://sfdt.dev/)**  ·  [npm](https://www.npmjs.com/package/@sfdt/cli)  ·  [Usage guide](docs/USAGE.md)

[![npm version](https://img.shields.io/npm/v/@sfdt/cli.svg)](https://www.npmjs.com/package/@sfdt/cli)
[![npm downloads](https://img.shields.io/npm/dm/@sfdt/cli.svg)](https://www.npmjs.com/package/@sfdt/cli)
[![VS Code](https://img.shields.io/badge/VS%20Code-Marketplace-blue?logo=visualstudiocode)](https://marketplace.visualstudio.com/items?itemName=sfdt.sfdt-devtools)
![Open VSX Version](https://img.shields.io/open-vsx/v/sfdt/sfdt-devtools)
[![CI](https://github.com/scoobydrew83/sfdt/actions/workflows/ci.yml/badge.svg)](https://github.com/scoobydrew83/sfdt/actions/workflows/ci.yml)
[![CodeQL](https://github.com/scoobydrew83/sfdt/actions/workflows/codeql.yml/badge.svg)](https://github.com/scoobydrew83/sfdt/actions/workflows/codeql.yml)
[![license](https://img.shields.io/npm/l/@sfdt/cli.svg)](https://github.com/scoobydrew83/sfdt/blob/main/LICENSE)
[![node](https://img.shields.io/node/v/@sfdt/cli.svg)](https://nodejs.org)

</div>

## 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 `packageDirectories` with `--package` and `--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 as `sf 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](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](extension/README.md) and [extension/PRIVACY.md](extension/PRIVACY.md). | Published to the Chrome 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](vscode/README.md). | [Published to the VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=sfdt.sfdt-devtools) |
| **`@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

```bash
npm install -g @sfdt/cli
cd your-salesforce-project
sfdt init
sfdt deploy
```

### Other install methods

```bash
# 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-run
```

> The `@sfdt/plugin` package is a thin wrapper over `@sfdt/cli` (it shells out to
> the same binary), so every command, flag, and `--json` output is identical to
> running `sfdt` directly. `--json` commands emit a Salesforce-native
> `{ status, result, warnings }` envelope. As an unsigned third-party plugin, `sf`
> shows 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):**

```bash
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 dashboard
```

Full install reference — every method plus CI usage — at **[sfdt.dev/cli/installation](https://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](https://sfdt.dev/cli/commands)**. Locally, `sfdt --help` (and
`sfdt <command> --help`) is always current. The machine-readable inventory is
[`generated/commands.json`](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.xml` from 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 over `sf 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** — `audit` and `monitor` check 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 org
```

### config.json

```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` / `timeoutMs` apply only to the `http` provider; CLI providers (`claude`/`gemini`/`openai`) ignore them. See [AI Features](#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](https://www.npmjs.com/package/@anthropic-ai/claude-code):

```bash
npm install -g @anthropic-ai/claude-code
```

```json
{ "ai": { "provider": "claude" } }
```

Claude's interactive mode lets AI commands read your repository files directly with tool use.

### Gemini

Requires the Gemini CLI:

```bash
npm install -g @google/gemini-cli
```

```json
{ "ai": { "provider": "gemini", "model": "" } }
```

Authentication and model selection are handled by the Gemini CLI.

### OpenAI

Requires the Codex CLI:

```bash
npm install -g @openai/codex
```

```json
{ "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`.

```jsonc
// 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`.

```json
{ "features": { "ai": false } }
```

## Web Dashboard (`sfdt ui`)

`sfdt ui` starts a local Express server and opens the SFDT dashboard in your browser:

```bash
sfdt ui                   # opens http://localhost:7654
sfdt ui --port 8080       # custom port
sfdt ui --no-open         # start server without opening browser
```

The dashboard has 25 pages, grouped in the sidebar. The authoritative list is
[`generated/gui-pages.json`](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):

```bash
npm run build:gui
```

The 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:

```javascript
// 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:

```bash
npm install --save-dev sfdt-plugin-my-thing
```

sfdt 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/`:

```javascript
// .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:

```json
{ "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`).

```yaml
- 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: ci
```

`args-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:

```yaml
    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:**

```bash
docker build -t sfdt .
```

**Run against a mounted project:**

```bash
docker run --rm \
  -v "$(pwd):/project" \
  -e SFDX_AUTH_URL="$SFDX_AUTH_URL" \
  sfdt deploy
```

**GitHub Actions example:**

```yaml
- name: Deploy
  run: |
    docker run --rm \
      -v "${{ github.workspace }}:/project" \
      -e SF_ORG_INSTANCE_URL="${{ secrets.SF_ORG_INSTANCE_URL }}" \
      sfdt deploy --skip-preflight
```

Install the selected AI provider CLI in the container when using AI features:

```bash
docker run --rm -v "$(pwd):/project" \
  sfdt explain --latest
```

## Pull Groups

Pull groups let you define named sets of metadata types in `.sfdt/pull-config.json`:

```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`:

```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:sqlite` module, 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](https://www.npmjs.com/package/@anthropic-ai/claude-code) for AI features with the `claude` provider
- **Optional:** Gemini CLI or Codex CLI for AI features with the `gemini` or `openai` provider
- **Optional:** an OpenAI-compatible HTTP endpoint (Ollama, OpenRouter, MiniMax, …) for AI features with the `http` provider — no extra CLI required
- **Optional:** [GitHub CLI](https://cli.github.com/) (`gh`) for PR creation during deployments

## Development

```bash
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 lint
```

After `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:

```bash
npm run generate:catalogs   # rewrite generated/* from the code
npm run check:all-contracts # what CI runs — fails on drift
```

CI fails on catalog drift, so a surface change without a regenerated catalog will not merge.

## Contributing

Contributions are welcome! Please follow these steps:

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/your-feature`)
3. Make your changes with tests
4. Run the test suite (`npm test`) and linter (`npm run lint`)
5. Commit with a descriptive message
6. 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](https://github.com/scoobydrew83/sfdt/security/advisories/new) rather than opening a public issue. See [SECURITY.md](SECURITY.md) for the full policy.

## License

[Apache-2.0](LICENSE)
