Scaffold a plugin
This recipe turns an empty folder into a plugin package that builds out of the
box — a working example manifest, behaviors, and a sample workflow, ready to
be adapted. It uses meridian-plugin new, works in any directory (no access to
the platform repository needed), and is the first step of
Build a plugin, end to end. For the why
behind plugins, see What a plugin is.
Prerequisites
- Node 20+ and a package manager (npm/pnpm/yarn).
- Access to the SDK (
@meridian/shared) and the CLI (@meridian/plugin-cli), published by the platform team — either through a reachable package registry, or as a set of.tgztarballs handed to you directly. Both routes are covered below.
Scaffold the project
Via a registry
Run the CLI with
npx, passing the plugin's package-style name:npx @meridian/plugin-cli new @acme/vitalsThe CLI prints where it wrote the project:
Scaffolded @acme/vitals in /…/vitals Next: cd into it, `npm install`, then `npm run build`.Install its dependencies:
cd vitals npm install
Via tarballs (no registry access)
If the platform team handed you .tgz files instead of registry access,
install them all in a single command — npm prunes unsaved packages between
separate installs — then drive the CLI through the local
node_modules/.bin (the scaffold's own npm scripts already do this):
# one-time bootstrap of the scaffold (the team can also send you a
# pre-scaffolded folder instead)
npx --package ./meridian-plugin-cli-0.1.0.tgz meridian-plugin new @acme/vitals
cd vitals
npm install --no-save ../meridian-shared-0.1.0.tgz ../meridian-engine-core-0.1.0.tgz \
../meridian-plugin-cli-0.1.0.tgz typescript
The tarballs must be produced with pnpm pack, not npm pack: the
packages' bin/exports fields live under publishConfig
(libs/plugin-cli/package.json, libs/shared/package.json,
libs/engine-core/package.json), which only pnpm applies when packing or
publishing. An npm pack tarball ships without the meridian-plugin binary
and with exports still pointing at src/*.ts. For the same reason, npm link does not expose the meridian-plugin command either — there is no
top-level bin field by design.
Once installed this way, the rest of this guide and
Build a plugin, end to end work unchanged;
substitute npm install (registry) with the tarball install above wherever you
see it, and run the CLI's other commands the same way — npm run gen-types,
npm run validate …, npm run build all resolve to meridian-plugin …
through the scaffolded package.json scripts either way.
Choose the name and destination folder
meridian-plugin new <name> [dir] (libs/plugin-cli/src/commands/scaffold.ts):
<name>is the plugin's package-style identifier, the same string you will later put undername:inplugin.yamland under an instance'splugins:map — typically scoped, e.g.@acme/vitals.[dir]is optional. If omitted, the destination directory is<name>with its npm scope stripped (@acme/vitals→./vitals). Pass a second argument to scaffold elsewhere:meridian-plugin new @acme/vitals ./packages/vitals.- Scaffolding refuses to run if the destination already contains a
package.json, so it never overwrites an existing project — pick an empty or non-existent directory.
What you get
vitals/
package.json # depends on the SDK; build/gen-types/validate scripts
tsconfig.json # Node ESM, emits to dist/
plugin.yaml # an example: value-set + type + context kind + trigger + compute
src/behaviors/example.ts # the behaviors, typed
workflows/example.workflow.yaml # a sample workflow you can validate
.gitignore # node_modules/, dist/
README.md
package.json already wires build, gen-types and validate to the
meridian-plugin CLI, and depends on @meridian/shared (runtime) plus
@meridian/plugin-cli and typescript (dev). plugin.yaml declares one
value-set, one type, one context kind (subject — so the template stands
alone), a trigger node and a compute node — a small,
coherent starting point, not a placeholder to delete. The full manifest schema
is in Plugin manifest; every command the
scripts call is documented in the CLI reference.
Verify
Confirm the scaffold compiles before you change anything:
npm run build # → meridian-plugin build (esbuild bundle to dist/)
A successful run reports the bundled module count and exits zero:
✓ @acme/vitals@0.1.0 → dist/ (1 module(s) bundled, runtime deps included).
If this fails on a fresh scaffold, re-check the install step above (in particular the tarball set, if you're not using a registry) before touching any generated files.
Related
- Build a plugin, end to end — the full tutorial this recipe opens: generate types, validate, build, and publish the project you just scaffolded.
- Plugin manifest — the complete
plugin.yamlschema. - CLI reference — every
meridian-plugincommand and flag. - Generate types and Validate a workflow — the next two commands you'll run against the scaffold.
- Build and bundle and
Bundling and distribution —
what
meridian-plugin buildactually produces. - Publish to the registry — ship the built
dist/once you're ready. - What a plugin is — the concepts behind contributions, autonomy, and the SDK boundary.
- Set up a plugin registry and Declare and install plugins — the operator side that eventually loads what you scaffold here.