npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nervill/metadelta

v0.25.0

Published

Salesforce CLI plugin to find recent metadata changes in an org

Readme

Last update / Última actualización: 2026-10-21 — @nervill/metadelta 0.25.0

Metadelta Salesforce CLI Plugin

English

Metadelta is a custom Salesforce CLI plugin that offers thirteen complementary command families:

  • sf metadelta find inspects a target org and reports metadata components modified by a specific user within a recent time window, optionally generating manifest files for deployment or Vlocity datapack migration. When it writes package.xml, the command stamps the file with the API version detected from the target org.
  • sf metadelta orgApiVersion prints the API version reported by a target org and is used internally by commands that need to align generated manifests with the org.
  • sf metadelta finddelta compares two Git branches and generates delta manifests under manifest/ for Salesforce Core (.xml) and Vlocity (.yaml), including destructive manifests when complete deletions are detected. ApexClass destructive entries require both .cls and .cls-meta.xml to be deleted, and Vlocity destructive entries require the entire datapack folder to be absent from the source branch. It can also merge missing components into existing manifests with --xml and --yaml without duplicating entries.
  • sf metadelta postvalidate re-retrieves the manifests you deployed (Core package.xml and/or Vlocity YAML), downloads the corresponding components into a temporary folder, and compares them to your local sources with a colorized diff table.
  • sf metadelta access exports aliases, captures encrypted auth URLs, and restores secure org access across Windows/Linux/WSL with an MFA checkpoint.
  • sf metadelta security users reads a security master matrix plus a target users list, resolves required IDs in the org, generates bulk-ready CSV files for role/PSG/group assignments, and can optionally apply changes via Bulk API or generate a current-state validation matrix via --validate, and compare that file against the master matrix locally via --compare.
  • sf metadelta initspace bootstraps a local Salesforce workspace by creating the base folder tree and seed project files required by this plugin.
  • sf metadelta monitor run starts a terminal monitor for Salesforce Core and Vlocity metadata drift using local filesystem snapshots, a local Git diff engine, optional XML/YAML scoped manifests, Vlocity modifier enrichment, a persistent JSONL change log, and optional CSV export.
  • sf metadelta task record and sf metadelta task play record/play Playwright-based Salesforce tasks with automatic recovery stabilizers, patched .metadelta.* playback, and orchestrated diagnostics.
  • sf metadelta cleanps extracts a focused copy of a permission set by keeping only the entries that match a fragment or appear in a curated allowlist.
  • sf metadelta findtest reviews Apex classes inside a local SFDX project, confirms the presence of their corresponding test classes, and can validate existing package.xml manifests prior to a deployment. Generated or updated manifests inherit the API version reported by the target org when available.
  • sf metadelta manual collect aggregates manual-step markdown documents stored under docs/, renders a consolidated index/banner per story, and offers a sprint-aware mode that only includes the files still pending merge into the base branch.
  • sf metadelta merge scans manifest XML files whose names contain a given substring, deduplicates their metadata members, and builds a consolidated globalpackage.xml (or a custom output filename).

Created by Nerio Villalobos ([email protected]).

Index

Installation

  1. Install the Salesforce CLI (requires version 2.102.6 or later):

    npm install --global @salesforce/[email protected]
  2. Install the latest published version from npm using the Salesforce CLI (recommended):

    sf plugins install @nervill/metadelta

    To install this exact release instead, pin the version:

    sf plugins install @nervill/[email protected]

    npmjs.com displays npm i @nervill/metadelta as the generic Node.js package command. Use sf plugins install so the package is registered as a Salesforce CLI plugin.

    Confirm installation with sf plugins, which should list @nervill/metadelta 0.25.0.

  3. Alternatively, install the current repository version directly from GitHub:

    sf plugins install github:NerioVillalobos/plugin-metadelta.git

    Confirm installation with sf plugins, which should list @nervill/metadelta 0.25.0.

    Metadelta plugin installation example

  4. (Optional, for local development) Clone this repository and install dependencies:

    git clone <repo-url>
    cd plugin-metadelta
    npm install
  5. Link the plugin to your local Salesforce CLI:

    npm run build
    sf plugins link .

    Confirm installation with sf plugins, which should list @nervill/metadelta 0.25.0 (link).


find command

Explanation

The plugin compares metadata changes for the specified user and prints a table of modified components. When requested, it also produces manifest files under the manifest/ directory.

Usage

Run the command from any directory after linking:

sf metadelta find --org <alias_or_username> [flags]

Flags

