Run nub <verb> with a verb Nub doesn't recognize and it resolves an executable named nub-<verb>, then runs it with the rest of the arguments forwarded untouched. This is the same convention behind git-foo and cargo-foo.

nub changeset publish      # runs nub-changeset publish
nub release --tag beta     # runs nub-release --tag beta

A plugin is a package that installs a nub-<verb> bin — there is no plugin manifest and no registration step. Install it like any other dependency and the verb works.

nub add -D nub-changeset   # ships a nub-changeset bin
nub changeset              # now resolves and runs it

Resolution

The prefixed name is resolved in two places, in order:

  • node_modules/.bin — the project-local bin, walking up from the current directory (the same chain nub run and nubx use).
  • PATH — a globally-installed plugin.

Only the prefixed nub-<verb> name is ever probed — never the bare verb.

Built-in precedence

A plugin can never shadow a built-in verb. Native commands and package-manager verbs are matched before any plugin lookup, so nub run, nub install, nub add, and the rest always dispatch to Nub itself, even if a nub-run bin exists in node_modules/.bin. The fallthrough fires only for a verb Nub has no built-in for.

Script names take the same priority. A verb that names a script in the local package.json, or one of the conventional names dev, build, test, start, and lint, resolves to the nub run <verb> hint before the plugin lookup — so a nub-build or nub-test plugin is never reached through its bare verb. Name a plugin for something that isn't a script (nub-changeset, nub-release), or invoke a script-named one through nub run or by its bin directly.

Writing a plugin

A plugin is a runnable executable named nub-<verb> — a compiled JavaScript bin or a native binary, the same as any other node_modules/.bin entry or a git/cargo subcommand. There's no plugin-specific build target: author it in TypeScript and publish the compiled output, as any other bin. Installed bins are not transpiled, so the entry that runs must be JavaScript or native.

A JavaScript plugin runs under Nub's runtime augmentation, as nub run runs a script — the version-gated globals are present. A native executable runs directly. The plugin's exit code is what Nub returns.

nub-changeset
#!/usr/bin/env node
const target = process.argv[2] ?? "staging";
console.log(`releasing ${target}`);

To run a plugin with augmentation disabled, pass --node before the verb — nub --node changeset, the same position a file run uses. Anything after the verb is forwarded to the plugin untouched, so a trailing nub changeset --node reaches the plugin as an argument and does not disable augmentation. See the runtime overview for the contract.

  • Bin runner — nubx, for running a tool by name without installing it as a subcommand.
  • Script runner — nub run, which shares the bin-resolution chain.
  • Runtime — TypeScript-first execution and the augmentation a JS/TS plugin inherits.