3.0.0 (2026-11-06)
- merge: a plugin's specs merge in import order, as in lazy.nvim, so a later spec under
dependenciesoverrides a top-level one and its functionoptssees the top-level opts - module_loader: a
requireloads a lazy plugin only when the plugin holds the module's file, as in lazy.nvim; one with only the top-level name stays lazy - commands:
:ZPack cleanand:ZPack synclist what they would delete and ask, defaulting to No;!skips the prompt - import: a
nilin a spec list no longer ends the list, so what follows it installs - lazy: a plugin whose function-form trigger throws stays lazy instead of loading at startup
- module_loader: remove the unreachable profiler;
setup({ profiling = ... })is ignored, andprint_profileandget_profileare gone - module_loader:
requireno longer loads a plugin whose name resembles a module it doesn't provide; a plugin registering a module fromplugin/needs a trigger of its own - keys: two strings in
keysare two keys unless the second can only be an rhs, so{ '<leader>d', 'dd' }needs a field or nesting to stay one key - keys: a later spec's key replaces an earlier one
- import: a bare name nothing defines is skipped rather than installed
- merge: a disabled dependency no longer disables the plugin listing it
- merge: the last spec to set
enabledorcondwins, instead of anyfalse - lazy: an
eventwith no real event in it, such asLazyFile, leaves the plugin eager with a warning - utils: a
versionstring git can't take as a ref, such as*, is a semver range - import: spec modules import in module name order
- version: version fields follow lazy.nvim's priority: commit, tag, version, sem_version, branch, default
- close lazy.nvim parity gaps in spec lists, keys, triggers and bare names (85ba638)
- import: import specs from a plugin's own modules (85ba638)
- merge: support opts_extend (#46) (b23ed1f), closes #45
- cmd: keep a numeric first argument on a lazy command's first use (936649e)
- commands:
:ZPack cleanand:ZPack synclist what they would delete and ask, defaulting to No;!skips the prompt (936649e) - commands:
:ZPack cleanleaves a plugin active this session for next time rather than raising (936649e) - ft: a lazy
ftplugin'sftdetect/is sourced once, as in lazy.nvim, so one that runsset filetype=firesFileTypeonce per file (bfd84e3) - hooks: a module brought in by an in-session update is found without a restart (936649e)
- hooks: removing a plugin drops its command and key proxies, so its command gives E492 and a command it shared with another lazy plugin works for that plugin; an
initremoving a plugin no longer re-fires its event or crashessetup()when the plugin has its owninit(bfd84e3) - import: a
nilin a spec list no longer ends the list, so what follows it installs (936649e) - import: a bare name nothing defines is skipped rather than installed (85ba638)
- import: a bare string in a spec list installs as a plugin, rather than being reported as invalid and skipped (936649e)
- import: an
importwith no plugin of its own finds its module in a listed plugin named after it, installing it if missing, on the first start (936649e) - import: spec modules import in module name order (85ba638)
- import: take a plugin's
lua/offpackage.pathafter startup (936649e) - import: warn when an import finds no specs (85ba638)
- keys: a key keeps its proxy while a plugin listing it is pending, as in lazy.nvim, so another plugin's
keyson the same lhs don't stop it loading (bfd84e3) - keys: a later spec's key replaces an earlier one (85ba638)
- keys: a plugin drops its key proxies as it loads, as in lazy.nvim, so a plugin that maps a key only when it's free (flash.nvim's
;and,) gets it (bfd84e3) - keys: two strings in
keysare two keys unless the second can only be an rhs, so{ '<leader>d', 'dd' }needs a field or nesting to stay one key (85ba638) - lazy: a plugin whose function-form trigger throws stays lazy instead of loading at startup (936649e)
- lazy: an
eventwith no real event in it, such asLazyFile, leaves the plugin eager with a warning (85ba638) - lazy: no proxy over what a function-form trigger already loaded (936649e)
- lazy: set up triggers in a session that installs a plugin (85ba638)
- loader: a function-form
optsthat throws is reported once and the plugin still loads without itsconfig, as in lazy.nvim, instead of staying pending (bfd84e3) - loader: a plugin already on 'runtimepath' that loads after a
configturned detection on gets itsftdetect/once, not twice (bfd84e3) - loader: a require or build after startup sources plugin/, and a failing dependency doesn't stop its parent (85ba638)
- loader: report a
plugin/script's error without failing the load (936649e) - loader: source a plugin's dependencies before its plugin/, in the order they're listed (85ba638)
- merge: a disabled dependency no longer disables the plugin listing it (85ba638)
- merge: a plugin's specs merge in import order, as in lazy.nvim, so a later spec under
dependenciesoverrides a top-level one and its functionoptssees the top-level opts (bfd84e3) - merge: a table listed at the top level and under a lazy parent's
dependenciesloads eagerly (#49) (85ba638) - merge: clear opts keys set to vim.NIL (#47) (3bd8cdf)
- merge: drop a removed plugin's specs before deriving what stays (#49) (871104e)
- merge: plugins under an optional or disabled parent's
specsare dropped (#49) (85ba638) - merge: removals resting on other removals no longer depend on import order (936649e)
- merge: the last spec to set
enabledorcondwins, instead of anyfalse(85ba638) - module_loader:
requireno longer loads a plugin whose name resembles a module it doesn't provide; a plugin registering a module fromplugin/needs a trigger of its own (936649e) - module_loader: a
requireloads a lazy plugin only when the plugin holds the module's file, as in lazy.nvim; one with only the top-level name stays lazy (241c9ba) - module_loader: a later
requireof a module whose load threw raises that error instead of returningtrue(241c9ba) - module_loader: a slash-form
require('lspconfig/util')loads the pending plugin holding the module (241c9ba) - module_loader: load the plugin holding a module in a shared namespace, including a compiled one (936649e)
- module_loader: when several pending plugins hold a module, the one named like it loads, as in lazy.nvim (241c9ba)
- pack: a soft version installs from the default branch after another plugin's clone failed (936649e)
- pack: read a tag as
vim.packdoes when deciding if one fits (936649e) - pack: restore a locked plugin at its revision when no tag fits its soft version (936649e)
- pack: say when a plugin needs a restart to reach a tag or to update (936649e)
- runtime: zpack escapes the path of each file it sources itself, so a
#or%in the data path no longer fails with E194/E484 and loses the file (bfd84e3) - setup:
setup()with nospecand nolua/plugins, or one holding only dotfiles, is quiet, as in 2.x (936649e) - setup:
setup()'s options no longer count as spec fields, so specs written beside them all install (936649e) - setup: a plugin vim.pack can't add no longer aborts setup() (85ba638)
- setup: a single spec with no array part, such as
{ import = ..., defaults = ... }, is read as a spec rather than ignored for the default import (936649e) - setup: a string is the module to import, in
setup('custom.plugins')and inspec, rather than reported and replaced by the default import (936649e) - setup: any other
setup()argument that isn't a table is reported, and the default import goes on (936649e) - setup: source plugin/ when setup() runs after startup (85ba638)
- startup:
:colorschemeloads the pending plugin holding the scheme first, as in lazy.nvim, so a Vim colors file can call its plugin'sautoload/from aninitor for a lazy plugin, and an eager scheme plugin set from an earlier plugin'sconfig(themery.nvim's restore) gets its ownconfigfirst (bfd84e3) - startup: a
requirefrom aninitloads the eager plugin holding the module, as in lazy.nvim, instead of failing with "module not found" (bfd84e3) - startup: a lazy plugin's
cmd,event,ftandkeysare set up before the inits, as in lazy.nvim, so aninitrunning one loads the plugin (bfd84e3) - startup: an eager plugin's
ftdetect/runs when filetype detection is on beforesetup(), as in lazy.nvim, so LazyVim's vimtex opens a.clsastex(bfd84e3) - startup: run an eager plugin's
configonce when a lazy dependent loads it (936649e) - utils: a
versionortagof the wrong type is dropped with a warning instead of failing every plugin (936649e) - utils: a
versionstring git can't take as a ref, such as*, is a semver range (85ba638) - validate: warn about a
sem_versionthat isn't a semver range, and fall through tobranchanddefaults.version(936649e) - version: a semver string no tag fits follows the default branch (85ba638)
- version: version fields follow lazy.nvim's priority: commit, tag, version, sem_version, branch, default (85ba638)
- module_loader: remove the unreachable profiler;
setup({ profiling = ... })is ignored, andprint_profileandget_profileare gone (936649e)
2.0.4 (2026-09-25)
- utils: allow non-GitHub URLs while still expanding the short form GitHub owner/repo paths (#42) (a6fdbf5)
- utils: only pass git URLs through [1]; flag local paths in validate (#44) (451a0bb), closes #42
2.0.3 (2026-09-22)
2.0.2 (2026-08-20)
2.0.1 (2026-08-02)
2.0.0 (2026-06-03)
2.0.0 removes every previously deprecated setup() option, the zpack.add()
function, and the legacy :Z* commands. Update your config before upgrading:
| Removed in 2.0 | Replacement |
|---|---|
setup({ confirm = … }) |
setup({ defaults = { confirm = … } }) |
setup({ disable_vim_loader = true }) |
setup({ performance = { vim_loader = false } }) |
setup({ plugins_dir = 'dir' }) |
a { import = 'dir' } entry in your spec list |
setup({ cmd_prefix = 'Z' }) |
setup({ cmd_name = 'Z' }) |
legacy :ZUpdate / :ZClean / :ZDelete / :ZRestore / :ZLoad / :ZBuild |
:ZPack update / clean / delete / restore / load / build (or :Z … with cmd_name = 'Z') |
setup({ auto_import = … }) |
removed — was already a no-op in 1.x |
require('zpack').add(…) |
removed — was already a no-op in 1.x |
See Upgrading from 1.x to 2.0 for the full migration guide.
- adopt Neovim plugin best practices (#23) (2f45de6)
- close lazy.nvim parity gaps across lazy triggers (#27) (9ed473a)
- lazy-parity: add defaults.lazy and defaults.version (#29) (d52f0dd)
- lazy.nvim spec parity (pin/optional/dev/specs/build/reload/sync) (#28) (b6f82cd)
- remove deprecated features for 2.0.0 (#25) (4681a74)
- support bang on :ZPack update/restore; document 0.13 native commands (4f9b312)
- sync state with vim.pack via PackChanged on plugin removal (0b65048)
- address PR #21/#22 review follow-ups (c9c6c61)
- command-specific legacy command deprecation notice (8970a43)
- correct bang placement in usage hints; always warn on legacy commands (2f4bf35)
- guard non-number priority and non-string import field (af3f109)
- guard non-string src/url/dir in normalize_source (b127f63)
- harden lazy-load path against operator-pending loss + throw-leaks (#26) (c6079d5)
- preserve typeahead order on multi-key sequences (6b99fcb)
- :ZPack with subcommands (d197fc6)