| Flag | Description | Default | |------|-------------|---------| | --org, -o | Required. Alias or username of the target org. | N/A | | --metafile | Path to a JSON file listing the metadata types to override the default selection. | Built‑in list | | --days | Number of days in the past to inspect for modifications. | 3 | | --namespace | Vlocity namespace to query datapacks (enables Vlocity datapack checks). | None | | --xml | When set, generates manifest/package-<branch_or_org>[-v#].xml containing found metadata. The resulting file uses the API version fetched from the specified org when available. | false | | --yaml | When set, generates manifest/package-vlocity-<branch_or_org>[-v#].yaml with Vlocity datapack entries. | false | | --audit | Full name of the user to audit. If omitted, the command uses the org user associated with the provided alias. | Authenticated user |

Using a custom metadata file

By default, the command builds its metadata type list by running sf force:mdapi:describemetadata --target-org so it stays synchronized with the connected org. If the describe call fails, a built-in fallback list is used. The resulting list is further filtered to include only types that expose both lastModifiedByName and lastModifiedDate, avoiding unnecessary queries. A maximum of five metadata types are processed in parallel to limit resource usage.

Some orgs advertise ContentWorkspace through metadata describe but reject the fields used by sf org list metadata for that type. find skips this non-queryable type so the remaining metadata audit continues without losing results.

The --metafile flag allows you to override the built‑in metadata list. Provide a JSON (.json) file that either contains a top-level array or an object with a metadataTypes array. The file must contain plain JSON (no module.exports = wrappers) and use UTF-8 encoding.

Create a file—for example mismetadatos.json—with the following content:

{
  "metadataTypes": [
    "Bot", "BotVersion", "CustomPermission", "FlexiPage", "Flow",
    "GenAiFunction", "GenAiPlanner", "GenAiPlugin", "GenAiPlannerBundle",
    "PermissionSet", "Profile", "StaticResource", "PermissionSetGroup"
  ]
}

Minimal example using an array:

[
  "ApexClass",
  "Flow"
]

Reference the file when running the command (prefix with ./ when the file lives in the current folder):

sf metadelta find --org myOrg --metafile ./mismetadatos.json

Tip: If you previously used a .js file with module.exports, rename it to end with .json and remove the assignment wrapper so only the JSON structure remains.

Note: When the path to your metafile contains spaces or special characters, wrap it in quotes (for example, --metafile "./metadata lists/mismetadatos.json").

Examples

  • Basic scan for the default user:
    sf metadelta find --org myOrg
  • Audit a different user for the last seven days and create a package.xml:
    sf metadelta find --org myOrg --audit "Jane Doe" --days 7 --xml
  • Check Vlocity datapacks with a custom namespace and output a Vlocity package file:
    sf metadelta find --org myOrg --namespace myns --yaml
    Metadelta plugin find command example

Output

The find command prints each matching component with its type, full name, last modified date, and modifier. When --xml or --yaml are set, the corresponding manifest files are created inside the manifest/ directory. If the command runs inside a Git repository, the manifest filename uses the current branch name; otherwise it falls back to the provided org alias. Existing files are preserved by adding incremental -v1, -v2, … suffixes.


orgApiVersion command

Explanation

Print the API version reported by a target org:

Usage

sf metadelta orgApiVersion --org <alias_or_username>

The command runs sf org display --target-org <alias> --json, extracts result.apiVersion, and prints only the version value. It is also used internally by commands that need to align generated manifests with the org API version.

Flags

| Flag | Description | Default | |------|-------------|---------| | --org, -o | Required. Alias or username of the target org. | N/A |

Metadelta plugin orgApiVersion command example

finddelta command

Explanation

Generate delta manifests by comparing two branches:

Usage

sf metadelta finddelta --from <source_branch> --to <base_branch> [--xml manifest/Release.xml] [--yaml manifest/vlocity.yaml]

Characteristics

  1. Runs a NUL-safe three-dot diff from the merge-base (git diff --name-status -z <to>...<from>) to detect additions, deletions, and renames without losing accented paths or unrelated changes from the base branch.
  2. Generates Core and Vlocity delta manifests under manifest/ using the from branch as the output name.
  3. Creates destructive manifests automatically when complete deletions exist.
  4. If --xml and/or --yaml are provided, merges only missing components into the destination manifests (no duplicates).
  5. Adds required parent files for decomposed metadata such as CustomObjectTranslation before resolving the temporary source tree.
  6. If the global Core resolver fails, retries independently by component so valid groups are preserved. Any unresolved group makes the delta incomplete, returns a non-zero exit code, and prevents --xml from being modified.
  7. Restricts Core candidates to the packageDirectories declared by the --from branch and applies that branch's .forceignore. Vlocity candidates under Vlocity/ remain eligible even when that directory is outside the Salesforce package directories.

Notes

Destructive hardening:

  • ApexClass entries are emitted only when both the .cls file and its .cls-meta.xml companion were deleted.
  • Vlocity entries are emitted only when the full Vlocity/<DataPackType>/<DataPackName> folder has no remaining files in the from branch.

Core outputs:

  • manifest/<from>.xml
  • manifest/Destructive-<from>.xml (only when needed)

Vlocity outputs:

  • manifest/<from>.yaml
  • manifest/Destructive-<from>.yaml (only when needed)

Flags

| Flag | Description | |------|-------------| | --from | Required. Source branch (typically the PR branch). | | --to | Required. Base branch for comparison. | | --xml | Existing destination package.xml to update with missing Core components. | | --yaml | Existing destination YAML manifest to update with missing Vlocity components. | | --strict | Fails when metadata candidate files cannot be resolved to Core/Vlocity components. | | --all-paths | Disables packageDirectories and .forceignore filtering for compatibility with the previous repository-wide behavior. | false |

By default, files outside the Salesforce package directories are reported as ignored and do not enter the Core manifests. Renames crossing the project boundary are converted into the applicable addition or deletion. Use --all-paths only when the repository intentionally treats its entire tree as deployable.

Metadelta plugin find command example

postvalidate command

Explanation

Validates a deployment by re‑retrieving the manifests you used (XML for Salesforce Core and/or YAML for Vlocity) into a temporary folder, comparing the downloaded files against your local sources, and rendering a colorized Component | Name | Diff table with ✓ for matches and ✗ for differences.

Characteristics

  1. Creates a temporary retrieve directory and hides the raw command output behind a spinner while the retrieves run.
  2. For Salesforce Core (--xml), runs sf project retrieve start --manifest <xml> --target-org <org> --output-dir <tempDir>.
  3. For Vlocity (--yaml), runs vlocity --sfdx.username <org> -job <yaml> packExport --maxDepth 0 into the same temp directory.
  4. Maps retrieved Core files back to your repo using sfdx-project.json package directories (including main/default), and datapacks against the directory you pass in --vlocity-dir (default Vlocity).
  5. Compares folder-to-folder ignoring whitespace, blank lines, XML/JS/YAML comments, Vlocity GlobalKey lines, and skips noise files like VlocityBuildErrors.log, VlocityBuildLog.yaml, and the vlocity-temp/ directory.
  6. Prints a box-style table with colored headers and status symbols, then deletes the temporary folder.

Flags

| Flag | Description | Default | |------|-------------|---------| | --xml | Path to the package.xml used for the Core deployment. Requires --org. | None | | --yaml | Path to the Vlocity manifest used for the datapack deployment. Requires --org. | None | | --org, -o | Alias or username for both Core and Vlocity retrieves. | CLI default | | --vlocity-dir | Local folder that stores your datapacks. Also probed when manifests contain a Vlocity/ prefix. | Vlocity |

Provide at least one manifest (--xml or --yaml). When both are present, the retrieves share the same temp folder and a single comparison pass.

Examples

  • Core only:
    sf metadelta postvalidate --xml manifest/SP1.2.11.0.xml --org SFOrg-prod
  • Vlocity only from a custom folder:
    sf metadelta postvalidate --yaml manifest/vlo-manifest.yaml --org SFOrg-Demo02 --vlocity-dir Vlocity
  • Core + Vlocity in one run:
    sf metadelta postvalidate --xml manifest/package.xml --yaml manifest/vlocity.yaml --org my-env --vlocity-dir Vlocity

Run the command from the Salesforce project root so Core retrieves line up with your packageDirectories structure. Datapacks are resolved relative to the current directory first and then to --vlocity-dir.


access command

Explanation

Metadelta Access is an Org Access Replication Tool with applied security controls. It automates a formerly manual process to export aliases, protect auth URLs, and restore org access across machines with MFA + passphrase encryption.

Use Metadelta Access to transfer org login access securely between machines:

Usage

sf metadelta access --all --output docs

Characteristics

  1. --all or --prefix <text> creates <output>/<name>/accessbackup.dat with connected aliases and usernames and also creates accessbackup.dat.mfa. During this step, the command tries to print an ASCII QR in the terminal (when Python qrcode is available); it always prints Secret + URI as fallback.
  2. --capture <folder> asks for MFA + passphrase, reads each alias auth URL with sf org display --verbose, encrypts it, and rewrites accessbackup.dat with encrypted payloads. Starting in v0.11.4, Metadelta injects SF_TEMP_SHOW_SECRETS=true only for this internal Salesforce CLI call so the automated capture receives the real sfdxAuthUrl even when recent Salesforce CLI versions redact secrets by default; users do not need to export the variable manually.
  3. --addaccess <folder> asks for MFA + passphrase, decrypts each entry, and restores auth using sfdx auth:sfdxurl:store -f <file> -a <alias> (fallback: sf org login sfdx-url when available).

Important: --addaccess only works after --capture has encrypted the file. If accessbackup.dat still contains alias;username rows, run capture first. Usage reminder: pass the folder as the value of the flag, for example sf metadelta access --addaccess docs/FolderName (do not duplicate the flag).

The command is implemented in Node.js only (no Python runtime/dependencies), so it works the same on Windows, Linux, and WSL as long as Salesforce CLI is installed.

Platform requirements (Windows / macOS / Linux / WSL)

To run sf metadelta access reliably, ensure the following prerequisites are available:

  1. Salesforce CLI
    • Required on all platforms.
    • Verify with:
      sf --version
  2. Authenticated org session(s)
    • Export/capture depends on active org sessions in your local CLI auth store.
    • Verify with:
      sf org list
  3. Node.js environment compatible with this plugin
    • The plugin requires Node.js 18+ (as declared in package.json).
  4. Legacy sfdx binary (recommended for replication restore)
    • Primary restore command uses sfdx auth:sfdxurl:store.
    • If unavailable, the command attempts sf org login sfdx-url fallback.
  5. Optional ASCII QR rendering dependency
    • If Python + qrcode module exists, the command prints an ASCII QR in terminal during MFA creation.
    • Without it, Secret + URI are still printed and can be entered manually in your authenticator app.

Platform notes:

  • Windows (PowerShell/CMD): keep Salesforce CLI binaries available in PATH and prefer running from a regular user terminal with profile initialization enabled.
  • macOS/Linux: ensure sf (and optionally sfdx) resolve from the same shell session where you run the plugin.
  • WSL: if mixing Windows and WSL auth contexts, validate where your CLI auth store is located and run export/restore in the same environment when possible.

Responsibility and security notice

By using metadelta access and all other commands in this plugin, you acknowledge that:

  • You are responsible for complying with your organization’s security policies.
  • You are responsible for protecting MFA secrets, passphrases, backup files, and generated auth artifacts.
  • You should only run these commands in trusted environments and with authorized org access.
  • The maintainers/authors are not responsible for misuse, credential leakage, or operational impact caused by incorrect handling.

Use the tool carefully, rotate credentials when needed, and treat backup files as sensitive secrets.


security users command

Explanation

Use this command to transform a security matrix into actionable Bulk API files for a target org:

Usage

sf metadelta security users --master data/master.csv --target-users data/target-users.csv --org myOrg

This workflow mirrors the original Python utility and is designed for controlled migrations of user access models.

Characteristics

  1. Reads the master matrix (--master) and the target users file (--target-users).
  2. Resolves org IDs by querying User, UserRole, PermissionSetGroup, and Group via Salesforce CLI.
  3. Builds these output files under --output-dir (default out):
    • user_role_updates.csv
    • permissionsetassignment_insert.csv
    • groupmember_insert.csv
    • validation_errors.csv
  4. Runs as dry-run by default (only generates files).
  5. When --apply is present, executes the corresponding bulk operations:
    • sf data update bulk -s User
    • sf data import bulk -s PermissionSetAssignment
    • sf data import bulk -s GroupMember
  6. When --validate is present, the command does not apply changes and instead generates validation_current_matrix.csv with one row per target user and current values for RoleName, PermissionSetGroup, PublicGroupPuesto, PublicGroupSegmento, and Queues.
  7. When --compare is present, no org connection is required: the command compares --file-validation against --master locally and outputs only users with differences in comparison_mismatches.csv using compact annotations: <value> means missing in user (present in master), and =value= means extra in user (not present in master).

Input

  • --master must include columns like: RoleName, PermissionSetGroup, PublicGroupPuesto, PublicGroupSegmento, Queues.
  • PermissionSetGroup, PublicGroupSegmento, and Queues support multiple values separated by |.
  • --target-users should include at least: Username, RoleName.

Flags

| Flag | Description | Default | |------|-------------|---------| | --master | Required. Master security matrix CSV. | N/A | | --target-users | Required. CSV with users to process. | N/A | | --org, -o | Required. Alias/username of the target org. | N/A | | --output-dir | Output directory for generated CSV files. In --validate/--compare, if you pass a value different from out, the command creates/uses <output-dir>/out. | out | | --apply | Applies generated operations through Bulk API. Cannot be combined with --validate. | false | | --validate | Generates validation_current_matrix.csv (current values per target user) without applying changes. | false | | --compare | Compares --file-validation vs --master locally and exports only users with differences to comparison_mismatches.csv in compact format (<value> missing, =value= extra). Cannot be combined with --apply or --validate. | false | | --file-validation | Path to validation_current_matrix.csv used by --compare. | None |

Examples

  • Dry run (generate files only):
    sf metadelta security users --master ./data/master.csv --target-users ./data/users.csv --org my-org
  • Apply mode (execute bulk operations):
    sf metadelta security users --master ./data/master.csv --target-users ./data/users.csv --org my-org --apply
  • Validate mode (export current assignments in matrix format):
    sf metadelta security users --master ./data/master.csv --target-users ./data/users.csv --org my-org --validate --output-dir ./reports
  • Compare mode (local comparison without org connection):
    sf metadelta security users --master ./data/security_master_matrix.csv --file-validation ./reports/out/validation_current_matrix.csv --compare --output-dir ./reports

initspace command

Explanation

Create the recommended workspace scaffold in your current directory:

Usage

sf metadelta initspace

Characteristics

  • Folders:
    • force-app/main/default
    • docs
    • data
    • manifest
    • scripts
  • Root files:
    • .gitignore (Metadelta template)
    • sfdx-project.json (sourceApiVersion 66.0)
    • package.xml (Metadata API version 66.0)

initspace is idempotent for directories (safe to re-run) and rewrites the three root files so they stay aligned with the plugin defaults.


monitor run command

Explanation

Starts a persistent terminal monitor for Salesforce Core and Vlocity metadata drift:

Usage

sf metadelta monitor run --org DEV

Flags

| Flag | Description | Default | | --- | --- | --- | | --org, -o | Alias or username of the target org. Required for the normal monitor mode. | Required unless --control or --watchdog-once is used | | --interval | Refresh interval in minutes. Values below 1 are normalized to 1. | 5 | | --scope | Metadata source to monitor: all, salesforce, or vlocity. | all | | --scope-xml | Path to a Salesforce Core package.xml. When present, the monitor retrieves and watches only the Core components listed in that manifest. | None | | --scope-yaml | Path to a Vlocity YAML job/manifest. When present, the monitor exports and watches only the DataPacks listed in that file. | None | | --export-csv | Path where the persistent change-log.jsonl should be exported as CSV when the monitor exits. The path is resolved from the directory where the command was started. | None | | --control | Opens the portable Metadelta Monitor control menu for watchdog targets and monitor launches. | false | | --watchdog-once | Runs one Teams watchdog cycle using change-log.jsonl targets from the watchdog config, then exits. | false | | --watchdog-config | Path to the watchdog config JSON. | ~/.metadelta/monitor/watchdog.config.json | | --teams-webhook-url | Microsoft Teams webhook URL for --watchdog-once. Prefer METADELTA_TEAMS_WEBHOOK_URL for secrets. | Environment/config | | --once | Run one refresh cycle and exit. Useful for validation. | false |

The monitor stores its runtime state under ~/.metadelta/monitor/<orgAlias>/. Inside that folder it maintains current/, manifest/, temp/, the local-only Git repository used as the diff engine, and refreshes every five minutes. NEXT shows the exact next refresh time instead of repainting a countdown, and RETRIEVE shows the last Salesforce Core + Vlocity retrieve/export duration. The first cycle creates the baseline and shows STATUS: BASELINE CREATED; later refreshes show added, modified, deleted, or renamed files. Full errors and Vlocity warnings are wrapped in a detail section and automatically pause UI repainting so the text can be selected/copied.

For Salesforce Core changes, the monitor queries the org metadata APIs to enrich each row with LastModifiedBy.Name and LastModifiedDate. For Vlocity changes, it now shares the Vlocity DataPack query catalog used by sf metadelta find, so paths such as vlocity/Promotion/<GlobalKey>/... are resolved against the parent DataPack record by Name, Id, or namespaced GlobalKey__c when available. This improves the modifier shown in the dashboard and in the change log instead of falling back to N/A for many Vlocity DataPack files.

When --scope-xml or --scope-yaml is present, the dashboard scope label reflects the custom scope: SALESFORCE-CUSTOM, VLOCITY-CUSTOM, or ALL-CUSTOM. The XML/YAML paths can use any filename and are resolved relative to the directory where you start the command, before the monitor changes into ~/.metadelta/monitor/<orgAlias>/.

Examples

Examples for scoped monitoring:

sf metadelta monitor run --org DEV --scope-xml manifest/core.xml
sf metadelta monitor run --org DEV --scope-yaml manifest/vlocity.yaml
sf metadelta monitor run --org DEV --scope-xml manifest/core.xml --scope-yaml manifest/vlocity.yaml

The UI has two navigable sections. SALESFORCE CORE / VLOCITY groups changes by metadata type and shows the count, latest change date, and latest modifier for each type. RECENT CHANGES lists the session-cumulative component changes with the most recently detected items first. The arrow keys move the > selector across both sections; when the selected row moves past the visible terminal area, the list scrolls so the selector remains visible. Press Enter or d on an individual change to see its file, metadata query, modifier, detection time, and Git diff summary. Press Enter or d on a metadata type to open TYPE DETAILS, which lists the changed components for that type in recent-first order.

Press p to pause/resume, r to refresh, s for Salesforce only, v for Vlocity only, a for all, and q, x, ESC, CTRL+C, or exit to quit.

For Vlocity-enabled orgs, the default monitor scope runs packExportAllDefault with a temporary job file that includes continueAfterError: true:

sf metadelta monitor run --org DEV --scope vlocity

When the scope is all, a Vlocity export failure does not block Salesforce Core monitoring; the UI keeps the Core diff and shows the Vlocity warning. For orgs without Vlocity CLI installed, use:

sf metadelta monitor run --org DEV --scope salesforce

RECENT CHANGES is cumulative within the active terminal session. Across restarts, the Git baseline and snapshots are preserved under ~/.metadelta/monitor/<orgAlias>/, so the next run continues from the last baseline instead of starting from scratch. A persistent append-only change log is written to ~/.metadelta/monitor/<orgAlias>/change-log.jsonl. It records SESSION_STARTED, CHANGE_DETECTED, and SESSION_ENDED events with the component type, component name, action, detection time, last modified date, and modifier when available. Vlocity files named *_SampleInputJson.json are ignored by the monitor because they are sample payloads and can produce noisy JSON parsing failures.

To export the accumulated persistent log to CSV when the monitor exits, use --export-csv. The export includes session and change events in stable columns such as event, org, scope, source, action, type, component, file, detectedAt, lastModifiedDate, lastModifiedBy, startedAt, endedAt, exitCode, and reason.

sf metadelta monitor run --org DEV --export-csv reports/metadelta-monitor.csv

The complementary watchdog mode reads the persistent change-log.jsonl files and sends Microsoft Teams alerts when a CHANGE_DETECTED event was touched by a user outside the configured DevOps allowlist. It is incremental and stores byte offsets in a local state file, so repeated scheduled executions do not resend already processed events. Put the webhook URL in METADELTA_TEAMS_WEBHOOK_URL or pass it explicitly only in secure local environments.

sf metadelta monitor run --watchdog-once
sf metadelta monitor run --watchdog-once --watchdog-config ~/.metadelta/monitor/watchdog.config.json

The control menu is a portable companion for managing watchdog targets and monitor launches without editing JSON by hand. It can add/remove org targets, configure or clear per-target scopeXml and scopeYaml, change each monitor interval, switch the menu language between English and Spanish, run one watchdog cycle, start monitors in background, use tmux for a multi-monitor TUI when available on Linux/WSL/macOS, and open all monitors as Windows Terminal tabs when wt.exe is available.

sf metadelta monitor run --control

Watchdog target entries can include custom manifests per org:

{
  "org": "Telecentro-qa",
  "logPath": "~/.metadelta/monitor/Telecentro-qa/change-log.jsonl",
  "scopeXml": "/absolute/path/Release.xml",
  "scopeYaml": "/absolute/path/Release.yaml",
  "interval": 8,
  "exportCsv": "~/.metadelta/Telecentro-qa-metadelta-monitor.csv"
}

Monitor persistence, scoped manifests, Vlocity enrichment, CSV export, and watchdog control (v0.16.0): sf metadelta monitor run preserves snapshots, Git baseline, and change-log.jsonl under ~/.metadelta/monitor/<orgAlias>/. Use --scope-xml and/or --scope-yaml to monitor only the components listed in a Core XML or Vlocity YAML manifest. Use --export-csv to produce an audit-friendly CSV copy of the persistent log when the command exits. Use --control and --watchdog-once for the complementary Teams watchdog/control workflow. finddelta bundled metadata fix (v0.17.0): sf metadelta finddelta compares individual members inside CustomLabels.labels-meta.xml, so unchanged custom labels are not incorrectly added to the generated delta manifest. findtest coverage improvements (v0.18.0): sf metadelta findtest detects runnable tests from Apex source annotations, includes manifest tests and source-reference matches, and validates ApexTrigger coverage through handlers or DML. findtest production deployment fix (v0.19.0): sf metadelta findtest detects whether the target org is a sandbox before selecting the fallback test level, omits NoTestRun in production, preserves dry-run validation, and returns a non-zero exit code when the deploy fails. findtest destructive orchestration (v0.22.0): sf metadelta findtest auto-detects the destructive manifest generated by finddelta, validates it before deployment, infers PRE/POST from conventional paths with a safe POST fallback, and passes the correct destructive flag to both dry-run and real deployments. find ContentWorkspace compatibility (v0.23.0): sf metadelta find skips ContentWorkspace when an org advertises it through metadata describe but rejects the fields used by sf org list metadata, allowing the remaining audit types to continue without the INVALID_FIELD: ManageableState warning. finddelta project-boundary filtering (v0.24.0): sf metadelta finddelta reads the source branch sfdx-project.json and .forceignore, limits Core metadata to configured packageDirectories, keeps Vlocity datapacks outside those directories, reports ignored paths, and converts renames across the project boundary into the correct add/delete entries. Use --all-paths only when the legacy unfiltered behavior is required. findtest coverage resolution (v0.25.0): sf metadelta findtest ignores interfaces, enum-only classes, and classes without executable code when checking coverage; resolves one level of indirect class coverage; recognizes type references in initializers, generics, casts, and .class; and supports explicit JSON/CSV mappings through --test-map or .metadelta/findtest-map.json. JSON output now reports the confidence and intermediate class used for each association. finddelta reliability fixes (v0.20.0): sf metadelta finddelta uses merge-base and NUL-safe Git diffs, supports large and binary metadata files, preserves partial bundles as active components instead of destructive deletes, resolves generic metadata companions, and offers --strict for unresolved metadata candidates. finddelta decomposed metadata fix (v0.21.0): sf metadelta finddelta includes required parent metadata for children such as CustomObjectTranslation, retries Core manifest resolution by component, reports unresolved files with a non-zero exit code, and prevents partial --xml merges.


task record / task play command

Explanation

Use task record to capture a Playwright flow and task play to replay it in another org with automatic patching, recovery stabilizers, and orchestrated diagnostics:

Usage

sf metadelta task record --org <alias>
sf metadelta task play --org <alias> --tstname tests/<recorded-file>.ts [--header] [--ai --ai-provider gemini --ai-model <model> --ai-key <key>]

Characteristics

Supported coverage for sf metadelta task play (scope and limits):

  • task play creates a temporary patched file (tests/.metadelta.*) to stabilize recurring Salesforce UI differences.
  • Optional AI hardening (--ai) runs after deterministic patching, uses a constrained AI fragility-analysis plan (targeted hardening, not full-file free rewrite), creates a second derived file (tests/.metadelta.<name>.ai.ts) when safe, supports model override with --ai-model/METADELTA_AI_MODEL, and falls back to the deterministic file if AI is unavailable/invalid.
  • Playback now also includes conservative idempotent guards for supported patterns (checkbox state, toggle state, and safe fill-value matches): when target state is already satisfied the step is skipped; otherwise it runs normally.
  • The command aims to auto-mitigate known recurrent failures first; if mitigation is not possible, it surfaces orchestrator-backed actionable errors instead of generic failures.

Flags

AI and idempotency options (quick reference):

| Option | Purpose | Required | |---|---|---| | --ai | Enables optional AI hardening stage over deterministic patched file. | No | | --ai-provider | AI provider selector (current supported value: gemini). | No (defaults to gemini when --ai is used) | | --ai-model | Overrides Gemini model (e.g. gemini-2.5-flash). | No | | --ai-key | API key passed inline (pipeline-friendly but prefer env secrets). | No (required only when --ai is enabled and no env key exists) |

Notes

Environment variables recognized by AI mode:

  • GEMINI_API_KEY or METADELTA_AI_KEY for credentials.
  • METADELTA_AI_MODEL or GEMINI_MODEL for model override.

How idempotent playback behaves:

  • Supported patterns (checkbox/switch/fill and specific toggle flows) are checked before acting.
  • If target state is confidently already satisfied, the step is skipped with concise log (⏭️ action skipped: already satisfied ...).
  • If state is uncertain, playback does not skip blindly; it keeps conservative execution.

Automatic mitigations currently covered:

  • frontdoor URL vs base origin separation to avoid malformed navigation URLs.
  • Retry flow for transient net::ERR_ABORTED style navigation interruptions.
  • Initial Setup popup recovery when Salesforce delays or redraws the “Setup Opens in a new tab…” menu item before opening the setup tab.
  • Visible-first fallback for ambiguous .slds-checkbox_faux clicks that would otherwise fail in Playwright strict mode.
  • Agentforce Agents recovery with full-page refresh, Setup reopen retries, broader Quick Find terms, and direct Setup-route fallback after enabling Einstein Setup.
  • Popup rebind/reopen handling when a recorded tab/window is closed and reused later.
  • App Launcher fallback path (combobox/placeholder/reopen launcher).
  • Dynamic selectors for Permission Set Assignments ([0], [2], [5], etc.).
  • Action Library waits + scroll handling before selecting actions.
  • Finish-button enablement checks before click.
  • Normalization of dynamic vfFrameId_* and related fragile selectors in recorded tests.

Known limits (not guaranteed by automation only):

  • Org-specific permissions/visibility gaps (apps/actions/records/features).
  • Deep UX/DOM changes not yet modeled by current stabilizers.
  • Missing business data dependencies in the target org.
  • External MFA/session policies that block unattended playback.

Expected reliability:

  • High for recurrent failures already covered by existing stabilizers.
  • Medium for new flows with moderate UI variation.
  • Low for orgs with strong functional divergence (permissions/data/features/layouts).

How we measure coverage:

  • Recommended metric is by recurrent failure families, not code lines.
  • Stabilization coverage = (recurrent failure families with automatic mitigation) / (recurrent failure families observed) × 100.
  • Current technical estimate: ~60%–70% coverage over known recurrent failure families (not all possible Salesforce scenarios).

Comments

Diagnostics + collaboration:

  1. Review test-results/.../error-context.md.
  2. Review .metadelta/metadelta-task-orchestrator.json for suggestions/history.
  3. Re-run with --header for visual evidence.
  4. Open Issues → New issue → task play bug report and complete all required fields.

Please report new failures using the task play bug report template so issues can be triaged publicly and prioritized incrementally.

MetaDelta Natural Task skill

The repository includes skills/metadelta-natural-task, an Agent Skill that turns a Salesforce procedure written in natural language into an inspected, validated, reusable Playwright task for sf metadelta task play.

The skill complements task record and task play; it does not replace them. It uses task record and Playwright inspection as evidence, rebuilds fragile recordings as durable TypeScript tests, checks whether the requested state is already satisfied, performs a no-save dry run, and only persists the change when the user explicitly authorizes the exact mutation.

Prerequisites on the machine that runs the skill:

  • Codex or Claude Code with terminal and workspace access.
  • Salesforce CLI with MetaDelta installed and the target org already authenticated.
  • Node.js and the Playwright runtime required by MetaDelta.
  • Permission to open the target Salesforce org and perform the requested operation.
Install in Codex from GitHub

After this directory is available on the repository's default branch:

python3 "${CODEX_HOME:-$HOME/.codex}/skills/.system/skill-installer/scripts/install-skill-from-github.py" \
  --repo NerioVillalobos/plugin-metadelta \
  --path skills/metadelta-natural-task

To install from a branch or tag that has not been merged into the default branch, add --ref <branch-or-tag>. Restart Codex after installation. Invoke the installed skill by asking Codex to use $metadelta-natural-task and include the org alias, desired outcome, and natural-language UI procedure in the same request.

Install in Claude Code

From a local checkout of this repository, install it for the current user:

mkdir -p ~/.claude/skills/metadelta-natural-task
cp -a skills/metadelta-natural-task/. ~/.claude/skills/metadelta-natural-task/

For project-only use, copy it into the target project instead:

mkdir -p /path/to/project/.claude/skills/metadelta-natural-task
cp -a skills/metadelta-natural-task/. /path/to/project/.claude/skills/metadelta-natural-task/

Start or restart Claude Code and invoke /metadelta-natural-task, followed by the org alias, desired outcome, and procedure. The optional agents/openai.yaml file is used by Codex and is harmless when Claude Code loads the skill. This repository currently distributes the skill as a standalone Agent Skill; installing it through Claude's plugin marketplace would additionally require Claude plugin/marketplace manifests.

Other local installation options

Install a local checkout manually for Codex:

mkdir -p "${CODEX_HOME:-$HOME/.codex}/skills/metadelta-natural-task"
cp -a skills/metadelta-natural-task/. "${CODEX_HOME:-$HOME/.codex}/skills/metadelta-natural-task/"

During skill development, use a symbolic link instead of copying so local repository changes are immediately visible. Choose the destination for the agent you use:

ln -s "$(pwd)/skills/metadelta-natural-task" ~/.codex/skills/metadelta-natural-task
# or
ln -s "$(pwd)/skills/metadelta-natural-task" ~/.claude/skills/metadelta-natural-task

The destination must not already exist before creating the symbolic link. A regular copy is preferable for stable installations; the link is intended for local development.

Usage example

Codex:

Use $metadelta-natural-task for org devNervill. Setup -> Session Settings ->
change Timeout Value to 1 hour -> Save. Inspect the current value first; if it
is already 1 hour, do not change it. Create and validate the durable task, run
the no-save dry run, and apply the change only after my explicit authorization.

Claude Code:

/metadelta-natural-task org devNervill. Setup -> Session Settings -> change
Timeout Value to 1 hour -> Save. Inspect the current value first; if it is
already 1 hour, do not change it. Create and validate the durable task, run the
no-save dry run, and apply the change only after my explicit authorization.

The expected durable result is a test such as tests/<org>-<goal>.ts. Temporary tests/.metadelta.* files produced during playback are execution evidence, not the reusable source file.


cleanps command

Explanation

Generate a trimmed permission-set file with:

Usage

sf metadelta cleanps --permissionset <name> --prefix <fragment> [flags]

The command locates the default package directory declared in sfdx-project.json, reads the matching permission-set XML under <packageDir>/main/default/permissionsets, and produces a filtered copy inside <project-root>/cleanps/ (the folder is created automatically when missing).

Characteristics

  1. Prefix-driven matches. Every candidate entry is evaluated against the fragment provided through --prefix. If any relevant value (such as the object name, record type, or tab API name) contains that fragment, the entire node is kept.
  2. Allowlist overrides. When you pass --exclude <file>, the command loads each non-empty line of the text file (relative paths are resolved from the project root). Any entry whose relevant value equals one of those lines is preserved even when it does not contain the prefix. Use this to retain standard objects or tabs that complement your custom solution.
  3. Section-aware filtering. The cleaner scans the following sections: applicationVisibilities, classAccesses, customPermissions, fieldPermissions, objectPermissions, pageAccesses, recordTypeVisibilities, tabSettings, and userPermissions. For composite fields such as fieldPermissions and recordTypeVisibilities, both the full API name (Account.Field__c) and its components (Account, Field__c) are checked against the prefix and allowlist so you can keep entire objects or individual fields.
  4. Preserve untouched metadata. Elements outside of the filtered sections (labels, descriptions, activation flags, etc.) are copied verbatim from the source permission set.

The default output file follows the pattern <PermissionSet>_<prefix>_filtered.permissionset-meta.xml. Use --output to provide a custom name (the .xml extension is appended automatically when omitted).

Flags

| Flag | Description | Default | |------|-------------|---------| | --permissionset, -p | Required. File name (with or without .permissionset-meta.xml) located under the project’s permission-set folder. | N/A | | --prefix, -f | Required. Fragment that must appear in an entry for it to remain in the cleaned file. | N/A | | --exclude, -e | Path to a newline-delimited text file containing exact values that must always be kept. | None | | --output, -o | Name of the XML file written under cleanps/. | <PermissionSet>_<prefix>_filtered.permissionset-meta.xml | | --project-dir | Optional root directory that holds sfdx-project.json. When omitted, the command walks up from the current working directory. | Auto-detected |


findtest command

Explanation

Analyse Apex classes and their associated tests with:

Usage

sf metadelta findtest [flags]

By default the command looks for sfdx-project.json in the current directory (or its parents) and inspects the force-app/main/default/classes folder.

Tip: After pulling plugin updates, run sf plugins link . again so the Salesforce CLI registers the new findtest command.

When --xml-name points to a manifest that needs to be updated (for example to add detected tests), the command refreshes its <version> node with the API version reported by the target org when --org/--target-org is supplied.

Examples

| Scenario | Example | |----------|---------| | Show the Apex ↔︎ test mapping in the console | sf metadelta findtest | | Restrict the report to the Apex classes listed in a manifest (analysis only) | sf metadelta findtest --xml-name manifest/package.xml | | Validate a manifest against a specific org while keeping a dry-run deploy | sf metadelta findtest --xml-name manifest/package.xml --org SFOrg | | Execute the deployment helper without --dry-run | sf metadelta findtest --xml-name manifest/package.xml --org SFOrg --run-deploy | | Validate/deploy the manifest and automatically include Destructive-<manifest>.xml | sf metadelta findtest --xml-name manifest/release.xml --org SFOrg | | Run a production-ready deployment that skips -l when no Apex tests are found | sf metadelta findtest --xml-name manifest/package.xml --org SFOrg --run-deploy-prod | | Ignore the manifest and inspect only local sources | sf metadelta findtest --only-local | | Include managed-package classes explicitly | sf metadelta findtest --xml-name manifest/package.xml --no-ignore-managed | | Use a custom Apex trigger directory | sf metadelta findtest --triggers-dir force-app/main/default/triggers |

Note: The deployment helper (dry-run or live deploy) requires --org or --target-org. Without either flag, the command only analyses manifests and local sources—even when --xml-name is provided.

Manual-step documentation detection

When you provide --xml-name (or --deploy), the command cross-checks the manifest name against files inside the project’s docs/ directory. If it finds documentation that references the manifest identifier (for example docs/Prefix-NumberStories-PRE.md for manifest/name-branch.xml), the console shows a prominent warning so you can review and run those manual steps before or instead of the deployment.

If the manifest file itself is missing but matching documentation exists under docs/, the command stops and reminds you to follow the documented manual procedure without using --dry-run or --run-deploy. When neither the manifest nor related documentation exist, it reports the missing XML file as an error.

How Apex tests are detected

sf metadelta findtest inspects the Apex source itself to classify classes. A class annotated with @IsTest (or using the legacy testMethod syntax) is treated as a test class; it is included in -t only when it also contains test methods. This prevents factories such as TestDataFactory from being passed as deploy tests. The class name is used only as a fallback when its source cannot be read.

Once the functional and test pools are separated, the command evaluates each class with the following steps:

  1. Direct suffix match. findtest attempts to append each of the known test suffixes (Test, _Test, TestClass, etc.) to the Apex class name and looks for an exact match. The comparison also tolerates trigger handler patterns by trimming a trailing Handler before trying the suffixes, so classes like MyTriggerHandler can pair with tests named MyTriggerTest.
  2. Content analysis. When there is no direct match, the command opens every test class and looks for whole-word references to the Apex class: instantiations (new MyClass), static member access (MyClass.someMethod(), or variable declarations (MyClass variable;). A reference is valid coverage and all matching tests are included, rather than only the highest-scoring suggestion. Scheduler, schedule, and schedulable role suffixes are also considered when matching related test names.
  3. Manifest reconciliation. Tests already listed in the manifest are included when they are runnable test classes, even when their production class is not part of the delta. Support classes annotated with @IsTest but without test methods are excluded. The command normalizes every <members> entry before comparing it against inferred tests, preventing duplicate insertions.
  4. Trigger coverage. ApexTrigger members in the manifest are inspected from force-app/main/default/triggers (or --triggers-dir). A trigger is considered covered when a runnable test references its handler/trigger or performs DML involving the trigger object. An uncovered trigger is reported as a blocking warning.
  5. Coverage exemptions and indirect coverage. Interfaces, enum-only classes, and classes with no executable code are reported as informational and do not block a deployment. When a manifest class is exercised by another manifest class whose test is already resolved, findtest associates that test using the indirecta confidence and does not block the deployment.
  6. Explicit test maps. Use --test-map with a JSON or CSV file when a relationship cannot be discovered statically, such as Type.forName or metadata-driven instantiation. If omitted, .metadelta/findtest-map.json is loaded automatically when present. A map entry takes priority over heuristic matches, and references to missing test files remain blocking.

Flags

| Flag | Description | Default | |------|-------------|---------| | --project-dir | Path to the Salesforce project root (folder that contains sfdx-project.json). If omitted, the command walks up from the current directory until it finds it. | Current project | | --source-dir | Relative or absolute path to the Apex classes directory. | force-app/main/default/classes | | --triggers-dir | Relative or absolute path to the Apex triggers directory used for manifest coverage analysis. | force-app/main/default/triggers | | --xml-name | Relative or absolute path to an existing package.xml. When provided, the console report starts from the Apex classes declared in that manifest and the same file is used for deployment validation. | N/A | | --deploy | Alias for providing the deployment manifest path. It behaves like --xml-name. | N/A | | --destructive-xml | Optional explicit path to a destructiveChanges.xml; if omitted, associated finddelta and conventional PRE/POST files are auto-detected. | Auto-detected | | --destructive-order | Selects auto, pre, or post. auto honors PRE/POST path markers and defaults unmarked finddelta output to post. | auto | | --org | Alias or username to use with the deployment helper. Mirrors --target-org but is shorter to type. | CLI default | | --target-org | Alias or username passed to sf project deploy start (same behaviour as --org). | CLI default | | --run-deploy | Executes the deployment helper without appending --dry-run. When omitted, the helper always adds --dry-run to keep the validation non-destructive. | false | | --run-deploy-prod | Compatibility alias for --run-deploy: runs without --dry-run. Production detection independently controls whether NoTestRun is allowed. | false | | --only-local | Ignores the manifest (if any) and analyses only the Apex classes present in the local repository. | false | | --ignore-managed, --no-ignore-managed | Skip (true) or include (false) classes whose names start with namespace__. | true | | --ignore-communities, --no-ignore-communities | Skip (true) or include (false) the built-in Communities controllers (ChangePasswordController, etc.). | true | | --verbose | Print detailed warnings for every class filtered out or missing locally. | false | | --test-map | Path to a JSON or CSV map of Apex production classes to test classes. | .metadelta/findtest-map.json when present | | --json | Emit a JSON summary with filtering metrics and per-class confidence (exact, reference, indirecta, mapa, none, or no_requiere). Indirect matches include via. | false |

Deployment helper flow

When you provide a manifest file through --xml-name or --deploy, the command:

  1. Reads the existing package.xml (the file must already exist).
  2. Searches for an associated destructive manifest. It recognizes Destructive-<manifest>.xml, preDestructive/destructiveChanges.xml, and postDestructive/destructiveChanges.xml; use --destructive-xml to provide an explicit path.
  3. Validates the active package and every detected destructive manifest before starting Salesforce CLI. Empty or malformed destructive manifests stop the command with a non-zero exit code.
  4. Checks for <types><name>ApexClass</name></types> and <types><name>ApexTrigger</name></types> entries. If neither is present, it detects Organization.IsSandbox in the target org. Sandbox orgs use -l NoTestRun; production orgs omit -l so Salesforce applies its default test level, both for dry-run and real deployment. Without an org, the workflow stops after the report.
  5. Builds the evaluation list by intersecting the manifest with the local filesystem, optionally removing managed-package members and Communities controllers. Use --verbose to list the skipped entries.
  6. Finds the associated test classes for each remaining Apex class. Direct name matches (MyClassTest, MyClass_Test, MyClassTests, …), expanded type references (initializers, generics, casts, and .class usage), and whole-word source references are included in -t; tests already present in the manifest are also included. Interfaces, enum-only classes, and classes without executable code are marked no_requiere. A class used by another manifest class inherits that class's resolved tests as indirecta for one dependency level. --test-map overrides these heuristics. Trigger coverage is resolved through its handler, trigger name, or DML on the trigger SObject.
  7. If any Apex class or trigger lacks an associated test, or a required test file is missing, the command reports the names. In sandboxes it skips sf project deploy start so you can fix the manifest or restore the files; in production, when no tests are resolved, it continues without -l so Salesforce applies its default test level.
  8. Executes one orchestrated sf project deploy start with the active manifest and the detected destructive manifest. --destructive-order auto honors PRE/POST directory markers and uses POST for an unmarked finddelta destructive. The command appends --dry-run unless you pass --run-deploy; deploy failures terminate findtest with a non-zero exit code. Use --org/--target-org to override the CLI default org.

When the Organization.IsSandbox query fails, findtest warns and treats the destination as production, omitting NoTestRun because Salesforce production organizations reject that test level. --run-deploy-prod remains supported as a compatibility alias for running without --dry-run.

Output

Every run starts with a summary line detailing how many classes came from the manifest (or filesystem), how many were filtered out, and how many remain in the local repository. The detailed mapping preserves the original script format (ApexClass → ApexTest). When a manifest is provided, the command automatically ignores managed-package entries (namespace__*) and common Communities controllers unless you opt back in; only classes that exist locally are considered for test discovery. Use --verbose to list the filtered names and --json to capture the underlying metrics programmatically.

Directly matched tests and tests that reference the Apex class in source are included in the mapping and deployment command. @IsTest support classes without test methods are excluded, while tests already listed in the manifest are retained when they are runnable. Interfaces, enum-only classes, and classes without executable code are shown as informational no_requiere entries. Indirect matches show the intermediate class, and explicit map matches show mapa; both appear in the JSON mapping with their confidence and selected tests.


manual collect command

Explanation

Build a consolidated runbook of manual steps by parsing markdown files stored under a directory such as docs/. Valid filenames follow the Prefix-<story>-<PRE|POST>.md pattern—files that start with Prefix are normalized automatically. Run the command with:

Usage

sf metadelta manual collect --docs ./docs --output ./docs/MANUAL-STEPS.md --all

By default (or when passing --all) the command gathers every matching .md, sorts entries so PRE steps appear before POST, orders them chronologically (filesystem mtime unless --order-by git is provided), and emits a markdown file with an index, a metadata banner, and the original content per story.

Enable partial mode to limit the output to the stories that are still pending merge between a sprint branch and its base branch. The command runs git diff --name-only <base>..<sprint> -- <docs> behind the scenes and filters the list to keep only the manual-step markdown files:

sf metadelta manual collect \
  --docs ./docs \
  --output ./docs/MANUAL-STEPS.md \
  --partial \
  --sprint-branch SP1/main \
  --base-branch master \
  --sprint-name SP1 \
  --order-by git

If no qualifying files remain in the requested range the command stops with a friendly message so you can confirm whether the sprint actually merged any documentation. Leaving out both --partial and --all behaves the same as --all for convenience.

Flags

| Flag | Description | Default | |------|-------------|---------| | --docs, -d | Required. Directory that hosts the manual-step .md files. | N/A | | --output, -o | Required. Destination markdown file that will contain the consolidated content. | N/A | | --partial | Restricts the output to the files pending merge between the base branch and the sprint branch. Requires --sprint-branch. | false | | --all | Forces the command to include every manual-step file in --docs. (This is also the default behaviour when --partial is not set.) | false | | --sprint-branch | Sprint branch used in partial mode. | N/A | | --sprint-name | Optional label shown in the markdown header/banner. | None | | --base-branch | Base branch used to compute the diff range when --partial is active. | master | | --order-by | Source for the ordering timestamp. Use git to rely on commit dates instead of file modification times. | mtime |


merge command

Explanation

Combine multiple manifest fragments into a single package with:

Usage

sf metadelta merge --xml-name <substring> [flags]

By default the command looks inside the manifest/ directory for XML files whose filenames contain the provided substring. It merges their <types> entries, deduplicating members per metadata type and keeping the highest API version found across the inputs. Each <members> node in the resulting manifest now carries an inline <!-- source --> comment listing the contributing manifest filenames (without the .xml suffix) so you can trace every component. The result is saved to manifest/globalpackage.xml, unless you override the filename.

When you add --partial --sprint-branch <name> [--base-branch master], the command limits its search to manifest files that are still pending merge between the specified sprint branch and its base branch. Internally it runs git diff --name-only <base>..<sprint> -- manifest/ (respecting --directory) and keeps only the matching XML files. If the diff is empty the command stops with a clear message so you can adjust the range or fallback to a full merge.

Flags

| Flag | Description | Default | |------|-------------|---------| | --xml-name, -x | Required. Substring that matching manifest filenames must contain. | N/A | | --directory, -d | Directory that holds the manifest XML files to merge. | manifest | | --output, -o | Name of the combined manifest file to generate. | globalpackage.xml | | --partial | Restricts the merge to manifest files pending merge between the base branch and the sprint branch. Requires --sprint-branch. | false | | --sprint-branch | Sprint branch that contains the manifests you want to consolidate. | N/A | | --base-branch | Base branch already deployed to production. Used to compute the diff in partial mode. | master |

Example

To merge every manifest whose filename contains Prefix into manifest/globalpackage.xml:

sf metadelta merge --xml-name Prefix

To restrict the merge to manifests that have not been merged back into master yet:

sf metadelta merge --xml-name Prefix --partial --sprint-branch Branch-Destination --base-branch master

Uninstalling

To unlink the plugin from your Salesforce CLI:

sf plugins unlink @nervill/metadelta

License

This project is released under the ISC License.


Español

Metadelta es un plugin personalizado de Salesforce CLI que ofrece trece familias de comandos complementarias:

  • sf metadelta find inspecciona una org de destino y reporta los componentes de metadatos modificados por un usuario específico durante un rango de tiempo reciente, generando opcionalmente manifiestos para despliegues o migraciones de paquetes de Vlocity. Al crear package.xml, la versión del manifiesto coincide con la versión de API detectada en la org de destino.
  • sf metadelta orgApiVersion imprime la versión de API reportada por una org de destino y se usa internamente por comandos que necesitan alinear manifiestos gene