Configured WordPress plugins live in nf.json under wordpress.plugins. The list is an env bootstrap checklist, not a full plugin lifecycle manager.
{
"wordpress": {
"plugins": [
"stream",
"wp-crontrol",
"insert-headers-and-footers",
"block-visibility",
"imsanity",
{
"slug": "acf-pro",
"source": "cache",
"activate": true,
"auto_update": false
},
{
"slug": "client-plugin",
"source": "repo"
},
{
"slug": "sitepress-multilingual-cms",
"install": false,
"note": "WPML; install manually from wpml.org account"
}
]
}
}
String entries install from wordpress.org, activate, and enable auto-updates by default. Object entries require slug, may set source to a zip URL/path, env var, repo, or cache, may set activate or auto_update to false, and may set install to false for manual/documentation-only plugins that nf should check but never install.
Use source: "repo" for project-specific plugins stored at plugins/<slug>/ in the repo. Local envs bind mount configured repo plugins into /var/www/html/wp-content/plugins/<slug> for live development, then nf plugin install activates them and configures auto-updates as requested. Remote installs package repo plugins into temporary zips, replace the installed copy through WP-CLI, and clean up. No plugin artifact is written to dist/ or committed to the repo.
Use source: "cache" for paid/private plugins whose installable zip is kept in nf’s local plugin cache under $NF_DATA_HOME/plugins/<slug>/<slug>.zip. The plugin’s main file must declare Plugin Name and Version headers. This is explicit; nf does not silently fall back from wordpress.org to the cache.
Keep private plugin URLs and license data in environment variables, not nf.json.
nf plugin list
nf plugin add stream
nf plugin add acf-pro --source '$NF_PLUGIN_ACF_PRO_ZIP'
nf plugin add acf-pro --source cache --no-auto-update
nf plugin add client-plugin --source repo
nf plugin add sitepress-multilingual-cms --manual --note 'WPML; install manually from wpml.org account'
nf plugin remove stream
nf plugin add <plugin> appends a plugin to nf.json without installing it. Add --source <source>, --manual, --note <note>, --no-activate, or --no-auto-update when defaults are not enough. With --source repo, nf also creates a minimal plugins/<slug>/<slug>.php plugin scaffold when that plugin directory does not exist yet.
nf plugin remove <plugin> removes a plugin from nf.json without uninstalling it.
nf plugin cache add acf-pro ~/Downloads/acf-pro.zip
nf plugin cache save sitepress-multilingual-cms
nf plugin cache pull acf-pro production
nf plugin cache list
nf plugin cache show acf-pro
nf plugin cache remove acf-pro
nf plugin cache add <plugin> <zip> copies an existing zip into $NF_DATA_HOME/plugins/<slug>/<slug>.zip.
nf plugin cache save <plugin> archives the plugin currently installed in the local WordPress env and stores it as the cached zip. Use this as a local recovery aid for paid/manual plugins that were installed through WordPress admin or vendor updaters.
nf plugin cache pull [plugin] [remote] inspects an installed remote plugin. Omit either value to choose interactively. If the slug exists on WordPress.org, nf downloads no code and adds or normalizes its nf.json entry as a simple string. Otherwise nf validates and packages the remote directory into $NF_DATA_HOME/plugins/<slug>/<slug>.zip, configures source: "cache", and retains existing install, activation, auto-update, and note settings. A plugin already configured with source: "repo" must use nf plugin pull instead.
nf plugin cache list and nf plugin cache show <plugin> inspect the local cache. nf plugin cache remove <plugin> deletes one cached plugin zip. Cached zips are local machine state, not project metadata, and are not committed.
nf plugin pull acf-pro production
Omit the plugin or remote to choose interactively. The whole Git worktree, including untracked files, must be clean before nf contacts the remote and immediately before it applies files. Known repo plugins are refreshed directly. Other installed plugins can be adopted only when they are private; nf creates or converts their nf.json entry to source: "repo" and writes to plugins/<slug>.
The pull overlays remote files, overwriting matching paths while preserving local-only development files excluded from deployment packages. It rejects unsafe archives, symlinks, and file/directory conflicts. The resulting Git changes are left unstaged for review and a normal commit.
Local env:
nf plugin status
nf plugin diff
Configured remote:
nf plugin status production
nf plugin diff production
nf plugin status [remote] compares nf.json against the local env or configured remote and reports whether each configured plugin is installed, active, and auto-update enabled. The code column is - for other plugin sources. For each source: "repo" plugin it is:
current when the installed code matches the repository package payloaddrifted when file contents or the set of relative file paths differsunavailable when the local source, installed plugin, or remote fingerprint cannot be readRemote status fingerprints the installed directory directly in the existing batched SSH status command. It does not trust a stored deployment hash, so direct remote edits, missing files, and extra files are detected. The deterministic comparison uses relative file paths and contents only and ignores timestamps, ownership, permissions, and archive metadata. The local side covers exactly the regular files that repo-plugin packaging deploys; .git directories are excluded from that payload, while unexpected regular files in the installed remote directory count as drift.
Local repo plugins use bind mounts for live development. When the repository source exists and WordPress sees the mounted plugin as installed, local code status is current; nf does not compare the mount with itself or treat local .git data excluded from packaging as drift.
nf plugin diff [remote] reports the install/activate/auto-update changes needed to make the local env or remote match nf.json. A drifted installed repo plugin reports refresh repo source; activation and auto-update changes remain visible on the same row. Manual plugins with install: false are still checked and report manual install required when missing. It also reports installed plugins that are not configured in nf.json. It does not mutate anything. It exits 0 when configured plugins and repo payloads match and no extras are installed, and 2 when drift exists.
Local env:
nf plugin install
Remote dry-run:
nf plugin install production --dry-run
Remote install:
nf plugin install production --yes
Local installs use configured repo plugin bind mounts and cached zips when requested. Remote installs run WP-CLI on the remote host. URL sources must be reachable from that host; local zip, cache, and repo sources are uploaded to a temporary remote directory before install and cleaned up afterward. Repo sources are zipped locally on demand before upload.
Plugin install is source-aware. Remote repo plugins are replaced from the current repo source on every explicit install. Cached plugins are replaced only when the cache package’s declared version is newer than the installed version; equal, older, or unversioned installed copies are preserved. Other sources install only missing plugins. A local install warns and skips configured cache or repo plugins whose source artifact is unavailable, then continues with the remaining plugins. The command also activates inactive plugins when requested and enables native WordPress auto-updates only when not already enabled. It does not install manual plugins, update WordPress.org/URL plugins, remove, pin, disable auto-updates, or manage plugin licenses.